all repos — telegram-bot-api @ 63e5c59035bfde1a7babea214eceb0e2e60e816f

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