@mks2508/telegram-message-builder 0.2.0 → 0.3.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.
Files changed (57) hide show
  1. package/dist/builder/builder.d.ts +87 -0
  2. package/dist/builder/index.d.ts +2 -0
  3. package/dist/builder/media.d.ts +351 -0
  4. package/dist/formatters/index.d.ts +86 -0
  5. package/dist/formatters/markdown.d.ts +178 -0
  6. package/dist/formatters/markdownv2.d.ts +183 -0
  7. package/dist/index.cjs +1057 -12
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +11 -0
  10. package/dist/index.js +1022 -13
  11. package/dist/index.js.map +1 -1
  12. package/dist/keyboard/index.d.ts +113 -0
  13. package/dist/types/constants.d.ts +13 -0
  14. package/dist/types/core.types.d.ts +74 -0
  15. package/dist/types/index.d.ts +4 -0
  16. package/dist/types/keyboard-types.index.d.ts +1 -0
  17. package/dist/types/keyboard.types.d.ts +95 -0
  18. package/dist/types/main.types.d.ts +22 -0
  19. package/dist/types/media.types.d.ts +157 -0
  20. package/package.json +1 -1
  21. package/src/builder/builder.d.ts +55 -0
  22. package/src/builder/builder.d.ts.map +1 -1
  23. package/src/builder/builder.ts +145 -10
  24. package/src/builder/index.d.ts +2 -1
  25. package/src/builder/index.d.ts.map +1 -1
  26. package/src/builder/index.ts +2 -1
  27. package/src/builder/media.d.ts +352 -0
  28. package/src/builder/media.d.ts.map +1 -0
  29. package/src/builder/media.test.ts +664 -0
  30. package/src/builder/media.ts +484 -0
  31. package/src/builder.test.ts +465 -0
  32. package/src/escaping.test.ts +2 -2
  33. package/src/formatters/index.d.ts +47 -0
  34. package/src/formatters/index.d.ts.map +1 -1
  35. package/src/formatters/index.ts +92 -1
  36. package/src/formatters/markdown.d.ts +179 -0
  37. package/src/formatters/markdown.d.ts.map +1 -0
  38. package/src/formatters/markdown.test.ts +417 -0
  39. package/src/formatters/markdown.ts +220 -0
  40. package/src/formatters/markdownv2.d.ts +184 -0
  41. package/src/formatters/markdownv2.d.ts.map +1 -0
  42. package/src/formatters/markdownv2.ts +235 -0
  43. package/src/formatters.test.ts +17 -7
  44. package/src/index.d.ts +2 -0
  45. package/src/index.d.ts.map +1 -1
  46. package/src/index.ts +12 -0
  47. package/src/integration.test.ts +523 -0
  48. package/src/media-integration.test.ts +384 -0
  49. package/src/types/index.d.ts +1 -0
  50. package/src/types/index.d.ts.map +1 -1
  51. package/src/types/index.ts +1 -0
  52. package/src/types/media.types.d.ts +158 -0
  53. package/src/types/media.types.d.ts.map +1 -0
  54. package/src/types/media.types.ts +178 -0
  55. package/src/types.test.ts +539 -0
  56. package/src/utils/index.d.ts +1 -1
  57. package/src/utils/index.ts +0 -5
