Skip to content

Commit 3217165

Browse files
fix(telegram): preserve inline buttons for empty capabilities (#96468)
Merged via squash. Prepared head SHA: 5e55b5d Co-authored-by: zhangguiping-xydt <[email protected]> Co-authored-by: vincentkoc <[email protected]> Reviewed-by: @vincentkoc
1 parent dbe2802 commit 3217165

5 files changed

Lines changed: 90 additions & 1 deletion

File tree

extensions/telegram/src/account-config.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ export function mergeTelegramAccountConfig(
9090
baseAllowFrom: base.allowFrom,
9191
accountAllowFrom: account.allowFrom,
9292
});
93+
const capabilities =
94+
Array.isArray(account.capabilities) && account.capabilities.length === 0
95+
? base.capabilities
96+
: (account.capabilities ?? base.capabilities);
9397

94-
return { ...base, ...account, allowFrom, groups };
98+
return { ...base, ...account, allowFrom, capabilities, groups };
9599
}

extensions/telegram/src/action-runtime.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,25 @@ describe("handleTelegramAction", () => {
17031703
expect(sendMessageTelegram).toHaveBeenCalled();
17041704
});
17051705

1706+
it("allows inline buttons when legacy capabilities are empty", async () => {
1707+
await handleTelegramAction(
1708+
{
1709+
action: "sendMessage",
1710+
to: "@testchannel",
1711+
content: "Choose",
1712+
presentation: {
1713+
blocks: [{ type: "buttons", buttons: [{ label: "Ok", value: "cmd:ok" }] }],
1714+
},
1715+
},
1716+
telegramConfig({ capabilities: [] }),
1717+
);
1718+
const call = mockCall(sendMessageTelegram, 0, "empty legacy capabilities");
1719+
expect(call[0]).toBe("@testchannel");
1720+
expect(requireRecord(call[2], "empty legacy capabilities options").buttons).toEqual([
1721+
[{ text: "Ok", callback_data: "cmd:ok" }],
1722+
]);
1723+
});
1724+
17061725
it("uses interactive button labels as fallback text when message text is omitted", async () => {
17071726
await handleTelegramAction(
17081727
{

extensions/telegram/src/channel-actions.contract.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,36 @@ describe("telegram actions contract", () => {
4343
expect(capabilities?.includes("richText")).toBe(expected);
4444
});
4545

46+
it("advertises inline buttons when legacy Telegram capabilities are empty", () => {
47+
const capabilities = telegramPlugin.agentPrompt?.messageToolCapabilities?.({
48+
cfg: {
49+
channels: {
50+
telegram: {
51+
botToken: "123:telegram-test-token",
52+
capabilities: [],
53+
},
54+
},
55+
} as OpenClawConfig,
56+
});
57+
58+
expect(capabilities).toContain("inlineButtons");
59+
});
60+
61+
it("does not advertise inline buttons for non-empty legacy Telegram capabilities without inlineButtons", () => {
62+
const capabilities = telegramPlugin.agentPrompt?.messageToolCapabilities?.({
63+
cfg: {
64+
channels: {
65+
telegram: {
66+
botToken: "123:telegram-test-token",
67+
capabilities: ["vision"],
68+
},
69+
},
70+
} as OpenClawConfig,
71+
});
72+
73+
expect(capabilities).not.toContain("inlineButtons");
74+
});
75+
4676
it("uses the selected Telegram account's rich text setting", () => {
4777
const capabilities = telegramPlugin.agentPrompt?.messageToolCapabilities?.({
4878
cfg: {

extensions/telegram/src/inline-buttons.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,39 @@ describe("resolveTelegramInlineButtonsScope (#75433 SecretRef tolerance)", () =>
109109
expect(isTelegramInlineButtonsEnabled({ cfg })).toBe(true);
110110
});
111111

112+
it("preserves the default inline-buttons scope when legacy capabilities are empty", () => {
113+
const cfg = {
114+
channels: {
115+
telegram: {
116+
botToken: { source: "exec", provider: "default", id: "telegram-token" },
117+
capabilities: [],
118+
},
119+
},
120+
} as unknown as OpenClawConfig;
121+
122+
expect(resolveTelegramInlineButtonsScope({ cfg })).toBe("allowlist");
123+
expect(isTelegramInlineButtonsEnabled({ cfg })).toBe(true);
124+
});
125+
126+
it("inherits the channel scope when an account legacy capabilities array is empty", () => {
127+
const cfg = {
128+
channels: {
129+
telegram: {
130+
capabilities: { inlineButtons: "off" },
131+
accounts: {
132+
ops: {
133+
botToken: "123:telegram-ops-token",
134+
capabilities: [],
135+
},
136+
},
137+
},
138+
},
139+
} as unknown as OpenClawConfig;
140+
141+
expect(resolveTelegramInlineButtonsScope({ cfg, accountId: "ops" })).toBe("off");
142+
expect(isTelegramInlineButtonsEnabled({ cfg, accountId: "ops" })).toBe(false);
143+
});
144+
112145
it('preserves configured "off" when botToken is an unresolved SecretRef', () => {
113146
const cfg = {
114147
channels: {

extensions/telegram/src/inline-buttons.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ export function resolveTelegramInlineButtonsScopeFromCapabilities(
4747
return DEFAULT_INLINE_BUTTONS_SCOPE;
4848
}
4949
if (Array.isArray(capabilities)) {
50+
if (capabilities.length === 0) {
51+
return DEFAULT_INLINE_BUTTONS_SCOPE;
52+
}
5053
const enabled = capabilities.some(
5154
(entry) => normalizeLowercaseStringOrEmpty(String(entry)) === "inlinebuttons",
5255
);

0 commit comments

Comments
 (0)