fix(telegram): use owners for exec approvals (#73852)

This commit is contained in:
pashpashpash
2026-04-28 16:34:46 -07:00
committed by GitHub
parent a235a487d0
commit 43fa40a35d
10 changed files with 27 additions and 21 deletions

View File

@@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Telegram/exec approvals: stop treating general Telegram chat allowlists and `defaultTo` routes as native exec approvers; Telegram now uses explicit `execApprovals.approvers` or owner identity from `commands.ownerAllowFrom`, matching the first-pairing owner bootstrap path. Thanks @pashpashpash.
- Plugin SDK/Discord: restore a deprecated `openclaw/plugin-sdk/discord` compatibility facade and the legacy compat group-policy warning export for the published `@openclaw/discord@2026.3.13` package, covering its config, account, directory, status, and thread-binding imports while keeping new plugins on generic SDK subpaths. Fixes #73685; supersedes #73703. Thanks @rderickson9 and @SymbolStar.
- Channels/Discord: suppress duplicate gateway monitors when multiple enabled accounts resolve to the same bot token, preferring config tokens over default env fallback and reporting skipped duplicates as disabled. Supersedes #73608. Thanks @kagura-agent.
- Control UI/Talk: decode Google Live binary WebSocket JSON frames and stop queued browser audio on interruption or shutdown, so browser Talk leaves `Connecting Talk...` and barge-in no longer plays stale audio. Fixes #73601 and #73460; supersedes #73466. Thanks @Spolen23 and @WadydX.

View File

@@ -778,10 +778,12 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \
Config path:
- `channels.telegram.execApprovals.enabled` (auto-enables when at least one approver is resolvable)
- `channels.telegram.execApprovals.approvers` (falls back to numeric owner IDs from `commands.ownerAllowFrom`, `allowFrom`, or `defaultTo`)
- `channels.telegram.execApprovals.approvers` (falls back to numeric owner IDs from `commands.ownerAllowFrom`)
- `channels.telegram.execApprovals.target`: `dm` (default) | `channel` | `both`
- `agentFilter`, `sessionFilter`
`channels.telegram.allowFrom`, `groupAllowFrom`, and `defaultTo` control who can talk to the bot and where it sends normal replies. They do not make someone an exec approver. The first approved DM pairing bootstraps `commands.ownerAllowFrom` when no command owner exists yet, so the one-owner setup still works without duplicating IDs under `execApprovals.approvers`.
Channel delivery shows the command text in the chat; only enable `channel` or `both` in trusted groups/topics. When the prompt lands in a forum topic, OpenClaw preserves the topic for the approval prompt and the follow-up. Exec approvals expire after 30 minutes by default.
Inline approval buttons also require `channels.telegram.capabilities.inlineButtons` to allow the target surface (`dm`, `group`, or `all`). Approval IDs prefixed with `plugin:` resolve through plugin approvals; others resolve through exec approvals first.

View File

@@ -271,8 +271,8 @@ Generic model:
Native approval clients auto-enable DM-first delivery when all of these are true:
- the channel supports native approval delivery
- approvers can be resolved from explicit `execApprovals.approvers` or that
channel's documented fallback sources
- approvers can be resolved from explicit `execApprovals.approvers` or owner
identity such as `commands.ownerAllowFrom`
- `channels.<channel>.execApprovals.enabled` is unset or `"auto"`
Set `enabled: false` to disable a native approval client explicitly. Set `enabled: true` to force
@@ -295,7 +295,7 @@ Shared behavior:
- when a native approval client auto-enables, the default native delivery target is approver DMs
- for Discord and Telegram, only resolved approvers can approve or deny
- Discord approvers can be explicit (`execApprovals.approvers`) or inferred from `commands.ownerAllowFrom`
- Telegram approvers can be explicit (`execApprovals.approvers`) or inferred from existing owner config (`allowFrom`, plus direct-message `defaultTo` where supported)
- Telegram approvers can be explicit (`execApprovals.approvers`) or inferred from `commands.ownerAllowFrom`
- Slack approvers can be explicit (`execApprovals.approvers`) or inferred from `commands.ownerAllowFrom`
- Slack native buttons preserve approval id kind, so `plugin:` ids can resolve plugin approvals
without a second Slack-local fallback layer

View File

@@ -40,8 +40,8 @@ describe("telegram native approval adapter", () => {
expect(text).toContain("`channels.telegram.execApprovals.approvers`");
expect(text).toContain("`commands.ownerAllowFrom`");
expect(text).toContain("`channels.telegram.allowFrom`");
expect(text).toContain("`channels.telegram.defaultTo`");
expect(text).not.toContain("`channels.telegram.allowFrom`");
expect(text).not.toContain("`channels.telegram.defaultTo`");
expect(text).not.toContain("`channels.telegram.dm.allowFrom`");
});
@@ -54,8 +54,8 @@ describe("telegram native approval adapter", () => {
expect(text).toContain("`channels.telegram.accounts.work.execApprovals.approvers`");
expect(text).toContain("`commands.ownerAllowFrom`");
expect(text).toContain("`channels.telegram.accounts.work.allowFrom`");
expect(text).toContain("`channels.telegram.accounts.work.defaultTo`");
expect(text).not.toContain("`channels.telegram.accounts.work.allowFrom`");
expect(text).not.toContain("`channels.telegram.accounts.work.defaultTo`");
expect(text).not.toContain("`channels.telegram.allowFrom`");
});

View File

@@ -92,7 +92,7 @@ const telegramNativeApprovalCapability = createApproverRestrictedNativeApprovalC
accountId && accountId !== "default"
? `channels.telegram.accounts.${accountId}`
: "channels.telegram";
return `Approve it from the Web UI or terminal UI for now. Telegram supports native exec approvals for this account. Configure \`${prefix}.execApprovals.approvers\`; if you leave it unset, OpenClaw can infer numeric owner IDs from \`commands.ownerAllowFrom\`, \`${prefix}.allowFrom\`, or direct-message \`${prefix}.defaultTo\` when possible. Leave \`${prefix}.execApprovals.enabled\` unset/\`auto\` or set it to \`true\`.`;
return `Approve it from the Web UI or terminal UI for now. Telegram supports native exec approvals for this account. Configure \`${prefix}.execApprovals.approvers\` or \`commands.ownerAllowFrom\`; leave \`${prefix}.execApprovals.enabled\` unset/\`auto\` or set it to \`true\`.`;
},
listAccountIds: listTelegramAccountIds,
hasApprovers: ({ cfg, accountId }) =>

