@mks2508/telegram-message-builder 0.2.0

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 (44) hide show
  1. package/dist/index.cjs +445 -0
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.js +422 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +39 -0
  6. package/src/builder/builder.d.ts +33 -0
  7. package/src/builder/builder.d.ts.map +1 -0
  8. package/src/builder/builder.ts +124 -0
  9. package/src/builder/index.d.ts +2 -0
  10. package/src/builder/index.d.ts.map +1 -0
  11. package/src/builder/index.ts +1 -0
  12. package/src/escaping.test.ts +133 -0
  13. package/src/formatters/index.d.ts +40 -0
  14. package/src/formatters/index.d.ts.map +1 -0
  15. package/src/formatters/index.ts +117 -0
  16. package/src/formatters.test.ts +163 -0
  17. package/src/index.d.ts +10 -0
  18. package/src/index.d.ts.map +1 -0
  19. package/src/index.ts +22 -0
  20. package/src/keyboard/index.d.ts +114 -0
  21. package/src/keyboard/index.d.ts.map +1 -0
  22. package/src/keyboard/index.ts +286 -0
  23. package/src/keyboard.test.ts +217 -0
  24. package/src/types/constants.d.ts +14 -0
  25. package/src/types/constants.d.ts.map +1 -0
  26. package/src/types/constants.ts +15 -0
  27. package/src/types/core.types.d.ts +75 -0
  28. package/src/types/core.types.d.ts.map +1 -0
  29. package/src/types/core.types.ts +80 -0
  30. package/src/types/index.d.ts +4 -0
  31. package/src/types/index.d.ts.map +1 -0
  32. package/src/types/index.ts +3 -0
  33. package/src/types/keyboard-types.index.d.ts +2 -0
  34. package/src/types/keyboard-types.index.d.ts.map +1 -0
  35. package/src/types/keyboard-types.index.ts +1 -0
  36. package/src/types/keyboard.types.d.ts +96 -0
  37. package/src/types/keyboard.types.d.ts.map +1 -0
  38. package/src/types/keyboard.types.ts +95 -0
  39. package/src/types/main.types.d.ts +23 -0
  40. package/src/types/main.types.d.ts.map +1 -0
  41. package/src/types/main.types.ts +25 -0
  42. package/src/utils/index.d.ts +6 -0
  43. package/src/utils/index.d.ts.map +1 -0
  44. package/src/utils/index.ts +5 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,445 @@
