helper_methods.go (view raw)
1package tgbotapi
2
3import (
4 "crypto/hmac"
5 "crypto/sha256"
6 "encoding/hex"
7 "errors"
8 "fmt"
9 "net/url"
10 "sort"
11 "strings"
12)
13
14// NewMessage creates a new Message.
15//
16// chatID is where to send it, text is the message text.
17func NewMessage(chatID int64, text string) MessageConfig {
18 return MessageConfig{
19 BaseChat: BaseChat{
20 ChatConfig: ChatConfig{
21 ChatID: chatID,
22 },
23 },
24 Text: text,
25 LinkPreviewOptions: LinkPreviewOptions{
26 IsDisabled: false,
27 },
28 }
29}
30
31// NewDeleteMessage creates a request to delete a message.
32func NewDeleteMessage(chatID int64, messageID int) DeleteMessageConfig {
33 return DeleteMessageConfig{
34 BaseChatMessage: BaseChatMessage{
35 ChatConfig: ChatConfig{
36 ChatID: chatID,
37 },
38 MessageID: messageID,
39 },
40 }
41}
42
43// NewMessageToChannel creates a new Message that is sent to a channel
44// by username.
45//
46// username is the username of the channel, text is the message text,
47// and the username should be in the form of `@username`.
48func NewMessageToChannel(username string, text string) MessageConfig {
49 return MessageConfig{
50 BaseChat: BaseChat{
51 ChatConfig: ChatConfig{
52 ChannelUsername: username,
53 }},
54 Text: text,
55 }
56}
57
58// NewForward creates a new forward.
59//
60// chatID is where to send it, fromChatID is the source chat,
61// and messageID is the ID of the original message.
62func NewForward(chatID int64, fromChatID int64, messageID int) ForwardConfig {
63 return ForwardConfig{
64 BaseChat: BaseChat{ChatConfig: ChatConfig{ChatID: chatID}},
65 FromChat: ChatConfig{ChatID: fromChatID},
66 MessageID: messageID,
67 }
68}
69
70// NewCopyMessage creates a new copy message.
71//
72// chatID is where to send it, fromChatID is the source chat,
73// and messageID is the ID of the original message.
74func NewCopyMessage(chatID int64, fromChatID int64, messageID int) CopyMessageConfig {
75 return CopyMessageConfig{
76 BaseChat: BaseChat{ChatConfig: ChatConfig{ChatID: chatID}},
77 FromChat: ChatConfig{ChatID: fromChatID},
78 MessageID: messageID,
79 }
80}
81
82// NewPhoto creates a new sendPhoto request.
83//
84// chatID is where to send it, file is a string path to the file,
85// FileReader, or FileBytes.
86//
87// Note that you must send animated GIFs as a document.
88func NewPhoto(chatID int64, file RequestFileData) PhotoConfig {
89 return PhotoConfig{
90 BaseFile: BaseFile{
91 BaseChat: BaseChat{ChatConfig: ChatConfig{ChatID: chatID}},
92 File: file,
93 },
94 }
95}
96
97// NewPhotoToChannel creates a new photo uploader to send a photo to a channel.
98//
99// Note that you must send animated GIFs as a document.
100func NewPhotoToChannel(username string, file RequestFileData) PhotoConfig {
101 return PhotoConfig{
102 BaseFile: BaseFile{
103 BaseChat: BaseChat{ChatConfig: ChatConfig{ChannelUsername: username}},
104 File: file,
105 },
106 }
107}
108
109// NewAudio creates a new sendAudio request.
110func NewAudio(chatID int64, file RequestFileData) AudioConfig {
111 return AudioConfig{
112 BaseFile: BaseFile{
113 BaseChat: BaseChat{ChatConfig: ChatConfig{ChatID: chatID}},
114 File: file,
115 },
116 }
117}
118
119// NewDocument creates a new sendDocument request.
120func NewDocument(chatID int64, file RequestFileData) DocumentConfig {
121 return DocumentConfig{
122 BaseFile: BaseFile{
123 BaseChat: BaseChat{ChatConfig: ChatConfig{ChatID: chatID}},
124 File: file,
125 },
126 }
127}
128
129// NewSticker creates a new sendSticker request.
130func NewSticker(chatID int64, file RequestFileData) StickerConfig {
131 return StickerConfig{
132 BaseFile: BaseFile{
133 BaseChat: BaseChat{ChatConfig: ChatConfig{ChatID: chatID}},
134 File: file,
135 },
136 }
137}
138
139// NewCustomEmojiStickerSetThumbnal creates a new setCustomEmojiStickerSetThumbnal request
140func NewCustomEmojiStickerSetThumbnal(name, customEmojiID string) SetCustomEmojiStickerSetThumbnailConfig {
141 return SetCustomEmojiStickerSetThumbnailConfig{
142 Name: name,
143 CustomEmojiID: customEmojiID,
144 }
145}
146
147// NewStickerSetTitle creates a new setStickerSetTitle request
148func NewStickerSetTitle(name, title string) SetStickerSetTitleConfig {
149 return SetStickerSetTitleConfig{
150 Name: name,
151 Title: title,
152 }
153}
154
155// NewDeleteStickerSet creates a new deleteStickerSet request
156func NewDeleteStickerSet(name, title string) DeleteStickerSetConfig {
157 return DeleteStickerSetConfig{
158 Name: name,
159 }
160}
161
162// NewVideo creates a new sendVideo request.
163func NewVideo(chatID int64, file RequestFileData) VideoConfig {
164 return VideoConfig{
165 BaseFile: BaseFile{
166 BaseChat: BaseChat{ChatConfig: ChatConfig{ChatID: chatID}},
167 File: file,
168 },
169 }
170}
171
172// NewAnimation creates a new sendAnimation request.
173func NewAnimation(chatID int64, file RequestFileData) AnimationConfig {
174 return AnimationConfig{
175 BaseFile: BaseFile{
176 BaseChat: BaseChat{ChatConfig: ChatConfig{ChatID: chatID}},
177 File: file,
178 },
179 }
180}
181
182// NewVideoNote creates a new sendVideoNote request.
183//
184// chatID is where to send it, file is a string path to the file,
185// FileReader, or FileBytes.
186func NewVideoNote(chatID int64, length int, file RequestFileData) VideoNoteConfig {
187 return VideoNoteConfig{
188 BaseFile: BaseFile{
189 BaseChat: BaseChat{ChatConfig: ChatConfig{ChatID: chatID}},
190 File: file,
191 },
192 Length: length,
193 }
194}
195
196// NewVoice creates a new sendVoice request.
197func NewVoice(chatID int64, file RequestFileData) VoiceConfig {
198 return VoiceConfig{
199 BaseFile: BaseFile{
200 BaseChat: BaseChat{ChatConfig: ChatConfig{ChatID: chatID}},
201 File: file,
202 },
203 }
204}
205
206// NewMediaGroup creates a new media group. Files should be an array of
207// two to ten InputMediaPhoto or InputMediaVideo.
208func NewMediaGroup(chatID int64, files []interface{}) MediaGroupConfig {
209 return MediaGroupConfig{
210 BaseChat: BaseChat{
211 ChatConfig: ChatConfig{ChatID: chatID},
212 },
213 Media: files,
214 }
215}
216
217// NewInputMediaPhoto creates a new InputMediaPhoto.
218func NewInputMediaPhoto(media RequestFileData) InputMediaPhoto {
219 return InputMediaPhoto{
220 BaseInputMedia{
221 Type: "photo",
222 Media: media,
223 },
224 }
225}
226
227// NewInputMediaVideo creates a new InputMediaVideo.
228func NewInputMediaVideo(media RequestFileData) InputMediaVideo {
229 return InputMediaVideo{
230 BaseInputMedia: BaseInputMedia{
231 Type: "video",
232 Media: media,
233 },
234 }
235}
236
237// NewInputMediaAnimation creates a new InputMediaAnimation.
238func NewInputMediaAnimation(media RequestFileData) InputMediaAnimation {
239 return InputMediaAnimation{
240 BaseInputMedia: BaseInputMedia{
241 Type: "animation",
242 Media: media,
243 },
244 }
245}
246
247// NewInputMediaAudio creates a new InputMediaAudio.
248func NewInputMediaAudio(media RequestFileData) InputMediaAudio {
249 return InputMediaAudio{
250 BaseInputMedia: BaseInputMedia{
251 Type: "audio",
252 Media: media,
253 },
254 }
255}
256
257// NewInputMediaDocument creates a new InputMediaDocument.
258func NewInputMediaDocument(media RequestFileData) InputMediaDocument {
259 return InputMediaDocument{
260 BaseInputMedia: BaseInputMedia{
261 Type: "document",
262 Media: media,
263 },
264 }
265}
266
267// NewContact allows you to send a shared contact.
268func NewContact(chatID int64, phoneNumber, firstName string) ContactConfig {
269 return ContactConfig{
270 BaseChat: BaseChat{
271 ChatConfig: ChatConfig{ChatID: chatID},
272 },
273 PhoneNumber: phoneNumber,
274 FirstName: firstName,
275 }
276}
277
278// NewLocation shares your location.
279//
280// chatID is where to send it, latitude and longitude are coordinates.
281func NewLocation(chatID int64, latitude float64, longitude float64) LocationConfig {
282 return LocationConfig{
283 BaseChat: BaseChat{
284 ChatConfig: ChatConfig{ChatID: chatID},
285 },
286 Latitude: latitude,
287 Longitude: longitude,
288 }
289}
290
291// NewVenue allows you to send a venue and its location.
292func NewVenue(chatID int64, title, address string, latitude, longitude float64) VenueConfig {
293 return VenueConfig{
294 BaseChat: BaseChat{
295 ChatConfig: ChatConfig{ChatID: chatID},
296 },
297 Title: title,
298 Address: address,
299 Latitude: latitude,
300 Longitude: longitude,
301 }
302}
303
304// NewChatAction sets a chat action.
305// Actions last for 5 seconds, or until your next action.
306//
307// chatID is where to send it, action should be set via Chat constants.
308func NewChatAction(chatID int64, action string) ChatActionConfig {
309 return ChatActionConfig{
310 BaseChat: BaseChat{ChatConfig: ChatConfig{ChatID: chatID}},
311 Action: action,
312 }
313}
314
315// NewUserProfilePhotos gets user profile photos.
316//
317// userID is the ID of the user you wish to get profile photos from.
318func NewUserProfilePhotos(userID int64) UserProfilePhotosConfig {
319 return UserProfilePhotosConfig{
320 UserID: userID,
321 Offset: 0,
322 Limit: 0,
323 }
324}
325
326// NewUpdate gets updates since the last Offset.
327//
328// offset is the last Update ID to include.
329// You likely want to set this to the last Update ID plus 1.
330func NewUpdate(offset int) UpdateConfig {
331 return UpdateConfig{
332 Offset: offset,
333 Limit: 0,
334 Timeout: 0,
335 }
336}
337
338// NewWebhook creates a new webhook.
339//
340// link is the url parsable link you wish to get the updates.
341func NewWebhook(link string) (WebhookConfig, error) {
342 u, err := url.Parse(link)
343
344 if err != nil {
345 return WebhookConfig{}, err
346 }
347
348 return WebhookConfig{
349 URL: u,
350 }, nil
351}
352
353// NewWebhookWithCert creates a new webhook with a certificate.
354//
355// link is the url you wish to get webhooks,
356// file contains a string to a file, FileReader, or FileBytes.
357func NewWebhookWithCert(link string, file RequestFileData) (WebhookConfig, error) {
358 u, err := url.Parse(link)
359
360 if err != nil {
361 return WebhookConfig{}, err
362 }
363
364 return WebhookConfig{
365 URL: u,
366 Certificate: file,
367 }, nil
368}
369
370// NewInlineQueryResultArticle creates a new inline query article.
371func NewInlineQueryResultArticle(id, title, messageText string) InlineQueryResultArticle {
372 return InlineQueryResultArticle{
373 Type: "article",
374 ID: id,
375 Title: title,
376 InputMessageContent: InputTextMessageContent{
377 Text: messageText,
378 },
379 }
380}
381
382// NewInlineQueryResultArticleMarkdown creates a new inline query article with Markdown parsing.
383func NewInlineQueryResultArticleMarkdown(id, title, messageText string) InlineQueryResultArticle {
384 return InlineQueryResultArticle{
385 Type: "article",
386 ID: id,
387 Title: title,
388 InputMessageContent: InputTextMessageContent{
389 Text: messageText,
390 ParseMode: "Markdown",
391 },
392 }
393}
394
395// NewInlineQueryResultArticleMarkdownV2 creates a new inline query article with MarkdownV2 parsing.
396func NewInlineQueryResultArticleMarkdownV2(id, title, messageText string) InlineQueryResultArticle {
397 return InlineQueryResultArticle{
398 Type: "article",
399 ID: id,
400 Title: title,
401 InputMessageContent: InputTextMessageContent{
402 Text: messageText,
403 ParseMode: "MarkdownV2",
404 },
405 }
406}
407
408// NewInlineQueryResultArticleHTML creates a new inline query article with HTML parsing.
409func NewInlineQueryResultArticleHTML(id, title, messageText string) InlineQueryResultArticle {
410 return InlineQueryResultArticle{
411 Type: "article",
412 ID: id,
413 Title: title,
414 InputMessageContent: InputTextMessageContent{
415 Text: messageText,
416 ParseMode: "HTML",
417 },
418 }
419}
420
421// NewInlineQueryResultGIF creates a new inline query GIF.
422func NewInlineQueryResultGIF(id, url string) InlineQueryResultGIF {
423 return InlineQueryResultGIF{
424 Type: "gif",
425 ID: id,
426 URL: url,
427 }
428}
429
430// NewInlineQueryResultCachedGIF create a new inline query with cached photo.
431func NewInlineQueryResultCachedGIF(id, gifID string) InlineQueryResultCachedGIF {
432 return InlineQueryResultCachedGIF{
433 Type: "gif",
434 ID: id,
435 GIFID: gifID,
436 }
437}
438
439// NewInlineQueryResultMPEG4GIF creates a new inline query MPEG4 GIF.
440func NewInlineQueryResultMPEG4GIF(id, url string) InlineQueryResultMPEG4GIF {
441 return InlineQueryResultMPEG4GIF{
442 Type: "mpeg4_gif",
443 ID: id,
444 URL: url,
445 }
446}
447
448// NewInlineQueryResultCachedMPEG4GIF create a new inline query with cached MPEG4 GIF.
449func NewInlineQueryResultCachedMPEG4GIF(id, MPEG4GIFID string) InlineQueryResultCachedMPEG4GIF {
450 return InlineQueryResultCachedMPEG4GIF{
451 Type: "mpeg4_gif",
452 ID: id,
453 MPEG4FileID: MPEG4GIFID,
454 }
455}
456
457// NewInlineQueryResultPhoto creates a new inline query photo.
458func NewInlineQueryResultPhoto(id, url string) InlineQueryResultPhoto {
459 return InlineQueryResultPhoto{
460 Type: "photo",
461 ID: id,
462 URL: url,
463 }
464}
465
466// NewInlineQueryResultPhotoWithThumb creates a new inline query photo.
467func NewInlineQueryResultPhotoWithThumb(id, url, thumb string) InlineQueryResultPhoto {
468 return InlineQueryResultPhoto{
469 Type: "photo",
470 ID: id,
471 URL: url,
472 ThumbURL: thumb,
473 }
474}
475
476// NewInlineQueryResultCachedPhoto create a new inline query with cached photo.
477func NewInlineQueryResultCachedPhoto(id, photoID string) InlineQueryResultCachedPhoto {
478 return InlineQueryResultCachedPhoto{
479 Type: "photo",
480 ID: id,
481 PhotoID: photoID,
482 }
483}
484
485// NewInlineQueryResultVideo creates a new inline query video.
486func NewInlineQueryResultVideo(id, url string) InlineQueryResultVideo {
487 return InlineQueryResultVideo{
488 Type: "video",
489 ID: id,
490 URL: url,
491 }
492}
493
494// NewInlineQueryResultCachedVideo create a new inline query with cached video.
495func NewInlineQueryResultCachedVideo(id, videoID, title string) InlineQueryResultCachedVideo {
496 return InlineQueryResultCachedVideo{
497 Type: "video",
498 ID: id,
499 VideoID: videoID,
500 Title: title,
501 }
502}
503
504// NewInlineQueryResultCachedSticker create a new inline query with cached sticker.
505func NewInlineQueryResultCachedSticker(id, stickerID, title string) InlineQueryResultCachedSticker {
506 return InlineQueryResultCachedSticker{
507 Type: "sticker",
508 ID: id,
509 StickerID: stickerID,
510 Title: title,
511 }
512}
513
514// NewInlineQueryResultAudio creates a new inline query audio.
515func NewInlineQueryResultAudio(id, url, title string) InlineQueryResultAudio {
516 return InlineQueryResultAudio{
517 Type: "audio",
518 ID: id,
519 URL: url,
520 Title: title,
521 }
522}
523
524// NewInlineQueryResultCachedAudio create a new inline query with cached photo.
525func NewInlineQueryResultCachedAudio(id, audioID string) InlineQueryResultCachedAudio {
526 return InlineQueryResultCachedAudio{
527 Type: "audio",
528 ID: id,
529 AudioID: audioID,
530 }
531}
532
533// NewInlineQueryResultVoice creates a new inline query voice.
534func NewInlineQueryResultVoice(id, url, title string) InlineQueryResultVoice {
535 return InlineQueryResultVoice{
536 Type: "voice",
537 ID: id,
538 URL: url,
539 Title: title,
540 }
541}
542
543// NewInlineQueryResultCachedVoice create a new inline query with cached photo.
544func NewInlineQueryResultCachedVoice(id, voiceID, title string) InlineQueryResultCachedVoice {
545 return InlineQueryResultCachedVoice{
546 Type: "voice",
547 ID: id,
548 VoiceID: voiceID,
549 Title: title,
550 }
551}
552
553// NewInlineQueryResultDocument creates a new inline query document.
554func NewInlineQueryResultDocument(id, url, title, mimeType string) InlineQueryResultDocument {
555 return InlineQueryResultDocument{
556 Type: "document",
557 ID: id,
558 URL: url,
559 Title: title,
560 MimeType: mimeType,
561 }
562}
563
564// NewInlineQueryResultCachedDocument create a new inline query with cached photo.
565func NewInlineQueryResultCachedDocument(id, documentID, title string) InlineQueryResultCachedDocument {
566 return InlineQueryResultCachedDocument{
567 Type: "document",
568 ID: id,
569 DocumentID: documentID,
570 Title: title,
571 }
572}
573
574// NewInlineQueryResultLocation creates a new inline query location.
575func NewInlineQueryResultLocation(id, title string, latitude, longitude float64) InlineQueryResultLocation {
576 return InlineQueryResultLocation{
577 Type: "location",
578 ID: id,
579 Title: title,
580 Latitude: latitude,
581 Longitude: longitude,
582 }
583}
584
585// NewInlineQueryResultVenue creates a new inline query venue.
586func NewInlineQueryResultVenue(id, title, address string, latitude, longitude float64) InlineQueryResultVenue {
587 return InlineQueryResultVenue{
588 Type: "venue",
589 ID: id,
590 Title: title,
591 Address: address,
592 Latitude: latitude,
593 Longitude: longitude,
594 }
595}
596
597// NewEditMessageText allows you to edit the text of a message.
598func NewEditMessageText(chatID int64, messageID int, text string) EditMessageTextConfig {
599 return EditMessageTextConfig{
600 BaseEdit: BaseEdit{
601 BaseChatMessage: BaseChatMessage{
602 ChatConfig: ChatConfig{
603 ChatID: chatID,
604 },
605 MessageID: messageID,
606 },
607 },
608 Text: text,
609 }
610}
611
612// NewEditMessageTextAndMarkup allows you to edit the text and reply markup of a message.
613func NewEditMessageTextAndMarkup(chatID int64, messageID int, text string, replyMarkup InlineKeyboardMarkup) EditMessageTextConfig {
614 return EditMessageTextConfig{
615 BaseEdit: BaseEdit{
616 BaseChatMessage: BaseChatMessage{
617 ChatConfig: ChatConfig{
618 ChatID: chatID,
619 },
620 MessageID: messageID,
621 },
622 ReplyMarkup: &replyMarkup,
623 },
624 Text: text,
625 }
626}
627
628// NewEditMessageCaption allows you to edit the caption of a message.
629func NewEditMessageCaption(chatID int64, messageID int, caption string) EditMessageCaptionConfig {
630 return EditMessageCaptionConfig{
631 BaseEdit: BaseEdit{
632 BaseChatMessage: BaseChatMessage{
633 ChatConfig: ChatConfig{
634 ChatID: chatID,
635 },
636 MessageID: messageID,
637 },
638 },
639 Caption: caption,
640 }
641}
642
643// NewEditMessageReplyMarkup allows you to edit the inline
644// keyboard markup.
645func NewEditMessageReplyMarkup(chatID int64, messageID int, replyMarkup InlineKeyboardMarkup) EditMessageReplyMarkupConfig {
646 return EditMessageReplyMarkupConfig{
647 BaseEdit: BaseEdit{
648 BaseChatMessage: BaseChatMessage{
649 ChatConfig: ChatConfig{
650 ChatID: chatID,
651 },
652 MessageID: messageID,
653 },
654 ReplyMarkup: &replyMarkup,
655 },
656 }
657}
658
659// NewRemoveKeyboard hides the keyboard, with the option for being selective
660// or hiding for everyone.
661func NewRemoveKeyboard(selective bool) ReplyKeyboardRemove {
662 return ReplyKeyboardRemove{
663 RemoveKeyboard: true,
664 Selective: selective,
665 }
666}
667
668// NewKeyboardButton creates a regular keyboard button.
669func NewKeyboardButton(text string) KeyboardButton {
670 return KeyboardButton{
671 Text: text,
672 }
673}
674
675// NewKeyboardButtonWebApp creates a keyboard button with text
676// which goes to a WebApp.
677func NewKeyboardButtonWebApp(text string, webapp WebAppInfo) KeyboardButton {
678 return KeyboardButton{
679 Text: text,
680 WebApp: &webapp,
681 }
682}
683
684// NewKeyboardButtonContact creates a keyboard button that requests
685// user contact information upon click.
686func NewKeyboardButtonContact(text string) KeyboardButton {
687 return KeyboardButton{
688 Text: text,
689 RequestContact: true,
690 }
691}
692
693// NewKeyboardButtonLocation creates a keyboard button that requests
694// user location information upon click.
695func NewKeyboardButtonLocation(text string) KeyboardButton {
696 return KeyboardButton{
697 Text: text,
698 RequestLocation: true,
699 }
700}
701
702// NewKeyboardButtonRow creates a row of keyboard buttons.
703func NewKeyboardButtonRow(buttons ...KeyboardButton) []KeyboardButton {
704 var row []KeyboardButton
705
706 row = append(row, buttons...)
707
708 return row
709}
710
711// NewReplyKeyboard creates a new regular keyboard with sane defaults.
712func NewReplyKeyboard(rows ...[]KeyboardButton) ReplyKeyboardMarkup {
713 var keyboard [][]KeyboardButton
714
715 keyboard = append(keyboard, rows...)
716
717 return ReplyKeyboardMarkup{
718 ResizeKeyboard: true,
719 Keyboard: keyboard,
720 }
721}
722
723// NewOneTimeReplyKeyboard creates a new one time keyboard.
724func NewOneTimeReplyKeyboard(rows ...[]KeyboardButton) ReplyKeyboardMarkup {
725 markup := NewReplyKeyboard(rows...)
726 markup.OneTimeKeyboard = true
727 return markup
728}
729
730// NewInlineKeyboardButtonData creates an inline keyboard button with text
731// and data for a callback.
732func NewInlineKeyboardButtonData(text, data string) InlineKeyboardButton {
733 return InlineKeyboardButton{
734 Text: text,
735 CallbackData: &data,
736 }
737}
738
739// NewInlineKeyboardButtonWebApp creates an inline keyboard button with text
740// which goes to a WebApp.
741func NewInlineKeyboardButtonWebApp(text string, webapp WebAppInfo) InlineKeyboardButton {
742 return InlineKeyboardButton{
743 Text: text,
744 WebApp: &webapp,
745 }
746}
747
748// NewInlineKeyboardButtonSwitchInlineQueryChoosenChat creates an inline keyboard button with text
749// which goes to a SwitchInlineQueryChosenChat.
750func NewInlineKeyboardButtonSwitchInlineQueryChoosenChat(text string, switchInlineQueryChosenChat SwitchInlineQueryChosenChat) InlineKeyboardButton {
751 return InlineKeyboardButton{
752 Text: text,
753 SwitchInlineQueryChosenChat: &switchInlineQueryChosenChat,
754 }
755}
756
757// NewInlineKeyboardButtonLoginURL creates an inline keyboard button with text
758// which goes to a LoginURL.
759func NewInlineKeyboardButtonLoginURL(text string, loginURL LoginURL) InlineKeyboardButton {
760 return InlineKeyboardButton{
761 Text: text,
762 LoginURL: &loginURL,
763 }
764}
765
766// NewInlineKeyboardButtonURL creates an inline keyboard button with text
767// which goes to a URL.
768func NewInlineKeyboardButtonURL(text, url string) InlineKeyboardButton {
769 return InlineKeyboardButton{
770 Text: text,
771 URL: &url,
772 }
773}
774
775// NewInlineKeyboardButtonSwitch creates an inline keyboard button with
776// text which allows the user to switch to a chat or return to a chat.
777func NewInlineKeyboardButtonSwitch(text, sw string) InlineKeyboardButton {
778 return InlineKeyboardButton{
779 Text: text,
780 SwitchInlineQuery: &sw,
781 }
782}
783
784// NewInlineKeyboardRow creates an inline keyboard row with buttons.
785func NewInlineKeyboardRow(buttons ...InlineKeyboardButton) []InlineKeyboardButton {
786 var row []InlineKeyboardButton
787
788 row = append(row, buttons...)
789
790 return row
791}
792
793// NewInlineKeyboardMarkup creates a new inline keyboard.
794func NewInlineKeyboardMarkup(rows ...[]InlineKeyboardButton) InlineKeyboardMarkup {
795 var keyboard [][]InlineKeyboardButton
796
797 keyboard = append(keyboard, rows...)
798
799 return InlineKeyboardMarkup{
800 InlineKeyboard: keyboard,
801 }
802}
803
804// NewCallback creates a new callback message.
805func NewCallback(id, text string) CallbackConfig {
806 return CallbackConfig{
807 CallbackQueryID: id,
808 Text: text,
809 ShowAlert: false,
810 }
811}
812
813// NewCallbackWithAlert creates a new callback message that alerts
814// the user.
815func NewCallbackWithAlert(id, text string) CallbackConfig {
816 return CallbackConfig{
817 CallbackQueryID: id,
818 Text: text,
819 ShowAlert: true,
820 }
821}
822
823// NewInvoice creates a new Invoice request to the user.
824func NewInvoice(chatID int64, title, description, payload, providerToken, startParameter, currency string, prices []LabeledPrice) InvoiceConfig {
825 return InvoiceConfig{
826 BaseChat: BaseChat{
827 ChatConfig: ChatConfig{ChatID: chatID},
828 },
829 Title: title,
830 Description: description,
831 Payload: payload,
832 ProviderToken: providerToken,
833 StartParameter: startParameter,
834 Currency: currency,
835 Prices: prices}
836}
837
838// NewChatTitle allows you to update the title of a chat.
839func NewChatTitle(chatID int64, title string) SetChatTitleConfig {
840 return SetChatTitleConfig{
841 ChatConfig: ChatConfig{
842 ChatID: chatID,
843 },
844 Title: title,
845 }
846}
847
848// NewChatDescription allows you to update the description of a chat.
849func NewChatDescription(chatID int64, description string) SetChatDescriptionConfig {
850 return SetChatDescriptionConfig{
851 ChatConfig: ChatConfig{
852 ChatID: chatID,
853 },
854 Description: description,
855 }
856}
857
858// NewChatPhoto allows you to update the photo for a chat.
859func NewChatPhoto(chatID int64, photo RequestFileData) SetChatPhotoConfig {
860 return SetChatPhotoConfig{
861 BaseFile: BaseFile{
862 BaseChat: BaseChat{
863 ChatConfig: ChatConfig{ChatID: chatID},
864 },
865 File: photo,
866 },
867 }
868}
869
870// NewDeleteChatPhoto allows you to delete the photo for a chat.
871func NewDeleteChatPhoto(chatID int64) DeleteChatPhotoConfig {
872 return DeleteChatPhotoConfig{
873 ChatConfig: ChatConfig{
874 ChatID: chatID,
875 },
876 }
877}
878
879// NewPoll allows you to create a new poll.
880func NewPoll(chatID int64, question string, options ...string) SendPollConfig {
881 return SendPollConfig{
882 BaseChat: BaseChat{
883 ChatConfig: ChatConfig{ChatID: chatID},
884 },
885 Question: question,
886 Options: options,
887 IsAnonymous: true, // This is Telegram's default.
888 }
889}
890
891// NewStopPoll allows you to stop a poll.
892func NewStopPoll(chatID int64, messageID int) StopPollConfig {
893 return StopPollConfig{
894 BaseEdit{
895 BaseChatMessage: BaseChatMessage{
896 ChatConfig: ChatConfig{
897 ChatID: chatID,
898 },
899 MessageID: messageID,
900 },
901 },
902 }
903}
904
905// NewDice allows you to send a random dice roll.
906func NewDice(chatID int64) DiceConfig {
907 return DiceConfig{
908 BaseChat: BaseChat{
909 ChatConfig: ChatConfig{ChatID: chatID},
910 },
911 }
912}
913
914// NewDiceWithEmoji allows you to send a random roll of one of many types.
915//
916// Emoji may be 🎲 (1-6), 🎯 (1-6), or 🏀 (1-5).
917func NewDiceWithEmoji(chatID int64, emoji string) DiceConfig {
918 return DiceConfig{
919 BaseChat: BaseChat{
920 ChatConfig: ChatConfig{ChatID: chatID},
921 },
922 Emoji: emoji,
923 }
924}
925
926// NewBotCommandScopeDefault represents the default scope of bot commands.
927func NewBotCommandScopeDefault() BotCommandScope {
928 return BotCommandScope{Type: "default"}
929}
930
931// NewBotCommandScopeAllPrivateChats represents the scope of bot commands,
932// covering all private chats.
933func NewBotCommandScopeAllPrivateChats() BotCommandScope {
934 return BotCommandScope{Type: "all_private_chats"}
935}
936
937// NewBotCommandScopeAllGroupChats represents the scope of bot commands,
938// covering all group and supergroup chats.
939func NewBotCommandScopeAllGroupChats() BotCommandScope {
940 return BotCommandScope{Type: "all_group_chats"}
941}
942
943// NewBotCommandScopeAllChatAdministrators represents the scope of bot commands,
944// covering all group and supergroup chat administrators.
945func NewBotCommandScopeAllChatAdministrators() BotCommandScope {
946 return BotCommandScope{Type: "all_chat_administrators"}
947}
948
949// NewBotCommandScopeChat represents the scope of bot commands, covering a
950// specific chat.
951func NewBotCommandScopeChat(chatID int64) BotCommandScope {
952 return BotCommandScope{
953 Type: "chat",
954 ChatID: chatID,
955 }
956}
957
958// NewBotCommandScopeChatAdministrators represents the scope of bot commands,
959// covering all administrators of a specific group or supergroup chat.
960func NewBotCommandScopeChatAdministrators(chatID int64) BotCommandScope {
961 return BotCommandScope{
962 Type: "chat_administrators",
963 ChatID: chatID,
964 }
965}
966
967// NewBotCommandScopeChatMember represents the scope of bot commands, covering a
968// specific member of a group or supergroup chat.
969func NewBotCommandScopeChatMember(chatID, userID int64) BotCommandScope {
970 return BotCommandScope{
971 Type: "chat_member",
972 ChatID: chatID,
973 UserID: userID,
974 }
975}
976
977// NewSetMyDescription allows you to change the bot's description, which is shown in the chat with the bot if the chat is empty.
978func NewSetMyDescription(description, languageCode string) SetMyDescriptionConfig {
979 return SetMyDescriptionConfig{
980 Description: description,
981 LanguageCode: languageCode,
982 }
983}
984
985// NewGetMyDescription returns the current bot description for the given user language
986func NewGetMyDescription(languageCode string) GetMyDescriptionConfig {
987 return GetMyDescriptionConfig{
988 LanguageCode: languageCode,
989 }
990}
991
992// NewSetMyShortDescription allows you change the bot's short description, which is shown on the bot's profile page and is sent together with the link when users share the bot.
993func NewSetMyShortDescription(shortDescription, languageCode string) SetMyShortDescriptionConfig {
994 return SetMyShortDescriptionConfig{
995 ShortDescription: shortDescription,
996 LanguageCode: languageCode,
997 }
998}
999
1000// NewGetMyShortDescription returns the current bot short description for the given user language.
1001func NewGetMyShortDescription(languageCode string) GetMyShortDescriptionConfig {
1002 return GetMyShortDescriptionConfig{
1003 LanguageCode: languageCode,
1004 }
1005}
1006
1007// NewGetMyName get the current bot name for the given user language
1008func NewGetMyName(languageCode string) GetMyNameConfig {
1009 return GetMyNameConfig{
1010 LanguageCode: languageCode,
1011 }
1012}
1013
1014// NewSetMyName change the bot's name
1015func NewSetMyName(languageCode, name string) SetMyNameConfig {
1016 return SetMyNameConfig{
1017 Name: name,
1018 LanguageCode: languageCode,
1019 }
1020}
1021
1022// NewGetMyCommandsWithScope allows you to set the registered commands for a
1023// given scope.
1024func NewGetMyCommandsWithScope(scope BotCommandScope) GetMyCommandsConfig {
1025 return GetMyCommandsConfig{Scope: &scope}
1026}
1027
1028// NewGetMyCommandsWithScopeAndLanguage allows you to set the registered
1029// commands for a given scope and language code.
1030func NewGetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) GetMyCommandsConfig {
1031 return GetMyCommandsConfig{Scope: &scope, LanguageCode: languageCode}
1032}
1033
1034// NewSetMyCommands allows you to set the registered commands.
1035func NewSetMyCommands(commands ...BotCommand) SetMyCommandsConfig {
1036 return SetMyCommandsConfig{Commands: commands}
1037}
1038
1039// NewSetMyCommandsWithScope allows you to set the registered commands for a given scope.
1040func NewSetMyCommandsWithScope(scope BotCommandScope, commands ...BotCommand) SetMyCommandsConfig {
1041 return SetMyCommandsConfig{Commands: commands, Scope: &scope}
1042}
1043
1044// NewSetMyCommandsWithScopeAndLanguage allows you to set the registered commands for a given scope
1045// and language code.
1046func NewSetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string, commands ...BotCommand) SetMyCommandsConfig {
1047 return SetMyCommandsConfig{Commands: commands, Scope: &scope, LanguageCode: languageCode}
1048}
1049
1050// NewDeleteMyCommands allows you to delete the registered commands.
1051func NewDeleteMyCommands() DeleteMyCommandsConfig {
1052 return DeleteMyCommandsConfig{}
1053}
1054
1055// NewDeleteMyCommandsWithScope allows you to delete the registered commands for a given
1056// scope.
1057func NewDeleteMyCommandsWithScope(scope BotCommandScope) DeleteMyCommandsConfig {
1058 return DeleteMyCommandsConfig{Scope: &scope}
1059}
1060
1061// NewDeleteMyCommandsWithScopeAndLanguage allows you to delete the registered commands for a given
1062// scope and language code.
1063func NewDeleteMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) DeleteMyCommandsConfig {
1064 return DeleteMyCommandsConfig{Scope: &scope, LanguageCode: languageCode}
1065}
1066
1067// ValidateWebAppData validate data received via the Web App
1068// https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
1069func ValidateWebAppData(token, telegramInitData string) (bool, error) {
1070 initData, err := url.ParseQuery(telegramInitData)
1071 if err != nil {
1072 return false, fmt.Errorf("error parsing data %w", err)
1073 }
1074
1075 dataCheckString := make([]string, 0, len(initData))
1076 for k, v := range initData {
1077 if k == "hash" {
1078 continue
1079 }
1080 if len(v) > 0 {
1081 dataCheckString = append(dataCheckString, fmt.Sprintf("%s=%s", k, v[0]))
1082 }
1083 }
1084
1085 sort.Strings(dataCheckString)
1086
1087 secret := hmac.New(sha256.New, []byte("WebAppData"))
1088 secret.Write([]byte(token))
1089
1090 hHash := hmac.New(sha256.New, secret.Sum(nil))
1091 hHash.Write([]byte(strings.Join(dataCheckString, "\n")))
1092
1093 hash := hex.EncodeToString(hHash.Sum(nil))
1094
1095 if initData.Get("hash") != hash {
1096 return false, errors.New("hash not equal")
1097 }
1098
1099 return true, nil
1100}