From 83fce046b80f2789d8be0af576837ceddb790f76 Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Wed, 17 Jun 2026 14:22:58 +0700 Subject: [PATCH 1/3] [messages] cancellation support --- go.mod | 2 +- go.sum | 2 + .../sms-gateway/handlers/messages/3rdparty.go | 30 ++++++++ .../handlers/messages/permissions.go | 2 + ...0617000000_add_cancelled_message_state.sql | 69 +++++++++++++++++ internal/sms-gateway/modules/events/events.go | 6 ++ .../sms-gateway/modules/messages/errors.go | 1 + .../sms-gateway/modules/messages/models.go | 18 +++-- .../modules/messages/repository.go | 31 +++++++- .../sms-gateway/modules/messages/service.go | 30 ++++++++ internal/sms-gateway/openapi/docs.go | 77 +++++++++++++++++++ 11 files changed, 257 insertions(+), 11 deletions(-) create mode 100644 internal/sms-gateway/models/migrations/mysql/20260617000000_add_cancelled_message_state.sql diff --git a/go.mod b/go.mod index fefc0183..79fc71e4 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.25.8 require ( firebase.google.com/go/v4 v4.20.0 - github.com/android-sms-gateway/client-go v1.13.0 + github.com/android-sms-gateway/client-go v1.13.1-0.20260617071248-b35dcdbad7ff github.com/ansrivas/fiberprometheus/v2 v2.17.0 github.com/capcom6/go-helpers v0.4.0 github.com/capcom6/go-infra-fx v0.5.7 diff --git a/go.sum b/go.sum index e8fb4270..4c91e4bf 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,8 @@ github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= github.com/android-sms-gateway/client-go v1.13.0 h1:EvKDi796R2ScCAiaekWYRekVjkjiJGK3d/LZcPKpfQQ= github.com/android-sms-gateway/client-go v1.13.0/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4= +github.com/android-sms-gateway/client-go v1.13.1-0.20260617071248-b35dcdbad7ff h1:guDM9z07Kbv0H2z8YlWtH5tAqkHIyT7qPbTHlCr7/yA= +github.com/android-sms-gateway/client-go v1.13.1-0.20260617071248-b35dcdbad7ff/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4= github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro= github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/ansrivas/fiberprometheus/v2 v2.17.0 h1:p0gqs5LsSCWGoSFF44fCJkyU+XcE6TLRqEMu80b2iCo= diff --git a/internal/sms-gateway/handlers/messages/3rdparty.go b/internal/sms-gateway/handlers/messages/3rdparty.go index ef23ecab..8f24968d 100644 --- a/internal/sms-gateway/handlers/messages/3rdparty.go +++ b/internal/sms-gateway/handlers/messages/3rdparty.go @@ -246,6 +246,35 @@ func (h *ThirdPartyController) get(userID string, c *fiber.Ctx) error { return c.JSON(converters.MessageStateToDTO(*state)) } +// @Summary Cancel message +// @Description Cancels a pending message by ID. The message must be in Pending state. +// @Security ApiAuth +// @Security JWTAuth +// @Tags User, Messages +// @Param id path string true "Message ID" +// @Success 200 {object} smsgateway.GetMessageResponse "Message state after cancellation" +// @Failure 400 {object} smsgateway.ErrorResponse "Invalid request" +// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized" +// @Failure 403 {object} smsgateway.ErrorResponse "Forbidden" +// @Failure 404 {object} smsgateway.ErrorResponse "Message not found" +// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error" +// @Router /3rdparty/v1/messages/{id} [delete] +// +// Cancel message. +func (h *ThirdPartyController) delete(userID string, c *fiber.Ctx) error { + id := c.Params("id") + + state, err := h.messagesSvc.CancelMessage(userID, id) + if err != nil { + if errors.Is(err, messages.ErrMessageNotPending) || errors.Is(err, messages.ErrMessageNotFound) { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + return fmt.Errorf("failed to cancel message: %w", err) + } + + return c.JSON(converters.MessageStateToDTO(*state)) +} + // Export inbox. // // Deprecated: use /3rdparty/v1/inbox/refresh instead. @@ -319,6 +348,7 @@ func (h *ThirdPartyController) Register(router fiber.Router) { router.Get("", permissions.RequireScope(ScopeList), userauth.WithUserID(h.list)) router.Post("", permissions.RequireScope(ScopeSend), userauth.WithUserID(h.post)) router.Get(":id", permissions.RequireScope(ScopeRead), userauth.WithUserID(h.get)).Name(route3rdPartyGetMessage) + router.Delete(":id", permissions.RequireScope(ScopeCancel), userauth.WithUserID(h.delete)) router.Post("inbox/export", permissions.RequireScope(ScopeExport), userauth.WithUserID(h.postInboxExport)) } diff --git a/internal/sms-gateway/handlers/messages/permissions.go b/internal/sms-gateway/handlers/messages/permissions.go index f793c124..f014dad5 100644 --- a/internal/sms-gateway/handlers/messages/permissions.go +++ b/internal/sms-gateway/handlers/messages/permissions.go @@ -10,6 +10,8 @@ const ( ScopeRead = smsgateway.ScopeMessagesRead // ScopeList is the permission scope required for listing messages. ScopeList = smsgateway.ScopeMessagesList + // ScopeCancel is the permission scope required for cancelling messages. + ScopeCancel = smsgateway.ScopeMessagesCancel // ScopeExport is the permission scope required for exporting messages. ScopeExport = smsgateway.ScopeMessagesExport ) diff --git a/internal/sms-gateway/models/migrations/mysql/20260617000000_add_cancelled_message_state.sql b/internal/sms-gateway/models/migrations/mysql/20260617000000_add_cancelled_message_state.sql new file mode 100644 index 00000000..ede66ab4 --- /dev/null +++ b/internal/sms-gateway/models/migrations/mysql/20260617000000_add_cancelled_message_state.sql @@ -0,0 +1,69 @@ +-- +goose Up +-- +goose StatementBegin +ALTER TABLE `messages` +MODIFY COLUMN `state` enum( + 'Pending', + 'Cancelling', + 'Cancelled', + 'Processed', + 'Sent', + 'Delivered', + 'Failed' + ) NOT NULL DEFAULT 'Pending'; +-- +goose StatementEnd +-- +goose StatementBegin +ALTER TABLE `message_recipients` +MODIFY COLUMN `state` enum( + 'Pending', + 'Cancelling', + 'Cancelled', + 'Processed', + 'Sent', + 'Delivered', + 'Failed' + ) NOT NULL DEFAULT 'Pending'; +-- +goose StatementEnd +-- +goose StatementBegin +ALTER TABLE `message_states` +MODIFY COLUMN `state` enum( + 'Pending', + 'Cancelling', + 'Cancelled', + 'Processed', + 'Sent', + 'Delivered', + 'Failed' + ) NOT NULL; +-- +goose StatementEnd +--- +-- +goose Down +-- +goose StatementBegin +ALTER TABLE `messages` +MODIFY COLUMN `state` enum( + 'Pending', + 'Processed', + 'Sent', + 'Delivered', + 'Failed' + ) NOT NULL DEFAULT 'Pending'; +-- +goose StatementEnd +-- +goose StatementBegin +ALTER TABLE `message_recipients` +MODIFY COLUMN `state` enum( + 'Pending', + 'Processed', + 'Sent', + 'Delivered', + 'Failed' + ) NOT NULL DEFAULT 'Pending'; +-- +goose StatementEnd +-- +goose StatementBegin +ALTER TABLE `message_states` +MODIFY COLUMN `state` enum( + 'Pending', + 'Processed', + 'Sent', + 'Delivered', + 'Failed' + ) NOT NULL; +-- +goose StatementEnd \ No newline at end of file diff --git a/internal/sms-gateway/modules/events/events.go b/internal/sms-gateway/modules/events/events.go index a54b09ac..7ad93d24 100644 --- a/internal/sms-gateway/modules/events/events.go +++ b/internal/sms-gateway/modules/events/events.go @@ -42,6 +42,12 @@ func NewMessagesExportRequestedEvent( ) } +func NewMessageCancelledEvent(messageID string) Event { + return NewEvent(smsgateway.PushMessageCancelled, map[string]string{ + "messageId": messageID, + }) +} + func NewSettingsUpdatedEvent() Event { return NewEvent(smsgateway.PushSettingsUpdated, nil) } diff --git a/internal/sms-gateway/modules/messages/errors.go b/internal/sms-gateway/modules/messages/errors.go index d118766b..2871e930 100644 --- a/internal/sms-gateway/modules/messages/errors.go +++ b/internal/sms-gateway/modules/messages/errors.go @@ -7,6 +7,7 @@ var ( ErrMessageNotFound = errors.New("message not found") ErrMultipleMessagesFound = errors.New("multiple messages found") ErrNoContent = errors.New("no text or data content") + ErrMessageNotPending = errors.New("message is not pending") ErrQueueLimitExceeded = errors.New("queue limits exceeded") ) diff --git a/internal/sms-gateway/modules/messages/models.go b/internal/sms-gateway/modules/messages/models.go index 37f51738..f4151f00 100644 --- a/internal/sms-gateway/modules/messages/models.go +++ b/internal/sms-gateway/modules/messages/models.go @@ -16,11 +16,13 @@ type ProcessingState string type MessageType string const ( - ProcessingStatePending ProcessingState = "Pending" - ProcessingStateProcessed ProcessingState = "Processed" - ProcessingStateSent ProcessingState = "Sent" - ProcessingStateDelivered ProcessingState = "Delivered" - ProcessingStateFailed ProcessingState = "Failed" + ProcessingStatePending ProcessingState = "Pending" + ProcessingStateCancelling ProcessingState = "Cancelling" + ProcessingStateCancelled ProcessingState = "Cancelled" + ProcessingStateProcessed ProcessingState = "Processed" + ProcessingStateSent ProcessingState = "Sent" + ProcessingStateDelivered ProcessingState = "Delivered" + ProcessingStateFailed ProcessingState = "Failed" MessageTypeText MessageType = "Text" MessageTypeData MessageType = "Data" @@ -34,7 +36,7 @@ type messageModel struct { ExtID string `gorm:"not null;type:varchar(36);uniqueIndex:unq_messages_id_device,priority:1"` Type MessageType `gorm:"not null;type:enum('Text','Data');default:Text"` Content string `gorm:"not null;type:text"` - State ProcessingState `gorm:"not null;type:enum('Pending','Sent','Processed','Delivered','Failed');default:Pending;index:idx_messages_device_state"` + State ProcessingState `gorm:"not null;type:enum('Pending','Cancelling','Cancelled','Sent','Processed','Delivered','Failed');default:Pending;index:idx_messages_device_state"` ValidUntil *time.Time `gorm:"type:datetime"` ScheduleAt *time.Time `gorm:"type:datetime"` SimNumber *uint8 `gorm:"type:tinyint(1) unsigned"` @@ -197,7 +199,7 @@ type messageRecipientModel struct { ID uint64 `gorm:"primaryKey;type:BIGINT UNSIGNED;autoIncrement"` MessageID uint64 `gorm:"uniqueIndex:unq_message_recipients_message_id_phone_number,priority:1;type:BIGINT UNSIGNED"` PhoneNumber string `gorm:"uniqueIndex:unq_message_recipients_message_id_phone_number,priority:2;type:varchar(128)"` - State ProcessingState `gorm:"not null;type:enum('Pending','Sent','Processed','Delivered','Failed');default:Pending"` + State ProcessingState `gorm:"not null;type:enum('Pending','Cancelling','Cancelled','Sent','Processed','Delivered','Failed');default:Pending"` Error *string `gorm:"type:varchar(256)"` } @@ -226,7 +228,7 @@ func (m *messageRecipientModel) toDomain() smsgateway.RecipientState { type messageStateModel struct { ID uint64 `gorm:"primaryKey;type:BIGINT UNSIGNED;autoIncrement"` MessageID uint64 `gorm:"not null;type:BIGINT UNSIGNED;uniqueIndex:unq_message_states_message_id_state,priority:1"` - State ProcessingState `gorm:"not null;type:enum('Pending','Sent','Processed','Delivered','Failed');uniqueIndex:unq_message_states_message_id_state,priority:2"` + State ProcessingState `gorm:"not null;type:enum('Pending','Cancelling','Cancelled','Sent','Processed','Delivered','Failed');uniqueIndex:unq_message_states_message_id_state,priority:2"` UpdatedAt time.Time `gorm:"<-:create;not null;autoupdatetime:false"` } diff --git a/internal/sms-gateway/modules/messages/repository.go b/internal/sms-gateway/modules/messages/repository.go index 81ccd1c5..b9f7ab83 100644 --- a/internal/sms-gateway/modules/messages/repository.go +++ b/internal/sms-gateway/modules/messages/repository.go @@ -177,7 +177,7 @@ func (r *Repository) UpdateState(message *messageModel) error { func (r *Repository) HashProcessed(ctx context.Context, ids []uint64) (int64, error) { rawSQL := "UPDATE `messages` `m`, `message_recipients` `r`\n" + "SET `m`.`is_hashed` = true, `m`.`content` = SHA2(COALESCE(JSON_VALUE(`content`, '$.text'), JSON_VALUE(`content`, '$.data')), 256), `r`.`phone_number` = LEFT(SHA2(phone_number, 256), 16)\n" + - "WHERE `m`.`id` = `r`.`message_id` AND `m`.`is_hashed` = false AND `m`.`is_encrypted` = false AND `m`.`state` <> 'Pending'" + "WHERE `m`.`id` = `r`.`message_id` AND `m`.`is_hashed` = false AND `m`.`is_encrypted` = false AND `m`.`state` NOT IN ('Pending', 'Cancelling')" params := []any{} if len(ids) > 0 { rawSQL += " AND `m`.`id` IN (?)" @@ -193,10 +193,37 @@ func (r *Repository) HashProcessed(ctx context.Context, ids []uint64) (int64, er return res.RowsAffected, nil } +func (r *Repository) CancelMessage(userID string, id string) error { + res := r.db.Model((*messageModel)(nil)). + Joins("JOIN devices ON messages.device_id = devices.id"). + Where("messages.ext_id = ? AND devices.user_id = ? AND messages.state = ?", id, userID, ProcessingStatePending). + Update("messages.state", ProcessingStateCancelling) + if res.Error != nil { + return fmt.Errorf("failed to cancel message: %w", res.Error) + } + if res.RowsAffected == 0 { + return ErrMessageNotPending + } + return nil +} + +func (r *Repository) ConfirmCancel(id string, deviceID string) error { + res := r.db.Model((*messageModel)(nil)). + Where("ext_id = ? AND device_id = ? AND state = ?", id, deviceID, ProcessingStateCancelling). + Update("state", ProcessingStateCancelled) + if res.Error != nil { + return fmt.Errorf("failed to confirm cancel: %w", res.Error) + } + if res.RowsAffected == 0 { + return ErrMessageNotFound + } + return nil +} + func (r *Repository) Cleanup(ctx context.Context, until time.Time) (int64, error) { res := r.db. WithContext(ctx). - Where("state <> ?", ProcessingStatePending). + Where("state NOT IN ?", []ProcessingState{ProcessingStatePending, ProcessingStateCancelling}). Where("created_at < ?", until). Delete(new(messageModel)) return res.RowsAffected, res.Error diff --git a/internal/sms-gateway/modules/messages/service.go b/internal/sms-gateway/modules/messages/service.go index d7007816..437c45b0 100644 --- a/internal/sms-gateway/modules/messages/service.go +++ b/internal/sms-gateway/modules/messages/service.go @@ -204,6 +204,36 @@ func (s *Service) GetState(userID string, id string) (*MessageState, error) { return state, nil } +// CancelMessage transitions a pending message to Cancelling state. +// Returns an error if the message is not in Pending state. +func (s *Service) CancelMessage(userID string, id string) (*MessageState, error) { + message, err := s.messages.get( + *new(SelectFilter).WithExtID(id).WithUserID(userID), + *new(SelectOptions).IncludeDevice(), + ) + if err != nil { + return nil, err + } + + if cancelErr := s.messages.CancelMessage(userID, id); cancelErr != nil { + return nil, cancelErr + } + + // Notify device about cancellation + go func(userID, deviceID, messageID string) { + if ntfErr := s.eventsSvc.Notify(userID, &deviceID, events.NewMessageCancelledEvent(messageID)); ntfErr != nil { + s.logger.Error( + "failed to notify device about cancellation", + zap.Error(ntfErr), + zap.String("user_id", userID), + zap.String("device_id", deviceID), + ) + } + }(userID, message.DeviceID, id) + + return s.GetState(userID, id) +} + func (s *Service) Enqueue( ctx context.Context, device devices.Device, diff --git a/internal/sms-gateway/openapi/docs.go b/internal/sms-gateway/openapi/docs.go index 858214bd..944c9f8d 100644 --- a/internal/sms-gateway/openapi/docs.go +++ b/internal/sms-gateway/openapi/docs.go @@ -875,6 +875,69 @@ const docTemplate = `{ } } } + }, + "delete": { + "security": [ + { + "ApiAuth": [] + }, + { + "JWTAuth": [] + } + ], + "description": "Cancels a pending message by ID. The message must be in Pending state.", + "tags": [ + "User", + "Messages" + ], + "summary": "Cancel message", + "parameters": [ + { + "type": "string", + "description": "Message ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Message state after cancellation", + "schema": { + "$ref": "#/definitions/smsgateway.GetMessageResponse" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/smsgateway.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/smsgateway.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/smsgateway.ErrorResponse" + } + }, + "404": { + "description": "Message not found", + "schema": { + "$ref": "#/definitions/smsgateway.ErrorResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/smsgateway.ErrorResponse" + } + } + } } }, "/3rdparty/v1/settings": { @@ -1731,6 +1794,7 @@ const docTemplate = `{ "inbox:list", "inbox:refresh", "logs:read", + "messages:cancel", "messages:send", "messages:read", "messages:list", @@ -1748,6 +1812,7 @@ const docTemplate = `{ "ScopeInboxList", "ScopeInboxRefresh", "ScopeLogsRead", + "ScopeMessagesCancel", "ScopeMessagesSend", "ScopeMessagesRead", "ScopeMessagesList", @@ -2047,12 +2112,16 @@ const docTemplate = `{ "type": "string", "enum": [ "Pending", + "Cancelling", + "Cancelled", "Processed", "Sent", "Delivered", "Failed" ], "x-enum-comments": { + "ProcessingStateCancelled": "Cancelled", + "ProcessingStateCancelling": "Cancelling (cancellation requested)", "ProcessingStateDelivered": "Delivered", "ProcessingStateFailed": "Failed", "ProcessingStatePending": "Pending", @@ -2061,6 +2130,8 @@ const docTemplate = `{ }, "x-enum-descriptions": [ "Pending", + "Cancelling (cancellation requested)", + "Cancelled", "Processed (received by device)", "Sent", "Delivered", @@ -2068,6 +2139,8 @@ const docTemplate = `{ ], "x-enum-varnames": [ "ProcessingStatePending", + "ProcessingStateCancelling", + "ProcessingStateCancelled", "ProcessingStateProcessed", "ProcessingStateSent", "ProcessingStateDelivered", @@ -2378,6 +2451,7 @@ const docTemplate = `{ "sms:sent", "sms:delivered", "sms:failed", + "sms:cancelled", "system:ping", "mms:received", "mms:downloaded" @@ -2385,6 +2459,7 @@ const docTemplate = `{ "x-enum-comments": { "WebhookEventMmsDownloaded": "Triggered when an MMS is downloaded.", "WebhookEventMmsReceived": "Triggered when an MMS is received.", + "WebhookEventSmsCancelled": "Triggered when an SMS is cancelled.", "WebhookEventSmsDataReceived": "Triggered when a data SMS is received.", "WebhookEventSmsDelivered": "Triggered when an SMS is delivered.", "WebhookEventSmsFailed": "Triggered when an SMS processing fails.", @@ -2398,6 +2473,7 @@ const docTemplate = `{ "Triggered when an SMS is sent.", "Triggered when an SMS is delivered.", "Triggered when an SMS processing fails.", + "Triggered when an SMS is cancelled.", "Triggered when the device pings the server.", "Triggered when an MMS is received.", "Triggered when an MMS is downloaded." @@ -2408,6 +2484,7 @@ const docTemplate = `{ "WebhookEventSmsSent", "WebhookEventSmsDelivered", "WebhookEventSmsFailed", + "WebhookEventSmsCancelled", "WebhookEventSystemPing", "WebhookEventMmsReceived", "WebhookEventMmsDownloaded" From 062d82bc483790ce98ad7400e0c6196cb6ce4a3a Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Wed, 17 Jun 2026 14:28:57 +0700 Subject: [PATCH 2/3] [messages] treat cancelling state as pending --- internal/sms-gateway/handlers/messages/params.go | 2 +- internal/sms-gateway/modules/messages/repository.go | 6 +++--- internal/sms-gateway/modules/messages/repository_filter.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/sms-gateway/handlers/messages/params.go b/internal/sms-gateway/handlers/messages/params.go index cc251b01..c492fce9 100644 --- a/internal/sms-gateway/handlers/messages/params.go +++ b/internal/sms-gateway/handlers/messages/params.go @@ -46,7 +46,7 @@ func (p *thirdPartyGetQueryParams) ToFilter() messages.SelectFilter { } if p.State != "" { - filter.State = messages.ProcessingState(p.State) + filter.State = append(filter.State, messages.ProcessingState(p.State)) } if p.DeviceID != "" { diff --git a/internal/sms-gateway/modules/messages/repository.go b/internal/sms-gateway/modules/messages/repository.go index b9f7ab83..78ca2f3e 100644 --- a/internal/sms-gateway/modules/messages/repository.go +++ b/internal/sms-gateway/modules/messages/repository.go @@ -47,8 +47,8 @@ func (r *Repository) list(filter SelectFilter, options SelectOptions) ([]message } // Apply state filter - if filter.State != "" { - query = query.Where("messages.state = ?", filter.State) + if len(filter.State) > 0 { + query = query.Where("messages.state IN ?", filter.State) } // Apply device filter @@ -103,7 +103,7 @@ func (r *Repository) list(filter SelectFilter, options SelectOptions) ([]message func (r *Repository) listPending(deviceID string, order Order) ([]messageModel, error) { messages, _, err := r.list( - *new(SelectFilter).WithDeviceID(deviceID).WithState(ProcessingStatePending), + *new(SelectFilter).WithDeviceID(deviceID).WithState(ProcessingStatePending).WithState(ProcessingStateCancelling), *new(SelectOptions).IncludeContent().IncludeRecipients().WithLimit(maxPendingBatch).WithOrderBy(order), ) diff --git a/internal/sms-gateway/modules/messages/repository_filter.go b/internal/sms-gateway/modules/messages/repository_filter.go index 8dbb8f0b..21154e31 100644 --- a/internal/sms-gateway/modules/messages/repository_filter.go +++ b/internal/sms-gateway/modules/messages/repository_filter.go @@ -19,7 +19,7 @@ type SelectFilter struct { DeviceID string StartDate time.Time EndDate time.Time - State ProcessingState + State []ProcessingState } func (f *SelectFilter) WithExtID(extID string) *SelectFilter { @@ -44,7 +44,7 @@ func (f *SelectFilter) WithDateRange(start, end time.Time) *SelectFilter { } func (f *SelectFilter) WithState(state ProcessingState) *SelectFilter { - f.State = state + f.State = append(f.State, state) return f } From b6065bff614c9b77c602847104e1b6e2270b1b6d Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Wed, 17 Jun 2026 14:40:44 +0700 Subject: [PATCH 3/3] [messages] pass message state to devices --- go.mod | 2 +- go.sum | 6 ++---- internal/sms-gateway/handlers/converters/messages.go | 1 + internal/sms-gateway/modules/messages/converters.go | 1 + internal/sms-gateway/modules/messages/domain.go | 1 + 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 79fc71e4..604503ed 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.25.8 require ( firebase.google.com/go/v4 v4.20.0 - github.com/android-sms-gateway/client-go v1.13.1-0.20260617071248-b35dcdbad7ff + github.com/android-sms-gateway/client-go v1.13.1-0.20260617073313-8d7d78d0d762 github.com/ansrivas/fiberprometheus/v2 v2.17.0 github.com/capcom6/go-helpers v0.4.0 github.com/capcom6/go-infra-fx v0.5.7 diff --git a/go.sum b/go.sum index 4c91e4bf..0a99ed5c 100644 --- a/go.sum +++ b/go.sum @@ -38,10 +38,8 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o= github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= -github.com/android-sms-gateway/client-go v1.13.0 h1:EvKDi796R2ScCAiaekWYRekVjkjiJGK3d/LZcPKpfQQ= -github.com/android-sms-gateway/client-go v1.13.0/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4= -github.com/android-sms-gateway/client-go v1.13.1-0.20260617071248-b35dcdbad7ff h1:guDM9z07Kbv0H2z8YlWtH5tAqkHIyT7qPbTHlCr7/yA= -github.com/android-sms-gateway/client-go v1.13.1-0.20260617071248-b35dcdbad7ff/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4= +github.com/android-sms-gateway/client-go v1.13.1-0.20260617073313-8d7d78d0d762 h1:UEUGzQuuqp2ifPJVIoZgH7ZFms2MsAQR7ajUvJ5wk6s= +github.com/android-sms-gateway/client-go v1.13.1-0.20260617073313-8d7d78d0d762/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4= github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro= github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/ansrivas/fiberprometheus/v2 v2.17.0 h1:p0gqs5LsSCWGoSFF44fCJkyU+XcE6TLRqEMu80b2iCo= diff --git a/internal/sms-gateway/handlers/converters/messages.go b/internal/sms-gateway/handlers/converters/messages.go index da451b40..33b84d8d 100644 --- a/internal/sms-gateway/handlers/converters/messages.go +++ b/internal/sms-gateway/handlers/converters/messages.go @@ -40,6 +40,7 @@ func MessageToMobileDTO(m messages.Message) smsgateway.MobileMessage { ScheduleAt: m.ScheduleAt, Priority: m.Priority, }, + State: smsgateway.ProcessingState(m.State), CreatedAt: m.CreatedAt, } } diff --git a/internal/sms-gateway/modules/messages/converters.go b/internal/sms-gateway/modules/messages/converters.go index 931bf0e1..7f413687 100644 --- a/internal/sms-gateway/modules/messages/converters.go +++ b/internal/sms-gateway/modules/messages/converters.go @@ -43,6 +43,7 @@ func messageToDomain(input messageModel) (Message, error) { ScheduleAt: input.ScheduleAt, Priority: smsgateway.MessagePriority(input.Priority), }, + State: input.State, CreatedAt: input.CreatedAt, }, nil } diff --git a/internal/sms-gateway/modules/messages/domain.go b/internal/sms-gateway/modules/messages/domain.go index 65b61a0d..8d090459 100644 --- a/internal/sms-gateway/modules/messages/domain.go +++ b/internal/sms-gateway/modules/messages/domain.go @@ -40,6 +40,7 @@ type MessageInput struct { type Message struct { MessageInput + State ProcessingState CreatedAt time.Time }