mirror of https://github.com/openclaw/openclaw.git
fix(signal): add groups config to Signal channel schema (#27199)
Merged via squash.
Prepared head SHA: 4ba4a39ddf
Co-authored-by: unisone <32521398+unisone@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
This commit is contained in:
parent
4e68684bd2
commit
61429230b2
|
|
@ -22,6 +22,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Config/validation: accept documented `agents.list[].params` per-agent overrides in strict config validation so `openclaw config validate` no longer rejects runtime-supported `cacheRetention`, `temperature`, and `maxTokens` settings. (#41171) Thanks @atian8179.
|
||||
- Android/onboarding QR scan: switch setup QR scanning to Google Code Scanner so onboarding uses a more reliable scanner instead of the legacy embedded ZXing flow. (#45021) Thanks @obviyus.
|
||||
- Config/web fetch: restore runtime validation for documented `tools.web.fetch.readability` and `tools.web.fetch.firecrawl` settings so valid web fetch configs no longer fail with unrecognized-key errors. (#42583) Thanks @stim64045-spec.
|
||||
- Signal/config validation: add `channels.signal.groups` schema support so per-group `requireMention`, `tools`, and `toolsBySender` overrides no longer get rejected during config validation. (#27199) Thanks @unisone.
|
||||
|
||||
## 2026.3.12
|
||||
|
||||
|
|
|
|||
|
|
@ -195,6 +195,8 @@ Groups:
|
|||
|
||||
- `channels.signal.groupPolicy = open | allowlist | disabled`.
|
||||
- `channels.signal.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.
|
||||
- `channels.signal.groups["<group-id>" | "*"]` can override group behavior with `requireMention`, `tools`, and `toolsBySender`.
|
||||
- Use `channels.signal.accounts.<id>.groups` for per-account overrides in multi-account setups.
|
||||
- Runtime note: if `channels.signal` is completely missing, runtime falls back to `groupPolicy="allowlist"` for group checks (even if `channels.defaults.groupPolicy` is set).
|
||||
|
||||
## How it works (behavior)
|
||||
|
|
@ -312,6 +314,8 @@ Provider options:
|
|||
- `channels.signal.allowFrom`: DM allowlist (E.164 or `uuid:<id>`). `open` requires `"*"`. Signal has no usernames; use phone/UUID ids.
|
||||
- `channels.signal.groupPolicy`: `open | allowlist | disabled` (default: allowlist).
|
||||
- `channels.signal.groupAllowFrom`: group sender allowlist.
|
||||
- `channels.signal.groups`: per-group overrides keyed by Signal group id (or `"*"`). Supported fields: `requireMention`, `tools`, `toolsBySender`.
|
||||
- `channels.signal.accounts.<id>.groups`: per-account version of `channels.signal.groups` for multi-account setups.
|
||||
- `channels.signal.historyLimit`: max group messages to include as context (0 disables).
|
||||
- `channels.signal.dmHistoryLimit`: DM history limit in user turns. Per-user overrides: `channels.signal.dms["<phone_or_uuid>"].historyLimit`.
|
||||
- `channels.signal.textChunkLimit`: outbound chunk size (chars).
|
||||
|
|
|
|||
|
|
@ -1,8 +1,15 @@
|
|||
import type { CommonChannelMessagingConfig } from "./types.channel-messaging-common.js";
|
||||
import type { GroupToolPolicyBySenderConfig, GroupToolPolicyConfig } from "./types.tools.js";
|
||||
|
||||
export type SignalReactionNotificationMode = "off" | "own" | "all" | "allowlist";
|
||||
export type SignalReactionLevel = "off" | "ack" | "minimal" | "extensive";
|
||||
|
||||
export type SignalGroupConfig = {
|
||||
requireMention?: boolean;
|
||||
tools?: GroupToolPolicyConfig;
|
||||
toolsBySender?: GroupToolPolicyBySenderConfig;
|
||||
};
|
||||
|
||||
export type SignalAccountConfig = CommonChannelMessagingConfig & {
|
||||
/** Optional explicit E.164 account for signal-cli. */
|
||||
account?: string;
|
||||
|
|
@ -24,6 +31,8 @@ export type SignalAccountConfig = CommonChannelMessagingConfig & {
|
|||
ignoreAttachments?: boolean;
|
||||
ignoreStories?: boolean;
|
||||
sendReadReceipts?: boolean;
|
||||
/** Per-group overrides keyed by Signal group id (or "*"). */
|
||||
groups?: Record<string, SignalGroupConfig>;
|
||||
/** Outbound text chunk size (chars). Default: 4000. */
|
||||
textChunkLimit?: number;
|
||||
/** Reaction notification mode (off|own|all|allowlist). Default: own. */
|
||||
|
|
|
|||
|
|
@ -971,6 +971,16 @@ export const SlackConfigSchema = SlackAccountSchema.safeExtend({
|
|||
validateSlackSigningSecretRequirements(value, ctx);
|
||||
});
|
||||
|
||||
const SignalGroupEntrySchema = z
|
||||
.object({
|
||||
requireMention: z.boolean().optional(),
|
||||
tools: ToolPolicySchema,
|
||||
toolsBySender: ToolPolicyBySenderSchema,
|
||||
})
|
||||
.strict();
|
||||
|
||||
const SignalGroupsSchema = z.record(z.string(), SignalGroupEntrySchema.optional()).optional();
|
||||
|
||||
export const SignalAccountSchemaBase = z
|
||||
.object({
|
||||
name: z.string().optional(),
|
||||
|
|
@ -995,6 +1005,7 @@ export const SignalAccountSchemaBase = z
|
|||
defaultTo: z.string().optional(),
|
||||
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
||||
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
||||
groups: SignalGroupsSchema,
|
||||
historyLimit: z.number().int().min(0).optional(),
|
||||
dmHistoryLimit: z.number().int().min(0).optional(),
|
||||
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { validateConfigObject } from "./config.js";
|
||||
|
||||
describe("signal groups schema", () => {
|
||||
it("accepts top-level Signal groups overrides", () => {
|
||||
const res = validateConfigObject({
|
||||
channels: {
|
||||
signal: {
|
||||
groups: {
|
||||
"*": {
|
||||
requireMention: false,
|
||||
},
|
||||
"+1234567890": {
|
||||
requireMention: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts per-account Signal groups overrides", () => {
|
||||
const res = validateConfigObject({
|
||||
channels: {
|
||||
signal: {
|
||||
accounts: {
|
||||
primary: {
|
||||
groups: {
|
||||
"*": {
|
||||
requireMention: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects unknown keys in Signal groups entries", () => {
|
||||
const res = validateConfigObject({
|
||||
channels: {
|
||||
signal: {
|
||||
groups: {
|
||||
"*": {
|
||||
requireMention: false,
|
||||
nope: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok).toBe(false);
|
||||
if (!res.ok) {
|
||||
expect(res.issues.some((issue) => issue.path.startsWith("channels.signal.groups"))).toBe(
|
||||
true,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue