diff --git a/pkg/channels/interfaces.go b/pkg/channels/interfaces.go index 580157ce0..99e977079 100644 --- a/pkg/channels/interfaces.go +++ b/pkg/channels/interfaces.go @@ -15,6 +15,11 @@ type MessageEditor interface { EditMessage(ctx context.Context, chatID string, messageID string, content string) error } +// MessageDeleter — channels that can delete a message by ID. +type MessageDeleter interface { + DeleteMessage(ctx context.Context, chatID string, messageID string) error +} + // ReactionCapable — channels that can add a reaction (e.g. 👀) to an inbound message. // ReactToMessage adds a reaction and returns an undo function to remove it. // The undo function MUST be idempotent and safe to call multiple times. diff --git a/pkg/channels/manager.go b/pkg/channels/manager.go index ba56bd789..c259e439a 100644 --- a/pkg/channels/manager.go +++ b/pkg/channels/manager.go @@ -133,10 +133,18 @@ func (m *Manager) preSend(ctx context.Context, name string, msg bus.OutboundMess } } - // 3. If a stream already finalized this message, skip both placeholder and send + // 3. If a stream already finalized this message, delete the placeholder and skip send if _, loaded := m.streamActive.LoadAndDelete(key); loaded { - // Also clean up any stale placeholder - m.placeholders.Delete(key) + if v, loaded := m.placeholders.LoadAndDelete(key); loaded { + if entry, ok := v.(placeholderEntry); ok && entry.id != "" { + // Prefer deleting the placeholder (cleaner UX than editing to same content) + if deleter, ok := ch.(MessageDeleter); ok { + deleter.DeleteMessage(ctx, msg.ChatID, entry.id) // best effort + } else if editor, ok := ch.(MessageEditor); ok { + editor.EditMessage(ctx, msg.ChatID, entry.id, msg.Content) // fallback + } + } + } return true } diff --git a/pkg/channels/telegram/telegram.go b/pkg/channels/telegram/telegram.go index a7df1ebc5..4daace52a 100644 --- a/pkg/channels/telegram/telegram.go +++ b/pkg/channels/telegram/telegram.go @@ -301,6 +301,22 @@ func (c *TelegramChannel) EditMessage(ctx context.Context, chatID string, messag return err } +// DeleteMessage implements channels.MessageDeleter. +func (c *TelegramChannel) DeleteMessage(ctx context.Context, chatID string, messageID string) error { + cid, err := parseChatID(chatID) + if err != nil { + return err + } + mid, err := strconv.Atoi(messageID) + if err != nil { + return err + } + return c.bot.DeleteMessage(ctx, &telego.DeleteMessageParams{ + ChatID: tu.ID(cid), + MessageID: mid, + }) +} + // SendPlaceholder implements channels.PlaceholderCapable. // It sends a placeholder message (e.g. "Thinking... 💭") that will later be // edited to the actual response via EditMessage (channels.MessageEditor).