all repos — telegram-bot-api @ 1451a8b7e25973c0e75ff0343cd299410dc45290

Golang bindings for the Telegram Bot API

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 reply markup 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// NewKeyboardButtonWebApp creates a keyboard button with text
629// which goes to a WebApp.
630func NewKeyboardButtonWebApp(text string, webapp WebAppInfo) KeyboardButton {
631	return KeyboardButton{
632		Text:   text,
633		WebApp: &webapp,
634	}
635}
636
637// NewKeyboardButtonContact creates a keyboard button that requests
638// user contact information upon click.
639func NewKeyboardButtonContact(text string) KeyboardButton {
640	return KeyboardButton{
641		Text:           text,
642		RequestContact: true,
643	}
644}
645
646// NewKeyboardButtonLocation creates a keyboard button that requests
647// user location information upon click.
648func NewKeyboardButtonLocation(text string) KeyboardButton {
649	return KeyboardButton{
650		Text:            text,
651		RequestLocation: true,
652	}
653}
654
655// NewKeyboardButtonRow creates a row of keyboard buttons.
656func NewKeyboardButtonRow(buttons ...KeyboardButton) []KeyboardButton {
657	var row []KeyboardButton
658
659	row = append(row, buttons...)
660
661	return row
662}
663
664// NewReplyKeyboard creates a new regular keyboard with sane defaults.
665func NewReplyKeyboard(rows ...[]KeyboardButton) ReplyKeyboardMarkup {
666	var keyboard [][]KeyboardButton
667
668	keyboard = append(keyboard, rows...)
669
670	return ReplyKeyboardMarkup{
671		ResizeKeyboard: true,
672		Keyboard:       keyboard,
673	}
674}
675
676// NewOneTimeReplyKeyboard creates a new one time keyboard.
677func NewOneTimeReplyKeyboard(rows ...[]KeyboardButton) ReplyKeyboardMarkup {
678	markup := NewReplyKeyboard(rows...)
679	markup.OneTimeKeyboard = true
680	return markup
681}
682
683// NewInlineKeyboardButtonData creates an inline keyboard button with text
684// and data for a callback.
685func NewInlineKeyboardButtonData(text, data string) InlineKeyboardButton {
686	return InlineKeyboardButton{
687		Text:         text,
688		CallbackData: &data,
689	}
690}
691
692// NewInlineKeyboardButtonWebApp creates an inline keyboard button with text
693// which goes to a WebApp.
694func NewInlineKeyboardButtonWebApp(text string, webapp WebAppInfo) InlineKeyboardButton {
695	return InlineKeyboardButton{
696		Text:   text,
697		WebApp: &webapp,
698	}
699}
700
701// NewInlineKeyboardButtonLoginURL creates an inline keyboard button with text
702// which goes to a LoginURL.
703func NewInlineKeyboardButtonLoginURL(text string, loginURL LoginURL) InlineKeyboardButton {
704	return InlineKeyboardButton{
705		Text:     text,
706		LoginURL: &loginURL,
707	}
708}
709
710// NewInlineKeyboardButtonURL creates an inline keyboard button with text
711// which goes to a URL.
712func NewInlineKeyboardButtonURL(text, url string) InlineKeyboardButton {
713	return InlineKeyboardButton{
714		Text: text,
715		URL:  &url,
716	}
717}
718
719// NewInlineKeyboardButtonSwitch creates an inline keyboard button with
720// text which allows the user to switch to a chat or return to a chat.
721func NewInlineKeyboardButtonSwitch(text, sw string) InlineKeyboardButton {
722	return InlineKeyboardButton{
723		Text:              text,
724		SwitchInlineQuery: &sw,
725	}
726}
727
728// NewInlineKeyboardRow creates an inline keyboard row with buttons.
729func NewInlineKeyboardRow(buttons ...InlineKeyboardButton) []InlineKeyboardButton {
730	var row []InlineKeyboardButton
731
732	row = append(row, buttons...)
733
734	return row
735}
736
737// NewInlineKeyboardMarkup creates a new inline keyboard.
738func NewInlineKeyboardMarkup(rows ...[]InlineKeyboardButton) InlineKeyboardMarkup {
739	var keyboard [][]InlineKeyboardButton
740
741	keyboard = append(keyboard, rows...)
742
743	return InlineKeyboardMarkup{
744		InlineKeyboard: keyboard,
745	}
746}
747
748// NewCallback creates a new callback message.
749func NewCallback(id, text string) CallbackConfig {
750	return CallbackConfig{
751		CallbackQueryID: id,
752		Text:            text,
753		ShowAlert:       false,
754	}
755}
756
757// NewCallbackWithAlert creates a new callback message that alerts
758// the user.
759func NewCallbackWithAlert(id, text string) CallbackConfig {
760	return CallbackConfig{
761		CallbackQueryID: id,
762		Text:            text,
763		ShowAlert:       true,
764	}
765}
766
767// NewInvoice creates a new Invoice request to the user.
768func NewInvoice(chatID int64, title, description, payload, providerToken, startParameter, currency string, prices []LabeledPrice) InvoiceConfig {
769	return InvoiceConfig{
770		BaseChat:       BaseChat{ChatID: chatID},
771		Title:          title,
772		Description:    description,
773		Payload:        payload,
774		ProviderToken:  providerToken,
775		StartParameter: startParameter,
776		Currency:       currency,
777		Prices:         prices}
778}
779
780// NewChatTitle allows you to update the title of a chat.
781func NewChatTitle(chatID int64, title string) SetChatTitleConfig {
782	return SetChatTitleConfig{
783		ChatID: chatID,
784		Title:  title,
785	}
786}
787
788// NewChatDescription allows you to update the description of a chat.
789func NewChatDescription(chatID int64, description string) SetChatDescriptionConfig {
790	return SetChatDescriptionConfig{
791		ChatID:      chatID,
792		Description: description,
793	}
794}
795
796// NewChatPhoto allows you to update the photo for a chat.
797func NewChatPhoto(chatID int64, photo RequestFileData) SetChatPhotoConfig {
798	return SetChatPhotoConfig{
799		BaseFile: BaseFile{
800			BaseChat: BaseChat{
801				ChatID: chatID,
802			},
803			File: photo,
804		},
805	}
806}
807
808// NewDeleteChatPhoto allows you to delete the photo for a chat.
809func NewDeleteChatPhoto(chatID int64) DeleteChatPhotoConfig {
810	return DeleteChatPhotoConfig{
811		ChatID: chatID,
812	}
813}
814
815// NewPoll allows you to create a new poll.
816func NewPoll(chatID int64, question string, options ...string) SendPollConfig {
817	return SendPollConfig{
818		BaseChat: BaseChat{
819			ChatID: chatID,
820		},
821		Question:    question,
822		Options:     options,
823		IsAnonymous: true, // This is Telegram's default.
824	}
825}
826
827// NewStopPoll allows you to stop a poll.
828func NewStopPoll(chatID int64, messageID int) StopPollConfig {
829	return StopPollConfig{
830		BaseEdit{
831			ChatID:    chatID,
832			MessageID: messageID,
833		},
834	}
835}
836
837// NewDice allows you to send a random dice roll.
838func NewDice(chatID int64) DiceConfig {
839	return DiceConfig{
840		BaseChat: BaseChat{
841			ChatID: chatID,
842		},
843	}
844}
845
846// NewDiceWithEmoji allows you to send a random roll of one of many types.
847//
848// Emoji may be 🎲 (1-6), 🎯 (1-6), or 🏀 (1-5).
849func NewDiceWithEmoji(chatID int64, emoji string) DiceConfig {
850	return DiceConfig{
851		BaseChat: BaseChat{
852			ChatID: chatID,
853		},
854		Emoji: emoji,
855	}
856}
857
858// NewBotCommandScopeDefault represents the default scope of bot commands.
859func NewBotCommandScopeDefault() BotCommandScope {
860	return BotCommandScope{Type: "default"}
861}
862
863// NewBotCommandScopeAllPrivateChats represents the scope of bot commands,
864// covering all private chats.
865func NewBotCommandScopeAllPrivateChats() BotCommandScope {
866	return BotCommandScope{Type: "all_private_chats"}
867}
868
869// NewBotCommandScopeAllGroupChats represents the scope of bot commands,
870// covering all group and supergroup chats.
871func NewBotCommandScopeAllGroupChats() BotCommandScope {
872	return BotCommandScope{Type: "all_group_chats"}
873}
874
875// NewBotCommandScopeAllChatAdministrators represents the scope of bot commands,
876// covering all group and supergroup chat administrators.
877func NewBotCommandScopeAllChatAdministrators() BotCommandScope {
878	return BotCommandScope{Type: "all_chat_administrators"}
879}
880
881// NewBotCommandScopeChat represents the scope of bot commands, covering a
882// specific chat.
883func NewBotCommandScopeChat(chatID int64) BotCommandScope {
884	return BotCommandScope{
885		Type:   "chat",
886		ChatID: chatID,
887	}
888}
889
890// NewBotCommandScopeChatAdministrators represents the scope of bot commands,
891// covering all administrators of a specific group or supergroup chat.
892func NewBotCommandScopeChatAdministrators(chatID int64) BotCommandScope {
893	return BotCommandScope{
894		Type:   "chat_administrators",
895		ChatID: chatID,
896	}
897}
898
899// NewBotCommandScopeChatMember represents the scope of bot commands, covering a
900// specific member of a group or supergroup chat.
901func NewBotCommandScopeChatMember(chatID, userID int64) BotCommandScope {
902	return BotCommandScope{
903		Type:   "chat_member",
904		ChatID: chatID,
905		UserID: userID,
906	}
907}
908
909// NewGetMyCommandsWithScope allows you to set the registered commands for a
910// given scope.
911func NewGetMyCommandsWithScope(scope BotCommandScope) GetMyCommandsConfig {
912	return GetMyCommandsConfig{Scope: &scope}
913}
914
915// NewGetMyCommandsWithScopeAndLanguage allows you to set the registered
916// commands for a given scope and language code.
917func NewGetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) GetMyCommandsConfig {
918	return GetMyCommandsConfig{Scope: &scope, LanguageCode: languageCode}
919}
920
921// NewSetMyCommands allows you to set the registered commands.
922func NewSetMyCommands(commands ...BotCommand) SetMyCommandsConfig {
923	return SetMyCommandsConfig{Commands: commands}
924}
925
926// NewSetMyCommandsWithScope allows you to set the registered commands for a given scope.
927func NewSetMyCommandsWithScope(scope BotCommandScope, commands ...BotCommand) SetMyCommandsConfig {
928	return SetMyCommandsConfig{Commands: commands, Scope: &scope}
929}
930
931// NewSetMyCommandsWithScopeAndLanguage allows you to set the registered commands for a given scope
932// and language code.
933func NewSetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string, commands ...BotCommand) SetMyCommandsConfig {
934	return SetMyCommandsConfig{Commands: commands, Scope: &scope, LanguageCode: languageCode}
935}
936
937// NewDeleteMyCommands allows you to delete the registered commands.
938func NewDeleteMyCommands() DeleteMyCommandsConfig {
939	return DeleteMyCommandsConfig{}
940}
941
942// NewDeleteMyCommandsWithScope allows you to delete the registered commands for a given
943// scope.
944func NewDeleteMyCommandsWithScope(scope BotCommandScope) DeleteMyCommandsConfig {
945	return DeleteMyCommandsConfig{Scope: &scope}
946}
947
948// NewDeleteMyCommandsWithScopeAndLanguage allows you to delete the registered commands for a given
949// scope and language code.
950func NewDeleteMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) DeleteMyCommandsConfig {
951	return DeleteMyCommandsConfig{Scope: &scope, LanguageCode: languageCode}
952}
953
954// ValidateWebAppData validate data received via the Web App
955// https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
956func ValidateWebAppData(token, telegramInitData string) (bool, error) {
957	initData, err := url.ParseQuery(telegramInitData)
958	if err != nil {
959		return false, fmt.Errorf("error parsing data %w", err)
960	}
961
962	dataCheckString := make([]string, 0, len(initData))
963	for k, v := range initData {
964		if k == "hash" {
965			continue
966		}
967		if len(v) > 0 {
968			dataCheckString = append(dataCheckString, fmt.Sprintf("%s=%s", k, v[0]))
969		}
970	}
971
972	sort.Strings(dataCheckString)
973
974	secret := hmac.New(sha256.New, []byte("WebAppData"))
975	secret.Write([]byte(token))
976
977	hHash := hmac.New(sha256.New, secret.Sum(nil))
978	hHash.Write([]byte(strings.Join(dataCheckString, "\n")))
979
980	hash := hex.EncodeToString(hHash.Sum(nil))
981
982	if initData.Get("hash") != hash {
983		return false, errors.New("hash not equal")
984	}
985
986	return true, nil
987}