Interactive /mybots menu for @BotFather
Button-driven bot management: paginated picker, per-bot API token / revoke, Edit Bot (name/description/about/commands/botpic), Bot Settings toggles (inline/groups/privacy), and delete. Navigation edits the menu message in place via a new editServiceBotMessage helper. Edit Botpic accepts a photo the user sends to @BotFather and sets it as the bot's profile photo (new files.SetAvatarFromExistingPhoto, wired through bots.SetBotUserpic / WithBotAvatarStore); photos.uploadProfilePhoto does not accept a bot target so this is the only route.
This commit is contained in:
parent
1ebf8ddb5a
commit
11142f74c5
10 changed files with 1467 additions and 26 deletions
|
|
@ -6,6 +6,7 @@ package bots
|
|||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
|
@ -76,6 +77,13 @@ type verificationApplications interface {
|
|||
Application(ctx context.Context, applicationID int64) (domain.VerificationApplication, error)
|
||||
}
|
||||
|
||||
// botAvatarStore renders an already-stored photo (one a user sent to @BotFather)
|
||||
// into a bot's profile photo. app/files.Service satisfies it. It is a narrow port
|
||||
// because a service bot must not reach into the file layer for anything else.
|
||||
type botAvatarStore interface {
|
||||
SetAvatarFromExistingPhoto(ctx context.Context, ownerType domain.PeerType, ownerID, sourcePhotoID int64, date int) (domain.Photo, error)
|
||||
}
|
||||
|
||||
// The third-party verification ports live in verifierbot.go
|
||||
// (customVerifications, verifierBotTargets): they are the built-in @verifierbot's
|
||||
// only way to reach the feature, and are kept next to the dialog that uses them.
|
||||
|
|
@ -120,6 +128,7 @@ type Service struct {
|
|||
verifierTargets verifierBotTargets
|
||||
gifCatalog gifCatalogSource
|
||||
telegramLogin *telegramloginapp.Service
|
||||
botAvatar botAvatarStore
|
||||
hooks RouterHooks
|
||||
textDrafts TextDraftPusher
|
||||
userCache store.UserCache
|
||||
|
|
@ -176,6 +185,17 @@ func WithBlockChecker(c blockChecker) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// WithBotAvatarStore injects the ability to set a bot's profile photo from a
|
||||
// photo a user sent to @BotFather ("Edit Botpic"). Absent it, that action reports
|
||||
// that it is unavailable on this server.
|
||||
func WithBotAvatarStore(a botAvatarStore) Option {
|
||||
return func(s *Service) {
|
||||
if a != nil {
|
||||
s.botAvatar = a
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithPublicChannelUsernameResolver 注入公开频道 username 查询能力,用于 bot
|
||||
// username 预检,避免 bot 与 public channel 产生同名可见入口。
|
||||
func WithPublicChannelUsernameResolver(c publicChannelUsernameResolver) Option {
|
||||
|
|
@ -902,6 +922,30 @@ func (s *Service) SetPrivacy(ctx context.Context, botUserID int64, enabled bool)
|
|||
return version, nil
|
||||
}
|
||||
|
||||
// ErrBotUserpicUnsupported reports that this server has no file layer wired for
|
||||
// setting a bot's profile photo (WithBotAvatarStore was not supplied).
|
||||
var ErrBotUserpicUnsupported = errors.New("bot profile photo is not supported by this server")
|
||||
|
||||
// SetBotUserpic makes an already-stored photo (one a user sent to @BotFather) the
|
||||
// bot's current profile photo. sourcePhotoID is a message photo id; the file
|
||||
// layer re-renders it into the avatar size set.
|
||||
func (s *Service) SetBotUserpic(ctx context.Context, botUserID, sourcePhotoID int64) error {
|
||||
if s == nil || botUserID == 0 {
|
||||
return domain.ErrBotNotFound
|
||||
}
|
||||
if s.botAvatar == nil {
|
||||
return ErrBotUserpicUnsupported
|
||||
}
|
||||
if sourcePhotoID <= 0 {
|
||||
return domain.ErrPhotoInvalid
|
||||
}
|
||||
if _, err := s.botAvatar.SetAvatarFromExistingPhoto(ctx, domain.PeerTypeUser, botUserID, sourcePhotoID, int(s.now().Unix())); err != nil {
|
||||
return err
|
||||
}
|
||||
s.invalidateBotReadCaches(ctx, botUserID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanSendMessage reports whether botUserID has explicit permission to initiate
|
||||
// direct messages with userID.
|
||||
func (s *Service) CanSendMessage(ctx context.Context, userID, botUserID int64) (bool, error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue