clawgram 2.19.1 → 2.19.4
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/channel.js +93 -30
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/channel.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.createChannelPlugin = void 0;
|
|
6
|
+
exports.createChannelPlugin = exports.CORE_ACTION_SYNONYMS = void 0;
|
|
7
7
|
const core_1 = require("openclaw/plugin-sdk/core");
|
|
8
8
|
const node_os_1 = __importDefault(require("node:os"));
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -177,8 +177,48 @@ function readAccountManageChats(account) {
|
|
|
177
177
|
const entries = Array.isArray(raw) ? raw : [raw];
|
|
178
178
|
return entries.map((entry) => String(entry).trim()).filter(Boolean);
|
|
179
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Core's own name for a clawgram action, and the only thing that makes the
|
|
182
|
+
* action reachable from the agent's `message` tool.
|
|
183
|
+
*
|
|
184
|
+
* Core keys its target policy by `CHANNEL_MESSAGE_ACTION_NAMES`, and an action
|
|
185
|
+
* outside that vocabulary is simultaneously "requires a target" and "does not
|
|
186
|
+
* accept a target" — there is no call that satisfies both. Declaring `chatId`
|
|
187
|
+
* through `messageActionTargetAliases` looks like the fix and is not: core
|
|
188
|
+
* resolves the channel with `getBootstrapChannelPlugin`, which only knows
|
|
189
|
+
* bundled channels, so a plugin channel's declaration is never read. Measured
|
|
190
|
+
* on 2026-08-30 — `thread-list` reached `handleAction` and `topics` did not,
|
|
191
|
+
* from the same caller, on the same chat.
|
|
192
|
+
*
|
|
193
|
+
* Every name on the right maps to core target mode `"none"` except
|
|
194
|
+
* `channel-info`, which is `"to"` and therefore arrives with the chat in
|
|
195
|
+
* `params.to` — a spelling all of these parsers already accept.
|
|
196
|
+
*/
|
|
197
|
+
exports.CORE_ACTION_SYNONYMS = {
|
|
198
|
+
"thread-list": "topics",
|
|
199
|
+
"channel-list": "dialogs",
|
|
200
|
+
"channel-info": "chatInfo",
|
|
201
|
+
"member-info": "participants",
|
|
202
|
+
"download-file": "fetch-media",
|
|
203
|
+
// Chat management. `kick` was already accepted; the rest were advertised
|
|
204
|
+
// under names core does not know and were therefore never callable from the
|
|
205
|
+
// tool at all — 2.19.4 gives them core's nearest name. `transferOwnership`
|
|
206
|
+
// and `inviteLink` have no counterpart in that vocabulary and stay
|
|
207
|
+
// gateway-only, as does `joins`.
|
|
208
|
+
"channel-create": "createGroup",
|
|
209
|
+
addParticipant: "addMembers",
|
|
210
|
+
kick: "removeMember",
|
|
211
|
+
"role-add": "promoteAdmin",
|
|
212
|
+
"role-remove": "demoteAdmin",
|
|
213
|
+
};
|
|
180
214
|
/** Canonical management action for every accepted spelling. */
|
|
181
215
|
const MANAGE_ACTION_ALIASES = {
|
|
216
|
+
// Core's spellings first — these are the only ones the agent's tool can
|
|
217
|
+
// reach; see CORE_ACTION_SYNONYMS.
|
|
218
|
+
"channel-create": "createGroup",
|
|
219
|
+
addParticipant: "addMembers",
|
|
220
|
+
"role-add": "promoteAdmin",
|
|
221
|
+
"role-remove": "demoteAdmin",
|
|
182
222
|
createGroup: "createGroup",
|
|
183
223
|
createChat: "createGroup",
|
|
184
224
|
"create-group": "createGroup",
|
|
@@ -405,11 +445,12 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
|
|
|
405
445
|
"Explicit targets may be @username, numeric Telegram user id, phone/contact resolvable by Telegram, group chat ids, or clawgram:<target>.",
|
|
406
446
|
"For Telegram forum topics, send to the group chat id and pass the topic id separately as `threadId`.",
|
|
407
447
|
"Use the `react` action to acknowledge a message with an emoji instead of sending a reply; pass an empty `emoji` (or `remove: true`) to take the reaction back.",
|
|
408
|
-
"Use the `
|
|
409
|
-
"Use the `
|
|
448
|
+
"Use the `channel-info` action to learn what a chat is — title, type, member count, description, pinned message — instead of guessing from its id. Name the chat with `chatId` and do not pass `target`: core refuses it for this action, and the descriptive spelling `chatInfo` is not callable from this tool at all.",
|
|
449
|
+
"Use the `thread-list` action to list a forum's topics by name (optional `query` narrows by title); that is where a `threadId` comes from when someone names a topic instead of quoting a message in it. Name the chat with `chatId` and do not pass `target` — core refuses it for this action. `topics` is the same call under a name core does not know, and is only reachable through the gateway RPC.",
|
|
410
450
|
"Pass that `threadId` to `read` as well: without it a forum read returns every topic interleaved rather than the one that was asked about.",
|
|
411
|
-
"Use the `download-file` action
|
|
412
|
-
"Use the `
|
|
451
|
+
"Use the `download-file` action to fetch the attachment on a message `read` reported. Name the chat with `chatId` and the message with `messageId`; do not pass `target` — core refuses it for this action: `mode: \"read\"` returns a description of an image or a transcript of a voice note, `\"file\"` returns a path to reuse, `\"both\"` (default) returns both. `read` only says an attachment exists; this is what brings it.",
|
|
452
|
+
"Use the `channel-list` action to find out which group chats this account is actually in — including ones nobody has configured yet. It reports id, title and type only, never direct chats, and only when the account enables `discoverChats`.",
|
|
453
|
+
"Use `member-info` with a `chatId` to list who is in a chat, and `kick` with a `chatId` and `userId` to remove someone from a managed chat. The rest of the chat-management family and `joins` have no name core knows, so they are reachable only through the gateway RPC, not from this tool.",
|
|
413
454
|
"Use `createGroup` (title, optional about, optional users) to create a new Telegram supergroup; `addMembers`/`removeMember` change who is in a managed chat, `promoteAdmin`/`demoteAdmin` grant or revoke admin rights, `transferOwnership` hands the chat over, `inviteLink` issues an invite link for people Telegram refused to add directly.",
|
|
414
455
|
],
|
|
415
456
|
messageToolCapabilities: () => [
|
|
@@ -417,10 +458,10 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
|
|
|
417
458
|
"clawgram can send text messages to direct chats and groups from the connected personal account.",
|
|
418
459
|
"clawgram supports Telegram forum topics via the `threadId` parameter on group sends.",
|
|
419
460
|
"clawgram can add and clear emoji reactions on messages. A plain Telegram account holds one reaction per message, so a new emoji replaces the previous one.",
|
|
420
|
-
"clawgram can describe a chat via `
|
|
421
|
-
"clawgram can list the topics of a forum supergroup via `
|
|
422
|
-
"clawgram can fetch the attachment on any message inside its read scope via `
|
|
423
|
-
"clawgram can list the group chats the account belongs to via `
|
|
461
|
+
"clawgram can describe a chat via `channel-info`: title, type (direct/group/supergroup/channel), member count, description, whether it is a forum, and the pinned message id.",
|
|
462
|
+
"clawgram can list the topics of a forum supergroup via `thread-list`: id, title, last message, and whether a topic is closed, hidden or pinned.",
|
|
463
|
+
"clawgram can fetch the attachment on any message inside its read scope via `download-file`: images come back described, voice notes transcribed, and either can be returned as a file path for reuse.",
|
|
464
|
+
"clawgram can list the group chats the account belongs to via `channel-list`, when the account sets discoverChats. Metadata only, no direct chats — it answers \"where am I\", not \"what was said\".",
|
|
424
465
|
"clawgram can manage chats where the account's manageChats config allows it: create supergroups, add and remove members, promote and demote admins, transfer ownership, and export invite links.",
|
|
425
466
|
],
|
|
426
467
|
},
|
|
@@ -1387,29 +1428,40 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
|
|
|
1387
1428
|
// Leaving it out does not degrade to a text send: the agent simply
|
|
1388
1429
|
// never sees a way to send the file, announces it in words, and the
|
|
1389
1430
|
// file stays on disk. That is exactly what happened on 2026-08-07.
|
|
1431
|
+
// Only names core already knows. An action outside
|
|
1432
|
+
// `CHANNEL_MESSAGE_ACTION_NAMES` cannot be called from the agent's
|
|
1433
|
+
// `message` tool at all — it is simultaneously "requires a target"
|
|
1434
|
+
// and "does not accept a target" — so advertising one is handing the
|
|
1435
|
+
// agent a trap. It cost a broken reply in a live chat on 2026-08-31:
|
|
1436
|
+
// the agent picked the descriptive `chatInfo`, got both halves of
|
|
1437
|
+
// the contradiction, and the turn ended in `✉️ Message failed`.
|
|
1438
|
+
//
|
|
1439
|
+
// The descriptive spellings (`topics`, `dialogs`, `chatInfo`,
|
|
1440
|
+
// `participants`, `joins`, `fetch-media`, the manage family) still
|
|
1441
|
+
// work in `handleAction`, so gateway RPC and existing skills keep
|
|
1442
|
+
// calling them — RPC does not consult this list. They are simply not
|
|
1443
|
+
// offered to the agent, which has no way to use them.
|
|
1444
|
+
//
|
|
1445
|
+
// `upload-file` is what core dispatches when an agent has an
|
|
1446
|
+
// attachment to deliver — a generated image is the common case.
|
|
1447
|
+
// Leaving it out does not degrade to a text send: the agent simply
|
|
1448
|
+
// never sees a way to send the file, announces it in words, and the
|
|
1449
|
+
// file stays on disk. That is exactly what happened on 2026-08-07.
|
|
1450
|
+
//
|
|
1451
|
+
// `kick` is core's name for `removeMember`; the rest of the manage
|
|
1452
|
+
// family has no core equivalent and stays gateway-only until it gets
|
|
1453
|
+
// one. `joins` likewise.
|
|
1390
1454
|
actions: [
|
|
1391
|
-
"send", "read", "
|
|
1455
|
+
"send", "read", "react", "upload-file",
|
|
1392
1456
|
// Reading an attachment that is already in a chat. `read` reports
|
|
1393
1457
|
// that a photo exists; this is what turns it into something the
|
|
1394
1458
|
// agent can look at or pass on.
|
|
1395
|
-
|
|
1396
|
-
//
|
|
1397
|
-
|
|
1398
|
-
// keyed by that vocabulary: an action it does not know is both
|
|
1399
|
-
// "requires a target" (`MESSAGE_ACTION_TARGET_MODE[action] !==
|
|
1400
|
-
// "none"` is true for `undefined`) and "does not accept a target"
|
|
1401
|
-
// (the same lookup defaults to `"none"` when a target is passed).
|
|
1402
|
-
// That contradiction is unresolvable from the caller's side —
|
|
1403
|
-
// measured on 2026-08-24, when the agent tried every combination
|
|
1404
|
-
// and got one of the two errors each time. `download-file` is
|
|
1405
|
-
// mapped to `"none"`, so it has no such contradiction;
|
|
1406
|
-
// `fetch-media` stays as the descriptive name and is made usable
|
|
1407
|
-
// by the alias declaration below.
|
|
1408
|
-
"fetch-media", "download-file",
|
|
1459
|
+
"download-file",
|
|
1460
|
+
// Core's names for the chat-shaped reads — see CORE_ACTION_SYNONYMS.
|
|
1461
|
+
"thread-list", "channel-list", "channel-info", "member-info",
|
|
1409
1462
|
// Chat management (2.12.0) — gated by the account's manageChats
|
|
1410
1463
|
// scope; without it every one of these is refused.
|
|
1411
|
-
"
|
|
1412
|
-
"promoteAdmin", "demoteAdmin", "transferOwnership", "inviteLink",
|
|
1464
|
+
"channel-create", "addParticipant", "kick", "role-add", "role-remove",
|
|
1413
1465
|
],
|
|
1414
1466
|
capabilities: [],
|
|
1415
1467
|
mediaSourceParams: {
|
|
@@ -1423,6 +1475,17 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
|
|
|
1423
1475
|
// reaches `handleAction`. The chat is named by `chatId` rather than
|
|
1424
1476
|
// `target` because core reserves `target` for actions in its own
|
|
1425
1477
|
// vocabulary and throws on it for everything else.
|
|
1478
|
+
//
|
|
1479
|
+
// This declaration alone does not rescue an action, and 2.19.1 read too
|
|
1480
|
+
// much into it. Core resolves the channel through
|
|
1481
|
+
// `getBootstrapChannelPlugin`, which only ever returns a *bundled*
|
|
1482
|
+
// channel; for a plugin channel the lookup misses and the declaration is
|
|
1483
|
+
// never consulted. Measured on the live server on 2026-08-30: `topics`
|
|
1484
|
+
// was refused for `target`, `chatId`, `groupId` and the prefixed form
|
|
1485
|
+
// alike even with `chatId` declared here. What actually carried
|
|
1486
|
+
// `fetch-media` through was its second name, `download-file` — see
|
|
1487
|
+
// CORE_ACTION_SYNONYMS. This stays because it costs nothing and is
|
|
1488
|
+
// correct the day core consults plugin channels too.
|
|
1426
1489
|
messageActionTargetAliases: {
|
|
1427
1490
|
"fetch-media": { aliases: ["chatId"] },
|
|
1428
1491
|
"download-file": { aliases: ["chatId"] },
|
|
@@ -1639,7 +1702,7 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
|
|
|
1639
1702
|
// Membership is a read, so the same `readChats` scope that gates history
|
|
1640
1703
|
// gates it too: this cannot become a way to enumerate chats the account
|
|
1641
1704
|
// was never allowed to read.
|
|
1642
|
-
if (action === "participants" || action === "members") {
|
|
1705
|
+
if (action === "participants" || action === "members" || action === "member-info") {
|
|
1643
1706
|
const participantsParams = (0, history_1.parseListParticipantsParams)(params);
|
|
1644
1707
|
const participantsAccountId = resolveRuntimeAccountId(cfg, accountId);
|
|
1645
1708
|
if (!participantsAccountId) {
|
|
@@ -1679,7 +1742,7 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
|
|
|
1679
1742
|
// id could only be lifted off an inbound message — so a topic nobody had
|
|
1680
1743
|
// written in yet was unreachable, and one named in words was unfindable.
|
|
1681
1744
|
// Titles say what a chat is working on, so the read scope gates them.
|
|
1682
|
-
if (action === "topics" || action === "forumTopics") {
|
|
1745
|
+
if (action === "topics" || action === "forumTopics" || action === "thread-list") {
|
|
1683
1746
|
const topicsParams = (0, topics_1.parseTopicsParams)(params);
|
|
1684
1747
|
const topicsAccountId = resolveRuntimeAccountId(cfg, accountId);
|
|
1685
1748
|
if (!topicsAccountId) {
|
|
@@ -1716,7 +1779,7 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
|
|
|
1716
1779
|
// Which chats this account is in. Not gated by `readChats` — the whole
|
|
1717
1780
|
// point is to find chats that are not in it yet — so it has a gate of
|
|
1718
1781
|
// its own, is metadata only, and never reports direct chats.
|
|
1719
|
-
if (action === "dialogs" || action === "chats") {
|
|
1782
|
+
if (action === "dialogs" || action === "chats" || action === "channel-list") {
|
|
1720
1783
|
const dialogsParams = (0, dialogs_1.parseDialogsParams)(params);
|
|
1721
1784
|
const dialogsAccountId = resolveRuntimeAccountId(cfg, accountId);
|
|
1722
1785
|
if (!dialogsAccountId) {
|
|
@@ -1776,7 +1839,7 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
|
|
|
1776
1839
|
// Describing a chat is a read, so the same `readChats` scope that gates
|
|
1777
1840
|
// history gates it too — this must not become a way to learn the title
|
|
1778
1841
|
// and size of a chat the account was never allowed to read.
|
|
1779
|
-
if (action === "chatInfo" || action === "getChatInfo"
|
|
1842
|
+
if (action === "chatInfo" || action === "getChatInfo" || action === "channel-info"
|
|
1780
1843
|
|| action === "chatMetadata" || action === "getChatMetadata") {
|
|
1781
1844
|
const chatInfoParams = (0, chat_info_1.parseChatInfoParams)(params, toolContext);
|
|
1782
1845
|
const chatInfoAccountId = resolveRuntimeAccountId(cfg, accountId);
|
package/openclaw.plugin.json
CHANGED
|
@@ -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.19.
|
|
5
|
+
"version": "2.19.4",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
package/package.json
CHANGED