@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.
Files changed (2) hide show
  1. package/index.ts +166 -0
  2. 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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@occum-net/occumclaw",
3
- "version": "0.2.2",
3
+ "version": "0.4.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },