all repos — telegram-bot-api @ 2e912ef4616e92b376e71a28e79f684692b31ad4

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