Treat anonymous admins as admins for /bridge, /thread etc.

When a group owner/admin enables "Remain anonymous", their messages
are sent from @GroupAnonymousBot with msg.SenderChat pointing to the
group itself. GetChatMember was then checking the bot's status (not
admin) and rejecting valid owners with "only for admins".

New helper isTgAnonymousAdmin(msg) returns true when SenderChat.ID
equals Chat.ID; in that case we skip the GetChatMember check and
treat the user as admin.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andrey Lugovskoy 2026-04-20 17:31:37 +04:00
parent 111e4a8c0d
commit adb5727c2a
3 changed files with 40 additions and 4 deletions

View file

@ -17,6 +17,16 @@ func isTgAdmin(memberStatus string) bool {
return memberStatus == "creator" || memberStatus == "administrator"
}
// isTgAnonymousAdmin returns true if the message was sent by an anonymous admin
// (owner/admin with "Remain anonymous" enabled). In this case msg.From is
// @GroupAnonymousBot and msg.SenderChat is the group itself.
func isTgAnonymousAdmin(msg *TGMessage) bool {
if msg == nil || msg.SenderChat == nil {
return false
}
return msg.SenderChat.ID == msg.Chat.ID
}
// isMaxGroup returns true if the MAX chat type indicates a group.
func isMaxGroup(chatType maxschemes.ChatType) bool {
return chatType == maxschemes.CHAT || chatType == maxschemes.CHANNEL

View file

@ -50,6 +50,27 @@ func TestIsTgAdmin(t *testing.T) {
}
}
func TestIsTgAnonymousAdmin(t *testing.T) {
chat := ChatInfo{ID: -100123, Type: "supergroup"}
tests := []struct {
name string
msg *TGMessage
want bool
}{
{"nil", nil, false},
{"no sender chat", &TGMessage{Chat: chat}, false},
{"different sender chat", &TGMessage{Chat: chat, SenderChat: &ChatInfo{ID: -999}}, false},
{"same sender chat (anonymous)", &TGMessage{Chat: chat, SenderChat: &ChatInfo{ID: chat.ID}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isTgAnonymousAdmin(tt.msg); got != tt.want {
t.Errorf("got %v, want %v", got, tt.want)
}
})
}
}
func TestIsMaxGroup(t *testing.T) {
tests := []struct {
name string

View file

@ -289,10 +289,15 @@ func (b *Bridge) listenTelegram(ctx context.Context) {
// Проверка прав админа в группах
isGroup := isTgGroup(msg.Chat.Type)
isAdmin := false
if isGroup && msg.From != nil {
status, err := b.tg.GetChatMember(ctx, msg.Chat.ID, msg.From.ID)
if err == nil {
isAdmin = isTgAdmin(status)
if isGroup {
if isTgAnonymousAdmin(msg) {
// Владелец/админ с "Remain anonymous" — шлёт от имени группы
isAdmin = true
} else if msg.From != nil {
status, err := b.tg.GetChatMember(ctx, msg.Chat.ID, msg.From.ID)
if err == nil {
isAdmin = isTgAdmin(status)
}
}
}