fix(feishu): honor wildcard group config for reply policy (#29456)

## Summary
- honor Feishu wildcard group policy fallback via `channels.feishu.groups["*"]` when no explicit group entry matches
- keep exact and case-insensitive explicit group matches higher precedence than wildcard fallback
- add changelog credit and TypeScript-safe test assertions

## Verification
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: Wayne Pika <262095977+WaynePika@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
Tak Hoffman
2026-02-27 23:22:38 -06:00
committed by GitHub
parent 8a2273e210
commit 60bf56517f
3 changed files with 62 additions and 2 deletions

View File

@@ -32,6 +32,7 @@ Docs: https://docs.openclaw.ai
- Feishu/Inbound rich-text parsing: preserve `share_chat` payload summaries when available and add explicit parsing for rich-text `code`/`code_block`/`pre` tags so forwarded and code-heavy messages keep useful context in agent input. (#28591) Thanks @kevinWangSheng.
- Feishu/Local media sends: propagate `mediaLocalRoots` through Feishu outbound media sending into `loadWebMedia` so local path attachments work with post-CVE local-root enforcement. (#27884) Thanks @joelnishanth.
- Feishu/Group sender allowlist fallback: add global `channels.feishu.groupSenderAllowFrom` sender authorization for group chats, with per-group `groups.<id>.allowFrom` precedence and regression coverage for allow/block/precedence behavior. (#29174) Thanks @1MoreBuild.
- Feishu/Group wildcard policy fallback: honor `channels.feishu.groups["*"]` when no explicit group match exists so unmatched groups inherit wildcard reply-policy settings instead of falling back to global defaults. (#29456) Thanks @WaynePika.
- Feishu/Docx append/write ordering: insert converted Docx blocks sequentially (single-block creates) so Feishu append/write preserves markdown block order instead of returning shuffled sections in asynchronous batch inserts. (#26172, #26022) Thanks @echoVic.
- Feishu/Docx convert fallback chunking: recursively split oversized markdown chunks (including long no-heading sections) when `document.convert` hits content limits, while keeping fenced-code-aware split boundaries whenever possible. (#14402) Thanks @lml2468.
- Feishu/Inbound media regression coverage: add explicit tests for message resource type mapping (`image` stays `image`, non-image maps to `file`) to prevent reintroducing unsupported Feishu `type=audio` fetches. (#16311, #8746) Thanks @Yaxuan42.

View File

@@ -1,7 +1,62 @@
import { describe, expect, it } from "vitest";
import { isFeishuGroupAllowed, resolveFeishuAllowlistMatch } from "./policy.js";
import {
isFeishuGroupAllowed,
resolveFeishuAllowlistMatch,
resolveFeishuGroupConfig,
} from "./policy.js";
import type { FeishuConfig } from "./types.js";
describe("feishu policy", () => {
describe("resolveFeishuGroupConfig", () => {
it("falls back to wildcard group config when direct match is missing", () => {
const cfg = {
groups: {
"*": { requireMention: false },
"oc-explicit": { requireMention: true },
},
} as unknown as FeishuConfig;
const resolved = resolveFeishuGroupConfig({
cfg,
groupId: "oc-missing",
});
expect(resolved).toEqual({ requireMention: false });
});
it("prefers exact group config over wildcard", () => {
const cfg = {
groups: {
"*": { requireMention: false },
"oc-explicit": { requireMention: true },
},
} as unknown as FeishuConfig;
const resolved = resolveFeishuGroupConfig({
cfg,
groupId: "oc-explicit",
});
expect(resolved).toEqual({ requireMention: true });
});
it("keeps case-insensitive matching for explicit group ids", () => {
const cfg = {
groups: {
"*": { requireMention: false },
OC_UPPER: { requireMention: true },
},
} as unknown as FeishuConfig;
const resolved = resolveFeishuGroupConfig({
cfg,
groupId: "oc_upper",
});
expect(resolved).toEqual({ requireMention: true });
});
});
describe("resolveFeishuAllowlistMatch", () => {
it("allows wildcard", () => {
expect(

View File

@@ -56,6 +56,7 @@ export function resolveFeishuGroupConfig(params: {
groupId?: string | null;
}): FeishuGroupConfig | undefined {
const groups = params.cfg?.groups ?? {};
const wildcard = groups["*"];
const groupId = params.groupId?.trim();
if (!groupId) {
return undefined;
@@ -68,7 +69,10 @@ export function resolveFeishuGroupConfig(params: {
const lowered = groupId.toLowerCase();
const matchKey = Object.keys(groups).find((key) => key.toLowerCase() === lowered);
return matchKey ? groups[matchKey] : undefined;
if (matchKey) {
return groups[matchKey];
}
return wildcard;
}
export function resolveFeishuGroupToolPolicy(