@capybara-hq/plugin-telegram 0.1.0-rc.4 → 0.1.0-rc.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/.capybara-plugin/plugin.json +2 -2
- package/dist/index.mjs +110 -10
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"name": "telegram",
|
|
4
|
-
"version": "0.1.0-rc.
|
|
4
|
+
"version": "0.1.0-rc.5",
|
|
5
5
|
"publisher": "Capybara",
|
|
6
6
|
"description": {
|
|
7
7
|
"en": "Telegram (channel@1 bot over the Bot API: long polling or webhook, inline keyboard cards)",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"entry": "./dist/index.mjs",
|
|
18
18
|
"manifest": {
|
|
19
19
|
"name": "telegram",
|
|
20
|
-
"version": "0.1.0-rc.
|
|
20
|
+
"version": "0.1.0-rc.5",
|
|
21
21
|
"sdk": "^0.1.0-rc.4",
|
|
22
22
|
"provides": [
|
|
23
23
|
"channel@1"
|
package/dist/index.mjs
CHANGED
|
@@ -265,6 +265,102 @@ import {
|
|
|
265
265
|
renderCardAsText
|
|
266
266
|
} from "@capybara-hq/plugin-sdk";
|
|
267
267
|
|
|
268
|
+
// src/tables.ts
|
|
269
|
+
var FENCE_OPEN = /^\s*(?:```|~~~)/;
|
|
270
|
+
var FENCE_CLOSE = /^\s*(```|~~~)\s*$/;
|
|
271
|
+
var DELIM_CELL = /^:?-+:?$/;
|
|
272
|
+
function splitRow(line) {
|
|
273
|
+
let s = line.trim();
|
|
274
|
+
if (!/(^|[^\\])\|/.test(s)) return void 0;
|
|
275
|
+
if (s.startsWith("|")) s = s.slice(1);
|
|
276
|
+
if (s.endsWith("|") && !s.endsWith("\\|")) s = s.slice(0, -1);
|
|
277
|
+
const cells = [];
|
|
278
|
+
let cur = "";
|
|
279
|
+
for (let i = 0; i < s.length; i++) {
|
|
280
|
+
const c = s[i];
|
|
281
|
+
if (c === "\\" && s[i + 1] === "|") {
|
|
282
|
+
cur += "|";
|
|
283
|
+
i++;
|
|
284
|
+
} else if (c === "|") {
|
|
285
|
+
cells.push(cur.trim());
|
|
286
|
+
cur = "";
|
|
287
|
+
} else cur += c;
|
|
288
|
+
}
|
|
289
|
+
cells.push(cur.trim());
|
|
290
|
+
return cells;
|
|
291
|
+
}
|
|
292
|
+
var candidate = (line) => !/^\s{4,}/.test(line) && !/^\s*>/.test(line);
|
|
293
|
+
function delimiterRow(line, width) {
|
|
294
|
+
if (line === void 0 || !candidate(line)) return false;
|
|
295
|
+
const cells = splitRow(line);
|
|
296
|
+
return cells !== void 0 && cells.length === width && cells.every((c) => DELIM_CELL.test(c));
|
|
297
|
+
}
|
|
298
|
+
var fit = (cells, width) => Array.from({ length: width }, (_, i) => cells[i] ?? "");
|
|
299
|
+
function segmentTables(md) {
|
|
300
|
+
const lines = md.replace(/\r\n?/g, "\n").split("\n");
|
|
301
|
+
const out = [];
|
|
302
|
+
let text = [];
|
|
303
|
+
const flushText = () => {
|
|
304
|
+
if (text.length > 0) out.push({ kind: "text", lines: text });
|
|
305
|
+
text = [];
|
|
306
|
+
};
|
|
307
|
+
let inFence = false;
|
|
308
|
+
for (let i = 0; i < lines.length; i++) {
|
|
309
|
+
const line = lines[i];
|
|
310
|
+
if (inFence ? FENCE_CLOSE.test(line) : FENCE_OPEN.test(line)) {
|
|
311
|
+
inFence = !inFence;
|
|
312
|
+
text.push(line);
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
const header = !inFence && candidate(line) ? splitRow(line) : void 0;
|
|
316
|
+
if (!header || !delimiterRow(lines[i + 1], header.length)) {
|
|
317
|
+
text.push(line);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
const rows = [];
|
|
321
|
+
let j = i + 2;
|
|
322
|
+
for (; j < lines.length; j++) {
|
|
323
|
+
const l = lines[j];
|
|
324
|
+
if (l.trim() === "" || FENCE_OPEN.test(l) || !candidate(l)) break;
|
|
325
|
+
const cells = splitRow(l);
|
|
326
|
+
if (!cells) break;
|
|
327
|
+
rows.push(fit(cells, header.length));
|
|
328
|
+
}
|
|
329
|
+
flushText();
|
|
330
|
+
out.push({ kind: "table", table: { header, rows } });
|
|
331
|
+
i = j - 1;
|
|
332
|
+
}
|
|
333
|
+
flushText();
|
|
334
|
+
return out;
|
|
335
|
+
}
|
|
336
|
+
var BR = /<br\s*\/?>/gi;
|
|
337
|
+
var CJK_END = /[⺀-鿿가-豈--]$/u;
|
|
338
|
+
var INDENT = " ";
|
|
339
|
+
var pair = (h, v) => h === "" ? v : `${h}${CJK_END.test(h) ? ":" : ": "}${v}`;
|
|
340
|
+
var lead = (cell) => /`|\*\*|__/.test(cell) ? cell : `**${cell}**`;
|
|
341
|
+
function tableToListMarkdown(t) {
|
|
342
|
+
const clean = (s) => s.replace(BR, " / ").trim();
|
|
343
|
+
const header = t.header.map(clean);
|
|
344
|
+
if (t.rows.length === 0) return [header.filter((h) => h !== "").join(" · ")];
|
|
345
|
+
const out = [];
|
|
346
|
+
for (const raw of t.rows) {
|
|
347
|
+
const [first = "", ...rest] = raw.map(clean);
|
|
348
|
+
const head = first === "" ? "" : lead(first);
|
|
349
|
+
const items = rest.flatMap((v, i) => v === "" ? [] : [pair(header[i + 1] ?? "", v)]);
|
|
350
|
+
if (head === "" && items.length === 0) continue;
|
|
351
|
+
if (header.length === 2) {
|
|
352
|
+
out.push(`- ${[head, rest[0] ?? ""].filter((s) => s !== "").join(" — ")}`);
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
out.push(`- ${head === "" ? items.shift() ?? "" : head}`);
|
|
356
|
+
for (const it of items) out.push(`${INDENT}${it}`);
|
|
357
|
+
}
|
|
358
|
+
return out;
|
|
359
|
+
}
|
|
360
|
+
function tablesToLists(md) {
|
|
361
|
+
return segmentTables(md).flatMap((s) => s.kind === "text" ? s.lines : tableToListMarkdown(s.table)).join("\n");
|
|
362
|
+
}
|
|
363
|
+
|
|
268
364
|
// src/html.ts
|
|
269
365
|
var TEXT_LIMIT = 4096;
|
|
270
366
|
function escapeHtml(s) {
|
|
@@ -347,7 +443,7 @@ function codeBlock(lang, lines) {
|
|
|
347
443
|
return l === "" ? `<pre>${body}</pre>` : `<pre><code class="language-${l}">${body}</code></pre>`;
|
|
348
444
|
}
|
|
349
445
|
function markdownToTelegramHtml(md) {
|
|
350
|
-
const lines = md.replace(/\r\n?/g, "\n").replaceAll("\0", "").split("\n");
|
|
446
|
+
const lines = tablesToLists(md.replace(/\r\n?/g, "\n").replaceAll("\0", "")).split("\n");
|
|
351
447
|
const out = [];
|
|
352
448
|
let fence;
|
|
353
449
|
let quote;
|
|
@@ -410,12 +506,12 @@ function truncateHtml(html, limit = TEXT_LIMIT) {
|
|
|
410
506
|
const closing = /(?:<\/[a-z-]+>)*$/.exec(head)?.[0] ?? "";
|
|
411
507
|
return `${head.slice(0, head.length - closing.length)}…${closing}`;
|
|
412
508
|
}
|
|
413
|
-
function fitHtml(
|
|
509
|
+
function fitHtml(lead2, body, limit = TEXT_LIMIT) {
|
|
414
510
|
const sep = "\n\n";
|
|
415
|
-
if (
|
|
416
|
-
if (body === "") return truncateHtml(
|
|
511
|
+
if (lead2 === "") return truncateHtml(body, limit);
|
|
512
|
+
if (body === "") return truncateHtml(lead2, limit);
|
|
417
513
|
const room = limit - visibleLength(body) - sep.length;
|
|
418
|
-
if (room >= 2) return `${truncateHtml(
|
|
514
|
+
if (room >= 2) return `${truncateHtml(lead2, room)}${sep}${body}`;
|
|
419
515
|
return truncateHtml(body, limit);
|
|
420
516
|
}
|
|
421
517
|
function splitHtml(html, limit = TEXT_LIMIT) {
|
|
@@ -426,6 +522,7 @@ function splitHtml(html, limit = TEXT_LIMIT) {
|
|
|
426
522
|
let stack = [];
|
|
427
523
|
let prefix = "";
|
|
428
524
|
let cand;
|
|
525
|
+
let nl;
|
|
429
526
|
const emit = (toks, count, at) => {
|
|
430
527
|
if (count > 0) parts.push(prefix + toks.map((t) => t.text).join("") + closers(at));
|
|
431
528
|
};
|
|
@@ -439,6 +536,7 @@ function splitHtml(html, limit = TEXT_LIMIT) {
|
|
|
439
536
|
continue;
|
|
440
537
|
}
|
|
441
538
|
if (vis + t.len > limit) {
|
|
539
|
+
if (nl && nl.vis >= limit / 2) cand = nl;
|
|
442
540
|
if (cand && cand.vis >= limit / 2) {
|
|
443
541
|
emit(cur.slice(0, cand.i), cand.vis, cand.stack);
|
|
444
542
|
const rest = cur.slice(cand.i + 1);
|
|
@@ -452,8 +550,10 @@ function splitHtml(html, limit = TEXT_LIMIT) {
|
|
|
452
550
|
cur = [];
|
|
453
551
|
}
|
|
454
552
|
cand = void 0;
|
|
553
|
+
nl = void 0;
|
|
455
554
|
}
|
|
456
555
|
if (/^\s$/.test(t.text)) cand = { i: cur.length, vis, stack: [...stack] };
|
|
556
|
+
if (t.text === "\n") nl = cand;
|
|
457
557
|
cur.push(t);
|
|
458
558
|
vis += t.len;
|
|
459
559
|
}
|
|
@@ -1045,14 +1145,14 @@ function createTelegramChannel(o) {
|
|
|
1045
1145
|
return text === "" ? [] : splitHtml(markdownToTelegramHtml(message.text)).map((html) => ({ html }));
|
|
1046
1146
|
}
|
|
1047
1147
|
const dup = cards.some((c) => cardHeadline(c, locale)?.trim() === text);
|
|
1048
|
-
const
|
|
1049
|
-
const
|
|
1148
|
+
const lead2 = text === "" || dup ? "" : markdownToTelegramHtml(message.text);
|
|
1149
|
+
const fit2 = (p, leading) => ({
|
|
1050
1150
|
...p,
|
|
1051
1151
|
html: fitHtml(leading, p.html),
|
|
1052
|
-
...p.withoutUrlButtons && { withoutUrlButtons:
|
|
1152
|
+
...p.withoutUrlButtons && { withoutUrlButtons: fit2(p.withoutUrlButtons, leading) }
|
|
1053
1153
|
});
|
|
1054
1154
|
const posts = await renderCards(cards, locale, cardTokenStore(o.kv, botId), o.log);
|
|
1055
|
-
return posts.map((p, i) =>
|
|
1155
|
+
return posts.map((p, i) => fit2(p, i === 0 ? lead2 : ""));
|
|
1056
1156
|
}
|
|
1057
1157
|
async function withUrlButtonFallback(post, attempt) {
|
|
1058
1158
|
try {
|
|
@@ -1242,7 +1342,7 @@ function createTelegramChannel(o) {
|
|
|
1242
1342
|
// src/index.ts
|
|
1243
1343
|
var index_default = defineExtension({
|
|
1244
1344
|
name: "telegram",
|
|
1245
|
-
version: "0.1.0-rc.
|
|
1345
|
+
version: "0.1.0-rc.5",
|
|
1246
1346
|
sdk: "^0.1.0-rc.4",
|
|
1247
1347
|
provides: [CHANNEL_EXTENSION_POINT],
|
|
1248
1348
|
permissions: { network: ["api.telegram.org"] },
|
package/package.json
CHANGED