all repos — telegram-bot-api @ 678ff6fe454b136b3b34266ac183dbcf8a129ab1

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