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