usernames: report reserved names as taken in the check paths too

account.checkUsername / channels.checkUsername / bots.checkUsername said a
reserved name was available and only updateUsername rejected it. Add the
blocklist check to peerUsernameAvailable (covers account + channel, both
backends) and to bots.Service.CheckUsername, so the client shows "username is
taken" immediately.
This commit is contained in:
Astra 2026-09-09 21:41:53 +01:00
parent 5b2f68c61e
commit 367f2be59c
7 changed files with 79 additions and 5 deletions

View file

@ -121,6 +121,7 @@ type Service struct {
messages store.MessageStore
blocker blockChecker
channels publicChannelUsernameResolver
reserved reservedUsernameChecker
stickers stickerSetCreator
installer userStickerSetInstaller
aiChat aiChatGenerator
@ -199,6 +200,21 @@ func WithBotAvatarStore(a botAvatarStore) Option {
// WithPublicChannelUsernameResolver 注入公开频道 username 查询能力,用于 bot
// username 预检,避免 bot 与 public channel 产生同名可见入口。
// reservedUsernameChecker reports whether a name is on the operator blocklist.
type reservedUsernameChecker interface {
IsReserved(ctx context.Context, usernameLower string) (bool, error)
}
// WithReservedUsernames wires the operator username blocklist so CheckUsername
// reports a reserved bot name as taken instead of available.
func WithReservedUsernames(c reservedUsernameChecker) Option {
return func(s *Service) {
if c != nil {
s.reserved = c
}
}
}
func WithPublicChannelUsernameResolver(c publicChannelUsernameResolver) Option {
return func(s *Service) {
if c != nil {
@ -533,6 +549,13 @@ func (s *Service) CheckUsername(ctx context.Context, ownerUserID int64, username
if !domain.ValidBotUsername(username) {
return false, domain.ErrBotUsernameInvalid
}
if s.reserved != nil {
if r, err := s.reserved.IsReserved(ctx, strings.ToLower(username)); err != nil {
return false, err
} else if r {
return false, nil
}
}
if _, found, err := s.users.ByUsername(ctx, username); err != nil {
return false, err
} else if found {