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