Skip to content

fix: Telegram native command auth in groups checks DM allowlist instead of groupAllowFrom#38

Open
newtontech wants to merge 157 commits intomainfrom
fix/issue-30234
Open

fix: Telegram native command auth in groups checks DM allowlist instead of groupAllowFrom#38
newtontech wants to merge 157 commits intomainfrom
fix/issue-30234

Conversation

@newtontech
Copy link
Copy Markdown
Owner

Summary

This PR fixes the Telegram native command authorization bug where commands in groups were incorrectly checking the DM allowlist instead of groupAllowFrom.

Changes

  • Modified resolveTelegramCommandAuth to correctly use effectiveGroupAllow for group commands instead of the DM allowlist (allowFrom)
  • Fixed auth rejection messages to include thread params so they appear in the correct forum topic instead of #General

Root Cause

The issue occurred in resolveTelegramCommandAuth where the final authorization check was using dmAllow (from allowFrom) for all commands, including group commands. This meant that even when a user was in groupAllowFrom, they would be rejected if they were not in the DM allowlist.

Fix Details

  1. Authorization Check: Changed the final auth check to use effectiveGroupAllow for group commands and dmAllow for DM commands
  2. Thread Params: Added thread parameter construction early in the function and included them in auth rejection messages

Testing

  • All existing tests pass:
    • src/telegram/bot-native-commands.test.ts (6 tests)
    • src/telegram/bot-native-commands.plugin-auth.test.ts (2 tests)
    • src/telegram/bot-native-commands.session-meta.test.ts (2 tests)

Impact

  • Users in groupAllowFrom can now use native commands in groups without needing to be in the DM allowlist
  • Auth rejection messages in forum topics now appear in the correct topic

Fixes openclaw#30234

