fix: reopen pending human conversations
Build and publish Docker images / Build and publish images (push) Successful in 2m23s

This commit is contained in:
2026-09-14 18:55:51 +08:00
parent 665e531dc3
commit 9b8d575657
7 changed files with 121 additions and 14 deletions
@@ -1404,6 +1404,7 @@ func widgetConversationPayload(conversation model.Conversation) gin.H {
"inbox_id": conversation.InboxID,
"contact_last_seen_at": conversation.ContactLastSeenAt,
"status": conversation.Status,
"ai_takeover_active": conversation.AssigneeAgentBotID != nil && conversation.Status == string(model.ConversationStatusPending),
"custom_attributes": conversation.CustomAttributes,
}
}
@@ -529,6 +529,18 @@ func (s *WidgetService) createIncomingMessage(ctx context.Context, conversation
if err != nil {
return err
}
// Pending without an active Captain bot is a human-waiting conversation.
// Reopen it when the customer replies so it returns to the normal inbox
// and does not look like an agent is typing forever in the widget.
if !reopen && !conversation.Muted && conversation.Status == string(model.ConversationStatusPending) &&
conversation.AssigneeAgentBotID == nil && captainJob == nil {
reopen = true
if err := tx.Model(&model.Conversation{}).
Where("id = ? AND account_id = ?", conversation.ID, conversation.AccountID).
Updates(map[string]any{"status": string(model.ConversationStatusOpen), "snoozed_until": nil}).Error; err != nil {
return err
}
}
if s.realtime != nil && event != nil {
var committedConversation model.Conversation
if err := tx.Where("id = ? AND account_id = ?", conversation.ID, conversation.AccountID).First(&committedConversation).Error; err != nil {
@@ -354,6 +354,20 @@ func TestWidgetService_PublicCreateMessageUpdatesConversationBeforeDispatch(t *t
assert.Equal(t, wantTimestamp, *eventConversation.LastActivityAt)
}
func TestWidgetService_PublicCreateMessageReopensPendingHumanConversation(t *testing.T) {
db, svc := setupWidgetServiceTest(t)
_, _, _, _, conversation, displayID, _ := seedPublicMessageTest(t, db, model.ConversationStatusPending)
message, reopened, _, err := svc.PublicCreateMessage(context.Background(), "public-api", "visitor-source", displayID, PublicMessageRequest{Content: "reply to human"})
require.NoError(t, err)
assert.Equal(t, message.CreatedAt.Unix(), *reopened.LastActivityAt)
assert.Equal(t, string(model.ConversationStatusOpen), reopened.Status)
var persisted model.Conversation
require.NoError(t, db.First(&persisted, conversation.ID).Error)
assert.Equal(t, string(model.ConversationStatusOpen), persisted.Status)
}
func TestWidgetService_PublicCreateMessageReturnsDispatchError(t *testing.T) {
db, svc := setupWidgetServiceTest(t)
_, _, _, _, _, displayID, _ := seedPublicMessageTest(t, db, model.ConversationStatusOpen)