From 5561c330687b2100ed2427c5274267be6a0b28e0 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Thu, 1 Oct 2026 13:59:12 +0700 Subject: [PATCH] feat(misc): default /gacha options to 5 stars with a 3*/4* prefix Rarity is now a leading prefix such as "4* Pho" or "3* Rice"; options without one are 5 stars. This replaces the trailing "*5" tag and its 3-star default. --- README.md | 2 +- docs/deploy-coolify-selfhosted.md | 5 +++-- internal/modules/misc/gacha_command.go | 16 ++++++++-------- internal/modules/misc/gacha_command_test.go | 20 +++++++++++--------- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index aea6173..45c5f59 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` (admin), `/stickerid` (owner) | -| `misc` | `/ping`, `/ping_stats` (admin), `/random`, `/wheelofnames`, `/gacha` (Genshin-style wish; tag `*4`/`*5` for rarity), `/ff` (admin), `/xlt1`, `/giaxang` (Petrolimex retail fuel prices), `/the_answer` (owner), `/trongtruonghop` + `/tth`, `/trongtruonghopvng` + `/tthvng` disclaimers | +| `misc` | `/ping`, `/ping_stats` (admin), `/random`, `/wheelofnames`, `/gacha` (Genshin-style wish; options are 5★ by default, prefix `4*` or `3*`), `/ff` (admin), `/xlt1`, `/giaxang` (Petrolimex retail fuel prices), `/the_answer` (owner), `/trongtruonghop` + `/tth`, `/trongtruonghopvng` + `/tthvng` disclaimers | | `amlich` | Vietnamese lunar calendar: `/amlich` (dương lịch → âm lịch, defaults to today), `/duonglich` (âm lịch → dương lịch, `nhuan` flag for leap months); dates accept `d`, `d/m`, or `d/m/yyyy` — missing parts fill from today in the input's calendar. Years 1800–2199 only | | `wordle` | Daily Wordle game: `/wordle [word]`, `/wordle_new`, `/wordle_giveup`, `/wordle_stats` | | `loldle` | League-of-Legends "guess the champion": `/loldle [champion]`, `/loldle_giveup`, `/loldle_stats`, `/loldle_setmax` (owner) | diff --git a/docs/deploy-coolify-selfhosted.md b/docs/deploy-coolify-selfhosted.md index bedb0a8..056713a 100644 --- a/docs/deploy-coolify-selfhosted.md +++ b/docs/deploy-coolify-selfhosted.md @@ -89,8 +89,9 @@ reply is immediate. segment, so `.../api/gif` becomes `.../api/gacha`. It renders a 7-second `640x360` silent MP4 wish animation, posts `Wishing...` while it renders, and falls back to a text reply such as `★★★★★ Pizza` on the same failures. Every -option is equally likely, as with `/random`; a `*4` or `*5` tag only sets the -rarity shown in the animation and reply. Untagged options are 3★. +option is equally likely, as with `/random`; the rarity only sets what the +animation and reply show. Options are 5★ by default; prefix `4*` or `3*` to +lower one, e.g. `/gacha Pizza, 4* Pho, 3* Rice`. ## 1. MongoDB Atlas (M0) diff --git a/internal/modules/misc/gacha_command.go b/internal/modules/misc/gacha_command.go index dc3a159..d7ba7ad 100644 --- a/internal/modules/misc/gacha_command.go +++ b/internal/modules/misc/gacha_command.go @@ -17,17 +17,17 @@ import ( ) const ( - gachaUsage = "Usage: /gacha \nTag rarity with *4 or *5, e.g. /gacha Pizza*5, Pho*4, Rice" + gachaUsage = "Usage: /gacha \nOptions are 5* by default; prefix 4* or 3* to lower one, e.g. /gacha Pizza, 4* Pho, 3* Rice" gachaPlaceholder = "Wishing..." gachaFilename = "gacha.mp4" gachaMinRarity = 3 gachaMaxRarity = 5 - gachaDefaultRarity = gachaMinRarity + gachaDefaultRarity = gachaMaxRarity ) -// gachaRarityTag matches a trailing "*3", "*4", or "*5" rarity tag. -var gachaRarityTag = regexp.MustCompile(`\s*\*\s*([345])$`) +// gachaRarityTag matches a leading "3*", "4*", or "5*" rarity prefix. +var gachaRarityTag = regexp.MustCompile(`^([345])\s*\*\s*`) type gachaOption struct { Label string @@ -35,15 +35,15 @@ type gachaOption struct { } // parseGachaOptions splits comma-separated options and strips each one's -// rarity tag. Untagged options are 3★; options left blank after the tag is -// removed are dropped. +// rarity prefix. Unprefixed options are 5★; options left blank after the +// prefix is removed are dropped. func parseGachaOptions(arg string) []gachaOption { parts := splitWheelOptions(arg) out := make([]gachaOption, 0, len(parts)) for _, part := range parts { option := gachaOption{Label: part, Rarity: gachaDefaultRarity} if m := gachaRarityTag.FindStringSubmatchIndex(part); m != nil { - option.Label = strings.TrimSpace(part[:m[0]]) + option.Label = strings.TrimSpace(part[m[1]:]) option.Rarity = int(part[m[2]] - '0') } if option.Label != "" { @@ -62,7 +62,7 @@ func gachaCommand() modules.Command { return modules.Command{ Name: "gacha", Visibility: modules.VisibilityPublic, - Description: "Wish for one comma-separated option, Genshin style; tag *4 or *5 for rarity", + Description: "Wish for one comma-separated option, Genshin style; 5* by default, prefix 4* or 3*", Parameters: "", Handler: func(ctx context.Context, b *bot.Bot, update *models.Update) error { if update.Message == nil { diff --git a/internal/modules/misc/gacha_command_test.go b/internal/modules/misc/gacha_command_test.go index c4df169..500f6ac 100644 --- a/internal/modules/misc/gacha_command_test.go +++ b/internal/modules/misc/gacha_command_test.go @@ -14,14 +14,16 @@ import ( var mp4Bytes = []byte("\x00\x00\x00\x18ftypisom-remote") func TestParseGachaOptions(t *testing.T) { - got := parseGachaOptions(" Pizza*5, Pho *4 , Rice, Bun * 3, *5, , Cake*7, a*b ") + got := parseGachaOptions(" Pizza, 4* Pho , 3*Rice, 3 * Bun, 5* Cake, 5*, , 7* Tea, a*b, Pho 4* ") want := []gachaOption{ {Label: "Pizza", Rarity: 5}, {Label: "Pho", Rarity: 4}, {Label: "Rice", Rarity: 3}, {Label: "Bun", Rarity: 3}, - {Label: "Cake*7", Rarity: 3}, - {Label: "a*b", Rarity: 3}, + {Label: "Cake", Rarity: 5}, + {Label: "7* Tea", Rarity: 5}, + {Label: "a*b", Rarity: 5}, + {Label: "Pho 4*", Rarity: 5}, } if !reflect.DeepEqual(got, want) { t.Fatalf("parseGachaOptions() = %#v, want %#v", got, want) @@ -29,7 +31,7 @@ func TestParseGachaOptions(t *testing.T) { } func TestGacha_EmptyArgsRepliesUsage(t *testing.T) { - for _, text := range []string{"/gacha", "/gacha , ,", "/gacha *5"} { + for _, text := range []string{"/gacha", "/gacha , ,", "/gacha 5*"} { t.Run(text, func(t *testing.T) { rb, _ := installMisc(t, 999) rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, text)) @@ -43,7 +45,7 @@ func TestGacha_EmptyArgsRepliesUsage(t *testing.T) { func TestGacha_NotConfiguredRepliesWithStars(t *testing.T) { rb, _ := installMisc(t, 999) - rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, "/gacha Pizza*5")) + rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, "/gacha Pizza")) calls := rb.Sent() if len(calls) != 1 || calls[0].Method != "sendMessage" || calls[0].Text() != "★★★★★ Pizza" { @@ -68,7 +70,7 @@ func TestGacha_UsesRemoteAPIWhenConfigured(t *testing.T) { t.Setenv(wheelOfNamesAPITokenEnv, "remote-token") rb, _ := installMisc(t, 999) - rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, "/gacha Pho*4")) + rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, "/gacha 4* Pho")) if gotPath != "/api/gacha" { t.Fatalf("path = %q, want /api/gacha", gotPath) @@ -113,7 +115,7 @@ func TestGacha_RemoteFailureFallsBackToText(t *testing.T) { t.Setenv(wheelOfNamesAPIURLEnv, server.URL+"/api/gif") rb, _ := installMisc(t, 999) - rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, "/gacha Rice")) + rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, "/gacha 3* Rice")) sent := rb.Sent() if len(sent) != 2 { @@ -134,7 +136,7 @@ func TestGacha_SendAnimationFailureFallsBackToText(t *testing.T) { rb, _ := installMisc(t, 999) rb.FailMethod("sendAnimation", http.StatusInternalServerError, "") - rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, "/gacha Rice")) + rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, "/gacha 3* Rice")) call := rb.LastSent() if call.Method != "editMessageText" || call.Text() != "★★★ Rice" { @@ -151,7 +153,7 @@ func TestGacha_ForwardsMessageThreadID(t *testing.T) { t.Setenv(wheelOfNamesAPIURLEnv, server.URL+"/api/gif") rb, _ := installMisc(t, 999) - update := testutil.NewSupergroupMessage(-100, 7, "/gacha Rice") + update := testutil.NewSupergroupMessage(-100, 7, "/gacha 3* Rice") update.Message.MessageThreadID = 42 rb.Bot.ProcessUpdate(context.Background(), update)