fix(tgbot): scope the add-client wizard to the admin, not the chat (#6604)

Two admins in one group chat shared one draft and one wizard step: clientDrafts and userStateStore were keyed by chat id alone, so the second admin's wizard opened on the first one's email and limits, and whichever of them tapped a control last decided what the other created.

Key both stores by (chat, user) instead. A private chat is unaffected: its two ids are equal, so the key matches what the chat alone used to be. A message with no sender (a channel post) keys to user 0, which no admin holds.

Fixes #6593.
This commit is contained in:
sdhfsl
2026-09-27 02:31:39 +08:00
committed by GitHub
parent 3b9ca47a4e
commit ee2ff48c81
7 changed files with 179 additions and 53 deletions
@@ -20,8 +20,8 @@ const clientDraftTestChatID = -9001
// Regression test: the draft is sent with ParseMode HTML, so Markdown markers
// were rendered literally and an unescaped value could break the whole message.
func TestClientDraftMessageRendersHTML(t *testing.T) {
draft := addClientDrafts.forChat(clientDraftTestChatID)
t.Cleanup(func() { addClientDrafts.reset(clientDraftTestChatID) })
draft := addClientDrafts.forActor(chatUser{chatID: clientDraftTestChatID, userID: 1})
t.Cleanup(func() { addClientDrafts.reset(chatUser{chatID: clientDraftTestChatID, userID: 1}) })
draft.email = "a@b.c"
draft.comment = "<b>promo</b> & <10 GB>"
@@ -67,10 +67,10 @@ func TestAddClientPromptsEscapeDraftValues(t *testing.T) {
url, texts := draftTexts(t)
swapTestBot(t, url)
draft := addClientDrafts.forChat(1)
draft := addClientDrafts.forActor(chatUser{chatID: 1, userID: 1})
origRunning := isRunning
t.Cleanup(func() {
addClientDrafts.reset(1)
addClientDrafts.reset(chatUser{chatID: 1, userID: 1})
isRunning = origRunning
})
isRunning = true