all repos — telegram-bot-api @ 17d3e395d52d40e76b22f043424937a4e8d2f58c

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