all repos — telegram-bot-api @ bot-api-7.1

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) 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}
 850
 851// NewChatTitle allows you to update the title of a chat.
 852func NewChatTitle(chatID int64, title string) SetChatTitleConfig {
 853	return SetChatTitleConfig{
 854		ChatConfig: ChatConfig{
 855			ChatID: chatID,
 856		},
 857		Title: title,
 858	}
 859}
 860
 861// NewChatDescription allows you to update the description of a chat.
 862func NewChatDescription(chatID int64, description string) SetChatDescriptionConfig {
 863	return SetChatDescriptionConfig{
 864		ChatConfig: ChatConfig{
 865			ChatID: chatID,
 866		},
 867		Description: description,
 868	}
 869}
 870
 871func NewPinChatMessage(chatID int64, messageID int, disableNotification bool) PinChatMessageConfig {
 872	return PinChatMessageConfig{
 873		BaseChatMessage: BaseChatMessage{
 874			ChatConfig: ChatConfig{
 875				ChatID: chatID,
 876			},
 877			MessageID: messageID,
 878		},
 879		DisableNotification: disableNotification,
 880	}
 881}
 882
 883func NewUnpinChatMessage(chatID int64, messageID int) UnpinChatMessageConfig {
 884	return UnpinChatMessageConfig{
 885		BaseChatMessage: BaseChatMessage{
 886			ChatConfig: ChatConfig{
 887				ChatID: chatID,
 888			},
 889			MessageID: messageID,
 890		},
 891	}
 892}
 893
 894func NewGetChatMember(chatID, userID int64) GetChatMemberConfig {
 895	return GetChatMemberConfig{
 896		ChatConfigWithUser: ChatConfigWithUser{
 897			ChatConfig: ChatConfig{
 898				ChatID: chatID,
 899			},
 900			UserID: userID,
 901		},
 902	}
 903}
 904
 905func NewChatMember(chatID, userID int64) ChatMemberConfig {
 906	return ChatMemberConfig{
 907		ChatConfig: ChatConfig{
 908			ChatID: chatID,
 909		},
 910		UserID: userID,
 911	}
 912}
 913
 914// NewChatPhoto allows you to update the photo for a chat.
 915func NewChatPhoto(chatID int64, photo RequestFileData) SetChatPhotoConfig {
 916	return SetChatPhotoConfig{
 917		BaseFile: BaseFile{
 918			BaseChat: BaseChat{
 919				ChatConfig: ChatConfig{ChatID: chatID},
 920			},
 921			File: photo,
 922		},
 923	}
 924}
 925
 926// NewDeleteChatPhoto allows you to delete the photo for a chat.
 927func NewDeleteChatPhoto(chatID int64) DeleteChatPhotoConfig {
 928	return DeleteChatPhotoConfig{
 929		ChatConfig: ChatConfig{
 930			ChatID: chatID,
 931		},
 932	}
 933}
 934
 935// NewPoll allows you to create a new poll.
 936func NewPoll(chatID int64, question string, options ...string) SendPollConfig {
 937	return SendPollConfig{
 938		BaseChat: BaseChat{
 939			ChatConfig: ChatConfig{ChatID: chatID},
 940		},
 941		Question:    question,
 942		Options:     options,
 943		IsAnonymous: true, // This is Telegram's default.
 944	}
 945}
 946
 947// NewStopPoll allows you to stop a poll.
 948func NewStopPoll(chatID int64, messageID int) StopPollConfig {
 949	return StopPollConfig{
 950		BaseEdit{
 951			BaseChatMessage: BaseChatMessage{
 952				ChatConfig: ChatConfig{
 953					ChatID: chatID,
 954				},
 955				MessageID: messageID,
 956			},
 957		},
 958	}
 959}
 960
 961// NewDice allows you to send a random dice roll.
 962func NewDice(chatID int64) DiceConfig {
 963	return DiceConfig{
 964		BaseChat: BaseChat{
 965			ChatConfig: ChatConfig{ChatID: chatID},
 966		},
 967	}
 968}
 969
 970// NewDiceWithEmoji allows you to send a random roll of one of many types.
 971//
 972// Emoji may be 🎲 (1-6), 🎯 (1-6), or 🏀 (1-5).
 973func NewDiceWithEmoji(chatID int64, emoji string) DiceConfig {
 974	return DiceConfig{
 975		BaseChat: BaseChat{
 976			ChatConfig: ChatConfig{ChatID: chatID},
 977		},
 978		Emoji: emoji,
 979	}
 980}
 981
 982// NewBotCommandScopeDefault represents the default scope of bot commands.
 983func NewBotCommandScopeDefault() BotCommandScope {
 984	return BotCommandScope{Type: "default"}
 985}
 986
 987// NewBotCommandScopeAllPrivateChats represents the scope of bot commands,
 988// covering all private chats.
 989func NewBotCommandScopeAllPrivateChats() BotCommandScope {
 990	return BotCommandScope{Type: "all_private_chats"}
 991}
 992
 993// NewBotCommandScopeAllGroupChats represents the scope of bot commands,
 994// covering all group and supergroup chats.
 995func NewBotCommandScopeAllGroupChats() BotCommandScope {
 996	return BotCommandScope{Type: "all_group_chats"}
 997}
 998
 999// NewBotCommandScopeAllChatAdministrators represents the scope of bot commands,
