Skip to content

Commit e2c30d9

Browse files
committed
fix(telegram): preserve inline-keyboard payloads when suppressing NO_REPLY
1 parent bc77d95 commit e2c30d9

3 files changed

Lines changed: 30 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai
1010

1111
### Fixes
1212

13+
- Telegram/silent reply: suppress the `NO_REPLY` sentinel inside `sendMessageTelegram` before any API call, mirroring the existing Slack guard, while preserving sends that carry an inline keyboard or media payload so structured replies still post. (#68304) Thanks @1aifanatic.
1314
- Agents/bootstrap: resolve bootstrap from workspace truth instead of stale session transcript markers, keep embedded bootstrap instructions on a hidden user-context prelude, suppress normal `/new` and `/reset` greetings while `BOOTSTRAP.md` is still pending, and make the embedded runner read the bootstrap ritual before replying normally.
1415
- Onboarding/non-interactive: preserve existing gateway auth tokens during re-onboard so active local gateway clients are not disconnected by an implicit token rotation. (#67821) Thanks @BKF-Gitty.
1516
- Gateway/hello-ok: always report negotiated auth metadata for successful shared-auth handshakes, including control-ui bypass coverage when no device token is issued. (#67810) Thanks @BunsDev.

extensions/telegram/src/send.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,33 @@ describe("sendMessageTelegram", () => {
17641764
expect(sendMessage.mock.calls.map((call) => String(call[1] ?? "")).join("")).toBe(plainText);
17651765
expect(res.messageId).toBe("96");
17661766
});
1767+
1768+
it("suppresses NO_REPLY sentinel before any API call when no payload is attached", async () => {
1769+
const sendMessage = vi.fn();
1770+
const api = { sendMessage } as unknown as { sendMessage: typeof sendMessage };
1771+
1772+
const res = await sendMessageTelegram("123", "NO_REPLY", { token: "tok", api });
1773+
1774+
expect(sendMessage).not.toHaveBeenCalled();
1775+
expect(res).toEqual({ messageId: "suppressed", chatId: "" });
1776+
});
1777+
1778+
it("still sends NO_REPLY when inline buttons are attached so the keyboard payload is preserved", async () => {
1779+
const sendMessage = vi.fn().mockResolvedValue({ message_id: 7, chat: { id: "123" } });
1780+
const api = { sendMessage } as unknown as { sendMessage: typeof sendMessage };
1781+
1782+
const res = await sendMessageTelegram("123", "NO_REPLY", {
1783+
token: "tok",
1784+
api,
1785+
buttons: [[{ text: "OK", callback_data: "ok" }]],
1786+
});
1787+
1788+
expect(sendMessage).toHaveBeenCalledTimes(1);
1789+
expect(sendMessage.mock.calls[0][2]?.reply_markup).toEqual({
1790+
inline_keyboard: [[{ text: "OK", callback_data: "ok" }]],
1791+
});
1792+
expect(res.messageId).toBe("7");
1793+
});
17671794
});
17681795

17691796
describe("reactMessageTelegram", () => {

extensions/telegram/src/send.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,8 @@ export async function sendMessageTelegram(
620620
opts: TelegramSendOpts = {},
621621
): Promise<TelegramSendResult> {
622622
const trimmedText = normalizeOptionalString(text) ?? "";
623-
if (isSilentReplyText(trimmedText) && !opts.mediaUrl) {
623+
const hasButtons = Array.isArray(opts.buttons) && opts.buttons.length > 0;
624+
if (isSilentReplyText(trimmedText) && !opts.mediaUrl && !hasButtons) {
624625
logVerbose("telegram send: suppressed NO_REPLY token before API call");
625626
return { messageId: "suppressed", chatId: "" };
626627
}

0 commit comments

Comments
 (0)