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