@yaebal/rich 0.0.1
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/LICENSE +21 -0
- package/README.md +109 -0
- package/lib/blocks.d.ts +115 -0
- package/lib/blocks.d.ts.map +1 -0
- package/lib/blocks.js +149 -0
- package/lib/blocks.js.map +1 -0
- package/lib/draft.d.ts +44 -0
- package/lib/draft.d.ts.map +1 -0
- package/lib/draft.js +85 -0
- package/lib/draft.js.map +1 -0
- package/lib/escape.d.ts +5 -0
- package/lib/escape.d.ts.map +1 -0
- package/lib/escape.js +9 -0
- package/lib/escape.js.map +1 -0
- package/lib/guards.d.ts +106 -0
- package/lib/guards.d.ts.map +1 -0
- package/lib/guards.js +55 -0
- package/lib/guards.js.map +1 -0
- package/lib/index.d.ts +33 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +32 -0
- package/lib/index.js.map +1 -0
- package/lib/index.test.d.ts +2 -0
- package/lib/index.test.d.ts.map +1 -0
- package/lib/index.test.js +130 -0
- package/lib/index.test.js.map +1 -0
- package/lib/inline.d.ts +79 -0
- package/lib/inline.d.ts.map +1 -0
- package/lib/inline.js +120 -0
- package/lib/inline.js.map +1 -0
- package/lib/message.d.ts +26 -0
- package/lib/message.d.ts.map +1 -0
- package/lib/message.js +31 -0
- package/lib/message.js.map +1 -0
- package/lib/plaintext.d.ts +16 -0
- package/lib/plaintext.d.ts.map +1 -0
- package/lib/plaintext.js +104 -0
- package/lib/plaintext.js.map +1 -0
- package/lib/send.d.ts +27 -0
- package/lib/send.d.ts.map +1 -0
- package/lib/send.js +52 -0
- package/lib/send.js.map +1 -0
- package/package.json +51 -0
- package/src/blocks.ts +250 -0
- package/src/draft.ts +116 -0
- package/src/escape.ts +9 -0
- package/src/guards.ts +169 -0
- package/src/index.test.ts +201 -0
- package/src/index.ts +199 -0
- package/src/inline.ts +152 -0
- package/src/message.ts +45 -0
- package/src/plaintext.ts +143 -0
- package/src/send.ts +81 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline.js","sourceRoot":"","sources":["../src/inline.ts"],"names":[],"mappings":"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"}
|
package/lib/message.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { InputRichMessage } from "@yaebal/types";
|
|
2
|
+
import type { Insertable } from "./inline.js";
|
|
3
|
+
export interface DocumentOptions {
|
|
4
|
+
/** `InputRichMessage.is_rtl` — show the message right-to-left. */
|
|
5
|
+
rtl?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* `InputRichMessage.skip_entity_detection` — turn off auto-detection of urls,
|
|
8
|
+
* emails, `@mentions`, `#hashtags`, `$cashtags`, `/bot_commands`, and phone
|
|
9
|
+
* numbers in plain text.
|
|
10
|
+
*/
|
|
11
|
+
skipEntityDetection?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* assemble top-level blocks (from blocks.ts) into an `InputRichMessage.html`
|
|
15
|
+
* payload. pass the result straight to `sendRichMessage`/`sendRichMessageDraft`
|
|
16
|
+
* or a `RichMessageDraft`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function document(blocks: Insertable[], options?: DocumentOptions): InputRichMessage;
|
|
19
|
+
/**
|
|
20
|
+
* a raw markdown payload — telegram parses `InputRichMessage.markdown` the same
|
|
21
|
+
* way as `html`, but the extended block syntax (tables, `tg-thinking`, …) is not
|
|
22
|
+
* documented in markdown form, so unlike `document()` this has no builder: pass a
|
|
23
|
+
* literal string.
|
|
24
|
+
*/
|
|
25
|
+
export declare function markdown(source: string, options?: DocumentOptions): InputRichMessage;
|
|
26
|
+
//# sourceMappingURL=message.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../src/message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,eAAe;IAC/B,kEAAkE;IAClE,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,OAAO,GAAE,eAAoB,GAAG,gBAAgB,CAQ9F;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,gBAAgB,CAQxF"}
|
package/lib/message.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { toHtml } from "./inline.js";
|
|
2
|
+
/**
|
|
3
|
+
* assemble top-level blocks (from blocks.ts) into an `InputRichMessage.html`
|
|
4
|
+
* payload. pass the result straight to `sendRichMessage`/`sendRichMessageDraft`
|
|
5
|
+
* or a `RichMessageDraft`.
|
|
6
|
+
*/
|
|
7
|
+
export function document(blocks, options = {}) {
|
|
8
|
+
return {
|
|
9
|
+
html: blocks.map(toHtml).join(""),
|
|
10
|
+
...(options.rtl !== undefined ? { is_rtl: options.rtl } : {}),
|
|
11
|
+
...(options.skipEntityDetection !== undefined
|
|
12
|
+
? { skip_entity_detection: options.skipEntityDetection }
|
|
13
|
+
: {}),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* a raw markdown payload — telegram parses `InputRichMessage.markdown` the same
|
|
18
|
+
* way as `html`, but the extended block syntax (tables, `tg-thinking`, …) is not
|
|
19
|
+
* documented in markdown form, so unlike `document()` this has no builder: pass a
|
|
20
|
+
* literal string.
|
|
21
|
+
*/
|
|
22
|
+
export function markdown(source, options = {}) {
|
|
23
|
+
return {
|
|
24
|
+
markdown: source,
|
|
25
|
+
...(options.rtl !== undefined ? { is_rtl: options.rtl } : {}),
|
|
26
|
+
...(options.skipEntityDetection !== undefined
|
|
27
|
+
? { skip_entity_detection: options.skipEntityDetection }
|
|
28
|
+
: {}),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message.js","sourceRoot":"","sources":["../src/message.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAarC;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAoB,EAAE,UAA2B,EAAE;IAC3E,OAAO;QACN,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,GAAG,CAAC,OAAO,CAAC,mBAAmB,KAAK,SAAS;YAC5C,CAAC,CAAC,EAAE,qBAAqB,EAAE,OAAO,CAAC,mBAAmB,EAAE;YACxD,CAAC,CAAC,EAAE,CAAC;KACN,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,UAA2B,EAAE;IACrE,OAAO;QACN,QAAQ,EAAE,MAAM;QAChB,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,GAAG,CAAC,OAAO,CAAC,mBAAmB,KAAK,SAAS;YAC5C,CAAC,CAAC,EAAE,qBAAqB,EAAE,OAAO,CAAC,mBAAmB,EAAE;YACxD,CAAC,CAAC,EAAE,CAAC;KACN,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RichBlock, RichMessage, RichText } from "@yaebal/types";
|
|
2
|
+
/**
|
|
3
|
+
* the schema documents `RichText` fields as "either a String for plain text, an
|
|
4
|
+
* Array of RichText, or" one of the marked-up variants — but the generated
|
|
5
|
+
* `RichText` type only lists the object variants. widen locally so plain-string
|
|
6
|
+
* leaves (by far the common case) don't need a cast at every call site.
|
|
7
|
+
*/
|
|
8
|
+
type RichTextLike = RichText | string | RichTextLike[] | undefined;
|
|
9
|
+
/** flatten a `RichText` field (bold/link/spoiler/…) down to its plain characters. */
|
|
10
|
+
export declare function richTextToPlainText(text: RichTextLike): string;
|
|
11
|
+
/** flatten one `RichBlock` (and its nested blocks) down to plain text, one block per line. */
|
|
12
|
+
export declare function richBlockToPlainText(block: RichBlock): string;
|
|
13
|
+
/** flatten a whole `RichMessage.blocks` tree to plain text — search indices, logs, notifications. */
|
|
14
|
+
export declare function richMessageToPlainText(message: RichMessage): string;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=plaintext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plaintext.d.ts","sourceRoot":"","sources":["../src/plaintext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,SAAS,EAqBT,WAAW,EACX,QAAQ,EAGR,MAAM,eAAe,CAAC;AAEvB;;;;;GAKG;AACH,KAAK,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,YAAY,EAAE,GAAG,SAAS,CAAC;AAenE,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAiB9D;AASD,8FAA8F;AAC9F,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CA4D7D;AAED,qGAAqG;AACrG,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAEnE"}
|
package/lib/plaintext.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* every generated `.type` field is plain `string`, not a literal — so unlike a
|
|
3
|
+
* normal discriminated union, `switch (x.type)` narrows the *string*, not `x`
|
|
4
|
+
* itself, and (worse) several `RichBlock`/`RichText` interfaces are structurally
|
|
5
|
+
* identical (e.g. `RichBlockParagraph`/`RichBlockFooter`/`RichBlockThinking` are
|
|
6
|
+
* all just `{ type; text }`), which makes typescript's guard-based negative
|
|
7
|
+
* narrowing collapse to `never` if you lean on it across a chain. so: switch on
|
|
8
|
+
* the string, cast once per case instead of narrowing.
|
|
9
|
+
*/
|
|
10
|
+
function as(value) {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
/** flatten a `RichText` field (bold/link/spoiler/…) down to its plain characters. */
|
|
14
|
+
export function richTextToPlainText(text) {
|
|
15
|
+
if (text === undefined)
|
|
16
|
+
return "";
|
|
17
|
+
if (typeof text === "string")
|
|
18
|
+
return text;
|
|
19
|
+
if (Array.isArray(text))
|
|
20
|
+
return text.map(richTextToPlainText).join("");
|
|
21
|
+
switch (text.type) {
|
|
22
|
+
case "custom_emoji":
|
|
23
|
+
return as(text).alternative_text;
|
|
24
|
+
case "mathematical_expression":
|
|
25
|
+
return as(text).expression;
|
|
26
|
+
case "anchor":
|
|
27
|
+
return "";
|
|
28
|
+
// every remaining variant wraps a nested `text` field (bold, italic, url,
|
|
29
|
+
// mention, hashtag, date_time, reference, anchor_link, text_mention, …).
|
|
30
|
+
default:
|
|
31
|
+
return richTextToPlainText(as(text).text);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function captionToPlainText(caption) {
|
|
35
|
+
if (!caption)
|
|
36
|
+
return "";
|
|
37
|
+
const credit = caption.credit ? ` (${richTextToPlainText(caption.credit)})` : "";
|
|
38
|
+
return `${richTextToPlainText(caption.text)}${credit}\n`;
|
|
39
|
+
}
|
|
40
|
+
/** flatten one `RichBlock` (and its nested blocks) down to plain text, one block per line. */
|
|
41
|
+
export function richBlockToPlainText(block) {
|
|
42
|
+
switch (block.type) {
|
|
43
|
+
case "paragraph":
|
|
44
|
+
return `${richTextToPlainText(as(block).text)}\n`;
|
|
45
|
+
case "heading":
|
|
46
|
+
return `${richTextToPlainText(as(block).text)}\n`;
|
|
47
|
+
case "pre":
|
|
48
|
+
return `${richTextToPlainText(as(block).text)}\n`;
|
|
49
|
+
case "footer":
|
|
50
|
+
return `${richTextToPlainText(as(block).text)}\n`;
|
|
51
|
+
case "thinking":
|
|
52
|
+
return `${richTextToPlainText(as(block).text)}\n`;
|
|
53
|
+
case "divider":
|
|
54
|
+
return "---\n";
|
|
55
|
+
case "anchor":
|
|
56
|
+
return "";
|
|
57
|
+
case "mathematical_expression":
|
|
58
|
+
return `${as(block).expression}\n`;
|
|
59
|
+
case "list": {
|
|
60
|
+
const { items } = as(block);
|
|
61
|
+
return `${items
|
|
62
|
+
.map((entry) => `- ${entry.blocks.map(richBlockToPlainText).join("").trim()}`)
|
|
63
|
+
.join("\n")}\n`;
|
|
64
|
+
}
|
|
65
|
+
case "blockquote": {
|
|
66
|
+
const { blocks, credit } = as(block);
|
|
67
|
+
return `${blocks.map(richBlockToPlainText).join("")}${credit ? ` — ${richTextToPlainText(credit)}` : ""}\n`;
|
|
68
|
+
}
|
|
69
|
+
case "pullquote": {
|
|
70
|
+
const { text, credit } = as(block);
|
|
71
|
+
return `${richTextToPlainText(text)}${credit ? ` — ${richTextToPlainText(credit)}` : ""}\n`;
|
|
72
|
+
}
|
|
73
|
+
case "collage":
|
|
74
|
+
case "slideshow": {
|
|
75
|
+
const { blocks, caption } = as(block);
|
|
76
|
+
return `${blocks.map(richBlockToPlainText).join("")}${captionToPlainText(caption)}`;
|
|
77
|
+
}
|
|
78
|
+
case "table": {
|
|
79
|
+
const { cells } = as(block);
|
|
80
|
+
return `${cells.map((row) => row.map((c) => richTextToPlainText(c.text)).join(" | ")).join("\n")}\n`;
|
|
81
|
+
}
|
|
82
|
+
case "details": {
|
|
83
|
+
const { summary, blocks } = as(block);
|
|
84
|
+
return `${richTextToPlainText(summary)}\n${blocks.map(richBlockToPlainText).join("")}`;
|
|
85
|
+
}
|
|
86
|
+
case "map":
|
|
87
|
+
return captionToPlainText(as(block).caption);
|
|
88
|
+
case "animation":
|
|
89
|
+
case "audio":
|
|
90
|
+
case "photo":
|
|
91
|
+
case "video":
|
|
92
|
+
case "voice_note": {
|
|
93
|
+
const { caption } = as(block);
|
|
94
|
+
return captionToPlainText(caption);
|
|
95
|
+
}
|
|
96
|
+
default:
|
|
97
|
+
return "";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** flatten a whole `RichMessage.blocks` tree to plain text — search indices, logs, notifications. */
|
|
101
|
+
export function richMessageToPlainText(message) {
|
|
102
|
+
return message.blocks.map(richBlockToPlainText).join("").trim();
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=plaintext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plaintext.js","sourceRoot":"","sources":["../src/plaintext.ts"],"names":[],"mappings":"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"}
|
package/lib/send.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Api, Context, Message, Plugin } from "@yaebal/core";
|
|
2
|
+
import type { InputRichMessage } from "@yaebal/types";
|
|
3
|
+
import { RichMessageDraft, type RichMessageDraftOptions } from "./draft.js";
|
|
4
|
+
/**
|
|
5
|
+
* `sendRichMessage` — standalone, no plugin install required (the `@yaebal/fmt` /
|
|
6
|
+
* `@yaebal/keyboard` "pure helper" style). `extra` covers the rest of
|
|
7
|
+
* `SendRichMessageParams`: `reply_markup`, `reply_parameters`,
|
|
8
|
+
* `message_effect_id`, `disable_notification`, `protect_content`,
|
|
9
|
+
* `business_connection_id`, `message_thread_id`, `direct_messages_topic_id`,
|
|
10
|
+
* `allow_paid_broadcast`, `suggested_post_parameters`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sendRichMessage(api: Api, chatId: number | string, input: InputRichMessage | string, extra?: Record<string, unknown>): Promise<Message>;
|
|
13
|
+
/**
|
|
14
|
+
* `sendRichMessageDraft` — a single ephemeral (30s-ttl) push. private chats
|
|
15
|
+
* only. most callers want `RichMessageDraft` (draft.ts) instead, which keeps the
|
|
16
|
+
* ttl alive across a stream and forces a final `sendRichMessage` commit.
|
|
17
|
+
*/
|
|
18
|
+
export declare function sendRichMessageDraft(api: Api, chatId: number, draftId: number, input: InputRichMessage | string, extra?: Record<string, unknown>): Promise<boolean>;
|
|
19
|
+
export interface RichContext {
|
|
20
|
+
/** `ctx.send`-flavored `sendRichMessage`, bound to the current chat. */
|
|
21
|
+
sendRichMessage(input: InputRichMessage | string, extra?: Record<string, unknown>): Promise<Message>;
|
|
22
|
+
/** open a `RichMessageDraft` streaming session bound to the current chat. */
|
|
23
|
+
richMessageDraft(draftId: number, options?: RichMessageDraftOptions): RichMessageDraft;
|
|
24
|
+
}
|
|
25
|
+
/** `bot.install(rich())` — adds `ctx.sendRichMessage` / `ctx.richMessageDraft`. */
|
|
26
|
+
export declare function rich(): Plugin<Context, RichContext>;
|
|
27
|
+
//# sourceMappingURL=send.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAM5E;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC9B,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,KAAK,EAAE,gBAAgB,GAAG,MAAM,EAChC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACjC,OAAO,CAAC,OAAO,CAAC,CAMlB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CACnC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,gBAAgB,GAAG,MAAM,EAChC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACjC,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,MAAM,WAAW,WAAW;IAC3B,wEAAwE;IACxE,eAAe,CACd,KAAK,EAAE,gBAAgB,GAAG,MAAM,EAChC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,6EAA6E;IAC7E,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,gBAAgB,CAAC;CACvF;AAED,mFAAmF;AACnF,wBAAgB,IAAI,IAAI,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,CAoBnD"}
|
package/lib/send.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { RichMessageDraft } from "./draft.js";
|
|
2
|
+
function toInput(input) {
|
|
3
|
+
return typeof input === "string" ? { html: input } : input;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* `sendRichMessage` — standalone, no plugin install required (the `@yaebal/fmt` /
|
|
7
|
+
* `@yaebal/keyboard` "pure helper" style). `extra` covers the rest of
|
|
8
|
+
* `SendRichMessageParams`: `reply_markup`, `reply_parameters`,
|
|
9
|
+
* `message_effect_id`, `disable_notification`, `protect_content`,
|
|
10
|
+
* `business_connection_id`, `message_thread_id`, `direct_messages_topic_id`,
|
|
11
|
+
* `allow_paid_broadcast`, `suggested_post_parameters`.
|
|
12
|
+
*/
|
|
13
|
+
export function sendRichMessage(api, chatId, input, extra = {}) {
|
|
14
|
+
return api.call("sendRichMessage", {
|
|
15
|
+
chat_id: chatId,
|
|
16
|
+
rich_message: toInput(input),
|
|
17
|
+
...extra,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* `sendRichMessageDraft` — a single ephemeral (30s-ttl) push. private chats
|
|
22
|
+
* only. most callers want `RichMessageDraft` (draft.ts) instead, which keeps the
|
|
23
|
+
* ttl alive across a stream and forces a final `sendRichMessage` commit.
|
|
24
|
+
*/
|
|
25
|
+
export function sendRichMessageDraft(api, chatId, draftId, input, extra = {}) {
|
|
26
|
+
return api.call("sendRichMessageDraft", {
|
|
27
|
+
chat_id: chatId,
|
|
28
|
+
draft_id: draftId,
|
|
29
|
+
rich_message: toInput(input),
|
|
30
|
+
...extra,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** `bot.install(rich())` — adds `ctx.sendRichMessage` / `ctx.richMessageDraft`. */
|
|
34
|
+
export function rich() {
|
|
35
|
+
return (composer) => composer.decorate({
|
|
36
|
+
sendRichMessage(input, extra = {}) {
|
|
37
|
+
const chatId = this.chat?.id;
|
|
38
|
+
if (chatId === undefined) {
|
|
39
|
+
return Promise.reject(new Error("sendRichMessage(): no chat in this update"));
|
|
40
|
+
}
|
|
41
|
+
return sendRichMessage(this.api, chatId, input, extra);
|
|
42
|
+
},
|
|
43
|
+
richMessageDraft(draftId, options) {
|
|
44
|
+
const chatId = this.chat?.id;
|
|
45
|
+
if (chatId === undefined) {
|
|
46
|
+
throw new Error("richMessageDraft(): no chat in this update");
|
|
47
|
+
}
|
|
48
|
+
return new RichMessageDraft(this.api, chatId, draftId, options);
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=send.js.map
|
package/lib/send.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send.js","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAgC,MAAM,YAAY,CAAC;AAE5E,SAAS,OAAO,CAAC,KAAgC;IAChD,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AAC5D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC9B,GAAQ,EACR,MAAuB,EACvB,KAAgC,EAChC,QAAiC,EAAE;IAEnC,OAAO,GAAG,CAAC,IAAI,CAAU,iBAAiB,EAAE;QAC3C,OAAO,EAAE,MAAM;QACf,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC;QAC5B,GAAG,KAAK;KACR,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CACnC,GAAQ,EACR,MAAc,EACd,OAAe,EACf,KAAgC,EAChC,QAAiC,EAAE;IAEnC,OAAO,GAAG,CAAC,IAAI,CAAU,sBAAsB,EAAE;QAChD,OAAO,EAAE,MAAM;QACf,QAAQ,EAAE,OAAO;QACjB,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC;QAC5B,GAAG,KAAK;KACR,CAAC,CAAC;AACJ,CAAC;AAYD,mFAAmF;AACnF,MAAM,UAAU,IAAI;IACnB,OAAO,CAAC,QAAQ,EAAE,EAAE,CACnB,QAAQ,CAAC,QAAQ,CAAc;QAC9B,eAAe,CAAgB,KAAK,EAAE,KAAK,GAAG,EAAE;YAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC7B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC1B,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC,CAAC;YAC/E,CAAC;YAED,OAAO,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QACxD,CAAC;QACD,gBAAgB,CAAgB,OAAO,EAAE,OAAO;YAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC7B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAC/D,CAAC;YAED,OAAO,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACjE,CAAC;KACD,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yaebal/rich",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "yaebal rich — sendRichMessage / sendRichMessageDraft: a typed block builder, a 30s-ttl draft/streaming session, and full read-side coverage of telegram's rich message model.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./lib/index.js",
|
|
7
|
+
"types": "./lib/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"import": "./lib/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"lib",
|
|
16
|
+
"src"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@yaebal/core": "0.0.7",
|
|
20
|
+
"@yaebal/types": "10.1.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "latest"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"telegram",
|
|
30
|
+
"telegram-bot",
|
|
31
|
+
"yaebal",
|
|
32
|
+
"rich-message",
|
|
33
|
+
"sendRichMessage",
|
|
34
|
+
"sendRichMessageDraft",
|
|
35
|
+
"streaming"
|
|
36
|
+
],
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/neverlane/yaebal",
|
|
41
|
+
"directory": "packages/rich"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc -p tsconfig.json",
|
|
48
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
49
|
+
"test": "node --test lib"
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/blocks.ts
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { escapeAttr, escapeText } from "./escape.js";
|
|
2
|
+
import { type Insertable, type RichNode, toHtml } from "./inline.js";
|
|
3
|
+
|
|
4
|
+
function join(children: Insertable[]): string {
|
|
5
|
+
return children.map(toHtml).join("");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** a caption + optional credit for a media/collage/slideshow/map block (`RichBlockCaption`). */
|
|
9
|
+
export interface Caption {
|
|
10
|
+
caption?: Insertable;
|
|
11
|
+
/** `RichBlockCaption.credit`, confirmed `<cite>`. */
|
|
12
|
+
credit?: Insertable;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function figcaption({ caption, credit }: Caption): string {
|
|
16
|
+
if (caption === undefined && credit === undefined) return "";
|
|
17
|
+
|
|
18
|
+
const creditHtml = credit === undefined ? "" : `<cite>${toHtml(credit)}</cite>`;
|
|
19
|
+
return `<figcaption>${toHtml(caption)}${creditHtml}</figcaption>`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// --- confirmed tags ---
|
|
23
|
+
|
|
24
|
+
/** `RichBlockParagraph`, `<p>`. */
|
|
25
|
+
export function paragraph(...children: Insertable[]): RichNode {
|
|
26
|
+
return { html: `<p>${join(children)}</p>` };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** `RichBlockSectionHeading`, `<h1>`–`<h6>`. `level` is telegram's `size` (1 largest, 6 smallest). */
|
|
30
|
+
export function heading(level: 1 | 2 | 3 | 4 | 5 | 6, ...children: Insertable[]): RichNode {
|
|
31
|
+
return { html: `<h${level}>${join(children)}</h${level}>` };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** `RichBlockPreformatted`, nested `<pre><code>`, matching classic parse_mode html. */
|
|
35
|
+
export function preformatted(text: string, language?: string): RichNode {
|
|
36
|
+
const cls = language ? ` class="language-${escapeAttr(language)}"` : "";
|
|
37
|
+
return { html: `<pre><code${cls}>${escapeText(text)}</code></pre>` };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** `RichBlockFooter`, `<footer>`. */
|
|
41
|
+
export function footer(...children: Insertable[]): RichNode {
|
|
42
|
+
return { html: `<footer>${join(children)}</footer>` };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** `RichBlockDivider`, `<hr/>`. */
|
|
46
|
+
export function divider(): RichNode {
|
|
47
|
+
return { html: "<hr/>" };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** `RichBlockMathematicalExpression`, confirmed `<tg-math-block>` (LaTeX). */
|
|
51
|
+
export function mathBlock(expression: string): RichNode {
|
|
52
|
+
return { html: `<tg-math-block>${escapeText(expression)}</tg-math-block>` };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** `RichBlockAnchor`, `<a name="…">` at block level. */
|
|
56
|
+
export function anchorBlock(name: string): RichNode {
|
|
57
|
+
return { html: `<a name="${escapeAttr(name)}"></a>` };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** `RichBlockBlockQuotation`, `<blockquote>`, with an optional `<cite>` credit. */
|
|
61
|
+
export function blockquote(children: Insertable[], credit?: Insertable): RichNode {
|
|
62
|
+
const creditHtml = credit === undefined ? "" : `<cite>${toHtml(credit)}</cite>`;
|
|
63
|
+
return { html: `<blockquote>${join(children)}${creditHtml}</blockquote>` };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** `RichBlockPullQuotation`, loosely `<aside>` per the schema, with an optional `<cite>` credit. */
|
|
67
|
+
export function pullquote(text: Insertable, credit?: Insertable): RichNode {
|
|
68
|
+
const creditHtml = credit === undefined ? "" : `<cite>${toHtml(credit)}</cite>`;
|
|
69
|
+
return { html: `<aside>${toHtml(text)}${creditHtml}</aside>` };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** `RichBlockCollage`, confirmed custom tag `<tg-collage>`. */
|
|
73
|
+
export function collage(blocks: Insertable[], caption: Caption = {}): RichNode {
|
|
74
|
+
return { html: `<tg-collage>${join(blocks)}${figcaption(caption)}</tg-collage>` };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** `RichBlockSlideshow`, confirmed custom tag `<tg-slideshow>`. */
|
|
78
|
+
export function slideshow(blocks: Insertable[], caption: Caption = {}): RichNode {
|
|
79
|
+
return { html: `<tg-slideshow>${join(blocks)}${figcaption(caption)}</tg-slideshow>` };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface TableCellOptions {
|
|
83
|
+
header?: boolean;
|
|
84
|
+
colspan?: number;
|
|
85
|
+
rowspan?: number;
|
|
86
|
+
/** default `"left"`, matching `RichBlockTableCell.align`. */
|
|
87
|
+
align?: "left" | "center" | "right";
|
|
88
|
+
/** default `"top"`, matching `RichBlockTableCell.valign`. */
|
|
89
|
+
valign?: "top" | "middle" | "bottom";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** one `<td>`/`<th>` cell; `content` omitted ⇒ an invisible cell (per `RichBlockTableCell`). */
|
|
93
|
+
export function cell(content?: Insertable, options: TableCellOptions = {}): RichNode {
|
|
94
|
+
const tag = options.header ? "th" : "td";
|
|
95
|
+
const attrs =
|
|
96
|
+
(options.colspan ? ` colspan="${options.colspan}"` : "") +
|
|
97
|
+
(options.rowspan ? ` rowspan="${options.rowspan}"` : "") +
|
|
98
|
+
// `align`/`valign` are telegram's own field names for this cell — the values
|
|
99
|
+
// (left/center/right, top/middle/bottom) match the classic html attributes exactly.
|
|
100
|
+
(options.align ? ` align="${options.align}"` : "") +
|
|
101
|
+
(options.valign ? ` valign="${options.valign}"` : "");
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
html:
|
|
105
|
+
content === undefined
|
|
106
|
+
? `<${tag}${attrs}></${tag}>`
|
|
107
|
+
: `<${tag}${attrs}>${toHtml(content)}</${tag}>`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface TableOptions {
|
|
112
|
+
/** best-effort: rendered as the `border` attribute. */
|
|
113
|
+
bordered?: boolean;
|
|
114
|
+
/** best-effort: no standard html equivalent, rendered as `data-striped`. */
|
|
115
|
+
striped?: boolean;
|
|
116
|
+
caption?: Insertable;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** `RichBlockTable`, confirmed `<table>`. build rows with `cell()`. */
|
|
120
|
+
export function table(rows: Insertable[][], options: TableOptions = {}): RichNode {
|
|
121
|
+
const attrs = (options.bordered ? " border" : "") + (options.striped ? " data-striped" : "");
|
|
122
|
+
const body = rows.map((row) => `<tr>${join(row)}</tr>`).join("");
|
|
123
|
+
const captionHtml =
|
|
124
|
+
options.caption === undefined ? "" : `<caption>${toHtml(options.caption)}</caption>`;
|
|
125
|
+
|
|
126
|
+
return { html: `<table${attrs}>${captionHtml}${body}</table>` };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface DetailsOptions {
|
|
130
|
+
/** `RichBlockDetails.is_open` — content visible by default. defaults to `false`. */
|
|
131
|
+
open?: boolean;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** `RichBlockDetails`, confirmed `<details>`/`<summary>`. */
|
|
135
|
+
export function details(
|
|
136
|
+
summary: Insertable,
|
|
137
|
+
blocks: Insertable[],
|
|
138
|
+
options: DetailsOptions = {},
|
|
139
|
+
): RichNode {
|
|
140
|
+
return {
|
|
141
|
+
html: `<details${options.open ? " open" : ""}><summary>${toHtml(summary)}</summary>${join(blocks)}</details>`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface ListItemOptions {
|
|
146
|
+
/** an unchecked/checked checkbox prefix (`RichBlockListItem.has_checkbox`/`is_checked`). */
|
|
147
|
+
checkbox?: boolean;
|
|
148
|
+
checked?: boolean;
|
|
149
|
+
/** ordered-list numeric override (`RichBlockListItem.value`) — standard `<li value>`. */
|
|
150
|
+
value?: number;
|
|
151
|
+
/** ordered-list label style override (`RichBlockListItem.type`) — standard `<li type>`. */
|
|
152
|
+
type?: "a" | "A" | "i" | "I";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** one `<li>` for `list()`. */
|
|
156
|
+
export function item(blocks: Insertable[], options: ListItemOptions = {}): RichNode {
|
|
157
|
+
const attrs =
|
|
158
|
+
(options.value !== undefined ? ` value="${options.value}"` : "") +
|
|
159
|
+
(options.type ? ` type="${options.type}"` : "");
|
|
160
|
+
|
|
161
|
+
const checkboxHtml =
|
|
162
|
+
options.checkbox === undefined
|
|
163
|
+
? ""
|
|
164
|
+
: `<input type="checkbox"${options.checked ? " checked" : ""}/> `;
|
|
165
|
+
|
|
166
|
+
return { html: `<li${attrs}>${checkboxHtml}${join(blocks)}</li>` };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface ListOptions {
|
|
170
|
+
/** `<ol>` instead of `<ul>`. defaults to `false`. */
|
|
171
|
+
ordered?: boolean;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** `RichBlockList`, `<ul>`/`<ol>` of `item()`s. */
|
|
175
|
+
export function list(items: Insertable[], options: ListOptions = {}): RichNode {
|
|
176
|
+
const tag = options.ordered ? "ol" : "ul";
|
|
177
|
+
return { html: `<${tag}>${join(items)}</${tag}>` };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface MapOptions {
|
|
181
|
+
/** 13–20, per `RichBlockMap.zoom`. */
|
|
182
|
+
zoom: number;
|
|
183
|
+
width: number;
|
|
184
|
+
height: number;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* `RichBlockMap`, confirmed custom tag `<tg-map>` — attribute names/encoding are a
|
|
189
|
+
* best-effort guess (lat/long/zoom/width/height are telegram's own field names).
|
|
190
|
+
*/
|
|
191
|
+
export function map(
|
|
192
|
+
location: { latitude: number; longitude: number },
|
|
193
|
+
options: MapOptions,
|
|
194
|
+
caption: Caption = {},
|
|
195
|
+
): RichNode {
|
|
196
|
+
const attrs =
|
|
197
|
+
` latitude="${location.latitude}" longitude="${location.longitude}"` +
|
|
198
|
+
` zoom="${options.zoom}" width="${options.width}" height="${options.height}"`;
|
|
199
|
+
|
|
200
|
+
return { html: `<tg-map${attrs}>${figcaption(caption)}</tg-map>` };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface MediaOptions extends Caption {
|
|
204
|
+
/** cover the media with a spoiler animation until tapped. */
|
|
205
|
+
spoiler?: boolean;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function figure(tag: string, src: string, options: MediaOptions): RichNode {
|
|
209
|
+
const spoilerAttr = options.spoiler ? " data-media-spoiler" : "";
|
|
210
|
+
const media = `<${tag} src="${escapeAttr(src)}"${spoilerAttr}></${tag}>`;
|
|
211
|
+
const cap = figcaption(options);
|
|
212
|
+
|
|
213
|
+
return { html: cap ? `<figure>${media}${cap}</figure>` : media };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* `RichBlockPhoto`, confirmed `<img>`. media is referenced by url — `sendRichMessage`
|
|
218
|
+
* has no multipart/`attach://` upload path (unlike `sendPhoto`), so there is no
|
|
219
|
+
* `MediaSource` overload here; host the file and pass its url.
|
|
220
|
+
*/
|
|
221
|
+
export function image(src: string, options: MediaOptions = {}): RichNode {
|
|
222
|
+
return figure("img", src, options);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* `RichBlockVideo` **and** `RichBlockAnimation` both map to `<video>` per the
|
|
227
|
+
* schema (telegram tells the two apart server-side, e.g. by looping/silence —
|
|
228
|
+
* not by tag). there is no separate `animation()` builder for that reason.
|
|
229
|
+
*/
|
|
230
|
+
export function video(src: string, options: MediaOptions = {}): RichNode {
|
|
231
|
+
return figure("video", src, options);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* `RichBlockAudio` **and** `RichBlockVoiceNote` both map to `<audio>` per the
|
|
236
|
+
* schema (again disambiguated server-side, not by tag) — there is no separate
|
|
237
|
+
* `voiceNote()` builder for that reason.
|
|
238
|
+
*/
|
|
239
|
+
export function audio(src: string, options: MediaOptions = {}): RichNode {
|
|
240
|
+
return figure("audio", src, options);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* `RichBlockThinking`, confirmed custom tag `<tg-thinking>`. **draft-only** — the
|
|
245
|
+
* schema states it can be used in `sendRichMessageDraft` payloads but is never
|
|
246
|
+
* received back on a real message. see `RichMessageDraft` in draft.ts.
|
|
247
|
+
*/
|
|
248
|
+
export function thinking(...children: Insertable[]): RichNode {
|
|
249
|
+
return { html: `<tg-thinking>${join(children)}</tg-thinking>` };
|
|
250
|
+
}
|