clawgram 2.14.0 → 2.17.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/dist/normalize.js CHANGED
@@ -4,6 +4,7 @@ exports.toStringId = toStringId;
4
4
  exports.toPeerChatId = toPeerChatId;
5
5
  exports.toPeerChannelId = toPeerChannelId;
6
6
  exports.normalizeTelegramEvent = normalizeTelegramEvent;
7
+ const helpers_1 = require("./helpers");
7
8
  function toStringId(value) {
8
9
  if (value === null || value === undefined)
9
10
  return undefined;
@@ -106,11 +107,10 @@ function normalizeTelegramEvent(event, accountId) {
106
107
  ? msg.text
107
108
  : undefined;
108
109
  const chatType = inferTelegramChatType(msg, chatId);
109
- const senderUsername = typeof msg.sender?.username === "string"
110
- ? msg.sender.username
111
- : typeof msg._sender?.username === "string"
112
- ? msg._sender.username
113
- : undefined;
110
+ // Not the raw field: a sender holding several handles (or a collectible one)
111
+ // keeps them in `usernames[]` and leaves `username` empty, so an allowFrom
112
+ // entry written as `@handle` would silently never match that person.
113
+ const senderUsername = (0, helpers_1.resolveActiveUsername)(msg.sender) ?? (0, helpers_1.resolveActiveUsername)(msg._sender);
114
114
  const senderDisplay = typeof msg.sender?.firstName === "string"
115
115
  ? [msg.sender.firstName, msg.sender.lastName].filter(Boolean).join(" ").trim()
116
116
  : typeof msg._sender?.firstName === "string"
package/dist/topics.js ADDED
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TOPICS_MAX_LIMIT = exports.TOPICS_DEFAULT_LIMIT = void 0;
4
+ exports.parseTopicsParams = parseTopicsParams;
5
+ exports.normalizeForumTopics = normalizeForumTopics;
6
+ /**
7
+ * Forum topics — the names a supergroup organises itself by.
8
+ *
9
+ * A forum chat addresses replies by topic, and `chatInfo` can only say *that*
10
+ * a chat is a forum. Everything else about a topic reached the assistant as a
11
+ * bare number lifted off an inbound message, so a person could ask it to work
12
+ * in "Визитка - представление" and it had no way to turn that name into an id —
13
+ * or to know the topic existed before somebody wrote in it.
14
+ *
15
+ * Parsing and shaping are pure so they can be tested without a Telegram
16
+ * client; the transport lives in `GramJsClientManager.listTopics`.
17
+ */
18
+ const normalize_js_1 = require("./normalize.js");
19
+ exports.TOPICS_DEFAULT_LIMIT = 100;
20
+ exports.TOPICS_MAX_LIMIT = 500;
21
+ function parseTopicsParams(params) {
22
+ const rawTarget = params.chatId ?? params.target ?? params.to ?? params.chat;
23
+ const target = typeof rawTarget === "string" ? rawTarget.trim() : "";
24
+ if (!target) {
25
+ throw new Error("clawgram: topics requires a chatId");
26
+ }
27
+ const rawQuery = params.query ?? params.search ?? params.title;
28
+ const trimmedQuery = typeof rawQuery === "string" ? rawQuery.trim() : "";
29
+ const query = trimmedQuery.length > 0 ? trimmedQuery : undefined;
30
+ const rawLimit = params.limit;
31
+ if (rawLimit === undefined || rawLimit === null || rawLimit === "") {
32
+ return { target, limit: exports.TOPICS_DEFAULT_LIMIT, query };
33
+ }
34
+ const parsed = Number(rawLimit);
35
+ if (!Number.isFinite(parsed) || parsed <= 0) {
36
+ throw new Error("clawgram: topics limit must be a positive number");
37
+ }
38
+ return { target, limit: Math.min(Math.floor(parsed), exports.TOPICS_MAX_LIMIT), query };
39
+ }
40
+ /** Telegram sends flags only when they are set; an absent flag is not `false`. */
41
+ function optionalFlag(value) {
42
+ return typeof value === "boolean" ? value : undefined;
43
+ }
44
+ function matchesQuery(title, query) {
45
+ if (!query)
46
+ return true;
47
+ return title.toLocaleLowerCase().includes(query.toLocaleLowerCase());
48
+ }
49
+ /**
50
+ * `channels.getForumTopics` returns live topics and tombstones in one list.
51
+ * A `ForumTopicDeleted` carries an id and nothing else: it cannot be named,
52
+ * so it is dropped rather than surfaced as a topic with a blank title.
53
+ */
54
+ function normalizeForumTopics(raw, options = {}) {
55
+ if (!Array.isArray(raw))
56
+ return [];
57
+ const topics = [];
58
+ for (const entry of raw) {
59
+ if (!entry || typeof entry !== "object")
60
+ continue;
61
+ const candidate = entry;
62
+ const title = typeof candidate.title === "string" ? candidate.title : undefined;
63
+ const topicId = (0, normalize_js_1.toStringId)(candidate.id);
64
+ if (!title || !topicId)
65
+ continue;
66
+ if (!matchesQuery(title, options.query))
67
+ continue;
68
+ const topic = { topicId, title };
69
+ const topMessageId = (0, normalize_js_1.toStringId)(candidate.topMessage);
70
+ if (topMessageId !== undefined)
71
+ topic.topMessageId = topMessageId;
72
+ const closed = optionalFlag(candidate.closed);
73
+ if (closed !== undefined)
74
+ topic.closed = closed;
75
+ const hidden = optionalFlag(candidate.hidden);
76
+ if (hidden !== undefined)
77
+ topic.hidden = hidden;
78
+ const pinned = optionalFlag(candidate.pinned);
79
+ if (pinned !== undefined)
80
+ topic.pinned = pinned;
81
+ topics.push(topic);
82
+ }
83
+ return topics;
84
+ }
@@ -2,7 +2,7 @@
2
2
  "id": "clawgram",
3
3
  "name": "Clawgram",
4
4
  "description": "Clawgram — personal Telegram (MTProto userbot) channel for OpenClaw. Your AI assistant reads and responds as you.",
5
- "version": "2.14.0",
5
+ "version": "2.17.0",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
@@ -69,6 +69,69 @@
69
69
  }
70
70
  ]
