From de76a74c32559615eb46bebccb9cd174cb05bb27 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sun, 19 Jul 2026 20:09:22 +0700 Subject: [PATCH] feat(misc): add protected /ff giving-up template Replies with a fixed Vietnamese copypasta for when T1 starts losing, built from the local community's running jokes. Protected, so it stays out of the public command menu like the other gated commands. --- README.md | 2 +- internal/modules/misc/ff_command.go | 47 ++++++++++++++++++++++++++ internal/modules/misc/handlers_test.go | 30 ++++++++++++++++ internal/modules/misc/misc.go | 5 +-- internal/modules/misc/misc_test.go | 1 + 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 internal/modules/misc/ff_command.go diff --git a/README.md b/README.md index fc57077..181b085 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Atlas via long polling and an in-process cron scheduler. | Module | What it does | |---|---| | `util` | `/help`, `/info`, `/stickerid` | -| `misc` | `/ping`, `/ping_stats`, `/random`, `/wheelofnames`, `/the_answer`, `/trongtruonghop` + `/tth`, `/trongtruonghopvng` + `/tthvng` disclaimers | +| `misc` | `/ping`, `/ping_stats`, `/random`, `/wheelofnames`, `/ff`, `/the_answer`, `/trongtruonghop` + `/tth`, `/trongtruonghopvng` + `/tthvng` disclaimers | | `wordle` | Daily Wordle game | | `loldle` | League-of-Legends "guess the champion" | | `lol` | Pro-match schedule (`/lol`, `/lol_tomorrow`, `/lol_this_week`, `/lol_next_week`) + daily push | diff --git a/internal/modules/misc/ff_command.go b/internal/modules/misc/ff_command.go new file mode 100644 index 0000000..d0d1811 --- /dev/null +++ b/internal/modules/misc/ff_command.go @@ -0,0 +1,47 @@ +package misc + +import ( + "context" + + "github.com/go-telegram/bot" + "github.com/go-telegram/bot/models" + + "github.com/tiennm99/miti99bot/internal/modules" + "github.com/tiennm99/miti99bot/internal/modules/util/chathelper" +) + +// ffTemplate is the fixed rant /ff replies with. Static text, no substitution — +// the joke is that it is a copy-paste "văn mẫu" pasted into the group chat every +// time T1 starts losing, arguing that watching on only makes it hurt more. +// +// Every punchline is a running joke of the Vietnamese LMHT community rather than +// a role-by-role callout: "Tếu 1"/"rạp xiếc trung ương"/"di sản văn hóa hài kịch +// của LCK", the "gói Premium hết hạn tháng 11" bit about peaking only at Worlds, +// the "vía" the fan rituals are meant to summon, and "Tê Con" — the affectionate +// nickname Vietnamese fans gave T1 that the org itself adopted. +const ffTemplate = `🏳️ TẮT STREAM ĐI, TẾU 1 LẠI LÊN SÓNG RỒI 🏳️ + +Nhìn tổng thể là đủ hiểu: rạp xiếc trung ương mở màn, di sản văn hóa hài kịch của LCK lại tấu một show mới. Không cần soi ai riêng đâu — cả đội đang tấu hài rất đều, rất đoàn kết. + +Xem tiếp KHÔNG cứu được, xem tiếp chỉ ĐAU HƠN, càng nhiều highlight miễn phí cho đối thủ. + +Gói Premium hết hạn từ tháng 11 rồi, năm nay không gia hạn nữa đâu — vía cũng hết hạn theo luôn. + +Tắt sớm = ngủ đủ giấc, mai còn đi làm. +Cố xem tới hết = vẫn thua, mà mất ngủ, còn làm tâm trạng bực bội thêm. + +/FF SAVE TIME ĐI TÊ CON ƠI. 🙏🙏` + +func ffCommand() modules.Command { + return modules.Command{ + Name: "ff", + Visibility: modules.VisibilityProtected, + Description: "Văn mẫu đầu hàng khi T1 sắp thua — tắt stream cho đỡ đau", + Handler: func(ctx context.Context, b *bot.Bot, update *models.Update) error { + if update.Message == nil { + return nil + } + return chathelper.Reply(ctx, b, update.Message, ffTemplate) + }, + } +} diff --git a/internal/modules/misc/handlers_test.go b/internal/modules/misc/handlers_test.go index 70d4760..f18a795 100644 --- a/internal/modules/misc/handlers_test.go +++ b/internal/modules/misc/handlers_test.go @@ -418,6 +418,36 @@ func TestTrongTruongHopVNG_NoUsernameFallsBackToDisplayNameMention(t *testing.T) } } +func TestFF_DeniedToNonAdmin(t *testing.T) { + rb, _ := installMisc(t, 999) + + rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, "/ff")) + if calls := rb.Sent(); len(calls) != 0 { + t.Errorf("non-admin /ff replied: %+v", calls) + } +} + +func TestFF_RepliesTemplate(t *testing.T) { + rb, _ := installMisc(t, 999) + + // Args are ignored — the reply is the same canned rant every time. + rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(999, "/ff ignored arg")) + if got := rb.LastSent().Text(); got != ffTemplate { + t.Errorf("/ff reply = %q, want the ff template", got) + } +} + +// The template signs off with an uppercase /FF, so tapping that link must reach +// the same handler the lowercase form does. +func TestFF_UppercaseFormDispatches(t *testing.T) { + rb, _ := installMisc(t, 999) + + rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(999, "/FF")) + if got := rb.LastSent().Text(); got != ffTemplate { + t.Errorf("/FF reply = %q, want the ff template", got) + } +} + func TestTheAnswer_OwnerOnly(t *testing.T) { rb, _ := installMisc(t, 999) diff --git a/internal/modules/misc/misc.go b/internal/modules/misc/misc.go index f6b1e81..7c3a95b 100644 --- a/internal/modules/misc/misc.go +++ b/internal/modules/misc/misc.go @@ -1,8 +1,8 @@ // Package misc is a small stub module that proves the framework end-to-end: // /ping (public, exercises KV write), /ping_stats (protected, exercises KV // read), /random (public random picker), /wheelofnames (public wheel picker -// with optional GIF), /the_answer (private easter egg), and small public disclaimer -// commands. +// with optional GIF), /ff (protected give-up-on-T1 rant), /the_answer (private +// easter egg), and small public disclaimer commands. package misc import ( @@ -54,6 +54,7 @@ func New(deps modules.Deps) modules.Module { pingStatsCommand(store), randomCommand(), wheelOfNamesCommand(), + ffCommand(), theAnswerCommand(), disclaimerCommand("trongtruonghop", "Phát biểu disclaimer mặc định", defaultTarget, true), disclaimerCommand("tth", "Phát biểu disclaimer mặc định", defaultTarget, true), diff --git a/internal/modules/misc/misc_test.go b/internal/modules/misc/misc_test.go index dc3416b..79155b3 100644 --- a/internal/modules/misc/misc_test.go +++ b/internal/modules/misc/misc_test.go @@ -25,6 +25,7 @@ func TestNew_RegistersExpectedCommands(t *testing.T) { "ping_stats": modules.VisibilityProtected, "random": modules.VisibilityPublic, "wheelofnames": modules.VisibilityPublic, + "ff": modules.VisibilityProtected, "the_answer": modules.VisibilityPrivate, "trongtruonghop": modules.VisibilityPublic, "tth": modules.VisibilityPublic,