clawgram 2.23.0 → 2.25.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/README.md +2 -1
- package/dist/channel.js +19 -878
- package/dist/chunk.js +43 -0
- package/dist/gramjs-client.js +24 -1
- package/dist/helpers.js +26 -0
- package/dist/inbound-pipeline.js +937 -0
- package/dist/outbound.js +13 -0
- package/dist/update-config.js +0 -8
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -1
package/dist/chunk.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TELEGRAM_CAPTION_LIMIT = exports.TELEGRAM_TEXT_LIMIT = void 0;
|
|
4
|
+
exports.chunkTelegramText = chunkTelegramText;
|
|
5
|
+
/**
|
|
6
|
+
* Telegram limits: 4096 characters per message, 1024 per media caption.
|
|
7
|
+
*
|
|
8
|
+
* Core chunks only the replies it dispatches itself; a `send` issued through
|
|
9
|
+
* the message tool — the way the guard scripts deliver reports — reaches
|
|
10
|
+
* GramJS whole, and GramJS does not split either, so a long report failed
|
|
11
|
+
* with MESSAGE_TOO_LONG and the agent saw "✉️ Message failed" while the
|
|
12
|
+
* human saw nothing (audit B5-03).
|
|
13
|
+
*
|
|
14
|
+
* Splitting prefers paragraph breaks, then line breaks, then a hard cut at
|
|
15
|
+
* the limit. It runs on the text as the agent wrote it — before HTML
|
|
16
|
+
* rendering — so a chunk boundary can only fall between the agent's own
|
|
17
|
+
* lines, never inside an entity GramJS produced.
|
|
18
|
+
*/
|
|
19
|
+
exports.TELEGRAM_TEXT_LIMIT = 4096;
|
|
20
|
+
exports.TELEGRAM_CAPTION_LIMIT = 1024;
|
|
21
|
+
function chunkTelegramText(text, limit = exports.TELEGRAM_TEXT_LIMIT) {
|
|
22
|
+
const chars = Array.from(text);
|
|
23
|
+
if (chars.length <= limit)
|
|
24
|
+
return [text];
|
|
25
|
+
const chunks = [];
|
|
26
|
+
let rest = text;
|
|
27
|
+
while (Array.from(rest).length > limit) {
|
|
28
|
+
const window = Array.from(rest).slice(0, limit).join("");
|
|
29
|
+
let cut = window.lastIndexOf("\n\n");
|
|
30
|
+
if (cut < limit / 2)
|
|
31
|
+
cut = window.lastIndexOf("\n");
|
|
32
|
+
if (cut < limit / 2)
|
|
33
|
+
cut = window.lastIndexOf(" ");
|
|
34
|
+
if (cut < limit / 2)
|
|
35
|
+
cut = window.length;
|
|
36
|
+
const piece = rest.slice(0, cut).replace(/\s+$/u, "");
|
|
37
|
+
chunks.push(piece);
|
|
38
|
+
rest = rest.slice(cut).replace(/^\s+/u, "");
|
|
39
|
+
}
|
|
40
|
+
if (rest)
|
|
41
|
+
chunks.push(rest);
|
|
42
|
+
return chunks;
|
|
43
|
+
}
|
package/dist/gramjs-client.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.GramJsClientManager = void 0;
|
|
4
4
|
exports.buildParticipantsQuery = buildParticipantsQuery;
|
|
5
5
|
exports.buildVoiceNoteParams = buildVoiceNoteParams;
|
|
6
|
+
const chunk_1 = require("./chunk");
|
|
6
7
|
const telegram_1 = require("telegram");
|
|
7
8
|
const sessions_1 = require("telegram/sessions");
|
|
8
9
|
// Deep import, but the documented one: GramJS ships its SRP helper here and
|
|
@@ -313,7 +314,8 @@ class GramJsClientManager {
|
|
|
313
314
|
// но теперь виден в логе: если он в логе частый, значит кэш не спасает и
|
|
314
315
|
// адресация идёт не тем ключом (A6-14).
|
|
315
316
|
peerLog.info("clawgram peer resolve falling back to dialog scan", {
|
|
316
|
-
|
|
317
|
+
// Цель может быть телефоном или @handle человека — в журнал идёт только её форма (B5-09).
|
|
318
|
+
targetShape: String(raw).startsWith("+") ? "phone" : String(raw).startsWith("@") ? "handle" : /^-?\d+/.test(String(raw)) ? "id" : "other",
|
|
317
319
|
kind: kind ?? null,
|
|
318
320
|
});
|
|
319
321
|
const dialogs = await this.client.getDialogs({ limit: 200 }).catch(() => []);
|
|
@@ -418,6 +420,17 @@ class GramJsClientManager {
|
|
|
418
420
|
const resolved = await this.resolvePeer(args.target, { kind: args.targetKind });
|
|
419
421
|
const messageThreadId = args.messageThreadId ?? resolved.messageThreadId;
|
|
420
422
|
const replyParams = buildForumReplyParams(messageThreadId, args.replyToMessageId);
|
|
423
|
+
// Длиннее лимита Telegram — несколько сообщений подряд, а не
|
|
424
|
+
// MESSAGE_TOO_LONG (B5-03). Нарезка — по тексту агента, до рендера.
|
|
425
|
+
const chunks = (0, chunk_1.chunkTelegramText)(args.text, chunk_1.TELEGRAM_TEXT_LIMIT);
|
|
426
|
+
if (chunks.length > 1) {
|
|
427
|
+
let last;
|
|
428
|
+
for (const [index, chunk] of chunks.entries()) {
|
|
429
|
+
last = await this.sendText({ ...args, text: chunk,
|
|
430
|
+
...(index > 0 ? { replyToMessageId: undefined } : {}) });
|
|
431
|
+
}
|
|
432
|
+
return last;
|
|
433
|
+
}
|
|
421
434
|
return this.client.sendMessage(resolved.peer, {
|
|
422
435
|
// In html mode the text is rendered first: the agent writes markdown,
|
|
423
436
|
// Telegram HTML, or both, and GramJS's HTML parser alone would ship
|
|
@@ -757,6 +770,16 @@ class GramJsClientManager {
|
|
|
757
770
|
const resolved = await this.resolvePeer(args.target);
|
|
758
771
|
const messageThreadId = args.messageThreadId ?? resolved.messageThreadId;
|
|
759
772
|
const replyParams = buildForumReplyParams(messageThreadId, args.replyToMessageId);
|
|
773
|
+
// Подпись длиннее 1024 — файл с первой частью, остальное текстом следом
|
|
774
|
+
// (B5-03); раньше вся подпись уезжала целиком и падала на лимите.
|
|
775
|
+
const captionChunks = args.caption ? (0, chunk_1.chunkTelegramText)(args.caption, chunk_1.TELEGRAM_CAPTION_LIMIT) : [];
|
|
776
|
+
if (captionChunks.length > 1) {
|
|
777
|
+
const sent = await this.sendMedia({ ...args, caption: captionChunks[0] });
|
|
778
|
+
for (const chunk of captionChunks.slice(1)) {
|
|
779
|
+
await this.sendText({ target: args.target, text: chunk, parseMode: args.parseMode, messageThreadId });
|
|
780
|
+
}
|
|
781
|
+
return sent;
|
|
782
|
+
}
|
|
760
783
|
return this.client.sendFile(resolved.peer, {
|
|
761
784
|
file: args.file,
|
|
762
785
|
// Captions are agent prose too — the outbound path sends `caption ??
|
package/dist/helpers.js
CHANGED
|
@@ -47,6 +47,8 @@ exports.resolveReplyParseMode = resolveReplyParseMode;
|
|
|
47
47
|
exports.resolveOutboundParseMode = resolveOutboundParseMode;
|
|
48
48
|
exports.resolveDryRun = resolveDryRun;
|
|
49
49
|
exports.parseOptionalThreadId = parseOptionalThreadId;
|
|
50
|
+
exports.readAccountReactionLevel = readAccountReactionLevel;
|
|
51
|
+
exports.readAccountReactionModel = readAccountReactionModel;
|
|
50
52
|
const node_fs_1 = require("node:fs");
|
|
51
53
|
const node_path_1 = __importDefault(require("node:path"));
|
|
52
54
|
const core_1 = require("openclaw/plugin-sdk/core");
|
|
@@ -904,3 +906,27 @@ function parseOptionalThreadId(value) {
|
|
|
904
906
|
const parsed = Number.parseInt(trimmed, 10);
|
|
905
907
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
906
908
|
}
|
|
909
|
+
/** Reads the configured reaction level for an account, tolerating a missing config. */
|
|
910
|
+
function readAccountReactionLevel(cfg, accountId) {
|
|
911
|
+
const resolvedAccountId = resolveConfiguredAccountId(cfg, accountId);
|
|
912
|
+
if (!resolvedAccountId) {
|
|
913
|
+
return undefined;
|
|
914
|
+
}
|
|
915
|
+
return cfg?.channels?.[constants_1.CHANNEL_ID]?.accounts?.[resolvedAccountId]?.reactionLevel;
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Model ref for the emoji pick, when the account names one.
|
|
919
|
+
*
|
|
920
|
+
* Picking one emoji out of a fixed list of 68 is the cheapest judgement this
|
|
921
|
+
* channel makes and the only model call it makes on its own; running it on the
|
|
922
|
+
* agent's own head spends the expensive quota on a decision a small model
|
|
923
|
+
* makes just as well.
|
|
924
|
+
*/
|
|
925
|
+
function readAccountReactionModel(cfg, accountId) {
|
|
926
|
+
const resolvedAccountId = resolveConfiguredAccountId(cfg, accountId);
|
|
927
|
+
if (!resolvedAccountId) {
|
|
928
|
+
return undefined;
|
|
929
|
+
}
|
|
930
|
+
const raw = cfg?.channels?.[constants_1.CHANNEL_ID]?.accounts?.[resolvedAccountId]?.reactionModel;
|
|
931
|
+
return typeof raw === "string" && raw.trim() ? raw.trim() : undefined;
|
|
932
|
+
}
|