From 0d866cb7a0097b734375d7a9a473faabc71b769d Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Tue, 30 Jun 2026 08:56:01 +0700 Subject: [PATCH] fix(wc): send silent daily push at midnight ict --- README.md | 2 +- internal/modules/wc/cron.go | 13 +++++++------ internal/modules/wc/cron_test.go | 6 +++--- internal/modules/wc/handlers.go | 2 +- internal/modules/wc/handlers_test.go | 4 ++-- internal/modules/wc/wc.go | 2 +- internal/modules/wc/wc_test.go | 4 ++-- 7 files changed, 17 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index d400643..67a87c6 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Atlas via long polling and an in-process cron scheduler. | `wordle` | Daily Wordle game | | `loldle` | League-of-Legends "guess the champion" | | `lolschedule` | Pro-match schedule + daily push | -| `wc` | World Cup schedule + daily push | +| `wc` | World Cup schedule + silent daily push | | `stock` | VN-stocks paper trading | | `gold` | Gold paper trading (opt-in; primary VNAppMob SJC buy/sell VND/luong, fallback spot XAU) | | `coin` | Crypto paper trading in USD (Binance -> Coinbase -> CoinGecko price fallback) | diff --git a/internal/modules/wc/cron.go b/internal/modules/wc/cron.go index fb01732..1d24de5 100644 --- a/internal/modules/wc/cron.go +++ b/internal/modules/wc/cron.go @@ -16,8 +16,8 @@ import ( const dailyPushCronName = "wc_daily_push" -// 01:00 UTC is 08:00 ICT. -const dailyPushSchedule = "0 1 * * *" +// 17:00 UTC is 00:00 UTC+7 (ICT). +const dailyPushSchedule = "0 17 * * *" const lastPushDateKey = "daily_push:last_date" @@ -114,10 +114,11 @@ func runDailyPush(ctx context.Context, s *state, sender messageSender) error { } } if _, err := sender.SendMessage(ctx, &bot.SendMessageParams{ - ChatID: sub.ChatID, - MessageThreadID: sub.ThreadID, - Text: text, - ParseMode: models.ParseModeHTML, + ChatID: sub.ChatID, + MessageThreadID: sub.ThreadID, + Text: text, + ParseMode: models.ParseModeHTML, + DisableNotification: true, }); err != nil { log.Warn("wc daily push send failed", "chat", sub.ChatID, "thread", sub.ThreadID, "err", err) failed++ diff --git a/internal/modules/wc/cron_test.go b/internal/modules/wc/cron_test.go index 5a77c21..a9bfd2e 100644 --- a/internal/modules/wc/cron_test.go +++ b/internal/modules/wc/cron_test.go @@ -81,8 +81,8 @@ func TestRunDailyPush_SendsAndIsIdempotent(t *testing.T) { t.Fatalf("calls = %d, want 1", len(sender.calls)) } call := sender.calls[0] - if call.MessageThreadID != 7 || call.ParseMode != models.ParseModeHTML { - t.Fatalf("call = %+v, want thread 7 HTML", call) + if call.MessageThreadID != 7 || call.ParseMode != models.ParseModeHTML || !call.DisableNotification { + t.Fatalf("call = %+v, want thread 7 HTML silent notification", call) } } @@ -107,7 +107,7 @@ func TestRunDailyPush_PrunesDeadChat(t *testing.T) { func TestDailyPushCronRegistrationAndNilBot(t *testing.T) { s := newTestState() c := s.dailyPushCron() - if c.Name != dailyPushCronName || c.Schedule != dailyPushSchedule || c.Handler == nil { + if c.Name != dailyPushCronName || c.Schedule != "0 17 * * *" || c.Handler == nil { t.Fatalf("cron = %+v", c) } err := s.dailyPushHandler(context.Background(), modules.Deps{Store: storage.NewMemoryProvider().Collection("wc")}) diff --git a/internal/modules/wc/handlers.go b/internal/modules/wc/handlers.go index d0d0bf4..ba9f134 100644 --- a/internal/modules/wc/handlers.go +++ b/internal/modules/wc/handlers.go @@ -97,7 +97,7 @@ func (s *state) handleSubscribe(ctx context.Context, b *bot.Bot, update *models. } if added { return chathelper.Reply(ctx, b, msg, - "Subscribed. You'll get today's World Cup schedule at 08:00 ICT.\n"+ + "Subscribed. You'll get today's World Cup schedule at 00:00 UTC+7.\n"+ "If you block the bot, you'll be auto-unsubscribed on the next push.") } return chathelper.Reply(ctx, b, msg, "Already subscribed.") diff --git a/internal/modules/wc/handlers_test.go b/internal/modules/wc/handlers_test.go index a5d5245..673b31e 100644 --- a/internal/modules/wc/handlers_test.go +++ b/internal/modules/wc/handlers_test.go @@ -79,8 +79,8 @@ func TestHandleSchedule_BadDateInput(t *testing.T) { func TestHandleSubscribe_AddsAndIsIdempotent(t *testing.T) { rb, store := installWC(t, sampleMatchesBody, fakeNow) rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, "/wc_subscribe")) - if got := rb.LastSent().Text(); !strings.Contains(got, "Subscribed") { - t.Fatalf("first reply = %q, want subscribed", got) + if got := rb.LastSent().Text(); !strings.Contains(got, "Subscribed") || !strings.Contains(got, "00:00 UTC+7") { + t.Fatalf("first reply = %q, want subscribed with 00:00 UTC+7", got) } rb.Reset() rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, "/wc_subscribe")) diff --git a/internal/modules/wc/wc.go b/internal/modules/wc/wc.go index b61fd9a..ec24391 100644 --- a/internal/modules/wc/wc.go +++ b/internal/modules/wc/wc.go @@ -36,7 +36,7 @@ func New(deps modules.Deps) modules.Module { { Name: "wc_subscribe", Visibility: modules.VisibilityPublic, - Description: "Get the daily World Cup schedule digest at 08:00 ICT", + Description: "Get the daily World Cup schedule digest at 00:00 UTC+7", Handler: s.handleSubscribe, }, { diff --git a/internal/modules/wc/wc_test.go b/internal/modules/wc/wc_test.go index 94406ba..5b5d29f 100644 --- a/internal/modules/wc/wc_test.go +++ b/internal/modules/wc/wc_test.go @@ -18,7 +18,7 @@ func TestNewRegistersExpectedCommandsAndCron(t *testing.T) { t.Fatalf("missing command %s", name) } } - if len(mod.Crons) != 1 || mod.Crons[0].Name != dailyPushCronName { - t.Fatalf("crons = %+v, want %s", mod.Crons, dailyPushCronName) + if len(mod.Crons) != 1 || mod.Crons[0].Name != dailyPushCronName || mod.Crons[0].Schedule != "0 17 * * *" { + t.Fatalf("crons = %+v, want %s at 00:00 UTC+7", mod.Crons, dailyPushCronName) } }