71
71
  }
72
+ },
73
+ "tools": {
74
+ "type": "object",
75
+ "additionalProperties": false,
76
+ "properties": {
77
+ "allow": {
78
+ "type": "array",
79
+ "items": {
80
+ "type": "string"
81
+ }
82
+ },
83
+ "alsoAllow": {
84
+ "type": "array",
85
+ "items": {
86
+ "type": "string"
87
+ }
88
+ },
89
+ "deny": {
90
+ "type": "array",
91
+ "items": {
92
+ "type": "string"
93
+ }
94
+ }
95
+ }
96
+ },
97
+ "toolsBySender": {
98
+ "type": "object",
99
+ "propertyNames": {
100
+ "type": "string"
101
+ },
102
+ "additionalProperties": {
103
+ "type": "object",
104
+ "additionalProperties": false,
105
+ "properties": {
106
+ "allow": {
107
+ "type": "array",
108
+ "items": {
109
+ "type": "string"
110
+ }
111
+ },
112
+ "alsoAllow": {
113
+ "type": "array",
114
+ "items": {
115
+ "type": "string"
116
+ }
117
+ },
118
+ "deny": {
119
+ "type": "array",
120
+ "items": {
121
+ "type": "string"
122
+ }
123
+ }
124
+ }
125
+ }
126
+ },
127
+ "skills": {
128
+ "type": "array",
129
+ "items": {
130
+ "type": "string"
131
+ }
132
+ },
133
+ "systemPrompt": {
134
+ "type": "string"
72
135
  }
73
136
  }
74
137
  }
@@ -311,6 +374,69 @@
311
374
  }
312
375
  ]
313
376
  }
377
+ },
378
+ "tools": {
379
+ "type": "object",
380
+ "additionalProperties": false,
381
+ "properties": {
382
+ "allow": {
383
+ "type": "array",
384
+ "items": {
385
+ "type": "string"
386
+ }
387
+ },
388
+ "alsoAllow": {
389
+ "type": "array",
390
+ "items": {
391
+ "type": "string"
392
+ }
393
+ },
394
+ "deny": {
395
+ "type": "array",
396
+ "items": {
397
+ "type": "string"
398
+ }
399
+ }
400
+ }
401
+ },
402
+ "toolsBySender": {
403
+ "type": "object",
404
+ "propertyNames": {
405
+ "type": "string"
406
+ },
407
+ "additionalProperties": {
408
+ "type": "object",
409
+ "additionalProperties": false,
410
+ "properties": {
411
+ "allow": {
412
+ "type": "array",
413
+ "items": {
414
+ "type": "string"
415
+ }
416
+ },
417
+ "alsoAllow": {
418
+ "type": "array",
419
+ "items": {
420
+ "type": "string"
421
+ }
422
+ },
423
+ "deny": {
424
+ "type": "array",
425
+ "items": {
426
+ "type": "string"
427
+ }
428
+ }
429
+ }
430
+ }
431
+ },
432
+ "skills": {
433
+ "type": "array",
434
+ "items": {
435
+ "type": "string"
436
+ }
437
+ },
438
+ "systemPrompt": {
439
+ "type": "string"
314
440
  }
