fix: register /cancel as a proper command

Previously /cancel was intercepted inside the 'unknown' branch, and the
comparison used the command WITHOUT the leading slash while parseCommand
returns it WITH one ('/cancel') — the check never matched, so /cancel
always fell through to the unknown-command reply, even with a pending
ask/approval.

Now a first-class command (menu included): closes the TG-side
answer/approval channel when one is pending, otherwise reports that
nothing is awaiting a reply.
This commit is contained in:
RRRosmontis
2026-09-02 16:07:53 +08:00
parent cde6979f2d
commit cecb655a4b
5 changed files with 34 additions and 22 deletions
+14 -12
View File
@@ -107,6 +107,7 @@ export class TelegramBridge {
{ command: 'stop', description: '中止当前正在运行的任务' }, { command: 'stop', description: '中止当前正在运行的任务' },
{ command: 'mission', description: '查看任务清单与完成情况' }, { command: 'mission', description: '查看任务清单与完成情况' },
{ command: 'new', description: '在当前工作区新开对话并附着' }, { command: 'new', description: '在当前工作区新开对话并附着' },
{ command: 'cancel', description: '关闭 TG 端作答/审批(Web 端仍可答)' },
{ command: 'help', description: '显示帮助' }, { command: 'help', description: '显示帮助' },
]).then(() => { ]).then(() => {
this.ctx.logger.info('dsh-telegram-channel: bot commands registered'); this.ctx.logger.info('dsh-telegram-channel: bot commands registered');
@@ -204,19 +205,20 @@ export class TelegramBridge {
case 'new': case 'new':
await this.newSessionHere(chatId); await this.newSessionHere(chatId);
return; return;
case 'unknown': case 'cancel': {
// /cancel closes the TG-side answer/approval channel; the UI side stays open. // /cancel closes the TG-side answer/approval channel; the UI side stays open.
if (parsed.command === 'cancel') { let cancelled = false;
let cancelled = false; if (this.pendingApprovalsTG.delete(String(chatId)))
if (this.pendingApprovalsTG.delete(String(chatId))) cancelled = true;
cancelled = true; if (this.pendingAsks.delete(String(chatId)))
if (this.pendingAsks.delete(String(chatId))) cancelled = true;
cancelled = true; if (cancelled)
if (cancelled) { await this.enqueueNotice(chatId, '已关闭 TG 端作答/审批(该问题仍可在 Web 端回答)');
await this.enqueueNotice(chatId, '已关闭 TG 端作答/审批(该问题仍可在 Web 端回答)'); else
return; await this.client.sendMessage(chatId, '当前没有待作答的提问或审批');
} return;
} }
case 'unknown':
await this.client.sendMessage(chatId, MSG.unknown(parsed.command)); await this.client.sendMessage(chatId, MSG.unknown(parsed.command));
return; return;
case 'plain': { case 'plain': {
+3
View File
@@ -53,6 +53,9 @@ export type ParsedCommand = {
} | { } | {
type: 'new'; type: 'new';
text: string; text: string;
} | {
type: 'cancel';
text: string;
} | { } | {
type: 'unknown'; type: 'unknown';
command: string; command: string;
+2
View File
@@ -95,6 +95,8 @@ export function parseCommand(text) {
case '/new': case '/new':
case '/create': case '/create':
return { type: 'new', text }; return { type: 'new', text };
case '/cancel':
return { type: 'cancel', text };
default: default:
return { type: 'unknown', command, text }; return { type: 'unknown', command, text };
} }
+12 -10
View File
@@ -218,6 +218,7 @@ export class TelegramBridge {
{ command: 'stop', description: '中止当前正在运行的任务' }, { command: 'stop', description: '中止当前正在运行的任务' },
{ command: 'mission', description: '查看任务清单与完成情况' }, { command: 'mission', description: '查看任务清单与完成情况' },
{ command: 'new', description: '在当前工作区新开对话并附着' }, { command: 'new', description: '在当前工作区新开对话并附着' },
{ command: 'cancel', description: '关闭 TG 端作答/审批(Web 端仍可答)' },
{ command: 'help', description: '显示帮助' }, { command: 'help', description: '显示帮助' },
]).then(() => { ]).then(() => {
this.ctx.logger.info('dsh-telegram-channel: bot commands registered') this.ctx.logger.info('dsh-telegram-channel: bot commands registered')
@@ -320,17 +321,18 @@ export class TelegramBridge {
case 'new': case 'new':
await this.newSessionHere(chatId) await this.newSessionHere(chatId)
return return
case 'unknown': case 'cancel': {
// /cancel closes the TG-side answer/approval channel; the UI side stays open. // /cancel closes the TG-side answer/approval channel; the UI side stays open.
if (parsed.command === 'cancel') { let cancelled = false
let cancelled = false if (this.pendingApprovalsTG.delete(String(chatId))) cancelled = true
if (this.pendingApprovalsTG.delete(String(chatId))) cancelled = true if (this.pendingAsks.delete(String(chatId))) cancelled = true
if (this.pendingAsks.delete(String(chatId))) cancelled = true if (cancelled)
if (cancelled) { await this.enqueueNotice(chatId, '已关闭 TG 端作答/审批(该问题仍可在 Web 端回答)')
await this.enqueueNotice(chatId, '已关闭 TG 端作答/审批(该问题仍可在 Web 端回答)') else
return await this.client.sendMessage(chatId, '当前没有待作答的提问或审批')
} return
} }
case 'unknown':
await this.client.sendMessage(chatId, MSG.unknown(parsed.command)) await this.client.sendMessage(chatId, MSG.unknown(parsed.command))
return return
case 'plain': { case 'plain': {
+3
View File
@@ -73,6 +73,7 @@ export type ParsedCommand =
| { type: 'stop'; text: string } | { type: 'stop'; text: string }
| { type: 'mission'; text: string } | { type: 'mission'; text: string }
| { type: 'new'; text: string } | { type: 'new'; text: string }
| { type: 'cancel'; text: string }
| { type: 'unknown'; command: string; text: string } | { type: 'unknown'; command: string; text: string }
| { type: 'plain'; text: string } | { type: 'plain'; text: string }
@@ -107,6 +108,8 @@ export function parseCommand(text: string): ParsedCommand {
case '/new': case '/new':
case '/create': case '/create':
return { type: 'new', text } return { type: 'new', text }
case '/cancel':
return { type: 'cancel', text }
default: default:
return { type: 'unknown', command, text } return { type: 'unknown', command, text }
} }