telegix 1.1.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/lib/api.js ADDED
@@ -0,0 +1,1840 @@
1
+ /**
2
+ * Telegix - Pure JavaScript Telegram Bot API Client
3
+ * @module telegix/api
4
+ */
5
+
6
+ import { TelegramError, NetworkError } from './errors.js';
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+
10
+ /**
11
+ * Checks if value is a file/stream/buffer that needs multipart/form-data upload
12
+ * @param {any} value
13
+ * @returns {boolean}
14
+ */
15
+ export function isUploadableFile(value) {
16
+ if (!value) return false;
17
+ if (typeof value === 'string') return false;
18
+ if (value instanceof Blob || value instanceof Uint8Array || Buffer.isBuffer(value)) return true;
19
+ if (typeof value === 'object' && ('source' in value || 'url' in value)) return true;
20
+ if (typeof value === 'object' && typeof value.pipe === 'function') return true; // Stream
21
+ return false;
22
+ }
23
+
24
+ /**
25
+ * Normalizes input source into a Blob/File suitable for FormData
26
+ * @param {any} input
27
+ * @param {string} [defaultFilename='file']
28
+ * @returns {Promise<{ blob: Blob|string, filename?: string }>}
29
+ */
30
+ export async function normalizeFileSource(input, defaultFilename = 'file') {
31
+ if (typeof input === 'string') {
32
+ // If it's a file path on local filesystem
33
+ if (fs.existsSync(input)) {
34
+ const buffer = await fs.promises.readFile(input);
35
+ const filename = path.basename(input) || defaultFilename;
36
+ return {
37
+ blob: new Blob([buffer]),
38
+ filename,
39
+ };
40
+ }
41
+ // Otherwise it's a file_id or URL string
42
+ return { blob: input };
43
+ }
44
+
45
+ if (Buffer.isBuffer(input) || input instanceof Uint8Array) {
46
+ return {
47
+ blob: new Blob([input]),
48
+ filename: defaultFilename,
49
+ };
50
+ }
51
+
52
+ if (input instanceof Blob) {
53
+ return {
54
+ blob: input,
55
+ filename: defaultFilename,
56
+ };
57
+ }
58
+
59
+ if (typeof input === 'object') {
60
+ const filename = input.filename || defaultFilename;
61
+
62
+ if (input.url && typeof input.url === 'string') {
63
+ return { blob: input.url };
64
+ }
65
+
66
+ if (input.source) {
67
+ if (typeof input.source === 'string') {
68
+ if (fs.existsSync(input.source)) {
69
+ const buffer = await fs.promises.readFile(input.source);
70
+ return {
71
+ blob: new Blob([buffer]),
72
+ filename: input.filename || path.basename(input.source) || defaultFilename,
73
+ };
74
+ }
75
+ return { blob: input.source };
76
+ }
77
+ if (Buffer.isBuffer(input.source) || input.source instanceof Uint8Array) {
78
+ return {
79
+ blob: new Blob([input.source]),
80
+ filename,
81
+ };
82
+ }
83
+ if (input.source instanceof Blob) {
84
+ return {
85
+ blob: input.source,
86
+ filename,
87
+ };
88
+ }
89
+ // Stream
90
+ if (typeof input.source.pipe === 'function') {
91
+ const chunks = [];
92
+ for await (const chunk of input.source) {
93
+ chunks.push(chunk);
94
+ }
95
+ return {
96
+ blob: new Blob(chunks),
97
+ filename,
98
+ };
99
+ }
100
+ }
101
+
102
+ if (typeof input.pipe === 'function') {
103
+ const chunks = [];
104
+ for await (const chunk of input) {
105
+ chunks.push(chunk);
106
+ }
107
+ return {
108
+ blob: new Blob(chunks),
109
+ filename,
110
+ };
111
+ }
112
+ }
113
+
114
+ return { blob: input };
115
+ }
116
+
117
+ /**
118
+ * Normalizes payload and handles automatic wrapping of keyboards and builders into reply_markup
119
+ * @param {object} payload
120
+ * @returns {object}
121
+ */
122
+ export function normalizeTelegramPayload(payload) {
123
+ if (!payload || typeof payload !== 'object') return payload;
124
+
125
+ let norm = { ...payload };
126
+
127
+ // Always delete any stray toJSON property from root payload to prevent JSON.stringify hijacking
128
+ if ('toJSON' in norm) {
129
+ delete norm.toJSON;
130
+ }
131
+
132
+ // 1. If payload contains top-level keyboard
133
+ if (norm.keyboard) {
134
+ const keyboard = norm.keyboard;
135
+ const is_persistent = Boolean(norm.is_persistent);
136
+ const resize_keyboard = norm.resize_keyboard ?? true;
137
+ const one_time_keyboard = Boolean(norm.one_time_keyboard);
138
+ const input_field_placeholder = norm.input_field_placeholder;
139
+ const selective = Boolean(norm.selective);
140
+
141
+ delete norm.keyboard;
142
+ delete norm.is_persistent;
143
+ delete norm.resize_keyboard;
144
+ delete norm.one_time_keyboard;
145
+ delete norm.input_field_placeholder;
146
+ delete norm.selective;
147
+
148
+ if (!norm.reply_markup) {
149
+ norm.reply_markup = {
150
+ keyboard,
151
+ is_persistent,
152
+ resize_keyboard,
153
+ one_time_keyboard,
154
+ input_field_placeholder,
155
+ selective,
156
+ };
157
+ }
158
+ }
159
+
160
+ // 2. If payload contains top-level inline_keyboard
161
+ if (norm.inline_keyboard) {
162
+ const inline_keyboard = norm.inline_keyboard;
163
+ delete norm.inline_keyboard;
164
+ if (!norm.reply_markup) {
165
+ norm.reply_markup = { inline_keyboard };
166
+ }
167
+ }
168
+
169
+ // 3. If payload contains top-level remove_keyboard
170
+ if (norm.remove_keyboard) {
171
+ const selective = Boolean(norm.selective);
172
+ delete norm.remove_keyboard;
173
+ delete norm.selective;
174
+ if (!norm.reply_markup) {
175
+ norm.reply_markup = { remove_keyboard: true, selective };
176
+ }
177
+ }
178
+
179
+ // 4. If payload contains top-level force_reply
180
+ if (norm.force_reply) {
181
+ const selective = Boolean(norm.selective);
182
+ const input_field_placeholder = norm.input_field_placeholder;
183
+ delete norm.force_reply;
184
+ delete norm.selective;
185
+ delete norm.input_field_placeholder;
186
+ if (!norm.reply_markup) {
187
+ norm.reply_markup = { force_reply: true, selective, input_field_placeholder };
188
+ }
189
+ }
190
+
191
+ // 5. If reply_markup is an object with toJSON or nested reply_markup
192
+ if (norm.reply_markup) {
193
+ if (typeof norm.reply_markup.toJSON === 'function') {
194
+ norm.reply_markup = norm.reply_markup.toJSON();
195
+ }
196
+ while (norm.reply_markup && typeof norm.reply_markup === 'object' && norm.reply_markup.reply_markup) {
197
+ norm.reply_markup = norm.reply_markup.reply_markup;
198
+ if (typeof norm.reply_markup?.toJSON === 'function') {
199
+ norm.reply_markup = norm.reply_markup.toJSON();
200
+ }
201
+ }
202
+ }
203
+
204
+ return norm;
205
+ }
206
+
207
+ export class Telegram {
208
+ /**
209
+ * @param {string} token - Bot Token from @BotFather
210
+ * @param {object} [options]
211
+ * @param {string} [options.apiRoot='https://api.telegram.org']
212
+ * @param {boolean} [options.testEnv=false]
213
+ * @param {number} [options.timeout=60000] - Request timeout in ms
214
+ */
215
+ constructor(token, options = {}) {
216
+ if (!token || typeof token !== 'string') {
217
+ throw new Error('Telegix: Telegram Bot Token is required and must be a string.');
218
+ }
219
+ this.token = token.trim();
220
+ this.apiRoot = options.apiRoot || 'https://api.telegram.org';
221
+ this.testEnv = Boolean(options.testEnv);
222
+ this.timeout = options.timeout || 60000;
223
+ this.options = options;
224
+ }
225
+
226
+ /**
227
+ * Returns base URL for Telegram API calls
228
+ * @returns {string}
229
+ */
230
+ getBaseUrl() {
231
+ return `${this.apiRoot}/bot${this.token}${this.testEnv ? '/test' : ''}`;
232
+ }
233
+
234
+ /**
235
+ * Returns base URL for Telegram downloaded files
236
+ * @returns {string}
237
+ */
238
+ getFileBaseUrl() {
239
+ return `${this.apiRoot}/file/bot${this.token}${this.testEnv ? '/test' : ''}`;
240
+ }
241
+
242
+ /**
243
+ * Low-level API caller
244
+ * @param {string} method - Telegram API method name (e.g. 'sendMessage', 'sendPhoto')
245
+ * @param {object} [payload={}] - Parameters for the method
246
+ * @param {object} [options={}] - Custom options / abort signal
247
+ * @returns {Promise<any>} Result from Telegram Bot API
248
+ */
249
+ async call(method, payload = {}, options = {}) {
250
+ const url = `${this.getBaseUrl()}/${method}`;
251
+ const normalizedPayload = normalizeTelegramPayload(payload);
252
+ let hasUpload = false;
253
+
254
+ // Check if any payload property is an uploadable file
255
+ for (const key of Object.keys(normalizedPayload)) {
256
+ if (isUploadableFile(normalizedPayload[key])) {
257
+ hasUpload = true;
258
+ break;
259
+ }
260
+ }
261
+
262
+ let requestInit = {
263
+ method: 'POST',
264
+ signal: options.signal,
265
+ };
266
+
267
+ if (hasUpload) {
268
+ const formData = new FormData();
269
+ for (const [key, value] of Object.entries(normalizedPayload)) {
270
+ if (value === undefined || value === null) continue;
271
+
272
+ if (isUploadableFile(value)) {
273
+ const { blob, filename } = await normalizeFileSource(value, key);
274
+ if (typeof blob === 'string') {
275
+ formData.append(key, blob);
276
+ } else {
277
+ formData.append(key, blob, filename || 'file');
278
+ }
279
+ } else if (typeof value === 'object') {
280
+ // Serialize nested objects like reply_markup, entities, media arrays
281
+ const serialized = typeof value.toJSON === 'function' ? value.toJSON() : value;
282
+ formData.append(key, JSON.stringify(serialized));
283
+ } else {
284
+ formData.append(key, String(value));
285
+ }
286
+ }
287
+ requestInit.body = formData;
288
+ } else {
289
+ // Serialize any custom toJSON objects (like Markup)
290
+ const cleanPayload = {};
291
+ for (const [key, value] of Object.entries(normalizedPayload)) {
292
+ if (value === undefined || value === null || key === 'toJSON') continue;
293
+ if (typeof value === 'object' && typeof value.toJSON === 'function') {
294
+ cleanPayload[key] = value.toJSON();
295
+ } else {
296
+ cleanPayload[key] = value;
297
+ }
298
+ }
299
+ delete cleanPayload.toJSON;
300
+
301
+ requestInit.headers = {
302
+ 'Content-Type': 'application/json',
303
+ };
304
+ requestInit.body = JSON.stringify(cleanPayload);
305
+ }
306
+
307
+ let response;
308
+ try {
309
+ response = await fetch(url, requestInit);
310
+ } catch (err) {
311
+ throw new NetworkError(err, method);
312
+ }
313
+
314
+ let data;
315
+ try {
316
+ data = await response.json();
317
+ } catch (err) {
318
+ throw new TelegramError(
319
+ { error_code: response.status, description: `HTTP ${response.statusText || response.status}` },
320
+ method,
321
+ payload
322
+ );
323
+ }
324
+
325
+ if (!data.ok) {
326
+ if (data.error_code === 429 && data.parameters?.retry_after && options.autoRetry !== false) {
327
+ const retryAfter = data.parameters.retry_after;
328
+ await new Promise((resolve) => setTimeout(resolve, (retryAfter + 1) * 1000));
329
+ return this.call(method, payload, options);
330
+ }
331
+ throw new TelegramError(data, method, payload);
332
+ }
333
+
334
+ return data.result;
335
+ }
336
+
337
+ // ==========================================
338
+ // Telegram Bot API Methods Implementation
339
+ // ==========================================
340
+
341
+ /**
342
+ * Get basic information about the bot
343
+ */
344
+ getMe() {
345
+ return this.call('getMe');
346
+ }
347
+
348
+ /**
349
+ * Log out from the cloud Bot API server before at-home local server migration
350
+ */
351
+ logOut() {
352
+ return this.call('logOut');
353
+ }
354
+
355
+ /**
356
+ * Close the bot instance before moving it between machines
357
+ */
358
+ close() {
359
+ return this.call('close');
360
+ }
361
+
362
+ /**
363
+ * Send text message
364
+ * @param {number|string} chatId
365
+ * @param {string} text
366
+ * @param {object} [extra]
367
+ */
368
+ sendMessage(chatId, text, extra = {}) {
369
+ return this.call('sendMessage', { chat_id: chatId, text, ...extra });
370
+ }
371
+
372
+ /**
373
+ * Forward a message
374
+ * @param {number|string} chatId
375
+ * @param {number|string} fromChatId
376
+ * @param {number} messageId
377
+ * @param {object} [extra]
378
+ */
379
+ forwardMessage(chatId, fromChatId, messageId, extra = {}) {
380
+ return this.call('forwardMessage', {
381
+ chat_id: chatId,
382
+ from_chat_id: fromChatId,
383
+ message_id: messageId,
384
+ ...extra,
385
+ });
386
+ }
387
+
388
+ /**
389
+ * Forward multiple messages
390
+ */
391
+ forwardMessages(chatId, fromChatId, messageIds, extra = {}) {
392
+ return this.call('forwardMessages', {
393
+ chat_id: chatId,
394
+ from_chat_id: fromChatId,
395
+ message_ids: messageIds,
396
+ ...extra,
397
+ });
398
+ }
399
+
400
+ /**
401
+ * Copy a message
402
+ */
403
+ copyMessage(chatId, fromChatId, messageId, extra = {}) {
404
+ return this.call('copyMessage', {
405
+ chat_id: chatId,
406
+ from_chat_id: fromChatId,
407
+ message_id: messageId,
408
+ ...extra,
409
+ });
410
+ }
411
+
412
+ /**
413
+ * Copy multiple messages
414
+ */
415
+ copyMessages(chatId, fromChatId, messageIds, extra = {}) {
416
+ return this.call('copyMessages', {
417
+ chat_id: chatId,
418
+ from_chat_id: fromChatId,
419
+ message_ids: messageIds,
420
+ ...extra,
421
+ });
422
+ }
423
+
424
+ /**
425
+ * Send photo
426
+ */
427
+ sendPhoto(chatId, photo, extra = {}) {
428
+ return this.call('sendPhoto', { chat_id: chatId, photo, ...extra });
429
+ }
430
+
431
+ /**
432
+ * Send audio
433
+ */
434
+ sendAudio(chatId, audio, extra = {}) {
435
+ return this.call('sendAudio', { chat_id: chatId, audio, ...extra });
436
+ }
437
+
438
+ /**
439
+ * Send document
440
+ */
441
+ sendDocument(chatId, document, extra = {}) {
442
+ return this.call('sendDocument', { chat_id: chatId, document, ...extra });
443
+ }
444
+
445
+ /**
446
+ * Send video
447
+ */
448
+ sendVideo(chatId, video, extra = {}) {
449
+ return this.call('sendVideo', { chat_id: chatId, video, ...extra });
450
+ }
451
+
452
+ /**
453
+ * Send animation / GIF
454
+ */
455
+ sendAnimation(chatId, animation, extra = {}) {
456
+ return this.call('sendAnimation', { chat_id: chatId, animation, ...extra });
457
+ }
458
+
459
+ /**
460
+ * Send voice note
461
+ */
462
+ sendVoice(chatId, voice, extra = {}) {
463
+ return this.call('sendVoice', { chat_id: chatId, voice, ...extra });
464
+ }
465
+
466
+ /**
467
+ * Send video note (round video)
468
+ */
469
+ sendVideoNote(chatId, videoNote, extra = {}) {
470
+ return this.call('sendVideoNote', { chat_id: chatId, video_note: videoNote, ...extra });
471
+ }
472
+
473
+ /**
474
+ * Send media group (album)
475
+ */
476
+ sendMediaGroup(chatId, media, extra = {}) {
477
+ return this.call('sendMediaGroup', { chat_id: chatId, media, ...extra });
478
+ }
479
+
480
+ /**
481
+ * Send location
482
+ */
483
+ sendLocation(chatId, latitude, longitude, extra = {}) {
484
+ return this.call('sendLocation', { chat_id: chatId, latitude, longitude, ...extra });
485
+ }
486
+
487
+ /**
488
+ * Edit live location
489
+ */
490
+ editMessageLiveLocation(latitude, longitude, extra = {}) {
491
+ return this.call('editMessageLiveLocation', { latitude, longitude, ...extra });
492
+ }
493
+
494
+ /**
495
+ * Stop live location
496
+ */
497
+ stopMessageLiveLocation(extra = {}) {
498
+ return this.call('stopMessageLiveLocation', extra);
499
+ }
500
+
501
+ /**
502
+ * Send venue
503
+ */
504
+ sendVenue(chatId, latitude, longitude, title, address, extra = {}) {
505
+ return this.call('sendVenue', {
506
+ chat_id: chatId,
507
+ latitude,
508
+ longitude,
509
+ title,
510
+ address,
511
+ ...extra,
512
+ });
513
+ }
514
+
515
+ /**
516
+ * Send phone contact
517
+ */
518
+ sendContact(chatId, phoneNumber, firstName, extra = {}) {
519
+ return this.call('sendContact', {
520
+ chat_id: chatId,
521
+ phone_number: phoneNumber,
522
+ first_name: firstName,
523
+ ...extra,
524
+ });
525
+ }
526
+
527
+ /**
528
+ * Send poll
529
+ */
530
+ sendPoll(chatId, question, options, extra = {}) {
531
+ return this.call('sendPoll', { chat_id: chatId, question, options, ...extra });
532
+ }
533
+
534
+ /**
535
+ * Send dice
536
+ */
537
+ sendDice(chatId, extra = {}) {
538
+ return this.call('sendDice', { chat_id: chatId, ...extra });
539
+ }
540
+
541
+ /**
542
+ * Send chat action (typing, upload_photo, record_video, etc.)
543
+ */
544
+ sendChatAction(chatId, action, extra = {}) {
545
+ return this.call('sendChatAction', { chat_id: chatId, action, ...extra });
546
+ }
547
+
548
+ /**
549
+ * Set message reaction
550
+ */
551
+ setMessageReaction(chatId, messageId, reaction, extra = {}) {
552
+ const formattedReaction = Array.isArray(reaction)
553
+ ? reaction.map((r) => (typeof r === 'string' ? { type: 'emoji', emoji: r } : r))
554
+ : typeof reaction === 'string'
555
+ ? [{ type: 'emoji', emoji: reaction }]
556
+ : reaction;
557
+ return this.call('setMessageReaction', {
558
+ chat_id: chatId,
559
+ message_id: messageId,
560
+ reaction: formattedReaction,
561
+ ...extra,
562
+ });
563
+ }
564
+
565
+ /**
566
+ * Get user profile photos
567
+ */
568
+ getUserProfilePhotos(userId, extra = {}) {
569
+ return this.call('getUserProfilePhotos', { user_id: userId, ...extra });
570
+ }
571
+
572
+ /**
573
+ * Get file info
574
+ */
575
+ getFile(fileId) {
576
+ return this.call('getFile', { file_id: fileId });
577
+ }
578
+
579
+ /**
580
+ * Helper to get direct download URL of a file
581
+ */
582
+ async getFileLink(fileId) {
583
+ if (typeof fileId === 'object' && fileId.file_path) {
584
+ return `${this.getFileBaseUrl()}/${fileId.file_path}`;
585
+ }
586
+ const file = await this.getFile(fileId);
587
+ return `${this.getFileBaseUrl()}/${file.file_path}`;
588
+ }
589
+
590
+ /**
591
+ * Ban chat member
592
+ */
593
+ banChatMember(chatId, userId, extra = {}) {
594
+ return this.call('banChatMember', { chat_id: chatId, user_id: userId, ...extra });
595
+ }
596
+
597
+ /**
598
+ * Unban chat member
599
+ */
600
+ unbanChatMember(chatId, userId, extra = {}) {
601
+ return this.call('unbanChatMember', { chat_id: chatId, user_id: userId, ...extra });
602
+ }
603
+
604
+ /**
605
+ * Restrict chat member
606
+ */
607
+ restrictChatMember(chatId, userId, permissions, extra = {}) {
608
+ return this.call('restrictChatMember', {
609
+ chat_id: chatId,
610
+ user_id: userId,
611
+ permissions,
612
+ ...extra,
613
+ });
614
+ }
615
+
616
+ /**
617
+ * Promote chat member
618
+ */
619
+ promoteChatMember(chatId, userId, rights = {}) {
620
+ return this.call('promoteChatMember', { chat_id: chatId, user_id: userId, ...rights });
621
+ }
622
+
623
+ /**
624
+ * Set chat administrator custom title
625
+ */
626
+ setChatAdministratorCustomTitle(chatId, userId, customTitle) {
627
+ return this.call('setChatAdministratorCustomTitle', {
628
+ chat_id: chatId,
629
+ user_id: userId,
630
+ custom_title: customTitle,
631
+ });
632
+ }
633
+
634
+ /**
635
+ * Ban chat sender chat
636
+ */
637
+ banChatSenderChat(chatId, senderChatId) {
638
+ return this.call('banChatSenderChat', { chat_id: chatId, sender_chat_id: senderChatId });
639
+ }
640
+
641
+ /**
642
+ * Unban chat sender chat
643
+ */
644
+ unbanChatSenderChat(chatId, senderChatId) {
645
+ return this.call('unbanChatSenderChat', { chat_id: chatId, sender_chat_id: senderChatId });
646
+ }
647
+
648
+ /**
649
+ * Set chat permissions
650
+ */
651
+ setChatPermissions(chatId, permissions, extra = {}) {
652
+ return this.call('setChatPermissions', { chat_id: chatId, permissions, ...extra });
653
+ }
654
+
655
+ /**
656
+ * Export chat invite link
657
+ */
658
+ exportChatInviteLink(chatId) {
659
+ return this.call('exportChatInviteLink', { chat_id: chatId });
660
+ }
661
+
662
+ /**
663
+ * Create chat invite link
664
+ */
665
+ createChatInviteLink(chatId, extra = {}) {
666
+ return this.call('createChatInviteLink', { chat_id: chatId, ...extra });
667
+ }
668
+
669
+ /**
670
+ * Edit chat invite link
671
+ */
672
+ editChatInviteLink(chatId, inviteLink, extra = {}) {
673
+ return this.call('editChatInviteLink', { chat_id: chatId, invite_link: inviteLink, ...extra });
674
+ }
675
+
676
+ /**
677
+ * Revoke chat invite link
678
+ */
679
+ revokeChatInviteLink(chatId, inviteLink) {
680
+ return this.call('revokeChatInviteLink', { chat_id: chatId, invite_link: inviteLink });
681
+ }
682
+
683
+ /**
684
+ * Approve chat join request
685
+ */
686
+ approveChatJoinRequest(chatId, userId) {
687
+ return this.call('approveChatJoinRequest', { chat_id: chatId, user_id: userId });
688
+ }
689
+
690
+ /**
691
+ * Decline chat join request
692
+ */
693
+ declineChatJoinRequest(chatId, userId) {
694
+ return this.call('declineChatJoinRequest', { chat_id: chatId, user_id: userId });
695
+ }
696
+
697
+ /**
698
+ * Set chat photo
699
+ */
700
+ setChatPhoto(chatId, photo) {
701
+ return this.call('setChatPhoto', { chat_id: chatId, photo });
702
+ }
703
+
704
+ /**
705
+ * Delete chat photo
706
+ */
707
+ deleteChatPhoto(chatId) {
708
+ return this.call('deleteChatPhoto', { chat_id: chatId });
709
+ }
710
+
711
+ /**
712
+ * Set chat title
713
+ */
714
+ setChatTitle(chatId, title) {
715
+ return this.call('setChatTitle', { chat_id: chatId, title });
716
+ }
717
+
718
+ /**
719
+ * Set chat description
720
+ */
721
+ setChatDescription(chatId, description) {
722
+ return this.call('setChatDescription', { chat_id: chatId, description });
723
+ }
724
+
725
+ /**
726
+ * Pin message in chat
727
+ */
728
+ pinChatMessage(chatId, messageId, extra = {}) {
729
+ return this.call('pinChatMessage', { chat_id: chatId, message_id: messageId, ...extra });
730
+ }
731
+
732
+ /**
733
+ * Unpin message in chat
734
+ */
735
+ unpinChatMessage(chatId, messageId, extra = {}) {
736
+ return this.call('unpinChatMessage', { chat_id: chatId, message_id: messageId, ...extra });
737
+ }
738
+
739
+ /**
740
+ * Unpin all chat messages
741
+ */
742
+ unpinAllChatMessages(chatId) {
743
+ return this.call('unpinAllChatMessages', { chat_id: chatId });
744
+ }
745
+
746
+ /**
747
+ * Leave chat
748
+ */
749
+ leaveChat(chatId) {
750
+ return this.call('leaveChat', { chat_id: chatId });
751
+ }
752
+
753
+ /**
754
+ * Get chat info
755
+ */
756
+ getChat(chatId) {
757
+ return this.call('getChat', { chat_id: chatId });
758
+ }
759
+
760
+ /**
761
+ * Get chat administrators
762
+ */
763
+ getChatAdministrators(chatId) {
764
+ return this.call('getChatAdministrators', { chat_id: chatId });
765
+ }
766
+
767
+ /**
768
+ * Get chat member count
769
+ */
770
+ getChatMemberCount(chatId) {
771
+ return this.call('getChatMemberCount', { chat_id: chatId });
772
+ }
773
+
774
+ /**
775
+ * Get chat member info
776
+ */
777
+ getChatMember(chatId, userId) {
778
+ return this.call('getChatMember', { chat_id: chatId, user_id: userId });
779
+ }
780
+
781
+ /**
782
+ * Set chat sticker set
783
+ */
784
+ setChatStickerSet(chatId, stickerSetName) {
785
+ return this.call('setChatStickerSet', { chat_id: chatId, sticker_set_name: stickerSetName });
786
+ }
787
+
788
+ /**
789
+ * Delete chat sticker set
790
+ */
791
+ deleteChatStickerSet(chatId) {
792
+ return this.call('deleteChatStickerSet', { chat_id: chatId });
793
+ }
794
+
795
+ /**
796
+ * Answer callback query
797
+ */
798
+ answerCallbackQuery(callbackQueryId, extra = {}) {
799
+ return this.call('answerCallbackQuery', {
800
+ callback_query_id: callbackQueryId,
801
+ ...extra,
802
+ });
803
+ }
804
+
805
+ /**
806
+ * Edit message text
807
+ */
808
+ editMessageText(chatId, messageId, inlineMessageId, text, extra = {}) {
809
+ const payload = { text, ...extra };
810
+ if (chatId) payload.chat_id = chatId;
811
+ if (messageId) payload.message_id = messageId;
812
+ if (inlineMessageId) payload.inline_message_id = inlineMessageId;
813
+ return this.call('editMessageText', payload);
814
+ }
815
+
816
+ /**
817
+ * Edit message caption
818
+ */
819
+ editMessageCaption(chatId, messageId, inlineMessageId, caption, extra = {}) {
820
+ const payload = { caption, ...extra };
821
+ if (chatId) payload.chat_id = chatId;
822
+ if (messageId) payload.message_id = messageId;
823
+ if (inlineMessageId) payload.inline_message_id = inlineMessageId;
824
+ return this.call('editMessageCaption', payload);
825
+ }
826
+
827
+ /**
828
+ * Edit message media
829
+ */
830
+ editMessageMedia(chatId, messageId, inlineMessageId, media, extra = {}) {
831
+ const payload = { media, ...extra };
832
+ if (chatId) payload.chat_id = chatId;
833
+ if (messageId) payload.message_id = messageId;
834
+ if (inlineMessageId) payload.inline_message_id = inlineMessageId;
835
+ return this.call('editMessageMedia', payload);
836
+ }
837
+
838
+ /**
839
+ * Edit message reply markup
840
+ */
841
+ editMessageReplyMarkup(chatId, messageId, inlineMessageId, replyMarkup, extra = {}) {
842
+ const payload = { reply_markup: replyMarkup, ...extra };
843
+ if (chatId) payload.chat_id = chatId;
844
+ if (messageId) payload.message_id = messageId;
845
+ if (inlineMessageId) payload.inline_message_id = inlineMessageId;
846
+ return this.call('editMessageReplyMarkup', payload);
847
+ }
848
+
849
+ /**
850
+ * Stop poll
851
+ */
852
+ stopPoll(chatId, messageId, extra = {}) {
853
+ return this.call('stopPoll', { chat_id: chatId, message_id: messageId, ...extra });
854
+ }
855
+
856
+ /**
857
+ * Delete single message
858
+ */
859
+ deleteMessage(chatId, messageId) {
860
+ return this.call('deleteMessage', { chat_id: chatId, message_id: messageId });
861
+ }
862
+
863
+ /**
864
+ * Delete multiple messages
865
+ */
866
+ deleteMessages(chatId, messageIds) {
867
+ return this.call('deleteMessages', { chat_id: chatId, message_ids: messageIds });
868
+ }
869
+
870
+ /**
871
+ * Answer inline query
872
+ */
873
+ answerInlineQuery(inlineQueryId, results, extra = {}) {
874
+ return this.call('answerInlineQuery', {
875
+ inline_query_id: inlineQueryId,
876
+ results,
877
+ ...extra,
878
+ });
879
+ }
880
+
881
+ /**
882
+ * Answer web app query
883
+ */
884
+ answerWebAppQuery(webAppQueryId, result) {
885
+ return this.call('answerWebAppQuery', {
886
+ web_app_query_id: webAppQueryId,
887
+ result,
888
+ });
889
+ }
890
+
891
+ /**
892
+ * Set webhook
893
+ */
894
+ setWebhook(url, extra = {}) {
895
+ return this.call('setWebhook', { url, ...extra });
896
+ }
897
+
898
+ /**
899
+ * Delete webhook
900
+ */
901
+ deleteWebhook(extra = {}) {
902
+ return this.call('deleteWebhook', extra);
903
+ }
904
+
905
+ /**
906
+ * Get webhook info
907
+ */
908
+ getWebhookInfo() {
909
+ return this.call('getWebhookInfo');
910
+ }
911
+
912
+ /**
913
+ * Get updates via polling
914
+ */
915
+ getUpdates(offset, limit, timeout, allowedUpdates) {
916
+ const payload = {};
917
+ if (offset !== undefined) payload.offset = offset;
918
+ if (limit !== undefined) payload.limit = limit;
919
+ if (timeout !== undefined) payload.timeout = timeout;
920
+ if (allowedUpdates !== undefined) payload.allowed_updates = allowedUpdates;
921
+ return this.call('getUpdates', payload);
922
+ }
923
+
924
+ /**
925
+ * Bot commands and metadata
926
+ */
927
+ setMyCommands(commands, extra = {}) {
928
+ return this.call('setMyCommands', { commands, ...extra });
929
+ }
930
+
931
+ deleteMyCommands(extra = {}) {
932
+ return this.call('deleteMyCommands', extra);
933
+ }
934
+
935
+ getMyCommands(extra = {}) {
936
+ return this.call('getMyCommands', extra);
937
+ }
938
+
939
+ setMyName(name, extra = {}) {
940
+ return this.call('setMyName', { name, ...extra });
941
+ }
942
+
943
+ getMyName(extra = {}) {
944
+ return this.call('getMyName', extra);
945
+ }
946
+
947
+ setMyDescription(description, extra = {}) {
948
+ return this.call('setMyDescription', { description, ...extra });
949
+ }
950
+
951
+ getMyDescription(extra = {}) {
952
+ return this.call('getMyDescription', extra);
953
+ }
954
+
955
+ setMyShortDescription(shortDescription, extra = {}) {
956
+ return this.call('setMyShortDescription', { short_description: shortDescription, ...extra });
957
+ }
958
+
959
+ getMyShortDescription(extra = {}) {
960
+ return this.call('getMyShortDescription', extra);
961
+ }
962
+
963
+ setChatMenuButton(extra = {}) {
964
+ return this.call('setChatMenuButton', extra);
965
+ }
966
+
967
+ getChatMenuButton(extra = {}) {
968
+ return this.call('getChatMenuButton', extra);
969
+ }
970
+
971
+ setMyDefaultAdministratorRights(extra = {}) {
972
+ return this.call('setMyDefaultAdministratorRights', extra);
973
+ }
974
+
975
+ getMyDefaultAdministratorRights(extra = {}) {
976
+ return this.call('getMyDefaultAdministratorRights', extra);
977
+ }
978
+
979
+ // ==========================================
980
+ // Forum Topics Management (Telegram API)
981
+ // ==========================================
982
+
983
+ /**
984
+ * Create a topic in a forum supergroup chat
985
+ * @param {number|string} chatId
986
+ * @param {string} name
987
+ * @param {object} [extra] - icon_color, icon_custom_emoji_id
988
+ */
989
+ createForumTopic(chatId, name, extra = {}) {
990
+ return this.call('createForumTopic', { chat_id: chatId, name, ...extra });
991
+ }
992
+
993
+ /**
994
+ * Edit name and icon of a forum topic
995
+ * @param {number|string} chatId
996
+ * @param {number} messageThreadId
997
+ * @param {object} [extra] - name, icon_custom_emoji_id
998
+ */
999
+ editForumTopic(chatId, messageThreadId, extra = {}) {
1000
+ return this.call('editForumTopic', {
1001
+ chat_id: chatId,
1002
+ message_thread_id: messageThreadId,
1003
+ ...extra,
1004
+ });
1005
+ }
1006
+
1007
+ /**
1008
+ * Close an open topic in a forum supergroup chat
1009
+ * @param {number|string} chatId
1010
+ * @param {number} messageThreadId
1011
+ */
1012
+ closeForumTopic(chatId, messageThreadId) {
1013
+ return this.call('closeForumTopic', {
1014
+ chat_id: chatId,
1015
+ message_thread_id: messageThreadId,
1016
+ });
1017
+ }
1018
+
1019
+ /**
1020
+ * Reopen a closed topic in a forum supergroup chat
1021
+ * @param {number|string} chatId
1022
+ * @param {number} messageThreadId
1023
+ */
1024
+ reopenForumTopic(chatId, messageThreadId) {
1025
+ return this.call('reopenForumTopic', {
1026
+ chat_id: chatId,
1027
+ message_thread_id: messageThreadId,
1028
+ });
1029
+ }
1030
+
1031
+ /**
1032
+ * Delete a forum topic along with all its messages
1033
+ * @param {number|string} chatId
1034
+ * @param {number} messageThreadId
1035
+ */
1036
+ deleteForumTopic(chatId, messageThreadId) {
1037
+ return this.call('deleteForumTopic', {
1038
+ chat_id: chatId,
1039
+ message_thread_id: messageThreadId,
1040
+ });
1041
+ }
1042
+
1043
+ /**
1044
+ * Unpin all messages in a forum topic
1045
+ * @param {number|string} chatId
1046
+ * @param {number} messageThreadId
1047
+ */
1048
+ unpinAllForumTopicMessages(chatId, messageThreadId) {
1049
+ return this.call('unpinAllForumTopicMessages', {
1050
+ chat_id: chatId,
1051
+ message_thread_id: messageThreadId,
1052
+ });
1053
+ }
1054
+
1055
+ /**
1056
+ * Edit General forum topic
1057
+ * @param {number|string} chatId
1058
+ * @param {string} name
1059
+ */
1060
+ editGeneralForumTopic(chatId, name) {
1061
+ return this.call('editGeneralForumTopic', { chat_id: chatId, name });
1062
+ }
1063
+
1064
+ /**
1065
+ * Close General forum topic
1066
+ * @param {number|string} chatId
1067
+ */
1068
+ closeGeneralForumTopic(chatId) {
1069
+ return this.call('closeGeneralForumTopic', { chat_id: chatId });
1070
+ }
1071
+
1072
+ /**
1073
+ * Reopen General forum topic
1074
+ * @param {number|string} chatId
1075
+ */
1076
+ reopenGeneralForumTopic(chatId) {
1077
+ return this.call('reopenGeneralForumTopic', { chat_id: chatId });
1078
+ }
1079
+
1080
+ /**
1081
+ * Hide General forum topic
1082
+ * @param {number|string} chatId
1083
+ */
1084
+ hideGeneralForumTopic(chatId) {
1085
+ return this.call('hideGeneralForumTopic', { chat_id: chatId });
1086
+ }
1087
+
1088
+ /**
1089
+ * Unhide General forum topic
1090
+ * @param {number|string} chatId
1091
+ */
1092
+ unhideGeneralForumTopic(chatId) {
1093
+ return this.call('unhideGeneralForumTopic', { chat_id: chatId });
1094
+ }
1095
+
1096
+ // ==========================================
1097
+ // Telegram Stars & Payments
1098
+ // ==========================================
1099
+
1100
+ /**
1101
+ * Send invoice (Supports Telegram Stars XTR and standard currencies)
1102
+ * @param {number|string} chatId
1103
+ * @param {string} title
1104
+ * @param {string} description
1105
+ * @param {string} payload
1106
+ * @param {string} currency - e.g. 'XTR' for Telegram Stars, or 'USD', 'EUR', 'IDR'
1107
+ * @param {Array<{label: string, amount: number}>} prices
1108
+ * @param {object} [extra]
1109
+ */
1110
+ sendInvoice(chatId, title, description, payload, currency, prices, extra = {}) {
1111
+ return this.call('sendInvoice', {
1112
+ chat_id: chatId,
1113
+ title,
1114
+ description,
1115
+ payload,
1116
+ currency,
1117
+ prices,
1118
+ ...extra,
1119
+ });
1120
+ }
1121
+
1122
+ /**
1123
+ * Create an invoice link that can be paid in Telegram
1124
+ * @param {string} title
1125
+ * @param {string} description
1126
+ * @param {string} payload
1127
+ * @param {string} currency - e.g. 'XTR'
1128
+ * @param {Array<{label: string, amount: number}>} prices
1129
+ * @param {object} [extra]
1130
+ * @returns {Promise<string>}
1131
+ */
1132
+ createInvoiceLink(title, description, payload, currency, prices, extra = {}) {
1133
+ return this.call('createInvoiceLink', {
1134
+ title,
1135
+ description,
1136
+ payload,
1137
+ currency,
1138
+ prices,
1139
+ ...extra,
1140
+ });
1141
+ }
1142
+
1143
+ /**
1144
+ * Answer shipping query
1145
+ * @param {string} shippingQueryId
1146
+ * @param {boolean} ok
1147
+ * @param {object} [extra]
1148
+ */
1149
+ answerShippingQuery(shippingQueryId, ok, extra = {}) {
1150
+ return this.call('answerShippingQuery', {
1151
+ shipping_query_id: shippingQueryId,
1152
+ ok: Boolean(ok),
1153
+ ...extra,
1154
+ });
1155
+ }
1156
+
1157
+ /**
1158
+ * Answer pre checkout query
1159
+ * @param {string} preCheckoutQueryId
1160
+ * @param {boolean} ok
1161
+ * @param {string} [errorMessage]
1162
+ */
1163
+ answerPreCheckoutQuery(preCheckoutQueryId, ok, errorMessage = undefined) {
1164
+ const payload = {
1165
+ pre_checkout_query_id: preCheckoutQueryId,
1166
+ ok: Boolean(ok),
1167
+ };
1168
+ if (!ok && errorMessage) {
1169
+ payload.error_message = errorMessage;
1170
+ }
1171
+ return this.call('answerPreCheckoutQuery', payload);
1172
+ }
1173
+
1174
+ /**
1175
+ * Get transactions of the bot in Telegram Stars
1176
+ * @param {object} [extra] - offset, limit
1177
+ */
1178
+ getStarTransactions(extra = {}) {
1179
+ return this.call('getStarTransactions', extra);
1180
+ }
1181
+
1182
+ /**
1183
+ * Refund a successful payment in Telegram Stars
1184
+ * @param {number} userId
1185
+ * @param {string} telegramPaymentChargeId
1186
+ */
1187
+ refundStarPayment(userId, telegramPaymentChargeId) {
1188
+ return this.call('refundStarPayment', {
1189
+ user_id: userId,
1190
+ telegram_payment_charge_id: telegramPaymentChargeId,
1191
+ });
1192
+ }
1193
+
1194
+ /**
1195
+ * Edit user Star subscription status
1196
+ * @param {number} userId
1197
+ * @param {string} telegramPaymentChargeId
1198
+ * @param {boolean} isCanceled
1199
+ */
1200
+ editUserStarSubscription(userId, telegramPaymentChargeId, isCanceled) {
1201
+ return this.call('editUserStarSubscription', {
1202
+ user_id: userId,
1203
+ telegram_payment_charge_id: telegramPaymentChargeId,
1204
+ is_canceled: Boolean(isCanceled),
1205
+ });
1206
+ }
1207
+
1208
+ // ==========================================
1209
+ // Paid Media (Telegram Stars)
1210
+ // ==========================================
1211
+
1212
+ /**
1213
+ * Send paid media (photos/videos requiring Telegram Stars to unlock)
1214
+ * @param {number|string} chatId
1215
+ * @param {number} starCount - Number of Telegram Stars required
1216
+ * @param {Array<object>} media - Array of InputPaidMediaPhoto / InputPaidMediaVideo
1217
+ * @param {object} [extra]
1218
+ */
1219
+ sendPaidMedia(chatId, starCount, media, extra = {}) {
1220
+ return this.call('sendPaidMedia', {
1221
+ chat_id: chatId,
1222
+ star_count: starCount,
1223
+ media,
1224
+ ...extra,
1225
+ });
1226
+ }
1227
+
1228
+ /**
1229
+ * Edit message paid media
1230
+ * @param {number|string} chatId
1231
+ * @param {number} [messageId]
1232
+ * @param {string} [inlineMessageId]
1233
+ * @param {Array<object>} media
1234
+ * @param {object} [extra]
1235
+ */
1236
+ editMessagePaidMedia(chatId, messageId, inlineMessageId, media, extra = {}) {
1237
+ const payload = { media, ...extra };
1238
+ if (chatId) payload.chat_id = chatId;
1239
+ if (messageId) payload.message_id = messageId;
1240
+ if (inlineMessageId) payload.inline_message_id = inlineMessageId;
1241
+ return this.call('editMessagePaidMedia', payload);
1242
+ }
1243
+
1244
+ // ==========================================
1245
+ // Gifts & Verifications
1246
+ // ==========================================
1247
+
1248
+ /**
1249
+ * Send a gift to a given user
1250
+ * @param {number} userId
1251
+ * @param {string} giftId
1252
+ * @param {object} [extra] - text, text_parse_mode, text_entities, pay_for_upgrade
1253
+ */
1254
+ sendGift(userId, giftId, extra = {}) {
1255
+ return this.call('sendGift', {
1256
+ user_id: userId,
1257
+ gift_id: giftId,
1258
+ ...extra,
1259
+ });
1260
+ }
1261
+
1262
+ /**
1263
+ * Get list of gifts that can be sent by the bot to users
1264
+ */
1265
+ getAvailableGifts() {
1266
+ return this.call('getAvailableGifts');
1267
+ }
1268
+
1269
+ /**
1270
+ * Get gifts received by a user
1271
+ * @param {number} userId
1272
+ * @param {object} [extra] - offset, limit
1273
+ */
1274
+ getUserGifts(userId, extra = {}) {
1275
+ return this.call('getUserGifts', { user_id: userId, ...extra });
1276
+ }
1277
+
1278
+ /**
1279
+ * Verify a user on behalf of the organization
1280
+ * @param {number} userId
1281
+ * @param {string} [customDescription='']
1282
+ */
1283
+ verifyUser(userId, customDescription = '') {
1284
+ return this.call('verifyUser', {
1285
+ user_id: userId,
1286
+ custom_description: customDescription,
1287
+ });
1288
+ }
1289
+
1290
+ /**
1291
+ * Verify a chat on behalf of the organization
1292
+ * @param {number|string} chatId
1293
+ * @param {string} [customDescription='']
1294
+ */
1295
+ verifyChat(chatId, customDescription = '') {
1296
+ return this.call('verifyChat', {
1297
+ chat_id: chatId,
1298
+ custom_description: customDescription,
1299
+ });
1300
+ }
1301
+
1302
+ /**
1303
+ * Remove verification from a user
1304
+ * @param {number} userId
1305
+ */
1306
+ removeUserVerification(userId) {
1307
+ return this.call('removeUserVerification', { user_id: userId });
1308
+ }
1309
+
1310
+ /**
1311
+ * Remove verification from a chat
1312
+ * @param {number|string} chatId
1313
+ */
1314
+ removeChatVerification(chatId) {
1315
+ return this.call('removeChatVerification', { chat_id: chatId });
1316
+ }
1317
+
1318
+ // ==========================================
1319
+ // Telegram Business API
1320
+ // ==========================================
1321
+
1322
+ /**
1323
+ * Get information about the connection of the bot with a business account
1324
+ * @param {string} businessConnectionId
1325
+ */
1326
+ getBusinessConnection(businessConnectionId) {
1327
+ return this.call('getBusinessConnection', {
1328
+ business_connection_id: businessConnectionId,
1329
+ });
1330
+ }
1331
+
1332
+ // ==========================================
1333
+ // Boosts & Prepared Inline Messages
1334
+ // ==========================================
1335
+
1336
+ /**
1337
+ * Get list of boosts added to a chat by a user
1338
+ * @param {number|string} chatId
1339
+ * @param {number} userId
1340
+ */
1341
+ getUserChatBoosts(chatId, userId) {
1342
+ return this.call('getUserChatBoosts', {
1343
+ chat_id: chatId,
1344
+ user_id: userId,
1345
+ });
1346
+ }
1347
+
1348
+ /**
1349
+ * Save prepared inline message for mini-app sharing
1350
+ * @param {number} userId
1351
+ * @param {object} result - InlineQueryResult
1352
+ * @param {object} [extra] - allow_user_chats, allow_bot_chats, allow_group_chats, allow_channel_chats
1353
+ */
1354
+ savePreparedInlineMessage(userId, result, extra = {}) {
1355
+ return this.call('savePreparedInlineMessage', {
1356
+ user_id: userId,
1357
+ result,
1358
+ ...extra,
1359
+ });
1360
+ }
1361
+
1362
+ // ==========================================
1363
+ // Stickers & Custom Emojis
1364
+ // ==========================================
1365
+
1366
+ /**
1367
+ * Send a sticker
1368
+ * @param {number|string} chatId
1369
+ * @param {any} sticker - file_id, url, path, Buffer, Stream
1370
+ * @param {object} [extra]
1371
+ */
1372
+ sendSticker(chatId, sticker, extra = {}) {
1373
+ return this.call('sendSticker', { chat_id: chatId, sticker, ...extra });
1374
+ }
1375
+
1376
+ /**
1377
+ * Get sticker set by name
1378
+ * @param {string} name
1379
+ */
1380
+ getStickerSet(name) {
1381
+ return this.call('getStickerSet', { name });
1382
+ }
1383
+
1384
+ /**
1385
+ * Get custom emoji stickers by IDs
1386
+ * @param {Array<string>} customEmojiIds
1387
+ */
1388
+ getCustomEmojiStickers(customEmojiIds) {
1389
+ return this.call('getCustomEmojiStickers', {
1390
+ custom_emoji_ids: customEmojiIds,
1391
+ });
1392
+ }
1393
+
1394
+ /**
1395
+ * Upload sticker file
1396
+ * @param {number} userId
1397
+ * @param {any} sticker
1398
+ * @param {'static'|'animated'|'video'} stickerFormat
1399
+ */
1400
+ uploadStickerFile(userId, sticker, stickerFormat) {
1401
+ return this.call('uploadStickerFile', {
1402
+ user_id: userId,
1403
+ sticker,
1404
+ sticker_format: stickerFormat,
1405
+ });
1406
+ }
1407
+
1408
+ /**
1409
+ * Create new sticker set
1410
+ * @param {number} userId
1411
+ * @param {string} name
1412
+ * @param {string} title
1413
+ * @param {Array<object>} stickers
1414
+ * @param {object} [extra]
1415
+ */
1416
+ createNewStickerSet(userId, name, title, stickers, extra = {}) {
1417
+ return this.call('createNewStickerSet', {
1418
+ user_id: userId,
1419
+ name,
1420
+ title,
1421
+ stickers,
1422
+ ...extra,
1423
+ });
1424
+ }
1425
+
1426
+ /**
1427
+ * Add sticker to existing set
1428
+ * @param {number} userId
1429
+ * @param {string} name
1430
+ * @param {object} sticker
1431
+ */
1432
+ addStickerToSet(userId, name, sticker) {
1433
+ return this.call('addStickerToSet', {
1434
+ user_id: userId,
1435
+ name,
1436
+ sticker,
1437
+ });
1438
+ }
1439
+
1440
+ /**
1441
+ * Set sticker position in set
1442
+ * @param {string} sticker
1443
+ * @param {number} position
1444
+ */
1445
+ setStickerPositionInSet(sticker, position) {
1446
+ return this.call('setStickerPositionInSet', {
1447
+ sticker,
1448
+ position,
1449
+ });
1450
+ }
1451
+
1452
+ /**
1453
+ * Delete sticker from set
1454
+ * @param {string} sticker
1455
+ */
1456
+ deleteStickerFromSet(sticker) {
1457
+ return this.call('deleteStickerFromSet', { sticker });
1458
+ }
1459
+
1460
+ /**
1461
+ * Set sticker set title
1462
+ * @param {string} name
1463
+ * @param {string} title
1464
+ */
1465
+ setStickerSetTitle(name, title) {
1466
+ return this.call('setStickerSetTitle', { name, title });
1467
+ }
1468
+
1469
+ /**
1470
+ * Delete sticker set
1471
+ * @param {string} name
1472
+ */
1473
+ deleteStickerSet(name) {
1474
+ return this.call('deleteStickerSet', { name });
1475
+ }
1476
+
1477
+ // ==========================================
1478
+ // Games & Passport
1479
+ // ==========================================
1480
+
1481
+ /**
1482
+ * Send game
1483
+ * @param {number|string} chatId
1484
+ * @param {string} gameShortName
1485
+ * @param {object} [extra]
1486
+ */
1487
+ sendGame(chatId, gameShortName, extra = {}) {
1488
+ return this.call('sendGame', {
1489
+ chat_id: chatId,
1490
+ game_short_name: gameShortName,
1491
+ ...extra,
1492
+ });
1493
+ }
1494
+
1495
+ /**
1496
+ * Set user score in game
1497
+ * @param {number} userId
1498
+ * @param {number} score
1499
+ * @param {object} [extra]
1500
+ */
1501
+ setGameScore(userId, score, extra = {}) {
1502
+ return this.call('setGameScore', {
1503
+ user_id: userId,
1504
+ score,
1505
+ ...extra,
1506
+ });
1507
+ }
1508
+
1509
+ /**
1510
+ * Get game high scores
1511
+ * @param {number} userId
1512
+ * @param {object} [extra]
1513
+ */
1514
+ getGameHighScores(userId, extra = {}) {
1515
+ return this.call('getGameHighScores', {
1516
+ user_id: userId,
1517
+ ...extra,
1518
+ });
1519
+ }
1520
+
1521
+ /**
1522
+ * Set passport data errors
1523
+ * @param {number} userId
1524
+ * @param {Array<object>} errors
1525
+ */
1526
+ setPassportDataErrors(userId, errors) {
1527
+ return this.call('setPassportDataErrors', {
1528
+ user_id: userId,
1529
+ errors,
1530
+ });
1531
+ }
1532
+
1533
+ // ==========================================
1534
+ // Bot API 10.3 (August 24, 2026) Updates
1535
+ // ==========================================
1536
+
1537
+ /**
1538
+ * Send a rich message
1539
+ * @param {number|string} chatId
1540
+ * @param {object|Array} richMessage
1541
+ * @param {object} [extra]
1542
+ */
1543
+ sendRichMessage(chatId, richMessage, extra = {}) {
1544
+ const rm = typeof richMessage?.compile === 'function'
1545
+ ? richMessage.compile()
1546
+ : (typeof richMessage?.build === 'function' ? richMessage.build() : richMessage);
1547
+
1548
+ const payload = {
1549
+ chat_id: chatId,
1550
+ rich_message: rm,
1551
+ ...(rm?.reply_markup ? { reply_markup: rm.reply_markup } : {}),
1552
+ ...extra,
1553
+ };
1554
+
1555
+ return this.call('sendRichMessage', payload).catch(async (err) => {
1556
+ // Graceful fallback for environments/servers with standard sendMessage
1557
+ if (
1558
+ err.errorCode === 404 ||
1559
+ err.description?.includes('Method not found') ||
1560
+ err.description?.includes('Unknown method') ||
1561
+ err.description?.includes('Bad Request')
1562
+ ) {
1563
+ const text = rm?.text || (typeof rm === 'string' ? rm : ' ');
1564
+ const parseMode = rm?.parse_mode || extra.parse_mode || 'HTML';
1565
+ const replyMarkup = rm?.reply_markup || extra.reply_markup;
1566
+ return this.sendMessage(chatId, text, {
1567
+ parse_mode: parseMode,
1568
+ ...(replyMarkup ? { reply_markup: replyMarkup } : {}),
1569
+ ...extra,
1570
+ });
1571
+ }
1572
+ throw err;
1573
+ });
1574
+ }
1575
+
1576
+ /**
1577
+ * Send a rich message draft
1578
+ * @param {number|string} chatId
1579
+ * @param {object|Array} draft
1580
+ * @param {object} [extra]
1581
+ */
1582
+ sendRichMessageDraft(chatId, draft, extra = {}) {
1583
+ const d = typeof draft?.compile === 'function'
1584
+ ? draft.compile()
1585
+ : (typeof draft?.build === 'function' ? draft.build() : draft);
1586
+
1587
+ const draftId = extra.draft_id ?? d?.draft_id ?? Math.floor(Math.random() * 2147483647) + 1;
1588
+
1589
+ return this.call('sendRichMessageDraft', {
1590
+ chat_id: chatId,
1591
+ draft_id: draftId,
1592
+ draft: d,
1593
+ ...extra,
1594
+ }).catch(async (err) => {
1595
+ // Fallback: If draft method is not supported, attempt standard sendMessageDraft
1596
+ if (
1597
+ err.errorCode === 404 ||
1598
+ err.description?.includes('Method not found') ||
1599
+ err.description?.includes('Unknown method')
1600
+ ) {
1601
+ const text = d?.text || (typeof d === 'string' ? d : ' ');
1602
+ return this.sendMessageDraft(chatId, text, {
1603
+ draft_id: draftId,
1604
+ ...extra,
1605
+ }).catch(() => null);
1606
+ }
1607
+ throw err;
1608
+ });
1609
+ }
1610
+
1611
+ /**
1612
+ * Edit rich message text
1613
+ * @param {number|string} chatId
1614
+ * @param {number} messageId
1615
+ * @param {object|Array} richMessage
1616
+ * @param {object} [extra]
1617
+ */
1618
+ editRichMessageText(chatId, messageId, richMessage, extra = {}) {
1619
+ const rm = typeof richMessage?.compile === 'function'
1620
+ ? richMessage.compile()
1621
+ : (typeof richMessage?.build === 'function' ? richMessage.build() : richMessage);
1622
+
1623
+ const payload = {
1624
+ chat_id: chatId,
1625
+ message_id: messageId,
1626
+ rich_message: rm,
1627
+ ...(rm?.reply_markup ? { reply_markup: rm.reply_markup } : {}),
1628
+ ...extra,
1629
+ };
1630
+
1631
+ return this.call('editRichMessageText', payload).catch(async (err) => {
1632
+ if (
1633
+ err.errorCode === 404 ||
1634
+ err.description?.includes('Method not found') ||
1635
+ err.description?.includes('Unknown method') ||
1636
+ err.description?.includes('Bad Request')
1637
+ ) {
1638
+ const text = rm?.text || (typeof rm === 'string' ? rm : ' ');
1639
+ const parseMode = rm?.parse_mode || extra.parse_mode || 'HTML';
1640
+ const replyMarkup = rm?.reply_markup || extra.reply_markup;
1641
+ return this.editMessageText(chatId, messageId, undefined, text, {
1642
+ parse_mode: parseMode,
1643
+ ...(replyMarkup ? { reply_markup: replyMarkup } : {}),
1644
+ ...extra,
1645
+ });
1646
+ }
1647
+ throw err;
1648
+ });
1649
+ }
1650
+
1651
+ /**
1652
+ * Edit rich message caption
1653
+ * @param {number|string} chatId
1654
+ * @param {number} messageId
1655
+ * @param {string} caption
1656
+ * @param {object} [extra]
1657
+ */
1658
+ editRichMessageCaption(chatId, messageId, caption, extra = {}) {
1659
+ return this.call('editRichMessageCaption', {
1660
+ chat_id: chatId,
1661
+ message_id: messageId,
1662
+ caption,
1663
+ ...extra,
1664
+ }).catch(async (err) => {
1665
+ if (
1666
+ err.errorCode === 404 ||
1667
+ err.description?.includes('Method not found') ||
1668
+ err.description?.includes('Unknown method') ||
1669
+ err.description?.includes('Bad Request')
1670
+ ) {
1671
+ return this.editMessageCaption(chatId, messageId, undefined, caption, extra);
1672
+ }
1673
+ throw err;
1674
+ });
1675
+ }
1676
+
1677
+ /**
1678
+ * Send an ephemeral message
1679
+ * @param {number|string} chatId
1680
+ * @param {string} text
1681
+ * @param {object|number} [ephemeralParameters={}]
1682
+ * @param {object} [extra={}]
1683
+ */
1684
+ sendEphemeralMessage(chatId, text, ephemeralParameters = {}, extra = {}) {
1685
+ let params = ephemeralParameters;
1686
+ if (typeof ephemeralParameters === 'number') {
1687
+ params = { lifetime: ephemeralParameters };
1688
+ }
1689
+ const payload = {
1690
+ chat_id: chatId,
1691
+ text,
1692
+ ...(params && typeof params === 'object' ? { ephemeral_parameters: params } : {}),
1693
+ ...extra,
1694
+ };
1695
+
1696
+ const autoDeleteSeconds =
1697
+ params?.autoDeleteSeconds ||
1698
+ params?.lifetime ||
1699
+ (typeof ephemeralParameters === 'number' ? ephemeralParameters : null);
1700
+
1701
+ return this.call('sendEphemeralMessage', payload).catch(async (err) => {
1702
+ // Fallback: If method is not supported or rejected by Bot API, send message and auto-delete
1703
+ if (
1704
+ err.errorCode === 404 ||
1705
+ err.description?.includes('Method not found') ||
1706
+ err.description?.includes('Unknown method') ||
1707
+ err.description?.includes('Bad Request')
1708
+ ) {
1709
+ const msg = await this.sendMessage(chatId, text, extra);
1710
+ if (autoDeleteSeconds && msg?.message_id) {
1711
+ setTimeout(() => {
1712
+ this.deleteMessage(chatId, msg.message_id).catch(() => {});
1713
+ }, autoDeleteSeconds * 1000);
1714
+ }
1715
+ return msg;
1716
+ }
1717
+ throw err;
1718
+ });
1719
+ }
1720
+
1721
+ /**
1722
+ * Edit ephemeral message text
1723
+ * @param {number|string} chatId
1724
+ * @param {number} messageId
1725
+ * @param {string} text
1726
+ * @param {object} [extra]
1727
+ */
1728
+ editEphemeralMessageText(chatId, messageId, text, extra = {}) {
1729
+ return this.call('editEphemeralMessageText', {
1730
+ chat_id: chatId,
1731
+ message_id: messageId,
1732
+ text,
1733
+ ...extra,
1734
+ });
1735
+ }
1736
+
1737
+ /**
1738
+ * Edit ephemeral message media
1739
+ * @param {number|string} chatId
1740
+ * @param {number} messageId
1741
+ * @param {object} media
1742
+ * @param {object} [extra]
1743
+ */
1744
+ editEphemeralMessageMedia(chatId, messageId, media, extra = {}) {
1745
+ return this.call('editEphemeralMessageMedia', {
1746
+ chat_id: chatId,
1747
+ message_id: messageId,
1748
+ media,
1749
+ ...extra,
1750
+ });
1751
+ }
1752
+
1753
+ /**
1754
+ * Edit ephemeral message caption
1755
+ * @param {number|string} chatId
1756
+ * @param {number} messageId
1757
+ * @param {string} caption
1758
+ * @param {object} [extra]
1759
+ */
1760
+ editEphemeralMessageCaption(chatId, messageId, caption, extra = {}) {
1761
+ return this.call('editEphemeralMessageCaption', {
1762
+ chat_id: chatId,
1763
+ message_id: messageId,
1764
+ caption,
1765
+ ...extra,
1766
+ });
1767
+ }
1768
+
1769
+ /**
1770
+ * Delete an ephemeral message
1771
+ * @param {number|string} chatId
1772
+ * @param {number} messageId
1773
+ */
1774
+ deleteEphemeralMessage(chatId, messageId) {
1775
+ return this.call('deleteEphemeralMessage', {
1776
+ chat_id: chatId,
1777
+ message_id: messageId,
1778
+ });
1779
+ }
1780
+
1781
+ /**
1782
+ * Get managed bot access settings
1783
+ * @param {number} userId - Unique identifier of the target user who manages the bot
1784
+ * @param {object} [extra]
1785
+ */
1786
+ getManagedBotAccessSettings(userId, extra = {}) {
1787
+ if (!userId) {
1788
+ throw new Error('Telegram.getManagedBotAccessSettings(userId) requires a valid userId.');
1789
+ }
1790
+ return this.call('getManagedBotAccessSettings', {
1791
+ user_id: userId,
1792
+ ...extra,
1793
+ });
1794
+ }
1795
+
1796
+ /**
1797
+ * Set managed bot access settings
1798
+ * @param {number} userId - Unique identifier of the target user who manages the bot
1799
+ * @param {object} [settings={}]
1800
+ * @param {object} [extra={}]
1801
+ */
1802
+ setManagedBotAccessSettings(userId, settings = {}, extra = {}) {
1803
+ if (!userId) {
1804
+ throw new Error('Telegram.setManagedBotAccessSettings(userId, settings) requires a valid userId.');
1805
+ }
1806
+ return this.call('setManagedBotAccessSettings', {
1807
+ user_id: userId,
1808
+ ...settings,
1809
+ ...extra,
1810
+ });
1811
+ }
1812
+
1813
+ /**
1814
+ * Get user personal chat messages
1815
+ * @param {number} userId
1816
+ * @param {object} [extra]
1817
+ */
1818
+ getUserPersonalChatMessages(userId, extra = {}) {
1819
+ return this.call('getUserPersonalChatMessages', {
1820
+ user_id: userId,
1821
+ ...extra,
1822
+ });
1823
+ }
1824
+
1825
+ /**
1826
+ * Send message draft
1827
+ * @param {number|string} chatId
1828
+ * @param {string} text
1829
+ * @param {object} [extra]
1830
+ */
1831
+ sendMessageDraft(chatId, text, extra = {}) {
1832
+ const draftId = extra.draft_id ?? Math.floor(Math.random() * 2147483647) + 1;
1833
+ return this.call('sendMessageDraft', {
1834
+ chat_id: chatId,
1835
+ draft_id: draftId,
1836
+ text,
1837
+ ...extra,
1838
+ });
1839
+ }
1840
+ }