waba-coreapp-mock/server.go
2022-04-21 16:54:08 +03:00

200 lines
4.4 KiB
Go

package main
import (
"net/http"
"regexp"
"time"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/labstack/gommon/log"
)
var (
validate = validator.New()
NotDigitsRegex = regexp.MustCompile("\\D+")
)
type Mock struct {
ContactsSuccess bool `json:"contacts_success"`
MessagesSuccess bool `json:"messages_success"`
MessagesStatus string `json:"messages_success_status" validate:"oneof=sent read failed"`
Webhook string `json:"webhook" validate:"url,startswith=http"`
WebhookHeaders map[string]string `json:"webhook_headers"`
}
type Server struct {
g *gin.Engine
shooter *Shooter
mock Mock
}
func NewServer() (s *Server) {
s = &Server{
g: gin.New(),
mock: Mock{
ContactsSuccess: true,
MessagesSuccess: true,
MessagesStatus: "sent",
Webhook: "",
WebhookHeaders: map[string]string{},
},
}
s.updateShooter()
s.g.GET("/mock", s.mockData)
s.g.POST("/mock", s.updateMockData)
api := s.g.Group("/v1")
{
api.POST("/contacts", s.contactsHandler)
api.POST("/messages", s.messagesHandler)
}
return s
}
func (s *Server) Run(addr ...string) error {
return s.g.Run(addr...)
}
func (s *Server) updateShooter() {
if s.shooter == nil {
s.shooter = NewShooter(s.mock.Webhook, s.mock.WebhookHeaders)
return
}
s.shooter.Webhook = s.mock.Webhook
s.shooter.Headers = s.mock.WebhookHeaders
}
func (s *Server) baseResponseOk() BaseResponse {
return BaseResponse{
Meta: &Metadata{
Success: true,
APIStatus: "stable",
Version: "v2.31.5",
},
}
}
func (s *Server) bindRequest(c *gin.Context, req interface{}) error {
if err := c.ShouldBindJSON(&req); err != nil {
log.Printf("error: %s\n", err)
return err
}
if err := validate.Struct(req); err != nil {
log.Printf("error: %s\n", err)
return err
}
return nil
}
func (s *Server) mockData(c *gin.Context) {
c.JSON(http.StatusOK, s.mock)
}
func (s *Server) updateMockData(c *gin.Context) {
var mock Mock
if err := c.ShouldBindJSON(&mock); err != nil {
c.AbortWithStatus(http.StatusBadRequest)
return
}
current := s.mock
current.ContactsSuccess = mock.ContactsSuccess
current.MessagesSuccess = mock.MessagesSuccess
if mock.MessagesStatus != "" {
current.MessagesStatus = mock.MessagesStatus
}
if mock.Webhook != "" {
current.Webhook = mock.Webhook
}
if mock.WebhookHeaders != nil {
current.WebhookHeaders = mock.WebhookHeaders
}
if err := validate.Struct(current); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
s.mock = current
s.updateShooter()
c.JSON(http.StatusOK, s.mock)
}
func (s *Server) contactsHandler(c *gin.Context) {
var req ContactsRequest
if err := s.bindRequest(c, &req); err != nil || !s.mock.ContactsSuccess {
c.AbortWithStatus(http.StatusBadRequest)
return
}
res := ContactsResponse{
BaseResponse: s.baseResponseOk(),
Contacts: make([]Contact, len(req.Contacts)),
}
for i, contact := range req.Contacts {
res.Contacts[i] = Contact{
WaID: NotDigitsRegex.ReplaceAllString(contact, ""),
Input: contact,
Status: ContactStatusValid,
}
}
c.JSON(http.StatusOK, res)
}
func (s *Server) messagesHandler(c *gin.Context) {
var req Message
if err := s.bindRequest(c, &req); err != nil || !s.mock.MessagesSuccess {
c.AbortWithStatus(http.StatusBadRequest)
return
}
messageID := RandomString(27)
text := ""
if req.Text != nil {
text = req.Text.Body
}
log.Printf("Received new message: %#v\n", req)
if s.mock.Webhook != "" {
defer func(msgID, text string, to string) {
go func(msgID, text string, to string) {
time.Sleep(time.Millisecond * 500)
code, err := s.shooter.SendStatus(InboundStatus{
Type: "message",
ID: messageID,
RecipientID: to,
Status: s.mock.MessagesStatus,
})
if err != nil {
log.Printf("error: %s\n", err)
return
}
log.Printf("status webhook code: %d\n", code)
if text == "reply" {
code, err := s.shooter.SendText("Replying to the message", to)
if err != nil {
log.Printf("error: %s\n", err)
return
}
log.Printf("reply webhook code: %d\n", code)
}
}(msgID, text, req.To)
}(messageID, text, req.To)
}
c.JSON(http.StatusOK, MessagesResponse{
BaseResponse: s.baseResponseOk(),
Messages: []IDModel{{
ID: messageID,
}},
})
}