315
441
  }
316
442
  }
@@ -329,6 +455,10 @@
329
455
  },
330
456
  "description": "Chat ids the account may MANAGE (2.12.0): add/remove members, promote/demote admins, transfer ownership, export invite links; a non-empty list also unlocks createGroup. Opposite default to readChats: absent or empty means chat management is off, [\"*\"] allows every chat."
331
457
  },
458
+ "discoverChats": {
459
+ "type": "boolean",
460
+ "description": "Allow the `dialogs` action (2.16.0) to list the group chats this account belongs to — id, title and type only, never direct chats and never message content. Off unless set: it is the one read that deliberately reaches past readChats, because its job is to find chats nobody has configured yet."
461
+ },
332
462
  "twoFaPassword": {
333
463
  "description": "The account's Telegram 2FA (cloud) password — needed only by transferOwnership, which Telegram guards with an SRP proof. Literal value, or a SecretRef resolved at account start-up.",
334
464
  "anyOf": [
@@ -382,9 +512,10 @@
382
512
  "enum": [
383
513
  "markdown",
384
514
  "md",
385
- "html"
515
+ "html",
516
+ "none"
386
517
  ],
387
- "description": "Parse mode for replies (2.3.1). The send action takes parseMode per call; the reply pipeline has no per-call slot, so the reply format is configured here. Absent means plain text."
518
+ "description": "Outbound format for replies, core-delivered text, captions and send calls that omit parseMode. html (2.15.0) renders agent markdown and Telegram HTML into entities; markdown is GramJS's five-delimiter parser; none disables parsing entirely. Absent keeps GramJS's historical default, which is its markdown parser not plain text."
388
519
  }
389
520
  },
390
521
  "required": [
@@ -402,6 +533,26 @@
402
533
  "help": "Chat ids the assistant may manage (create groups, add/remove members, admins, ownership, invite links). Leave empty to keep chat management off; use * to allow every chat.",
403
534
  "advanced": true
404
535
  },
536
+ "accounts.*.groups.*.tools": {
537
+ "label": "Group Tool Policy",
538
+ "help": "Tool allow/alsoAllow/deny for this group. Applies to gateway tools (message, sessions_*, cron, memory_*, …). Under CLI backends such as claude-cli the native exec/read/write tools are not filtered per group — bind the group to a separate agent for that.",
539
+ "advanced": true
540
+ },
541
+ "accounts.*.groups.*.toolsBySender": {
542
+ "label": "Group Tool Policy by Sender",
543
+ "help": "Per-sender tool policy inside this group. Keys use core's typed grammar: id:<telegram id>, username:<handle>, name:<display name>, or * as the fallback.",
544
+ "advanced": true
545
+ },
546
+ "accounts.*.groups.*.skills": {
547
+ "label": "Group Skills",
548
+ "help": "Skill allowlist for this group. Omit to inherit the agent's skills; [] means no skills in this group.",
549
+ "advanced": true
550
+ },
551
+ "accounts.*.groups.*.systemPrompt": {
552
+ "label": "Group System Prompt",
553
+ "help": "Trusted prompt block appended for messages from this group — the place to state what this chat is about and what stays out of it.",
554
+ "advanced": true
555
+ },
405
556
  "accounts.*.twoFaPassword": {
406
557
  "label": "2FA Password",
407
558
  "help": "The account's Telegram two-step verification password. Only needed for ownership transfer.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawgram",
3
- "version": "2.14.0",
3
+ "version": "2.17.0",
4
4
  "description": "Clawgram — personal Telegram (MTProto userbot) channel for OpenClaw. Your AI assistant reads and responds as you.",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {