mirror of
https://github.com/tiennm99/tiennm99bot.git
synced 2026-10-20 00:15:50 +00:00
feat(config): resolve the bot username from BOT_USERNAME or getMe
The default sticker pack name and the lol User-Agent now follow whichever bot runs the code, so a bot account change needs no code edit. The default pack becomes miti99_by_<bot username>.
This commit is contained in:
1 parent
c18006fabc
commit
79104ef3d7
16 files changed
+198
-41
No files matched your search
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/tiennm99/miti99bot/internal/testutil"
|
||||
)
|
||||
|
||||
func TestResolveBotUsername(t *testing.T) {
|
||||
t.Run("configured value wins without calling getMe", func(t *testing.T) {
|
||||
rb := testutil.NewRecordingBot(t)
|
||||
if got := resolveBotUsername(context.Background(), rb.Bot, "envbot"); got != "envbot" {
|
||||
t.Errorf("got %q, want envbot", got)
|
||||
}
|
||||
for _, c := range rb.Sent() {
|
||||
if c.Method == "getMe" {
|
||||
t.Error("getMe called although BOT_USERNAME was set")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("falls back to getMe", func(t *testing.T) {
|
||||
rb := testutil.NewRecordingBot(t)
|
||||
rb.StubMethod("getMe", `{"id":1,"is_bot":true,"username":"apibot"}`)
|
||||
if got := resolveBotUsername(context.Background(), rb.Bot, ""); got != "apibot" {
|
||||
t.Errorf("got %q, want apibot", got)
|
||||
}
|
||||
})
|
||||
|
||||
// RecordingBot answers an unstubbed method with a bare `true`, which cannot
|
||||
// decode into a User: the failure must yield "" rather than stop startup.
|
||||
t.Run("getMe failure yields empty", func(t *testing.T) {
|
||||
rb := testutil.NewRecordingBot(t)
|
||||
if got := resolveBotUsername(context.Background(), rb.Bot, ""); got != "" {
|
||||
t.Errorf("got %q, want empty", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLoadConfig_BotUsername(t *testing.T) {
|
||||
t.Setenv("PORT", "8080")
|
||||
for in, want := range map[string]string{"": "", "examplebot": "examplebot", " @examplebot ": "examplebot"} {
|
||||
t.Setenv("BOT_USERNAME", in)
|
||||
if got := loadConfig().BotUsername; got != want {
|
||||
t.Errorf("BOT_USERNAME=%q: got %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
-1
@@ -17,6 +17,8 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/go-telegram/bot"
|
||||
|
||||
"github.com/tiennm99/miti99bot/internal/cron"
|
||||
"github.com/tiennm99/miti99bot/internal/deploynotify"
|
||||
"github.com/tiennm99/miti99bot/internal/log"
|
||||
@@ -164,8 +166,10 @@ func main() {
|
||||
log.Fatal("telegram bot init failed", "err", err)
|
||||
}
|
||||
|
||||
botUsername := resolveBotUsername(rootCtx, b, cfg.BotUsername)
|
||||
reg, err := modules.Build(cfg.Modules, factories(), provider, modules.BuildOptions{
|
||||
Bot: b,
|
||||
Bot: b,
|
||||
BotUsername: botUsername,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("module registry build failed", "err", err)
|
||||
@@ -355,11 +359,36 @@ func buildProvider(ctx context.Context, cfg config) (storage.Provider, func(), e
|
||||
}
|
||||
}
|
||||
|
||||
// botUsernameTimeout bounds the startup getMe. It is best-effort, so a slow
|
||||
// Telegram must not hold up the rest of startup for long.
|
||||
const botUsernameTimeout = 10 * time.Second
|
||||
|
||||
// resolveBotUsername returns the bot's username: BOT_USERNAME when set,
|
||||
// otherwise one getMe call. A failed getMe is logged and yields "" rather than
|
||||
// stopping startup; modules that need the username then learn it lazily or
|
||||
// do without.
|
||||
func resolveBotUsername(ctx context.Context, b *bot.Bot, configured string) string {
|
||||
if configured != "" {
|
||||
log.Info("bot username", "username", configured, "source", "BOT_USERNAME")
|
||||
return configured
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, botUsernameTimeout)
|
||||
defer cancel()
|
||||
me, err := b.GetMe(ctx)
|
||||
if err != nil || me == nil || me.Username == "" {
|
||||
log.Warn("getMe failed; bot username unknown until a module asks again", "err", err)
|
||||
return ""
|
||||
}
|
||||
log.Info("bot username", "username", me.Username, "source", "getMe")
|
||||
return me.Username
|
||||
}
|
||||
|
||||
// config is the process configuration read from the environment by loadConfig.
|
||||
type config struct {
|
||||
Port string
|
||||
TelegramBotToken string
|
||||
SourceCommit string // Coolify-injected commit SHA (runtime env) for deploynotify
|
||||
BotUsername string // optional; empty = ask Telegram via getMe at startup
|
||||
Modules []string
|
||||
BotOwnerID int64
|
||||
AdminUserIDs map[int64]bool
|
||||
@@ -392,6 +421,7 @@ func loadConfig() config {
|
||||
Port: port,
|
||||
TelegramBotToken: envMap["TELEGRAM_BOT_TOKEN"],
|
||||
SourceCommit: envMap["SOURCE_COMMIT"],
|
||||
BotUsername: strings.TrimPrefix(strings.TrimSpace(envMap["BOT_USERNAME"]), "@"),
|
||||
Modules: splitCSV(envMap["MODULES"]),
|
||||
BotOwnerID: parseInt64(envMap["OWNER_ID"]),
|
||||
AdminUserIDs: parseInt64Set(envMap["ADMIN_IDS"]),
|
||||
|
||||
Reference in new issue
Block a user