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