From 2ffd31885e7594b92985c1db6bbfb8cef42d9a75 Mon Sep 17 00:00:00 2001 From: Artem Tsyrulnikov Date: Mon, 1 Jun 2026 22:02:55 +0300 Subject: [PATCH] fix: ensure poll unpins reliably and remove duplicate quiz - Remove the extra Wednesday 16:19 send_quiz job; only the Friday 17:00 quiz remains, matching the /start description. The duplicate send overwrote the poll mapping, orphaning the earlier pinned poll so it was never unpinned. - Extract unpinPoll() and call it at the start of CreatePairs so the poll is always unpinned, even when no pairs can be formed (previously early returns skipped the unpin block entirely). Co-Authored-By: Claude Opus 4.8 (1M context) --- cmd/handlers.go | 47 +++++++++++++++++++++++++++++------------------ cmd/main.go | 2 -- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/cmd/handlers.go b/cmd/handlers.go index 3b3f1d1..caf616d 100644 --- a/cmd/handlers.go +++ b/cmd/handlers.go @@ -391,8 +391,37 @@ func appendUnpairedMessage(ctx context.Context, db *sql.DB, message string, grou return message } +// unpinPoll unpins the active poll for the group and removes its mapping. +// Safe to call even if there is no active poll. +func unpinPoll(ctx context.Context, db *sql.DB, api echotron.API, groupID int64) { + pollMapping, err := database.GetPollMappingByGroupID(ctx, db, groupID) + if err != nil { + log.Warn().Err(err).Int64("group_id", groupID).Msg("GetPollMappingByGroupID failed (no active poll)") + return + } + if pollMapping == nil { + return + } + + _, err = api.UnpinChatMessage(groupID, &echotron.UnpinMessageOptions{MessageID: int(pollMapping.MessageID)}) + if err != nil { + log.Warn().Err(err).Int64("group_id", groupID).Int64("message_id", pollMapping.MessageID).Msg("UnpinChatMessage failed (check bot permissions)") + } else { + log.Info().Int64("group_id", groupID).Int64("message_id", pollMapping.MessageID).Msg("Poll message unpinned") + } + + // Delete poll mapping after attempting to unpin (even if unpin failed) + if err := database.DeletePollMapping(ctx, db, groupID); err != nil { + log.Warn().Err(err).Int64("group_id", groupID).Msg("DeletePollMapping failed") + } +} + // CreatePairs generates random pairs func CreatePairs(ctx context.Context, db *sql.DB, api echotron.API, groupID int64) { + // Always unpin the active poll first, regardless of whether pairs get + // created below — otherwise an early return would leave the poll pinned. + unpinPoll(ctx, db, api, groupID) + availablePairs, err := database.GetAvailablePairs(ctx, db, groupID) if err != nil { log.Error().Err(err).Int64("group_id", groupID).Msg("GetAvailablePairs failed") @@ -428,24 +457,6 @@ func CreatePairs(ctx context.Context, db *sql.DB, api echotron.API, groupID int6 sendMessage(api, message, groupID) - // Unpin the poll message - pollMapping, err := database.GetPollMappingByGroupID(ctx, db, groupID) - if err != nil { - log.Warn().Err(err).Int64("group_id", groupID).Msg("GetPollMappingByGroupID failed (no active poll)") - } else if pollMapping != nil { - _, err = api.UnpinChatMessage(groupID, &echotron.UnpinMessageOptions{MessageID: int(pollMapping.MessageID)}) - if err != nil { - log.Warn().Err(err).Int64("group_id", groupID).Int64("message_id", pollMapping.MessageID).Msg("UnpinChatMessage failed (check bot permissions)") - } else { - log.Info().Int64("group_id", groupID).Int64("message_id", pollMapping.MessageID).Msg("Poll message unpinned") - } - - // Delete poll mapping after attempting to unpin (even if unpin failed) - if err := database.DeletePollMapping(ctx, db, groupID); err != nil { - log.Warn().Err(err).Int64("group_id", groupID).Msg("DeletePollMapping failed") - } - } - if err = database.ClearAllParticipants(ctx, db, groupID); err != nil { log.Error().Err(err).Int64("group_id", groupID).Msg("ClearAllParticipants failed") } diff --git a/cmd/main.go b/cmd/main.go index 40cb280..f455ebc 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -206,8 +206,6 @@ func startScheduler(db *sql.DB, api echotron.API, stopChan chan struct{}) { // Friday 17:00 - send quiz scheduleJob("send_quiz", time.Friday, 17, 0, SendQuizToAllGroups, db, api, stopChan, moscowTZ) - scheduleJob("send_quiz", time.Wednesday, 16, 19, SendQuizToAllGroups, db, api, stopChan, moscowTZ) - // Sunday 19:00 - create pairs scheduleJob("create_pairs", time.Sunday, 19, 0, CreatePairsForAllGroups, db, api, stopChan, moscowTZ)