1
+
2
+ //#region src/types/constants.ts
3
+ /**
4
+ * Constantes del package.
5
+ *
6
+ * @module
7
+ */
8
+ /**
9
+ * Prefijo por defecto para saludos.
10
+ */
11
+ const DEFAULT_PREFIX = "Hello";
12
+ /**
13
+ * Sufijo por defecto para saludos.
14
+ */
15
+ const DEFAULT_SUFFIX = "!";
16
+
17
+ //#endregion
18
+ //#region src/formatters/index.ts
19
+ function escapeHTML(text) {
20
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
21
+ }
22
+ function escapeMarkdown(text) {
23
+ return text.replace(/_/g, "\\_").replace(/\*/g, "\\*").replace(/`/g, "\\`").replace(/\[/g, "\\[");
24
+ }
25
+ function escapeMarkdownV2(text) {
26
+ const reserved = "_*[]()~`>#+-=|{}.!";
27
+ let result = text;
28
+ for (const char of reserved) result = result.split(char).join(`\\${char}`);
29
+ return result;
30
+ }
31
+ function bold(text) {
32
+ return `<b>${escapeHTML(text)}</b>`;
33
+ }
34
+ function italic(text) {
35
+ return `<i>${escapeHTML(text)}</i>`;
36
+ }
37
+ function underline(text) {
38
+ return `<u>${escapeHTML(text)}</u>`;
39
+ }
40
+ function strikethrough(text) {
41
+ return `<s>${escapeHTML(text)}</s>`;
42
+ }
43
+ function spoiler(text) {
44
+ return `<tg-spoiler>${escapeHTML(text)}</tg-spoiler>`;
45
+ }
46
+ function code(text) {
47
+ return `<code>${escapeHTML(text)}</code>`;
48
+ }
49
+ function pre(text) {
50
+ return `<pre>${escapeHTML(text)}</pre>`;
51
+ }
52
+ function codeBlock(text, language) {
53
+ return language ? `<pre><code class="language-${language}">${escapeHTML(text)}</code></pre>` : `<pre>${escapeHTML(text)}</pre>`;
54
+ }
55
+ function link(text, url$1) {
56
+ return `<a href="${escapeHTML(url$1)}">${escapeHTML(text)}</a>`;
57
+ }
58
+ function mention(userId, name) {
59
+ const display = name ? escapeHTML(name) : escapeHTML(`user${userId}`);
60
+ return `<a href="tg://user?id=${userId}">${display}</a>`;
61
+ }
62
+ function hashtag(tag) {
63
+ return `<a href="tg://hashtag?tag=${escapeHTML(tag)}">#${escapeHTML(tag)}</a>`;
64
+ }
65
+ function customEmoji(emojiId) {
66
+ return `<tg-emoji emoji-id="${escapeHTML(emojiId)}">👻</tg-emoji>`;
67
+ }
68
+ function email(email$1) {
69
+ return `<a href="mailto:${escapeHTML(email$1)}">${escapeHTML(email$1)}</a>`;
70
+ }
71
+ function url(link$1) {
72
+ return `<a href="${escapeHTML(link$1)}">${escapeHTML(link$1)}</a>`;
73
+ }
74
+ function escape(text, mode = "html") {
75
+ switch (mode) {
76
+ case "html": return escapeHTML(text);
77
+ case "markdown": return escapeMarkdown(text);
78
+ case "markdownv2": return escapeMarkdownV2(text);
79
+ }
80
+ }
81
+ const fmt = {
82
+ bold,
83
+ italic,
84
+ underline,
85
+ strikethrough,
86
+ spoiler,
87
+ code,
88
+ pre,
89
+ codeBlock,
90
+ link,
91
+ mention,
92
+ hashtag,
93
+ email,
94
+ url,
95
+ customEmoji,
96
+ escape,
97
+ escapeHTML,
98
+ escapeMarkdown,
99
+ escapeMarkdownV2
100
+ };
101
+
102
+ //#endregion
103
+ //#region src/keyboard/index.ts
104
+ /**
105
+ * Telegram Keyboard Builder - Fluent API for building keyboards
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * const keyboard = TelegramKeyboardBuilder.inline()
110
+ * .urlButton('Google', 'https://google.com')
111
+ * .row()
112
+ * .callbackButton('Yes', 'yes')
113
+ * .callbackButton('No', 'no')
114
+ * .buildMarkup()
115
+ * ```
116
+ */
117
+ var TelegramKeyboardBuilder = class TelegramKeyboardBuilder {
118
+ rows = [];
119
+ type = "inline";
120
+ currentRow = [];
121
+ constructor(type) {
122
+ this.type = type;
123
+ }
124
+ /**
125
+ * Create an inline keyboard builder
126
+ */
127
+ static inline() {
128
+ return new TelegramKeyboardBuilder("inline");
129
+ }
130
+ /**
131
+ * Create a reply keyboard builder
132
+ */
133
+ static reply() {
134
+ return new TelegramKeyboardBuilder("reply");
135
+ }
136
+ /**
137
+ * Create a force reply builder
138
+ */
139
+ static forceReply() {
140
+ return new TelegramKeyboardBuilder("force");
141
+ }
142
+ /**
143
+ * Add a URL button (inline only)
144
+ */
145
+ urlButton(text, url$1) {
146
+ if (this.type !== "inline") throw new Error("urlButton is only available for inline keyboards");
147
+ this.currentRow.push({
148
+ text,
149
+ url: url$1
150
+ });
151
+ return this;
152
+ }
153
+ /**
154
+ * Add a callback button (inline only)
155
+ */
156
+ callbackButton(text, data) {
157
+ if (this.type !== "inline") throw new Error("callbackButton is only available for inline keyboards");
158
+ this.currentRow.push({
159
+ text,
160
+ callback_data: data
161
+ });
162
+ return this;
163
+ }
164
+ /**
165
+ * Add a web app button (inline only)
166
+ */
167
+ webAppButton(text, url$1) {
168
+ if (this.type !== "inline") throw new Error("webAppButton is only available for inline keyboards");
169
+ this.currentRow.push({
170
+ text,
171
+ web_app: { url: url$1 }
172
+ });
173
+ return this;
174
+ }
175
+ /**
176
+ * Add a login button (inline only)
177
+ */
178
+ loginButton(text, url$1, options) {
179
+ if (this.type !== "inline") throw new Error("loginButton is only available for inline keyboards");
180
+ this.currentRow.push({
181
+ text,
182
+ login_url: {
183
+ url: url$1,
184
+ forward_text: options?.forwardText,
185
+ bot_username: options?.botUsername,
186
+ request_write_access: options?.requestWriteAccess
187
+ }
188
+ });
189
+ return this;
190
+ }
191
+ /**
192
+ * Add a switch inline query button (inline only)
193
+ */
194
+ switchInlineQueryButton(text, query) {
195
+ if (this.type !== "inline") throw new Error("switchInlineQueryButton is only available for inline keyboards");
196
+ this.currentRow.push({
197
+ text,
198
+ switch_inline_query: query
199
+ });
200
+ return this;
201
+ }
202
+ /**
203
+ * Add a switch inline query current chat button (inline only)
204
+ */
205
+ switchInlineQueryCurrentChatButton(text, query) {
206
+ if (this.type !== "inline") throw new Error("switchInlineQueryCurrentChatButton is only available for inline keyboards");
207
+ this.currentRow.push({
208
+ text,
209
+ switch_inline_query_current_chat: query
210
+ });
211
+ return this;
212
+ }
213
+ /**
214
+ * Add a callback game button (inline only)
215
+ */
216
+ callbackGameButton(text, shortName) {
217
+ if (this.type !== "inline") throw new Error("callbackGameButton is only available for inline keyboards");
218
+ this.currentRow.push({
219
+ text,
220
+ callback_game: shortName
221
+ });
222
+ return this;
223
+ }
224
+ /**
225
+ * Add a pay button (inline only)
226
+ */
227
+ payButton(text) {
228
+ if (this.type !== "inline") throw new Error("payButton is only available for inline keyboards");
229
+ this.currentRow.push({
230
+ text,
231
+ pay: text
232
+ });
233
+ return this;
234
+ }
235
+ /**
236
+ * Add a text button (reply keyboard only)
237
+ */
238
+ textButton(text) {
239
+ if (this.type !== "reply") throw new Error("textButton is only available for reply keyboards");
240
+ this.currentRow.push({ text });
241
+ return this;
242
+ }
243
+ /**
244
+ * Add a request contact button (reply keyboard only)
245
+ */
246
+ requestContactButton(text) {
247
+ if (this.type !== "reply") throw new Error("requestContactButton is only available for reply keyboards");
248
+ this.currentRow.push({
249
+ text,
250
+ request_contact: true
251
+ });
252
+ return this;
253
+ }
254
+ /**
255
+ * Add a request location button (reply keyboard only)
256
+ */
257
+ requestLocationButton(text) {
258
+ if (this.type !== "reply") throw new Error("requestLocationButton is only available for reply keyboards");
259
+ this.currentRow.push({
260
+ text,
261
+ request_location: true
262
+ });
263
+ return this;
264
+ }
265
+ /**
266
+ * Add a request poll button (reply keyboard only)
267
+ */
268
+ requestPollButton(text, pollType) {
269
+ if (this.type !== "reply") throw new Error("requestPollButton is only available for reply keyboards");
270
+ this.currentRow.push({
271
+ text,
272
+ request_poll: { type: pollType }
273
+ });
274
+ return this;
275
+ }
276
+ /**
277
+ * Add a custom button to current row
278
+ */
279
+ button(button) {
280
+ this.currentRow.push(button);
281
+ return this;
282
+ }
283
+ /**
284
+ * Finish current row and start a new one
285
+ */
286
+ row() {
287
+ if (this.currentRow.length > 0) {
288
+ this.rows.push([...this.currentRow]);
289
+ this.currentRow = [];
290
+ }
291
+ return this;
292
+ }
293
+ /**
294
+ * Build inline keyboard markup
295
+ */
296
+ buildMarkup() {
297
+ if (this.currentRow.length > 0) this.rows.push([...this.currentRow]);
298
+ return { inline_keyboard: this.rows };
299
+ }
300
+ /**
301
+ * Build reply keyboard markup
302
+ */
303
+ buildReplyMarkup() {
304
+ if (this.currentRow.length > 0) this.rows.push([...this.currentRow]);
305
+ return {
306
+ keyboard: this.rows,
307
+ resize_keyboard: true,
308
+ one_time_keyboard: false,
309
+ selective: false
310
+ };
311
+ }
312
+ /**
313
+ * Build force reply markup
314
+ */
315
+ buildForceReplyMarkup() {
316
+ return { force_reply: true };
317
+ }
318
+ /**
319
+ * Build markup based on keyboard type
320
+ */
321
+ build() {
322
+ switch (this.type) {
323
+ case "inline": return this.buildMarkup();
324
+ case "reply": return this.buildReplyMarkup();
325
+ case "force": return this.buildForceReplyMarkup();
326
+ }
327
+ }
328
+ };
329
+
330
+ //#endregion
331
+ //#region src/builder/builder.ts
332
+ /**
333
+ * Telegram Message Builder - Fluent API for building formatted Telegram messages
334
+ */
335
+ var TelegramMessageBuilder = class TelegramMessageBuilder {
336
+ parts = [];
337
+ parseMode = "html";
338
+ options = {};
339
+ constructor(parseMode) {
340
+ this.parseMode = parseMode;
341
+ }
342
+ static text() {
343
+ return new TelegramMessageBuilder("html");
344
+ }
345
+ setParseMode(mode) {
346
+ this.parseMode = mode;
347
+ return this;
348
+ }
349
+ title(text) {
350
+ this.parts.push(bold(text));
351
+ return this;
352
+ }
353
+ section(text) {
354
+ this.parts.push(underline(text));
355
+ return this;
356
+ }
357
+ line(key, value, opts) {
358
+ let formattedValue = value;
359
+ if (opts?.bold) formattedValue = bold(value);
360
+ else if (opts?.italic) formattedValue = italic(value);
361
+ else if (opts?.code) formattedValue = code(value);
362
+ else if (opts?.underline) formattedValue = underline(value);
363
+ this.parts.push(`${key}: ${formattedValue}`);
364
+ return this;
365
+ }
366
+ codeBlock(text, language) {
367
+ this.parts.push(codeBlock(text, language));
368
+ return this;
369
+ }
370
+ listItem(text) {
371
+ this.parts.push(`• ${text}`);
372
+ return this;
373
+ }
374
+ newline(count = 1) {
375
+ for (let i = 0; i < count; i++) this.parts.push("\n\n");
376
+ return this;
377
+ }
378
+ separator() {
379
+ this.parts.push("---");
380
+ return this;
381
+ }
382
+ text(text) {
383
+ this.parts.push(escape(text, this.parseMode));
384
+ return this;
385
+ }
386
+ link(text, url$1) {
387
+ this.parts.push(link(text, url$1));
388
+ return this;
389
+ }
390
+ mention(userId, name) {
391
+ this.parts.push(mention(userId, name));
392
+ return this;
393
+ }
394
+ hashtag(tag) {
395
+ this.parts.push(hashtag(tag));
396
+ return this;
397
+ }
398
+ build() {
399
+ return {
400
+ text: this.parts.join("\n"),
401
+ parse_mode: this.parseMode,
402
+ ...this.options
403
+ };
404
+ }
405
+ setOption(key, value) {
406
+ this.options[key] = value;
407
+ return this;
408
+ }
409
+ setOptions(options) {
410
+ this.options = {
411
+ ...this.options,
412
+ ...options
413
+ };
414
+ return this;
415
+ }
416
+ getParseMode() {
417
+ return this.parseMode;
418
+ }
419
+ };
420
+
421
+ //#endregion
422
+ exports.DEFAULT_PREFIX = DEFAULT_PREFIX;
423
+ exports.DEFAULT_SUFFIX = DEFAULT_SUFFIX;
424
+ exports.TelegramKeyboardBuilder = TelegramKeyboardBuilder;
425
+ exports.TelegramMessageBuilder = TelegramMessageBuilder;
426
+ exports.bold = bold;
427
+ exports.code = code;
428
+ exports.codeBlock = codeBlock;
429
+ exports.customEmoji = customEmoji;
430
+ exports.email = email;
431
+ exports.escape = escape;
432
+ exports.escapeHTML = escapeHTML;
433
+ exports.escapeMarkdown = escapeMarkdown;
434
+ exports.escapeMarkdownV2 = escapeMarkdownV2;
435
+ exports.fmt = fmt;
436
+ exports.hashtag = hashtag;
437
+ exports.italic = italic;
438
+ exports.link = link;
439
+ exports.mention = mention;
440
+ exports.pre = pre;
441
+ exports.spoiler = spoiler;
442
+ exports.strikethrough = strikethrough;
443
+ exports.underline = underline;
444
+ exports.url = url;
445
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["text: string","language?: string","url: string","url","userId: number","name?: string","tag: string","emojiId: string","email: string","email","link: string","link","mode: ParseMode","type: \"inline\" | \"reply\" | \"force\"","text: string","url: string","data: string","options?: {\n forwardText?: string;\n botUsername?: string;\n requestWriteAccess?: boolean;\n }","query: string","shortName: string","pollType: string","button: IInlineKeyboardButton | IKeyboardButton","parseMode: ParseMode","mode: ParseMode","text: string","key: string","value: string","opts?: {\n bold?: boolean;\n italic?: boolean;\n code?: boolean;\n underline?: boolean;\n }","language?: string","count: number","url: string","url","userId: number","name?: string","tag: string","value: unknown","options: Record<string, unknown>"],"sources":["../src/types/constants.ts","../src/formatters/index.ts","../src/keyboard/index.ts","../src/builder/builder.ts"],"sourcesContent":["/**\n * Constantes del package.\n *\n * @module\n */\n\n/**\n * Prefijo por defecto para saludos.\n */\nexport const DEFAULT_PREFIX = \"Hello\";\n\n/**\n * Sufijo por defecto para saludos.\n */\nexport const DEFAULT_SUFFIX = \"!\";\n","import type { ParseMode } from \"../types\";\n\nexport function escapeHTML(text: string): string {\n return text\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\nexport function escapeMarkdown(text: string): string {\n return text\n .replace(/_/g, \"\\\\_\")\n .replace(/\\*/g, \"\\\\*\")\n .replace(/`/g, \"\\\\`\")\n .replace(/\\[/g, \"\\\\[\");\n}\n\nexport function escapeMarkdownV2(text: string): string {\n const reserved = \"_*[]()~`>#+-=|{}.!\";\n let result = text;\n for (const char of reserved) {\n result = result.split(char).join(`\\\\${char}`);\n }\n return result;\n}\n\nexport function bold(text: string): string {\n return `<b>${escapeHTML(text)}</b>`;\n}\n\nexport function italic(text: string): string {\n return `<i>${escapeHTML(text)}</i>`;\n}\n\nexport function underline(text: string): string {\n return `<u>${escapeHTML(text)}</u>`;\n}\n\nexport function strikethrough(text: string): string {\n return `<s>${escapeHTML(text)}</s>`;\n}\n\nexport function spoiler(text: string): string {\n return `<tg-spoiler>${escapeHTML(text)}</tg-spoiler>`;\n}\n\nexport function code(text: string): string {\n return `<code>${escapeHTML(text)}</code>`;\n}\n\nexport function pre(text: string): string {\n return `<pre>${escapeHTML(text)}</pre>`;\n}\n\nexport function codeBlock(text: string, language?: string): string {\n return language\n ? `<pre><code class=\"language-${language}\">${escapeHTML(text)}</code></pre>`\n : `<pre>${escapeHTML(text)}</pre>`;\n}\n\nexport function link(text: string, url: string): string {\n return `<a href=\"${escapeHTML(url)}\">${escapeHTML(text)}</a>`;\n}\n\nexport function mention(userId: number, name?: string): string {\n const display = name ? escapeHTML(name) : escapeHTML(`user${userId}`);\n return `<a href=\"tg://user?id=${userId}\">${display}</a>`;\n}\n\nexport function hashtag(tag: string): string {\n return `<a href=\"tg://hashtag?tag=${escapeHTML(tag)}\">#${escapeHTML(tag)}</a>`;\n}\n\nexport function customEmoji(emojiId: string): string {\n return `<tg-emoji emoji-id=\"${escapeHTML(emojiId)}\">👻</tg-emoji>`;\n}\n\nexport function email(email: string): string {\n return `<a href=\"mailto:${escapeHTML(email)}\">${escapeHTML(email)}</a>`;\n}\n\nexport function url(link: string): string {\n return `<a href=\"${escapeHTML(link)}\">${escapeHTML(link)}</a>`;\n}\n\nexport function escape(text: string, mode: ParseMode = \"html\"): string {\n switch (mode) {\n case \"html\":\n return escapeHTML(text);\n case \"markdown\":\n return escapeMarkdown(text);\n case \"markdownv2\":\n return escapeMarkdownV2(text);\n }\n}\n\nexport const fmt = {\n bold,\n italic,\n underline,\n strikethrough,\n spoiler,\n code,\n pre,\n codeBlock,\n link,\n mention,\n hashtag,\n email,\n url,\n customEmoji,\n escape,\n escapeHTML,\n escapeMarkdown,\n escapeMarkdownV2,\n};\n","/**\n * @fileoverview Keyboard Builders\n * @description Fluent API for building Telegram keyboards\n * @module telegram-message-builder/keyboard\n */\n\nimport type {\n IInlineKeyboardMarkup,\n IInlineKeyboardButton,\n IReplyKeyboardMarkup,\n IKeyboardButton,\n IForceReplyMarkup,\n} from \"../types\";\n\n/**\n * Telegram Keyboard Builder - Fluent API for building keyboards\n *\n * @example\n * ```typescript\n * const keyboard = TelegramKeyboardBuilder.inline()\n * .urlButton('Google', 'https://google.com')\n * .row()\n * .callbackButton('Yes', 'yes')\n * .callbackButton('No', 'no')\n * .buildMarkup()\n * ```\n */\nexport class TelegramKeyboardBuilder {\n private rows: IInlineKeyboardButton[][] | IKeyboardButton[][] = [];\n private type: \"inline\" | \"reply\" | \"force\" = \"inline\";\n private currentRow: IInlineKeyboardButton[] | IKeyboardButton[] = [];\n\n private constructor(type: \"inline\" | \"reply\" | \"force\") {\n this.type = type;\n }\n\n /**\n * Create an inline keyboard builder\n */\n static inline(): TelegramKeyboardBuilder {\n return new TelegramKeyboardBuilder(\"inline\");\n }\n\n /**\n * Create a reply keyboard builder\n */\n static reply(): TelegramKeyboardBuilder {\n return new TelegramKeyboardBuilder(\"reply\");\n }\n\n /**\n * Create a force reply builder\n */\n static forceReply(): TelegramKeyboardBuilder {\n return new TelegramKeyboardBuilder(\"force\");\n }\n\n /**\n * Add a URL button (inline only)\n */\n urlButton(text: string, url: string): this {\n if (this.type !== \"inline\") {\n throw new Error(\"urlButton is only available for inline keyboards\");\n }\n this.currentRow.push({ text, url });\n return this;\n }\n\n /**\n * Add a callback button (inline only)\n */\n callbackButton(text: string, data: string): this {\n if (this.type !== \"inline\") {\n throw new Error(\"callbackButton is only available for inline keyboards\");\n }\n this.currentRow.push({ text, callback_data: data });\n return this;\n }\n\n /**\n * Add a web app button (inline only)\n */\n webAppButton(text: string, url: string): this {\n if (this.type !== \"inline\") {\n throw new Error(\"webAppButton is only available for inline keyboards\");\n }\n this.currentRow.push({ text, web_app: { url } });\n return this;\n }\n\n /**\n * Add a login button (inline only)\n */\n loginButton(\n text: string,\n url: string,\n options?: {\n forwardText?: string;\n botUsername?: string;\n requestWriteAccess?: boolean;\n },\n ): this {\n if (this.type !== \"inline\") {\n throw new Error(\"loginButton is only available for inline keyboards\");\n }\n this.currentRow.push({\n text,\n login_url: {\n url,\n forward_text: options?.forwardText,\n bot_username: options?.botUsername,\n request_write_access: options?.requestWriteAccess,\n },\n });\n return this;\n }\n\n /**\n * Add a switch inline query button (inline only)\n */\n switchInlineQueryButton(text: string, query: string): this {\n if (this.type !== \"inline\") {\n throw new Error(\n \"switchInlineQueryButton is only available for inline keyboards\",\n );\n }\n this.currentRow.push({ text, switch_inline_query: query });\n return this;\n }\n\n /**\n * Add a switch inline query current chat button (inline only)\n */\n switchInlineQueryCurrentChatButton(text: string, query: string): this {\n if (this.type !== \"inline\") {\n throw new Error(\n \"switchInlineQueryCurrentChatButton is only available for inline keyboards\",\n );\n }\n this.currentRow.push({ text, switch_inline_query_current_chat: query });\n return this;\n }\n\n /**\n * Add a callback game button (inline only)\n */\n callbackGameButton(text: string, shortName: string): this {\n if (this.type !== \"inline\") {\n throw new Error(\n \"callbackGameButton is only available for inline keyboards\",\n );\n }\n this.currentRow.push({ text, callback_game: shortName });\n return this;\n }\n\n /**\n * Add a pay button (inline only)\n */\n payButton(text: string): this {\n if (this.type !== \"inline\") {\n throw new Error(\"payButton is only available for inline keyboards\");\n }\n this.currentRow.push({ text, pay: text });\n return this;\n }\n\n /**\n * Add a text button (reply keyboard only)\n */\n textButton(text: string): this {\n if (this.type !== \"reply\") {\n throw new Error(\"textButton is only available for reply keyboards\");\n }\n this.currentRow.push({ text });\n return this;\n }\n\n /**\n * Add a request contact button (reply keyboard only)\n */\n requestContactButton(text: string): this {\n if (this.type !== \"reply\") {\n throw new Error(\n \"requestContactButton is only available for reply keyboards\",\n );\n }\n this.currentRow.push({ text, request_contact: true });\n return this;\n }\n\n /**\n * Add a request location button (reply keyboard only)\n */\n requestLocationButton(text: string): this {\n if (this.type !== \"reply\") {\n throw new Error(\n \"requestLocationButton is only available for reply keyboards\",\n );\n }\n this.currentRow.push({ text, request_location: true });\n return this;\n }\n\n /**\n * Add a request poll button (reply keyboard only)\n */\n requestPollButton(text: string, pollType: string): this {\n if (this.type !== \"reply\") {\n throw new Error(\n \"requestPollButton is only available for reply keyboards\",\n );\n }\n this.currentRow.push({ text, request_poll: { type: pollType } });\n return this;\n }\n\n /**\n * Add a custom button to current row\n */\n button(button: IInlineKeyboardButton | IKeyboardButton): this {\n this.currentRow.push(button);\n return this;\n }\n\n /**\n * Finish current row and start a new one\n */\n row(): this {\n if (this.currentRow.length > 0) {\n this.rows.push([...this.currentRow]);\n this.currentRow = [];\n }\n return this;\n }\n\n /**\n * Build inline keyboard markup\n */\n buildMarkup(): IInlineKeyboardMarkup {\n // Add last row if not empty\n if (this.currentRow.length > 0) {\n this.rows.push([...this.currentRow]);\n }\n\n return { inline_keyboard: this.rows as IInlineKeyboardButton[][] };\n }\n\n /**\n * Build reply keyboard markup\n */\n buildReplyMarkup(): IReplyKeyboardMarkup {\n // Add last row if not empty\n if (this.currentRow.length > 0) {\n this.rows.push([...this.currentRow]);\n }\n\n return {\n keyboard: this.rows as IKeyboardButton[][],\n resize_keyboard: true,\n one_time_keyboard: false,\n selective: false,\n };\n }\n\n /**\n * Build force reply markup\n */\n buildForceReplyMarkup(): IForceReplyMarkup {\n return { force_reply: true };\n }\n\n /**\n * Build markup based on keyboard type\n */\n build(): IInlineKeyboardMarkup | IReplyKeyboardMarkup | IForceReplyMarkup {\n switch (this.type) {\n case \"inline\":\n return this.buildMarkup();\n case \"reply\":\n return this.buildReplyMarkup();\n case \"force\":\n return this.buildForceReplyMarkup();\n }\n }\n}\n","import type { TelegramMessage, ParseMode } from \"../types\";\nimport * as fmt from \"../formatters\";\n\n/**\n * Telegram Message Builder - Fluent API for building formatted Telegram messages\n */\nexport class TelegramMessageBuilder {\n private parts: string[] = [];\n private parseMode: ParseMode = \"html\";\n private options: Record<string, unknown> = {};\n\n private constructor(parseMode: ParseMode) {\n this.parseMode = parseMode;\n }\n\n static text(): TelegramMessageBuilder {\n return new TelegramMessageBuilder(\"html\");\n }\n\n setParseMode(mode: ParseMode): this {\n this.parseMode = mode;\n return this;\n }\n\n title(text: string): this {\n this.parts.push(fmt.bold(text));\n return this;\n }\n\n section(text: string): this {\n this.parts.push(fmt.underline(text));\n return this;\n }\n\n line(\n key: string,\n value: string,\n opts?: {\n bold?: boolean;\n italic?: boolean;\n code?: boolean;\n underline?: boolean;\n },\n ): this {\n let formattedValue = value;\n\n if (opts?.bold) {\n formattedValue = fmt.bold(value);\n } else if (opts?.italic) {\n formattedValue = fmt.italic(value);\n } else if (opts?.code) {\n formattedValue = fmt.code(value);\n } else if (opts?.underline) {\n formattedValue = fmt.underline(value);\n }\n\n this.parts.push(`${key}: ${formattedValue}`);\n return this;\n }\n\n codeBlock(text: string, language?: string): this {\n this.parts.push(fmt.codeBlock(text, language));\n return this;\n }\n\n listItem(text: string): this {\n this.parts.push(`• ${text}`);\n return this;\n }\n\n newline(count: number = 1): this {\n for (let i = 0; i < count; i++) {\n this.parts.push(\"\\n\\n\");\n }\n return this;\n }\n\n separator(): this {\n this.parts.push(\"---\");\n return this;\n }\n\n text(text: string): this {\n this.parts.push(fmt.escape(text, this.parseMode));\n return this;\n }\n\n link(text: string, url: string): this {\n this.parts.push(fmt.link(text, url));\n return this;\n }\n\n mention(userId: number, name?: string): this {\n this.parts.push(fmt.mention(userId, name));\n return this;\n }\n\n hashtag(tag: string): this {\n this.parts.push(fmt.hashtag(tag));\n return this;\n }\n\n build(): TelegramMessage {\n return {\n text: this.parts.join(\"\\n\"),\n parse_mode: this.parseMode,\n ...this.options,\n };\n }\n\n setOption(key: string, value: unknown): this {\n this.options[key] = value;\n return this;\n }\n\n setOptions(options: Record<string, unknown>): this {\n this.options = { ...this.options, ...options };\n return this;\n }\n\n getParseMode(): ParseMode {\n return this.parseMode;\n }\n}\n"],"mappings":"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"}