@@ -0,0 +1,465 @@
1
+ import { describe, it, expect } from "bun:test";
2
+ import { TelegramMessageBuilder } from "./builder";
3
+
4
+ describe("TelegramMessageBuilder", () => {
5
+ describe("Initialization", () => {
6
+ it("should create builder with HTML default", () => {
7
+ const builder = TelegramMessageBuilder.text();
8
+ expect(builder.getParseMode()).toBe("html");
9
+ });
10
+
11
+ it("should allow setting parse mode to markdown", () => {
12
+ const builder = TelegramMessageBuilder.text().setParseMode("markdown");
13
+ expect(builder.getParseMode()).toBe("markdown");
14
+ });
15
+
16
+ it("should allow setting parse mode to markdownv2", () => {
17
+ const builder = TelegramMessageBuilder.text().setParseMode("markdownv2");
18
+ expect(builder.getParseMode()).toBe("markdownv2");
19
+ });
20
+ });
21
+
22
+ describe("Formatting Methods - HTML Mode", () => {
23
+ it("should format title with bold", () => {
24
+ const message = TelegramMessageBuilder.text().title("Test Title").build();
25
+ expect(message.text).toContain("<b>Test Title</b>");
26
+ });
27
+
28
+ it("should format section with underline", () => {
29
+ const message = TelegramMessageBuilder.text()
30
+ .section("Test Section")
31
+ .build();
32
+ expect(message.text).toContain("<u>Test Section</u>");
33
+ });
34
+
35
+ it("should format line with key and value", () => {
36
+ const message = TelegramMessageBuilder.text()
37
+ .line("Status", "Active")
38
+ .build();
39
+ expect(message.text).toContain("Status: Active");
40
+ });
41
+
42
+ it("should format line with bold option", () => {
43
+ const message = TelegramMessageBuilder.text()
44
+ .line("Status", "Active", { bold: true })
45
+ .build();
46
+ expect(message.text).toContain("Status: <b>Active</b>");
47
+ });
48
+
49
+ it("should format line with italic option", () => {
50
+ const message = TelegramMessageBuilder.text()
51
+ .line("Status", "Active", { italic: true })
52
+ .build();
53
+ expect(message.text).toContain("Status: <i>Active</i>");
54
+ });
55
+
56
+ it("should format line with code option", () => {
57
+ const message = TelegramMessageBuilder.text()
58
+ .line("Command", "npm install", { code: true })
59
+ .build();
60
+ expect(message.text).toContain("Command: <code>npm install</code>");
61
+ });
62
+
63
+ it("should format line with underline option", () => {
64
+ const message = TelegramMessageBuilder.text()
65
+ .line("Note", "Important", { underline: true })
66
+ .build();
67
+ expect(message.text).toContain("Note: <u>Important</u>");
68
+ });
69
+
70
+ it("should format code block", () => {
71
+ const message = TelegramMessageBuilder.text()
72
+ .codeBlock("const x = 1;")
73
+ .build();
74
+ expect(message.text).toContain("<pre><code>const x = 1;</code></pre>");
75
+ });
76
+
77
+ it("should format code block with language", () => {
78
+ const message = TelegramMessageBuilder.text()
79
+ .codeBlock("const x = 1;", "javascript")
80
+ .build();
81
+ expect(message.text).toContain(
82
+ '<pre><code class="language-javascript">const x = 1;</code></pre>',
83
+ );
84
+ });
85
+
86
+ it("should format list item", () => {
87
+ const message = TelegramMessageBuilder.text().listItem("Item 1").build();
88
+ expect(message.text).toContain("• Item 1");
89
+ });
90
+
91
+ it("should add separator", () => {
92
+ const message = TelegramMessageBuilder.text().separator().build();
93
+ expect(message.text).toContain("---");
94
+ });
95
+
96
+ it("should add raw text with escaping", () => {
97
+ const message = TelegramMessageBuilder.text()
98
+ .text("<b>Hello</b>")
99
+ .build();
100
+ expect(message.text).toContain("&lt;b&gt;Hello&lt;/b&gt;");
101
+ });
102
+
103
+ it("should format link", () => {
104
+ const message = TelegramMessageBuilder.text()
105
+ .link("Click here", "https://example.com")
106
+ .build();
107
+ expect(message.text).toContain(
108
+ '<a href="https://example.com">Click here</a>',
109
+ );
110
+ });
111
+
112
+ it("should format mention with default name", () => {
113
+ const message = TelegramMessageBuilder.text().mention(123456).build();
114
+ expect(message.text).toContain(
115
+ '<a href="tg://user?id=123456">user123456</a>',
116
+ );
117
+ });
118
+
119
+ it("should format mention with custom name", () => {
120
+ const message = TelegramMessageBuilder.text()
121
+ .mention(123456, "John Doe")
122
+ .build();
123
+ expect(message.text).toContain(
124
+ '<a href="tg://user?id=123456">John Doe</a>',
125
+ );
126
+ });
127
+
128
+ it("should format hashtag", () => {
129
+ const message = TelegramMessageBuilder.text().hashtag("test").build();
130
+ expect(message.text).toContain(
131
+ '<a href="tg://hashtag?tag=test">#test</a>',
132
+ );
133
+ });
134
+ });
135
+
136
+ describe("Formatting Methods - Markdown Mode", () => {
137
+ it("should format title with bold markdown", () => {
138
+ const message = TelegramMessageBuilder.text()
139
+ .setParseMode("markdown")
140
+ .title("Test Title")
141
+ .build();
142
+ expect(message.text).toContain("*Test Title*");
143
+ });
144
+
145
+ it("should format section with underline markdown (HTML fallback)", () => {
146
+ const message = TelegramMessageBuilder.text()
147
+ .setParseMode("markdown")
148
+ .section("Test Section")
149
+ .build();
150
+ expect(message.text).toContain("<u>Test Section</u>");
151
+ });
152
+
153
+ it("should format line with bold option markdown", () => {
154
+ const message = TelegramMessageBuilder.text()
155
+ .setParseMode("markdown")
156
+ .line("Status", "Active", { bold: true })
157
+ .build();
158
+ expect(message.text).toContain("Status: *Active*");
159
+ });
160
+
161
+ it("should format code block markdown", () => {
162
+ const message = TelegramMessageBuilder.text()
163
+ .setParseMode("markdown")
164
+ .codeBlock("const x = 1;")
165
+ .build();
166
+ expect(message.text).toContain("const x = 1;\n");
167
+ });
168
+
169
+ it("should format code block with language markdown", () => {
170
+ const message = TelegramMessageBuilder.text()
171
+ .setParseMode("markdown")
172
+ .codeBlock("const x = 1;", "javascript")
173
+ .build();
174
+ expect(message.text).toContain("javascript\nconst x = 1;\n");
175
+ });
176
+
177
+ it("should format link markdown", () => {
178
+ const message = TelegramMessageBuilder.text()
179
+ .setParseMode("markdown")
180
+ .link("Click here", "https://example.com")
181
+ .build();
182
+ expect(message.text).toContain("[Click here](https://example.com)");
183
+ });
184
+
185
+ it("should format mention markdown", () => {
186
+ const message = TelegramMessageBuilder.text()
187
+ .setParseMode("markdown")
188
+ .mention(123456, "John")
189
+ .build();
190
+ expect(message.text).toContain("tg://user?id=123456");
191
+ });
192
+
193
+ it("should format hashtag markdown", () => {
194
+ const message = TelegramMessageBuilder.text()
195
+ .setParseMode("markdown")
196
+ .hashtag("test")
197
+ .build();
198
+ expect(message.text).toContain("#test");
199
+ });
200
+ });
201
+
202
+ describe("Formatting Methods - MarkdownV2 Mode", () => {
203
+ it("should format title with bold markdownv2", () => {
204
+ const message = TelegramMessageBuilder.text()
205
+ .setParseMode("markdownv2")
206
+ .title("Test Title")
207
+ .build();
208
+ expect(message.text).toContain("*Test Title*");
209
+ });
210
+
211
+ it("should format section with underline markdownv2", () => {
212
+ const message = TelegramMessageBuilder.text()
213
+ .setParseMode("markdownv2")
214
+ .section("Test Section")
215
+ .build();
216
+ expect(message.text).toContain("__Test Section__");
217
+ });
218
+
219
+ it("should format line with bold option markdownv2", () => {
220
+ const message = TelegramMessageBuilder.text()
221
+ .setParseMode("markdownv2")
222
+ .line("Status", "Active", { bold: true })
223
+ .build();
224
+ expect(message.text).toContain("Status: *Active*");
225
+ });
226
+
227
+ it("should format line with italic option markdownv2", () => {
228
+ const message = TelegramMessageBuilder.text()
229
+ .setParseMode("markdownv2")
230
+ .line("Status", "Active", { italic: true })
231
+ .build();
232
+ expect(message.text).toContain("Status: _Active_");
233
+ });
234
+
235
+ it("should format code block markdownv2", () => {
236
+ const message = TelegramMessageBuilder.text()
237
+ .setParseMode("markdownv2")
238
+ .codeBlock("const x = 1;")
239
+ .build();
240
+ expect(message.text).toContain("const x = 1;\n");
241
+ });
242
+
243
+ it("should format link markdownv2", () => {
244
+ const message = TelegramMessageBuilder.text()
245
+ .setParseMode("markdownv2")
246
+ .link("Click here", "https://example.com")
247
+ .build();
248
+ expect(message.text).toContain("[Click here](https://example.com)");
249
+ });
250
+
251
+ it("should format mention markdownv2", () => {
252
+ const message = TelegramMessageBuilder.text()
253
+ .setParseMode("markdownv2")
254
+ .mention(123456, "John")
255
+ .build();
256
+ expect(message.text).toContain("[John](tg://user?id=123456)");
257
+ });
258
+
259
+ it("should format hashtag markdownv2", () => {
260
+ const message = TelegramMessageBuilder.text()
261
+ .setParseMode("markdownv2")
262
+ .hashtag("test")
263
+ .build();
264
+ expect(message.text).toContain("#test");
265
+ });
266
+ });
267
+
268
+ describe("Layout Methods", () => {
269
+ it("should add single newline", () => {
270
+ const message = TelegramMessageBuilder.text()
271
+ .text("Line 1")
272
+ .newline()
273
+ .text("Line 2")
274
+ .build();
275
+ // Each newline adds "\n\n" to parts, and parts are joined with "\n"
276
+ // Result: "Line 1" + "\n" (join) + "\n\n" (newline) + "\n" (join) + "Line 2"
277
+ // = "Line 1\n\n\n\nLine 2"
278
+ expect(message.text).toContain("Line 1");
279
+ expect(message.text).toContain("Line 2");
280
+ expect(message.text).toMatch(/Line 1\n\n\n\nLine 2/);
281
+ });
282
+
283
+ it("should add multiple newlines", () => {
284
+ const message = TelegramMessageBuilder.text()
285
+ .text("Line 1")
286
+ .newline(3)
287
+ .text("Line 2")
288
+ .build();
289
+ // 3 newlines = each adds "\n\n", joined with "\n"
290
+ expect(message.text).toContain("Line 1");
291
+ expect(message.text).toContain("Line 2");
292
+ });
293
+
294
+ it("should add separator between content", () => {
295
+ const message = TelegramMessageBuilder.text()
296
+ .text("Before")
297
+ .separator()
298
+ .text("After")
299
+ .build();
300
+ expect(message.text).toContain("Before\n---\nAfter");
301
+ });
302
+ });
303
+
304
+ describe("Method Chaining", () => {
305
+ it("should support fluent API chaining", () => {
306
+ const message = TelegramMessageBuilder.text()
307
+ .title("Welcome")
308
+ .newline()
309
+ .section("User Info")
310
+ .line("Name", "John", { bold: true })
311
+ .line("Email", "john@example.com")
312
+ .separator()
313
+ .codeBlock("console.log('Hello');", "javascript")
314
+ .build();
315
+
316
+ expect(message.text).toContain("<b>Welcome</b>");
317
+ expect(message.text).toContain("Name: <b>John</b>");
318
+ expect(message.text).toContain("Email: john@example.com");
319
+ expect(message.text).toContain("---");
320
+ expect(message.text).toContain("language-javascript");
321
+ expect(message.parse_mode).toBe("html");
322
+ });
323
+
324
+ it("should work with markdown mode chaining", () => {
325
+ const message = TelegramMessageBuilder.text()
326
+ .setParseMode("markdown")
327
+ .title("Welcome")
328
+ .line("Status", "Active", { bold: true })
329
+ .link("Click", "https://example.com")
330
+ .build();
331
+
332
+ expect(message.text).toContain("*Welcome*");
333
+ expect(message.text).toContain("Status: *Active*");
334
+ expect(message.text).toContain("[Click](https://example.com)");
335
+ expect(message.parse_mode).toBe("markdown");
336
+ });
337
+ });
338
+
339
+ describe("Build Options", () => {
340
+ it("should build with default HTML parse mode", () => {
341
+ const message = TelegramMessageBuilder.text().title("Test").build();
342
+
343
+ expect(message.parse_mode).toBe("html");
344
+ expect(message.text).toContain("<b>Test</b>");
345
+ });
346
+
347
+ it("should build with custom option", () => {
348
+ const message = TelegramMessageBuilder.text()
349
+ .setOption("disable_web_page_preview", true)
350
+ .title("Test")
351
+ .build();
352
+
353
+ expect(message.disable_web_page_preview).toBe(true);
354
+ });
355
+
356
+ it("should build with multiple custom options", () => {
357
+ const message = TelegramMessageBuilder.text()
358
+ .setOptions({
359
+ disable_web_page_preview: true,
360
+ protect_content: true,
361
+ })
362
+ .title("Test")
363
+ .build();
364
+
365
+ expect(message.disable_web_page_preview).toBe(true);
366
+ expect(message.protect_content).toBe(true);
367
+ });
368
+
369
+ it("should return TelegramMessage interface structure", () => {
370
+ const message = TelegramMessageBuilder.text().title("Test").build();
371
+
372
+ expect(message).toHaveProperty("text");
373
+ expect(message).toHaveProperty("parse_mode");
374
+ expect(typeof message.text).toBe("string");
375
+ expect(typeof message.parse_mode).toBe("string");
376
+ });
377
+ });
378
+
379
+ describe("Complex Messages", () => {
380
+ it("should build complex message with all features", () => {
381
+ const message = TelegramMessageBuilder.text()
382
+ .title("User Profile")
383
+ .newline()
384
+ .section("Personal Information")
385
+ .line("Name", "John Doe", { bold: true })
386
+ .line("Age", "30", { code: true })
387
+ .line("Status", "Active", { italic: true })
388
+ .newline()
389
+ .section("Contact")
390
+ .line("Email", "john@example.com")
391
+ .line("Telegram", "@johndoe")
392
+ .separator()
393
+ .codeBlock("const user = { name: 'John', age: 30 };", "javascript")
394
+ .newline()
395
+ .listItem("Feature 1")
396
+ .listItem("Feature 2")
397
+ .listItem("Feature 3")
398
+ .newline()
399
+ .link("View Profile", "https://example.com/profile")
400
+ .mention(123456, "John Doe")
401
+ .hashtag("profile")
402
+ .setOption("disable_notification", true)
403
+ .build();
404
+
405
+ expect(message.text).toContain("<b>User Profile</b>");
406
+ expect(message.text).toContain("<u>Personal Information</u>");
407
+ expect(message.text).toContain("Name: <b>John Doe</b>");
408
+ expect(message.text).toContain("Age: <code>30</code>");
409
+ expect(message.text).toContain("Status: <i>Active</i>");
410
+ expect(message.text).toContain("---");
411
+ expect(message.text).toContain("language-javascript");
412
+ expect(message.text).toContain("• Feature 1");
413
+ expect(message.text).toContain("View Profile");
414
+ expect(message.text).toContain("tg://user?id=123456");
415
+ expect(message.text).toContain("#profile");
416
+ expect(message.disable_notification).toBe(true);
417
+ });
418
+
419
+ it("should handle empty message", () => {
420
+ const message = TelegramMessageBuilder.text().build();
421
+ expect(message.text).toBe("");
422
+ });
423
+
424
+ it("should escape HTML special characters in text", () => {
425
+ const message = TelegramMessageBuilder.text()
426
+ .text("<script>alert('xss')</script>")
427
+ .build();
428
+ expect(message.text).toContain("&lt;script&gt;");
429
+ expect(message.text).not.toContain("<script>");
430
+ });
431
+ });
432
+
433
+ describe("Cross-Mode Consistency", () => {
434
+ it("should build same message in HTML, Markdown, and MarkdownV2", () => {
435
+ const html = TelegramMessageBuilder.text()
436
+ .title("Test")
437
+ .line("Status", "Active", { bold: true })
438
+ .build();
439
+
440
+ const markdown = TelegramMessageBuilder.text()
441
+ .setParseMode("markdown")
442
+ .title("Test")
443
+ .line("Status", "Active", { bold: true })
444
+ .build();
445
+
446
+ const markdownv2 = TelegramMessageBuilder.text()
447
+ .setParseMode("markdownv2")
448
+ .title("Test")
449
+ .line("Status", "Active", { bold: true })
450
+ .build();
451
+
452
+ expect(html.text).toContain("<b>Test</b>");
453
+ expect(html.text).toContain("Status: <b>Active</b>");
454
+ expect(html.parse_mode).toBe("html");
455
+
456
+ expect(markdown.text).toContain("*Test*");
457
+ expect(markdown.text).toContain("Status: *Active*");
458
+ expect(markdown.parse_mode).toBe("markdown");
459
+
460
+ expect(markdownv2.text).toContain("*Test*");
461
+ expect(markdownv2.text).toContain("Status: *Active*");
462
+ expect(markdownv2.parse_mode).toBe("markdownv2");
463
+ });
464
+ });
465
+ });
@@ -68,7 +68,7 @@ describe("Escaping", () => {
68
68
  const reserved = "_*[]()~`>#+-=|{}.!";
69
69
  const result = fmt.escapeMarkdownV2(reserved);
70
70
  expect(result).toBe(
71
- "\\_\\*\\[\\]\\]\\)\\~\\`\\>\\#\\+\\-\\=\\|\\{\\}\\}.\\!",
71
+ "\\_\\*\\[\\]\\(\\)\\~\\`\\>\\#\\+\\-\\=\\|\\{\\}\\.\\!",
72
72
  );
73
73
  });
