all repos — telegram-bot-api @ c33a1d7c567142ea1770c270da0f4410e0dc0eaf

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) SetCustomEmojiStickerSetThumbnalConfig {
 135	return SetCustomEmojiStickerSetThumbnalConfig{
 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// NewInlineKeyboardButtonLoginURL creates an inline keyboard button with text
 727// which goes to a LoginURL.
 728func NewInlineKeyboardButtonLoginURL(text string, loginURL LoginURL) InlineKeyboardButton {
 729	return InlineKeyboardButton{
 730		Text:     text,
 731		LoginURL: &loginURL,
 732	}
 733}
 734
 735// NewInlineKeyboardButtonURL creates an inline keyboard button with text
 736// which goes to a URL.
 737func NewInlineKeyboardButtonURL(text, url string) InlineKeyboardButton {
 738	return InlineKeyboardButton{
 739		Text: text,
 740		URL:  &url,
 741	}
 742}
 743
 744// NewInlineKeyboardButtonSwitch creates an inline keyboard button with
 745// text which allows the user to switch to a chat or return to a chat.
 746func NewInlineKeyboardButtonSwitch(text, sw string) InlineKeyboardButton {
 747	return InlineKeyboardButton{
 748		Text:              text,
 749		SwitchInlineQuery: &sw,
 750	}
 751}
 752
 753// NewInlineKeyboardRow creates an inline keyboard row with buttons.
 754func NewInlineKeyboardRow(buttons ...InlineKeyboardButton) []InlineKeyboardButton {
 755	var row []InlineKeyboardButton
 756
 757	row = append(row, buttons...)
 758
 759	return row
 760}
 761
 762// NewInlineKeyboardMarkup creates a new inline keyboard.
 763func NewInlineKeyboardMarkup(rows ...[]InlineKeyboardButton) InlineKeyboardMarkup {
 764	var keyboard [][]InlineKeyboardButton
 765
 766	keyboard = append(keyboard, rows...)
 767
 768	return InlineKeyboardMarkup{
 769		InlineKeyboard: keyboard,
 770	}
 771}
 772
 773// NewCallback creates a new callback message.
 774func NewCallback(id, text string) CallbackConfig {
 775	return CallbackConfig{
 776		CallbackQueryID: id,
 777		Text:            text,
 778		ShowAlert:       false,
 779	}
 780}
 781
 782// NewCallbackWithAlert creates a new callback message that alerts
 783// the user.
 784func NewCallbackWithAlert(id, text string) CallbackConfig {
 785	return CallbackConfig{
 786		CallbackQueryID: id,
 787		Text:            text,
 788		ShowAlert:       true,
 789	}
 790}
 791
 792// NewInvoice creates a new Invoice request to the user.
 793func NewInvoice(chatID int64, title, description, payload, providerToken, startParameter, currency string, prices []LabeledPrice) InvoiceConfig {
 794	return InvoiceConfig{
 795		BaseChat:       BaseChat{ChatID: chatID},
 796		Title:          title,
 797		Description:    description,
 798		Payload:        payload,
 799		ProviderToken:  providerToken,
 800		StartParameter: startParameter,
 801		Currency:       currency,
 802		Prices:         prices}
 803}
 804
 805// NewChatTitle allows you to update the title of a chat.
 806func NewChatTitle(chatID int64, title string) SetChatTitleConfig {
 807	return SetChatTitleConfig{
 808		ChatID: chatID,
 809		Title:  title,
 810	}
 811}
 812
 813// NewChatDescription allows you to update the description of a chat.
 814func NewChatDescription(chatID int64, description string) SetChatDescriptionConfig {
 815	return SetChatDescriptionConfig{
 816		ChatID:      chatID,
 817		Description: description,
 818	}
 819}
 820
 821// NewChatPhoto allows you to update the photo for a chat.
 822func NewChatPhoto(chatID int64, photo RequestFileData) SetChatPhotoConfig {
 823	return SetChatPhotoConfig{
 824		BaseFile: BaseFile{
 825			BaseChat: BaseChat{
 826				ChatID: chatID,
 827			},
 828			File: photo,
 829		},
 830	}
 831}
 832
 833// NewDeleteChatPhoto allows you to delete the photo for a chat.
 834func NewDeleteChatPhoto(chatID int64) DeleteChatPhotoConfig {
 835	return DeleteChatPhotoConfig{
 836		ChatID: chatID,
 837	}
 838}
 839
 840// NewPoll allows you to create a new poll.
 841func NewPoll(chatID int64, question string, options ...string) SendPollConfig {
 842	return SendPollConfig{
 843		BaseChat: BaseChat{
 844			ChatID: chatID,
 845		},
 846		Question:    question,
 847		Options:     options,
 848		IsAnonymous: true, // This is Telegram's default.
 849	}
 850}
 851
 852// NewStopPoll allows you to stop a poll.
 853func NewStopPoll(chatID int64, messageID int) StopPollConfig {
 854	return StopPollConfig{
 855		BaseEdit{
 856			ChatID:    chatID,
 857			MessageID: messageID,
 858		},
 859	}
 860}
 861
 862// NewDice allows you to send a random dice roll.
 863func NewDice(chatID int64) DiceConfig {
 864	return DiceConfig{
 865		BaseChat: BaseChat{
 866			ChatID: chatID,
 867		},
 868	}
 869}
 870
 871// NewDiceWithEmoji allows you to send a random roll of one of many types.
 872//
 873// Emoji may be 🎲 (1-6), 🎯 (1-6), or 🏀 (1-5).
 874func NewDiceWithEmoji(chatID int64, emoji string) DiceConfig {
 875	return DiceConfig{
 876		BaseChat: BaseChat{
 877			ChatID: chatID,
 878		},
 879		Emoji: emoji,
 880	}
 881}
 882
 883// NewBotCommandScopeDefault represents the default scope of bot commands.
 884func NewBotCommandScopeDefault() BotCommandScope {
 885	return BotCommandScope{Type: "default"}
 886}
 887
 888// NewBotCommandScopeAllPrivateChats represents the scope of bot commands,
 889// covering all private chats.
 890func NewBotCommandScopeAllPrivateChats() BotCommandScope {
 891	return BotCommandScope{Type: "all_private_chats"}
 892}
 893
 894// NewBotCommandScopeAllGroupChats represents the scope of bot commands,
 895// covering all group and supergroup chats.
 896func NewBotCommandScopeAllGroupChats() BotCommandScope {
 897	return BotCommandScope{Type: "all_group_chats"}
 898}
 899
 900// NewBotCommandScopeAllChatAdministrators represents the scope of bot commands,
 901// covering all group and supergroup chat administrators.
 902func NewBotCommandScopeAllChatAdministrators() BotCommandScope {
 903	return BotCommandScope{Type: "all_chat_administrators"}
 904}
 905
 906// NewBotCommandScopeChat represents the scope of bot commands, covering a
 907// specific chat.
 908func NewBotCommandScopeChat(chatID int64) BotCommandScope {
 909	return BotCommandScope{
 910		Type:   "chat",
 911		ChatID: chatID,
 912	}
 913}
 914
 915// NewBotCommandScopeChatAdministrators represents the scope of bot commands,
 916// covering all administrators of a specific group or supergroup chat.
 917func NewBotCommandScopeChatAdministrators(chatID int64) BotCommandScope {
 918	return BotCommandScope{
 919		Type:   "chat_administrators",
 920		ChatID: chatID,
 921	}
 922}
 923
 924// NewBotCommandScopeChatMember represents the scope of bot commands, covering a
 925// specific member of a group or supergroup chat.
 926func NewBotCommandScopeChatMember(chatID, userID int64) BotCommandScope {
 927	return BotCommandScope{
 928		Type:   "chat_member",
 929		ChatID: chatID,
 930		UserID: userID,
 931	}
 932}
 933
 934// NewSetMyDescription allows you to change the bot's description, which is shown in the chat with the bot if the chat is empty.
 935func NewSetMyDescription(description, languageCode string) SetMyDescriptionConfig {
 936	return SetMyDescriptionConfig{
 937		Description:  description,
 938		LanguageCode: languageCode,
 939	}
 940}
 941
 942// NewGetMyDescription returns the current bot description for the given user language
 943func NewGetMyDescription(languageCode string) GetMyDescriptionConfig {
 944	return GetMyDescriptionConfig{
 945		LanguageCode: languageCode,
 946	}
 947}
 948
 949// 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.
 950func NewSetMyShortDescription(shortDescription, languageCode string) SetMyShortDescriptionConfig {
 951	return SetMyShortDescriptionConfig{
 952		ShortDescription: shortDescription,
 953		LanguageCode:     languageCode,
 954	}
 955}
 956
 957// NewGetMyShortDescription returns the current bot short description for the given user language.
 958func NewGetMyShortDescription(languageCode string) GetMyShortDescriptionConfig {
 959	return GetMyShortDescriptionConfig{
 960		LanguageCode: languageCode,
 961	}
 962}
 963
 964// NewGetMyCommandsWithScope allows you to set the registered commands for a
 965// given scope.
 966func NewGetMyCommandsWithScope(scope BotCommandScope) GetMyCommandsConfig {
 967	return GetMyCommandsConfig{Scope: &scope}
 968}
 969
 970// NewGetMyCommandsWithScopeAndLanguage allows you to set the registered
 971// commands for a given scope and language code.
 972func NewGetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) GetMyCommandsConfig {
 973	return GetMyCommandsConfig{Scope: &scope, LanguageCode: languageCode}
 974}
 975
 976// NewSetMyCommands allows you to set the registered commands.
 977func NewSetMyCommands(commands ...BotCommand) SetMyCommandsConfig {
 978	return SetMyCommandsConfig{Commands: commands}
 979}
 980
 981// NewSetMyCommandsWithScope allows you to set the registered commands for a given scope.
 982func NewSetMyCommandsWithScope(scope BotCommandScope, commands ...BotCommand) SetMyCommandsConfig {
 983	return SetMyCommandsConfig{Commands: commands, Scope: &scope}
 984}
 985
 986// NewSetMyCommandsWithScopeAndLanguage allows you to set the registered commands for a given scope
 987// and language code.
 988func NewSetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string, commands ...BotCommand) SetMyCommandsConfig {
 989	return SetMyCommandsConfig{Commands: commands, Scope: &scope, LanguageCode: languageCode}
 990}
 991
 992// NewDeleteMyCommands allows you to delete the registered commands.
 993func NewDeleteMyCommands() DeleteMyCommandsConfig {
 994	return DeleteMyCommandsConfig{}
 995}
 996
 997// NewDeleteMyCommandsWithScope allows you to delete the registered commands for a given
 998// scope.
 999func NewDeleteMyCommandsWithScope(scope BotCommandScope) DeleteMyCommandsConfig {
1000	return DeleteMyCommandsConfig{Scope: &scope}
1001}
1002
1003// NewDeleteMyCommandsWithScopeAndLanguage allows you to delete the registered commands for a given
1004// scope and language code.
1005func NewDeleteMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) DeleteMyCommandsConfig {
1006	return DeleteMyCommandsConfig{Scope: &scope, LanguageCode: languageCode}
1007}
1008
1009// ValidateWebAppData validate data received via the Web App
1010// https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
1011func ValidateWebAppData(token, telegramInitData string) (bool, error) {
1012	initData, err := url.ParseQuery(telegramInitData)
1013	if err != nil {
1014		return false, fmt.Errorf("error parsing data %w", err)
1015	}
1016
1017	dataCheckString := make([]string, 0, len(initData))
1018	for k, v := range initData {
1019		if k == "hash" {
1020			continue
1021		}
1022		if len(v) > 0 {
1023			dataCheckString = append(dataCheckString, fmt.Sprintf("%s=%s", k, v[0]))
1024		}
1025	}
1026
1027	sort.Strings(dataCheckString)
1028
1029	secret := hmac.New(sha256.New, []byte("WebAppData"))
1030	secret.Write([]byte(token))
1031
1032	hHash := hmac.New(sha256.New, secret.Sum(nil))
1033	hHash.Write([]byte(strings.Join(dataCheckString, "\n")))
1034
1035	hash := hex.EncodeToString(hHash.Sum(nil))
1036
1037	if initData.Get("hash") != hash {
1038		return false, errors.New("hash not equal")
1039	}
1040
1041	return true, nil
1042}