From 2f97d58c8349db935272545fa23439d9627929d3 Mon Sep 17 00:00:00 2001 From: viettranx Date: Wed, 1 Apr 2026 23:24:36 +0700 Subject: [PATCH] feat(heartbeat): support topic suffix in delivery targets - Use array_to_string([5:]) in PG to capture full chatId with topic:N suffix - Add extractSessionKeyTail() for SQLite forum group support - Join on base chatId for contact display name resolution --- internal/store/pg/heartbeat.go | 6 ++++-- internal/store/sqlitestore/heartbeat.go | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/internal/store/pg/heartbeat.go b/internal/store/pg/heartbeat.go index 7386260d..387dff46 100644 --- a/internal/store/pg/heartbeat.go +++ b/internal/store/pg/heartbeat.go @@ -276,12 +276,14 @@ func (s *PGHeartbeatStore) ListLogs(ctx context.Context, agentID uuid.UUID, limi // ListDeliveryTargets returns distinct (channel, chatID, title, kind) pairs from sessions for an agent. // Uses idx_sessions_agent (btree on agent_id) for fast lookup. -// Session key format: agent:{key}:{channel}:{kind}:{chatId} +// Session key format: agent:{key}:{channel}:{kind}:{chatId}[:topic:N] +// Extracts full chatId including topic/thread suffix via array_to_string([5:]). +// JOIN on base chatId (split_part segment 5) for contact display name resolution. func (s *PGHeartbeatStore) ListDeliveryTargets(ctx context.Context, agentID uuid.UUID) ([]store.DeliveryTarget, error) { rows, err := s.db.QueryContext(ctx, `SELECT DISTINCT ON (s.channel, chat_id) s.channel, - split_part(s.session_key, ':', 5) AS chat_id, + array_to_string((string_to_array(s.session_key::text, ':'))[5:], ':') AS chat_id, COALESCE( s.metadata->>'chat_title', cc.display_name, diff --git a/internal/store/sqlitestore/heartbeat.go b/internal/store/sqlitestore/heartbeat.go index f84c099b..d9c4b0f8 100644 --- a/internal/store/sqlitestore/heartbeat.go +++ b/internal/store/sqlitestore/heartbeat.go @@ -319,8 +319,8 @@ func (s *SQLiteHeartbeatStore) ListDeliveryTargets(ctx context.Context, agentID return nil, err } - // Parse chatId: 5th colon-separated segment of session_key. - chatID := extractSessionKeyPart(sessionKey, 5) + // Parse chatId: everything after 4th colon (includes :topic:N suffix for forum groups). + chatID := extractSessionKeyTail(sessionKey, 5) if chatID == "" { continue } @@ -369,3 +369,14 @@ func extractSessionKeyPart(key string, n int) string { } return parts[n-1] } + +// extractSessionKeyTail returns everything from the n-th colon-delimited segment onwards. +// For session key "agent:default:telegram:group:-100123:topic:42" with n=5, +// returns "-100123:topic:42" (includes topic/thread suffix). +func extractSessionKeyTail(key string, n int) string { + parts := strings.SplitN(key, ":", n) + if len(parts) < n { + return "" + } + return parts[n-1] +}