1000// covering all group and supergroup chat administrators.
1001func NewBotCommandScopeAllChatAdministrators() BotCommandScope {
1002	return BotCommandScope{Type: "all_chat_administrators"}
1003}
1004
1005// NewBotCommandScopeChat represents the scope of bot commands, covering a
1006// specific chat.
1007func NewBotCommandScopeChat(chatID int64) BotCommandScope {
1008	return BotCommandScope{
1009		Type:   "chat",
1010		ChatID: chatID,
1011	}
1012}
1013
1014// NewBotCommandScopeChatAdministrators represents the scope of bot commands,
1015// covering all administrators of a specific group or supergroup chat.
1016func NewBotCommandScopeChatAdministrators(chatID int64) BotCommandScope {
1017	return BotCommandScope{
1018		Type:   "chat_administrators",
1019		ChatID: chatID,
1020	}
1021}
1022
1023// NewBotCommandScopeChatMember represents the scope of bot commands, covering a
1024// specific member of a group or supergroup chat.
1025func NewBotCommandScopeChatMember(chatID, userID int64) BotCommandScope {
1026	return BotCommandScope{
1027		Type:   "chat_member",
1028		ChatID: chatID,
1029		UserID: userID,
1030	}
1031}
1032
1033// NewSetMyDescription allows you to change the bot's description, which is shown in the chat with the bot if the chat is empty.
1034func NewSetMyDescription(description, languageCode string) SetMyDescriptionConfig {
1035	return SetMyDescriptionConfig{
1036		Description:  description,
1037		LanguageCode: languageCode,
1038	}
1039}
1040
1041// NewGetMyDescription returns the current bot description for the given user language
1042func NewGetMyDescription(languageCode string) GetMyDescriptionConfig {
1043	return GetMyDescriptionConfig{
1044		LanguageCode: languageCode,
1045	}
1046}
1047
1048// 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.
1049func NewSetMyShortDescription(shortDescription, languageCode string) SetMyShortDescriptionConfig {
1050	return SetMyShortDescriptionConfig{
1051		ShortDescription: shortDescription,
1052		LanguageCode:     languageCode,
1053	}
1054}
1055
1056// NewGetMyShortDescription returns the current bot short description for the given user language.
1057func NewGetMyShortDescription(languageCode string) GetMyShortDescriptionConfig {
1058	return GetMyShortDescriptionConfig{
1059		LanguageCode: languageCode,
1060	}
1061}
1062
1063// NewGetMyName get the current bot name for the given user language
1064func NewGetMyName(languageCode string) GetMyNameConfig {
1065	return GetMyNameConfig{
1066		LanguageCode: languageCode,
1067	}
1068}
1069
1070// NewSetMyName change the bot's name
1071func NewSetMyName(languageCode, name string) SetMyNameConfig {
1072	return SetMyNameConfig{
1073		Name:         name,
1074		LanguageCode: languageCode,
1075	}
1076}
1077
1078// NewGetMyCommandsWithScope allows you to set the registered commands for a
1079// given scope.
1080func NewGetMyCommandsWithScope(scope BotCommandScope) GetMyCommandsConfig {
1081	return GetMyCommandsConfig{Scope: &scope}
1082}
1083
1084// NewGetMyCommandsWithScopeAndLanguage allows you to set the registered
1085// commands for a given scope and language code.
1086func NewGetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) GetMyCommandsConfig {
1087	return GetMyCommandsConfig{Scope: &scope, LanguageCode: languageCode}
1088}
1089
1090// NewSetMyCommands allows you to set the registered commands.
1091func NewSetMyCommands(commands ...BotCommand) SetMyCommandsConfig {
1092	return SetMyCommandsConfig{Commands: commands}
1093}
1094
1095// NewSetMyCommandsWithScope allows you to set the registered commands for a given scope.
1096func NewSetMyCommandsWithScope(scope BotCommandScope, commands ...BotCommand) SetMyCommandsConfig {
1097	return SetMyCommandsConfig{Commands: commands, Scope: &scope}
1098}
1099
1100// NewSetMyCommandsWithScopeAndLanguage allows you to set the registered commands for a given scope
1101// and language code.
1102func NewSetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string, commands ...BotCommand) SetMyCommandsConfig {
1103	return SetMyCommandsConfig{Commands: commands, Scope: &scope, LanguageCode: languageCode}
1104}
1105
1106// NewDeleteMyCommands allows you to delete the registered commands.
1107func NewDeleteMyCommands() DeleteMyCommandsConfig {
1108	return DeleteMyCommandsConfig{}
1109}
1110
1111// NewDeleteMyCommandsWithScope allows you to delete the registered commands for a given
1112// scope.
1113func NewDeleteMyCommandsWithScope(scope BotCommandScope) DeleteMyCommandsConfig {
1114	return DeleteMyCommandsConfig{Scope: &scope}
1115}
1116
1117// NewDeleteMyCommandsWithScopeAndLanguage allows you to delete the registered commands for a given
1118// scope and language code.
1119func NewDeleteMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) DeleteMyCommandsConfig {
1120	return DeleteMyCommandsConfig{Scope: &scope, LanguageCode: languageCode}
1121}
1122
1123// ValidateWebAppData validate data received via the Web App
1124// https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
1125func ValidateWebAppData(token, telegramInitData string) (bool, error) {
1126	initData, err := url.ParseQuery(telegramInitData)
1127	if err != nil {
1128		return false, fmt.Errorf("error parsing data %w", err)
1129	}
1130
1131	dataCheckString := make([]string, 0, len(initData))
1132	for k, v := range initData {
1133		if k == "hash" {
1134			continue
1135		}
1136		if len(v) > 0 {
1137			dataCheckString = append(dataCheckString, fmt.Sprintf("%s=%s", k, v[0]))
1138		}
1139	}
1140
1141	sort.Strings(dataCheckString)
1142
1143	secret := hmac.New(sha256.New, []byte("WebAppData"))
1144	secret.Write([]byte(token))
1145
1146	hHash := hmac.New(sha256.New, secret.Sum(nil))
1147	hHash.Write([]byte(strings.Join(dataCheckString, "\n")))
1148
1149	hash := hex.EncodeToString(hHash.Sum(nil))
1150
1151	if initData.Get("hash") != hash {
1152		return false, errors.New("hash not equal")
1153	}
1154
1155	return true, nil
1156}