74
74
 
@@ -122,7 +122,7 @@ describe("Escaping", () => {
122
122
  it("should select MarkdownV2 mode", () => {
123
123
  const text = "[]()~`>#+-=|{}.!";
124
124
  const result = fmt.escape(text, "markdownv2");
125
- expect(result).toBe("\\[\\]\\)\\~\\`\\>\\#\\+\\-\\=\\|\\{\\}\\}.\\!");
125
+ expect(result).toBe("\\[\\]\\(\\)\\~\\`\\>\\#\\+\\-\\=\\|\\{\\}\\.\\!");
126
126
  });
127
127
 
128
128
  it("should default to HTML mode", () => {
@@ -1,4 +1,8 @@
1
1
  import type { ParseMode } from "../types";
2
+ import { boldMD, italicMD, codeMD, codeBlockMD, linkMD, mentionMD, hashtagMD, underlineMD, strikethroughMD, spoilerMD, emailMD, urlMD, customEmojiMD, rawMD } from "./markdown";
3
+ import { boldMDv2, italicMDv2, underlineMDv2, strikethroughMDv2, spoilerMDv2, codeMDv2, codeBlockMDv2, linkMDv2, mentionMDv2, hashtagMDv2, emailMDv2, urlMDv2, customEmojiMDv2, rawMDv2, escapeMDv2 } from "./markdownv2";
4
+ export * from "./markdown";
5
+ export * from "./markdownv2";
2
6
  export declare function escapeHTML(text: string): string;
3
7
  export declare function escapeMarkdown(text: string): string;
4
8
  export declare function escapeMarkdownV2(text: string): string;
@@ -16,6 +20,19 @@ export declare function hashtag(tag: string): string;
16
20
  export declare function customEmoji(emojiId: string): string;
17
21
  export declare function email(email: string): string;
18
22
  export declare function url(link: string): string;
23
+ /**
24
+ * Raw HTML string - bypasses escaping for combining pre-formatted content
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * // Instead of this (which escapes inner tags):
29
+ * fmt.bold(fmt.italic("Text")) // <b>&lt;i&gt;Text&lt;/i&gt;</b>
30
+ *
31
+ * // Use this:
32
+ * fmt.bold(fmt.raw(fmt.italic("Text"))) // <b><i>Text</i></b>
33
+ * ```
34
+ */
35
+ export declare function raw(html: string): string;
19
36
  export declare function escape(text: string, mode?: ParseMode): string;
20
37
  export declare const fmt: {
21
38
  bold: typeof bold;
@@ -36,5 +53,35 @@ export declare const fmt: {
36
53
  escapeHTML: typeof escapeHTML;
37
54
  escapeMarkdown: typeof escapeMarkdown;
38
55
  escapeMarkdownV2: typeof escapeMarkdownV2;
56
+ raw: typeof raw;
57
+ boldMD: typeof boldMD;
58
+ italicMD: typeof italicMD;
59
+ codeMD: typeof codeMD;
60
+ codeBlockMD: typeof codeBlockMD;
61
+ linkMD: typeof linkMD;
62
+ mentionMD: typeof mentionMD;
63
+ hashtagMD: typeof hashtagMD;
64
+ underlineMD: typeof underlineMD;
65
+ strikethroughMD: typeof strikethroughMD;
66
+ spoilerMD: typeof spoilerMD;
67
+ emailMD: typeof emailMD;
68
+ urlMD: typeof urlMD;
69
+ customEmojiMD: typeof customEmojiMD;
70
+ rawMD: typeof rawMD;
71
+ boldMDv2: typeof boldMDv2;
72
+ italicMDv2: typeof italicMDv2;
73
+ underlineMDv2: typeof underlineMDv2;
74
+ strikethroughMDv2: typeof strikethroughMDv2;
75
+ spoilerMDv2: typeof spoilerMDv2;
76
+ codeMDv2: typeof codeMDv2;
77
+ codeBlockMDv2: typeof codeBlockMDv2;
78
+ linkMDv2: typeof linkMDv2;
79
+ mentionMDv2: typeof mentionMDv2;
80
+ hashtagMDv2: typeof hashtagMDv2;
81
+ emailMDv2: typeof emailMDv2;
82
+ urlMDv2: typeof urlMDv2;
83
+ customEmojiMDv2: typeof customEmojiMDv2;
84
+ rawMDv2: typeof rawMDv2;
85
+ escapeMDv2: typeof escapeMDv2;
39
86
  };
40
87
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM/C;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMnD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOrD;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAIjE;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,SAAkB,GAAG,MAAM,CASrE;AAED,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;CAmBf,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAG1C,OAAO,EACL,MAAM,EACN,QAAQ,EACR,MAAM,EACN,WAAW,EACX,MAAM,EACN,SAAS,EACT,SAAS,EACT,WAAW,EACX,eAAe,EACf,SAAS,EACT,OAAO,EACP,KAAK,EACL,aAAa,EACb,KAAK,EACN,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,QAAQ,EACR,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,WAAW,EACX,WAAW,EACX,SAAS,EACT,OAAO,EACP,eAAe,EACf,OAAO,EACP,UAAU,EACX,MAAM,cAAc,CAAC;AAGtB,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAE7B,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM/C;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMnD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOrD;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAIjE;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,SAAkB,GAAG,MAAM,CASrE;AAED,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsDf,CAAC"}
@@ -1,5 +1,45 @@
1
1
  import type { ParseMode } from "../types";
2
2
 
3
+ // Import Markdown and MarkdownV2 formatters for use in fmt object
4
+ import {
5
+ boldMD,
6
+ italicMD,
7
+ codeMD,
8
+ codeBlockMD,
9
+ linkMD,
10
+ mentionMD,
11
+ hashtagMD,
12
+ underlineMD,
13
+ strikethroughMD,
14
+ spoilerMD,
15
+ emailMD,
16
+ urlMD,
17
+ customEmojiMD,
18
+ rawMD,
19
+ } from "./markdown";
20
+
21
+ import {
22
+ boldMDv2,
23
+ italicMDv2,
24
+ underlineMDv2,
25
+ strikethroughMDv2,
26
+ spoilerMDv2,
27
+ codeMDv2,
28
+ codeBlockMDv2,
29
+ linkMDv2,
30
+ mentionMDv2,
31
+ hashtagMDv2,
32
+ emailMDv2,
33
+ urlMDv2,
34
+ customEmojiMDv2,
35
+ rawMDv2,
36
+ escapeMDv2,
37
+ } from "./markdownv2";
38
+
39
+ // Re-export all formatters for external use
40
+ export * from "./markdown";
41
+ export * from "./markdownv2";
42
+
3
43
  export function escapeHTML(text: string): string {
4
44
  return text
5
45
  .replace(/&/g, "&amp;")
@@ -56,7 +96,7 @@ export function pre(text: string): string {
56
96
  export function codeBlock(text: string, language?: string): string {
57
97
  return language
58
98
  ? `<pre><code class="language-${language}">${escapeHTML(text)}</code></pre>`
59
- : `<pre>${escapeHTML(text)}</pre>`;
99
+ : `<pre><code>${escapeHTML(text)}</code></pre>`;
60
100
  }
61
101
 
62
102
  export function link(text: string, url: string): string {
@@ -84,6 +124,22 @@ export function url(link: string): string {
84
124
  return `<a href="${escapeHTML(link)}">${escapeHTML(link)}</a>`;
85
125
  }
86
126
 
127
+ /**
128
+ * Raw HTML string - bypasses escaping for combining pre-formatted content
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * // Instead of this (which escapes inner tags):
133
+ * fmt.bold(fmt.italic("Text")) // <b>&lt;i&gt;Text&lt;/i&gt;</b>
134
+ *
135
+ * // Use this:
136
+ * fmt.bold(fmt.raw(fmt.italic("Text"))) // <b><i>Text</i></b>
137
+ * ```
138
+ */
139
+ export function raw(html: string): string {
140
+ return html;
141
+ }
142
+
87
143
  export function escape(text: string, mode: ParseMode = "html"): string {
88
144
  switch (mode) {
89
145
  case "html":
@@ -96,6 +152,7 @@ export function escape(text: string, mode: ParseMode = "html"): string {
96
152
  }
97
153
 
98
154
  export const fmt = {
155
+ // HTML formatters
99
156
  bold,
100
157
  italic,
101
158
  underline,
@@ -114,4 +171,38 @@ export const fmt = {
114
171
  escapeHTML,
115
172
  escapeMarkdown,
116
173
  escapeMarkdownV2,
174
+ raw,
175
+
176
+ // Markdown formatters
177
+ boldMD,
178
+ italicMD,
179
+ codeMD,
180
+ codeBlockMD,
181
+ linkMD,
182
+ mentionMD,
183
+ hashtagMD,
184
+ underlineMD,
185
+ strikethroughMD,
186
+ spoilerMD,
187
+ emailMD,
188
+ urlMD,
189
+ customEmojiMD,
190
+ rawMD,
191
+
192
+ // MarkdownV2 formatters
193
+ boldMDv2,
194
+ italicMDv2,
195
+ underlineMDv2,
196
+ strikethroughMDv2,
197
+ spoilerMDv2,
198
+ codeMDv2,
199
+ codeBlockMDv2,
200
+ linkMDv2,
201
+ mentionMDv2,
202
+ hashtagMDv2,
203
+ emailMDv2,
204
+ urlMDv2,
205
+ customEmojiMDv2,
206
+ rawMDv2,
207
+ escapeMDv2,
117
208
  };