Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/channels/telegram/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,23 @@ export const privateBotCommands: BotCommand[] = [
{ command: "expires", description: "查看当前会话销毁策略" },
{ command: "whoami", description: "查看你的 Telegram 用户 ID" },
{ command: "history", description: "查看最近消息摘要,例如 /history 20" },
{ command: "search", description: "搜索历史消息,例如 /search 关键词" },
{ command: "note", description: "保存内部备注,不会外发" },
{ command: "notes", description: "查看最近内部备注,例如 /notes 10" },
{ command: "tag", description: "添加标签" },
{ command: "untag", description: "移除标签" },
{ command: "tags", description: "列出当前会话标签" },
{ command: "priority", description: "设置优先级 low/normal/high/urgent" },
{ command: "assign", description: "分配负责人" },
{ command: "mine", description: "查看分配给自己的会话" },
{ command: "close", description: "关闭会话" },
{ command: "open", description: "重新打开会话" },
{ command: "mute", description: "静音提醒,例如 /mute 2h" },
{ command: "ban", description: "封禁联系人" },
{ command: "unban", description: "解除封禁" },
{ command: "delete", description: "删除当前 Topic 并清理数据库,需 /delete confirm" },
{ command: "reset", description: "清空会话消息和草稿,需 /reset confirm" },
{ command: "audit", description: "查看本会话审计记录,例如 /audit 20" },
{ command: "draft", description: "AI 回复草稿:/draft view|send|discard 或 /draft 重新生成" },
{ command: "ai_on", description: "开启当前会话的 AI 草稿" },
{ command: "ai_off", description: "关闭当前会话的 AI 草稿" },
Expand All @@ -42,20 +45,23 @@ export const adminBotCommands: BotCommand[] = [
{ command: "expires", description: "查看当前会话销毁策略" },
{ command: "whoami", description: "查看你的 Telegram 管理员 ID" },
{ command: "history", description: "查看最近消息摘要,例如 /history 20" },
{ command: "search", description: "搜索历史消息,例如 /search 关键词" },
{ command: "note", description: "保存内部备注,不会外发" },
{ command: "notes", description: "查看最近内部备注,例如 /notes 10" },
{ command: "tag", description: "添加标签" },
{ command: "untag", description: "移除标签" },
{ command: "tags", description: "列出当前会话标签" },
{ command: "priority", description: "设置优先级 low/normal/high/urgent" },
{ command: "assign", description: "分配负责人" },
{ command: "mine", description: "查看分配给自己的会话" },
{ command: "close", description: "关闭会话" },
{ command: "open", description: "重新打开会话" },
{ command: "mute", description: "静音提醒,例如 /mute 2h" },
{ command: "ban", description: "封禁联系人" },
{ command: "unban", description: "解除封禁" },
{ command: "delete", description: "删除当前 Topic 并清理数据库,需 /delete confirm" },
{ command: "reset", description: "清空会话消息和草稿,需 /reset confirm" },
{ command: "audit", description: "查看本会话审计记录,例如 /audit 20" },
{ command: "draft", description: "AI 回复草稿:/draft view|send|discard 或 /draft 重新生成" },
{ command: "ai_on", description: "开启当前会话的 AI 草稿" },
{ command: "ai_off", description: "关闭当前会话的 AI 草稿" },
Expand Down
12 changes: 12 additions & 0 deletions src/channels/telegram/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,18 @@ export async function handlePrivateMessage(ctx: Context, deps: TelegramMessageDe
}
}

if (bundle.conversation.priority === "urgent" && bundle.conversation.assignedAdminId) {
try {
await ctx.api.sendMessage(
deps.config.TELEGRAM_MANAGEMENT_CHAT_ID,
`紧急消息提醒:负责人 ${bundle.conversation.assignedAdminId} 请关注本会话。`,
{ message_thread_id: topic.messageThreadId },
);
} catch {
// 提醒失败不影响主流程
}
}
Comment on lines +302 to +312

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

把紧急提醒放到“投递成功”分支里,而不是放在整个 try/catch 之后。

现在这段代码有两个相反但都错误的结果:如果 copyWithDelivery() 失败并走了 Line 280-Line 299 的错误处理,这里仍会继续发提醒;而如果首个 Topic 失效并走了 Line 220-Line 265 的重建成功路径,这里又永远执行不到。这样既不满足“成功投递后再提醒”,也会漏掉重建 Topic 后的成功投递。建议用 deliveredToManagement 标记,或把提醒抽到每个成功投递分支里,在任何早退之前执行。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/channels/telegram/messages.ts` around lines 302 - 312, The urgent
reminder logic in messages.ts is currently outside the successful delivery flow,
so it can run after copyWithDelivery() failures and miss the rebuilt-topic
success path. Move the telegram alert tied to bundle.conversation.priority and
assignedAdminId into the actual “delivered successfully” branches in the main
send flow, or gate it with a deliveredToManagement flag that is set only after a
successful send. Keep the reminder near ctx.api.sendMessage and
topic.messageThreadId handling so it executes only after successful delivery,
including after Topic rebuild recovery.


const draft = await deps.aiDrafts.generate(bundle.conversation.id, savedMessage.id);
if (draft.status === "ready") {
await ctx.api.sendMessage(deps.config.TELEGRAM_MANAGEMENT_CHAT_ID, `AI 草稿(不会自动发送):\n\n${draft.text}`, {
Expand Down
21 changes: 21 additions & 0 deletions test/core.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,24 @@ describe("conversation service", () => {
const empty = service.listByAssignee("999", 20);
assert.equal(empty.length, 0);
});

it("supports urgent priority with assignee for alert trigger", async () => {
const service = new ConversationService(handle.db, 30);
const bundle = await service.getOrCreateConversation({
platform: "telegram",
externalUserId: "700",
displayName: "UrgentUser",
});
service.setPriority(bundle.conversation.id, "urgent");
service.assign(bundle.conversation.id, "500");

const conv = await service.getConversation(bundle.conversation.id);
assert.ok(conv);
assert.equal(conv!.priority, "urgent");
assert.equal(conv!.assignedAdminId, "500");
// Alert condition: priority === "urgent" && assignedAdminId is truthy
assert.ok(conv!.priority === "urgent" && conv!.assignedAdminId !== null);
});
Comment on lines +736 to +752

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

这个用例没有覆盖新增的 Telegram 紧急提醒行为。

它只验证了 priorityassignedAdminId 的持久化,然后在测试里重复了一遍触发条件;但并没有调用 handlePrivateMessage(),也没有断言提醒是否真的发到了管理群对应 message_thread_id。这条链路正好是这次 PR 的核心改动,而且生产代码会吞掉提醒异常,单靠当前测试抓不到错线程/错时机/失败后仍提醒这类回归。建议补一个消息流测试,直接断言:成功投递后才发送提醒,并且提醒发到当前会话 Topic。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/core.test.ts` around lines 736 - 752, The current test only checks
priority and assignee persistence in ConversationService and does not exercise
the Telegram alert path. Update the test around handlePrivateMessage to simulate
an incoming Telegram message, then assert that the urgent reminder is actually
sent to the management group using the current conversation’s
message_thread_id/topic. Also verify the reminder is only emitted after
successful delivery and that no alert is sent on failure, since the production
flow swallows reminder exceptions.

});

describe("permissions and rate limits", () => {
Expand Down Expand Up @@ -824,6 +842,9 @@ describe("telegram helpers", () => {
assert.ok(adminBotCommands.some((command) => command.command === "ai_on"));
assert.ok(adminBotCommands.some((command) => command.command === "ai_off"));
assert.ok(adminBotCommands.some((command) => command.command === "help"));
assert.ok(adminBotCommands.some((command) => command.command === "search"));
assert.ok(adminBotCommands.some((command) => command.command === "mine"));
assert.ok(adminBotCommands.some((command) => command.command === "audit"));
assert.ok(adminBotCommands.every((command) => !command.command.startsWith("/")));
});

Expand Down