feat(ask): 键盘常态提供 ✏️ 输入自定义答案 按钮

- 每个带选项的提问键盘固定追加第三行 ✏️ 输入自定义答案
- 点击后该题进入待文本状态(textWanted),丢弃该题已点选的按钮项
- 下一条文本消息自动填充最先待文本的题;带 1:/2: 前缀的作答同样可清除待文本状态
- 已实测:✏️ → 发送 111 → custom 答案正确回流
This commit is contained in:
root
2026-09-17 11:21:30 +08:00
parent 373abf586d
commit 2c50a35795
2 changed files with 43 additions and 4 deletions
+22 -2
View File
@@ -101,6 +101,8 @@ interface TgPendingAsk {
kbSel: Map<number, Set<number>>
/** question index → answer parsed from a text reply (letters or custom). */
textAnswers: Map<number, { id?: string; selected: string[]; custom?: string }>
/** questions the user chose to answer with free text (✏️ button): the next text reply fills them. */
textWanted: Set<number>
}
/** chatId → pending approval request mirrored to Telegram. */
@@ -1636,6 +1638,7 @@ export class TelegramBridge {
at: Date.now(),
kbSel: new Map(),
textAnswers: new Map(),
textWanted: new Set(),
}
this.pendingAsks.set(String(chatId), pending)
void this.deliverAskPrompt(Number(chatId), pending)
@@ -1672,6 +1675,8 @@ export class TelegramBridge {
text: `${j + 1}. ${o.label}`.slice(0, 48),
callback_data: `${ASK_CB}${i}:${j}`,
}]),
// Standing third choice: free-text answer, always available.
[{ text: '✏️ 输入自定义答案', callback_data: `${ASK_CB}${i}:custom` }],
...(q.multiSelect ? [[{ text: '✅ 提交所选', callback_data: `${ASK_CB}${i}:ok` }]] : []),
],
}
@@ -1734,8 +1739,14 @@ export class TelegramBridge {
else parts.push([null, line])
}
if (parts.some(([idx]) => idx === null)) {
await this.enqueueNotice(chatId, `共 ${questions.length} 个问题,请每行用 1:/2: 前缀分别作答`)
return
// ✏️ free-text route: a question awaiting text absorbs the whole message.
const target = [...pending.textWanted].sort((a, b) => a - b)[0]
if (target !== undefined) {
parts = [[target, text.trim()]]
} else {
await this.enqueueNotice(chatId, `共 ${questions.length} 个问题,请每行用 1:/2: 前缀分别作答`)
return
}
}
}
for (const [idx, raw] of parts) {
@@ -1764,6 +1775,7 @@ export class TelegramBridge {
} else {
pending.textAnswers.set(idx, { id: q.id, selected: [], custom: cleaned })
}
pending.textWanted.delete(idx)
}
// Keyboard selections take precedence per question; resolve when complete.
if (!this.finalizeAskIfComplete(chatId, pending)) {
@@ -1789,6 +1801,14 @@ export class TelegramBridge {
return
}
const opts = Array.isArray(q.options) ? q.options : []
if (action === 'custom') {
// Free-text path: discard button selections for this question and wait
// for the user's next text message (routed via textWanted).
pending.kbSel.delete(qi)
pending.textWanted.add(qi)
await this.client.answerCallbackQuery(cq.id, '请直接发送你的答案文本')
return
}
if (action === 'ok') {
if (!pending.kbSel.get(qi)?.size) {
await this.client.answerCallbackQuery(cq.id, '请先点选选项')