mirror of
https://github.com/Neur0toxine/waba-coreapp-mock.git
synced 2024-11-21 20:46:10 +03:00
more configurability
This commit is contained in:
parent
6825c32bcf
commit
b5bd0480b1
4
main.go
4
main.go
@ -1,7 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mkideal/cli"
|
||||
@ -22,6 +24,8 @@ func main() {
|
||||
gin.SetMode(gin.DebugMode)
|
||||
}
|
||||
|
||||
http.DefaultClient.Timeout = time.Second * 30
|
||||
|
||||
return NewServer().Run(argv.Address)
|
||||
}))
|
||||
}
|
||||
|
98
models.go
98
models.go
@ -110,6 +110,104 @@ type Message struct {
|
||||
Interactive *MessageInteractive `json:"interactive,omitempty"`
|
||||
}
|
||||
|
||||
type InboundMessage struct {
|
||||
Message
|
||||
Button *InboundMessageButton `json:"button,omitempty"`
|
||||
Context *InboundMessageContext `json:"context,omitempty"`
|
||||
From string `json:"from,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Identity *InboundMessageIdentity `json:"identity,omitempty"`
|
||||
Timestamp string `json:"timestamp,omitempty"`
|
||||
System *MessageSystem `json:"system,omitempty"`
|
||||
Voice *MessageMedia `json:"voice,omitempty"`
|
||||
Referral *Referral `json:"referral,omitempty"`
|
||||
Errors []InboundError `json:"errors,omitempty"`
|
||||
}
|
||||
|
||||
type InboundWebhook struct {
|
||||
Contacts []InboundContact `json:"contacts,omitempty"`
|
||||
Messages []InboundMessage `json:"messages,omitempty"`
|
||||
Statuses []InboundStatus `json:"statuses,omitempty"`
|
||||
Errors []InboundError `json:"errors,omitempty"`
|
||||
}
|
||||
|
||||
type InboundContact struct {
|
||||
Profile *Profile `json:"profile,omitempty"`
|
||||
WaID string `json:"wa_id,omitempty"`
|
||||
}
|
||||
|
||||
type InboundMessageButton struct {
|
||||
Payload string `json:"payload,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
}
|
||||
|
||||
type InboundMessageIdentity struct {
|
||||
Acknowledged string `json:"acknowledged,omitempty"`
|
||||
CreatedTimestamp string `json:"created_timestamp,omitempty"`
|
||||
Hash string `json:"hash,omitempty"`
|
||||
}
|
||||
|
||||
type InboundMessageContext struct {
|
||||
From string `json:"from,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
GroupID string `json:"group_id,omitempty"`
|
||||
Mentions []string `json:"mentions,omitempty"`
|
||||
Forwarded bool `json:"forwarded,omitempty"`
|
||||
FrequentlyForwarded bool `json:"frequently_forwarded,omitempty"`
|
||||
}
|
||||
|
||||
type InboundStatus struct {
|
||||
Conversation *InboundStatusConversation `json:"conversation,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Pricing *InboundStatusPricing `json:"pricing,omitempty"`
|
||||
RecipientID string `json:"recipient_id,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Timestamp json.Number `json:"timestamp,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type InboundStatusPricing struct {
|
||||
Billable bool `json:"billable,omitempty"`
|
||||
PricingModel string `json:"pricing_model,omitempty"`
|
||||
}
|
||||
|
||||
type InboundStatusConversation struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Origin InboundStatusConversationOrigin `json:"origin,omitempty"`
|
||||
ExpirationTimestamp json.Number `json:"expiration_timestamp,omitempty"`
|
||||
}
|
||||
|
||||
type InboundStatusConversationOrigin struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type InboundError struct {
|
||||
Code int `json:"code,omitempty"`
|
||||
Details string `json:"details,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
}
|
||||
|
||||
type Referral struct {
|
||||
Headline string `json:"headline,omitempty"`
|
||||
Body string `json:"body,omitempty"`
|
||||
SourceType string `json:"source_type,omitempty"`
|
||||
SourceID string `json:"source_id,omitempty"`
|
||||
SourceURL string `json:"source_url,omitempty"`
|
||||
Image *MessageMedia `json:"image,omitempty"`
|
||||
Video *MessageMedia `json:"video,omitempty"`
|
||||
}
|
||||
|
||||
type MessageSystem struct {
|
||||
Body string `json:"body,omitempty"`
|
||||
NewWaID string `json:"new_wa_id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Identity string `json:"identity,omitempty"`
|
||||
}
|
||||
|
||||
type Profile struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
type MessagesResponse struct {
|
||||
BaseResponse
|
||||
Messages []IDModel `json:"messages,omitempty"`
|
||||
|
98
server.go
98
server.go
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
@ -15,17 +16,31 @@ var (
|
||||
)
|
||||
|
||||
type Mock struct {
|
||||
ContactsSuccess bool `json:"contacts_success"`
|
||||
MessagesSuccess bool `json:"messages_success"`
|
||||
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
|
||||
mock Mock
|
||||
g *gin.Engine
|
||||
shooter *Shooter
|
||||
mock Mock
|
||||
}
|
||||
|
||||
func NewServer() (s *Server) {
|
||||
s = &Server{g: gin.New()}
|
||||
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")
|
||||
@ -40,6 +55,15 @@ 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{
|
||||
@ -68,11 +92,34 @@ func (s *Server) mockData(c *gin.Context) {
|
||||
|
||||
func (s *Server) updateMockData(c *gin.Context) {
|
||||
var mock Mock
|
||||
if err := s.bindRequest(c, &mock); err != nil {
|
||||
if err := c.ShouldBindJSON(&mock); err != nil {
|
||||
c.AbortWithStatus(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
s.mock = mock
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@ -106,12 +153,47 @@ func (s *Server) messagesHandler(c *gin.Context) {
|
||||
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: RandomString(27),
|
||||
ID: messageID,
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
84
shooter.go
Normal file
84
shooter.go
Normal file
@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Shooter struct {
|
||||
Webhook string
|
||||
Headers map[string]string
|
||||
}
|
||||
|
||||
func NewShooter(webhook string, headers map[string]string) *Shooter {
|
||||
return &Shooter{
|
||||
Webhook: webhook,
|
||||
Headers: headers,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Shooter) makeRequest(v interface{}) (*http.Request, error) {
|
||||
wh, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, s.Webhook, bytes.NewReader(wh))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for h, v := range s.Headers {
|
||||
req.Header.Set(h, v)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (s *Shooter) SendStatus(status InboundStatus) (int, error) {
|
||||
req, err := s.makeRequest(InboundWebhook{
|
||||
Statuses: []InboundStatus{status},
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return resp.StatusCode, nil
|
||||
}
|
||||
|
||||
func (s *Shooter) SendText(text, from string) (int, error) {
|
||||
req, err := s.makeRequest(InboundWebhook{
|
||||
Contacts: []InboundContact{{
|
||||
Profile: &Profile{
|
||||
Name: from,
|
||||
},
|
||||
WaID: from,
|
||||
}},
|
||||
Messages: []InboundMessage{{
|
||||
Message: Message{
|
||||
Type: "text",
|
||||
Text: &MessageText{
|
||||
Body: text,
|
||||
},
|
||||
},
|
||||
From: from,
|
||||
ID: RandomString(27),
|
||||
}},
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return resp.StatusCode, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user