openperf and others added 30 commits February 27, 2026 11:26
…tch (openclaw#27094)

Fix tool-call lookup failures when models emit whitespace-padded names by normalizing
both transcript history and live streamed embedded-runner tool calls before dispatch.

Co-authored-by: wangchunyue <[email protected]>
Co-authored-by: Sid <[email protected]>
Co-authored-by: Philipp Spiess <[email protected]>
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: e418326
Co-authored-by: dsantoreis <[email protected]>
Co-authored-by: Evizero <[email protected]>
Reviewed-by: @Evizero
… thanks @rodrigouroz

Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: rodrigouroz <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
* Dependencies: make @discordjs/opus an optional peer

* Dependencies: bump node-llama-cpp peer to 3.16.2

* Dependencies: pin Google auth deps to warning-free versions

* CI: reduce Dependabot cooldown to 2 days

* CI: fix invalid Dependabot npm registry config

* CI: restore Dependabot npm registry with token auth

* Dependencies: remove global Google auth pnpm overrides

* CI: make Dependabot updates daily

* Dependencies: restore optional install semantics for @discordjs/opus

* CI: keep Docker Dependabot interval weekly
…nclaw#17874)

* feat(memory-lancedb): add custom baseUrl and dimensions support

* fix(memory-lancedb): strict model typing and safe dimension resolution

* style: fix formatting in memory-lancedb config

* fix(memory-lancedb): sync manifest schema with new embedding options

---------

Co-authored-by: OpenClaw Bot <[email protected]>


Verified:
- pnpm build
- pnpm check
- pnpm test:macmini (fails in this environment at src/daemon/launchd.integration.test.ts beforeAll hook timeout; merged with Tak override)

Co-authored-by: rodrigouroz <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Land contributor PR openclaw#29032 by @maloqab with Slack native alias docs, integration tests, and changelog entry.

Co-authored-by: maloqab <[email protected]>
- Remove trailing blank line after Jonathan Taylor entry
- Escape underscore in @jlehman_ X handle

Fixes openclaw#29039
…nclaw#26050) thanks @bmendonca3

Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: bmendonca3 <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
… custom provider probes (openclaw#27380)

* fix(onboard): increase verification timeout and reduce max_tokens for custom provider probes

The onboard wizard sends a chat-completion request to verify custom
providers.  With max_tokens: 1024 and a 10 s timeout, large local
models (e.g. Qwen3.5-27B on llama.cpp) routinely time out because
the server needs to load the model and generate up to 1024 tokens
before responding.

Changes:
- Raise VERIFY_TIMEOUT_MS from 10 s to 30 s
- Lower max_tokens from 1024 to 1 (verification only needs a single
  token to confirm the API is reachable and the model ID is valid)
- Add explicit stream: false to both OpenAI and Anthropic probes

Closes openclaw#27346

Made-with: Cursor

* Changelog: note custom-provider onboarding verification fix

---------

Co-authored-by: Philipp Spiess <[email protected]>
When an agent triggers a gateway restart in supervised mode, the process
exits expecting launchd KeepAlive to respawn it. But ThrottleInterval
(default 10s, or 60s on older installs) can delay or prevent restart.

Now calls triggerOpenClawRestart() to issue an explicit launchctl
kickstart before exiting, ensuring immediate respawn. Falls back to
in-process restart if kickstart fails.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
* agents: auto-discover Ollama models without API key

* tests: cover Ollama autodiscovery warning behavior
openclaw#28295) thanks @zhoulongchao77

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: zhoulongchao77 <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
…#27884) (openclaw#27928) thanks @joelnishanth

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: joelnishanth <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
…penclaw#28822)

Merged via squash.

Prepared head SHA: 83d4329
Co-authored-by: lailoo <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark
…I calls (openclaw#28907) thanks @Glucksberg

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: Glucksberg <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
…penclaw#28269) thanks @Glucksberg

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: Glucksberg <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
… / quota errors (openclaw#28494) thanks @guoqunabc

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: guoqunabc <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
obviyus and others added 30 commits February 28, 2026 20:05
…claw#18529)

* feat(feishu): add parentId and rootId to inbound context

Add ParentMessageId and RootMessageId fields to Feishu inbound message context,
enabling agents to:
- Identify quoted/replied messages
- Fetch original message content via Feishu API
- Build proper message thread context

The parent_id and root_id fields already exist in FeishuMessageContext but were
not being passed to the agent's inbound context.

Fixes: Allows proper handling of quoted card messages and message thread reconstruction.

* feat(feishu): parse interactive card content in quoted messages

Add support for extracting readable text from interactive card messages
when fetching quoted/replied message content.

Previously, only text messages were parsed. Now interactive cards
(with div and markdown elements) are also converted to readable text.

* 更新 bot.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(types): add RootMessageId to MsgContext type definition

* style: fix formatting in bot.ts

* ci: trigger rebuild

* ci: retry flaky tests

* Feishu: add reply-context and interactive-quote regressions

---------

Co-authored-by: qiangu <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: 牛牛 <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
… table ops (openclaw#29411)

* feat(feishu): add markdown tables, insert, color_text, table ops, and image fixes

Extends feishu_doc on top of openclaw#20304 with capabilities that are not yet covered:

Markdown → native table rendering:
- write/append now use the Descendant API instead of Children API,
  enabling GFM markdown tables (block_type 31/32) to render as native
  Feishu tables automatically
- Adaptive column widths calculated from cell content (CJK chars 2x weight)
- Batch insertion for large documents (>1000 blocks, docx-batch-insert.ts)

New actions:
- insert: positional markdown insertion after a given block_id
- color_text: apply color/bold to a text block via [red]...[/red] markup
- insert_table_row / insert_table_column: add rows or columns to a table
- delete_table_rows / delete_table_columns: remove rows or columns
- merge_table_cells: merge a rectangular cell range

Image upload fixes (affects write, append, and upload_image):
- upload_image now accepts data URI and plain base64 in addition to
  url/file_path, covering DALL-E b64_json, canvas screenshots, etc.
- Fix: pass Buffer directly to drive.media.uploadAll instead of
  Readable.from(), which caused Content-Length mismatch for large images
- Fix: same Readable bug fixed in upload_file
- Fix: pass drive_route_token via extra field for correct multi-datacenter
  routing (per API docs: required when parent_node is a document block ID)

* fix(feishu): add documentBlockDescendant mock to docx.test.ts

write/append now use the Descendant API (documentBlockDescendant.create)
instead of Children API. The existing test mock was missing this SDK
method, causing processImages to never be reached and fetchRemoteMedia
to go uncalled.

Added blockDescendantCreateMock returning an image block so the
'skips image upload when markdown image URL is blocked' test flows
through processImages as expected.

* fix(feishu): address bot review feedback

- resolveUploadInput: remove length < 1024 guard on file path detection.
  Prefix patterns (isAbsolute / ~ / ./ / ../) already correctly distinguish
  file paths from base64 strings at any length. The old guard caused file
  paths ≥1024 chars to fall through to the base64 branch incorrectly.

- parseColorMarkup: add comment clarifying that mismatched closing tags
  (e.g. [red]text[/green]) are intentional — opening tag style is applied,
  closing tag is consumed regardless of name.

* fix(feishu): address second-round codex bot review feedback

P1 - Reject single oversized subtrees in batch insert (docx-batch-insert.ts):
  A first-level block whose descendant count exceeds BATCH_SIZE (1000) cannot
  be split atomically (e.g. a very large table). Previously such a block was
  silently added to the current batch and sent as an oversized request,
  violating the API limit. Now throws a descriptive error so callers know to
  reduce the content size.

P2 - Preserve unmatched brackets in color markup parser (docx-color-text.ts):
  Text like 'Revenue [Q1] up' contains a bracket pair with no matching '[/...]'
  closer. The original regex dropped the '[' character in this case, silently
  corrupting the text. Fixed by appending '|\[' to the plain-text alternative
  so any '[' that does not open a complete tag is captured as literal text.

* fix(feishu): address third-round codex bot review feedback

P2 - Throw ENOENT for non-existing absolute image paths (docx.ts):
  Previously a non-existing absolute path like /tmp/missing.png fell
  through to Buffer.from(..., 'base64') and uploaded garbage bytes.
  Now throws a descriptive ENOENT error and hints at data URI format
  for callers intending to pass JPEG binary data (which starts with /9j/).

P2 - Fail clearly when insert anchor block is not found (docx.ts):
  insertDoc previously set insertIndex to -1 (append) when after_block_id
  was absent from the parent's child list, silently inserting at the wrong
  position. Two fixes:
  1. Paginate through all children (documentBlockChildren.get returns up to
     200 per page) before searching for the anchor.
  2. Throw a descriptive error if after_block_id is still not found after
     full pagination, instead of silently falling back to append.

* fix(feishu): address fourth-round codex bot review feedback

- Enforce mutual exclusivity across all three upload sources (url, file_path,
  image): throw immediately when more than one is provided, instead of silently
  preferring the image branch and ignoring the others.
- Validate plain base64 payloads before decoding: reject strings that contain
  characters outside the standard base64 alphabet ([A-Za-z0-9+/=]) so that
  malformed inputs fail fast with a clear error rather than decoding to garbage
  bytes and producing an opaque Feishu API failure downstream.
  Also throw if the decoded buffer is empty.

* fix(feishu): address fifth-round codex bot review feedback

- parseColorMarkup: restrict opening tag regex to known colour/style names
  (bg:*, bold, red, orange, yellow, green, blue, purple, grey/gray) so that
  ordinary bracket tokens like [Q1] can no longer consume a subsequent real
  closing tag ([/red]) and corrupt the surrounding styled spans.  Unknown tags
  now fall through to the plain-text alternatives and are emitted literally.
- resolveUploadInput: estimate decoded byte count from base64 input length
  (ceil(len * 3 / 4)) BEFORE allocating the full Buffer, preventing oversized
  payloads from spiking memory before the maxBytes limit is enforced.  Applies
  to both the data-URI branch and the plain-base64 branch.

* fix(feishu): address sixth-round codex bot review feedback

- docx-table-ops: apply MIN/MAX_COLUMN_WIDTH clamping in the empty-table
  branch so tables with 15+ columns don't produce sub-50 widths that Feishu
  rejects as invalid column_width values.
- docx.ts (data URI branch): validate the ';base64' marker before decoding
  so plain/URL-encoded data URIs are rejected with a clear error; also validate
  the payload against the base64 alphabet (same guard already applied in the
  plain-base64 branch) so malformed inputs fail fast rather than producing
  opaque downstream Feishu errors.

* Feishu: align docx descendant insertion tests and changelog

---------

Co-authored-by: Tak Hoffman <[email protected]>
* feat(feishu): add chat members/info tool support

* Feishu: harden chat tool schema and coverage

---------

Co-authored-by: Nereo <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
… thanks @Takhoffman

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: Tak Hoffman <[email protected]>
…claw#19541)

* fix(cron): repair isolated next wake scheduling

* cron: harden isolated next-wake timestamp guards

---------

Co-authored-by: Tak Hoffman <[email protected]>
…enclaw#26284)

* feat(cron): add --account flag for multi-account delivery routing

Add support for explicit delivery account routing in cron jobs across CLI, normalization, delivery planning, and isolated delivery target resolution.

Highlights:
- Add --account <id> to cron add and cron edit
- Add optional delivery.accountId to cron types and delivery plan
- Normalize and trim delivery.accountId in cron create/update normalization
- Prefer explicit accountId over session lastAccountId and bindings fallback
- Thread accountId through isolated cron run delivery resolution
- Preserve cron edit --best-effort-deliver/--no-best-effort-deliver behavior by keeping implicit announce mode
- Expand tests for account passthrough/merge/precedence and CLI account flows

* cron: resolve rebase duplicate accountId fields

---------

Co-authored-by: Tak Hoffman <[email protected]>
openclaw#28017)

When a cron job's delivery target resolution fails (resolvedDelivery.ok
is false), the agent was still started with requireExplicitMessageTarget:
true. This caused "Action send requires a target" errors because the
agent's message tool demanded a target that was never resolved.

Condition the flag on both deliveryRequested AND resolvedDelivery.ok so
the agent can still use messaging tools freely when no valid delivery
target exists.

Fixes openclaw#27898

Co-authored-by: Claude Opus 4.6 <[email protected]>
…w#28898)

Ignore persisted sessionKey overrides for sessionTarget=main jobs so cron system events consistently route to the agent main session after upgrades.

Closes openclaw#28770
…#29716)

Cron announce flow treated queued/steered outcomes as delivered even when no direct outbound send was confirmed, which could report false-positive delivery state. This change keeps cron delivery strict: only direct-path announce results count as delivered.

Closes openclaw#29660
openclaw#27778)

* fix: clear delivery routing state when creating isolated cron sessions

When `resolveCronSession()` creates a new session (forceNew / isolated),
the `...entry` spread preserves `lastThreadId`, `lastTo`, `lastChannel`,
and `lastAccountId` from the prior session. This causes announce-mode
cron deliveries to post as thread replies instead of channel top-level
messages when `delivery.to` matches the channel of a prior conversation.

Clear delivery routing metadata on new session creation so isolated
cron sessions start with a clean delivery state.

Closes openclaw#27751

✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved)

* fix: also clear deliveryContext to prevent lastThreadId repopulation

normalizeSessionEntryDelivery (called on store writes) repopulates
lastThreadId from deliveryContext.threadId. Clearing only the last*
fields is insufficient — deliveryContext must also be cleared when
creating a new isolated session.

✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved)
…nclaw#28508) (openclaw#28583)

* fix(cron): pass heartbeat target=last for main-session cron jobs

When a cron job with sessionTarget=main and wakeMode=now fires, it
triggers a heartbeat via runHeartbeatOnce. Since e2362d3 changed the
default heartbeat target from "last" to "none", these cron-triggered
heartbeats silently discard their responses instead of delivering them
to the last active channel (e.g. Telegram).

Fix: pass heartbeat: { target: "last" } from the cron timer to
runHeartbeatOnce for main-session jobs, and wire the override through
the gateway cron service builder. This restores delivery for
sessionTarget=main cron jobs without reverting the intentional default
change for regular heartbeats.

Regression introduced in: e2362d3 (2026-02-25)

Fixes openclaw#28508

* Cron: align server-cron wake routing expectations for main-target jobs

---------

Co-authored-by: Tak Hoffman <[email protected]>
…26414)

* fix: sed escaping and UID mismatch in Podman Quadlet setup

Fix two bugs in the Podman/Quadlet installation path:

1. setup-podman.sh line 227: Remove `/` from sed escape character class.
   The sed substitution uses `|` as delimiter, so `/` doesn't need
   escaping. Including it causes paths like `/home/openclaw` to become
   `\/home\/openclaw`, which Podman rejects as invalid volume names.

2. openclaw.container.in: Add `User=%U:%G` after `UserNS=keep-id`.
   The Dockerfile sets `USER node` (UID 1000), but the `openclaw` system
   user created by setup-podman.sh may get a different UID (e.g., 1001).
   Without `User=%U:%G`, the container process runs as UID 1000 and
   cannot read config files owned by the openclaw user.

Closes openclaw#26400

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* scripts: extract quadlet sed replacement escaping helper

* podman: document quadlet user mapping rationale

* scripts: correct sed replacement escaping for pipe delimiter

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
…derer swap (openclaw#25326)

* fix(browser): resolve correct targetId in navigate response after renderer swap

When `navigateViaPlaywright` triggers a Chrome renderer-process swap
(e.g. navigating from chrome-extension:// to https://), the old
`tab.targetId` captured before navigation becomes stale. The `/navigate`
route previously returned this stale targetId in its response.

After navigation, re-resolve the current tab by matching against the
final URL via `profileCtx.listTabs()`. If the old target is already gone
but the new one is not yet visible (extension re-attach in progress),
retry after 800ms.

Follow-up to openclaw#19744 (67bac62) which fixed the extension-side stale
session cleanup.

* fix(browser): prefer non-stale targetId when multiple tabs share the same URL

When multiple tabs have the same URL after navigation, find() could pick
a pre-existing tab instead of the newly created one. Now only re-resolve
when the old target is gone (renderer swap detected), and prefer the tab
whose targetId differs from the old one.

* fix(browser): encapsulate targetId resolution logic after navigation

Introduced a new function `resolveTargetIdAfterNavigate` to handle the resolution of the correct targetId after a navigation event that may trigger a renderer swap. This refactor improves code clarity and reuses the logic for determining the current targetId, ensuring that the correct tab is identified even when multiple tabs share the same URL.

* refactor(tests): simplify listTabs initialization in agent snapshot tests

Updated the initialization of listTabs in the agent snapshot tests for better readability by removing unnecessary line breaks. This change enhances code clarity without altering the test logic.

* fix(ui): widen Set type to accept string tokens in external-link helper

* chore: retrigger CI (unrelated Windows flaky test)

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
…ad of groupAllowFrom

Fixes openclaw#30234

- Modified resolveTelegramCommandAuth to use effectiveGroupAllow for group commands
  instead of incorrectly using the DM allowlist (allowFrom)
- Added thread params to auth rejection messages so they appear in the correct
  forum topic instead of #General
- The fix ensures group commands now properly authorize against groupAllowFrom
  and group/topic-level allowFrom configurations
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Telegram native command auth in groups checks DM allowlist instead of groupAllowFrom