fix(outbound): fail closed heartbeat reserved Telegram misses

This commit is contained in:
张贵萍0668001030
2026-06-19 14:02:39 +08:00
committed by Ayaan Zaidi
parent 116758e69a
commit 3092b4fd0d
2 changed files with 118 additions and 0 deletions

View File

@@ -1194,6 +1194,117 @@ describe("resolveSessionDeliveryTarget", () => {
expect(resolved.reason).toBe("dm-blocked");
});
it("resolves heartbeat reserved targets through directory before session routing", async () => {
const listGroups = vi
.fn()
.mockResolvedValue([{ kind: "group", id: "-1002458651455", name: "current" }]);
const listGroupsLive = vi.fn().mockResolvedValue([]);
setActivePluginRegistry(
createTargetsTestRegistry([
{
...createTestChannelPlugin({
id: "telegram",
label: "Telegram",
outbound: {
deliveryMode: "direct",
resolveTarget: ({ to }) =>
to
? { ok: true as const, to: to.trim() }
: { ok: false as const, error: new Error("target required") },
},
messaging: {
targetPrefixes: ["telegram", "tg"],
targetResolver: {
reservedLiterals: ["current", "self", "this", "me"],
hint: "<chatId>",
},
resolveOutboundSessionRoute: ({ target, resolvedTarget }) => ({
sessionKey: `main:telegram:group:${target}`,
baseSessionKey: `main:telegram:group:${target}`,
peer: { kind: resolvedTarget?.kind === "user" ? "direct" : "group", id: target },
chatType: resolvedTarget?.kind === "user" ? "direct" : "group",
from: `telegram:group:${target}`,
to: target,
}),
},
}),
directory: {
listGroups,
listGroupsLive,
},
},
]),
);
const resolved = await resolveHeartbeatDeliveryTargetWithSessionRoute({
cfg: {},
agentId: "main",
heartbeat: {
target: "telegram",
to: "current",
},
});
expect(resolved.channel).toBe("telegram");
expect(resolved.to).toBe("-1002458651455");
expect(listGroups).toHaveBeenCalled();
});
it("fails closed when a heartbeat reserved target misses the directory", async () => {
const listGroups = vi.fn().mockResolvedValue([]);
const listGroupsLive = vi.fn().mockResolvedValue([]);
setActivePluginRegistry(
createTargetsTestRegistry([
{
...createTestChannelPlugin({
id: "telegram",
label: "Telegram",
outbound: {
deliveryMode: "direct",
resolveTarget: ({ to }) =>
to
? { ok: true as const, to: to.trim() }
: { ok: false as const, error: new Error("target required") },
},
messaging: {
targetPrefixes: ["telegram", "tg"],
targetResolver: {
reservedLiterals: ["current", "self", "this", "me"],
hint: "<chatId>",
},
resolveOutboundSessionRoute: ({ target }) => ({
sessionKey: `main:telegram:group:${target}`,
baseSessionKey: `main:telegram:group:${target}`,
peer: { kind: "group", id: target },
chatType: "group",
from: `telegram:group:${target}`,
to: target,
}),
},
}),
directory: {
listGroups,
listGroupsLive,
},
},
]),
);
const resolved = await resolveHeartbeatDeliveryTargetWithSessionRoute({
cfg: {},
agentId: "main",
heartbeat: {
target: "telegram",
to: "current",
},
});
expect(resolved.channel).toBe("none");
expect(resolved.reason).toBe("no-target");
expect(listGroups).toHaveBeenCalled();
expect(listGroupsLive).toHaveBeenCalled();
});
it("keeps heartbeat route canonicalization best-effort when target resolution fails", async () => {
setActivePluginRegistry(
createTargetsTestRegistry([

View File

@@ -361,6 +361,13 @@ export async function resolveHeartbeatDeliveryTargetWithSessionRoute(params: {
})();
if (targetResolution?.ok) {
routeResolvedTarget = targetResolution.target;
} else if (targetResolution?.error.message.includes("Reserved target")) {
return buildNoHeartbeatDeliveryTarget({
reason: "no-target",
accountId: delivery.accountId,
lastChannel: delivery.lastChannel,
lastAccountId: delivery.lastAccountId,
});
}
if (routeResolvedTarget?.kind === "user" && heartbeat?.directPolicy === "block") {
return buildNoHeartbeatDeliveryTarget({