UNPKG

41.9 kBPlain TextView Raw
1import * as tg from './core/types/typegram'
2import * as tt from './telegram-types'
3import { Deunionize, PropOr, UnionKeys } from './deunionize'
4import ApiClient from './core/network/client'
5import { Guard, Guarded, MaybeArray } from './core/helpers/util'
6import Telegram from './telegram'
7import { FmtString } from './format'
8import d from 'debug'
9
10const debug = d('telegraf:context')
11
12type Tail<T> = T extends [unknown, ...infer U] ? U : never
13
14type Shorthand<FName extends Exclude<keyof Telegram, keyof ApiClient>> = Tail<
15 Parameters<Telegram[FName]>
16>
17
18/**
19 * Narrows down `C['update']` (and derived getters)
20 * to specific update type `U`.
21 *
22 * Used by [[`Composer`]],
23 * possibly useful for splitting a bot into multiple files.
24 */
25export type NarrowedContext<
26 C extends Context,
27 U extends tg.Update,
28> = Context<U> & Omit<C, keyof Context>
29
30export type FilteredContext<
31 Ctx extends Context,
32 Filter extends tt.UpdateType | Guard<Ctx['update']>,
33> = Filter extends tt.UpdateType
34 ? NarrowedContext<Ctx, Extract<tg.Update, Record<Filter, object>>>
35 : NarrowedContext<Ctx, Guarded<Filter>>
36
37export class Context<U extends Deunionize<tg.Update> = tg.Update> {
38 // eslint-disable-next-line @typescript-eslint/no-explicit-any
39 readonly state: Record<string | symbol, any> = {}
40
41 constructor(
42 readonly update: U,
43 readonly telegram: Telegram,
44 readonly botInfo: tg.UserFromGetMe
45 ) {}
46
47 get updateType() {
48 for (const key in this.update) {
49 if (typeof this.update[key] === 'object') return key as UpdateTypes<U>
50 }
51
52 throw new Error(
53 `Cannot determine \`updateType\` of ${JSON.stringify(this.update)}`
54 )
55 }
56
57 get me() {
58 return this.botInfo?.username
59 }
60
61 /**
62 * @deprecated Use ctx.telegram instead
63 */
64 get tg() {
65 return this.telegram
66 }
67
68 get message() {
69 return this.update.message as PropOr<U, 'message'>
70 }
71
72 get editedMessage() {
73 return this.update.edited_message as PropOr<U, 'edited_message'>
74 }
75
76 get inlineQuery() {
77 return this.update.inline_query as PropOr<U, 'inline_query'>
78 }
79
80 get shippingQuery() {
81 return this.update.shipping_query as PropOr<U, 'shipping_query'>
82 }
83
84 get preCheckoutQuery() {
85 return this.update.pre_checkout_query as PropOr<U, 'pre_checkout_query'>
86 }
87
88 get chosenInlineResult() {
89 return this.update.chosen_inline_result as PropOr<U, 'chosen_inline_result'>
90 }
91
92 get channelPost() {
93 return this.update.channel_post as PropOr<U, 'channel_post'>
94 }
95
96 get editedChannelPost() {
97 return this.update.edited_channel_post as PropOr<U, 'edited_channel_post'>
98 }
99
100 get callbackQuery() {
101 return this.update.callback_query as PropOr<U, 'callback_query'>
102 }
103
104 get poll() {
105 return this.update.poll as PropOr<U, 'poll'>
106 }
107
108 get pollAnswer() {
109 return this.update.poll_answer as PropOr<U, 'poll_answer'>
110 }
111
112 get myChatMember() {
113 return this.update.my_chat_member as PropOr<U, 'my_chat_member'>
114 }
115
116 get chatMember() {
117 return this.update.chat_member as PropOr<U, 'chat_member'>
118 }
119
120 get chatJoinRequest() {
121 return this.update.chat_join_request as PropOr<U, 'chat_join_request'>
122 }
123
124 get chat(): Getter<U, 'chat'> {
125 return (
126 this.chatMember ??
127 this.myChatMember ??
128 this.chatJoinRequest ??
129 getMessageFromAnySource(this)
130 )?.chat as Getter<U, 'chat'>
131 }
132
133 get senderChat() {
134 return getMessageFromAnySource(this)?.sender_chat as Getter<
135 U,
136 'sender_chat'
137 >
138 }
139
140 get from() {
141 return (
142 this.callbackQuery ??
143 this.inlineQuery ??
144 this.shippingQuery ??
145 this.preCheckoutQuery ??
146 this.chosenInlineResult ??
147 this.chatMember ??
148 this.myChatMember ??
149 this.chatJoinRequest ??
150 getMessageFromAnySource(this)
151 )?.from as Getter<U, 'from'>
152 }
153
154 get inlineMessageId() {
155 return (this.callbackQuery ?? this.chosenInlineResult)?.inline_message_id
156 }
157
158 get passportData() {
159 if (this.message == null) return undefined
160 if (!('passport_data' in this.message)) return undefined
161 return this.message?.passport_data
162 }
163
164 get webAppData() {
165 if (
166 !(
167 'message' in this.update &&
168 this.update.message &&
169 'web_app_data' in this.update.message
170 )
171 )
172 return undefined
173
174 const { data, button_text } = this.update.message.web_app_data
175
176 return {
177 data: {
178 json<T>() {
179 return JSON.parse(data) as T
180 },
181 text() {
182 return data
183 },
184 },
185 button_text,
186 }
187 }
188
189 /**
190 * @deprecated use {@link Telegram.webhookReply}
191 */
192 get webhookReply(): boolean {
193 return this.telegram.webhookReply
194 }
195
196 set webhookReply(enable: boolean) {
197 this.telegram.webhookReply = enable
198 }
199
200 /**
201 * @internal
202 */
203 assert<T extends string | number | object>(
204 value: T | undefined,
205 method: string
206 ): asserts value is T {
207 if (value === undefined) {
208 throw new TypeError(
209 `Telegraf: "${method}" isn't available for "${this.updateType}"`
210 )
211 }
212 }
213
214 has<Filter extends tt.UpdateType | Guard<Context['update']>>(
215 filters: MaybeArray<Filter>
216 ): this is FilteredContext<Context, Filter> {
217 if (!Array.isArray(filters)) filters = [filters]
218 for (const filter of filters)
219 if (
220 // TODO: this should change to === 'function' once TS bug is fixed
221 // https://github.com/microsoft/TypeScript/pull/51502
222 typeof filter !== 'string'
223 ? // filter is a type guard
224 filter(this.update)
225 : // check if filter is the update type
226 filter in this.update
227 )
228 return true
229
230 return false
231 }
232
233 /**
234 * @see https://core.telegram.org/bots/api#answerinlinequery
235 */
236 answerInlineQuery(...args: Shorthand<'answerInlineQuery'>) {
237 this.assert(this.inlineQuery, 'answerInlineQuery')
238 return this.telegram.answerInlineQuery(this.inlineQuery.id, ...args)
239 }
240
241 /**
242 * @see https://core.telegram.org/bots/api#answercallbackquery
243 */
244 answerCbQuery(...args: Shorthand<'answerCbQuery'>) {
245 this.assert(this.callbackQuery, 'answerCbQuery')
246 return this.telegram.answerCbQuery(this.callbackQuery.id, ...args)
247 }
248
249 /**
250 * @see https://core.telegram.org/bots/api#answercallbackquery
251 */
252 answerGameQuery(...args: Shorthand<'answerGameQuery'>) {
253 this.assert(this.callbackQuery, 'answerGameQuery')
254 return this.telegram.answerGameQuery(this.callbackQuery.id, ...args)
255 }
256
257 /**
258 * @see https://core.telegram.org/bots/api#answershippingquery
259 */
260 answerShippingQuery(...args: Shorthand<'answerShippingQuery'>) {
261 this.assert(this.shippingQuery, 'answerShippingQuery')
262 return this.telegram.answerShippingQuery(this.shippingQuery.id, ...args)
263 }
264
265 /**
266 * @see https://core.telegram.org/bots/api#answerprecheckoutquery
267 */
268 answerPreCheckoutQuery(...args: Shorthand<'answerPreCheckoutQuery'>) {
269 this.assert(this.preCheckoutQuery, 'answerPreCheckoutQuery')
270 return this.telegram.answerPreCheckoutQuery(
271 this.preCheckoutQuery.id,
272 ...args
273 )
274 }
275
276 /**
277 * @see https://core.telegram.org/bots/api#editmessagetext
278 */
279 editMessageText(text: string | FmtString, extra?: tt.ExtraEditMessageText) {
280 this.assert(this.callbackQuery ?? this.inlineMessageId, 'editMessageText')
281 return this.telegram.editMessageText(
282 this.chat?.id,
283 this.callbackQuery?.message?.message_id,
284 this.inlineMessageId,
285 text,
286 extra
287 )
288 }
289
290 /**
291 * @see https://core.telegram.org/bots/api#editmessagecaption
292 */
293 editMessageCaption(
294 caption: string | FmtString | undefined,
295 extra?: tt.ExtraEditMessageCaption
296 ) {
297 this.assert(
298 this.callbackQuery ?? this.inlineMessageId,
299 'editMessageCaption'
300 )
301 return this.telegram.editMessageCaption(
302 this.chat?.id,
303 this.callbackQuery?.message?.message_id,
304 this.inlineMessageId,
305 caption,
306 extra
307 )
308 }
309
310 /**
311 * @see https://core.telegram.org/bots/api#editmessagemedia
312 */
313 editMessageMedia(
314 media: tt.WrapCaption<tg.InputMedia>,
315 extra?: tt.ExtraEditMessageMedia
316 ) {
317 this.assert(this.callbackQuery ?? this.inlineMessageId, 'editMessageMedia')
318 return this.telegram.editMessageMedia(
319 this.chat?.id,
320 this.callbackQuery?.message?.message_id,
321 this.inlineMessageId,
322 media,
323 extra
324 )
325 }
326
327 /**
328 * @see https://core.telegram.org/bots/api#editmessagereplymarkup
329 */
330 editMessageReplyMarkup(markup: tg.InlineKeyboardMarkup | undefined) {
331 this.assert(
332 this.callbackQuery ?? this.inlineMessageId,
333 'editMessageReplyMarkup'
334 )
335 return this.telegram.editMessageReplyMarkup(
336 this.chat?.id,
337 this.callbackQuery?.message?.message_id,
338 this.inlineMessageId,
339 markup
340 )
341 }
342
343 /**
344 * @see https://core.telegram.org/bots/api#editmessagelivelocation
345 */
346 editMessageLiveLocation(
347 latitude: number,
348 longitude: number,
349 extra?: tt.ExtraEditMessageLiveLocation
350 ) {
351 this.assert(
352 this.callbackQuery ?? this.inlineMessageId,
353 'editMessageLiveLocation'
354 )
355 return this.telegram.editMessageLiveLocation(
356 this.chat?.id,
357 this.callbackQuery?.message?.message_id,
358 this.inlineMessageId,
359 latitude,
360 longitude,
361 extra
362 )
363 }
364
365 /**
366 * @see https://core.telegram.org/bots/api#stopmessagelivelocation
367 */
368 stopMessageLiveLocation(markup?: tg.InlineKeyboardMarkup) {
369 this.assert(
370 this.callbackQuery ?? this.inlineMessageId,
371 'stopMessageLiveLocation'
372 )
373 return this.telegram.stopMessageLiveLocation(
374 this.chat?.id,
375 this.callbackQuery?.message?.message_id,
376 this.inlineMessageId,
377 markup
378 )
379 }
380
381 /**
382 * @see https://core.telegram.org/bots/api#sendmessage
383 */
384 sendMessage(text: string | FmtString, extra?: tt.ExtraReplyMessage) {
385 this.assert(this.chat, 'sendMessage')
386 return this.telegram.sendMessage(this.chat.id, text, {
387 message_thread_id: getThreadId(this),
388 ...extra,
389 })
390 }
391
392 /**
393 * @see https://core.telegram.org/bots/api#sendmessage
394 */
395 reply(...args: Shorthand<'sendMessage'>) {
396 return this.sendMessage(...args)
397 }
398
399 /**
400 * @see https://core.telegram.org/bots/api#getchat
401 */
402 getChat(...args: Shorthand<'getChat'>) {
403 this.assert(this.chat, 'getChat')
404 return this.telegram.getChat(this.chat.id, ...args)
405 }
406
407 /**
408 * @see https://core.telegram.org/bots/api#exportchatinvitelink
409 */
410 exportChatInviteLink(...args: Shorthand<'exportChatInviteLink'>) {
411 this.assert(this.chat, 'exportChatInviteLink')
412 return this.telegram.exportChatInviteLink(this.chat.id, ...args)
413 }
414
415 /**
416 * @see https://core.telegram.org/bots/api#createchatinvitelink
417 */
418 createChatInviteLink(...args: Shorthand<'createChatInviteLink'>) {
419 this.assert(this.chat, 'createChatInviteLink')
420 return this.telegram.createChatInviteLink(this.chat.id, ...args)
421 }
422
423 /**
424 * @see https://core.telegram.org/bots/api#editchatinvitelink
425 */
426 editChatInviteLink(...args: Shorthand<'editChatInviteLink'>) {
427 this.assert(this.chat, 'editChatInviteLink')
428 return this.telegram.editChatInviteLink(this.chat.id, ...args)
429 }
430
431 /**
432 * @see https://core.telegram.org/bots/api#revokechatinvitelink
433 */
434 revokeChatInviteLink(...args: Shorthand<'revokeChatInviteLink'>) {
435 this.assert(this.chat, 'revokeChatInviteLink')
436 return this.telegram.revokeChatInviteLink(this.chat.id, ...args)
437 }
438
439 /**
440 * @see https://core.telegram.org/bots/api#banchatmember
441 */
442 banChatMember(...args: Shorthand<'banChatMember'>) {
443 this.assert(this.chat, 'banChatMember')
444 return this.telegram.banChatMember(this.chat.id, ...args)
445 }
446
447 /**
448 * @see https://core.telegram.org/bots/api#banchatmember
449 * @deprecated since API 5.3. Use {@link Context.banChatMember}
450 */
451 get kickChatMember() {
452 return this.banChatMember
453 }
454
455 /**
456 * @see https://core.telegram.org/bots/api#unbanchatmember
457 */
458 unbanChatMember(...args: Shorthand<'unbanChatMember'>) {
459 this.assert(this.chat, 'unbanChatMember')
460 return this.telegram.unbanChatMember(this.chat.id, ...args)
461 }
462
463 /**
464 * @see https://core.telegram.org/bots/api#restrictchatmember
465 */
466 restrictChatMember(...args: Shorthand<'restrictChatMember'>) {
467 this.assert(this.chat, 'restrictChatMember')
468 return this.telegram.restrictChatMember(this.chat.id, ...args)
469 }
470
471 /**
472 * @see https://core.telegram.org/bots/api#promotechatmember
473 */
474 promoteChatMember(...args: Shorthand<'promoteChatMember'>) {
475 this.assert(this.chat, 'promoteChatMember')
476 return this.telegram.promoteChatMember(this.chat.id, ...args)
477 }
478
479 /**
480 * @see https://core.telegram.org/bots/api#setchatadministratorcustomtitle
481 */
482 setChatAdministratorCustomTitle(
483 ...args: Shorthand<'setChatAdministratorCustomTitle'>
484 ) {
485 this.assert(this.chat, 'setChatAdministratorCustomTitle')
486 return this.telegram.setChatAdministratorCustomTitle(this.chat.id, ...args)
487 }
488
489 /**
490 * @see https://core.telegram.org/bots/api#setchatphoto
491 */
492 setChatPhoto(...args: Shorthand<'setChatPhoto'>) {
493 this.assert(this.chat, 'setChatPhoto')
494 return this.telegram.setChatPhoto(this.chat.id, ...args)
495 }
496
497 /**
498 * @see https://core.telegram.org/bots/api#deletechatphoto
499 */
500 deleteChatPhoto(...args: Shorthand<'deleteChatPhoto'>) {
501 this.assert(this.chat, 'deleteChatPhoto')
502 return this.telegram.deleteChatPhoto(this.chat.id, ...args)
503 }
504
505 /**
506 * @see https://core.telegram.org/bots/api#setchattitle
507 */
508 setChatTitle(...args: Shorthand<'setChatTitle'>) {
509 this.assert(this.chat, 'setChatTitle')
510 return this.telegram.setChatTitle(this.chat.id, ...args)
511 }
512
513 /**
514 * @see https://core.telegram.org/bots/api#setchatdescription
515 */
516 setChatDescription(...args: Shorthand<'setChatDescription'>) {
517 this.assert(this.chat, 'setChatDescription')
518 return this.telegram.setChatDescription(this.chat.id, ...args)
519 }
520
521 /**
522 * @see https://core.telegram.org/bots/api#pinchatmessage
523 */
524 pinChatMessage(...args: Shorthand<'pinChatMessage'>) {
525 this.assert(this.chat, 'pinChatMessage')
526 return this.telegram.pinChatMessage(this.chat.id, ...args)
527 }
528
529 /**
530 * @see https://core.telegram.org/bots/api#unpinchatmessage
531 */
532 unpinChatMessage(...args: Shorthand<'unpinChatMessage'>) {
533 this.assert(this.chat, 'unpinChatMessage')
534 return this.telegram.unpinChatMessage(this.chat.id, ...args)
535 }
536
537 /**
538 * @see https://core.telegram.org/bots/api#unpinallchatmessages
539 */
540 unpinAllChatMessages(...args: Shorthand<'unpinAllChatMessages'>) {
541 this.assert(this.chat, 'unpinAllChatMessages')
542 return this.telegram.unpinAllChatMessages(this.chat.id, ...args)
543 }
544
545 /**
546 * @see https://core.telegram.org/bots/api#leavechat
547 */
548 leaveChat(...args: Shorthand<'leaveChat'>) {
549 this.assert(this.chat, 'leaveChat')
550 return this.telegram.leaveChat(this.chat.id, ...args)
551 }
552
553 /**
554 * @see https://core.telegram.org/bots/api#setchatpermissions
555 */
556 setChatPermissions(...args: Shorthand<'setChatPermissions'>) {
557 this.assert(this.chat, 'setChatPermissions')
558 return this.telegram.setChatPermissions(this.chat.id, ...args)
559 }
560
561 /**
562 * @see https://core.telegram.org/bots/api#getchatadministrators
563 */
564 getChatAdministrators(...args: Shorthand<'getChatAdministrators'>) {
565 this.assert(this.chat, 'getChatAdministrators')
566 return this.telegram.getChatAdministrators(this.chat.id, ...args)
567 }
568
569 /**
570 * @see https://core.telegram.org/bots/api#getchatmember
571 */
572 getChatMember(...args: Shorthand<'getChatMember'>) {
573 this.assert(this.chat, 'getChatMember')
574 return this.telegram.getChatMember(this.chat.id, ...args)
575 }
576
577 /**
578 * @see https://core.telegram.org/bots/api#getchatmembercount
579 */
580 getChatMembersCount(...args: Shorthand<'getChatMembersCount'>) {
581 this.assert(this.chat, 'getChatMembersCount')
582 return this.telegram.getChatMembersCount(this.chat.id, ...args)
583 }
584
585 /**
586 * @see https://core.telegram.org/bots/api#setpassportdataerrors
587 */
588 setPassportDataErrors(errors: readonly tg.PassportElementError[]) {
589 this.assert(this.from, 'setPassportDataErrors')
590 return this.telegram.setPassportDataErrors(this.from.id, errors)
591 }
592
593 /**
594 * @see https://core.telegram.org/bots/api#sendphoto
595 */
596 sendPhoto(photo: string | tg.InputFile, extra?: tt.ExtraPhoto) {
597 this.assert(this.chat, 'sendPhoto')
598 return this.telegram.sendPhoto(this.chat.id, photo, {
599 message_thread_id: getThreadId(this),
600 ...extra,
601 })
602 }
603
604 /**
605 * @see https://core.telegram.org/bots/api#sendphoto
606 */
607 replyWithPhoto(...args: Shorthand<'sendPhoto'>) {
608 return this.sendPhoto(...args)
609 }
610
611 /**
612 * @see https://core.telegram.org/bots/api#sendmediagroup
613 */
614 sendMediaGroup(media: tt.MediaGroup, extra?: tt.ExtraMediaGroup) {
615 this.assert(this.chat, 'sendMediaGroup')
616 return this.telegram.sendMediaGroup(this.chat.id, media, {
617 message_thread_id: getThreadId(this),
618 ...extra,
619 })
620 }
621
622 /**
623 * @see https://core.telegram.org/bots/api#sendmediagroup
624 */
625 replyWithMediaGroup(...args: Shorthand<'sendMediaGroup'>) {
626 return this.sendMediaGroup(...args)
627 }
628
629 /**
630 * @see https://core.telegram.org/bots/api#sendaudio
631 */
632 sendAudio(audio: string | tg.InputFile, extra?: tt.ExtraAudio) {
633 this.assert(this.chat, 'sendAudio')
634 return this.telegram.sendAudio(this.chat.id, audio, {
635 message_thread_id: getThreadId(this),
636 ...extra,
637 })
638 }
639
640 /**
641 * @see https://core.telegram.org/bots/api#sendaudio
642 */
643 replyWithAudio(...args: Shorthand<'sendAudio'>) {
644 return this.sendAudio(...args)
645 }
646
647 /**
648 * @see https://core.telegram.org/bots/api#senddice
649 */
650 sendDice(extra?: tt.ExtraDice) {
651 this.assert(this.chat, 'sendDice')
652 return this.telegram.sendDice(this.chat.id, {
653 message_thread_id: getThreadId(this),
654 ...extra,
655 })
656 }
657
658 /**
659 * @see https://core.telegram.org/bots/api#senddice
660 */
661 replyWithDice(...args: Shorthand<'sendDice'>) {
662 return this.sendDice(...args)
663 }
664
665 /**
666 * @see https://core.telegram.org/bots/api#senddocument
667 */
668 sendDocument(document: string | tg.InputFile, extra?: tt.ExtraDocument) {
669 this.assert(this.chat, 'sendDocument')
670 return this.telegram.sendDocument(this.chat.id, document, {
671 message_thread_id: getThreadId(this),
672 ...extra,
673 })
674 }
675
676 /**
677 * @see https://core.telegram.org/bots/api#senddocument
678 */
679 replyWithDocument(...args: Shorthand<'sendDocument'>) {
680 return this.sendDocument(...args)
681 }
682
683 /**
684 * @see https://core.telegram.org/bots/api#sendsticker
685 */
686 sendSticker(sticker: string | tg.InputFile, extra?: tt.ExtraSticker) {
687 this.assert(this.chat, 'sendSticker')
688 return this.telegram.sendSticker(this.chat.id, sticker, {
689 message_thread_id: getThreadId(this),
690 ...extra,
691 })
692 }
693
694 /**
695 * @see https://core.telegram.org/bots/api#sendsticker
696 */
697 replyWithSticker(...args: Shorthand<'sendSticker'>) {
698 return this.sendSticker(...args)
699 }
700
701 /**
702 * @see https://core.telegram.org/bots/api#sendvideo
703 */
704 sendVideo(video: string | tg.InputFile, extra?: tt.ExtraVideo) {
705 this.assert(this.chat, 'sendVideo')
706 return this.telegram.sendVideo(this.chat.id, video, {
707 message_thread_id: getThreadId(this),
708 ...extra,
709 })
710 }
711
712 /**
713 * @see https://core.telegram.org/bots/api#sendvideo
714 */
715 replyWithVideo(...args: Shorthand<'sendVideo'>) {
716 return this.sendVideo(...args)
717 }
718
719 /**
720 * @see https://core.telegram.org/bots/api#sendanimation
721 */
722 sendAnimation(animation: string | tg.InputFile, extra?: tt.ExtraAnimation) {
723 this.assert(this.chat, 'sendAnimation')
724 return this.telegram.sendAnimation(this.chat.id, animation, {
725 message_thread_id: getThreadId(this),
726 ...extra,
727 })
728 }
729
730 /**
731 * @see https://core.telegram.org/bots/api#sendanimation
732 */
733 replyWithAnimation(...args: Shorthand<'sendAnimation'>) {
734 return this.sendAnimation(...args)
735 }
736
737 /**
738 * @see https://core.telegram.org/bots/api#sendvideonote
739 */
740 sendVideoNote(
741 videoNote: string | tg.InputFileVideoNote,
742 extra?: tt.ExtraVideoNote
743 ) {
744 this.assert(this.chat, 'sendVideoNote')
745 return this.telegram.sendVideoNote(this.chat.id, videoNote, {
746 message_thread_id: getThreadId(this),
747 ...extra,
748 })
749 }
750
751 /**
752 * @see https://core.telegram.org/bots/api#sendvideonote
753 */
754 replyWithVideoNote(...args: Shorthand<'sendVideoNote'>) {
755 return this.sendVideoNote(...args)
756 }
757
758 /**
759 * @see https://core.telegram.org/bots/api#sendinvoice
760 */
761 sendInvoice(invoice: tt.NewInvoiceParameters, extra?: tt.ExtraInvoice) {
762 this.assert(this.chat, 'sendInvoice')
763 return this.telegram.sendInvoice(this.chat.id, invoice, {
764 message_thread_id: getThreadId(this),
765 ...extra,
766 })
767 }
768
769 /**
770 * @see https://core.telegram.org/bots/api#sendinvoice
771 */
772 replyWithInvoice(...args: Shorthand<'sendInvoice'>) {
773 return this.sendInvoice(...args)
774 }
775
776 /**
777 * @see https://core.telegram.org/bots/api#sendgame
778 */
779 sendGame(game: string, extra?: tt.ExtraGame) {
780 this.assert(this.chat, 'sendGame')
781 return this.telegram.sendGame(this.chat.id, game, {
782 message_thread_id: getThreadId(this),
783 ...extra,
784 })
785 }
786
787 /**
788 * @see https://core.telegram.org/bots/api#sendgame
789 */
790 replyWithGame(...args: Shorthand<'sendGame'>) {
791 return this.sendGame(...args)
792 }
793
794 /**
795 * @see https://core.telegram.org/bots/api#sendvoice
796 */
797 sendVoice(voice: string | tg.InputFile, extra?: tt.ExtraVoice) {
798 this.assert(this.chat, 'sendVoice')
799 return this.telegram.sendVoice(this.chat.id, voice, {
800 message_thread_id: getThreadId(this),
801 ...extra,
802 })
803 }
804
805 /**
806 * @see https://core.telegram.org/bots/api#sendvoice
807 */
808 replyWithVoice(...args: Shorthand<'sendVoice'>) {
809 return this.sendVoice(...args)
810 }
811
812 /**
813 * @see https://core.telegram.org/bots/api#sendpoll
814 */
815 sendPoll(poll: string, options: readonly string[], extra?: tt.ExtraPoll) {
816 this.assert(this.chat, 'sendPoll')
817 return this.telegram.sendPoll(this.chat.id, poll, options, {
818 message_thread_id: getThreadId(this),
819 ...extra,
820 })
821 }
822
823 /**
824 * @see https://core.telegram.org/bots/api#sendpoll
825 */
826 replyWithPoll(...args: Shorthand<'sendPoll'>) {
827 return this.sendPoll(...args)
828 }
829
830 /**
831 * @see https://core.telegram.org/bots/api#sendpoll
832 */
833 sendQuiz(quiz: string, options: readonly string[], extra?: tt.ExtraPoll) {
834 this.assert(this.chat, 'sendQuiz')
835 return this.telegram.sendQuiz(this.chat.id, quiz, options, {
836 message_thread_id: getThreadId(this),
837 ...extra,
838 })
839 }
840
841 /**
842 * @see https://core.telegram.org/bots/api#sendpoll
843 */
844 replyWithQuiz(...args: Shorthand<'sendQuiz'>) {
845 return this.sendQuiz(...args)
846 }
847
848 /**
849 * @see https://core.telegram.org/bots/api#stoppoll
850 */
851 stopPoll(...args: Shorthand<'stopPoll'>) {
852 this.assert(this.chat, 'stopPoll')
853 return this.telegram.stopPoll(this.chat.id, ...args)
854 }
855
856 /**
857 * @see https://core.telegram.org/bots/api#sendchataction
858 */
859 sendChatAction(
860 action: Shorthand<'sendChatAction'>[0],
861 extra?: tt.ExtraSendChatAction
862 ) {
863 this.assert(this.chat, 'sendChatAction')
864 return this.telegram.sendChatAction(this.chat.id, action, {
865 message_thread_id: getThreadId(this),
866 ...extra,
867 })
868 }
869
870 /**
871 * @see https://core.telegram.org/bots/api#sendchataction
872 *
873 * Sends the sendChatAction request repeatedly, with a delay between requests,
874 * as long as the provided callback function is being processed.
875 *
876 * The sendChatAction errors should be ignored, because the goal is the actual long process completing and performing an action.
877 *
878 * @param action - chat action type.
879 * @param callback - a function to run along with the chat action.
880 * @param extra - extra parameters for sendChatAction.
881 * @param {number} [extra.intervalDuration=8000] - The duration (in milliseconds) between subsequent sendChatAction requests.
882 */
883 async persistentChatAction(
884 action: Shorthand<'sendChatAction'>[0],
885 callback: () => Promise<void>,
886 {
887 intervalDuration,
888 ...extra
889 }: tt.ExtraSendChatAction & { intervalDuration?: number } = {}
890 ) {
891 await this.sendChatAction(action, { ...extra })
892
893 const timer = setInterval(
894 () =>
895 this.sendChatAction(action, { ...extra }).catch((err) => {
896 debug('Ignored error while persisting sendChatAction:', err)
897 }),
898 intervalDuration ?? 4000
899 )
900
901 try {
902 await callback()
903 } finally {
904 clearInterval(timer)
905 }
906 }
907
908 /**
909 * @deprecated use {@link Context.sendChatAction} instead
910 * @see https://core.telegram.org/bots/api#sendchataction
911 */
912 replyWithChatAction(...args: Shorthand<'sendChatAction'>) {
913 return this.sendChatAction(...args)
914 }
915
916 /**
917 * @see https://core.telegram.org/bots/api#sendlocation
918 */
919 sendLocation(latitude: number, longitude: number, extra?: tt.ExtraLocation) {
920 this.assert(this.chat, 'sendLocation')
921 return this.telegram.sendLocation(this.chat.id, latitude, longitude, {
922 message_thread_id: getThreadId(this),
923 ...extra,
924 })
925 }
926
927 /**
928 * @see https://core.telegram.org/bots/api#sendlocation
929 */
930 replyWithLocation(...args: Shorthand<'sendLocation'>) {
931 return this.sendLocation(...args)
932 }
933
934 /**
935 * @see https://core.telegram.org/bots/api#sendvenue
936 */
937 sendVenue(
938 latitude: number,
939 longitude: number,
940 title: string,
941 address: string,
942 extra?: tt.ExtraVenue
943 ) {
944 this.assert(this.chat, 'sendVenue')
945 return this.telegram.sendVenue(
946 this.chat.id,
947 latitude,
948 longitude,
949 title,
950 address,
951 { message_thread_id: getThreadId(this), ...extra }
952 )
953 }
954
955 /**
956 * @see https://core.telegram.org/bots/api#sendvenue
957 */
958 replyWithVenue(...args: Shorthand<'sendVenue'>) {
959 return this.sendVenue(...args)
960 }
961
962 /**
963 * @see https://core.telegram.org/bots/api#sendcontact
964 */
965 sendContact(phoneNumber: string, firstName: string, extra?: tt.ExtraContact) {
966 this.assert(this.chat, 'sendContact')
967 return this.telegram.sendContact(this.chat.id, phoneNumber, firstName, {
968 message_thread_id: getThreadId(this),
969 ...extra,
970 })
971 }
972
973 /**
974 * @see https://core.telegram.org/bots/api#sendcontact
975 */
976 replyWithContact(...args: Shorthand<'sendContact'>) {
977 return this.sendContact(...args)
978 }
979
980 /**
981 * @deprecated use {@link Telegram.getStickerSet}
982 * @see https://core.telegram.org/bots/api#getstickerset
983 */
984 getStickerSet(setName: string) {
985 return this.telegram.getStickerSet(setName)
986 }
987
988 /**
989 * @see https://core.telegram.org/bots/api#setchatstickerset
990 */
991 setChatStickerSet(setName: string) {
992 this.assert(this.chat, 'setChatStickerSet')
993 return this.telegram.setChatStickerSet(this.chat.id, setName)
994 }
995
996 /**
997 * @see https://core.telegram.org/bots/api#deletechatstickerset
998 */
999 deleteChatStickerSet() {
1000 this.assert(this.chat, 'deleteChatStickerSet')
1001 return this.telegram.deleteChatStickerSet(this.chat.id)
1002 }
1003
1004 /**
1005 * Use this method to create a topic in a forum supergroup chat. The bot must be an administrator in the chat for this
1006 * to work and must have the can_manage_topics administrator rights. Returns information about the created topic as a
1007 * ForumTopic object.
1008 *
1009 * @see https://core.telegram.org/bots/api#createforumtopic
1010 */
1011 createForumTopic(...args: Shorthand<'createForumTopic'>) {
1012 this.assert(this.chat, 'createForumTopic')
1013 return this.telegram.createForumTopic(this.chat.id, ...args)
1014 }
1015
1016 /**
1017 * Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must be an administrator in
1018 * the chat for this to work and must have can_manage_topics administrator rights, unless it is the creator of the
1019 * topic. Returns True on success.
1020 *
1021 * @see https://core.telegram.org/bots/api#editforumtopic
1022 */
1023 editForumTopic(extra: tt.ExtraEditForumTopic) {
1024 this.assert(this.chat, 'editForumTopic')
1025 this.assert(this.message?.message_thread_id, 'editForumTopic')
1026 return this.telegram.editForumTopic(
1027 this.chat.id,
1028 this.message.message_thread_id,
1029 extra
1030 )
1031 }
1032
1033 /**
1034 * Use this method to close an open topic in a forum supergroup chat. The bot must be an administrator in the chat
1035 * for this to work and must have the can_manage_topics administrator rights, unless it is the creator of the topic.
1036 * Returns True on success.
1037 *
1038 * @see https://core.telegram.org/bots/api#closeforumtopic
1039 */
1040 closeForumTopic() {
1041 this.assert(this.chat, 'closeForumTopic')
1042 this.assert(this.message?.message_thread_id, 'closeForumTopic')
1043
1044 return this.telegram.closeForumTopic(
1045 this.chat.id,
1046 this.message.message_thread_id
1047 )
1048 }
1049
1050 /**
1051 * Use this method to reopen a closed topic in a forum supergroup chat. The bot must be an administrator in the chat
1052 * for this to work and must have the can_manage_topics administrator rights, unless it is the creator of the topic.
1053 * Returns True on success.
1054 *
1055 * @see https://core.telegram.org/bots/api#reopenforumtopic
1056 */
1057 reopenForumTopic() {
1058 this.assert(this.chat, 'reopenForumTopic')
1059 this.assert(this.message?.message_thread_id, 'reopenForumTopic')
1060
1061 return this.telegram.reopenForumTopic(
1062 this.chat.id,
1063 this.message.message_thread_id
1064 )
1065 }
1066
1067 /**
1068 * Use this method to delete a forum topic along with all its messages in a forum supergroup chat. The bot must be an
1069 * administrator in the chat for this to work and must have the can_delete_messages administrator rights.
1070 * Returns True on success.
1071 *
1072 * @see https://core.telegram.org/bots/api#deleteforumtopic
1073 */
1074 deleteForumTopic() {
1075 this.assert(this.chat, 'deleteForumTopic')
1076 this.assert(this.message?.message_thread_id, 'deleteForumTopic')
1077
1078 return this.telegram.deleteForumTopic(
1079 this.chat.id,
1080 this.message.message_thread_id
1081 )
1082 }
1083
1084 /**
1085 * Use this method to clear the list of pinned messages in a forum topic. The bot must be an administrator in the chat
1086 * for this to work and must have the can_pin_messages administrator right in the supergroup. Returns True on success.
1087 *
1088 * @see https://core.telegram.org/bots/api#unpinallforumtopicmessages
1089 */
1090 unpinAllForumTopicMessages() {
1091 this.assert(this.chat, 'unpinAllForumTopicMessages')
1092 this.assert(this.message?.message_thread_id, 'unpinAllForumTopicMessages')
1093
1094 return this.telegram.unpinAllForumTopicMessages(
1095 this.chat.id,
1096 this.message.message_thread_id
1097 )
1098 }
1099
1100 /**
1101 * Use this method to edit the name of the 'General' topic in a forum supergroup chat. The bot must be an administrator
1102 * in the chat for this to work and must have can_manage_topics administrator rights. Returns True on success.
1103 *
1104 * @see https://core.telegram.org/bots/api#editgeneralforumtopic
1105 */
1106 editGeneralForumTopic(name: string) {
1107 this.assert(this.chat, 'editGeneralForumTopic')
1108 return this.telegram.editGeneralForumTopic(this.chat.id, name)
1109 }
1110
1111 /**
1112 * Use this method to close an open 'General' topic in a forum supergroup chat. The bot must be an administrator in the
1113 * chat for this to work and must have the can_manage_topics administrator rights. Returns True on success.
1114 *
1115 * @see https://core.telegram.org/bots/api#closegeneralforumtopic
1116 */
1117 closeGeneralForumTopic() {
1118 this.assert(this.chat, 'closeGeneralForumTopic')
1119 return this.telegram.closeGeneralForumTopic(this.chat.id)
1120 }
1121
1122 /**
1123 * Use this method to reopen a closed 'General' topic in a forum supergroup chat. The bot must be an administrator in
1124 * the chat for this to work and must have the can_manage_topics administrator rights. The topic will be automatically
1125 * unhidden if it was hidden. Returns True on success.
1126 *
1127 * @see https://core.telegram.org/bots/api#reopengeneralforumtopic
1128 */
1129 reopenGeneralForumTopic() {
1130 this.assert(this.chat, 'reopenGeneralForumTopic')
1131 return this.telegram.reopenGeneralForumTopic(this.chat.id)
1132 }
1133
1134 /**
1135 * Use this method to hide the 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat
1136 * for this to work and must have the can_manage_topics administrator rights. The topic will be automatically closed
1137 * if it was open. Returns True on success.
1138 *
1139 * @see https://core.telegram.org/bots/api#hidegeneralforumtopic
1140 */
1141 hideGeneralForumTopic() {
1142 this.assert(this.chat, 'hideGeneralForumTopic')
1143 return this.telegram.hideGeneralForumTopic(this.chat.id)
1144 }
1145
1146 /**
1147 * Use this method to unhide the 'General' topic in a forum supergroup chat. The bot must be an administrator in the
1148 * chat for this to work and must have the can_manage_topics administrator rights. Returns True on success.
1149 *
1150 * @see https://core.telegram.org/bots/api#unhidegeneralforumtopic
1151 */
1152 unhideGeneralForumTopic() {
1153 this.assert(this.chat, 'unhideGeneralForumTopic')
1154 return this.telegram.unhideGeneralForumTopic(this.chat.id)
1155 }
1156
1157 /**
1158 * Use this method to clear the list of pinned messages in a General forum topic.
1159 * The bot must be an administrator in the chat for this to work and must have the can_pin_messages administrator
1160 * right in the supergroup.
1161 *
1162 * @param chat_id Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)
1163 *
1164 * @see https://core.telegram.org/bots/api#unpinallgeneralforumtopicmessages
1165 */
1166 unpinAllGeneralForumTopicMessages() {
1167 this.assert(this.chat, 'unpinAllGeneralForumTopicMessages')
1168 return this.telegram.unpinAllGeneralForumTopicMessages(this.chat.id)
1169 }
1170
1171 /**
1172 * @deprecated use {@link Telegram.setStickerPositionInSet}
1173 * @see https://core.telegram.org/bots/api#setstickerpositioninset
1174 */
1175 setStickerPositionInSet(sticker: string, position: number) {
1176 return this.telegram.setStickerPositionInSet(sticker, position)
1177 }
1178
1179 /**
1180 * @deprecated use {@link Telegram.setStickerSetThumbnail}
1181 * @see https://core.telegram.org/bots/api#setstickersetthumbnail
1182 */
1183 setStickerSetThumb(...args: Parameters<Telegram['setStickerSetThumbnail']>) {
1184 return this.telegram.setStickerSetThumbnail(...args)
1185 }
1186
1187 setStickerSetThumbnail(
1188 ...args: Parameters<Telegram['setStickerSetThumbnail']>
1189 ) {
1190 return this.telegram.setStickerSetThumbnail(...args)
1191 }
1192
1193 setStickerMaskPosition(
1194 ...args: Parameters<Telegram['setStickerMaskPosition']>
1195 ) {
1196 return this.telegram.setStickerMaskPosition(...args)
1197 }
1198
1199 setStickerKeywords(...args: Parameters<Telegram['setStickerKeywords']>) {
1200 return this.telegram.setStickerKeywords(...args)
1201 }
1202
1203 setStickerEmojiList(...args: Parameters<Telegram['setStickerEmojiList']>) {
1204 return this.telegram.setStickerEmojiList(...args)
1205 }
1206
1207 deleteStickerSet(...args: Parameters<Telegram['deleteStickerSet']>) {
1208 return this.telegram.deleteStickerSet(...args)
1209 }
1210
1211 setStickerSetTitle(...args: Parameters<Telegram['setStickerSetTitle']>) {
1212 return this.telegram.setStickerSetTitle(...args)
1213 }
1214
1215 setCustomEmojiStickerSetThumbnail(
1216 ...args: Parameters<Telegram['setCustomEmojiStickerSetThumbnail']>
1217 ) {
1218 return this.telegram.setCustomEmojiStickerSetThumbnail(...args)
1219 }
1220
1221 /**
1222 * @deprecated use {@link Telegram.deleteStickerFromSet}
1223 * @see https://core.telegram.org/bots/api#deletestickerfromset
1224 */
1225 deleteStickerFromSet(sticker: string) {
1226 return this.telegram.deleteStickerFromSet(sticker)
1227 }
1228
1229 /**
1230 * @see https://core.telegram.org/bots/api#uploadstickerfile
1231 */
1232 uploadStickerFile(...args: Shorthand<'uploadStickerFile'>) {
1233 this.assert(this.from, 'uploadStickerFile')
1234 return this.telegram.uploadStickerFile(this.from.id, ...args)
1235 }
1236
1237 /**
1238 * @see https://core.telegram.org/bots/api#createnewstickerset
1239 */
1240 createNewStickerSet(...args: Shorthand<'createNewStickerSet'>) {
1241 this.assert(this.from, 'createNewStickerSet')
1242 return this.telegram.createNewStickerSet(this.from.id, ...args)
1243 }
1244
1245 /**
1246 * @see https://core.telegram.org/bots/api#addstickertoset
1247 */
1248 addStickerToSet(...args: Shorthand<'addStickerToSet'>) {
1249 this.assert(this.from, 'addStickerToSet')
1250 return this.telegram.addStickerToSet(this.from.id, ...args)
1251 }
1252
1253 /**
1254 * @deprecated use {@link Telegram.getMyCommands}
1255 * @see https://core.telegram.org/bots/api#getmycommands
1256 */
1257 getMyCommands() {
1258 return this.telegram.getMyCommands()
1259 }
1260
1261 /**
1262 * @deprecated use {@link Telegram.setMyCommands}
1263 * @see https://core.telegram.org/bots/api#setmycommands
1264 */
1265 setMyCommands(commands: readonly tg.BotCommand[]) {
1266 return this.telegram.setMyCommands(commands)
1267 }
1268
1269 /**
1270 * @deprecated use {@link Context.replyWithMarkdownV2}
1271 * @see https://core.telegram.org/bots/api#sendmessage
1272 */
1273 replyWithMarkdown(markdown: string, extra?: tt.ExtraReplyMessage) {
1274 return this.reply(markdown, { parse_mode: 'Markdown', ...extra })
1275 }
1276
1277 /**
1278 * @see https://core.telegram.org/bots/api#sendmessage
1279 */
1280 replyWithMarkdownV2(markdown: string, extra?: tt.ExtraReplyMessage) {
1281 return this.reply(markdown, { parse_mode: 'MarkdownV2', ...extra })
1282 }
1283
1284 /**
1285 * @see https://core.telegram.org/bots/api#sendmessage
1286 */
1287 replyWithHTML(html: string, extra?: tt.ExtraReplyMessage) {
1288 return this.reply(html, { parse_mode: 'HTML', ...extra })
1289 }
1290
1291 /**
1292 * @see https://core.telegram.org/bots/api#deletemessage
1293 */
1294 deleteMessage(messageId?: number) {
1295 this.assert(this.chat, 'deleteMessage')
1296 if (typeof messageId !== 'undefined') {
1297 return this.telegram.deleteMessage(this.chat.id, messageId)
1298 }
1299 const message = getMessageFromAnySource(this)
1300 this.assert(message, 'deleteMessage')
1301 return this.telegram.deleteMessage(this.chat.id, message.message_id)
1302 }
1303
1304 /**
1305 * @see https://core.telegram.org/bots/api#forwardmessage
1306 */
1307 forwardMessage(
1308 chatId: string | number,
1309 extra?: Shorthand<'forwardMessage'>[2]
1310 ) {
1311 const message = getMessageFromAnySource(this)
1312 this.assert(message, 'forwardMessage')
1313 return this.telegram.forwardMessage(
1314 chatId,
1315 message.chat.id,
1316 message.message_id,
1317 extra
1318 )
1319 }
1320
1321 /**
1322 * @see https://core.telegram.org/bots/api#copymessage
1323 */
1324 copyMessage(chatId: string | number, extra?: tt.ExtraCopyMessage) {
1325 const message = getMessageFromAnySource(this)
1326 this.assert(message, 'copyMessage')
1327 return this.telegram.copyMessage(
1328 chatId,
1329 message.chat.id,
1330 message.message_id,
1331 extra
1332 )
1333 }
1334
1335 /**
1336 * @see https://core.telegram.org/bots/api#approvechatjoinrequest
1337 */
1338 approveChatJoinRequest(userId: number) {
1339 this.assert(this.chat, 'approveChatJoinRequest')
1340 return this.telegram.approveChatJoinRequest(this.chat.id, userId)
1341 }
1342
1343 /**
1344 * @see https://core.telegram.org/bots/api#declinechatjoinrequest
1345 */
1346 declineChatJoinRequest(userId: number) {
1347 this.assert(this.chat, 'declineChatJoinRequest')
1348 return this.telegram.declineChatJoinRequest(this.chat.id, userId)
1349 }
1350
1351 /**
1352 * @see https://core.telegram.org/bots/api#banchatsenderchat
1353 */
1354 banChatSenderChat(senderChatId: number) {
1355 this.assert(this.chat, 'banChatSenderChat')
1356 return this.telegram.banChatSenderChat(this.chat.id, senderChatId)
1357 }
1358
1359 /**
1360 * @see https://core.telegram.org/bots/api#unbanchatsenderchat
1361 */
1362 unbanChatSenderChat(senderChatId: number) {
1363 this.assert(this.chat, 'unbanChatSenderChat')
1364 return this.telegram.unbanChatSenderChat(this.chat.id, senderChatId)
1365 }
1366
1367 /**
1368 * Use this method to change the bot's menu button in the current private chat. Returns true on success.
1369 * @see https://core.telegram.org/bots/api#setchatmenubutton
1370 */
1371 setChatMenuButton(menuButton?: tg.MenuButton) {
1372 this.assert(this.chat, 'setChatMenuButton')
1373 return this.telegram.setChatMenuButton({ chatId: this.chat.id, menuButton })
1374 }
1375
1376 /**
1377 * Use this method to get the current value of the bot's menu button in the current private chat. Returns MenuButton on success.
1378 * @see https://core.telegram.org/bots/api#getchatmenubutton
1379 */
1380 getChatMenuButton() {
1381 this.assert(this.chat, 'getChatMenuButton')
1382 return this.telegram.getChatMenuButton({ chatId: this.chat.id })
1383 }
1384
1385 /**
1386 * @see https://core.telegram.org/bots/api#setmydefaultadministratorrights
1387 */
1388 setMyDefaultAdministratorRights(
1389 extra?: Parameters<Telegram['setMyDefaultAdministratorRights']>[0]
1390 ) {
1391 return this.telegram.setMyDefaultAdministratorRights(extra)
1392 }
1393
1394 /**
1395 * @see https://core.telegram.org/bots/api#getmydefaultadministratorrights
1396 */
1397 getMyDefaultAdministratorRights(
1398 extra?: Parameters<Telegram['getMyDefaultAdministratorRights']>[0]
1399 ) {
1400 return this.telegram.getMyDefaultAdministratorRights(extra)
1401 }
1402}
1403
1404export default Context
1405
1406type UpdateTypes<U extends Deunionize<tg.Update>> = Extract<
1407 UnionKeys<U>,
1408 tt.UpdateType
1409>
1410
1411export type GetUpdateContent<U extends tg.Update> =
1412 U extends tg.Update.CallbackQueryUpdate
1413 ? U['callback_query']['message']
1414 : U[UpdateTypes<U>]
1415
1416type Getter<U extends Deunionize<tg.Update>, P extends string> = PropOr<
1417 GetUpdateContent<U>,
1418 P
1419>
1420
1421function getMessageFromAnySource<U extends tg.Update>(ctx: Context<U>) {
1422 return (
1423 ctx.message ??
1424 ctx.editedMessage ??
1425 ctx.callbackQuery?.message ??
1426 ctx.channelPost ??
1427 ctx.editedChannelPost
1428 )
1429}
1430
1431const getThreadId = <U extends tg.Update>(ctx: Context<U>) => {
1432 const msg = getMessageFromAnySource(ctx)
1433 return msg?.is_topic_message ? msg.message_thread_id : undefined
1434}