FILE / ScuroNeko/Laniakea
bot_config.go
Исходный файл и его история в репозитории.
(fix): compact payload escape, Draft.push validation, plugin logger ownership, worker StopAndWait, getChatLimiter deadlock, runner ctx-after-tick (refactor): remove NewPayload, buildSceneKey from sceneRuntime, unify ToJSON fallback (tests): compact round-trip, Draft.push state, RateLimiter.Cleanup eviction (doc): changelog v1.0.0 rewrite, NewCommand dual-use godoc
225 lines
6.7 KiB
Go
225 lines
6.7 KiB
Go
package laniakea
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
|
|
"git.scuroneko.dev/scuroneko/laniakea/tgapi"
|
|
"git.scuroneko.dev/scuroneko/sneklog/v2"
|
|
)
|
|
|
|
// AddPrefixes adds one or more command prefixes (e.g., "/", "!").
|
|
// The bot must have at least one prefix before any runtime entry point starts.
|
|
func (bot *Bot[T]) AddPrefixes(prefixes ...string) *Bot[T] {
|
|
if !bot.configMutable("AddPrefixes") {
|
|
return bot
|
|
}
|
|
bot.prefixes = append(bot.prefixes, prefixes...)
|
|
return bot
|
|
}
|
|
|
|
// SetDraftProvider replaces the default DraftProvider with a custom one.
|
|
// Useful for using LinearDraftIDGenerator to persist draft IDs across restarts.
|
|
func (bot *Bot[T]) SetDraftProvider(p *DraftProvider) *Bot[T] {
|
|
if !bot.configMutable("SetDraftProvider") {
|
|
return bot
|
|
}
|
|
bot.draftProvider = p
|
|
return bot
|
|
}
|
|
|
|
// GetDraftProvider returns the draft provider currently used by the bot.
|
|
func (bot *Bot[T]) GetDraftProvider() *DraftProvider {
|
|
return bot.draftProvider
|
|
}
|
|
|
|
// SetObserver sets an event observer for instrumentation.
|
|
func (bot *Bot[T]) SetObserver(observer Observer) *Bot[T] {
|
|
if !bot.configMutable("SetObserver") {
|
|
return bot
|
|
}
|
|
if observer == nil {
|
|
if bot.logger != nil {
|
|
bot.logger.Warn("SetObserver called with nil observer; instrumentation will be disabled")
|
|
}
|
|
bot.observer = nil
|
|
return bot
|
|
}
|
|
bot.observer = observer
|
|
return bot
|
|
}
|
|
|
|
// GetObserver returns the bot's event observer, or nil if no observer is set.
|
|
func (bot *Bot[T]) GetObserver() Observer {
|
|
return bot.observer
|
|
}
|
|
|
|
// SetSessionStore replaces the session store used for scene management.
|
|
func (bot *Bot[T]) SetSessionStore(store SessionStore) *Bot[T] {
|
|
if !bot.configMutable("SetSessionStore") {
|
|
return bot
|
|
}
|
|
if store == nil {
|
|
bot.logger.Warn("SetSessionStore called with nil store; nothing changed")
|
|
return bot
|
|
}
|
|
bot.sessionStore = store
|
|
return bot
|
|
}
|
|
|
|
// GetSessionStore returns the session store used for scene management.
|
|
func (bot *Bot[T]) GetSessionStore() SessionStore {
|
|
return bot.sessionStore
|
|
}
|
|
|
|
// SetSceneScopePriority sets the lookup order for resolving active scene sessions.
|
|
func (bot *Bot[T]) SetSceneScopePriority(priority []SceneScope) *Bot[T] {
|
|
if !bot.configMutable("SetSceneScopePriority") {
|
|
return bot
|
|
}
|
|
newPriority := make([]SceneScope, 0, 3)
|
|
for _, scope := range priority {
|
|
if scope != SceneScopeUser && scope != SceneScopeChat && scope != SceneScopeUserChat {
|
|
bot.logger.Warnln(fmt.Sprintf("invalid scene scope %v in priority list; ignoring", scope))
|
|
continue
|
|
}
|
|
if slices.Index(newPriority, scope) >= 0 {
|
|
bot.logger.Warnln(fmt.Sprintf("duplicate scope %v in scene scope priority; ignoring duplicates", scope))
|
|
continue
|
|
}
|
|
newPriority = append(newPriority, scope)
|
|
}
|
|
if len(newPriority) == 0 || len(newPriority) > 3 {
|
|
bot.logger.Warnln("scene scope priority must have 1 to 3 scopes; ignoring invalid input")
|
|
return bot
|
|
}
|
|
bot.sceneScopePriority = append([]SceneScope(nil), newPriority...)
|
|
return bot
|
|
}
|
|
|
|
// SetAppData injects shared application data into the bot.
|
|
//
|
|
// The data is accessible to commands, payload handlers, middleware, scenes,
|
|
// and runners through the generic type parameter T.
|
|
//
|
|
// For shared dependencies such as *sql.DB, prefer using a pointer type as T.
|
|
// Value-typed application data is supported, but the bot warns once because
|
|
// handlers receive T by value.
|
|
func (bot *Bot[T]) SetAppData(ctx T) *Bot[T] {
|
|
if !bot.configMutable("SetAppData") {
|
|
return bot
|
|
}
|
|
if !bot.warnedValueData && shouldWarnOnValueAppData[T]() && bot.logger != nil {
|
|
bot.logger.Warnln("app data uses a value type; shared dependencies should usually use a pointer type as T")
|
|
bot.warnedValueData = true
|
|
}
|
|
bot.appData = ctx
|
|
bot.hasAppData = true
|
|
return bot
|
|
}
|
|
|
|
// GetAppData returns the injected application data.
|
|
// If SetAppData was not called, it returns the zero value of T.
|
|
func (bot *Bot[T]) GetAppData() T { return bot.appData }
|
|
|
|
// SetUpdateTypes sets the list of update types the bot will request from Telegram.
|
|
// Overwrites any previously set types.
|
|
func (bot *Bot[T]) SetUpdateTypes(t ...tgapi.UpdateType) *Bot[T] {
|
|
if !bot.configMutable("UpdateTypes") {
|
|
return bot
|
|
}
|
|
bot.updateTypes = make([]tgapi.UpdateType, 0)
|
|
bot.updateTypes = append(bot.updateTypes, t...)
|
|
return bot
|
|
}
|
|
|
|
// AddUpdateType adds one or more update types to the list.
|
|
// Does not overwrite existing types.
|
|
func (bot *Bot[T]) AddUpdateType(t ...tgapi.UpdateType) *Bot[T] {
|
|
if !bot.configMutable("AddUpdateType") {
|
|
return bot
|
|
}
|
|
bot.updateTypes = append(bot.updateTypes, t...)
|
|
return bot
|
|
}
|
|
|
|
// GetUpdateTypes returns the list of update types the bot is configured to receive.
|
|
func (bot *Bot[T]) GetUpdateTypes() []tgapi.UpdateType {
|
|
return append([]tgapi.UpdateType(nil), bot.updateTypes...)
|
|
}
|
|
|
|
// SetPayloadType sets the default payload encoding type used for callback data.
|
|
// JSON stores payload as a string: `{"cmd":"command","args":[...]}`.
|
|
// Base64 stores the same JSON encoded as a Base64URL string.
|
|
// InlineKeyboard.SetPayloadType may override this value for an individual keyboard.
|
|
func (bot *Bot[T]) SetPayloadType(t BotPayloadType) *Bot[T] {
|
|
if !bot.configMutable("SetPayloadType") {
|
|
return bot
|
|
}
|
|
bot.payloadType = t
|
|
return bot
|
|
}
|
|
|
|
// GetPayloadType returns the bot's default callback payload encoding type.
|
|
func (bot *Bot[T]) GetPayloadType() BotPayloadType { return bot.payloadType }
|
|
|
|
// SetStrictPayloadType enables or disables strict callback payload decoding.
|
|
// When enabled, callback payloads must match the bot's default payload type.
|
|
func (bot *Bot[T]) SetStrictPayloadType(strict bool) *Bot[T] {
|
|
if !bot.configMutable("SetStrictPayloadType") {
|
|
return bot
|
|
}
|
|
bot.strictPayloadType = strict
|
|
return bot
|
|
}
|
|
|
|
// SetErrorTemplate sets the format string for error messages sent to users.
|
|
// Use "%s" to insert the error message.
|
|
// Example: "❌ Error: %s" → "❌ Error: Command not found".
|
|
func (bot *Bot[T]) SetErrorTemplate(s string) *Bot[T] {
|
|
if !bot.configMutable("ErrorTemplate") {
|
|
return bot
|
|
}
|
|
bot.errorTemplate = s
|
|
return bot
|
|
}
|
|
|
|
// SetDebug enables or disables debug logging.
|
|
func (bot *Bot[T]) SetDebug(debug bool) *Bot[T] {
|
|
bot.debug = debug
|
|
level := sneklog.FATAL
|
|
if debug {
|
|
level = sneklog.DEBUG
|
|
}
|
|
|
|
bot.logger.SetLevel(level)
|
|
if bot.requestLogger != nil {
|
|
bot.requestLogger.SetLevel(level)
|
|
}
|
|
for _, p := range bot.plugins {
|
|
if p.logger == nil {
|
|
continue
|
|
}
|
|
p.logger.SetLevel(level)
|
|
}
|
|
return bot
|
|
}
|
|
|
|
// SetL10n sets the localization (i18n) provider for the bot.
|
|
//
|
|
// The L10n instance must be pre-populated with translations.
|
|
// Translations are accessed via Bot.L10n(lang, key).
|
|
//
|
|
// Replaces any previously set L10n instance.
|
|
func (bot *Bot[T]) SetL10n(l *L10n) *Bot[T] {
|
|
if !bot.configMutable("SetL10n") {
|
|
return bot
|
|
}
|
|
if l == nil {
|
|
bot.logger.Warn("SetL10n called with nil L10n; localization will not change")
|
|
return bot
|
|
}
|
|
bot.l10n = l
|
|
return bot
|
|
}
|