1
0

helpers.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987
  1. package tgbotapi
  2. import (
  3. "crypto/hmac"
  4. "crypto/sha256"
  5. "encoding/hex"
  6. "errors"
  7. "fmt"
  8. "net/url"
  9. "sort"
  10. "strings"
  11. )
  12. // NewMessage creates a new Message.
  13. //
  14. // chatID is where to send it, text is the message text.
  15. func NewMessage(chatID int64, text string) MessageConfig {
  16. return MessageConfig{
  17. BaseChat: BaseChat{
  18. ChatID: chatID,
  19. ReplyToMessageID: 0,
  20. },
  21. Text: text,
  22. DisableWebPagePreview: false,
  23. }
  24. }
  25. // NewDeleteMessage creates a request to delete a message.
  26. func NewDeleteMessage(chatID int64, messageID int) DeleteMessageConfig {
  27. return DeleteMessageConfig{
  28. ChatID: chatID,
  29. MessageID: messageID,
  30. }
  31. }
  32. // NewMessageToChannel creates a new Message that is sent to a channel
  33. // by username.
  34. //
  35. // username is the username of the channel, text is the message text,
  36. // and the username should be in the form of `@username`.
  37. func NewMessageToChannel(username string, text string) MessageConfig {
  38. return MessageConfig{
  39. BaseChat: BaseChat{
  40. ChannelUsername: username,
  41. },
  42. Text: text,
  43. }
  44. }
  45. // NewForward creates a new forward.
  46. //
  47. // chatID is where to send it, fromChatID is the source chat,
  48. // and messageID is the ID of the original message.
  49. func NewForward(chatID int64, fromChatID int64, messageID int) ForwardConfig {
  50. return ForwardConfig{
  51. BaseChat: BaseChat{ChatID: chatID},
  52. FromChatID: fromChatID,
  53. MessageID: messageID,
  54. }
  55. }
  56. // NewCopyMessage creates a new copy message.
  57. //
  58. // chatID is where to send it, fromChatID is the source chat,
  59. // and messageID is the ID of the original message.
  60. func NewCopyMessage(chatID int64, fromChatID int64, messageID int) CopyMessageConfig {
  61. return CopyMessageConfig{
  62. BaseChat: BaseChat{ChatID: chatID},
  63. FromChatID: fromChatID,
  64. MessageID: messageID,
  65. }
  66. }
  67. // NewPhoto creates a new sendPhoto request.
  68. //
  69. // chatID is where to send it, file is a string path to the file,
  70. // FileReader, or FileBytes.
  71. //
  72. // Note that you must send animated GIFs as a document.
  73. func NewPhoto(chatID int64, file RequestFileData) PhotoConfig {
  74. return PhotoConfig{
  75. BaseFile: BaseFile{
  76. BaseChat: BaseChat{ChatID: chatID},
  77. File: file,
  78. },
  79. }
  80. }
  81. // NewPhotoToChannel creates a new photo uploader to send a photo to a channel.
  82. //
  83. // Note that you must send animated GIFs as a document.
  84. func NewPhotoToChannel(username string, file RequestFileData) PhotoConfig {
  85. return PhotoConfig{
  86. BaseFile: BaseFile{
  87. BaseChat: BaseChat{
  88. ChannelUsername: username,
  89. },
  90. File: file,
  91. },
  92. }
  93. }
  94. // NewAudio creates a new sendAudio request.
  95. func NewAudio(chatID int64, file RequestFileData) AudioConfig {
  96. return AudioConfig{
  97. BaseFile: BaseFile{
  98. BaseChat: BaseChat{ChatID: chatID},
  99. File: file,
  100. },
  101. }
  102. }
  103. // NewDocument creates a new sendDocument request.
  104. func NewDocument(chatID int64, file RequestFileData) DocumentConfig {
  105. return DocumentConfig{
  106. BaseFile: BaseFile{
  107. BaseChat: BaseChat{ChatID: chatID},
  108. File: file,
  109. },
  110. }
  111. }
  112. // NewSticker creates a new sendSticker request.
  113. func NewSticker(chatID int64, file RequestFileData) StickerConfig {
  114. return StickerConfig{
  115. BaseFile: BaseFile{
  116. BaseChat: BaseChat{ChatID: chatID},
  117. File: file,
  118. },
  119. }
  120. }
  121. // NewVideo creates a new sendVideo request.
  122. func NewVideo(chatID int64, file RequestFileData) VideoConfig {
  123. return VideoConfig{
  124. BaseFile: BaseFile{
  125. BaseChat: BaseChat{ChatID: chatID},
  126. File: file,
  127. },
  128. }
  129. }
  130. // NewAnimation creates a new sendAnimation request.
  131. func NewAnimation(chatID int64, file RequestFileData) AnimationConfig {
  132. return AnimationConfig{
  133. BaseFile: BaseFile{
  134. BaseChat: BaseChat{ChatID: chatID},
  135. File: file,
  136. },
  137. }
  138. }
  139. // NewVideoNote creates a new sendVideoNote request.
  140. //
  141. // chatID is where to send it, file is a string path to the file,
  142. // FileReader, or FileBytes.
  143. func NewVideoNote(chatID int64, length int, file RequestFileData) VideoNoteConfig {
  144. return VideoNoteConfig{
  145. BaseFile: BaseFile{
  146. BaseChat: BaseChat{ChatID: chatID},
  147. File: file,
  148. },
  149. Length: length,
  150. }
  151. }
  152. // NewVoice creates a new sendVoice request.
  153. func NewVoice(chatID int64, file RequestFileData) VoiceConfig {
  154. return VoiceConfig{
  155. BaseFile: BaseFile{
  156. BaseChat: BaseChat{ChatID: chatID},
  157. File: file,
  158. },
  159. }
  160. }
  161. // NewMediaGroup creates a new media group. Files should be an array of
  162. // two to ten InputMediaPhoto or InputMediaVideo.
  163. func NewMediaGroup(chatID int64, files []interface{}) MediaGroupConfig {
  164. return MediaGroupConfig{
  165. ChatID: chatID,
  166. Media: files,
  167. }
  168. }
  169. // NewInputMediaPhoto creates a new InputMediaPhoto.
  170. func NewInputMediaPhoto(media RequestFileData) InputMediaPhoto {
  171. return InputMediaPhoto{
  172. BaseInputMedia{
  173. Type: "photo",
  174. Media: media,
  175. },
  176. }
  177. }
  178. // NewInputMediaVideo creates a new InputMediaVideo.
  179. func NewInputMediaVideo(media RequestFileData) InputMediaVideo {
  180. return InputMediaVideo{
  181. BaseInputMedia: BaseInputMedia{
  182. Type: "video",
  183. Media: media,
  184. },
  185. }
  186. }
  187. // NewInputMediaAnimation creates a new InputMediaAnimation.
  188. func NewInputMediaAnimation(media RequestFileData) InputMediaAnimation {
  189. return InputMediaAnimation{
  190. BaseInputMedia: BaseInputMedia{
  191. Type: "animation",
  192. Media: media,
  193. },
  194. }
  195. }
  196. // NewInputMediaAudio creates a new InputMediaAudio.
  197. func NewInputMediaAudio(media RequestFileData) InputMediaAudio {
  198. return InputMediaAudio{
  199. BaseInputMedia: BaseInputMedia{
  200. Type: "audio",
  201. Media: media,
  202. },
  203. }
  204. }
  205. // NewInputMediaDocument creates a new InputMediaDocument.
  206. func NewInputMediaDocument(media RequestFileData) InputMediaDocument {
  207. return InputMediaDocument{
  208. BaseInputMedia: BaseInputMedia{
  209. Type: "document",
  210. Media: media,
  211. },
  212. }
  213. }
  214. // NewContact allows you to send a shared contact.
  215. func NewContact(chatID int64, phoneNumber, firstName string) ContactConfig {
  216. return ContactConfig{
  217. BaseChat: BaseChat{
  218. ChatID: chatID,
  219. },
  220. PhoneNumber: phoneNumber,
  221. FirstName: firstName,
  222. }
  223. }
  224. // NewLocation shares your location.
  225. //
  226. // chatID is where to send it, latitude and longitude are coordinates.
  227. func NewLocation(chatID int64, latitude float64, longitude float64) LocationConfig {
  228. return LocationConfig{
  229. BaseChat: BaseChat{
  230. ChatID: chatID,
  231. },
  232. Latitude: latitude,
  233. Longitude: longitude,
  234. }
  235. }
  236. // NewVenue allows you to send a venue and its location.
  237. func NewVenue(chatID int64, title, address string, latitude, longitude float64) VenueConfig {
  238. return VenueConfig{
  239. BaseChat: BaseChat{
  240. ChatID: chatID,
  241. },
  242. Title: title,
  243. Address: address,
  244. Latitude: latitude,
  245. Longitude: longitude,
  246. }
  247. }
  248. // NewChatAction sets a chat action.
  249. // Actions last for 5 seconds, or until your next action.
  250. //
  251. // chatID is where to send it, action should be set via Chat constants.
  252. func NewChatAction(chatID int64, action string) ChatActionConfig {
  253. return ChatActionConfig{
  254. BaseChat: BaseChat{ChatID: chatID},
  255. Action: action,
  256. }
  257. }
  258. // NewUserProfilePhotos gets user profile photos.
  259. //
  260. // userID is the ID of the user you wish to get profile photos from.
  261. func NewUserProfilePhotos(userID int64) UserProfilePhotosConfig {
  262. return UserProfilePhotosConfig{
  263. UserID: userID,
  264. Offset: 0,
  265. Limit: 0,
  266. }
  267. }
  268. // NewUpdate gets updates since the last Offset.
  269. //
  270. // offset is the last Update ID to include.
  271. // You likely want to set this to the last Update ID plus 1.
  272. func NewUpdate(offset int) UpdateConfig {
  273. return UpdateConfig{
  274. Offset: offset,
  275. Limit: 0,
  276. Timeout: 0,
  277. }
  278. }
  279. // NewWebhook creates a new webhook.
  280. //
  281. // link is the url parsable link you wish to get the updates.
  282. func NewWebhook(link string) (WebhookConfig, error) {
  283. u, err := url.Parse(link)
  284. if err != nil {
  285. return WebhookConfig{}, err
  286. }
  287. return WebhookConfig{
  288. URL: u,
  289. }, nil
  290. }
  291. // NewWebhookWithCert creates a new webhook with a certificate.
  292. //
  293. // link is the url you wish to get webhooks,
  294. // file contains a string to a file, FileReader, or FileBytes.
  295. func NewWebhookWithCert(link string, file RequestFileData) (WebhookConfig, error) {
  296. u, err := url.Parse(link)
  297. if err != nil {
  298. return WebhookConfig{}, err
  299. }
  300. return WebhookConfig{
  301. URL: u,
  302. Certificate: file,
  303. }, nil
  304. }
  305. // NewInlineQueryResultArticle creates a new inline query article.
  306. func NewInlineQueryResultArticle(id, title, messageText string) InlineQueryResultArticle {
  307. return InlineQueryResultArticle{
  308. Type: "article",
  309. ID: id,
  310. Title: title,
  311. InputMessageContent: InputTextMessageContent{
  312. Text: messageText,
  313. },
  314. }
  315. }
  316. // NewInlineQueryResultArticleMarkdown creates a new inline query article with Markdown parsing.
  317. func NewInlineQueryResultArticleMarkdown(id, title, messageText string) InlineQueryResultArticle {
  318. return InlineQueryResultArticle{
  319. Type: "article",
  320. ID: id,
  321. Title: title,
  322. InputMessageContent: InputTextMessageContent{
  323. Text: messageText,
  324. ParseMode: "Markdown",
  325. },
  326. }
  327. }
  328. // NewInlineQueryResultArticleMarkdownV2 creates a new inline query article with MarkdownV2 parsing.
  329. func NewInlineQueryResultArticleMarkdownV2(id, title, messageText string) InlineQueryResultArticle {
  330. return InlineQueryResultArticle{
  331. Type: "article",
  332. ID: id,
  333. Title: title,
  334. InputMessageContent: InputTextMessageContent{
  335. Text: messageText,
  336. ParseMode: "MarkdownV2",
  337. },
  338. }
  339. }
  340. // NewInlineQueryResultArticleHTML creates a new inline query article with HTML parsing.
  341. func NewInlineQueryResultArticleHTML(id, title, messageText string) InlineQueryResultArticle {
  342. return InlineQueryResultArticle{
  343. Type: "article",
  344. ID: id,
  345. Title: title,
  346. InputMessageContent: InputTextMessageContent{
  347. Text: messageText,
  348. ParseMode: "HTML",
  349. },
  350. }
  351. }
  352. // NewInlineQueryResultGIF creates a new inline query GIF.
  353. func NewInlineQueryResultGIF(id, url string) InlineQueryResultGIF {
  354. return InlineQueryResultGIF{
  355. Type: "gif",
  356. ID: id,
  357. URL: url,
  358. }
  359. }
  360. // NewInlineQueryResultCachedGIF create a new inline query with cached photo.
  361. func NewInlineQueryResultCachedGIF(id, gifID string) InlineQueryResultCachedGIF {
  362. return InlineQueryResultCachedGIF{
  363. Type: "gif",
  364. ID: id,
  365. GIFID: gifID,
  366. }
  367. }
  368. // NewInlineQueryResultMPEG4GIF creates a new inline query MPEG4 GIF.
  369. func NewInlineQueryResultMPEG4GIF(id, url string) InlineQueryResultMPEG4GIF {
  370. return InlineQueryResultMPEG4GIF{
  371. Type: "mpeg4_gif",
  372. ID: id,
  373. URL: url,
  374. }
  375. }
  376. // NewInlineQueryResultCachedMPEG4GIF create a new inline query with cached MPEG4 GIF.
  377. func NewInlineQueryResultCachedMPEG4GIF(id, MPEG4GIFID string) InlineQueryResultCachedMPEG4GIF {
  378. return InlineQueryResultCachedMPEG4GIF{
  379. Type: "mpeg4_gif",
  380. ID: id,
  381. MPEG4FileID: MPEG4GIFID,
  382. }
  383. }
  384. // NewInlineQueryResultPhoto creates a new inline query photo.
  385. func NewInlineQueryResultPhoto(id, url string) InlineQueryResultPhoto {
  386. return InlineQueryResultPhoto{
  387. Type: "photo",
  388. ID: id,
  389. URL: url,
  390. }
  391. }
  392. // NewInlineQueryResultPhotoWithThumb creates a new inline query photo.
  393. func NewInlineQueryResultPhotoWithThumb(id, url, thumb string) InlineQueryResultPhoto {
  394. return InlineQueryResultPhoto{
  395. Type: "photo",
  396. ID: id,
  397. URL: url,
  398. ThumbURL: thumb,
  399. }
  400. }
  401. // NewInlineQueryResultCachedPhoto create a new inline query with cached photo.
  402. func NewInlineQueryResultCachedPhoto(id, photoID string) InlineQueryResultCachedPhoto {
  403. return InlineQueryResultCachedPhoto{
  404. Type: "photo",
  405. ID: id,
  406. PhotoID: photoID,
  407. }
  408. }
  409. // NewInlineQueryResultVideo creates a new inline query video.
  410. func NewInlineQueryResultVideo(id, url string) InlineQueryResultVideo {
  411. return InlineQueryResultVideo{
  412. Type: "video",
  413. ID: id,
  414. URL: url,
  415. }
  416. }
  417. // NewInlineQueryResultCachedVideo create a new inline query with cached video.
  418. func NewInlineQueryResultCachedVideo(id, videoID, title string) InlineQueryResultCachedVideo {
  419. return InlineQueryResultCachedVideo{
  420. Type: "video",
  421. ID: id,
  422. VideoID: videoID,
  423. Title: title,
  424. }
  425. }
  426. // NewInlineQueryResultCachedSticker create a new inline query with cached sticker.
  427. func NewInlineQueryResultCachedSticker(id, stickerID, title string) InlineQueryResultCachedSticker {
  428. return InlineQueryResultCachedSticker{
  429. Type: "sticker",
  430. ID: id,
  431. StickerID: stickerID,
  432. Title: title,
  433. }
  434. }
  435. // NewInlineQueryResultAudio creates a new inline query audio.
  436. func NewInlineQueryResultAudio(id, url, title string) InlineQueryResultAudio {
  437. return InlineQueryResultAudio{
  438. Type: "audio",
  439. ID: id,
  440. URL: url,
  441. Title: title,
  442. }
  443. }
  444. // NewInlineQueryResultCachedAudio create a new inline query with cached photo.
  445. func NewInlineQueryResultCachedAudio(id, audioID string) InlineQueryResultCachedAudio {
  446. return InlineQueryResultCachedAudio{
  447. Type: "audio",
  448. ID: id,
  449. AudioID: audioID,
  450. }
  451. }
  452. // NewInlineQueryResultVoice creates a new inline query voice.
  453. func NewInlineQueryResultVoice(id, url, title string) InlineQueryResultVoice {
  454. return InlineQueryResultVoice{
  455. Type: "voice",
  456. ID: id,
  457. URL: url,
  458. Title: title,
  459. }
  460. }
  461. // NewInlineQueryResultCachedVoice create a new inline query with cached photo.
  462. func NewInlineQueryResultCachedVoice(id, voiceID, title string) InlineQueryResultCachedVoice {
  463. return InlineQueryResultCachedVoice{
  464. Type: "voice",
  465. ID: id,
  466. VoiceID: voiceID,
  467. Title: title,
  468. }
  469. }
  470. // NewInlineQueryResultDocument creates a new inline query document.
  471. func NewInlineQueryResultDocument(id, url, title, mimeType string) InlineQueryResultDocument {
  472. return InlineQueryResultDocument{
  473. Type: "document",
  474. ID: id,
  475. URL: url,
  476. Title: title,
  477. MimeType: mimeType,
  478. }
  479. }
  480. // NewInlineQueryResultCachedDocument create a new inline query with cached photo.
  481. func NewInlineQueryResultCachedDocument(id, documentID, title string) InlineQueryResultCachedDocument {
  482. return InlineQueryResultCachedDocument{
  483. Type: "document",
  484. ID: id,
  485. DocumentID: documentID,
  486. Title: title,
  487. }
  488. }
  489. // NewInlineQueryResultLocation creates a new inline query location.
  490. func NewInlineQueryResultLocation(id, title string, latitude, longitude float64) InlineQueryResultLocation {
  491. return InlineQueryResultLocation{
  492. Type: "location",
  493. ID: id,
  494. Title: title,
  495. Latitude: latitude,
  496. Longitude: longitude,
  497. }
  498. }
  499. // NewInlineQueryResultVenue creates a new inline query venue.
  500. func NewInlineQueryResultVenue(id, title, address string, latitude, longitude float64) InlineQueryResultVenue {
  501. return InlineQueryResultVenue{
  502. Type: "venue",
  503. ID: id,
  504. Title: title,
  505. Address: address,
  506. Latitude: latitude,
  507. Longitude: longitude,
  508. }
  509. }
  510. // NewEditMessageText allows you to edit the text of a message.
  511. func NewEditMessageText(chatID int64, messageID int, text string) EditMessageTextConfig {
  512. return EditMessageTextConfig{
  513. BaseEdit: BaseEdit{
  514. ChatID: chatID,
  515. MessageID: messageID,
  516. },
  517. Text: text,
  518. }
  519. }
  520. // NewEditMessageTextAndMarkup allows you to edit the text and reply markup of a message.
  521. func NewEditMessageTextAndMarkup(chatID int64, messageID int, text string, replyMarkup InlineKeyboardMarkup) EditMessageTextConfig {
  522. return EditMessageTextConfig{
  523. BaseEdit: BaseEdit{
  524. ChatID: chatID,
  525. MessageID: messageID,
  526. ReplyMarkup: &replyMarkup,
  527. },
  528. Text: text,
  529. }
  530. }
  531. // NewEditMessageCaption allows you to edit the caption of a message.
  532. func NewEditMessageCaption(chatID int64, messageID int, caption string) EditMessageCaptionConfig {
  533. return EditMessageCaptionConfig{
  534. BaseEdit: BaseEdit{
  535. ChatID: chatID,
  536. MessageID: messageID,
  537. },
  538. Caption: caption,
  539. }
  540. }
  541. // NewEditMessageReplyMarkup allows you to edit the inline
  542. // keyboard markup.
  543. func NewEditMessageReplyMarkup(chatID int64, messageID int, replyMarkup InlineKeyboardMarkup) EditMessageReplyMarkupConfig {
  544. return EditMessageReplyMarkupConfig{
  545. BaseEdit: BaseEdit{
  546. ChatID: chatID,
  547. MessageID: messageID,
  548. ReplyMarkup: &replyMarkup,
  549. },
  550. }
  551. }
  552. // NewRemoveKeyboard hides the keyboard, with the option for being selective
  553. // or hiding for everyone.
  554. func NewRemoveKeyboard(selective bool) ReplyKeyboardRemove {
  555. return ReplyKeyboardRemove{
  556. RemoveKeyboard: true,
  557. Selective: selective,
  558. }
  559. }
  560. // NewKeyboardButton creates a regular keyboard button.
  561. func NewKeyboardButton(text string) KeyboardButton {
  562. return KeyboardButton{
  563. Text: text,
  564. }
  565. }
  566. // NewKeyboardButtonWebApp creates a keyboard button with text
  567. // which goes to a WebApp.
  568. func NewKeyboardButtonWebApp(text string, webapp WebAppInfo) KeyboardButton {
  569. return KeyboardButton{
  570. Text: text,
  571. WebApp: &webapp,
  572. }
  573. }
  574. // NewKeyboardButtonContact creates a keyboard button that requests
  575. // user contact information upon click.
  576. func NewKeyboardButtonContact(text string) KeyboardButton {
  577. return KeyboardButton{
  578. Text: text,
  579. RequestContact: true,
  580. }
  581. }
  582. // NewKeyboardButtonLocation creates a keyboard button that requests
  583. // user location information upon click.
  584. func NewKeyboardButtonLocation(text string) KeyboardButton {
  585. return KeyboardButton{
  586. Text: text,
  587. RequestLocation: true,
  588. }
  589. }
  590. // NewKeyboardButtonRow creates a row of keyboard buttons.
  591. func NewKeyboardButtonRow(buttons ...KeyboardButton) []KeyboardButton {
  592. var row []KeyboardButton
  593. row = append(row, buttons...)
  594. return row
  595. }
  596. // NewReplyKeyboard creates a new regular keyboard with sane defaults.
  597. func NewReplyKeyboard(rows ...[]KeyboardButton) ReplyKeyboardMarkup {
  598. var keyboard [][]KeyboardButton
  599. keyboard = append(keyboard, rows...)
  600. return ReplyKeyboardMarkup{
  601. ResizeKeyboard: true,
  602. Keyboard: keyboard,
  603. }
  604. }
  605. // NewOneTimeReplyKeyboard creates a new one time keyboard.
  606. func NewOneTimeReplyKeyboard(rows ...[]KeyboardButton) ReplyKeyboardMarkup {
  607. markup := NewReplyKeyboard(rows...)
  608. markup.OneTimeKeyboard = true
  609. return markup
  610. }
  611. // NewInlineKeyboardButtonData creates an inline keyboard button with text
  612. // and data for a callback.
  613. func NewInlineKeyboardButtonData(text, data string) InlineKeyboardButton {
  614. return InlineKeyboardButton{
  615. Text: text,
  616. CallbackData: &data,
  617. }
  618. }
  619. // NewInlineKeyboardButtonWebApp creates an inline keyboard button with text
  620. // which goes to a WebApp.
  621. func NewInlineKeyboardButtonWebApp(text string, webapp WebAppInfo) InlineKeyboardButton {
  622. return InlineKeyboardButton{
  623. Text: text,
  624. WebApp: &webapp,
  625. }
  626. }
  627. // NewInlineKeyboardButtonLoginURL creates an inline keyboard button with text
  628. // which goes to a LoginURL.
  629. func NewInlineKeyboardButtonLoginURL(text string, loginURL LoginURL) InlineKeyboardButton {
  630. return InlineKeyboardButton{
  631. Text: text,
  632. LoginURL: &loginURL,
  633. }
  634. }
  635. // NewInlineKeyboardButtonURL creates an inline keyboard button with text
  636. // which goes to a URL.
  637. func NewInlineKeyboardButtonURL(text, url string) InlineKeyboardButton {
  638. return InlineKeyboardButton{
  639. Text: text,
  640. URL: &url,
  641. }
  642. }
  643. // NewInlineKeyboardButtonSwitch creates an inline keyboard button with
  644. // text which allows the user to switch to a chat or return to a chat.
  645. func NewInlineKeyboardButtonSwitch(text, sw string) InlineKeyboardButton {
  646. return InlineKeyboardButton{
  647. Text: text,
  648. SwitchInlineQuery: &sw,
  649. }
  650. }
  651. // NewInlineKeyboardRow creates an inline keyboard row with buttons.
  652. func NewInlineKeyboardRow(buttons ...InlineKeyboardButton) []InlineKeyboardButton {
  653. var row []InlineKeyboardButton
  654. row = append(row, buttons...)
  655. return row
  656. }
  657. // NewInlineKeyboardMarkup creates a new inline keyboard.
  658. func NewInlineKeyboardMarkup(rows ...[]InlineKeyboardButton) InlineKeyboardMarkup {
  659. var keyboard [][]InlineKeyboardButton
  660. keyboard = append(keyboard, rows...)
  661. return InlineKeyboardMarkup{
  662. InlineKeyboard: keyboard,
  663. }
  664. }
  665. // NewCallback creates a new callback message.
  666. func NewCallback(id, text string) CallbackConfig {
  667. return CallbackConfig{
  668. CallbackQueryID: id,
  669. Text: text,
  670. ShowAlert: false,
  671. }
  672. }
  673. // NewCallbackWithAlert creates a new callback message that alerts
  674. // the user.
  675. func NewCallbackWithAlert(id, text string) CallbackConfig {
  676. return CallbackConfig{
  677. CallbackQueryID: id,
  678. Text: text,
  679. ShowAlert: true,
  680. }
  681. }
  682. // NewInvoice creates a new Invoice request to the user.
  683. func NewInvoice(chatID int64, title, description, payload, providerToken, startParameter, currency string, prices []LabeledPrice) InvoiceConfig {
  684. return InvoiceConfig{
  685. BaseChat: BaseChat{ChatID: chatID},
  686. Title: title,
  687. Description: description,
  688. Payload: payload,
  689. ProviderToken: providerToken,
  690. StartParameter: startParameter,
  691. Currency: currency,
  692. Prices: prices}
  693. }
  694. // NewChatTitle allows you to update the title of a chat.
  695. func NewChatTitle(chatID int64, title string) SetChatTitleConfig {
  696. return SetChatTitleConfig{
  697. ChatID: chatID,
  698. Title: title,
  699. }
  700. }
  701. // NewChatDescription allows you to update the description of a chat.
  702. func NewChatDescription(chatID int64, description string) SetChatDescriptionConfig {
  703. return SetChatDescriptionConfig{
  704. ChatID: chatID,
  705. Description: description,
  706. }
  707. }
  708. // NewChatPhoto allows you to update the photo for a chat.
  709. func NewChatPhoto(chatID int64, photo RequestFileData) SetChatPhotoConfig {
  710. return SetChatPhotoConfig{
  711. BaseFile: BaseFile{
  712. BaseChat: BaseChat{
  713. ChatID: chatID,
  714. },
  715. File: photo,
  716. },
  717. }
  718. }
  719. // NewDeleteChatPhoto allows you to delete the photo for a chat.
  720. func NewDeleteChatPhoto(chatID int64) DeleteChatPhotoConfig {
  721. return DeleteChatPhotoConfig{
  722. ChatID: chatID,
  723. }
  724. }
  725. // NewPoll allows you to create a new poll.
  726. func NewPoll(chatID int64, question string, options ...string) SendPollConfig {
  727. return SendPollConfig{
  728. BaseChat: BaseChat{
  729. ChatID: chatID,
  730. },
  731. Question: question,
  732. Options: options,
  733. IsAnonymous: true, // This is Telegram's default.
  734. }
  735. }
  736. // NewStopPoll allows you to stop a poll.
  737. func NewStopPoll(chatID int64, messageID int) StopPollConfig {
  738. return StopPollConfig{
  739. BaseEdit{
  740. ChatID: chatID,
  741. MessageID: messageID,
  742. },
  743. }
  744. }
  745. // NewDice allows you to send a random dice roll.
  746. func NewDice(chatID int64) DiceConfig {
  747. return DiceConfig{
  748. BaseChat: BaseChat{
  749. ChatID: chatID,
  750. },
  751. }
  752. }
  753. // NewDiceWithEmoji allows you to send a random roll of one of many types.
  754. //
  755. // Emoji may be 🎲 (1-6), 🎯 (1-6), or 🏀 (1-5).
  756. func NewDiceWithEmoji(chatID int64, emoji string) DiceConfig {
  757. return DiceConfig{
  758. BaseChat: BaseChat{
  759. ChatID: chatID,
  760. },
  761. Emoji: emoji,
  762. }
  763. }
  764. // NewBotCommandScopeDefault represents the default scope of bot commands.
  765. func NewBotCommandScopeDefault() BotCommandScope {
  766. return BotCommandScope{Type: "default"}
  767. }
  768. // NewBotCommandScopeAllPrivateChats represents the scope of bot commands,
  769. // covering all private chats.
  770. func NewBotCommandScopeAllPrivateChats() BotCommandScope {
  771. return BotCommandScope{Type: "all_private_chats"}
  772. }
  773. // NewBotCommandScopeAllGroupChats represents the scope of bot commands,
  774. // covering all group and supergroup chats.
  775. func NewBotCommandScopeAllGroupChats() BotCommandScope {
  776. return BotCommandScope{Type: "all_group_chats"}
  777. }
  778. // NewBotCommandScopeAllChatAdministrators represents the scope of bot commands,
  779. // covering all group and supergroup chat administrators.
  780. func NewBotCommandScopeAllChatAdministrators() BotCommandScope {
  781. return BotCommandScope{Type: "all_chat_administrators"}
  782. }
  783. // NewBotCommandScopeChat represents the scope of bot commands, covering a
  784. // specific chat.
  785. func NewBotCommandScopeChat(chatID int64) BotCommandScope {
  786. return BotCommandScope{
  787. Type: "chat",
  788. ChatID: chatID,
  789. }
  790. }
  791. // NewBotCommandScopeChatAdministrators represents the scope of bot commands,
  792. // covering all administrators of a specific group or supergroup chat.
  793. func NewBotCommandScopeChatAdministrators(chatID int64) BotCommandScope {
  794. return BotCommandScope{
  795. Type: "chat_administrators",
  796. ChatID: chatID,
  797. }
  798. }
  799. // NewBotCommandScopeChatMember represents the scope of bot commands, covering a
  800. // specific member of a group or supergroup chat.
  801. func NewBotCommandScopeChatMember(chatID, userID int64) BotCommandScope {
  802. return BotCommandScope{
  803. Type: "chat_member",
  804. ChatID: chatID,
  805. UserID: userID,
  806. }
  807. }
  808. // NewGetMyCommandsWithScope allows you to set the registered commands for a
  809. // given scope.
  810. func NewGetMyCommandsWithScope(scope BotCommandScope) GetMyCommandsConfig {
  811. return GetMyCommandsConfig{Scope: &scope}
  812. }
  813. // NewGetMyCommandsWithScopeAndLanguage allows you to set the registered
  814. // commands for a given scope and language code.
  815. func NewGetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) GetMyCommandsConfig {
  816. return GetMyCommandsConfig{Scope: &scope, LanguageCode: languageCode}
  817. }
  818. // NewSetMyCommands allows you to set the registered commands.
  819. func NewSetMyCommands(commands ...BotCommand) SetMyCommandsConfig {
  820. return SetMyCommandsConfig{Commands: commands}
  821. }
  822. // NewSetMyCommandsWithScope allows you to set the registered commands for a given scope.
  823. func NewSetMyCommandsWithScope(scope BotCommandScope, commands ...BotCommand) SetMyCommandsConfig {
  824. return SetMyCommandsConfig{Commands: commands, Scope: &scope}
  825. }
  826. // NewSetMyCommandsWithScopeAndLanguage allows you to set the registered commands for a given scope
  827. // and language code.
  828. func NewSetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string, commands ...BotCommand) SetMyCommandsConfig {
  829. return SetMyCommandsConfig{Commands: commands, Scope: &scope, LanguageCode: languageCode}
  830. }
  831. // NewDeleteMyCommands allows you to delete the registered commands.
  832. func NewDeleteMyCommands() DeleteMyCommandsConfig {
  833. return DeleteMyCommandsConfig{}
  834. }
  835. // NewDeleteMyCommandsWithScope allows you to delete the registered commands for a given
  836. // scope.
  837. func NewDeleteMyCommandsWithScope(scope BotCommandScope) DeleteMyCommandsConfig {
  838. return DeleteMyCommandsConfig{Scope: &scope}
  839. }
  840. // NewDeleteMyCommandsWithScopeAndLanguage allows you to delete the registered commands for a given
  841. // scope and language code.
  842. func NewDeleteMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) DeleteMyCommandsConfig {
  843. return DeleteMyCommandsConfig{Scope: &scope, LanguageCode: languageCode}
  844. }
  845. // ValidateWebAppData validate data received via the Web App
  846. // https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
  847. func ValidateWebAppData(token, telegramInitData string) (bool, error) {
  848. initData, err := url.ParseQuery(telegramInitData)
  849. if err != nil {
  850. return false, fmt.Errorf("error parsing data %w", err)
  851. }
  852. dataCheckString := make([]string, 0, len(initData))
  853. for k, v := range initData {
  854. if k == "hash" {
  855. continue
  856. }
  857. if len(v) > 0 {
  858. dataCheckString = append(dataCheckString, fmt.Sprintf("%s=%s", k, v[0]))
  859. }
  860. }
  861. sort.Strings(dataCheckString)
  862. secret := hmac.New(sha256.New, []byte("WebAppData"))
  863. secret.Write([]byte(token))
  864. hHash := hmac.New(sha256.New, secret.Sum(nil))
  865. hHash.Write([]byte(strings.Join(dataCheckString, "\n")))
  866. hash := hex.EncodeToString(hHash.Sum(nil))
  867. if initData.Get("hash") != hash {
  868. return false, errors.New("hash not equal")
  869. }
  870. return true, nil
  871. }