fix(telegram): grant in-chat members regardless of reported can_send
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m18s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m18s
The CanSendMessages loop-guard skipped exactly the stuck case — a restricted member whose chat_member event reports can_send=true yet who cannot actually write. Replace it with a precise loop guard (skip only the bot's own restrict action, i.e. the update whose performer is the bot) and grant any eligible in-chat member (member or restricted) otherwise. Also log the new member's can_send, is_member and the actor id for full visibility.
This commit is contained in:
@@ -3,6 +3,7 @@ package bot
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -13,7 +14,10 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const testChatID = 555
|
||||
const (
|
||||
testChatID = 555
|
||||
botSelfID = 111111 // the bot's own id in tests (for the loop guard)
|
||||
)
|
||||
|
||||
// chatAPI is a fake Bot API for the chat-gating tests: it answers getMe, returns a
|
||||
// scripted getChatMember status, and records restrictChatMember calls.
|
||||
@@ -59,6 +63,7 @@ func newChatBot(t *testing.T, api *chatAPI) *Bot {
|
||||
if err != nil {
|
||||
t.Fatalf("new bot: %v", err)
|
||||
}
|
||||
b.botID = botSelfID // normally set at startup; the chat_member updates default actor 0 != this
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -109,14 +114,22 @@ func TestHandleChatMemberGrantsEligibleMemberJoin(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleChatMemberGrantsRestrictedMember(t *testing.T) {
|
||||
// A default-deny group reports a present member as restricted with no send right —
|
||||
// the real-world case. Both a fresh join (left->restricted) and an already-present
|
||||
// restricted member (restricted->restricted) must be granted when eligible.
|
||||
for _, oldType := range []models.ChatMemberType{models.ChatMemberTypeLeft, models.ChatMemberTypeRestricted} {
|
||||
t.Run(string(oldType), func(t *testing.T) {
|
||||
// A default-deny group reports a present member as restricted — the real-world case.
|
||||
// A fresh join (left->restricted) or an already-present restricted member
|
||||
// (restricted->restricted), and regardless of the reported can_send (which can be
|
||||
// misleading), an eligible member is granted.
|
||||
for _, tc := range []struct {
|
||||
old models.ChatMemberType
|
||||
canSend bool
|
||||
}{
|
||||
{models.ChatMemberTypeLeft, false},
|
||||
{models.ChatMemberTypeRestricted, false},
|
||||
{models.ChatMemberTypeRestricted, true},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("%s_cansend=%v", tc.old, tc.canSend), func(t *testing.T) {
|
||||
api := &chatAPI{}
|
||||
b := eligibleBot(t, api, true, nil)
|
||||
b.handleChatMember(context.Background(), restrictedUpdate(testChatID, 777, oldType, false))
|
||||
b.handleChatMember(context.Background(), restrictedUpdate(testChatID, 777, tc.old, tc.canSend))
|
||||
if len(api.restricts) != 1 || !api.restricts[0].canSend {
|
||||
t.Fatalf("restricts = %+v, want one grant for a restricted member", api.restricts)
|
||||
}
|
||||
@@ -124,14 +137,16 @@ func TestHandleChatMemberGrantsRestrictedMember(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleChatMemberSkipsAlreadyAllowed(t *testing.T) {
|
||||
// The bot's own grant re-fires a chat_member event (restricted, can_send=true); it
|
||||
// must not loop into another grant.
|
||||
func TestHandleChatMemberSkipsBotsOwnAction(t *testing.T) {
|
||||
// The bot's own grant re-fires a chat_member update performed by the bot; it must be
|
||||
// skipped so a grant never loops into another grant.
|
||||
api := &chatAPI{}
|
||||
b := eligibleBot(t, api, true, nil)
|
||||
b.handleChatMember(context.Background(), restrictedUpdate(testChatID, 777, models.ChatMemberTypeRestricted, true))
|
||||
upd := restrictedUpdate(testChatID, 777, models.ChatMemberTypeRestricted, false)
|
||||
upd.From = models.User{ID: botSelfID}
|
||||
b.handleChatMember(context.Background(), upd)
|
||||
if len(api.restricts) != 0 {
|
||||
t.Fatalf("restricts = %+v, want none for an already-allowed member (no loop)", api.restricts)
|
||||
t.Fatalf("restricts = %+v, want none for the bot's own action (no loop)", api.restricts)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user