@occum-net/occumclaw 0.2.2 → 0.4.0
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 +166 -0
- 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,159 @@ 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:
|
|
158
|
+
// channels.occum.accounts.<accountId>.groups.<channelId>.tools
|
|
159
|
+
// channels.occum.accounts.<accountId>.groups.<channelId>.toolsBySender
|
|
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 config at: channels.occum.accounts.<accountId>.groups.<groupId>
|
|
267
|
+
* with fallback to wildcard "*" group, then per-sender overrides.
|
|
268
|
+
*/
|
|
269
|
+
function resolveOccumToolPolicy(params: ChannelGroupContext): GroupToolPolicyConfig | undefined {
|
|
270
|
+
const { cfg, groupId, accountId, senderId, senderName, senderUsername, senderE164 } = params;
|
|
271
|
+
|
|
272
|
+
const acctId = accountId ?? "default";
|
|
273
|
+
const groups: Record<string, OccumGroupConfig> | undefined =
|
|
274
|
+
cfg?.channels?.occum?.accounts?.[acctId]?.groups;
|
|
275
|
+
|
|
276
|
+
if (!groups || typeof groups !== "object") return undefined;
|
|
277
|
+
|
|
278
|
+
const groupConfig = groupId ? groups[groupId] ?? undefined : undefined;
|
|
279
|
+
const defaultConfig = groups["*"] ?? undefined;
|
|
280
|
+
|
|
281
|
+
const senderInfo = { senderId, senderName, senderUsername, senderE164 };
|
|
282
|
+
|
|
283
|
+
// Priority 1: Group-specific sender override
|
|
284
|
+
const groupSender = resolveToolsBySender(groupConfig?.toolsBySender, senderInfo);
|
|
285
|
+
if (groupSender) return groupSender;
|
|
286
|
+
|
|
287
|
+
// Priority 2: Group-specific tool policy
|
|
288
|
+
if (groupConfig?.tools) return groupConfig.tools;
|
|
289
|
+
|
|
290
|
+
// Priority 3: Wildcard sender override
|
|
291
|
+
const defaultSender = resolveToolsBySender(defaultConfig?.toolsBySender, senderInfo);
|
|
292
|
+
if (defaultSender) return defaultSender;
|
|
293
|
+
|
|
294
|
+
// Priority 4: Wildcard tool policy
|
|
295
|
+
if (defaultConfig?.tools) return defaultConfig.tools;
|
|
296
|
+
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
|
|
145
300
|
// ─── Account Config ─────────────────────────────────────────────────────────
|
|
146
301
|
|
|
147
302
|
interface OccumAccount {
|
|
@@ -212,6 +367,10 @@ function createOccumChannelPlugin(logger: PluginLogger) {
|
|
|
212
367
|
chatTypes: ["direct" as const],
|
|
213
368
|
},
|
|
214
369
|
|
|
370
|
+
groups: {
|
|
371
|
+
resolveToolPolicy: resolveOccumToolPolicy,
|
|
372
|
+
},
|
|
373
|
+
|
|
215
374
|
config: {
|
|
216
375
|
listAccountIds: (cfg: any) => listAccountIds(cfg),
|
|
217
376
|
resolveAccount: (cfg: any, accountId?: string | null) => resolveAccount(cfg, accountId),
|
|
@@ -369,6 +528,11 @@ function createOccumChannelPlugin(logger: PluginLogger) {
|
|
|
369
528
|
const replyChannelId = event.channelId;
|
|
370
529
|
const sessionId = crypto.randomUUID();
|
|
371
530
|
|
|
531
|
+
// Extract sender username from event body if available
|
|
532
|
+
const senderUsername = (event.body as any)?.senderUsername
|
|
533
|
+
?? (event.body as any)?.username
|
|
534
|
+
?? undefined;
|
|
535
|
+
|
|
372
536
|
const msgCtx: MsgContext = {
|
|
373
537
|
Body: text,
|
|
374
538
|
BodyForAgent: text,
|
|
@@ -381,10 +545,12 @@ function createOccumChannelPlugin(logger: PluginLogger) {
|
|
|
381
545
|
AccountId: ctx.accountId,
|
|
382
546
|
Provider: "occum",
|
|
383
547
|
ChatType: "direct",
|
|
548
|
+
GroupId: event.channelId,
|
|
384
549
|
SenderId: event.senderType === "user"
|
|
385
550
|
? String(event.senderUserId)
|
|
386
551
|
: String(event.senderAgentId),
|
|
387
552
|
SenderName: senderLabel,
|
|
553
|
+
SenderUsername: senderUsername,
|
|
388
554
|
Timestamp: new Date(event.createdAt).getTime(),
|
|
389
555
|
CommandAuthorized: true,
|
|
390
556
|
MessageSid: event.id,
|