all repos — telegram-bot-api @ 540e6677ca820110738c5d162151adf3ffd287ab

Golang bindings for the Telegram Bot API

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