1// Package tgbotapi has functions and types used for interacting with 2// the Telegram Bot API. 3package tgbotapi 4 5import ( 6 "bytes" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "net/http" 13 "net/url" 14 "os" 15 "strconv" 16 "strings" 17 "time" 18 19 "github.com/technoweenie/multipartstreamer" 20) 21 22// BotAPI allows you to interact with the Telegram Bot API. 23type BotAPI struct { 24 Token string `json:"token"` 25 Debug bool `json:"debug"` 26 Buffer int `json:"buffer"` 27 28 Self User `json:"-"` 29 Client *http.Client `json:"-"` 30 shutdownChannel chan interface{} 31} 32 33// NewBotAPI creates a new BotAPI instance. 34// 35// It requires a token, provided by @BotFather on Telegram. 36func NewBotAPI(token string) (*BotAPI, error) { 37 return NewBotAPIWithClient(token, &http.Client{}) 38} 39 40// NewBotAPIWithClient creates a new BotAPI instance 41// and allows you to pass a http.Client. 42// 43// It requires a token, provided by @BotFather on Telegram. 44func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) { 45 bot := &BotAPI{ 46 Token: token, 47 Client: client, 48 Buffer: 100, 49 shutdownChannel: make(chan interface{}), 50 } 51 52 self, err := bot.GetMe() 53 if err != nil { 54 return nil, err 55 } 56 57 bot.Self = self 58 59 return bot, nil 60} 61 62// MakeRequest makes a request to a specific endpoint with our token. 63func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) { 64 method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint) 65 66 resp, err := bot.Client.PostForm(method, params) 67 if err != nil { 68 return APIResponse{}, err 69 } 70 defer resp.Body.Close() 71 72 var apiResp APIResponse 73 bytes, err := bot.decodeAPIResponse(resp.Body, &apiResp) 74 if err != nil { 75 return apiResp, err 76 } 77 78 if bot.Debug { 79 log.Printf("%s resp: %s", endpoint, bytes) 80 } 81 82 if !apiResp.Ok { 83 parameters := ResponseParameters{} 84 if apiResp.Parameters != nil { 85 parameters = *apiResp.Parameters 86 } 87 return apiResp, Error{apiResp.Description, parameters} 88 } 89 90 return apiResp, nil 91} 92 93// decodeAPIResponse decode response and return slice of bytes if debug enabled. 94// If debug disabled, just decode http.Response.Body stream to APIResponse struct 95// for efficient memory usage 96func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse) (_ []byte, err error) { 97 if !bot.Debug { 98 dec := json.NewDecoder(responseBody) 99 err = dec.Decode(resp) 100 return 101 } 102 103 // if debug, read reponse body 104 data, err := ioutil.ReadAll(responseBody) 105 if err != nil { 106 return 107 } 108 109 err = json.Unmarshal(data, resp) 110 if err != nil { 111 return 112 } 113 114 return data, nil 115} 116 117// makeMessageRequest makes a request to a method that returns a Message. 118func (bot *BotAPI) makeMessageRequest(endpoint string, params url.Values) (Message, error) { 119 resp, err := bot.MakeRequest(endpoint, params) 120 if err != nil { 121 return Message{}, err 122 } 123 124 var message Message 125 json.Unmarshal(resp.Result, &message) 126 127 bot.debugLog(endpoint, params, message) 128 129 return message, nil 130} 131 132// UploadFile makes a request to the API with a file. 133// 134// Requires the parameter to hold the file not be in the params. 135// File should be a string to a file path, a FileBytes struct, 136// a FileReader struct, or a url.URL. 137// 138// Note that if your FileReader has a size set to -1, it will read 139// the file into memory to calculate a size. 140func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) { 141 ms := multipartstreamer.New() 142 143 switch f := file.(type) { 144 case string: 145 ms.WriteFields(params) 146 147 fileHandle, err := os.Open(f) 148 if err != nil { 149 return APIResponse{}, err 150 } 151 defer fileHandle.Close() 152 153 fi, err := os.Stat(f) 154 if err != nil { 155 return APIResponse{}, err 156 } 157 158 ms.WriteReader(fieldname, fileHandle.Name(), fi.Size(), fileHandle) 159 case FileBytes: 160 ms.WriteFields(params) 161 162 buf := bytes.NewBuffer(f.Bytes) 163 ms.WriteReader(fieldname, f.Name, int64(len(f.Bytes)), buf) 164 case FileReader: 165 ms.WriteFields(params) 166 167 if f.Size != -1 { 168 ms.WriteReader(fieldname, f.Name, f.Size, f.Reader) 169 170 break 171 } 172 173 data, err := ioutil.ReadAll(f.Reader) 174 if err != nil { 175 return APIResponse{}, err 176 } 177 178 buf := bytes.NewBuffer(data) 179 180 ms.WriteReader(fieldname, f.Name, int64(len(data)), buf) 181 case url.URL: 182 params[fieldname] = f.String() 183 184 ms.WriteFields(params) 185 default: 186 return APIResponse{}, errors.New(ErrBadFileType) 187 } 188 189 method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint) 190 191 req, err := http.NewRequest("POST", method, nil) 192 if err != nil { 193 return APIResponse{}, err 194 } 195 196 ms.SetupRequest(req) 197 198 res, err := bot.Client.Do(req) 199 if err != nil { 200 return APIResponse{}, err 201 } 202 defer res.Body.Close() 203 204 bytes, err := ioutil.ReadAll(res.Body) 205 if err != nil { 206 return APIResponse{}, err 207 } 208 209 if bot.Debug { 210 log.Println(string(bytes)) 211 } 212 213 var apiResp APIResponse 214 215 err = json.Unmarshal(bytes, &apiResp) 216 if err != nil { 217 return APIResponse{}, err 218 } 219 220 if !apiResp.Ok { 221 return APIResponse{}, errors.New(apiResp.Description) 222 } 223 224 return apiResp, nil 225} 226 227// GetFileDirectURL returns direct URL to file 228// 229// It requires the FileID. 230func (bot *BotAPI) GetFileDirectURL(fileID string) (string, error) { 231 file, err := bot.GetFile(FileConfig{fileID}) 232 233 if err != nil { 234 return "", err 235 } 236 237 return file.Link(bot.Token), nil 238} 239 240// GetMe fetches the currently authenticated bot. 241// 242// This method is called upon creation to validate the token, 243// and so you may get this data from BotAPI.Self without the need for 244// another request. 245func (bot *BotAPI) GetMe() (User, error) { 246 resp, err := bot.MakeRequest("getMe", nil) 247 if err != nil { 248 return User{}, err 249 } 250 251 var user User 252 json.Unmarshal(resp.Result, &user) 253 254 bot.debugLog("getMe", nil, user) 255 256 return user, nil 257} 258 259// IsMessageToMe returns true if message directed to this bot. 260// 261// It requires the Message. 262func (bot *BotAPI) IsMessageToMe(message Message) bool { 263 return strings.Contains(message.Text, "@"+bot.Self.UserName) 264} 265 266// Send will send a Chattable item to Telegram. 267// 268// It requires the Chattable to send. 269func (bot *BotAPI) Send(c Chattable) (Message, error) { 270 switch c.(type) { 271 case Fileable: 272 return bot.sendFile(c.(Fileable)) 273 default: 274 return bot.sendChattable(c) 275 } 276} 277 278// debugLog checks if the bot is currently running in debug mode, and if 279// so will display information about the request and response in the 280// debug log. 281func (bot *BotAPI) debugLog(context string, v url.Values, message interface{}) { 282 if bot.Debug { 283 log.Printf("%s req : %+v\n", context, v) 284 log.Printf("%s resp: %+v\n", context, message) 285 } 286} 287 288// sendExisting will send a Message with an existing file to Telegram. 289func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error) { 290 v, err := config.values() 291 292 if err != nil { 293 return Message{}, err 294 } 295 296 message, err := bot.makeMessageRequest(method, v) 297 if err != nil { 298 return Message{}, err 299 } 300 301 return message, nil 302} 303 304// uploadAndSend will send a Message with a new file to Telegram. 305func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error) { 306 params, err := config.params() 307 if err != nil { 308 return Message{}, err 309 } 310 311 file := config.getFile() 312 313 resp, err := bot.UploadFile(method, params, config.name(), file) 314 if err != nil { 315 return Message{}, err 316 } 317 318 var message Message 319 json.Unmarshal(resp.Result, &message) 320 321 bot.debugLog(method, nil, message) 322 323 return message, nil 324} 325 326// sendFile determines if the file is using an existing file or uploading 327// a new file, then sends it as needed. 328func (bot *BotAPI) sendFile(config Fileable) (Message, error) { 329 if config.useExistingFile() { 330 return bot.sendExisting(config.method(), config) 331 } 332 333 return bot.uploadAndSend(config.method(), config) 334} 335 336// sendChattable sends a Chattable. 337func (bot *BotAPI) sendChattable(config Chattable) (Message, error) { 338 v, err := config.values() 339 if err != nil { 340 return Message{}, err 341 } 342 343 message, err := bot.makeMessageRequest(config.method(), v) 344 345 if err != nil { 346 return Message{}, err 347 } 348 349 return message, nil 350} 351 352// GetUserProfilePhotos gets a user's profile photos. 353// 354// It requires UserID. 355// Offset and Limit are optional. 356func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) { 357 v := url.Values{} 358 v.Add("user_id", strconv.Itoa(config.UserID)) 359 if config.Offset != 0 { 360 v.Add("offset", strconv.Itoa(config.Offset)) 361 } 362 if config.Limit != 0 { 363 v.Add("limit", strconv.Itoa(config.Limit)) 364 } 365 366 resp, err := bot.MakeRequest("getUserProfilePhotos", v) 367 if err != nil { 368 return UserProfilePhotos{}, err 369 } 370 371 var profilePhotos UserProfilePhotos 372 json.Unmarshal(resp.Result, &profilePhotos) 373 374 bot.debugLog("GetUserProfilePhoto", v, profilePhotos) 375 376 return profilePhotos, nil 377} 378 379// GetFile returns a File which can download a file from Telegram. 380// 381// Requires FileID. 382func (bot *BotAPI) GetFile(config FileConfig) (File, error) { 383 v := url.Values{} 384 v.Add("file_id", config.FileID) 385 386 resp, err := bot.MakeRequest("getFile", v) 387 if err != nil { 388 return File{}, err 389 } 390 391 var file File 392 json.Unmarshal(resp.Result, &file) 393 394 bot.debugLog("GetFile", v, file) 395 396 return file, nil 397} 398 399// GetUpdates fetches updates. 400// If a WebHook is set, this will not return any data! 401// 402// Offset, Limit, and Timeout are optional. 403// To avoid stale items, set Offset to one higher than the previous item. 404// Set Timeout to a large number to reduce requests so you can get updates 405// instantly instead of having to wait between requests. 406func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) { 407 v := url.Values{} 408 if config.Offset != 0 { 409 v.Add("offset", strconv.Itoa(config.Offset)) 410 } 411 if config.Limit > 0 { 412 v.Add("limit", strconv.Itoa(config.Limit)) 413 } 414 if config.Timeout > 0 { 415 v.Add("timeout", strconv.Itoa(config.Timeout)) 416 } 417 418 resp, err := bot.MakeRequest("getUpdates", v) 419 if err != nil { 420 return []Update{}, err 421 } 422 423 var updates []Update 424 json.Unmarshal(resp.Result, &updates) 425 426 bot.debugLog("getUpdates", v, updates) 427 428 return updates, nil 429} 430 431// RemoveWebhook unsets the webhook. 432func (bot *BotAPI) RemoveWebhook() (APIResponse, error) { 433 return bot.MakeRequest("setWebhook", url.Values{}) 434} 435 436// SetWebhook sets a webhook. 437// 438// If this is set, GetUpdates will not get any data! 439// 440// If you do not have a legitimate TLS certificate, you need to include 441// your self signed certificate with the config. 442func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) { 443 444 if config.Certificate == nil { 445 v := url.Values{} 446 v.Add("url", config.URL.String()) 447 if config.MaxConnections != 0 { 448 v.Add("max_connections", strconv.Itoa(config.MaxConnections)) 449 } 450 451 return bot.MakeRequest("setWebhook", v) 452 } 453 454 params := make(map[string]string) 455 params["url"] = config.URL.String() 456 if config.MaxConnections != 0 { 457 params["max_connections"] = strconv.Itoa(config.MaxConnections) 458 } 459 460 resp, err := bot.UploadFile("setWebhook", params, "certificate", config.Certificate) 461 if err != nil { 462 return APIResponse{}, err 463 } 464 465 return resp, nil 466} 467 468// GetWebhookInfo allows you to fetch information about a webhook and if 469// one currently is set, along with pending update count and error messages. 470func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) { 471 resp, err := bot.MakeRequest("getWebhookInfo", url.Values{}) 472 if err != nil { 473 return WebhookInfo{}, err 474 } 475 476 var info WebhookInfo 477 err = json.Unmarshal(resp.Result, &info) 478 479 return info, err 480} 481 482// GetUpdatesChan starts and returns a channel for getting updates. 483func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) { 484 ch := make(chan Update, bot.Buffer) 485 486 go func() { 487 for { 488 select { 489 case <-bot.shutdownChannel: 490 return 491 default: 492 } 493 494 updates, err := bot.GetUpdates(config) 495 if err != nil { 496 log.Println(err) 497 log.Println("Failed to get updates, retrying in 3 seconds...") 498 time.Sleep(time.Second * 3) 499 500 continue 501 } 502 503 for _, update := range updates { 504 if update.UpdateID >= config.Offset { 505 config.Offset = update.UpdateID + 1 506 ch <- update 507 } 508 } 509 } 510 }() 511 512 return ch, nil 513} 514 515// StopReceivingUpdates stops the go routine which receives updates 516func (bot *BotAPI) StopReceivingUpdates() { 517 if bot.Debug { 518 log.Println("Stopping the update receiver routine...") 519 } 520 close(bot.shutdownChannel) 521} 522 523// ListenForWebhook registers a http handler for a webhook. 524func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel { 525 ch := make(chan Update, bot.Buffer) 526 527 http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) { 528 bytes, _ := ioutil.ReadAll(r.Body) 529 530 var update Update 531 json.Unmarshal(bytes, &update) 532 533 ch <- update 534 }) 535 536 return ch 537} 538 539// AnswerInlineQuery sends a response to an inline query. 540// 541// Note that you must respond to an inline query within 30 seconds. 542func (bot *BotAPI) AnswerInlineQuery(config InlineConfig) (APIResponse, error) { 543 v := url.Values{} 544 545 v.Add("inline_query_id", config.InlineQueryID) 546 v.Add("cache_time", strconv.Itoa(config.CacheTime)) 547 v.Add("is_personal", strconv.FormatBool(config.IsPersonal)) 548 v.Add("next_offset", config.NextOffset) 549 data, err := json.Marshal(config.Results) 550 if err != nil { 551 return APIResponse{}, err 552 } 553 v.Add("results", string(data)) 554 v.Add("switch_pm_text", config.SwitchPMText) 555 v.Add("switch_pm_parameter", config.SwitchPMParameter) 556 557 bot.debugLog("answerInlineQuery", v, nil) 558 559 return bot.MakeRequest("answerInlineQuery", v) 560} 561 562// AnswerCallbackQuery sends a response to an inline query callback. 563func (bot *BotAPI) AnswerCallbackQuery(config CallbackConfig) (APIResponse, error) { 564 v := url.Values{} 565 566 v.Add("callback_query_id", config.CallbackQueryID) 567 if config.Text != "" { 568 v.Add("text", config.Text) 569 } 570 v.Add("show_alert", strconv.FormatBool(config.ShowAlert)) 571 if config.URL != "" { 572 v.Add("url", config.URL) 573 } 574 v.Add("cache_time", strconv.Itoa(config.CacheTime)) 575 576 bot.debugLog("answerCallbackQuery", v, nil) 577 578 return bot.MakeRequest("answerCallbackQuery", v) 579} 580 581// KickChatMember kicks a user from a chat. Note that this only will work 582// in supergroups, and requires the bot to be an admin. Also note they 583// will be unable to rejoin until they are unbanned. 584func (bot *BotAPI) KickChatMember(config KickChatMemberConfig) (APIResponse, error) { 585 v := url.Values{} 586 587 if config.SuperGroupUsername == "" { 588 v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) 589 } else { 590 v.Add("chat_id", config.SuperGroupUsername) 591 } 592 v.Add("user_id", strconv.Itoa(config.UserID)) 593 594 if config.UntilDate != 0 { 595 v.Add("until_date", strconv.FormatInt(config.UntilDate, 10)) 596 } 597 598 bot.debugLog("kickChatMember", v, nil) 599 600 return bot.MakeRequest("kickChatMember", v) 601} 602 603// LeaveChat makes the bot leave the chat. 604func (bot *BotAPI) LeaveChat(config ChatConfig) (APIResponse, error) { 605 v := url.Values{} 606 607 if config.SuperGroupUsername == "" { 608 v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) 609 } else { 610 v.Add("chat_id", config.SuperGroupUsername) 611 } 612 613 bot.debugLog("leaveChat", v, nil) 614 615 return bot.MakeRequest("leaveChat", v) 616} 617 618// GetChat gets information about a chat. 619func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) { 620 v := url.Values{} 621 622 if config.SuperGroupUsername == "" { 623 v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) 624 } else { 625 v.Add("chat_id", config.SuperGroupUsername) 626 } 627 628 resp, err := bot.MakeRequest("getChat", v) 629 if err != nil { 630 return Chat{}, err 631 } 632 633 var chat Chat 634 err = json.Unmarshal(resp.Result, &chat) 635 636 bot.debugLog("getChat", v, chat) 637 638 return chat, err 639} 640 641// GetChatAdministrators gets a list of administrators in the chat. 642// 643// If none have been appointed, only the creator will be returned. 644// Bots are not shown, even if they are an administrator. 645func (bot *BotAPI) GetChatAdministrators(config ChatConfig) ([]ChatMember, error) { 646 v := url.Values{} 647 648 if config.SuperGroupUsername == "" { 649 v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) 650 } else { 651 v.Add("chat_id", config.SuperGroupUsername) 652 } 653 654 resp, err := bot.MakeRequest("getChatAdministrators", v) 655 if err != nil { 656 return []ChatMember{}, err 657 } 658 659 var members []ChatMember 660 err = json.Unmarshal(resp.Result, &members) 661 662 bot.debugLog("getChatAdministrators", v, members) 663 664 return members, err 665} 666 667// GetChatMembersCount gets the number of users in a chat. 668func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) { 669 v := url.Values{} 670 671 if config.SuperGroupUsername == "" { 672 v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) 673 } else { 674 v.Add("chat_id", config.SuperGroupUsername) 675 } 676 677 resp, err := bot.MakeRequest("getChatMembersCount", v) 678 if err != nil { 679 return -1, err 680 } 681 682 var count int 683 err = json.Unmarshal(resp.Result, &count) 684 685 bot.debugLog("getChatMembersCount", v, count) 686 687 return count, err 688} 689 690// GetChatMember gets a specific chat member. 691func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) { 692 v := url.Values{} 693 694 if config.SuperGroupUsername == "" { 695 v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) 696 } else { 697 v.Add("chat_id", config.SuperGroupUsername) 698 } 699 v.Add("user_id", strconv.Itoa(config.UserID)) 700 701 resp, err := bot.MakeRequest("getChatMember", v) 702 if err != nil { 703 return ChatMember{}, err 704 } 705 706 var member ChatMember 707 err = json.Unmarshal(resp.Result, &member) 708 709 bot.debugLog("getChatMember", v, member) 710 711 return member, err 712} 713 714// UnbanChatMember unbans a user from a chat. Note that this only will work 715// in supergroups and channels, and requires the bot to be an admin. 716func (bot *BotAPI) UnbanChatMember(config ChatMemberConfig) (APIResponse, error) { 717 v := url.Values{} 718 719 if config.SuperGroupUsername != "" { 720 v.Add("chat_id", config.SuperGroupUsername) 721 } else if config.ChannelUsername != "" { 722 v.Add("chat_id", config.ChannelUsername) 723 } else { 724 v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) 725 } 726 v.Add("user_id", strconv.Itoa(config.UserID)) 727 728 bot.debugLog("unbanChatMember", v, nil) 729 730 return bot.MakeRequest("unbanChatMember", v) 731} 732 733// RestrictChatMember to restrict a user in a supergroup. The bot must be an 734//administrator in the supergroup for this to work and must have the 735//appropriate admin rights. Pass True for all boolean parameters to lift 736//restrictions from a user. Returns True on success. 737func (bot *BotAPI) RestrictChatMember(config RestrictChatMemberConfig) (APIResponse, error) { 738 v := url.Values{} 739 740 if config.SuperGroupUsername != "" { 741 v.Add("chat_id", config.SuperGroupUsername) 742 } else if config.ChannelUsername != "" { 743 v.Add("chat_id", config.ChannelUsername) 744 } else { 745 v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) 746 } 747 v.Add("user_id", strconv.Itoa(config.UserID)) 748 749 if config.CanSendMessages != nil { 750 v.Add("can_send_messages", strconv.FormatBool(*config.CanSendMessages)) 751 } 752 if config.CanSendMediaMessages != nil { 753 v.Add("can_send_media_messages", strconv.FormatBool(*config.CanSendMediaMessages)) 754 } 755 if config.CanSendOtherMessages != nil { 756 v.Add("can_send_other_messages", strconv.FormatBool(*config.CanSendOtherMessages)) 757 } 758 if config.CanAddWebPagePreviews != nil { 759 v.Add("can_add_web_page_previews", strconv.FormatBool(*config.CanAddWebPagePreviews)) 760 } 761 if config.UntilDate != 0 { 762 v.Add("until_date", strconv.FormatInt(config.UntilDate, 10)) 763 } 764 765 bot.debugLog("restrictChatMember", v, nil) 766 767 return bot.MakeRequest("restrictChatMember", v) 768} 769 770// PromoteChatMember add admin rights to user 771func (bot *BotAPI) PromoteChatMember(config PromoteChatMemberConfig) (APIResponse, error) { 772 v := url.Values{} 773 774 if config.SuperGroupUsername != "" { 775 v.Add("chat_id", config.SuperGroupUsername) 776 } else if config.ChannelUsername != "" { 777 v.Add("chat_id", config.ChannelUsername) 778 } else { 779 v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) 780 } 781 v.Add("user_id", strconv.Itoa(config.UserID)) 782 783 if config.CanChangeInfo != nil { 784 v.Add("can_change_info", strconv.FormatBool(*config.CanChangeInfo)) 785 } 786 if config.CanPostMessages != nil { 787 v.Add("can_post_messages", strconv.FormatBool(*config.CanPostMessages)) 788 } 789 if config.CanEditMessages != nil { 790 v.Add("can_edit_messages", strconv.FormatBool(*config.CanEditMessages)) 791 } 792 if config.CanDeleteMessages != nil { 793 v.Add("can_delete_messages", strconv.FormatBool(*config.CanDeleteMessages)) 794 } 795 if config.CanInviteUsers != nil { 796 v.Add("can_invite_users", strconv.FormatBool(*config.CanInviteUsers)) 797 } 798 if config.CanRestrictMembers != nil { 799 v.Add("can_restrict_members", strconv.FormatBool(*config.CanRestrictMembers)) 800 } 801 if config.CanPinMessages != nil { 802 v.Add("can_pin_messages", strconv.FormatBool(*config.CanPinMessages)) 803 } 804 if config.CanPromoteMembers != nil { 805 v.Add("can_promote_members", strconv.FormatBool(*config.CanPromoteMembers)) 806 } 807 808 bot.debugLog("promoteChatMember", v, nil) 809 810 return bot.MakeRequest("promoteChatMember", v) 811} 812 813// GetGameHighScores allows you to get the high scores for a game. 814func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) { 815 v, _ := config.values() 816 817 resp, err := bot.MakeRequest(config.method(), v) 818 if err != nil { 819 return []GameHighScore{}, err 820 } 821 822 var highScores []GameHighScore 823 err = json.Unmarshal(resp.Result, &highScores) 824 825 return highScores, err 826} 827 828// AnswerShippingQuery allows you to reply to Update with shipping_query parameter. 829func (bot *BotAPI) AnswerShippingQuery(config ShippingConfig) (APIResponse, error) { 830 v := url.Values{} 831 832 v.Add("shipping_query_id", config.ShippingQueryID) 833 v.Add("ok", strconv.FormatBool(config.OK)) 834 if config.OK == true { 835 data, err := json.Marshal(config.ShippingOptions) 836 if err != nil { 837 return APIResponse{}, err 838 } 839 v.Add("shipping_options", string(data)) 840 } else { 841 v.Add("error_message", config.ErrorMessage) 842 } 843 844 bot.debugLog("answerShippingQuery", v, nil) 845 846 return bot.MakeRequest("answerShippingQuery", v) 847} 848 849// AnswerPreCheckoutQuery allows you to reply to Update with pre_checkout_query. 850func (bot *BotAPI) AnswerPreCheckoutQuery(config PreCheckoutConfig) (APIResponse, error) { 851 v := url.Values{} 852 853 v.Add("pre_checkout_query_id", config.PreCheckoutQueryID) 854 v.Add("ok", strconv.FormatBool(config.OK)) 855 if config.OK != true { 856 v.Add("error", config.ErrorMessage) 857 } 858 859 bot.debugLog("answerPreCheckoutQuery", v, nil) 860 861 return bot.MakeRequest("answerPreCheckoutQuery", v) 862} 863 864// DeleteMessage deletes a message in a chat 865func (bot *BotAPI) DeleteMessage(config DeleteMessageConfig) (APIResponse, error) { 866 v, err := config.values() 867 if err != nil { 868 return APIResponse{}, err 869 } 870 871 bot.debugLog(config.method(), v, nil) 872 873 return bot.MakeRequest(config.method(), v) 874} 875 876// GetInviteLink get InviteLink for a chat 877func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) { 878 v := url.Values{} 879 880 if config.SuperGroupUsername == "" { 881 v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) 882 } else { 883 v.Add("chat_id", config.SuperGroupUsername) 884 } 885 886 resp, err := bot.MakeRequest("exportChatInviteLink", v) 887 if err != nil { 888 return "", err 889 } 890 891 var inviteLink string 892 err = json.Unmarshal(resp.Result, &inviteLink) 893 894 return inviteLink, err 895} 896 897// PinChatMessage pin message in supergroup 898func (bot *BotAPI) PinChatMessage(config PinChatMessageConfig) (APIResponse, error) { 899 v, err := config.values() 900 if err != nil { 901 return APIResponse{}, err 902 } 903 904 bot.debugLog(config.method(), v, nil) 905 906 return bot.MakeRequest(config.method(), v) 907} 908 909// UnpinChatMessage unpin message in supergroup 910func (bot *BotAPI) UnpinChatMessage(config UnpinChatMessageConfig) (APIResponse, error) { 911 v, err := config.values() 912 if err != nil { 913 return APIResponse{}, err 914 } 915 916 bot.debugLog(config.method(), v, nil) 917 918 return bot.MakeRequest(config.method(), v) 919} 920 921// SetChatTitle change title of chat. 922func (bot *BotAPI) SetChatTitle(config SetChatTitleConfig) (APIResponse, error) { 923 v, err := config.values() 924 if err != nil { 925 return APIResponse{}, err 926 } 927 928 bot.debugLog(config.method(), v, nil) 929 930 return bot.MakeRequest(config.method(), v) 931} 932 933// SetChatDescription change description of chat. 934func (bot *BotAPI) SetChatDescription(config SetChatDescriptionConfig) (APIResponse, error) { 935 v, err := config.values() 936 if err != nil { 937 return APIResponse{}, err 938 } 939 940 bot.debugLog(config.method(), v, nil) 941 942 return bot.MakeRequest(config.method(), v) 943} 944 945// SetChatPhoto change photo of chat. 946func (bot *BotAPI) SetChatPhoto(config SetChatPhotoConfig) (APIResponse, error) { 947 params, err := config.params() 948 if err != nil { 949 return APIResponse{}, err 950 } 951 952 file := config.getFile() 953 954 return bot.UploadFile(config.method(), params, config.name(), file) 955} 956 957// DeleteChatPhoto delete photo of chat. 958func (bot *BotAPI) DeleteChatPhoto(config DeleteChatPhotoConfig) (APIResponse, error) { 959 v, err := config.values() 960 if err != nil { 961 return APIResponse{}, err 962 } 963 964 bot.debugLog(config.method(), v, nil) 965 966 return bot.MakeRequest(config.method(), v) 967} 968