fix(telegram/groups): treat empty `accounts.<id>.groups: {}` as unspe… · openclaw/openclaw@ab719c2
kinjitakabe
·
2026-05-13
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -67,6 +67,7 @@ Docs: https://docs.openclaw.ai
|
67 | 67 | - fix(gateway): honor minimal discovery mode for wide-area DNS-SD [AI]. (#80903) Thanks @pgondhi987. |
68 | 68 | - slack: enforce reaction notification policy [AI]. (#80907) Thanks @pgondhi987. |
69 | 69 | - Enforce gateway command scopes by caller context [AI]. (#80891) Thanks @pgondhi987. |
| 70 | +- Telegram/groups: in single-account setups, treat an explicit empty `accounts.<id>.groups: {}` map the same as undefined so the root `channels.telegram.groups` allowlist still applies, instead of silently dropping every group update under the default `groupPolicy: "allowlist"`. Multi-account semantics are unchanged so per-account explicit-empty groups still scope-disable a single account without affecting siblings; the explicit way to block all groups for any account remains `groupPolicy: "disabled"`. Fixes #79427. Thanks @nikolaykazakovvs-ux. |
70 | 71 | - Enforce Slack plugin approval button authorization [AI]. (#80899) Thanks @pgondhi987. |
71 | 72 | - Recognize PowerShell -ec inline commands [AI]. (#80893) Thanks @pgondhi987. |
72 | 73 | - fix(qqbot): authorize approval button callbacks [AI]. (#80892) Thanks @pgondhi987. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -68,9 +68,19 @@ export function mergeTelegramAccountConfig(
|
68 | 68 | const account = resolveTelegramAccountConfig(cfg, accountId) ?? {}; |
69 | 69 | |
70 | 70 | // Multi-account bots must not inherit channel-level groups unless explicitly set. |
| 71 | +// Single-account bots fall back to root `channels.telegram.groups` when the |
| 72 | +// account does not declare its own groups — including the empty-literal case |
| 73 | +// `accounts.<id>.groups: {}`, which is almost always a config-migration |
| 74 | +// artifact rather than an intentional "block all" declaration (use |
| 75 | +// `groupPolicy: "disabled"` for that). |
71 | 76 | const configuredAccountIds = Object.keys(cfg.channels?.telegram?.accounts ?? {}); |
72 | 77 | const isMultiAccount = configuredAccountIds.length > 1; |
73 | | -const groups = account.groups ?? (isMultiAccount ? undefined : channelGroups); |
| 78 | +const hasAccountGroups = account.groups && Object.keys(account.groups).length > 0; |
| 79 | +const groups = isMultiAccount |
| 80 | + ? account.groups |
| 81 | + : hasAccountGroups |
| 82 | + ? account.groups |
| 83 | + : channelGroups; |
74 | 84 | const allowFrom = resolveMergedAllowFrom({ |
75 | 85 | baseAllowFrom: base.allowFrom, |
76 | 86 | accountAllowFrom: account.allowFrom, |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -536,6 +536,24 @@ describe("resolveTelegramAccount groups inheritance (#30673)", () => {
|
536 | 536 | expect(resolved.config.groups).toEqual({ "-100123": { requireMention: false } }); |
537 | 537 | }); |
538 | 538 | |
| 539 | +it("inherits channel-level groups when single-account explicitly sets `groups: {}` (regression: #79427)", () => { |
| 540 | +const resolved = resolveTelegramAccount({ |
| 541 | +cfg: { |
| 542 | +channels: { |
| 543 | +telegram: { |
| 544 | +groups: { "-100123": { requireMention: false } }, |
| 545 | +accounts: { |
| 546 | +default: { botToken: "123:default", groups: {} }, |
| 547 | +}, |
| 548 | +}, |
| 549 | +}, |
| 550 | +}, |
| 551 | +accountId: "default", |
| 552 | +}); |
| 553 | + |
| 554 | +expect(resolved.config.groups).toEqual({ "-100123": { requireMention: false } }); |
| 555 | +}); |
| 556 | + |
539 | 557 | it("does NOT inherit channel-level groups to secondary account in multi-account setup", () => { |
540 | 558 | const resolved = resolveTelegramAccount({ |
541 | 559 | cfg: createMultiAccountGroupsConfig(), |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -169,6 +169,68 @@ describe("resolveChannelGroupPolicy", () => {
|
169 | 169 | }), |
170 | 170 | ).toBe(false); |
171 | 171 | }); |
| 172 | + |
| 173 | +it("falls back to root channel groups when account.groups is an empty object (regression: #79427)", () => { |
| 174 | +const cfg = { |
| 175 | +channels: { |
| 176 | +telegram: { |
| 177 | +groupPolicy: "allowlist", |
| 178 | +groups: { |
| 179 | +"-100123": { requireMention: false }, |
| 180 | +}, |
| 181 | +accounts: { |
| 182 | +default: { botToken: "123:default", groups: {} }, |
| 183 | +}, |
| 184 | +}, |
| 185 | +}, |
| 186 | +} as OpenClawConfig; |
| 187 | + |
| 188 | +const policy = resolveChannelGroupPolicy({ |
| 189 | + cfg, |
| 190 | +channel: "telegram", |
| 191 | +groupId: "-100123", |
| 192 | +accountId: "default", |
| 193 | +}); |
| 194 | + |
| 195 | +expect(policy.allowlistEnabled).toBe(true); |
| 196 | +expect(policy.allowed).toBe(true); |
| 197 | +}); |
| 198 | + |
| 199 | +it("uses populated account.groups instead of root when both are configured", () => { |
| 200 | +const cfg = { |
| 201 | +channels: { |
| 202 | +telegram: { |
| 203 | +groupPolicy: "allowlist", |
| 204 | +groups: { |
| 205 | +"-100root": { requireMention: false }, |
| 206 | +}, |
| 207 | +accounts: { |
| 208 | +default: { |
| 209 | +botToken: "123:default", |
| 210 | +groups: { "-100account": { requireMention: false } }, |
| 211 | +}, |
| 212 | +}, |
| 213 | +}, |
| 214 | +}, |
| 215 | +} as OpenClawConfig; |
| 216 | + |
| 217 | +expect( |
| 218 | +resolveChannelGroupPolicy({ |
| 219 | + cfg, |
| 220 | +channel: "telegram", |
| 221 | +groupId: "-100account", |
| 222 | +accountId: "default", |
| 223 | +}).allowed, |
| 224 | +).toBe(true); |
| 225 | +expect( |
| 226 | +resolveChannelGroupPolicy({ |
| 227 | + cfg, |
| 228 | +channel: "telegram", |
| 229 | +groupId: "-100root", |
| 230 | +accountId: "default", |
| 231 | +}).allowed, |
| 232 | +).toBe(false); |
| 233 | +}); |
172 | 234 | }); |
173 | 235 | |
174 | 236 | describe("resolveToolsBySender", () => { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -340,6 +340,21 @@ function resolveChannelGroups(
|
340 | 340 | return undefined; |
341 | 341 | } |
342 | 342 | const accountGroups = resolveAccountEntry(channelConfig.accounts, normalizedAccountId)?.groups; |
| 343 | +// In a single-account setup, treat an explicit empty account groups map |
| 344 | +// (`accounts.<id>.groups: {}`) the same as undefined for fallback: the empty |
| 345 | +// literal is almost always a config-migration artifact, not an intentional |
| 346 | +// "block all groups" declaration — the explicit way to block is |
| 347 | +// `groupPolicy: "disabled"` (or omitting the group from a populated |
| 348 | +// allowlist). Without this, an empty `{}` paired with the default |
| 349 | +// `groupPolicy: "allowlist"` silently denies every group update even though |
| 350 | +// root `channels.<channel>.groups` is populated. Multi-account contexts keep |
| 351 | +// the existing semantics so per-account explicit-empty groups still scope |
| 352 | +// disable a single account without affecting siblings. |
| 353 | +const isMultiAccount = Object.keys(channelConfig.accounts ?? {}).length > 1; |
| 354 | +if (!isMultiAccount) { |
| 355 | +const hasAccountGroups = accountGroups && Object.keys(accountGroups).length > 0; |
| 356 | +return hasAccountGroups ? accountGroups : channelConfig.groups; |
| 357 | +} |
343 | 358 | return accountGroups ?? channelConfig.groups; |
344 | 359 | } |
345 | 360 | |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。