fix(telegram): avoid duplicate group status replies on draft fallback
This commit is contained in:
parent
baa20a020c
commit
2b90f85a87
2 changed files with 99 additions and 4 deletions
|
|
@ -534,12 +534,15 @@ func (m *Manager) handleStatusSend(ctx context.Context, name string, w *channelW
|
||||||
}
|
}
|
||||||
if did == 0 {
|
if did == 0 {
|
||||||
did = generateDraftID(key)
|
did = generateDraftID(key)
|
||||||
|
}
|
||||||
|
if err := drafter.SendDraft(ctx, msg.ChatID, did, msg.Content); err == nil {
|
||||||
|
// Track draft only after successful send. If draft fails (e.g. group
|
||||||
|
// main thread), keep existing messageID entry so fallback edits can
|
||||||
|
// reuse the same status bubble instead of creating duplicates.
|
||||||
m.statusMsgIDs.Store(key, statusMsgEntry{
|
m.statusMsgIDs.Store(key, statusMsgEntry{
|
||||||
draftID: did,
|
draftID: did,
|
||||||
createdAt: time.Now(),
|
createdAt: time.Now(),
|
||||||
})
|
})
|
||||||
}
|
|
||||||
if err := drafter.SendDraft(ctx, msg.ChatID, did, msg.Content); err == nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Draft failed — fall through to edit-based approach
|
// Draft failed — fall through to edit-based approach
|
||||||
|
|
@ -628,12 +631,14 @@ func (m *Manager) handleTaskStatusSend(ctx context.Context, name string, w *chan
|
||||||
}
|
}
|
||||||
if did == 0 {
|
if did == 0 {
|
||||||
did = generateDraftID(taskKey)
|
did = generateDraftID(taskKey)
|
||||||
|
}
|
||||||
|
if err := drafter.SendDraft(ctx, msg.ChatID, did, msg.Content); err == nil {
|
||||||
|
// Track draft only after successful send to avoid clobbering an
|
||||||
|
// existing messageID entry when drafts are unsupported.
|
||||||
m.taskMsgIDs.Store(taskKey, statusMsgEntry{
|
m.taskMsgIDs.Store(taskKey, statusMsgEntry{
|
||||||
draftID: did,
|
draftID: did,
|
||||||
createdAt: time.Now(),
|
createdAt: time.Now(),
|
||||||
})
|
})
|
||||||
}
|
|
||||||
if err := drafter.SendDraft(ctx, msg.ChatID, did, msg.Content); err == nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Draft failed — fall through to edit-based approach
|
// Draft failed — fall through to edit-based approach
|
||||||
|
|
|
||||||
|
|
@ -1355,6 +1355,48 @@ func TestHandleStatusSend_DraftFails_FallsToEdit(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHandleStatusSend_DraftFailure_DoesNotClobberTrackedMessageID(t *testing.T) {
|
||||||
|
m := newTestManager()
|
||||||
|
var sendWithIDCount int
|
||||||
|
var editCount int
|
||||||
|
var editedMessageID string
|
||||||
|
|
||||||
|
ch := &mockDraftSender{
|
||||||
|
mockChannel: mockChannel{
|
||||||
|
sendFn: func(_ context.Context, _ bus.OutboundMessage) error { return nil },
|
||||||
|
},
|
||||||
|
draftFn: func(_ context.Context, _ string, _ int, _ string) error {
|
||||||
|
return fmt.Errorf("draft unsupported")
|
||||||
|
},
|
||||||
|
editFn: func(_ context.Context, _, messageID, _ string) error {
|
||||||
|
editCount++
|
||||||
|
editedMessageID = messageID
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
sendWithID: func(_ context.Context, _, _ string) (string, error) {
|
||||||
|
sendWithIDCount++
|
||||||
|
return "msg-1", nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
w := &channelWorker{ch: ch, limiter: rate.NewLimiter(rate.Inf, 1)}
|
||||||
|
|
||||||
|
msg := bus.OutboundMessage{Channel: "test", ChatID: "group-main", Content: "preview-1", IsStatus: true}
|
||||||
|
m.handleStatusSend(context.Background(), "test", w, msg)
|
||||||
|
|
||||||
|
msg.Content = "preview-2"
|
||||||
|
m.handleStatusSend(context.Background(), "test", w, msg)
|
||||||
|
|
||||||
|
if sendWithIDCount != 1 {
|
||||||
|
t.Fatalf("expected SendWithID to be called once, got %d", sendWithIDCount)
|
||||||
|
}
|
||||||
|
if editCount != 1 {
|
||||||
|
t.Fatalf("expected EditMessage to be called once, got %d", editCount)
|
||||||
|
}
|
||||||
|
if editedMessageID != "msg-1" {
|
||||||
|
t.Fatalf("expected EditMessage target msg-1, got %s", editedMessageID)
|
||||||
|
}
|
||||||
|
}
|
||||||
func TestHandleTaskStatusSend_UsesDraftSender(t *testing.T) {
|
func TestHandleTaskStatusSend_UsesDraftSender(t *testing.T) {
|
||||||
m := newTestManager()
|
m := newTestManager()
|
||||||
var draftCalled bool
|
var draftCalled bool
|
||||||
|
|
@ -1393,6 +1435,54 @@ func TestHandleTaskStatusSend_UsesDraftSender(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHandleTaskStatusSend_DraftFailure_DoesNotClobberTrackedMessageID(t *testing.T) {
|
||||||
|
m := newTestManager()
|
||||||
|
var sendWithIDCount int
|
||||||
|
var editCount int
|
||||||
|
var editedMessageID string
|
||||||
|
|
||||||
|
ch := &mockDraftSender{
|
||||||
|
mockChannel: mockChannel{
|
||||||
|
sendFn: func(_ context.Context, _ bus.OutboundMessage) error { return nil },
|
||||||
|
},
|
||||||
|
draftFn: func(_ context.Context, _ string, _ int, _ string) error {
|
||||||
|
return fmt.Errorf("draft unsupported")
|
||||||
|
},
|
||||||
|
editFn: func(_ context.Context, _, messageID, _ string) error {
|
||||||
|
editCount++
|
||||||
|
editedMessageID = messageID
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
sendWithID: func(_ context.Context, _, _ string) (string, error) {
|
||||||
|
sendWithIDCount++
|
||||||
|
return "task-msg-1", nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
w := &channelWorker{ch: ch, limiter: rate.NewLimiter(rate.Inf, 1)}
|
||||||
|
|
||||||
|
msg := bus.OutboundMessage{
|
||||||
|
Channel: "test",
|
||||||
|
ChatID: "group-main",
|
||||||
|
Content: "task-10%",
|
||||||
|
IsTaskStatus: true,
|
||||||
|
TaskID: "task-1",
|
||||||
|
}
|
||||||
|
m.handleTaskStatusSend(context.Background(), "test", w, msg)
|
||||||
|
|
||||||
|
msg.Content = "task-20%"
|
||||||
|
m.handleTaskStatusSend(context.Background(), "test", w, msg)
|
||||||
|
|
||||||
|
if sendWithIDCount != 1 {
|
||||||
|
t.Fatalf("expected SendWithID to be called once, got %d", sendWithIDCount)
|
||||||
|
}
|
||||||
|
if editCount != 1 {
|
||||||
|
t.Fatalf("expected EditMessage to be called once, got %d", editCount)
|
||||||
|
}
|
||||||
|
if editedMessageID != "task-msg-1" {
|
||||||
|
t.Fatalf("expected EditMessage target task-msg-1, got %s", editedMessageID)
|
||||||
|
}
|
||||||
|
}
|
||||||
func TestPreSend_ClearsDraftState(t *testing.T) {
|
func TestPreSend_ClearsDraftState(t *testing.T) {
|
||||||
m := newTestManager()
|
m := newTestManager()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue