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