@nextclaw/channel-runtime 0.1.4 → 0.1.5
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 +171 -8
- 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;
|
|
@@ -533,6 +543,159 @@ function buildAttachmentSummary(attachments) {
|
|
|
533
543
|
}
|
|
534
544
|
return `<media:document> (${count} ${count === 1 ? "file" : "files"})`;
|
|
535
545
|
}
|
|
546
|
+
function countLines(text) {
|
|
547
|
+
if (!text) {
|
|
548
|
+
return 0;
|
|
549
|
+
}
|
|
550
|
+
return text.split("\n").length;
|
|
551
|
+
}
|
|
552
|
+
function parseFenceLine(line) {
|
|
553
|
+
const match = line.match(FENCE_RE);
|
|
554
|
+
if (!match) {
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
const indent = match[1] ?? "";
|
|
558
|
+
const marker = match[2] ?? "";
|
|
559
|
+
return {
|
|
560
|
+
indent,
|
|
561
|
+
markerChar: marker[0] ?? "`",
|
|
562
|
+
markerLen: marker.length,
|
|
563
|
+
openLine: line
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
function closeFenceLine(openFence) {
|
|
567
|
+
return `${openFence.indent}${openFence.markerChar.repeat(openFence.markerLen)}`;
|
|
568
|
+
}
|
|
569
|
+
function closeFenceIfNeeded(text, openFence) {
|
|
570
|
+
if (!openFence) {
|
|
571
|
+
return text;
|
|
572
|
+
}
|
|
573
|
+
const closeLine = closeFenceLine(openFence);
|
|
574
|
+
if (!text) {
|
|
575
|
+
return closeLine;
|
|
576
|
+
}
|
|
577
|
+
if (!text.endsWith("\n")) {
|
|
578
|
+
return `${text}
|
|
579
|
+
${closeLine}`;
|
|
580
|
+
}
|
|
581
|
+
return `${text}${closeLine}`;
|
|
582
|
+
}
|
|
583
|
+
function splitLongLine(line, maxChars, opts) {
|
|
584
|
+
const limit = Math.max(1, Math.floor(maxChars));
|
|
585
|
+
if (line.length <= limit) {
|
|
586
|
+
return [line];
|
|
587
|
+
}
|
|
588
|
+
const chunks = [];
|
|
589
|
+
let remaining = line;
|
|
590
|
+
while (remaining.length > limit) {
|
|
591
|
+
if (opts.preserveWhitespace) {
|
|
592
|
+
chunks.push(remaining.slice(0, limit));
|
|
593
|
+
remaining = remaining.slice(limit);
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
const window = remaining.slice(0, limit);
|
|
597
|
+
let breakIndex = -1;
|
|
598
|
+
for (let index = window.length - 1; index >= 0; index -= 1) {
|
|
599
|
+
if (/\s/.test(window[index])) {
|
|
600
|
+
breakIndex = index;
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (breakIndex <= 0) {
|
|
605
|
+
breakIndex = limit;
|
|
606
|
+
}
|
|
607
|
+
chunks.push(remaining.slice(0, breakIndex));
|
|
608
|
+
remaining = remaining.slice(breakIndex);
|
|
609
|
+
}
|
|
610
|
+
if (remaining.length) {
|
|
611
|
+
chunks.push(remaining);
|
|
612
|
+
}
|
|
613
|
+
return chunks;
|
|
614
|
+
}
|
|
615
|
+
function chunkDiscordText(text, opts = {}) {
|
|
616
|
+
const maxChars = Math.max(1, Math.floor(opts.maxChars ?? DISCORD_TEXT_LIMIT));
|
|
617
|
+
const maxLines = Math.max(1, Math.floor(opts.maxLines ?? DISCORD_MAX_LINES_PER_MESSAGE));
|
|
618
|
+
const body = text ?? "";
|
|
619
|
+
if (!body) {
|
|
620
|
+
return [];
|
|
621
|
+
}
|
|
622
|
+
if (body.length <= maxChars && countLines(body) <= maxLines) {
|
|
623
|
+
return [body];
|
|
624
|
+
}
|
|
625
|
+
const lines = body.split("\n");
|
|
626
|
+
const chunks = [];
|
|
627
|
+
let current = "";
|
|
628
|
+
let currentLines = 0;
|
|
629
|
+
let openFence = null;
|
|
630
|
+
const flush = () => {
|
|
631
|
+
if (!current) {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
const payload = closeFenceIfNeeded(current, openFence);
|
|
635
|
+
if (payload.trim().length) {
|
|
636
|
+
chunks.push(payload);
|
|
637
|
+
}
|
|
638
|
+
current = "";
|
|
639
|
+
currentLines = 0;
|
|
640
|
+
if (openFence) {
|
|
641
|
+
current = openFence.openLine;
|
|
642
|
+
currentLines = 1;
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
for (const line of lines) {
|
|
646
|
+
const fenceInfo = parseFenceLine(line);
|
|
647
|
+
const wasInsideFence = openFence !== null;
|
|
648
|
+
let nextOpenFence = openFence;
|
|
649
|
+
if (fenceInfo) {
|
|
650
|
+
if (!openFence) {
|
|
651
|
+
nextOpenFence = fenceInfo;
|
|
652
|
+
} else if (openFence.markerChar === fenceInfo.markerChar && fenceInfo.markerLen >= openFence.markerLen) {
|
|
653
|
+
nextOpenFence = null;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
const reserveChars = nextOpenFence ? closeFenceLine(nextOpenFence).length + 1 : 0;
|
|
657
|
+
const reserveLines = nextOpenFence ? 1 : 0;
|
|
658
|
+
const effectiveMaxChars = maxChars - reserveChars;
|
|
659
|
+
const effectiveMaxLines = maxLines - reserveLines;
|
|
660
|
+
const charLimit = effectiveMaxChars > 0 ? effectiveMaxChars : maxChars;
|
|
661
|
+
const lineLimit = effectiveMaxLines > 0 ? effectiveMaxLines : maxLines;
|
|
662
|
+
const prefixLength = current.length > 0 ? current.length + 1 : 0;
|
|
663
|
+
const segmentLimit = Math.max(1, charLimit - prefixLength);
|
|
664
|
+
const segments = splitLongLine(line, segmentLimit, {
|
|
665
|
+
preserveWhitespace: wasInsideFence
|
|
666
|
+
});
|
|
667
|
+
for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex += 1) {
|
|
668
|
+
const segment2 = segments[segmentIndex];
|
|
669
|
+
const isContinuation = segmentIndex > 0;
|
|
670
|
+
const delimiter = isContinuation ? "" : current.length > 0 ? "\n" : "";
|
|
671
|
+
const addition = `${delimiter}${segment2}`;
|
|
672
|
+
const nextLength = current.length + addition.length;
|
|
673
|
+
const nextLineCount = currentLines + (isContinuation ? 0 : 1);
|
|
674
|
+
const exceedsChars = nextLength > charLimit;
|
|
675
|
+
const exceedsLines = nextLineCount > lineLimit;
|
|
676
|
+
if ((exceedsChars || exceedsLines) && current.length > 0) {
|
|
677
|
+
flush();
|
|
678
|
+
}
|
|
679
|
+
if (current.length > 0) {
|
|
680
|
+
current += addition;
|
|
681
|
+
if (!isContinuation) {
|
|
682
|
+
currentLines += 1;
|
|
683
|
+
}
|
|
684
|
+
} else {
|
|
685
|
+
current = segment2;
|
|
686
|
+
currentLines = 1;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
openFence = nextOpenFence;
|
|
690
|
+
}
|
|
691
|
+
if (current.length) {
|
|
692
|
+
const payload = closeFenceIfNeeded(current, openFence);
|
|
693
|
+
if (payload.trim().length) {
|
|
694
|
+
chunks.push(payload);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return chunks;
|
|
698
|
+
}
|
|
536
699
|
|
|
537
700
|
// src/channels/email.ts
|
|
538
701
|
import { ImapFlow } from "imapflow";
|