From 8761488903ed509e227e62f6180f39de32f6dbdd Mon Sep 17 00:00:00 2001 From: Bruno Henrique Silva Contente Date: Sun, 28 Jun 2026 22:37:00 -0300 Subject: [PATCH 1/2] feat(message): add POST /message/markplayed endpoint (played receipt / blue mic) --- pkg/message/handler/message_handler.go | 51 ++++++++++++++++++++++++++ pkg/message/service/message_service.go | 29 +++++++++++++++ pkg/routes/routes.go | 1 + 3 files changed, 81 insertions(+) diff --git a/pkg/message/handler/message_handler.go b/pkg/message/handler/message_handler.go index c283ccf1..a64c7ca1 100644 --- a/pkg/message/handler/message_handler.go +++ b/pkg/message/handler/message_handler.go @@ -12,6 +12,7 @@ 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) @@ -168,6 +169,56 @@ func (m *messageHandler) MarkRead(ctx *gin.Context) { 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}) +} + // DownloadImage download an image // @Summary Download an image // @Description Download an image diff --git a/pkg/message/service/message_service.go b/pkg/message/service/message_service.go index b2b21c72..932b9ce5 100644 --- a/pkg/message/service/message_service.go +++ b/pkg/message/service/message_service.go @@ -27,6 +27,7 @@ type MessageService interface { React(data *ReactStruct, instance *instance_model.Instance) (*MessageSendStruct, error) ChatPresence(data *ChatPresenceStruct, instance *instance_model.Instance) (string, error) MarkRead(data *MarkReadStruct, instance *instance_model.Instance) (string, error) + MarkPlayed(data *MarkPlayedStruct, instance *instance_model.Instance) (string, error) DownloadMedia(data *DownloadMediaStruct, instance *instance_model.Instance, request *http.Request) (*dataurl.DataURL, string, error) GetMessageStatus(data *MessageStatusStruct, instance *instance_model.Instance) (*message_model.Message, string, error) DeleteMessageEveryone(data *MessageStruct, instance *instance_model.Instance) (string, string, error) @@ -59,6 +60,11 @@ type MarkReadStruct struct { Number string `json:"number"` } +type MarkPlayedStruct struct { + Id []string `json:"id"` + Number string `json:"number"` +} + type DownloadMediaStruct struct { Message *waE2E.Message `json:"message"` } @@ -258,6 +264,29 @@ func (m *messageService) MarkRead(data *MarkReadStruct, instance *instance_model return ts.String(), nil } +func (m *messageService) MarkPlayed(data *MarkPlayedStruct, instance *instance_model.Instance) (string, error) { + client, err := m.ensureClientConnected(instance.Id) + if err != nil { + return "", err + } + + var ts time.Time + + jid, ok := utils.ParseJID(data.Number) + if !ok { + m.loggerWrapper.GetLogger(instance.Id).LogError("[%s] Error validating message fields", instance.Id) + return "", errors.New("invalid phone number") + } + + err = client.MarkRead(context.Background(), data.Id, time.Now(), jid, jid, types.ReceiptTypePlayed) + if err != nil { + m.loggerWrapper.GetLogger(instance.Id).LogError("[%s] error marking message as played: %v", instance.Id, err) + return "", errors.New("error marking message as played") + } + + return ts.String(), nil +} + func (m *messageService) DownloadMedia(data *DownloadMediaStruct, instance *instance_model.Instance, request *http.Request) (*dataurl.DataURL, string, error) { client, err := m.ensureClientConnected(instance.Id) if err != nil { diff --git a/pkg/routes/routes.go b/pkg/routes/routes.go index f51e7d96..3c6c4743 100644 --- a/pkg/routes/routes.go +++ b/pkg/routes/routes.go @@ -151,6 +151,7 @@ func (r *Routes) AssignRoutes(eng *gin.Engine) { routes.POST("/react", r.jidValidationMiddleware.ValidateJIDFields("number"), r.messageHandler.React) routes.POST("/presence", r.jidValidationMiddleware.ValidateNumberField(), r.messageHandler.ChatPresence) routes.POST("/markread", r.jidValidationMiddleware.ValidateNumberField(), r.messageHandler.MarkRead) + routes.POST("/markplayed", r.jidValidationMiddleware.ValidateNumberField(), r.messageHandler.MarkPlayed) routes.POST("/downloadmedia", r.messageHandler.DownloadMedia) routes.POST("/status", r.messageHandler.GetMessageStatus) routes.POST("/delete", r.jidValidationMiddleware.ValidateNumberField(), r.messageHandler.DeleteMessageEveryone) From c62d5890523a69f6cc3bce8a4d4060ef499a437d Mon Sep 17 00:00:00 2001 From: Bruno Henrique Silva Contente Date: Tue, 30 Jun 2026 19:50:35 -0300 Subject: [PATCH 2/2] docs(swagger): regenerate for POST /message/markplayed --- docs/docs.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++ docs/swagger.json | 60 +++++++++++++++++++++++++++++++++++++++++++++++ docs/swagger.yaml | 39 ++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) diff --git a/docs/docs.go b/docs/docs.go index c089c1f9..153d2e44 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1944,6 +1944,52 @@ const docTemplate = `{ } } }, + "/message/markplayed": { + "post": { + "description": "Mark an audio message as played", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Message" + ], + "summary": "Mark an audio message as played", + "parameters": [ + { + "description": "Mark an audio message as played", + "name": "message", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_EvolutionAPI_evolution-go_pkg_message_service.MarkPlayedStruct" + } + } + ], + "responses": { + "200": { + "description": "success", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Error on validation", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, "/message/markread": { "post": { "description": "Mark a message as read", @@ -3849,6 +3895,20 @@ const docTemplate = `{ } } }, + "github_com_EvolutionAPI_evolution-go_pkg_message_service.MarkPlayedStruct": { + "type": "object", + "properties": { + "id": { + "type": "array", + "items": { + "type": "string" + } + }, + "number": { + "type": "string" + } + } + }, "github_com_EvolutionAPI_evolution-go_pkg_message_service.MarkReadStruct": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 72a293c9..4b547bec 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1936,6 +1936,52 @@ } } }, + "/message/markplayed": { + "post": { + "description": "Mark an audio message as played", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Message" + ], + "summary": "Mark an audio message as played", + "parameters": [ + { + "description": "Mark an audio message as played", + "name": "message", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_EvolutionAPI_evolution-go_pkg_message_service.MarkPlayedStruct" + } + } + ], + "responses": { + "200": { + "description": "success", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Error on validation", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, "/message/markread": { "post": { "description": "Mark a message as read", @@ -3841,6 +3887,20 @@ } } }, + "github_com_EvolutionAPI_evolution-go_pkg_message_service.MarkPlayedStruct": { + "type": "object", + "properties": { + "id": { + "type": "array", + "items": { + "type": "string" + } + }, + "number": { + "type": "string" + } + } + }, "github_com_EvolutionAPI_evolution-go_pkg_message_service.MarkReadStruct": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index f99331ae..fc2f3aaf 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -234,6 +234,15 @@ definitions: messageId: type: string type: object + github_com_EvolutionAPI_evolution-go_pkg_message_service.MarkPlayedStruct: + properties: + id: + items: + type: string + type: array + number: + type: string + type: object github_com_EvolutionAPI_evolution-go_pkg_message_service.MarkReadStruct: properties: id: @@ -7870,6 +7879,36 @@ paths: summary: Edit a message tags: - Message + /message/markplayed: + post: + consumes: + - application/json + description: Mark an audio message as played + parameters: + - description: Mark an audio message as played + in: body + name: message + required: true + schema: + $ref: '#/definitions/github_com_EvolutionAPI_evolution-go_pkg_message_service.MarkPlayedStruct' + produces: + - application/json + responses: + "200": + description: success + schema: + $ref: '#/definitions/gin.H' + "400": + description: Error on validation + schema: + $ref: '#/definitions/gin.H' + "500": + description: Internal server error + schema: + $ref: '#/definitions/gin.H' + summary: Mark an audio message as played + tags: + - Message /message/markread: post: consumes: