Spaces:
Running
Running
restructure repo into production layout; add whatsapp-service, tests, ddl, docs, scripts; scrub hardcoded secrets
83d6851 | package message_handler | |
| import ( | |
| "net/http" | |
| instance_model "agentdeck-whatsapp-service/pkg/instance/model" | |
| message_service "agentdeck-whatsapp-service/pkg/message/service" | |
| "github.com/gin-gonic/gin" | |
| ) | |
| type MessageHandler interface { | |
| React(ctx *gin.Context) | |
| ChatPresence(ctx *gin.Context) | |
| MarkRead(ctx *gin.Context) | |
| MarkPlayed(ctx *gin.Context) | |
| DownloadMedia(ctx *gin.Context) | |
| GetMessageStatus(ctx *gin.Context) | |
| DeleteMessageEveryone(ctx *gin.Context) | |
| EditMessage(ctx *gin.Context) | |
| } | |
| type messageHandler struct { | |
| messageService message_service.MessageService | |
| } | |
| // React a message | |
| // @Summary React a message | |
| // @Description React to a message with support for fromMe field and participant field for group messages | |
| // @Tags Message | |
| // @Accept json | |
| // @Produce json | |
| // @Param message body message_service.ReactStruct true "React to a message with fromMe and participant fields" | |
| // @Success 200 {object} gin.H "success" | |
| // @Failure 400 {object} gin.H "Error on validation" | |
| // @Failure 500 {object} gin.H "Internal server error" | |
| // @Router /message/react [post] | |
| func (m *messageHandler) React(ctx *gin.Context) { | |
| getInstance := ctx.MustGet("instance") | |
| instance, ok := getInstance.(*instance_model.Instance) | |
| if !ok { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": "instance not found"}) | |
| return | |
| } | |
| var data *message_service.ReactStruct | |
| err := ctx.ShouldBindBodyWithJSON(&data) | |
| if err != nil { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| if data.Number == "" { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "phone number is required"}) | |
| return | |
| } | |
| if data.Reaction == "" { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "message reaction is required"}) | |
| return | |
| } | |
| message, err := m.messageService.React(data, instance) | |
| if err != nil { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| ctx.JSON(http.StatusOK, gin.H{"message": "success", "data": message}) | |
| } | |
| // ChatPresence set chat presence | |
| // @Summary Set chat presence | |
| // @Description Set chat presence | |
| // @Tags Message | |
| // @Accept json | |
| // @Produce json | |
| // @Param message body message_service.ChatPresenceStruct true "Set chat presence" | |
| // @Success 200 {object} gin.H "success" | |
| // @Failure 400 {object} gin.H "Error on validation" | |
| // @Failure 500 {object} gin.H "Internal server error" | |
| // @Router /message/presence [post] | |
| func (m *messageHandler) ChatPresence(ctx *gin.Context) { | |
| getInstance := ctx.MustGet("instance") | |
| instance, ok := getInstance.(*instance_model.Instance) | |
| if !ok { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": "instance not found"}) | |
| return | |
| } | |
| var data *message_service.ChatPresenceStruct | |
| err := ctx.ShouldBindBodyWithJSON(&data) | |
| if err != nil { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| if data.Number == "" { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "phone number is required"}) | |
| return | |
| } | |
| if data.State == "" { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "state is required"}) | |
| return | |
| } | |
| ts, err := m.messageService.ChatPresence(data, instance) | |
| if err != nil { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| responseData := gin.H{ | |
| "timestamp": ts, | |
| } | |
| ctx.JSON(http.StatusOK, gin.H{"message": "success", "data": responseData}) | |
| } | |
| // MarkRead mark a message as read | |
| // @Summary Mark a message as read | |
| // @Description Mark a message as read | |
| // @Tags Message | |
| // @Accept json | |
| // @Produce json | |
| // @Param message body message_service.MarkReadStruct true "Mark a message as read" | |
| // @Success 200 {object} gin.H "success" | |
| // @Failure 400 {object} gin.H "Error on validation" | |
| // @Failure 500 {object} gin.H "Internal server error" | |
| // @Router /message/markread [post] | |
| func (m *messageHandler) MarkRead(ctx *gin.Context) { | |
| getInstance := ctx.MustGet("instance") | |
| instance, ok := getInstance.(*instance_model.Instance) | |
| if !ok { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": "instance not found"}) | |
| return | |
| } | |
| var data *message_service.MarkReadStruct | |
| err := ctx.ShouldBindBodyWithJSON(&data) | |
| if err != nil { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| if data.Number == "" { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "phone number is required"}) | |
| return | |
| } | |
| if len(data.Id) < 1 { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "id is required"}) | |
| return | |
| } | |
| ts, err := m.messageService.MarkRead(data, instance) | |
| if err != nil { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| responseData := gin.H{ | |
| "timestamp": ts, | |
| } | |
| ctx.JSON(http.StatusOK, gin.H{"message": "success", "data": responseData}) | |
| } | |
| // MarkPlayed mark an audio message as played (blue mic icon) | |
| // @Summary Mark an audio message as played | |
| // @Description Mark an audio message as played | |
| // @Tags Message | |
| // @Accept json | |
| // @Produce json | |
| // @Param message body message_service.MarkPlayedStruct true "Mark an audio message as played" | |
| // @Success 200 {object} gin.H "success" | |
| // @Failure 400 {object} gin.H "Error on validation" | |
| // @Failure 500 {object} gin.H "Internal server error" | |
| // @Router /message/markplayed [post] | |
| func (m *messageHandler) MarkPlayed(ctx *gin.Context) { | |
| getInstance := ctx.MustGet("instance") | |
| instance, ok := getInstance.(*instance_model.Instance) | |
| if !ok { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": "instance not found"}) | |
| return | |
| } | |
| var data *message_service.MarkPlayedStruct | |
| err := ctx.ShouldBindBodyWithJSON(&data) | |
| if err != nil { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| if data.Number == "" { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "phone number is required"}) | |
| return | |
| } | |
| if len(data.Id) < 1 { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "id is required"}) | |
| return | |
| } | |
| ts, err := m.messageService.MarkPlayed(data, instance) | |
| if err != nil { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| responseData := gin.H{ | |
| "timestamp": ts, | |
| } | |
| ctx.JSON(http.StatusOK, gin.H{"message": "success", "data": responseData}) | |
| } | |
| // DownloadMedia download a media message (image, video, audio, document) | |
| // @Summary Download media | |
| // @Description Download the media content of a message (image, video, audio or document) | |
| // @Tags Message | |
| // @Accept json | |
| // @Produce json | |
| // @Param message body message_service.DownloadMediaStruct true "Download media" | |
| // @Success 200 {object} gin.H "success" | |
| // @Failure 400 {object} gin.H "Error on validation" | |
| // @Failure 500 {object} gin.H "Internal server error" | |
| // @Router /message/downloadmedia [post] | |
| func (m *messageHandler) DownloadMedia(ctx *gin.Context) { | |
| getInstance := ctx.MustGet("instance") | |
| instance, ok := getInstance.(*instance_model.Instance) | |
| if !ok { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": "instance not found"}) | |
| return | |
| } | |
| var data *message_service.DownloadMediaStruct | |
| err := ctx.ShouldBindBodyWithJSON(&data) | |
| if err != nil { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| dataUrl, ts, err := m.messageService.DownloadMedia(data, instance, ctx.Request) | |
| if err != nil { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| responseData := gin.H{ | |
| "base64": dataUrl.String(), | |
| "timestamp": ts, | |
| } | |
| ctx.JSON(http.StatusOK, gin.H{"message": "success", "data": responseData}) | |
| } | |
| // GetMessageStatus get message status | |
| // @Summary Get message status | |
| // @Description Get message status | |
| // @Tags Message | |
| // @Accept json | |
| // @Produce json | |
| // @Param message body message_service.MessageStatusStruct true "Get message status" | |
| // @Success 200 {object} gin.H "success" | |
| // @Failure 400 {object} gin.H "Error on validation" | |
| // @Failure 500 {object} gin.H "Internal server error" | |
| // @Router /message/status [post] | |
| func (m *messageHandler) GetMessageStatus(ctx *gin.Context) { | |
| getInstance := ctx.MustGet("instance") | |
| instance, ok := getInstance.(*instance_model.Instance) | |
| if !ok { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": "instance not found"}) | |
| return | |
| } | |
| var data *message_service.MessageStatusStruct | |
| err := ctx.ShouldBindBodyWithJSON(&data) | |
| if err != nil { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| if data.Id == "" { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "id is required"}) | |
| return | |
| } | |
| message, ts, err := m.messageService.GetMessageStatus(data, instance) | |
| if err != nil { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| responseData := gin.H{ | |
| "result": message, | |
| "timestamp": ts, | |
| } | |
| ctx.JSON(http.StatusOK, gin.H{"message": "success", "data": responseData}) | |
| } | |
| // DeleteMessageEveryone delete a message for everyone | |
| // @Summary Delete a message for everyone | |
| // @Description Delete a message for everyone | |
| // @Tags Message | |
| // @Accept json | |
| // @Produce json | |
| // @Param message body message_service.MessageStruct true "Delete a message for everyone" | |
| // @Success 200 {object} gin.H "success" | |
| // @Failure 400 {object} gin.H "Error on validation" | |
| // @Failure 500 {object} gin.H "Internal server error" | |
| // @Router /message/delete [post] | |
| func (m *messageHandler) DeleteMessageEveryone(ctx *gin.Context) { | |
| getInstance := ctx.MustGet("instance") | |
| instance, ok := getInstance.(*instance_model.Instance) | |
| if !ok { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": "instance not found"}) | |
| return | |
| } | |
| var data *message_service.MessageStruct | |
| err := ctx.ShouldBindBodyWithJSON(&data) | |
| if err != nil { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| if data.Chat == "" { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "chat is required"}) | |
| return | |
| } | |
| if data.MessageID == "" { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "messageId is required"}) | |
| return | |
| } | |
| msgId, ts, err := m.messageService.DeleteMessageEveryone(data, instance) | |
| if err != nil { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| responseData := gin.H{ | |
| "messageId": msgId, | |
| "timestamp": ts, | |
| } | |
| ctx.JSON(http.StatusOK, gin.H{"message": "success", "data": responseData}) | |
| } | |
| // EditMessage edit a message | |
| // @Summary Edit a message | |
| // @Description Edit a message | |
| // @Tags Message | |
| // @Accept json | |
| // @Produce json | |
| // @Param message body message_service.EditMessageStruct true "Edit a message" | |
| // @Success 200 {object} gin.H "success" | |
| // @Failure 400 {object} gin.H "Error on validation" | |
| // @Failure 500 {object} gin.H "Internal server error" | |
| // @Router /message/edit [post] | |
| func (m *messageHandler) EditMessage(ctx *gin.Context) { | |
| getInstance := ctx.MustGet("instance") | |
| instance, ok := getInstance.(*instance_model.Instance) | |
| if !ok { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": "instance not found"}) | |
| return | |
| } | |
| var data *message_service.EditMessageStruct | |
| err := ctx.ShouldBindBodyWithJSON(&data) | |
| if err != nil { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| if data.Chat == "" { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "chat is required"}) | |
| return | |
| } | |
| if data.Message == "" { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "message is required"}) | |
| return | |
| } | |
| if data.MessageID == "" { | |
| ctx.JSON(http.StatusBadRequest, gin.H{"error": "messageId is required"}) | |
| return | |
| } | |
| msgId, ts, err := m.messageService.EditMessage(data, instance) | |
| if err != nil { | |
| ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| responseData := gin.H{ | |
| "messageId": msgId, | |
| "timestamp": ts, | |
| } | |
| ctx.JSON(http.StatusOK, gin.H{"message": "success", "data": responseData}) | |
| } | |
| func NewMessageHandler( | |
| messageService message_service.MessageService, | |
| ) MessageHandler { | |
| return &messageHandler{ | |
| messageService: messageService, | |
| } | |
| } | |