View File

@@ -135,7 +135,7 @@ export const telegramChannelConfigUiHints = {
},
"execApprovals.approvers": {
label: "Telegram Exec Approval Approvers",
help: "Telegram user IDs allowed to approve exec requests for this bot account. Use numeric Telegram user IDs. If you leave this unset, OpenClaw falls back to numeric owner IDs inferred from commands.ownerAllowFrom, channels.telegram.allowFrom, and direct-message defaultTo when possible.",
help: "Telegram user IDs allowed to approve exec requests for this bot account. Use numeric Telegram user IDs. If you leave this unset, OpenClaw falls back to numeric owner IDs inferred from commands.ownerAllowFrom when possible.",
},
"execApprovals.agentFilter": {
label: "Telegram Exec Approval Agent Filter",

View File

@@ -121,7 +121,12 @@ describe("telegram exec approvals", () => {
isTelegramExecApprovalClientEnabled({
cfg: buildConfig(undefined, { allowFrom: ["123"] }),
}),
).toBe(true);
).toBe(false);
expect(
isTelegramExecApprovalClientEnabled({
cfg: buildConfig(undefined, { defaultTo: 123 }),
}),
).toBe(false);
expect(
isTelegramExecApprovalClientEnabled({
cfg: buildConfig({ approvers: ["123"] }),
@@ -160,7 +165,7 @@ describe("telegram exec approvals", () => {
expect(isTelegramExecApprovalApprover({ cfg, senderId: "67890" })).toBe(true);
});
it("infers approvers from allowFrom and direct defaultTo", () => {
it("does not infer approvers from Telegram chat allowlists", () => {
const cfg = buildConfig(
{ enabled: true },
{
@@ -169,9 +174,10 @@ describe("telegram exec approvals", () => {
},
);
expect(getTelegramExecApprovalApprovers({ cfg })).toEqual(["12345", "67890"]);
expect(isTelegramExecApprovalApprover({ cfg, senderId: "12345" })).toBe(true);
expect(isTelegramExecApprovalApprover({ cfg, senderId: "67890" })).toBe(true);
expect(getTelegramExecApprovalApprovers({ cfg })).toEqual([]);
expect(isTelegramExecApprovalClientEnabled({ cfg })).toBe(false);
expect(isTelegramExecApprovalApprover({ cfg, senderId: "12345" })).toBe(false);
expect(isTelegramExecApprovalApprover({ cfg, senderId: "67890" })).toBe(false);
});
it("defaults target to dm", () => {

View File

@@ -58,12 +58,9 @@ export function getTelegramExecApprovalApprovers(params: {
cfg: OpenClawConfig;
accountId?: string | null;
}): string[] {
const account = resolveTelegramAccount(params).config;
return resolveApprovalApprovers({
explicit: resolveTelegramExecApprovalConfig(params)?.approvers,
allowFrom: account.allowFrom,
extraAllowFrom: resolveTelegramOwnerApprovers(params.cfg),
defaultTo: account.defaultTo ? String(account.defaultTo) : null,
allowFrom: resolveTelegramOwnerApprovers(params.cfg),
normalizeApprover: normalizeTelegramDirectApproverId,
});
}

View File

@@ -15287,7 +15287,7 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
},
"execApprovals.approvers": {
label: "Telegram Exec Approval Approvers",
help: "Telegram user IDs allowed to approve exec requests for this bot account. Use numeric Telegram user IDs. If you leave this unset, OpenClaw falls back to numeric owner IDs inferred from channels.telegram.allowFrom and direct-message defaultTo when possible.",
help: "Telegram user IDs allowed to approve exec requests for this bot account. Use numeric Telegram user IDs. If you leave this unset, OpenClaw falls back to numeric owner IDs inferred from commands.ownerAllowFrom when possible.",
},
"execApprovals.agentFilter": {
label: "Telegram Exec Approval Agent Filter",

View File

@@ -67,7 +67,7 @@ export type TelegramExecApprovalTarget = "dm" | "channel" | "both";
export type TelegramExecApprovalConfig = {
/** Enable mode for Telegram exec approvals on this account. Default: auto when approvers can be resolved; false disables. */
enabled?: import("./types.approvals.js").NativeExecApprovalEnableMode;
/** Telegram user IDs allowed to approve exec requests. Optional: falls back to numeric owner IDs inferred from allowFrom/defaultTo when possible. */
/** Telegram user IDs allowed to approve exec requests. Optional: falls back to numeric owner IDs inferred from commands.ownerAllowFrom when possible. */
approvers?: Array<string | number>;
/** Only forward approvals for these agent IDs. Omit = all agents. */
agentFilter?: string[];