@nextclaw/channel-runtime 0.1.4 → 0.1.6
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/index.js +173 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -228,6 +228,9 @@ var DEFAULT_MEDIA_MAX_MB = 8;
|
|
|
228
228
|
var MEDIA_FETCH_TIMEOUT_MS = 15e3;
|
|
229
229
|
var TYPING_HEARTBEAT_MS = 8e3;
|
|
230
230
|
var TYPING_AUTO_STOP_MS = 45e3;
|
|
231
|
+
var DISCORD_TEXT_LIMIT = 2e3;
|
|
232
|
+
var DISCORD_MAX_LINES_PER_MESSAGE = 17;
|
|
233
|
+
var FENCE_RE = /^( {0,3})(`{3,}|~{3,})(.*)$/;
|
|
231
234
|
var DiscordChannel = class extends BaseChannel {
|
|
232
235
|
name = "discord";
|
|
233
236
|
client = null;
|
|
@@ -285,16 +288,23 @@ var DiscordChannel = class extends BaseChannel {
|
|
|
285
288
|
}
|
|
286
289
|
this.stopTyping(msg.chatId);
|
|
287
290
|
const textChannel = channel;
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (msg.replyTo) {
|
|
292
|
-
payload.reply = { messageReference: msg.replyTo };
|
|
291
|
+
const chunks = chunkDiscordText(msg.content ?? "");
|
|
292
|
+
if (chunks.length === 0) {
|
|
293
|
+
return;
|
|
293
294
|
}
|
|
294
|
-
|
|
295
|
-
|
|
295
|
+
const flags = msg.metadata?.silent === true ? MessageFlags.SuppressNotifications : void 0;
|
|
296
|
+
for (const chunk of chunks) {
|
|
297
|
+
const payload = {
|
|
298
|
+
content: chunk
|
|
299
|
+
};
|
|
300
|
+
if (msg.replyTo) {
|
|
301
|
+
payload.reply = { messageReference: msg.replyTo };
|
|
302
|
+
}
|
|
303
|
+
if (flags !== void 0) {
|
|
304
|
+
payload.flags = flags;
|
|
305
|
+
}
|
|
306
|
+
await textChannel.send(payload);
|
|
296
307
|
}
|
|
297
|
-
await textChannel.send(payload);
|
|
298
308
|
}
|
|
299
309
|
async handleIncoming(message) {
|
|
300
310
|
const selfUserId = this.client?.user?.id;
|
|
@@ -353,9 +363,8 @@ var DiscordChannel = class extends BaseChannel {
|
|
|
353
363
|
...attachmentIssues.length ? { attachment_issues: attachmentIssues } : {}
|
|
354
364
|
}
|
|
355
365
|
});
|
|
356
|
-
}
|
|
366
|
+
} finally {
|
|
357
367
|
this.stopTyping(channelId);
|
|
358
|
-
throw err;
|
|
359
368
|
}
|
|
360
369
|
}
|
|
361
370
|
resolveProxyAgent() {
|
|
@@ -533,6 +542,159 @@ function buildAttachmentSummary(attachments) {
|
|
|
533
542
|
}
|
|
534
543
|
return `<media:document> (${count} ${count === 1 ? "file" : "files"})`;
|
|
535
544
|
}
|
|
545
|
+
function countLines(text) {
|
|
546
|
+
if (!text) {
|
|
547
|
+
return 0;
|
|
548
|
+
}
|
|
549
|
+
return text.split("\n").length;
|
|
550
|
+
}
|
|
551
|
+
function parseFenceLine(line) {
|
|
552
|
+
const match = line.match(FENCE_RE);
|
|
553
|
+
if (!match) {
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
const indent = match[1] ?? "";
|
|
557
|
+
const marker = match[2] ?? "";
|
|
558
|
+
return {
|
|
559
|
+
indent,
|
|
560
|
+
markerChar: marker[0] ?? "`",
|
|
561
|
+
markerLen: marker.length,
|
|
562
|
+
openLine: line
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
function closeFenceLine(openFence) {
|
|
566
|
+
return `${openFence.indent}${openFence.markerChar.repeat(openFence.markerLen)}`;
|
|
567
|
+
}
|
|
568
|
+
function closeFenceIfNeeded(text, openFence) {
|
|
569
|
+
if (!openFence) {
|
|
570
|
+
return text;
|
|
571
|
+
}
|
|
572
|
+
const closeLine = closeFenceLine(openFence);
|
|
573
|
+
if (!text) {
|
|
574
|
+
return closeLine;
|
|
575
|
+
}
|
|
576
|
+
if (!text.endsWith("\n")) {
|
|
577
|
+
return `${text}
|
|
578
|
+
${closeLine}`;
|
|
579
|
+
}
|
|
580
|
+
return `${text}${closeLine}`;
|
|
581
|
+
}
|
|
582
|
+
function splitLongLine(line, maxChars, opts) {
|
|
583
|
+
const limit = Math.max(1, Math.floor(maxChars));
|
|
584
|
+
if (line.length <= limit) {
|
|
585
|
+
return [line];
|
|
586
|
+
}
|
|
587
|
+
const chunks = [];
|
|
588
|
+
let remaining = line;
|
|
589
|
+
while (remaining.length > limit) {
|
|
590
|
+
if (opts.preserveWhitespace) {
|
|
591
|
+
chunks.push(remaining.slice(0, limit));
|
|
592
|
+
remaining = remaining.slice(limit);
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
const window = remaining.slice(0, limit);
|
|
596
|
+
let breakIndex = -1;
|
|
597
|
+
for (let index = window.length - 1; index >= 0; index -= 1) {
|
|
598
|
+
if (/\s/.test(window[index])) {
|
|
599
|
+
breakIndex = index;
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (breakIndex <= 0) {
|
|
604
|
+
breakIndex = limit;
|
|
605
|
+
}
|
|
606
|
+
chunks.push(remaining.slice(0, breakIndex));
|
|
607
|
+
remaining = remaining.slice(breakIndex);
|
|
608
|
+
}
|
|
609
|
+
if (remaining.length) {
|
|
610
|
+
chunks.push(remaining);
|
|
611
|
+
}
|
|
612
|
+
return chunks;
|
|
613
|
+
}
|
|
614
|
+
function chunkDiscordText(text, opts = {}) {
|
|
615
|
+
const maxChars = Math.max(1, Math.floor(opts.maxChars ?? DISCORD_TEXT_LIMIT));
|
|
616
|
+
const maxLines = Math.max(1, Math.floor(opts.maxLines ?? DISCORD_MAX_LINES_PER_MESSAGE));
|
|
617
|
+
const body = text ?? "";
|
|
618
|
+
if (!body) {
|
|
619
|
+
return [];
|
|
620
|
+
}
|
|
621
|
+
if (body.length <= maxChars && countLines(body) <= maxLines) {
|
|
622
|
+
return [body];
|
|
623
|
+
}
|
|
624
|
+
const lines = body.split("\n");
|
|
625
|
+
const chunks = [];
|
|
626
|
+
let current = "";
|
|
627
|
+
let currentLines = 0;
|
|
628
|
+
let openFence = null;
|
|
629
|
+
const flush = () => {
|
|
630
|
+
if (!current) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
const payload = closeFenceIfNeeded(current, openFence);
|
|
634
|
+
if (payload.trim().length) {
|
|
635
|
+
chunks.push(payload);
|
|
636
|
+
}
|
|
637
|
+
current = "";
|
|
638
|
+
currentLines = 0;
|
|
639
|
+
if (openFence) {
|
|
640
|
+
current = openFence.openLine;
|
|
641
|
+
currentLines = 1;
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
for (const line of lines) {
|
|
645
|
+
const fenceInfo = parseFenceLine(line);
|
|
646
|
+
const wasInsideFence = openFence !== null;
|
|
647
|
+
let nextOpenFence = openFence;
|
|
648
|
+
if (fenceInfo) {
|
|
649
|
+
if (!openFence) {
|
|
650
|
+
nextOpenFence = fenceInfo;
|
|
651
|
+
} else if (openFence.markerChar === fenceInfo.markerChar && fenceInfo.markerLen >= openFence.markerLen) {
|
|
652
|
+
nextOpenFence = null;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
const reserveChars = nextOpenFence ? closeFenceLine(nextOpenFence).length + 1 : 0;
|
|
656
|
+
const reserveLines = nextOpenFence ? 1 : 0;
|
|
657
|
+
const effectiveMaxChars = maxChars - reserveChars;
|
|
658
|
+
const effectiveMaxLines = maxLines - reserveLines;
|
|
659
|
+
const charLimit = effectiveMaxChars > 0 ? effectiveMaxChars : maxChars;
|
|
660
|
+
const lineLimit = effectiveMaxLines > 0 ? effectiveMaxLines : maxLines;
|
|
661
|
+
const prefixLength = current.length > 0 ? current.length + 1 : 0;
|
|
662
|
+
const segmentLimit = Math.max(1, charLimit - prefixLength);
|
|
663
|
+
const segments = splitLongLine(line, segmentLimit, {
|
|
664
|
+
preserveWhitespace: wasInsideFence
|
|
665
|
+
});
|
|
666
|
+
for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex += 1) {
|
|
667
|
+
const segment2 = segments[segmentIndex];
|
|
668
|
+
const isContinuation = segmentIndex > 0;
|
|
669
|
+
const delimiter = isContinuation ? "" : current.length > 0 ? "\n" : "";
|
|
670
|
+
const addition = `${delimiter}${segment2}`;
|
|
671
|
+
const nextLength = current.length + addition.length;
|
|
672
|
+
const nextLineCount = currentLines + (isContinuation ? 0 : 1);
|
|
673
|
+
const exceedsChars = nextLength > charLimit;
|
|
674
|
+
const exceedsLines = nextLineCount > lineLimit;
|
|
675
|
+
if ((exceedsChars || exceedsLines) && current.length > 0) {
|
|
676
|
+
flush();
|
|
677
|
+
}
|
|
678
|
+
if (current.length > 0) {
|
|
679
|
+
current += addition;
|
|
680
|
+
if (!isContinuation) {
|
|
681
|
+
currentLines += 1;
|
|
682
|
+
}
|
|
683
|
+
} else {
|
|
684
|
+
current = segment2;
|
|
685
|
+
currentLines = 1;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
openFence = nextOpenFence;
|
|
689
|
+
}
|
|
690
|
+
if (current.length) {
|
|
691
|
+
const payload = closeFenceIfNeeded(current, openFence);
|
|
692
|
+
if (payload.trim().length) {
|
|
693
|
+
chunks.push(payload);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return chunks;
|
|
697
|
+
}
|
|
536
698
|
|
|
537
699
|
// src/channels/email.ts
|
|
538
700
|
import { ImapFlow } from "imapflow";
|
|
@@ -2386,9 +2548,8 @@ Just send me a text message to chat!`;
|
|
|
2386
2548
|
is_bot: sender.isBot,
|
|
2387
2549
|
is_group: message.chat.type !== "private"
|
|
2388
2550
|
});
|
|
2389
|
-
}
|
|
2551
|
+
} finally {
|
|
2390
2552
|
this.stopTyping(chatId);
|
|
2391
|
-
throw err;
|
|
2392
2553
|
}
|
|
2393
2554
|
}
|
|
2394
2555
|
async dispatchToBus(senderId, chatId, content, attachments, metadata) {
|