fix: waiting_since timestamp being set to null on a non-outgoing message.

- Set to null only after outgoing message is sent
- change imap log from debug to info
This commit is contained in:
Abhinav Raut
2025-06-07 19:57:28 +05:30
parent 5bcb0a2ad9
commit ade833fb7b
4 changed files with 77 additions and 53 deletions

View File

@@ -197,6 +197,7 @@ type queries struct {
GetUserActiveConversationsCount *sqlx.Stmt `query:"get-user-active-conversations-count"`
UpdateConversationFirstReplyAt *sqlx.Stmt `query:"update-conversation-first-reply-at"`
UpdateConversationLastReplyAt *sqlx.Stmt `query:"update-conversation-last-reply-at"`
UpdateConversationWaitingSince *sqlx.Stmt `query:"update-conversation-waiting-since"`
UpdateConversationAssigneeLastSeen *sqlx.Stmt `query:"update-conversation-assignee-last-seen"`
UpdateConversationAssignedUser *sqlx.Stmt `query:"update-conversation-assigned-user"`
UpdateConversationAssignedTeam *sqlx.Stmt `query:"update-conversation-assigned-team"`
@@ -460,6 +461,25 @@ func (c *Manager) UpdateConversationLastReplyAt(conversationUUID string, convers
return nil
}
// UpdateConversationWaitingSince updates the waiting since timestamp for a conversation.
func (c *Manager) UpdateConversationWaitingSince(conversationUUID string, at *time.Time) error {
res, err := c.q.UpdateConversationWaitingSince.Exec(conversationUUID, at)
if err != nil {
c.lo.Error("error updating conversation waiting since", "error", err)
return err
}
rows, _ := res.RowsAffected()
if rows > 0 {
if at != nil {
c.BroadcastConversationUpdate(conversationUUID, "waiting_since", at.Format(time.RFC3339))
} else {
c.BroadcastConversationUpdate(conversationUUID, "waiting_since", nil)
}
}
return nil
}
// UpdateConversationUserAssignee sets the assignee of a conversation to a specifc user.
func (c *Manager) UpdateConversationUserAssignee(uuid string, assigneeID int, actor umodels.User) error {
if err := c.UpdateAssignee(uuid, assigneeID, models.AssigneeTypeUser); err != nil {

View File

@@ -203,6 +203,9 @@ func (m *Manager) sendOutgoingMessage(message models.Message) {
}
m.UpdateConversationLastReplyAt(message.ConversationUUID, message.ConversationID, now)
// Clear waiting since timestamp as agent has replied to the conversation.
m.UpdateConversationWaitingSince(message.ConversationUUID, nil)
// Mark latest SLA event for next response as met.
metAt, err := m.slaStore.SetLatestSLAEventMetAt(conversation.AppliedSLAID.Int, sla.MetricNextResponse)
if err != nil && !errors.Is(err, sla.ErrLatestSLAEventNotFound) {
@@ -617,6 +620,10 @@ func (m *Manager) processIncomingMessage(in models.IncomingMessage) error {
}
}
// Set waiting since timestamp, this gets cleared when agent replies to the conversation.
now := time.Now()
m.UpdateConversationWaitingSince(in.Message.ConversationUUID, &now)
// Trigger automations on incoming message event.
m.automation.EvaluateConversationUpdateRules(in.Message.ConversationUUID, amodels.EventConversationMessageIncoming)

View File

@@ -331,6 +331,54 @@ SET custom_attributes = $2,
updated_at = NOW()
WHERE uuid = $1;
-- name: update-conversation-waiting-since
UPDATE conversations
SET waiting_since = $2,
updated_at = NOW()
WHERE uuid = $1;
-- name: remove-conversation-assignee
UPDATE conversations
SET
assigned_user_id = CASE WHEN $2 = 'user' THEN NULL ELSE assigned_user_id END,
assigned_team_id = CASE WHEN $2 = 'team' THEN NULL ELSE assigned_team_id END,
updated_at = NOW()
WHERE uuid = $1;
-- name: re-open-conversation
-- Open conversation if it is not already open and unset the assigned user if they are away and reassigning.
UPDATE conversations
SET
status_id = (SELECT id FROM conversation_statuses WHERE name = 'Open'),
snoozed_until = NULL,
updated_at = NOW(),
assigned_user_id = CASE
WHEN EXISTS (
SELECT 1 FROM users
WHERE users.id = conversations.assigned_user_id
AND users.availability_status = 'away_and_reassigning'
) THEN NULL
ELSE assigned_user_id
END
WHERE
uuid = $1
AND status_id IN (
SELECT id FROM conversation_statuses WHERE name NOT IN ('Open')
)
-- name: get-conversation-by-message-id
SELECT
c.id,
c.uuid,
c.assigned_team_id,
c.assigned_user_id
FROM conversation_messages m
JOIN conversations c ON m.conversation_id = c.id
WHERE m.id = $1;
-- name: delete-conversation
DELETE FROM conversations WHERE uuid = $1;
-- MESSAGE queries.
-- name: get-message-source-ids
SELECT
@@ -449,15 +497,6 @@ inserted_msg AS (
$5, $6, $7, $8, $9, $10, $11, $12
)
RETURNING id, uuid, created_at, conversation_id
),
updated_conversation AS (
UPDATE conversations
SET waiting_since = CASE
WHEN $8 = 'contact' THEN NOW()
WHEN $8 = 'agent' THEN NULL
ELSE waiting_since
END
WHERE id = (SELECT id FROM conversation_id)
)
SELECT id, uuid, created_at FROM inserted_msg;
@@ -466,51 +505,9 @@ SELECT conversation_id
FROM conversation_messages
WHERE source_id = ANY($1::text []);
-- name: get-conversation-by-message-id
SELECT
c.id,
c.uuid,
c.assigned_team_id,
c.assigned_user_id
FROM conversation_messages m
JOIN conversations c ON m.conversation_id = c.id
WHERE m.id = $1;
-- name: update-message-status
update conversation_messages set status = $1, updated_at = NOW() where uuid = $2;
-- name: remove-conversation-assignee
UPDATE conversations
SET
assigned_user_id = CASE WHEN $2 = 'user' THEN NULL ELSE assigned_user_id END,
assigned_team_id = CASE WHEN $2 = 'team' THEN NULL ELSE assigned_team_id END,
updated_at = NOW()
WHERE uuid = $1;
-- name: re-open-conversation
-- Open conversation if it is not already open and unset the assigned user if they are away and reassigning.
UPDATE conversations
SET
status_id = (SELECT id FROM conversation_statuses WHERE name = 'Open'),
snoozed_until = NULL,
updated_at = NOW(),
assigned_user_id = CASE
WHEN EXISTS (
SELECT 1 FROM users
WHERE users.id = conversations.assigned_user_id
AND users.availability_status = 'away_and_reassigning'
) THEN NULL
ELSE assigned_user_id
END
WHERE
uuid = $1
AND status_id IN (
SELECT id FROM conversation_statuses WHERE name NOT IN ('Open')
)
-- name: delete-conversation
DELETE FROM conversations WHERE uuid = $1;
-- name: get-latest-message
SELECT
m.created_at,

View File

@@ -54,7 +54,7 @@ func (e *Email) ReadIncomingMessages(ctx context.Context, cfg IMAPConfig) error
if err := e.processMailbox(ctx, scanInboxSince, cfg); err != nil && err != context.Canceled {
e.lo.Error("error searching emails", "error", err)
}
e.lo.Debug("email search complete", "mailbox", cfg.Mailbox, "inbox_id", e.Identifier())
e.lo.Info("email search complete", "mailbox", cfg.Mailbox, "inbox_id", e.Identifier())
}
}
}
@@ -98,7 +98,7 @@ func (e *Email) processMailbox(ctx context.Context, scanInboxSince time.Duration
// Scan emails since the specified duration.
since := time.Now().Add(-scanInboxSince)
e.lo.Debug("searching emails", "since", since, "mailbox", cfg.Mailbox, "inbox_id", e.Identifier())
e.lo.Info("searching emails", "since", since, "mailbox", cfg.Mailbox, "inbox_id", e.Identifier())
// Search for messages in the mailbox.
searchResults, err := e.searchMessages(client, since)