312 lines
7.8 KiB
Go
312 lines
7.8 KiB
Go
package whatsapp
|
|
|
|
import (
|
|
"regexp"
|
|
"rijig/config"
|
|
"rijig/utils"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
)
|
|
|
|
type QRResponse struct {
|
|
QRCode string `json:"qr_code,omitempty"`
|
|
Status string `json:"status"`
|
|
Message string `json:"message"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
}
|
|
|
|
type StatusResponse struct {
|
|
IsConnected bool `json:"is_connected"`
|
|
IsLoggedIn bool `json:"is_logged_in"`
|
|
Status string `json:"status"`
|
|
Message string `json:"message"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
}
|
|
|
|
type SendMessageRequest struct {
|
|
PhoneNumber string `json:"phone_number" validate:"required"`
|
|
Message string `json:"message" validate:"required"`
|
|
}
|
|
|
|
type SendMessageResponse struct {
|
|
PhoneNumber string `json:"phone_number"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
}
|
|
|
|
func GenerateQRHandler(c *fiber.Ctx) error {
|
|
wa := config.GetWhatsAppService()
|
|
if wa == nil {
|
|
return utils.InternalServerError(c, "WhatsApp service not initialized")
|
|
}
|
|
|
|
if wa.IsLoggedIn() {
|
|
data := QRResponse{
|
|
Status: "logged_in",
|
|
Message: "WhatsApp is already connected and logged in",
|
|
Timestamp: time.Now().Unix(),
|
|
}
|
|
return utils.SuccessWithData(c, "Already logged in", data)
|
|
}
|
|
|
|
qrDataURI, err := wa.GenerateQR()
|
|
if err != nil {
|
|
return utils.InternalServerError(c, "Failed to generate QR code: "+err.Error())
|
|
}
|
|
|
|
switch qrDataURI {
|
|
case "success":
|
|
data := QRResponse{
|
|
Status: "login_success",
|
|
Message: "WhatsApp login successful",
|
|
Timestamp: time.Now().Unix(),
|
|
}
|
|
return utils.SuccessWithData(c, "Successfully logged in", data)
|
|
|
|
case "already_connected":
|
|
data := QRResponse{
|
|
Status: "already_connected",
|
|
Message: "WhatsApp is already connected",
|
|
Timestamp: time.Now().Unix(),
|
|
}
|
|
return utils.SuccessWithData(c, "Already connected", data)
|
|
|
|
default:
|
|
|
|
data := QRResponse{
|
|
QRCode: qrDataURI,
|
|
Status: "qr_generated",
|
|
Message: "Scan QR code with WhatsApp to login",
|
|
Timestamp: time.Now().Unix(),
|
|
}
|
|
return utils.SuccessWithData(c, "QR code generated successfully", data)
|
|
}
|
|
}
|
|
|
|
func CheckLoginStatusHandler(c *fiber.Ctx) error {
|
|
wa := config.GetWhatsAppService()
|
|
if wa == nil {
|
|
return utils.InternalServerError(c, "WhatsApp service not initialized")
|
|
}
|
|
|
|
// if !wa.IsLoggedIn() {
|
|
// return utils.Unauthorized(c, "WhatsApp not logged in")
|
|
// }
|
|
|
|
isConnected := wa.IsConnected()
|
|
isLoggedIn := wa.IsLoggedIn()
|
|
|
|
var status string
|
|
var message string
|
|
|
|
if isLoggedIn && isConnected {
|
|
status = "connected_and_logged_in"
|
|
message = "WhatsApp is connected and logged in"
|
|
} else if isLoggedIn {
|
|
status = "logged_in_but_disconnected"
|
|
message = "WhatsApp is logged in but disconnected"
|
|
} else if isConnected {
|
|
status = "connected_but_not_logged_in"
|
|
message = "WhatsApp is connected but not logged in"
|
|
} else {
|
|
status = "disconnected"
|
|
message = "WhatsApp is disconnected"
|
|
}
|
|
|
|
data := StatusResponse{
|
|
IsConnected: isConnected,
|
|
IsLoggedIn: isLoggedIn,
|
|
Status: status,
|
|
Message: message,
|
|
Timestamp: time.Now().Unix(),
|
|
}
|
|
|
|
return utils.SuccessWithData(c, "Status retrieved successfully", data)
|
|
}
|
|
|
|
func WhatsAppLogoutHandler(c *fiber.Ctx) error {
|
|
wa := config.GetWhatsAppService()
|
|
if wa == nil {
|
|
return utils.InternalServerError(c, "WhatsApp service not initialized")
|
|
}
|
|
|
|
if !wa.IsLoggedIn() {
|
|
return utils.BadRequest(c, "No active session to logout")
|
|
}
|
|
|
|
err := wa.Logout()
|
|
if err != nil {
|
|
return utils.InternalServerError(c, "Failed to logout: "+err.Error())
|
|
}
|
|
|
|
data := map[string]interface{}{
|
|
"timestamp": time.Now().Unix(),
|
|
}
|
|
|
|
return utils.SuccessWithData(c, "Successfully logged out and session deleted", data)
|
|
}
|
|
|
|
func SendMessageHandler(c *fiber.Ctx) error {
|
|
wa := config.GetWhatsAppService()
|
|
if wa == nil {
|
|
return utils.InternalServerError(c, "WhatsApp service not initialized")
|
|
}
|
|
|
|
if !wa.IsLoggedIn() {
|
|
return utils.Unauthorized(c, "WhatsApp not logged in")
|
|
}
|
|
|
|
req := GetValidatedSendMessageRequest(c)
|
|
if req == nil {
|
|
return utils.BadRequest(c, "Invalid request data")
|
|
}
|
|
|
|
err := wa.SendMessage(req.PhoneNumber, req.Message)
|
|
if err != nil {
|
|
return utils.InternalServerError(c, "Failed to send message: "+err.Error())
|
|
}
|
|
|
|
data := SendMessageResponse{
|
|
PhoneNumber: req.PhoneNumber,
|
|
Timestamp: time.Now().Unix(),
|
|
}
|
|
|
|
return utils.SuccessWithData(c, "Message sent successfully", data)
|
|
}
|
|
|
|
func GetDeviceInfoHandler(c *fiber.Ctx) error {
|
|
wa := config.GetWhatsAppService()
|
|
if wa == nil {
|
|
return utils.InternalServerError(c, "WhatsApp service not initialized")
|
|
}
|
|
|
|
if !wa.IsLoggedIn() {
|
|
return utils.Unauthorized(c, "WhatsApp not logged in")
|
|
}
|
|
|
|
var deviceInfo map[string]interface{}
|
|
if wa.Client != nil && wa.Client.Store.ID != nil {
|
|
deviceInfo = map[string]interface{}{
|
|
"device_id": wa.Client.Store.ID.User,
|
|
"device_name": wa.Client.Store.ID.Device,
|
|
"is_logged_in": wa.IsLoggedIn(),
|
|
"is_connected": wa.IsConnected(),
|
|
"timestamp": time.Now().Unix(),
|
|
}
|
|
} else {
|
|
deviceInfo = map[string]interface{}{
|
|
"device_id": nil,
|
|
"device_name": nil,
|
|
"is_logged_in": false,
|
|
"is_connected": false,
|
|
"timestamp": time.Now().Unix(),
|
|
}
|
|
}
|
|
|
|
return utils.SuccessWithData(c, "Device info retrieved successfully", deviceInfo)
|
|
}
|
|
|
|
func HealthCheckHandler(c *fiber.Ctx) error {
|
|
wa := config.GetWhatsAppService()
|
|
if wa == nil {
|
|
return utils.InternalServerError(c, "WhatsApp service not initialized")
|
|
}
|
|
|
|
healthData := map[string]interface{}{
|
|
"service_status": "running",
|
|
"container_status": wa.Container != nil,
|
|
"client_status": wa.Client != nil,
|
|
"is_connected": wa.IsConnected(),
|
|
"is_logged_in": wa.IsLoggedIn(),
|
|
"timestamp": time.Now().Unix(),
|
|
}
|
|
|
|
message := "WhatsApp service is healthy"
|
|
if !wa.IsConnected() || !wa.IsLoggedIn() {
|
|
message = "WhatsApp service is running but not fully operational"
|
|
}
|
|
|
|
return utils.SuccessWithData(c, message, healthData)
|
|
}
|
|
|
|
func validatePhoneNumber(phoneNumber string) error {
|
|
|
|
cleaned := strings.ReplaceAll(phoneNumber, " ", "")
|
|
cleaned = strings.ReplaceAll(cleaned, "-", "")
|
|
cleaned = strings.ReplaceAll(cleaned, "+", "")
|
|
|
|
if !regexp.MustCompile(`^\d+$`).MatchString(cleaned) {
|
|
return fiber.NewError(fiber.StatusBadRequest, "Phone number must contain only digits")
|
|
}
|
|
|
|
if len(cleaned) < 10 {
|
|
return fiber.NewError(fiber.StatusBadRequest, "Phone number too short. Include country code (e.g., 628123456789)")
|
|
}
|
|
|
|
if len(cleaned) > 15 {
|
|
return fiber.NewError(fiber.StatusBadRequest, "Phone number too long")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateMessage(message string) error {
|
|
|
|
if strings.TrimSpace(message) == "" {
|
|
return fiber.NewError(fiber.StatusBadRequest, "Message cannot be empty")
|
|
}
|
|
|
|
if len(message) > 4096 {
|
|
return fiber.NewError(fiber.StatusBadRequest, "Message too long. Maximum 4096 characters allowed")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateSendMessageRequest(c *fiber.Ctx) error {
|
|
var req SendMessageRequest
|
|
if err := c.BodyParser(&req); err != nil {
|
|
return utils.BadRequest(c, "Invalid JSON format: "+err.Error())
|
|
}
|
|
|
|
if err := validatePhoneNumber(req.PhoneNumber); err != nil {
|
|
return utils.BadRequest(c, err.Error())
|
|
}
|
|
|
|
if err := validateMessage(req.Message); err != nil {
|
|
return utils.BadRequest(c, err.Error())
|
|
}
|
|
|
|
req.PhoneNumber = strings.ReplaceAll(req.PhoneNumber, " ", "")
|
|
req.PhoneNumber = strings.ReplaceAll(req.PhoneNumber, "-", "")
|
|
req.PhoneNumber = strings.ReplaceAll(req.PhoneNumber, "+", "")
|
|
|
|
c.Locals("validatedRequest", req)
|
|
|
|
return c.Next()
|
|
}
|
|
|
|
func GetValidatedSendMessageRequest(c *fiber.Ctx) *SendMessageRequest {
|
|
if req, ok := c.Locals("validatedRequest").(SendMessageRequest); ok {
|
|
return &req
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ValidateContentType() fiber.Handler {
|
|
return func(c *fiber.Ctx) error {
|
|
|
|
if c.Method() == "GET" {
|
|
return c.Next()
|
|
}
|
|
|
|
contentType := c.Get("Content-Type")
|
|
if !strings.Contains(contentType, "application/json") {
|
|
return utils.BadRequest(c, "Content-Type must be application/json")
|
|
}
|
|
|
|
return c.Next()
|
|
}
|
|
}
|