cc-claw 0.20.18 → 0.20.20
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/cli.js +92 -115
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ var VERSION;
|
|
|
33
33
|
var init_version = __esm({
|
|
34
34
|
"src/version.ts"() {
|
|
35
35
|
"use strict";
|
|
36
|
-
VERSION = true ? "0.20.
|
|
36
|
+
VERSION = true ? "0.20.20" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -7370,11 +7370,6 @@ ${ctx}`);
|
|
|
7370
7370
|
} catch {
|
|
7371
7371
|
}
|
|
7372
7372
|
}
|
|
7373
|
-
if (tier !== "slim" && tier !== "heartbeat") {
|
|
7374
|
-
sections.push(
|
|
7375
|
-
"[React] Start your response with [REACT:emoji] \u2014 one emoji from the Telegram allowed set matching the message tone. Examples: \u{1F914} question, \u{1FAE1} task/request, \u{1F525} exciting, \u{1F923} funny, \u{1F622} sad, \u{1F4AF} agree, \u{1F389} celebrate, \u{1F468}\u200D\u{1F4BB} coding, \u{1F92F} mind-blown, \u{1F440} interesting, \u{1F91D} thanks, \u{1F60E} chill."
|
|
7376
|
-
);
|
|
7377
|
-
}
|
|
7378
7373
|
if (sideQuestContext) {
|
|
7379
7374
|
const { getInFlightMessage: getInFlightMessage3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
7380
7375
|
const inFlightMsg = getInFlightMessage3(sideQuestContext.parentChatId);
|
|
@@ -8527,11 +8522,6 @@ function pickFallbackEmoji(userMessage) {
|
|
|
8527
8522
|
}
|
|
8528
8523
|
return "\u{1F44D}";
|
|
8529
8524
|
}
|
|
8530
|
-
function ensureReaction(responseText, userMessage) {
|
|
8531
|
-
if (/\[REACT:.+?\]/.test(responseText)) return responseText;
|
|
8532
|
-
const emoji = pickFallbackEmoji(userMessage);
|
|
8533
|
-
return `[REACT:${emoji}]${responseText}`;
|
|
8534
|
-
}
|
|
8535
8525
|
function isPathAllowed(filePath, cwd) {
|
|
8536
8526
|
const resolved = resolvePath(cwd ?? process.cwd(), filePath);
|
|
8537
8527
|
for (const pattern of BLOCKED_PATH_PATTERNS) {
|
|
@@ -8709,7 +8699,7 @@ async function sendOrEditKeyboard(chatId, channel, messageId, text, buttons) {
|
|
|
8709
8699
|
await channel.sendText(chatId, text, { parseMode: "plain" });
|
|
8710
8700
|
return void 0;
|
|
8711
8701
|
}
|
|
8712
|
-
var TONE_PATTERNS,
|
|
8702
|
+
var TONE_PATTERNS, BLOCKED_PATH_PATTERNS, CLI_INSTALL_HINTS, PERM_MODES, VERBOSE_LEVELS, HELP_CATEGORIES, USAGE_WINDOW_MAP, SKILLS_PER_PAGE, MAX_SIDE_QUESTS;
|
|
8713
8703
|
var init_helpers = __esm({
|
|
8714
8704
|
"src/router/helpers.ts"() {
|
|
8715
8705
|
"use strict";
|
|
@@ -8744,77 +8734,6 @@ var init_helpers = __esm({
|
|
|
8744
8734
|
// Chill / casual greeting
|
|
8745
8735
|
{ pattern: /^(hey|hi|hello|sup|yo|what'?s up|howdy)\b/i, emoji: "\u{1F60E}" }
|
|
8746
8736
|
];
|
|
8747
|
-
ALLOWED_REACTION_EMOJIS = /* @__PURE__ */ new Set([
|
|
8748
|
-
"\u{1F44D}",
|
|
8749
|
-
"\u{1F44E}",
|
|
8750
|
-
"\u2764",
|
|
8751
|
-
"\u{1F525}",
|
|
8752
|
-
"\u{1F970}",
|
|
8753
|
-
"\u{1F44F}",
|
|
8754
|
-
"\u{1F601}",
|
|
8755
|
-
"\u{1F914}",
|
|
8756
|
-
"\u{1F92F}",
|
|
8757
|
-
"\u{1F631}",
|
|
8758
|
-
"\u{1F92C}",
|
|
8759
|
-
"\u{1F622}",
|
|
8760
|
-
"\u{1F389}",
|
|
8761
|
-
"\u{1F929}",
|
|
8762
|
-
"\u{1F92E}",
|
|
8763
|
-
"\u{1F4A9}",
|
|
8764
|
-
"\u{1F64F}",
|
|
8765
|
-
"\u{1F44C}",
|
|
8766
|
-
"\u{1F54A}\uFE0F",
|
|
8767
|
-
"\u{1F921}",
|
|
8768
|
-
"\u{1F971}",
|
|
8769
|
-
"\u{1F974}",
|
|
8770
|
-
"\u{1F60D}",
|
|
8771
|
-
"\u{1F433}",
|
|
8772
|
-
"\u{1F31A}",
|
|
8773
|
-
"\u{1F32D}",
|
|
8774
|
-
"\u{1F4AF}",
|
|
8775
|
-
"\u{1F923}",
|
|
8776
|
-
"\u26A1",
|
|
8777
|
-
"\u{1F34C}",
|
|
8778
|
-
"\u{1F3C6}",
|
|
8779
|
-
"\u{1F494}",
|
|
8780
|
-
"\u{1F928}",
|
|
8781
|
-
"\u{1F610}",
|
|
8782
|
-
"\u{1F353}",
|
|
8783
|
-
"\u{1F37E}",
|
|
8784
|
-
"\u{1F48B}",
|
|
8785
|
-
"\u{1F595}",
|
|
8786
|
-
"\u{1F608}",
|
|
8787
|
-
"\u{1F634}",
|
|
8788
|
-
"\u{1F62D}",
|
|
8789
|
-
"\u{1F913}",
|
|
8790
|
-
"\u{1F47B}",
|
|
8791
|
-
"\u{1F468}\u200D\u{1F4BB}",
|
|
8792
|
-
"\u{1F440}",
|
|
8793
|
-
"\u{1F383}",
|
|
8794
|
-
"\u{1F648}",
|
|
8795
|
-
"\u{1F607}",
|
|
8796
|
-
"\u{1F628}",
|
|
8797
|
-
"\u{1F91D}",
|
|
8798
|
-
"\u{1F917}",
|
|
8799
|
-
"\u{1FAE1}",
|
|
8800
|
-
"\u{1F385}",
|
|
8801
|
-
"\u{1F384}",
|
|
8802
|
-
"\u2603\uFE0F",
|
|
8803
|
-
"\u{1F485}",
|
|
8804
|
-
"\u{1F92A}",
|
|
8805
|
-
"\u{1F5FF}",
|
|
8806
|
-
"\u{1F192}",
|
|
8807
|
-
"\u{1F498}",
|
|
8808
|
-
"\u{1F649}",
|
|
8809
|
-
"\u{1F984}",
|
|
8810
|
-
"\u{1F618}",
|
|
8811
|
-
"\u{1F48A}",
|
|
8812
|
-
"\u{1F64A}",
|
|
8813
|
-
"\u{1F60E}",
|
|
8814
|
-
"\u{1F47E}",
|
|
8815
|
-
"\u{1F937}",
|
|
8816
|
-
"\u{1F621}"
|
|
8817
|
-
]);
|
|
8818
8737
|
BLOCKED_PATH_PATTERNS = [
|
|
8819
8738
|
/\/\.ssh\//,
|
|
8820
8739
|
/\/\.gnupg\//,
|
|
@@ -18096,13 +18015,11 @@ var init_image_gen = __esm({
|
|
|
18096
18015
|
// src/router/response.ts
|
|
18097
18016
|
var response_exports = {};
|
|
18098
18017
|
__export(response_exports, {
|
|
18099
|
-
ensureReaction: () => ensureReaction,
|
|
18100
18018
|
handleResponseExhaustion: () => handleResponseExhaustion,
|
|
18101
18019
|
makeToolActionCallback: () => makeToolActionCallback,
|
|
18102
18020
|
pendingFallbackMessages: () => pendingFallbackMessages,
|
|
18103
18021
|
processFileSends: () => processFileSends2,
|
|
18104
18022
|
processImageGenerations: () => processImageGenerations,
|
|
18105
|
-
processReaction: () => processReaction,
|
|
18106
18023
|
sendResponse: () => sendResponse
|
|
18107
18024
|
});
|
|
18108
18025
|
import { readFile as readFile3 } from "fs/promises";
|
|
@@ -18168,25 +18085,8 @@ async function processImageGenerations(chatId, channel, text) {
|
|
|
18168
18085
|
}
|
|
18169
18086
|
return text.replace(pattern, "").trim();
|
|
18170
18087
|
}
|
|
18171
|
-
async function processReaction(chatId, channel, text, messageId) {
|
|
18172
|
-
const reactPatternGlobal = /\[\s*REACT:\s*(.+?)\s*\]/g;
|
|
18173
|
-
if (!reactPatternGlobal.test(text)) return text;
|
|
18174
|
-
let reacted = false;
|
|
18175
|
-
reactPatternGlobal.lastIndex = 0;
|
|
18176
|
-
let match;
|
|
18177
|
-
while ((match = reactPatternGlobal.exec(text)) !== null) {
|
|
18178
|
-
const emoji = match[1].trim();
|
|
18179
|
-
if (!reacted && messageId && typeof channel.reactToMessage === "function" && ALLOWED_REACTION_EMOJIS.has(emoji)) {
|
|
18180
|
-
channel.reactToMessage(chatId, messageId, emoji).catch((err) => {
|
|
18181
|
-
log(`[reaction] Failed for chat=${chatId}: ${err}`);
|
|
18182
|
-
});
|
|
18183
|
-
reacted = true;
|
|
18184
|
-
}
|
|
18185
|
-
}
|
|
18186
|
-
return text.replace(/\[\s*REACT:\s*(.+?)\s*\]/g, "").trim();
|
|
18187
|
-
}
|
|
18188
18088
|
async function sendResponse(chatId, channel, text, messageId, replyToMessageId) {
|
|
18189
|
-
text =
|
|
18089
|
+
text = text.replace(/\[\s*REACT:\s*(.+?)\s*\]/g, "").trim();
|
|
18190
18090
|
const { cleanText: afterUpdates, updates } = extractUserUpdates(text);
|
|
18191
18091
|
for (const { key, value } of updates) {
|
|
18192
18092
|
if (typeof channel.sendKeyboard === "function") {
|
|
@@ -18623,8 +18523,7 @@ async function handleVoice(msg, channel) {
|
|
|
18623
18523
|
if (vLiveStatus) await vLiveStatus.finalize(Date.now() - sigT0);
|
|
18624
18524
|
if (response.usage) addUsage(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, vModel, void 0, response.usage.contextSize);
|
|
18625
18525
|
if (await handleResponseExhaustion(response.text, chatId, msg, channel)) return;
|
|
18626
|
-
|
|
18627
|
-
await sendResponse(chatId, channel, voiceResponse, msg.messageId);
|
|
18526
|
+
await sendResponse(chatId, channel, response.text, msg.messageId);
|
|
18628
18527
|
} catch (err) {
|
|
18629
18528
|
error("[router] Voice error:", err);
|
|
18630
18529
|
await channel.sendText(chatId, `Voice processing error: ${errorMessage(err)}`, { parseMode: "plain" });
|
|
@@ -18638,7 +18537,8 @@ async function handleMedia(msg, channel) {
|
|
|
18638
18537
|
await channel.sendText(chatId, mLimitMsg, { parseMode: "plain" });
|
|
18639
18538
|
return;
|
|
18640
18539
|
}
|
|
18641
|
-
|
|
18540
|
+
const fileCount = 1 + (msg.extraFiles?.length ?? 0);
|
|
18541
|
+
await channel.sendText(chatId, fileCount > 1 ? `Processing ${fileCount} files...` : "Processing your file...", { parseMode: "plain" });
|
|
18642
18542
|
try {
|
|
18643
18543
|
if (msg.type === "video") {
|
|
18644
18544
|
const fileId = msg.metadata?.fileId ?? fileName;
|
|
@@ -18706,8 +18606,7 @@ Acknowledge receipt. Do NOT analyze the video unless they ask you to.`;
|
|
|
18706
18606
|
if (vidLiveStatus) await vidLiveStatus.finalize(Date.now() - vidT0);
|
|
18707
18607
|
if (response2.usage) addUsage(chatId, response2.usage.input, response2.usage.output, response2.usage.cacheRead, vidModel, void 0, response2.usage.contextSize);
|
|
18708
18608
|
if (await handleResponseExhaustion(response2.text, chatId, msg, channel)) return;
|
|
18709
|
-
|
|
18710
|
-
await sendResponse(chatId, channel, vidResponse, msg.messageId);
|
|
18609
|
+
await sendResponse(chatId, channel, response2.text, msg.messageId);
|
|
18711
18610
|
return;
|
|
18712
18611
|
}
|
|
18713
18612
|
if (!fileName) {
|
|
@@ -18722,13 +18621,39 @@ Acknowledge receipt. Do NOT analyze the video unless they ask you to.`;
|
|
|
18722
18621
|
if (msg.type === "photo" || isImageExt(ext)) {
|
|
18723
18622
|
const imgExt = msg.type === "photo" ? "jpg" : ext || "jpg";
|
|
18724
18623
|
tempFilePath = await saveMedia(fileBuffer, "img", imgExt);
|
|
18725
|
-
|
|
18624
|
+
const allPaths = [tempFilePath];
|
|
18625
|
+
if (msg.extraFiles?.length) {
|
|
18626
|
+
for (const extra of msg.extraFiles) {
|
|
18627
|
+
try {
|
|
18628
|
+
const extraBuffer = await channel.downloadFile(extra.fileName);
|
|
18629
|
+
const extraExt = extra.mimeType.startsWith("image/") ? extra.mimeType.split("/")[1] || "jpg" : "jpg";
|
|
18630
|
+
const extraPath = await saveMedia(extraBuffer, "img", extraExt);
|
|
18631
|
+
allPaths.push(extraPath);
|
|
18632
|
+
} catch (err) {
|
|
18633
|
+
log(`[media] Failed to download extra file: ${errorMessage(err)}`);
|
|
18634
|
+
}
|
|
18635
|
+
}
|
|
18636
|
+
}
|
|
18637
|
+
if (allPaths.length === 1) {
|
|
18638
|
+
prompt = caption ? `The user sent an image with caption: "${caption}"
|
|
18726
18639
|
|
|
18727
18640
|
The image has been saved to: ${tempFilePath}
|
|
18728
18641
|
|
|
18729
18642
|
Please read the image file and respond to their caption/question.` : `The user sent an image. It has been saved to: ${tempFilePath}
|
|
18730
18643
|
|
|
18731
18644
|
Please read the image file and describe what you see.`;
|
|
18645
|
+
} else {
|
|
18646
|
+
const pathList = allPaths.map((p, i) => `${i + 1}. ${p}`).join("\n");
|
|
18647
|
+
prompt = caption ? `The user sent ${allPaths.length} images with caption: "${caption}"
|
|
18648
|
+
|
|
18649
|
+
The images have been saved to:
|
|
18650
|
+
${pathList}
|
|
18651
|
+
|
|
18652
|
+
Please read all image files and respond to their caption/question.` : `The user sent ${allPaths.length} images. They have been saved to:
|
|
18653
|
+
${pathList}
|
|
18654
|
+
|
|
18655
|
+
Please read all image files and describe what you see.`;
|
|
18656
|
+
}
|
|
18732
18657
|
} else if (isTextExt(ext)) {
|
|
18733
18658
|
const MAX_TEXT_FILE_BYTES = 1048576;
|
|
18734
18659
|
if (fileBuffer.length > MAX_TEXT_FILE_BYTES) {
|
|
@@ -18780,8 +18705,7 @@ ${content}
|
|
|
18780
18705
|
if (mLiveStatus) await mLiveStatus.finalize(Date.now() - mT0);
|
|
18781
18706
|
if (response.usage) addUsage(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, mediaModel, void 0, response.usage.contextSize);
|
|
18782
18707
|
if (await handleResponseExhaustion(response.text, chatId, msg, channel)) return;
|
|
18783
|
-
|
|
18784
|
-
await sendResponse(chatId, channel, mediaResponse, msg.messageId);
|
|
18708
|
+
await sendResponse(chatId, channel, response.text, msg.messageId);
|
|
18785
18709
|
} catch (err) {
|
|
18786
18710
|
error("[router] Media error:", err);
|
|
18787
18711
|
const errStr = errorMessage(err);
|
|
@@ -24524,7 +24448,9 @@ ${PERM_MODES[chosen]}`,
|
|
|
24524
24448
|
return;
|
|
24525
24449
|
}
|
|
24526
24450
|
removePendingPlan(chatId);
|
|
24527
|
-
await replaceWithText(
|
|
24451
|
+
await replaceWithText(`\u{1F50D} ${plan.plan}
|
|
24452
|
+
|
|
24453
|
+
\u2705 Approved. Executing...`);
|
|
24528
24454
|
bypassBusyCheck.add(chatId);
|
|
24529
24455
|
const { handleMessage: handleMessage2 } = await Promise.resolve().then(() => (init_router2(), router_exports));
|
|
24530
24456
|
const overrideMsg = `[SYSTEM: Planning mode disabled. Execution APPROVED. Please execute the plan.]
|
|
@@ -24542,7 +24468,9 @@ ${plan.originalMessage}`;
|
|
|
24542
24468
|
}
|
|
24543
24469
|
removePendingPlan(chatId);
|
|
24544
24470
|
setRejectionMode(chatId);
|
|
24545
|
-
await
|
|
24471
|
+
await replaceWithText(`\u{1F50D} ${plan.plan}
|
|
24472
|
+
|
|
24473
|
+
\u274C Rejected \u2014 send feedback to revise.`);
|
|
24546
24474
|
return;
|
|
24547
24475
|
} else if (data === "exec:yolo") {
|
|
24548
24476
|
const plan = getPendingPlan(chatId);
|
|
@@ -24552,7 +24480,9 @@ ${plan.originalMessage}`;
|
|
|
24552
24480
|
}
|
|
24553
24481
|
removePendingPlan(chatId);
|
|
24554
24482
|
setExecMode(chatId, "yolo");
|
|
24555
|
-
await
|
|
24483
|
+
await replaceWithText(`\u{1F50D} ${plan.plan}
|
|
24484
|
+
|
|
24485
|
+
\u26A1 YOLO mode. Executing...`);
|
|
24556
24486
|
bypassBusyCheck.add(chatId);
|
|
24557
24487
|
const { handleMessage: handleMessage2 } = await Promise.resolve().then(() => (init_router2(), router_exports));
|
|
24558
24488
|
const overrideMsg = `[SYSTEM: Planning mode disabled. YOLO mode enabled. Proceed immediately without asking for approval.]
|
|
@@ -26286,6 +26216,12 @@ async function handleText(msg, channel) {
|
|
|
26286
26216
|
await channel.sendText(chatId, "Got it, I'll remember that.", { parseMode: "plain" });
|
|
26287
26217
|
return;
|
|
26288
26218
|
}
|
|
26219
|
+
if (msg.messageId && typeof channel.reactToMessage === "function") {
|
|
26220
|
+
const ackEmoji = pickFallbackEmoji(text);
|
|
26221
|
+
channel.reactToMessage(chatId, msg.messageId, ackEmoji).catch(() => {
|
|
26222
|
+
});
|
|
26223
|
+
}
|
|
26224
|
+
await channel.sendTyping?.(chatId);
|
|
26289
26225
|
const model2 = resolveModel(chatId);
|
|
26290
26226
|
let autoRouted = false;
|
|
26291
26227
|
let effectiveModel = model2;
|
|
@@ -26704,7 +26640,6 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26704
26640
|
if (activeSideQuests.has(chatId) && responseText && !responseText.startsWith("(No response")) {
|
|
26705
26641
|
responseText = "\u{1F4CB} <b>Main task</b>\n\n" + responseText;
|
|
26706
26642
|
}
|
|
26707
|
-
responseText = ensureReaction(responseText, cleanText || text);
|
|
26708
26643
|
await sendResponse(chatId, channel, responseText, msg.messageId);
|
|
26709
26644
|
try {
|
|
26710
26645
|
const adapter2 = getAdapterForChat(chatId);
|
|
@@ -28101,6 +28036,11 @@ var init_telegram2 = __esm({
|
|
|
28101
28036
|
// messageId → chatId
|
|
28102
28037
|
reactionHandlers = [];
|
|
28103
28038
|
throttle;
|
|
28039
|
+
// ── Media group debouncing ─────────────────────────────────────────
|
|
28040
|
+
// Telegram sends each photo in a media group as a separate update.
|
|
28041
|
+
// We buffer them by media_group_id and emit one merged IncomingMessage.
|
|
28042
|
+
mediaGroupBuffer = /* @__PURE__ */ new Map();
|
|
28043
|
+
static MEDIA_GROUP_DEBOUNCE_MS = 500;
|
|
28104
28044
|
// ── Polling health tracking ─────────────────────────────────────────
|
|
28105
28045
|
/** Timestamp of last update received from Telegram (message, callback, reaction) */
|
|
28106
28046
|
lastUpdateAt = 0;
|
|
@@ -28263,6 +28203,19 @@ var init_telegram2 = __esm({
|
|
|
28263
28203
|
return;
|
|
28264
28204
|
}
|
|
28265
28205
|
log(`[telegram] Processing ${msg.type}: "${msg.text?.slice(0, 50) || msg.command || "(media)"}"`);
|
|
28206
|
+
const mediaGroupId = ctx.message?.media_group_id;
|
|
28207
|
+
if (mediaGroupId && (msg.type === "photo" || msg.type === "video" || msg.type === "document")) {
|
|
28208
|
+
const existing = this.mediaGroupBuffer.get(mediaGroupId);
|
|
28209
|
+
if (existing) {
|
|
28210
|
+
existing.messages.push(msg);
|
|
28211
|
+
clearTimeout(existing.timer);
|
|
28212
|
+
existing.timer = setTimeout(() => this.flushMediaGroup(mediaGroupId), _TelegramChannel.MEDIA_GROUP_DEBOUNCE_MS);
|
|
28213
|
+
} else {
|
|
28214
|
+
const timer = setTimeout(() => this.flushMediaGroup(mediaGroupId), _TelegramChannel.MEDIA_GROUP_DEBOUNCE_MS);
|
|
28215
|
+
this.mediaGroupBuffer.set(mediaGroupId, { messages: [msg], timer, handler, channel: this });
|
|
28216
|
+
}
|
|
28217
|
+
return;
|
|
28218
|
+
}
|
|
28266
28219
|
if (isFastPathMessage(msg)) {
|
|
28267
28220
|
handler(msg, this).catch((err) => {
|
|
28268
28221
|
error("[telegram] Fast-path handler error:", err);
|
|
@@ -28711,6 +28664,30 @@ var init_telegram2 = __esm({
|
|
|
28711
28664
|
}
|
|
28712
28665
|
this.agentMessageIds.set(messageId, chatId);
|
|
28713
28666
|
}
|
|
28667
|
+
/**
|
|
28668
|
+
* Flush a buffered media group — merge all files into a single IncomingMessage
|
|
28669
|
+
* with the first file as primary and the rest in extraFiles.
|
|
28670
|
+
*/
|
|
28671
|
+
flushMediaGroup(groupId) {
|
|
28672
|
+
const group = this.mediaGroupBuffer.get(groupId);
|
|
28673
|
+
if (!group) return;
|
|
28674
|
+
this.mediaGroupBuffer.delete(groupId);
|
|
28675
|
+
const [primary, ...rest] = group.messages;
|
|
28676
|
+
if (rest.length > 0) {
|
|
28677
|
+
primary.extraFiles = rest.map((m) => ({
|
|
28678
|
+
fileName: m.fileName,
|
|
28679
|
+
mimeType: m.mimeType ?? "application/octet-stream"
|
|
28680
|
+
}));
|
|
28681
|
+
if (!primary.caption) {
|
|
28682
|
+
const withCaption = rest.find((m) => m.caption);
|
|
28683
|
+
if (withCaption) primary.caption = withCaption.caption;
|
|
28684
|
+
}
|
|
28685
|
+
log(`[telegram] Media group ${groupId}: merged ${group.messages.length} files into one message`);
|
|
28686
|
+
}
|
|
28687
|
+
group.handler(primary, group.channel).catch((err) => {
|
|
28688
|
+
error("[telegram] Media group handler error:", err);
|
|
28689
|
+
});
|
|
28690
|
+
}
|
|
28714
28691
|
buildIncomingMessage(ctx) {
|
|
28715
28692
|
const chatId = ctx.chat.id.toString();
|
|
28716
28693
|
const messageId = ctx.message?.message_id?.toString() ?? "";
|
package/package.json
CHANGED