clawgram 2.5.0 → 2.6.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/channel.js +97 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/channel.js
CHANGED
|
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.createChannelPlugin = void 0;
|
|
7
7
|
const core_1 = require("openclaw/plugin-sdk/core");
|
|
8
8
|
const node_os_1 = __importDefault(require("node:os"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
9
11
|
/** Attachments above this are left unread: a long recording or a huge image is
|
|
10
12
|
* a different conversation from a spoken line or a screenshot, and the
|
|
11
13
|
* transfer is not free. */
|
|
@@ -77,6 +79,25 @@ function parseOptionalThreadId(value) {
|
|
|
77
79
|
* "you sent something I could not read" than staying silent, which is
|
|
78
80
|
* indistinguishable from being offline.
|
|
79
81
|
*/
|
|
82
|
+
/**
|
|
83
|
+
* Locates the agent directory that image understanding needs.
|
|
84
|
+
*
|
|
85
|
+
* Image models are called with the agent's own credentials, so the pipeline
|
|
86
|
+
* refuses to run without this path — audio does not need it, which is why
|
|
87
|
+
* voice notes worked before images did. The platform exposes no resolver to
|
|
88
|
+
* plugins, so the documented layout is reconstructed here and checked before
|
|
89
|
+
* use: a wrong guess would fail the read anyway, and returning undefined lets
|
|
90
|
+
* the caller degrade instead of throwing.
|
|
91
|
+
*/
|
|
92
|
+
function resolveAgentDirForMedia(cfg) {
|
|
93
|
+
const stateDir = typeof process.env.OPENCLAW_STATE_DIR === "string" && process.env.OPENCLAW_STATE_DIR.trim()
|
|
94
|
+
? process.env.OPENCLAW_STATE_DIR.trim()
|
|
95
|
+
: node_path_1.default.join(node_os_1.default.homedir(), ".openclaw");
|
|
96
|
+
const configuredId = cfg?.agents?.defaults?.id;
|
|
97
|
+
const agentId = typeof configuredId === "string" && configuredId.trim() ? configuredId.trim() : "main";
|
|
98
|
+
const dir = node_path_1.default.join(stateDir, "agents", agentId, "agent");
|
|
99
|
+
return (0, node_fs_1.existsSync)(dir) ? dir : undefined;
|
|
100
|
+
}
|
|
80
101
|
async function readInboundAttachment(params) {
|
|
81
102
|
const media = params.runtime?.mediaUnderstanding;
|
|
82
103
|
const message = params.event?.message;
|
|
@@ -115,6 +136,7 @@ async function readInboundAttachment(params) {
|
|
|
115
136
|
filePath: downloaded.path,
|
|
116
137
|
cfg: params.cfg,
|
|
117
138
|
mime: downloaded.mimeType,
|
|
139
|
+
agentDir: resolveAgentDirForMedia(params.cfg),
|
|
118
140
|
});
|
|
119
141
|
const read = typeof result?.text === "string" ? result.text.trim() : "";
|
|
120
142
|
if (!read) {
|
|
@@ -1098,8 +1120,16 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
|
|
|
1098
1120
|
return null;
|
|
1099
1121
|
}
|
|
1100
1122
|
return {
|
|
1101
|
-
|
|
1123
|
+
// `upload-file` is what core dispatches when an agent has an
|
|
1124
|
+
// attachment to deliver — a generated image is the common case.
|
|
1125
|
+
// Leaving it out does not degrade to a text send: the agent simply
|
|
1126
|
+
// never sees a way to send the file, announces it in words, and the
|
|
1127
|
+
// file stays on disk. That is exactly what happened on 2026-08-07.
|
|
1128
|
+
actions: ["send", "read", "participants", "joins", "react", "chatInfo", "upload-file"],
|
|
1102
1129
|
capabilities: [],
|
|
1130
|
+
mediaSourceParams: {
|
|
1131
|
+
"upload-file": ["filePath", "path", "media"],
|
|
1132
|
+
},
|
|
1103
1133
|
};
|
|
1104
1134
|
},
|
|
1105
1135
|
extractToolSend: ({ args }) => (0, tool_send_1.extractToolSend)(args, "sendMessage"),
|
|
@@ -1286,6 +1316,72 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
|
|
|
1286
1316
|
removed: reactionParams.remove,
|
|
1287
1317
|
});
|
|
1288
1318
|
}
|
|
1319
|
+
// Core dispatches `upload-file`; `sendAttachment` is its legacy alias
|
|
1320
|
+
// and arrives from older callers, so both land here.
|
|
1321
|
+
if (action === "upload-file" || action === "sendAttachment") {
|
|
1322
|
+
const rawUploadTo = (0, helpers_1.resolveActionTarget)(params, toolContext);
|
|
1323
|
+
const uploadTo = (0, helpers_1.normalizeOutboundTarget)(rawUploadTo);
|
|
1324
|
+
const uploadAccountId = resolveRuntimeAccountId(cfg, accountId);
|
|
1325
|
+
if (!uploadAccountId) {
|
|
1326
|
+
throw new Error("clawgram: no configured account found");
|
|
1327
|
+
}
|
|
1328
|
+
// Core normalizes whichever of these it filled in to a local path
|
|
1329
|
+
// (see `mediaSourceParams` above); `mediaUrl` stays a URL, which
|
|
1330
|
+
// GramJS accepts as well.
|
|
1331
|
+
const file = (0, param_readers_1.readStringParam)(params, "filePath")
|
|
1332
|
+
?? (0, param_readers_1.readStringParam)(params, "path")
|
|
1333
|
+
?? (0, param_readers_1.readStringParam)(params, "media")
|
|
1334
|
+
?? (0, param_readers_1.readStringParam)(params, "mediaUrl");
|
|
1335
|
+
if (!file) {
|
|
1336
|
+
throw new Error("clawgram: upload-file requires filePath, path, media, or mediaUrl");
|
|
1337
|
+
}
|
|
1338
|
+
const captionText = (0, helpers_1.readMessageText)(params) || ((0, param_readers_1.readStringParam)(params, "caption") ?? "");
|
|
1339
|
+
// A caption is optional, but the silent-reply sentinel must never
|
|
1340
|
+
// reach Telegram as one — same reasoning as the `send` path below.
|
|
1341
|
+
const caption = captionText.trim() && (0, helpers_1.isSilentReplyText)(captionText)
|
|
1342
|
+
? ""
|
|
1343
|
+
: captionText.replaceAll("\\n", "\n");
|
|
1344
|
+
const uploadReplyToId = (0, param_readers_1.readStringOrNumberParam)(params, "replyToId") ?? (0, param_readers_1.readStringOrNumberParam)(params, "replyTo");
|
|
1345
|
+
const uploadThreadId = (0, param_readers_1.readStringOrNumberParam)(params, "threadId");
|
|
1346
|
+
actionLog.info("clawgram handleAction upload-file", {
|
|
1347
|
+
accountId: uploadAccountId,
|
|
1348
|
+
dryRun: dryRun === true,
|
|
1349
|
+
to: uploadTo,
|
|
1350
|
+
hasCaption: Boolean(caption),
|
|
1351
|
+
replyToId: uploadReplyToId ?? null,
|
|
1352
|
+
threadId: uploadThreadId ?? null,
|
|
1353
|
+
});
|
|
1354
|
+
if (dryRun === true) {
|
|
1355
|
+
return (0, core_1.jsonResult)({
|
|
1356
|
+
ok: true,
|
|
1357
|
+
dryRun: true,
|
|
1358
|
+
to: uploadTo,
|
|
1359
|
+
accountId: uploadAccountId,
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
const uploadGram = runtimes.get(uploadAccountId);
|
|
1363
|
+
if (!uploadGram) {
|
|
1364
|
+
throw new Error(`clawgram: runtime not found for account ${uploadAccountId}`);
|
|
1365
|
+
}
|
|
1366
|
+
const uploaded = await uploadGram.sendMedia({
|
|
1367
|
+
target: uploadTo,
|
|
1368
|
+
file,
|
|
1369
|
+
caption: caption || undefined,
|
|
1370
|
+
replyToMessageId: (0, helpers_1.resolveReplyToMessageIdForTarget)(rawUploadTo, uploadReplyToId),
|
|
1371
|
+
messageThreadId: parseOptionalThreadId(uploadThreadId),
|
|
1372
|
+
});
|
|
1373
|
+
actionLog.info("clawgram handleAction upload-file completed", {
|
|
1374
|
+
accountId: uploadAccountId,
|
|
1375
|
+
to: uploadTo,
|
|
1376
|
+
sentMessageId: String(uploaded?.id ?? ""),
|
|
1377
|
+
});
|
|
1378
|
+
return (0, core_1.jsonResult)({
|
|
1379
|
+
ok: true,
|
|
1380
|
+
to: uploadTo,
|
|
1381
|
+
accountId: uploadAccountId,
|
|
1382
|
+
messageId: String(uploaded?.id ?? ""),
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1289
1385
|
if (action !== "send") {
|
|
1290
1386
|
throw new Error(`clawgram: unsupported message action ${action}`);
|
|
1291
1387
|
}
|
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.
|
|
5
|
+
"version": "2.6.0",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
package/package.json
CHANGED