all repos — telegram-bot-api @ ffc28cfbacda3694833103a11da29ca0c315a579

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 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}