mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-09-10 04:20:01 +00:00
feat(misc): add VNG disclaimer aliases
This commit is contained in:
@@ -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`, `/wheelofnamesbeta`, `/the_answer`, `/trongtruonghop` + `/tth` disclaimer |
|
||||
| `misc` | `/ping`, `/ping_stats`, `/random`, `/wheelofnames`, `/wheelofnamesbeta`, `/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 |
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/gif"
|
||||
"math"
|
||||
@@ -638,17 +639,15 @@ func trongTruongHopUpdate(t *testing.T, text string, from *models.User) *models.
|
||||
return u
|
||||
}
|
||||
|
||||
func TestTrongTruongHop_DefaultArgUsesVNG(t *testing.T) {
|
||||
func TestTrongTruongHop_DefaultText(t *testing.T) {
|
||||
rb, _ := installMisc(t, 999)
|
||||
rb.Bot.ProcessUpdate(context.Background(), trongTruongHopUpdate(t, "/trongtruonghop",
|
||||
&models.User{ID: 7, Username: "boss", FirstName: "Boss"}))
|
||||
|
||||
got := rb.LastSent().Text()
|
||||
if !strings.Contains(got, "VNG") {
|
||||
t.Errorf("reply missing default target VNG: %q", got)
|
||||
}
|
||||
if n := strings.Count(got, "@boss"); n != 2 {
|
||||
t.Errorf("reply mentions @boss %d times, want 2: %q", n, got)
|
||||
want := fmt.Sprintf(trongTruongHopTemplate, defaultTarget, "@boss", "@boss")
|
||||
if got != want {
|
||||
t.Errorf("reply = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,8 +660,11 @@ func TestTrongTruongHop_CustomArg(t *testing.T) {
|
||||
if !strings.Contains(got, "Acme Corp") {
|
||||
t.Errorf("reply missing custom arg Acme Corp: %q", got)
|
||||
}
|
||||
if strings.Contains(got, "VNG") {
|
||||
t.Errorf("reply unexpectedly contains default VNG: %q", got)
|
||||
if strings.Contains(got, defaultTarget) {
|
||||
t.Errorf("reply unexpectedly contains default target: %q", got)
|
||||
}
|
||||
if n := strings.Count(got, "@boss"); n != 2 {
|
||||
t.Errorf("reply mentions @boss %d times, want 2: %q", n, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,27 +696,51 @@ func TestTrongTruongHop_HTMLEscapesArg(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrongTruongHop_NoUsernameFallsBackToLink(t *testing.T) {
|
||||
func TestTrongTruongHopVNG_DefaultText(t *testing.T) {
|
||||
rb, _ := installMisc(t, 999)
|
||||
rb.Bot.ProcessUpdate(context.Background(), trongTruongHopUpdate(t, "/trongtruonghop",
|
||||
&models.User{ID: 42, FirstName: "Anh"})) // no Username
|
||||
rb.Bot.ProcessUpdate(context.Background(), trongTruongHopUpdate(t, "/trongtruonghopvng ignored arg",
|
||||
&models.User{ID: 7, Username: "boss", FirstName: "Boss"}))
|
||||
|
||||
got := rb.LastSent().Text()
|
||||
wantLink := `<a href="tg://user?id=42">Anh</a>`
|
||||
if n := strings.Count(got, wantLink); n != 2 {
|
||||
t.Errorf("reply contains link %q %d times, want 2: %q", wantLink, n, got)
|
||||
want := fmt.Sprintf(trongTruongHopVNGTemplate, "@boss", "@boss")
|
||||
if got != want {
|
||||
t.Errorf("reply = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrongTruongHop_EmptyDisplayNameFallsBackToThanhVien(t *testing.T) {
|
||||
func TestTTHVNGAlias_DefaultText(t *testing.T) {
|
||||
rb, _ := installMisc(t, 999)
|
||||
rb.Bot.ProcessUpdate(context.Background(), trongTruongHopUpdate(t, "/trongtruonghop",
|
||||
&models.User{ID: 42})) // no Username, no FirstName/LastName
|
||||
rb.Bot.ProcessUpdate(context.Background(), trongTruongHopUpdate(t, "/tthvng ignored arg",
|
||||
&models.User{ID: 7, Username: "boss", FirstName: "Boss"}))
|
||||
|
||||
got := rb.LastSent().Text()
|
||||
wantLink := `<a href="tg://user?id=42">thành viên</a>`
|
||||
want := fmt.Sprintf(trongTruongHopVNGTemplate, "@boss", "@boss")
|
||||
if got != want {
|
||||
t.Errorf("reply = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrongTruongHop_NoUsernameFallsBackToDisplayNameMention(t *testing.T) {
|
||||
rb, _ := installMisc(t, 999)
|
||||
rb.Bot.ProcessUpdate(context.Background(), trongTruongHopUpdate(t, "/trongtruonghop",
|
||||
&models.User{ID: 42, FirstName: "Anh", LastName: "Le"}))
|
||||
|
||||
got := rb.LastSent().Text()
|
||||
wantLink := `<a href="tg://user?id=42">Anh Le</a>`
|
||||
if n := strings.Count(got, wantLink); n != 2 {
|
||||
t.Errorf("reply contains fallback link %d times, want 2: %q", n, got)
|
||||
t.Errorf("reply contains display-name mention %q %d times, want 2: %q", wantLink, n, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrongTruongHopVNG_NoUsernameFallsBackToDisplayNameMention(t *testing.T) {
|
||||
rb, _ := installMisc(t, 999)
|
||||
rb.Bot.ProcessUpdate(context.Background(), trongTruongHopUpdate(t, "/trongtruonghopvng ignored arg",
|
||||
&models.User{ID: 42, FirstName: "Anh", LastName: "Le"}))
|
||||
|
||||
got := rb.LastSent().Text()
|
||||
wantLink := `<a href="tg://user?id=42">Anh Le</a>`
|
||||
if n := strings.Count(got, wantLink); n != 2 {
|
||||
t.Errorf("reply contains display-name mention %q %d times, want 2: %q", wantLink, n, got)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// /ping (public, exercises KV write), /ping_stats (protected, exercises KV
|
||||
// read), /random (public random picker), /wheelofnames (public streaming
|
||||
// random picker), /wheelofnamesbeta (public GIF wheel picker), /the_answer
|
||||
// (private easter egg).
|
||||
// (private easter egg), and small public disclaimer commands.
|
||||
package misc
|
||||
|
||||
import (
|
||||
@@ -26,13 +26,17 @@ import (
|
||||
const lastPingKey = "last_ping"
|
||||
|
||||
// defaultTarget is the substituted "investigator" name when /trongtruonghop is
|
||||
// invoked without an argument.
|
||||
const defaultTarget = "VNG"
|
||||
// invoked without an argument. The command keeps a custom-arg override.
|
||||
const defaultTarget = "các cơ quan trực thuộc Bộ CA hoặc các tổ chức chính trị tương tự phục vụ cho nhà nước CHXHCNVN"
|
||||
|
||||
// trongTruongHopTemplate is the disclaimer rendered by /trongtruonghop. Three
|
||||
// %s slots: target (escaped), sender mention, sender mention.
|
||||
const trongTruongHopTemplate = "Trong trường hợp nhóm này bị điều tra bởi %s, %s khẳng định không liên quan tới nhóm hoặc những cá nhân khác trong nhóm này. %s không rõ tại sao lại có mặt ở đây vào thời điểm này, có lẽ tài khoản đã được thêm bởi một bên thứ ba."
|
||||
|
||||
// trongTruongHopVNGTemplate is the fixed-target disclaimer rendered by
|
||||
// /trongtruonghopvng. Two %s slots: sender mention, sender mention.
|
||||
const trongTruongHopVNGTemplate = "Trong trường hợp nhóm này bị điều tra bởi công ty cổ phần tập đoàn VNG nói chung và công ty 2morebits nói riêng, %s khẳng định không liên quan tới nhóm hoặc những cá nhân khác trong nhóm này. %s không rõ tại sao lại có mặt ở đây vào thời điểm này, có lẽ tài khoản đã được thêm bởi một bên thứ ba."
|
||||
|
||||
// lastPing is the value stored at the `last_ping` key: { at: <ms-since-epoch> }.
|
||||
// int64 ms-epoch (not time.Time → RFC3339) keeps the on-disk shape compact
|
||||
// and consistent with every other timestamp field in the bot's KV.
|
||||
@@ -54,6 +58,8 @@ func New(deps modules.Deps) modules.Module {
|
||||
theAnswerCommand(),
|
||||
trongTruongHopCommand("trongtruonghop"),
|
||||
trongTruongHopCommand("tth"),
|
||||
trongTruongHopVNGCommand("trongtruonghopvng"),
|
||||
trongTruongHopVNGCommand("tthvng"),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -104,11 +110,9 @@ func pingStatsCommand(store storage.DocStore[lastPing]) modules.Command {
|
||||
}
|
||||
}
|
||||
|
||||
// senderMention renders the mention used inside the trongtruonghop template.
|
||||
// Prefer @username (Telegram resolves it server-side and enforces a safe
|
||||
// charset). Fall back to a tg://user?id link with the user's display name when
|
||||
// the account has no username; escape the name because first/last names can
|
||||
// legitimately contain '<' or '&'.
|
||||
// senderMention renders the mention used inside disclaimer templates. Prefer
|
||||
// @username; when absent, link the sender's display name so Telegram still
|
||||
// mentions the account.
|
||||
func senderMention(u *models.User) string {
|
||||
if u == nil {
|
||||
return "thành viên"
|
||||
@@ -127,9 +131,9 @@ func trongTruongHopCommand(name string) modules.Command {
|
||||
return modules.Command{
|
||||
Name: name,
|
||||
Visibility: modules.VisibilityPublic,
|
||||
Description: "Phát biểu disclaimer cho thành viên hiện tại",
|
||||
Description: "Phát biểu disclaimer mặc định",
|
||||
Handler: func(ctx context.Context, b *bot.Bot, update *models.Update) error {
|
||||
if update.Message == nil || update.Message.From == nil {
|
||||
if update.Message == nil {
|
||||
return nil
|
||||
}
|
||||
arg := strings.TrimSpace(chathelper.ArgAfterCommand(update.Message.Text))
|
||||
@@ -143,6 +147,22 @@ func trongTruongHopCommand(name string) modules.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func trongTruongHopVNGCommand(name string) modules.Command {
|
||||
return modules.Command{
|
||||
Name: name,
|
||||
Visibility: modules.VisibilityPublic,
|
||||
Description: "Phát biểu disclaimer VNG mặc định",
|
||||
Handler: func(ctx context.Context, b *bot.Bot, update *models.Update) error {
|
||||
if update.Message == nil {
|
||||
return nil
|
||||
}
|
||||
mention := senderMention(update.Message.From)
|
||||
text := fmt.Sprintf(trongTruongHopVNGTemplate, mention, mention)
|
||||
return chathelper.ReplyHTML(ctx, b, update.Message, text)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func theAnswerCommand() modules.Command {
|
||||
return modules.Command{
|
||||
Name: "the_answer",
|
||||
|
||||
@@ -21,14 +21,16 @@ func TestNew_RegistersExpectedCommands(t *testing.T) {
|
||||
mod := New(deps)
|
||||
|
||||
want := map[string]modules.Visibility{
|
||||
"ping": modules.VisibilityPublic,
|
||||
"ping_stats": modules.VisibilityProtected,
|
||||
"random": modules.VisibilityPublic,
|
||||
"wheelofnames": modules.VisibilityPublic,
|
||||
"wheelofnamesbeta": modules.VisibilityPublic,
|
||||
"the_answer": modules.VisibilityPrivate,
|
||||
"trongtruonghop": modules.VisibilityPublic,
|
||||
"tth": modules.VisibilityPublic,
|
||||
"ping": modules.VisibilityPublic,
|
||||
"ping_stats": modules.VisibilityProtected,
|
||||
"random": modules.VisibilityPublic,
|
||||
"wheelofnames": modules.VisibilityPublic,
|
||||
"wheelofnamesbeta": modules.VisibilityPublic,
|
||||
"the_answer": modules.VisibilityPrivate,
|
||||
"trongtruonghop": modules.VisibilityPublic,
|
||||
"tth": modules.VisibilityPublic,
|
||||
"trongtruonghopvng": modules.VisibilityPublic,
|
||||
"tthvng": modules.VisibilityPublic,
|
||||
}
|
||||
if len(mod.Commands) != len(want) {
|
||||
t.Fatalf("commands count = %d, want %d", len(mod.Commands), len(want))
|
||||
|
||||
@@ -22,12 +22,20 @@
|
||||
},
|
||||
{
|
||||
"command": "trongtruonghop",
|
||||
"description": "Phát biểu disclaimer cho thành viên hiện tại"
|
||||
"description": "Phát biểu disclaimer mặc định"
|
||||
},
|
||||
{
|
||||
"command": "tth",
|
||||
"description": "Alias for /trongtruonghop"
|
||||
},
|
||||
{
|
||||
"command": "trongtruonghopvng",
|
||||
"description": "Phát biểu disclaimer VNG mặc định"
|
||||
},
|
||||
{
|
||||
"command": "tthvng",
|
||||
"description": "Alias for /trongtruonghopvng"
|
||||
},
|
||||
{
|
||||
"command": "wordle",
|
||||
"description": "Classic wordle; guess the 5-letter word"
|
||||
|
||||
Reference in New Issue
Block a user