惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
The GitHub Blog
The GitHub Blog
Vercel News
Vercel News
D
DataBreaches.Net
MongoDB | Blog
MongoDB | Blog
H
Help Net Security
小众软件
小众软件
美团技术团队
T
The Blog of Author Tim Ferriss
爱范儿
爱范儿
D
Docker
Martin Fowler
Martin Fowler
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
Blog — PlanetScale
Blog — PlanetScale
H
Hackread – Cybersecurity News, Data Breaches, AI and More
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
V2EX
S
SegmentFault 最新的问题
云风的 BLOG
云风的 BLOG
B
Blog
雷峰网
雷峰网
The Cloudflare Blog

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(telegram): send interactive fallback replies · opencl...
vincentkoc · 2026-05-04 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -63,6 +63,7 @@ Docs: https://docs.openclaw.ai

6363

- Feishu: use the shared channel progress formatter for streaming-card tool status lines, including raw command/detail output and message-tool filtering. Thanks @vincentkoc.

6464

- Mattermost: use the shared progress draft formatter for tool status previews, including raw command/detail output when `agents.defaults.toolProgressDetail: "raw"` is enabled. Thanks @vincentkoc.

6565

- Mattermost: suppress standalone default tool-progress messages while draft previews are active, including when draft tool lines are disabled. Thanks @vincentkoc.

66+

- Telegram: deliver button-only interactive replies by sending the shared fallback button-label text with the inline keyboard instead of dropping the reply as empty. Thanks @vincentkoc.

6667

- OpenAI Codex: honor `auth.order.openai-codex` when starting app-server clients without an explicit auth profile, so status/model probes and implicit startup use the configured Codex account instead of falling back to the default profile. Thanks @vincentkoc.

6768

- OpenAI Codex: let SSRF-guarded provider requests inherit OpenClaw's undici IPv4/IPv6 fallback policy, so ChatGPT-backed Codex runs recover on IPv4-working hosts when DNS still returns unreachable IPv6 addresses. Fixes #76857. Thanks @jplavoiemtl and @SymbolStar.

6869

- Gateway/systemd: preserve operator-added secrets in the Gateway env file across re-stage while clearing OpenClaw-managed keys (such as `OPENCLAW_GATEWAY_TOKEN`) so a fresh staging value is never shadowed by a stale env-file copy; operator secrets are also retained when the state-dir `.env` is empty. Fixes #76860. Thanks @hclsys.

Original file line numberDiff line numberDiff line change

@@ -36,6 +36,7 @@ import {

3636

renderTelegramHtmlText,

3737

wrapFileReferencesInHtml,

3838

} from "../format.js";

39+

import { resolveTelegramInteractiveTextFallback } from "../interactive-fallback.js";

3940

import { buildInlineKeyboard } from "../send.js";

4041

import { resolveTelegramVoiceSend } from "../voice.js";

4142

import {

@@ -751,7 +752,17 @@ export async function deliverReplies(params: {

751752

? [reply.mediaUrl]

752753

: [];

753754

const hasMedia = mediaList.length > 0;

754-

if (!reply?.text && !hasMedia) {

755+

const resolvedReplyText =

756+

resolveTelegramInteractiveTextFallback({

757+

text: reply?.text,

758+

interactive: reply?.interactive,

759+

}) ??

760+

reply?.text ??

761+

"";

762+

if (reply && resolvedReplyText !== (reply.text ?? "")) {

763+

reply = { ...reply, text: resolvedReplyText };

764+

}

765+

if (!resolvedReplyText && !hasMedia) {

755766

if (reply?.audioAsVoice) {

756767

logVerbose("telegram reply has audioAsVoice without media/text; skipping");

757768

continue;

@@ -760,7 +771,7 @@ export async function deliverReplies(params: {

760771

continue;

761772

}

762773
763-

const rawContent = reply.text || "";

774+

const rawContent = resolvedReplyText;

764775

const replyToId =

765776

params.replyToMode === "off" ? undefined : resolveTelegramReplyId(reply.replyToId);

766777

const replyQuote = resolveReplyQuoteForSend({

Original file line numberDiff line numberDiff line change

@@ -257,6 +257,35 @@ describe("deliverReplies", () => {

257257

);

258258

});

259259
260+

it("uses interactive button labels as fallback text for button-only replies", async () => {

261+

const runtime = createRuntime(false);

262+

const sendMessage = vi.fn().mockResolvedValue({ message_id: 3, chat: { id: "123" } });

263+

const bot = createBot({ sendMessage });

264+
265+

await deliverWith({

266+

replies: [

267+

{

268+

interactive: {

269+

blocks: [{ type: "buttons", buttons: [{ label: "Retry", value: "cmd:retry" }] }],

270+

},

271+

},

272+

],

273+

runtime,

274+

bot,

275+

});

276+
277+

expect(runtime.error).not.toHaveBeenCalled();

278+

expect(sendMessage).toHaveBeenCalledWith(

279+

"123",

280+

expect.stringContaining("Retry"),

281+

expect.objectContaining({

282+

reply_markup: {

283+

inline_keyboard: [[{ text: "Retry", callback_data: "cmd:retry" }]],

284+

},

285+

}),

286+

);

287+

});

288+
260289

it("reports message_sent success=false when hooks blank out a text-only reply", async () => {

261290

messageHookRunner.hasHooks.mockImplementation(

262291

(name: string) => name === "message_sending" || name === "message_sent",