@occum-net/occumclaw 0.2.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +171 -0
- package/openclaw.plugin.json +33 -1
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -74,8 +74,10 @@ interface MsgContext {
|
|
|
74
74
|
AccountId?: string;
|
|
75
75
|
Provider?: string;
|
|
76
76
|
ChatType?: string;
|
|
77
|
+
GroupId?: string;
|
|
77
78
|
SenderId?: string;
|
|
78
79
|
SenderName?: string;
|
|
80
|
+
SenderUsername?: string;
|
|
79
81
|
Timestamp?: number;
|
|
80
82
|
CommandAuthorized?: boolean;
|
|
81
83
|
MessageSid?: string;
|
|
@@ -142,6 +144,164 @@ interface OpenClawPluginApi {
|
|
|
142
144
|
registerChannel(registration: { plugin: any }): void;
|
|
143
145
|
}
|
|
144
146
|
|
|
147
|
+
// ─── Group Tool Policy (channel-level + per-sender overrides) ───────────────
|
|
148
|
+
//
|
|
149
|
+
// Follows the same resolution hierarchy as OpenClaw's built-in Telegram channel:
|
|
150
|
+
//
|
|
151
|
+
// 1. Group-specific sender override (groups.<channelId>.toolsBySender)
|
|
152
|
+
// 2. Group-specific tool policy (groups.<channelId>.tools)
|
|
153
|
+
// 3. Wildcard sender override (groups.*.toolsBySender)
|
|
154
|
+
// 4. Wildcard tool policy (groups.*.tools)
|
|
155
|
+
// 5. undefined → fall back to global (tools.allow / tools.profile)
|
|
156
|
+
//
|
|
157
|
+
// Config path (either works; plugin-style avoids config validation issues):
|
|
158
|
+
// channels.occum.accounts.<accountId>.groups.<channelId>.tools
|
|
159
|
+
// plugins.entries.occumclaw.config.groups.<channelId>.tools
|
|
160
|
+
//
|
|
161
|
+
// Per-sender key format:
|
|
162
|
+
// "id:<senderId>" — match by user/agent ID
|
|
163
|
+
// "username:<handle>" — match by username
|
|
164
|
+
// "name:<displayName>" — match by display name
|
|
165
|
+
// "*" — wildcard (any sender)
|
|
166
|
+
|
|
167
|
+
/** Tool policy: allow/alsoAllow/deny lists for a group or sender. */
|
|
168
|
+
interface GroupToolPolicyConfig {
|
|
169
|
+
allow?: string[];
|
|
170
|
+
alsoAllow?: string[];
|
|
171
|
+
deny?: string[];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Per-sender overrides keyed by "id:<val>", "username:<val>", "name:<val>", or "*". */
|
|
175
|
+
type GroupToolPolicyBySenderConfig = Record<string, GroupToolPolicyConfig>;
|
|
176
|
+
|
|
177
|
+
/** Config for a single occum.net channel (group) or the wildcard "*" default. */
|
|
178
|
+
interface OccumGroupConfig {
|
|
179
|
+
tools?: GroupToolPolicyConfig;
|
|
180
|
+
toolsBySender?: GroupToolPolicyBySenderConfig;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** ChannelGroupContext passed by OpenClaw to resolveToolPolicy. */
|
|
184
|
+
interface ChannelGroupContext {
|
|
185
|
+
cfg: any;
|
|
186
|
+
groupId?: string | null;
|
|
187
|
+
groupChannel?: string | null;
|
|
188
|
+
groupSpace?: string | null;
|
|
189
|
+
accountId?: string | null;
|
|
190
|
+
senderId?: string | null;
|
|
191
|
+
senderName?: string | null;
|
|
192
|
+
senderUsername?: string | null;
|
|
193
|
+
senderE164?: string | null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const SENDER_KEY_TYPES = ["id", "e164", "username", "name"] as const;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Match a sender against toolsBySender entries.
|
|
200
|
+
*
|
|
201
|
+
* Resolution order: id → e164 → username → name → wildcard "*".
|
|
202
|
+
* First match wins. All comparisons are case-insensitive.
|
|
203
|
+
*/
|
|
204
|
+
function resolveToolsBySender(
|
|
205
|
+
toolsBySender: GroupToolPolicyBySenderConfig | undefined,
|
|
206
|
+
sender: { senderId?: string | null; senderName?: string | null; senderUsername?: string | null; senderE164?: string | null },
|
|
207
|
+
): GroupToolPolicyConfig | undefined {
|
|
208
|
+
if (!toolsBySender) return undefined;
|
|
209
|
+
|
|
210
|
+
// Build lookup buckets and extract wildcard
|
|
211
|
+
const buckets: Record<string, Map<string, GroupToolPolicyConfig>> = {
|
|
212
|
+
id: new Map(),
|
|
213
|
+
e164: new Map(),
|
|
214
|
+
username: new Map(),
|
|
215
|
+
name: new Map(),
|
|
216
|
+
};
|
|
217
|
+
let wildcard: GroupToolPolicyConfig | undefined;
|
|
218
|
+
|
|
219
|
+
for (const [rawKey, policy] of Object.entries(toolsBySender)) {
|
|
220
|
+
if (!policy) continue;
|
|
221
|
+
const trimmed = rawKey.trim();
|
|
222
|
+
if (!trimmed) continue;
|
|
223
|
+
|
|
224
|
+
if (trimmed === "*") {
|
|
225
|
+
wildcard = policy;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const lower = trimmed.toLowerCase();
|
|
230
|
+
for (const type of SENDER_KEY_TYPES) {
|
|
231
|
+
const prefix = `${type}:`;
|
|
232
|
+
if (lower.startsWith(prefix)) {
|
|
233
|
+
const value = trimmed.slice(prefix.length).trim().toLowerCase();
|
|
234
|
+
if (value && !buckets[type].has(value)) {
|
|
235
|
+
buckets[type].set(value, policy);
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Match in priority order: id → e164 → username → name → wildcard
|
|
243
|
+
if (sender.senderId) {
|
|
244
|
+
const match = buckets.id.get(sender.senderId.trim().toLowerCase());
|
|
245
|
+
if (match) return match;
|
|
246
|
+
}
|
|
247
|
+
if (sender.senderE164) {
|
|
248
|
+
const match = buckets.e164.get(sender.senderE164.trim().toLowerCase());
|
|
249
|
+
if (match) return match;
|
|
250
|
+
}
|
|
251
|
+
if (sender.senderUsername) {
|
|
252
|
+
const match = buckets.username.get(sender.senderUsername.trim().toLowerCase());
|
|
253
|
+
if (match) return match;
|
|
254
|
+
}
|
|
255
|
+
if (sender.senderName) {
|
|
256
|
+
const match = buckets.name.get(sender.senderName.trim().toLowerCase());
|
|
257
|
+
if (match) return match;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return wildcard;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Resolve the effective tool policy for an inbound occum.net message.
|
|
265
|
+
*
|
|
266
|
+
* Looks up groups config from (in order):
|
|
267
|
+
* 1. channels.occum.accounts.<accountId>.groups (channel-style, if OpenClaw validates it)
|
|
268
|
+
* 2. plugins.entries.occumclaw.config.groups (plugin-style, always safe)
|
|
269
|
+
*
|
|
270
|
+
* Then resolves per-channel and per-sender overrides with wildcard fallback.
|
|
271
|
+
*/
|
|
272
|
+
function resolveOccumToolPolicy(params: ChannelGroupContext): GroupToolPolicyConfig | undefined {
|
|
273
|
+
const { cfg, groupId, accountId, senderId, senderName, senderUsername, senderE164 } = params;
|
|
274
|
+
|
|
275
|
+
// Try channel-style config first, then fall back to plugin config
|
|
276
|
+
const acctId = accountId ?? "default";
|
|
277
|
+
const groups: Record<string, OccumGroupConfig> | undefined =
|
|
278
|
+
cfg?.channels?.occum?.accounts?.[acctId]?.groups
|
|
279
|
+
?? cfg?.plugins?.entries?.occumclaw?.config?.groups;
|
|
280
|
+
|
|
281
|
+
if (!groups || typeof groups !== "object") return undefined;
|
|
282
|
+
|
|
283
|
+
const groupConfig = groupId ? groups[groupId] ?? undefined : undefined;
|
|
284
|
+
const defaultConfig = groups["*"] ?? undefined;
|
|
285
|
+
|
|
286
|
+
const senderInfo = { senderId, senderName, senderUsername, senderE164 };
|
|
287
|
+
|
|
288
|
+
// Priority 1: Group-specific sender override
|
|
289
|
+
const groupSender = resolveToolsBySender(groupConfig?.toolsBySender, senderInfo);
|
|
290
|
+
if (groupSender) return groupSender;
|
|
291
|
+
|
|
292
|
+
// Priority 2: Group-specific tool policy
|
|
293
|
+
if (groupConfig?.tools) return groupConfig.tools;
|
|
294
|
+
|
|
295
|
+
// Priority 3: Wildcard sender override
|
|
296
|
+
const defaultSender = resolveToolsBySender(defaultConfig?.toolsBySender, senderInfo);
|
|
297
|
+
if (defaultSender) return defaultSender;
|
|
298
|
+
|
|
299
|
+
// Priority 4: Wildcard tool policy
|
|
300
|
+
if (defaultConfig?.tools) return defaultConfig.tools;
|
|
301
|
+
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
|
|
145
305
|
// ─── Account Config ─────────────────────────────────────────────────────────
|
|
146
306
|
|
|
147
307
|
interface OccumAccount {
|
|
@@ -212,6 +372,10 @@ function createOccumChannelPlugin(logger: PluginLogger) {
|
|
|
212
372
|
chatTypes: ["direct" as const],
|
|
213
373
|
},
|
|
214
374
|
|
|
375
|
+
groups: {
|
|
376
|
+
resolveToolPolicy: resolveOccumToolPolicy,
|
|
377
|
+
},
|
|
378
|
+
|
|
215
379
|
config: {
|
|
216
380
|
listAccountIds: (cfg: any) => listAccountIds(cfg),
|
|
217
381
|
resolveAccount: (cfg: any, accountId?: string | null) => resolveAccount(cfg, accountId),
|
|
@@ -369,6 +533,11 @@ function createOccumChannelPlugin(logger: PluginLogger) {
|
|
|
369
533
|
const replyChannelId = event.channelId;
|
|
370
534
|
const sessionId = crypto.randomUUID();
|
|
371
535
|
|
|
536
|
+
// Extract sender username from event body if available
|
|
537
|
+
const senderUsername = (event.body as any)?.senderUsername
|
|
538
|
+
?? (event.body as any)?.username
|
|
539
|
+
?? undefined;
|
|
540
|
+
|
|
372
541
|
const msgCtx: MsgContext = {
|
|
373
542
|
Body: text,
|
|
374
543
|
BodyForAgent: text,
|
|
@@ -381,10 +550,12 @@ function createOccumChannelPlugin(logger: PluginLogger) {
|
|
|
381
550
|
AccountId: ctx.accountId,
|
|
382
551
|
Provider: "occum",
|
|
383
552
|
ChatType: "direct",
|
|
553
|
+
GroupId: event.channelId,
|
|
384
554
|
SenderId: event.senderType === "user"
|
|
385
555
|
? String(event.senderUserId)
|
|
386
556
|
: String(event.senderAgentId),
|
|
387
557
|
SenderName: senderLabel,
|
|
558
|
+
SenderUsername: senderUsername,
|
|
388
559
|
Timestamp: new Date(event.createdAt).getTime(),
|
|
389
560
|
CommandAuthorized: true,
|
|
390
561
|
MessageSid: event.id,
|
package/openclaw.plugin.json
CHANGED
|
@@ -9,7 +9,39 @@
|
|
|
9
9
|
"additionalProperties": false,
|
|
10
10
|
"properties": {
|
|
11
11
|
"agentToken": { "type": "string" },
|
|
12
|
-
"apiUrl": { "type": "string", "default": "https://api.occum.net" }
|
|
12
|
+
"apiUrl": { "type": "string", "default": "https://api.occum.net" },
|
|
13
|
+
"groups": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"description": "Per-channel and per-sender tool policy overrides. Keys are channel IDs or \"*\" for wildcard.",
|
|
16
|
+
"additionalProperties": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"tools": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"allow": { "type": "array", "items": { "type": "string" } },
|
|
23
|
+
"alsoAllow": { "type": "array", "items": { "type": "string" } },
|
|
24
|
+
"deny": { "type": "array", "items": { "type": "string" } }
|
|
25
|
+
},
|
|
26
|
+
"additionalProperties": false
|
|
27
|
+
},
|
|
28
|
+
"toolsBySender": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"description": "Per-sender overrides keyed by id:<val>, username:<val>, name:<val>, or *",
|
|
31
|
+
"additionalProperties": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"properties": {
|
|
34
|
+
"allow": { "type": "array", "items": { "type": "string" } },
|
|
35
|
+
"alsoAllow": { "type": "array", "items": { "type": "string" } },
|
|
36
|
+
"deny": { "type": "array", "items": { "type": "string" } }
|
|
37
|
+
},
|
|
38
|
+
"additionalProperties": false
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"additionalProperties": false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
13
45
|
},
|
|
14
46
|
"required": []
|
|
15
47
|
},
|