feat: add NFT usernames and bot verification (#22)

Implements collectible usernames, official verification workflows, and third-party bot verification after maintainer protocol and migration review.

The composite activity/moderation rating remains an admin-only read model; Telegram Stars Rating wire fields stay unset pending a dedicated official-semantics implementation.

Reviewed-Head: 2796345775ea0f908fb7734601e5e1dee4b653b9
Original-Head: fa082b892fd5180c9c9bc53c81c21cf5d250a75b

Co-authored-by: Egor Egorov <business.egor.sg@gmail.com>
This commit is contained in:
Egor Egorov 2026-07-27 20:18:00 +03:00 committed by GitHub
parent b0fd3976f1
commit fff8de783a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
169 changed files with 55769 additions and 282 deletions

View file

@ -61,6 +61,25 @@ func (r *Router) onMessagesGetBotCallbackAnswer(ctx context.Context, req *tg.Mes
}
botUserID := callback.BotUserID
// 内置进程内service bot 分支:@verifybot 这类 bot 没有 MTProto session、也没有
// Bot API 消费者,走下面的「推 updateBotCallbackQuery + 挂起 25s」必然超时回
// BOT_RESPONSE_TIMEOUT。因此在 registerContext / 推送之前同步问 responder点击本身
// 已由 resolveBotCallbackQuery 校验过(消息在请求者自己的盒里、对端正是该 bot、data
// 确实出现在该消息的 inline keyboard 中),此处只是把答案交给拥有该 bot 的实现。
// 不注册 query id外部 setBotCallbackAnswer 因此无法伪造/覆盖内置 bot 的应答。
if r.deps.ServiceBotCallbacks != nil && r.deps.ServiceBotCallbacks.HandlesBot(botUserID) {
ans, handled, err := r.deps.ServiceBotCallbacks.OnCallbackQuery(ctx, callback)
if err != nil {
r.log.Warn("service bot callback query",
zap.Int64("bot_user_id", botUserID), zap.Int64("user_id", userID), zap.Error(err))
return nil, internalErr()
}
if !handled {
return nil, dataInvalidErr()
}
return tgBotCallbackAnswer(ans), nil
}
queryID, pending, err := r.callbacks.registerContext(ctx, r.clock.Now(), botUserID, userID, botCallbackTimeout)
if err != nil {
r.log.Warn("register shared bot callback query", zap.Int64("bot_user_id", botUserID), zap.Error(err))