chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
313
internal/domain/bot.go
Normal file
313
internal/domain/bot.go
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxBotsPerOwner 是每个用户可创建的 bot 上限(对齐官方 appConfig bots_create_limit_default)。
|
||||
MaxBotsPerOwner = 20
|
||||
// BotTokenSecretLength 是 bot token 随机段长度(官方格式 <bot_id>:<35 位>)。
|
||||
BotTokenSecretLength = 35
|
||||
// BotUsernameSuffix 是 bot username 的强制后缀(大小写不敏感)。
|
||||
BotUsernameSuffix = "bot"
|
||||
// MaxBotNameLength 是 bot 显示名(first_name)上限。
|
||||
MaxBotNameLength = 64
|
||||
// MaxBotInlinePlaceholderLen 是 inline mode placeholder 上限(bots.inline_placeholder)。
|
||||
MaxBotInlinePlaceholderLen = 128
|
||||
// MaxBotAppShortNameLen 是 bot app short_name 上限。
|
||||
MaxBotAppShortNameLen = 64
|
||||
// MaxBotAppTitleLen 是 bot app title 上限。
|
||||
MaxBotAppTitleLen = 128
|
||||
// MaxBotAppDescriptionLen 是 bot app description 上限。
|
||||
MaxBotAppDescriptionLen = 512
|
||||
// MaxBotAppURLLen 是 bot app / attach menu webview URL 上限。
|
||||
MaxBotAppURLLen = 512
|
||||
// MaxBotAttachMenuPeerTypes 限制 attach menu peer_types 数量,避免无界响应。
|
||||
MaxBotAttachMenuPeerTypes = 8
|
||||
// MaxBotAttachMenuIcons 限制 attach menu icon 变体数量。
|
||||
MaxBotAttachMenuIcons = 8
|
||||
// MaxBotPreviewMedia 是单 bot main mini app preview media 上限。
|
||||
MaxBotPreviewMedia = 20
|
||||
// MaxBotRequestedPeerQuantity 限制 request peer 一次选择数量。
|
||||
MaxBotRequestedPeerQuantity = 10
|
||||
// MaxBotCustomMethodLen 限制 WebView custom method 名称长度。
|
||||
MaxBotCustomMethodLen = 128
|
||||
// MaxBotCustomMethodPayloadLen 限制 WebView custom method JSON 载荷长度。
|
||||
MaxBotCustomMethodPayloadLen = 4096
|
||||
)
|
||||
|
||||
// bot 业务错误。
|
||||
var (
|
||||
ErrBotTokenInvalid = errors.New("bot token invalid")
|
||||
ErrBotsTooMany = errors.New("bot create limit exceeded")
|
||||
ErrBotNameInvalid = errors.New("bot name invalid")
|
||||
ErrBotUsernameInvalid = errors.New("bot username invalid")
|
||||
ErrBotNotFound = errors.New("bot not found")
|
||||
// ErrBotCommandInvalid 表示命令名/描述非法(bots.setBotCommands)。
|
||||
ErrBotCommandInvalid = errors.New("bot command invalid")
|
||||
// ErrBotInfoInvalid 表示 setBotInfo 的 name/about/description 越界。
|
||||
ErrBotInfoInvalid = errors.New("bot info invalid")
|
||||
// ErrBotMenuButtonInvalid 表示 menu button 文本/URL 非法。
|
||||
ErrBotMenuButtonInvalid = errors.New("bot menu button invalid")
|
||||
// ErrBotInlinePlaceholderInvalid 表示 inline placeholder 为空以外的文本越界。
|
||||
ErrBotInlinePlaceholderInvalid = errors.New("bot inline placeholder invalid")
|
||||
// ErrBotAppInvalid 表示 bot app 元数据、URL 或 access hash 非法。
|
||||
ErrBotAppInvalid = errors.New("bot app invalid")
|
||||
// ErrBotAppShortNameInvalid 表示 bot app short_name 非法。
|
||||
ErrBotAppShortNameInvalid = errors.New("bot app short name invalid")
|
||||
// ErrBotAttachMenuInvalid 表示 attach menu catalog 或用户状态非法。
|
||||
ErrBotAttachMenuInvalid = errors.New("bot attach menu invalid")
|
||||
// ErrBotRequestedButtonInvalid 表示 request-peer button 上下文非法或已过期。
|
||||
ErrBotRequestedButtonInvalid = errors.New("bot requested button invalid")
|
||||
// ErrBotDownloadParamsInvalid 表示 mini app download 参数未通过安全策略。
|
||||
ErrBotDownloadParamsInvalid = errors.New("bot download params invalid")
|
||||
// ErrBotCustomMethodUnavailable 表示没有可完成 WebView custom method 的 bot 侧回答。
|
||||
ErrBotCustomMethodUnavailable = errors.New("bot custom method unavailable")
|
||||
// ErrBotSessionsNotRevoked 表示 token 已轮换但撤销已登录 session 失败
|
||||
//(token 不可回滚,调用方须告知用户重试以确保旧 session 终止)。
|
||||
ErrBotSessionsNotRevoked = errors.New("bot sessions not revoked")
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxBotCommands 是单个 bot 的命令上限(default scope)。
|
||||
MaxBotCommands = 100
|
||||
// MaxBotCommandLen 是命令名长度上限(不含前导 /)。
|
||||
MaxBotCommandLen = 32
|
||||
// MaxBotCommandDescriptionLen 是命令描述长度上限。
|
||||
MaxBotCommandDescriptionLen = 256
|
||||
// MaxBotAboutLen 是 bot about(users.about)长度上限。
|
||||
MaxBotAboutLen = 120
|
||||
// MaxBotDescriptionLen 是 bot description("What can this bot do?")长度上限。
|
||||
MaxBotDescriptionLen = 512
|
||||
// MaxBotMenuButtonTextLen / MaxBotMenuButtonURLLen 是菜单按钮文本/URL 上限。
|
||||
MaxBotMenuButtonTextLen = 64
|
||||
MaxBotMenuButtonURLLen = 512
|
||||
)
|
||||
|
||||
// BotCommand 是 bot 的一条命令(botInfo.commands 元素)。
|
||||
type BotCommand struct {
|
||||
Command string `json:"command"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// BotMenuButtonType 标识菜单按钮类型。
|
||||
type BotMenuButtonType int
|
||||
|
||||
const (
|
||||
// BotMenuButtonDefault 是默认(不显示特殊按钮,客户端按 commands 是否非空决定 '/' 圆钮)。
|
||||
BotMenuButtonDefault BotMenuButtonType = 0
|
||||
// BotMenuButtonCommands 显式让客户端展示命令菜单按钮。
|
||||
BotMenuButtonCommands BotMenuButtonType = 1
|
||||
// BotMenuButtonWebView 是带文本+URL 的 web view 按钮。
|
||||
BotMenuButtonWebView BotMenuButtonType = 2
|
||||
)
|
||||
|
||||
// BotMenuButton 是 bot 的菜单按钮(per-bot 全局;per-user 维度 P2 不做)。
|
||||
type BotMenuButton struct {
|
||||
Type BotMenuButtonType
|
||||
Text string
|
||||
URL string
|
||||
}
|
||||
|
||||
// BotInfoUpdate 是 bots.setBotInfo 的部分更新(name→users.first_name、
|
||||
// about→users.about、description→bots.description)。各 SetXxx 为 false 时不动该字段。
|
||||
type BotInfoUpdate struct {
|
||||
SetName bool
|
||||
Name string
|
||||
SetAbout bool
|
||||
About string
|
||||
SetDescription bool
|
||||
Description string
|
||||
}
|
||||
|
||||
// BotApp 是 mini app catalog 的一项。一个 bot 可拥有多个 app,ID/access_hash
|
||||
// 在创建后稳定,short_name 在同一 bot 下唯一。
|
||||
type BotApp struct {
|
||||
ID int64
|
||||
BotUserID int64
|
||||
ShortName string
|
||||
Title string
|
||||
Description string
|
||||
URL string
|
||||
PhotoID int64
|
||||
DocumentID int64
|
||||
AccessHash int64
|
||||
Hash int64
|
||||
Inactive bool
|
||||
RequestWriteAccess bool
|
||||
HasSettings bool
|
||||
Main bool
|
||||
}
|
||||
|
||||
// BotAppSettings 是 BotInfo.app_settings 的协议中立表示。
|
||||
type BotAppSettings struct {
|
||||
PlaceholderPath []byte
|
||||
BackgroundColor int
|
||||
BackgroundDarkColor int
|
||||
HeaderColor int
|
||||
HeaderDarkColor int
|
||||
HasBackgroundColor bool
|
||||
HasBackgroundDark bool
|
||||
HasHeaderColor bool
|
||||
HasHeaderDarkColor bool
|
||||
}
|
||||
|
||||
// BotAppPreviewMedia 是 main mini app preview media 的持久快照。
|
||||
type BotAppPreviewMedia struct {
|
||||
ID int64
|
||||
BotUserID int64
|
||||
AppID int64
|
||||
Position int
|
||||
PhotoID int64
|
||||
DocumentID int64
|
||||
}
|
||||
|
||||
// BotAttachMenuIconColor 描述 attach menu icon 的主题色覆盖。
|
||||
type BotAttachMenuIconColor struct {
|
||||
Name string
|
||||
Color int
|
||||
}
|
||||
|
||||
// BotAttachMenuIcon 描述 attach menu 的平台 icon 文档引用。
|
||||
type BotAttachMenuIcon struct {
|
||||
Name string
|
||||
DocumentID int64
|
||||
Colors []BotAttachMenuIconColor
|
||||
}
|
||||
|
||||
// BotAttachMenuBot 是全局 attach/side menu catalog 的一项。
|
||||
type BotAttachMenuBot struct {
|
||||
BotUserID int64
|
||||
AppID int64
|
||||
ShortName string
|
||||
Inactive bool
|
||||
HasSettings bool
|
||||
RequestWriteAccess bool
|
||||
ShowInAttachMenu bool
|
||||
ShowInSideMenu bool
|
||||
SideMenuDisclaimerNeeded bool
|
||||
PeerTypes []string
|
||||
Icons []BotAttachMenuIcon
|
||||
}
|
||||
|
||||
// BotAttachMenuState 是用户对某个 attach menu bot 的启用与写权限状态。
|
||||
type BotAttachMenuState struct {
|
||||
UserID int64
|
||||
BotUserID int64
|
||||
Enabled bool
|
||||
WriteAllowed bool
|
||||
}
|
||||
|
||||
// BotRequestedWebViewButton 是 bots.requestWebViewButton 创建的 request-peer 上下文。
|
||||
type BotRequestedWebViewButton struct {
|
||||
WebAppReqID string
|
||||
BotUserID int64
|
||||
UserID int64
|
||||
ButtonID int
|
||||
Text string
|
||||
PeerType string
|
||||
MaxQuantity int
|
||||
CreatedAt time.Time
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
// BotWebViewCustomMethodQuery 是 custom method 的 pending 记录。没有 bot 侧回答
|
||||
// registry 时只记录查询并显式返回 blocked 错误,避免假成功。
|
||||
type BotWebViewCustomMethodQuery struct {
|
||||
ID string
|
||||
BotUserID int64
|
||||
UserID int64
|
||||
CustomMethod string
|
||||
ParamsJSON string
|
||||
CreatedAt time.Time
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
// ValidBotCommandName 校验命令名:1-32 位小写字母/数字/下划线(不含前导 /)。
|
||||
func ValidBotCommandName(cmd string) bool {
|
||||
if len(cmd) == 0 || len(cmd) > MaxBotCommandLen {
|
||||
return false
|
||||
}
|
||||
for _, r := range cmd {
|
||||
switch {
|
||||
case r >= 'a' && r <= 'z', r >= '0' && r <= '9', r == '_':
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// BotProfile 是 bots 表一行:bot 账号的元数据与 token。
|
||||
// 显示名/username/about 复用 users 行,不在此重复。
|
||||
type BotProfile struct {
|
||||
BotUserID int64
|
||||
OwnerUserID int64
|
||||
TokenSecret string
|
||||
Description string
|
||||
Commands []BotCommand
|
||||
ChatHistory bool // privacy mode 关闭 = true(bot_chat_history flag)
|
||||
Nochats bool // true = 不允许加群(bot_nochats flag)
|
||||
InlineGeo bool
|
||||
InlinePlaceholder string
|
||||
MenuButton BotMenuButton
|
||||
HasMainApp bool
|
||||
HasAttachMenu bool
|
||||
HasPreviewMedias bool
|
||||
AppSettings *BotAppSettings
|
||||
}
|
||||
|
||||
// BotChatState 是内置 bot(当前仅 BotFather)与某用户的对话状态机持久态。
|
||||
type BotChatState struct {
|
||||
BotUserID int64
|
||||
UserID int64
|
||||
// Command 是进行中的主命令(newbot/token/revoke)。
|
||||
Command string `json:"command"`
|
||||
// Step 是主命令内的当前步骤(name/username/choose)。
|
||||
Step string `json:"step"`
|
||||
// Draft 暂存跨步骤的中间输入(如 newbot 已收的 name)。
|
||||
Draft map[string]string `json:"draft,omitempty"`
|
||||
}
|
||||
|
||||
// FormatBotToken 拼装完整 bot token(<bot_user_id>:<secret>)。
|
||||
func FormatBotToken(botUserID int64, secret string) string {
|
||||
return strconv.FormatInt(botUserID, 10) + ":" + secret
|
||||
}
|
||||
|
||||
// ParseBotToken 拆解完整 bot token。格式非法时 ok=false(不区分具体原因,
|
||||
// 调用方统一回 ACCESS_TOKEN_INVALID,避免泄漏存在性)。
|
||||
func ParseBotToken(token string) (botUserID int64, secret string, ok bool) {
|
||||
idPart, secret, found := strings.Cut(token, ":")
|
||||
if !found || idPart == "" || secret == "" {
|
||||
return 0, "", false
|
||||
}
|
||||
id, err := strconv.ParseInt(idPart, 10, 64)
|
||||
if err != nil || id <= 0 {
|
||||
return 0, "", false
|
||||
}
|
||||
return id, secret, true
|
||||
}
|
||||
|
||||
// ValidBotUsername 校验 bot username:5-32 位、字母开头、[A-Za-z0-9_]、
|
||||
// 以 bot 结尾(大小写不敏感)。BotFather 等种子账号不经此校验。
|
||||
func ValidBotUsername(username string) bool {
|
||||
if len(username) < 5 || len(username) > 32 {
|
||||
return false
|
||||
}
|
||||
for i, r := range username {
|
||||
switch {
|
||||
case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z':
|
||||
case r >= '0' && r <= '9', r == '_':
|
||||
if i == 0 {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return strings.HasSuffix(strings.ToLower(username), BotUsernameSuffix)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue