methods.go (view raw)
1package tgbotapi
2
3import (
4 "bytes"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "github.com/technoweenie/multipartstreamer"
9 "io"
10 "io/ioutil"
11 "log"
12 "net/http"
13 "net/url"
14 "os"
15 "strconv"
16)
17
18// Telegram constants
19const (
20 // APIEndpoint is the endpoint for all API methods, with formatting for Sprintf
21 APIEndpoint = "https://api.telegram.org/bot%s/%s"
22)
23
24// Constant values for ChatActions
25const (
26 ChatTyping = "typing"
27 ChatUploadPhoto = "upload_photo"
28 ChatRecordVideo = "record_video"
29 ChatUploadVideo = "upload_video"
30 ChatRecordAudio = "record_audio"
31 ChatUploadAudio = "upload_audio"
32 ChatUploadDocument = "upload_document"
33 ChatFindLocation = "find_location"
34)
35
36// API errors
37const (
38 // APIForbidden happens when a token is bad
39 APIForbidden = "forbidden"
40)
41
42// MessageConfig contains information about a SendMessage request.
43type MessageConfig struct {
44 ChatID int
45 Text string
46 DisableWebPagePreview bool
47 ReplyToMessageID int
48 ReplyMarkup interface{}
49}
50
51// ForwardConfig contains information about a ForwardMessage request.
52type ForwardConfig struct {
53 ChatID int
54 FromChatID int
55 MessageID int
56}
57
58// PhotoConfig contains information about a SendPhoto request.
59type PhotoConfig struct {
60 ChatID int
61 Caption string
62 ReplyToMessageID int
63 ReplyMarkup interface{}
64 UseExistingPhoto bool
65 FilePath string
66 File interface{}
67 FileID string
68}
69
70// AudioConfig contains information about a SendAudio request.
71type AudioConfig struct {
72 ChatID int
73 Duration int
74 Performer string
75 Title string
76 ReplyToMessageID int
77 ReplyMarkup interface{}
78 UseExistingAudio bool
79 FilePath string
80 File interface{}
81 FileID string
82}
83
84// DocumentConfig contains information about a SendDocument request.
85type DocumentConfig struct {
86 ChatID int
87 ReplyToMessageID int
88 ReplyMarkup interface{}
89 UseExistingDocument bool
90 FilePath string
91 File interface{}
92 FileID string
93}
94
95// StickerConfig contains information about a SendSticker request.
96type StickerConfig struct {
97 ChatID int
98 ReplyToMessageID int
99 ReplyMarkup interface{}
100 UseExistingSticker bool
101 FilePath string
102 File interface{}
103 FileID string
104}
105
106// VideoConfig contains information about a SendVideo request.
107type VideoConfig struct {
108 ChatID int
109 Duration int
110 Caption string
111 ReplyToMessageID int
112 ReplyMarkup interface{}
113 UseExistingVideo bool
114 FilePath string
115 File interface{}
116 FileID string
117}
118
119// VoiceConfig contains information about a SendVoice request.
120type VoiceConfig struct {
121 ChatID int
122 Duration int
123 ReplyToMessageID int
124 ReplyMarkup interface{}
125 UseExistingVoice bool
126 FilePath string
127 File interface{}
128 FileID string
129}
130
131// LocationConfig contains information about a SendLocation request.
132type LocationConfig struct {
133 ChatID int
134 Latitude float64
135 Longitude float64
136 ReplyToMessageID int
137 ReplyMarkup interface{}
138}
139
140// ChatActionConfig contains information about a SendChatAction request.
141type ChatActionConfig struct {
142 ChatID int
143 Action string
144}
145
146// UserProfilePhotosConfig contains information about a GetUserProfilePhotos request.
147type UserProfilePhotosConfig struct {
148 UserID int
149 Offset int
150 Limit int
151}
152
153// UpdateConfig contains information about a GetUpdates request.
154type UpdateConfig struct {
155 Offset int
156 Limit int
157 Timeout int
158}
159
160// WebhookConfig contains information about a SetWebhook request.
161type WebhookConfig struct {
162 Clear bool
163 URL *url.URL
164 Certificate interface{}
165}
166
167// FileBytes contains information about a set of bytes to upload as a File.
168type FileBytes struct {
169 Name string
170 Bytes []byte
171}
172
173// FileReader contains information about a reader to upload as a File.
174// If Size is -1, it will read the entire Reader into memory to calculate a Size.
175type FileReader struct {
176 Name string
177 Reader io.Reader
178 Size int64
179}
180
181// MakeRequest makes a request to a specific endpoint with our token.
182// All requests are POSTs because Telegram doesn't care, and it's easier.
183func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) {
184 resp, err := bot.Client.PostForm(fmt.Sprintf(APIEndpoint, bot.Token, endpoint), params)
185 if err != nil {
186 return APIResponse{}, err
187 }
188 defer resp.Body.Close()
189
190 if resp.StatusCode == http.StatusForbidden {
191 return APIResponse{}, errors.New(APIForbidden)
192 }
193
194 bytes, err := ioutil.ReadAll(resp.Body)
195 if err != nil {
196 return APIResponse{}, err
197 }
198
199 if bot.Debug {
200 log.Println(endpoint, string(bytes))
201 }
202
203 var apiResp APIResponse
204 json.Unmarshal(bytes, &apiResp)
205
206 if !apiResp.Ok {
207 return APIResponse{}, errors.New(apiResp.Description)
208 }
209
210 return apiResp, nil
211}
212
213// UploadFile makes a request to the API with a file.
214//
215// Requires the parameter to hold the file not be in the params.
216// File should be a string to a file path, a FileBytes struct, or a FileReader struct.
217func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) {
218 ms := multipartstreamer.New()
219 ms.WriteFields(params)
220
221 switch f := file.(type) {
222 case string:
223 fileHandle, err := os.Open(f)
224 if err != nil {
225 return APIResponse{}, err
226 }
227 defer fileHandle.Close()
228
229 fi, err := os.Stat(f)
230 if err != nil {
231 return APIResponse{}, err
232 }
233
234 ms.WriteReader(fieldname, fileHandle.Name(), fi.Size(), fileHandle)
235 case FileBytes:
236 buf := bytes.NewBuffer(f.Bytes)
237 ms.WriteReader(fieldname, f.Name, int64(len(f.Bytes)), buf)
238 case FileReader:
239 if f.Size == -1 {
240 data, err := ioutil.ReadAll(f.Reader)
241 if err != nil {
242 return APIResponse{}, err
243 }
244 buf := bytes.NewBuffer(data)
245
246 ms.WriteReader(fieldname, f.Name, int64(len(data)), buf)
247
248 break
249 }
250
251 ms.WriteReader(fieldname, f.Name, f.Size, f.Reader)
252 default:
253 return APIResponse{}, errors.New("bad file type")
254 }
255
256 req, err := http.NewRequest("POST", fmt.Sprintf(APIEndpoint, bot.Token, endpoint), nil)
257 ms.SetupRequest(req)
258 if err != nil {
259 return APIResponse{}, err
260 }
261
262 res, err := bot.Client.Do(req)
263 if err != nil {
264 return APIResponse{}, err
265 }
266 defer res.Body.Close()
267
268 bytes, err := ioutil.ReadAll(res.Body)
269 if err != nil {
270 return APIResponse{}, err
271 }
272
273 if bot.Debug {
274 log.Println(string(bytes[:]))
275 }
276
277 var apiResp APIResponse
278 json.Unmarshal(bytes, &apiResp)
279
280 return apiResp, nil
281}
282
283// GetMe fetches the currently authenticated bot.
284//
285// There are no parameters for this method.
286func (bot *BotAPI) GetMe() (User, error) {
287 resp, err := bot.MakeRequest("getMe", nil)
288 if err != nil {
289 return User{}, err
290 }
291
292 var user User
293 json.Unmarshal(resp.Result, &user)
294
295 if bot.Debug {
296 log.Printf("getMe: %+v\n", user)
297 }
298
299 return user, nil
300}
301
302// SendMessage sends a Message to a chat.
303//
304// Requires ChatID and Text.
305// DisableWebPagePreview, ReplyToMessageID, and ReplyMarkup are optional.
306func (bot *BotAPI) SendMessage(config MessageConfig) (Message, error) {
307 v := url.Values{}
308 v.Add("chat_id", strconv.Itoa(config.ChatID))
309 v.Add("text", config.Text)
310 v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview))
311 if config.ReplyToMessageID != 0 {
312 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
313 }
314 if config.ReplyMarkup != nil {
315 data, err := json.Marshal(config.ReplyMarkup)
316 if err != nil {
317 return Message{}, err
318 }
319
320 v.Add("reply_markup", string(data))
321 }
322
323 resp, err := bot.MakeRequest("SendMessage", v)
324 if err != nil {
325 return Message{}, err
326 }
327
328 var message Message
329 json.Unmarshal(resp.Result, &message)
330
331 if bot.Debug {
332 log.Printf("SendMessage req : %+v\n", v)
333 log.Printf("SendMessage resp: %+v\n", message)
334 }
335
336 return message, nil
337}
338
339// ForwardMessage forwards a message from one chat to another.
340//
341// Requires ChatID (destination), FromChatID (source), and MessageID.
342func (bot *BotAPI) ForwardMessage(config ForwardConfig) (Message, error) {
343 v := url.Values{}
344 v.Add("chat_id", strconv.Itoa(config.ChatID))
345 v.Add("from_chat_id", strconv.Itoa(config.FromChatID))
346 v.Add("message_id", strconv.Itoa(config.MessageID))
347
348 resp, err := bot.MakeRequest("forwardMessage", v)
349 if err != nil {
350 return Message{}, err
351 }
352
353 var message Message
354 json.Unmarshal(resp.Result, &message)
355
356 if bot.Debug {
357 log.Printf("forwardMessage req : %+v\n", v)
358 log.Printf("forwardMessage resp: %+v\n", message)
359 }
360
361 return message, nil
362}
363
364// SendPhoto sends or uploads a photo to a chat.
365//
366// Requires ChatID and FileID OR File.
367// Caption, ReplyToMessageID, and ReplyMarkup are optional.
368// File should be either a string, FileBytes, or FileReader.
369func (bot *BotAPI) SendPhoto(config PhotoConfig) (Message, error) {
370 if config.UseExistingPhoto {
371 v := url.Values{}
372 v.Add("chat_id", strconv.Itoa(config.ChatID))
373 v.Add("photo", config.FileID)
374 if config.Caption != "" {
375 v.Add("caption", config.Caption)
376 }
377 if config.ReplyToMessageID != 0 {
378 v.Add("reply_to_message_id", strconv.Itoa(config.ChatID))
379 }
380 if config.ReplyMarkup != nil {
381 data, err := json.Marshal(config.ReplyMarkup)
382 if err != nil {
383 return Message{}, err
384 }
385
386 v.Add("reply_markup", string(data))
387 }
388
389 resp, err := bot.MakeRequest("SendPhoto", v)
390 if err != nil {
391 return Message{}, err
392 }
393
394 var message Message
395 json.Unmarshal(resp.Result, &message)
396
397 if bot.Debug {
398 log.Printf("SendPhoto req : %+v\n", v)
399 log.Printf("SendPhoto resp: %+v\n", message)
400 }
401
402 return message, nil
403 }
404
405 params := make(map[string]string)
406 params["chat_id"] = strconv.Itoa(config.ChatID)
407 if config.Caption != "" {
408 params["caption"] = config.Caption
409 }
410 if config.ReplyToMessageID != 0 {
411 params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
412 }
413 if config.ReplyMarkup != nil {
414 data, err := json.Marshal(config.ReplyMarkup)
415 if err != nil {
416 return Message{}, err
417 }
418
419 params["reply_markup"] = string(data)
420 }
421
422 var file interface{}
423 if config.FilePath == "" {
424 file = config.File
425 } else {
426 file = config.FilePath
427 }
428
429 resp, err := bot.UploadFile("SendPhoto", params, "photo", file)
430 if err != nil {
431 return Message{}, err
432 }
433
434 var message Message
435 json.Unmarshal(resp.Result, &message)
436
437 if bot.Debug {
438 log.Printf("SendPhoto resp: %+v\n", message)
439 }
440
441 return message, nil
442}
443
444// SendAudio sends or uploads an audio clip to a chat.
445// If using a file, the file must be in the .mp3 format.
446//
447// When the fields title and performer are both empty and
448// the mime-type of the file to be sent is not audio/mpeg,
449// the file must be an .ogg file encoded with OPUS.
450// You may use the tgutils.EncodeAudio func to assist you with this, if needed.
451//
452// Requires ChatID and FileID OR File.
453// ReplyToMessageID and ReplyMarkup are optional.
454// File should be either a string, FileBytes, or FileReader.
455func (bot *BotAPI) SendAudio(config AudioConfig) (Message, error) {
456 if config.UseExistingAudio {
457 v := url.Values{}
458 v.Add("chat_id", strconv.Itoa(config.ChatID))
459 v.Add("audio", config.FileID)
460 if config.ReplyToMessageID != 0 {
461 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
462 }
463 if config.Duration != 0 {
464 v.Add("duration", strconv.Itoa(config.Duration))
465 }
466 if config.ReplyMarkup != nil {
467 data, err := json.Marshal(config.ReplyMarkup)
468 if err != nil {
469 return Message{}, err
470 }
471
472 v.Add("reply_markup", string(data))
473 }
474 if config.Performer != "" {
475 v.Add("performer", config.Performer)
476 }
477 if config.Title != "" {
478 v.Add("title", config.Title)
479 }
480
481 resp, err := bot.MakeRequest("sendAudio", v)
482 if err != nil {
483 return Message{}, err
484 }
485
486 var message Message
487 json.Unmarshal(resp.Result, &message)
488
489 if bot.Debug {
490 log.Printf("sendAudio req : %+v\n", v)
491 log.Printf("sendAudio resp: %+v\n", message)
492 }
493
494 return message, nil
495 }
496
497 params := make(map[string]string)
498
499 params["chat_id"] = strconv.Itoa(config.ChatID)
500 if config.ReplyToMessageID != 0 {
501 params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
502 }
503 if config.Duration != 0 {
504 params["duration"] = strconv.Itoa(config.Duration)
505 }
506 if config.ReplyMarkup != nil {
507 data, err := json.Marshal(config.ReplyMarkup)
508 if err != nil {
509 return Message{}, err
510 }
511
512 params["reply_markup"] = string(data)
513 }
514 if config.Performer != "" {
515 params["performer"] = config.Performer
516 }
517 if config.Title != "" {
518 params["title"] = config.Title
519 }
520
521 var file interface{}
522 if config.FilePath == "" {
523 file = config.File
524 } else {
525 file = config.FilePath
526 }
527
528 resp, err := bot.UploadFile("sendAudio", params, "audio", file)
529 if err != nil {
530 return Message{}, err
531 }
532
533 var message Message
534 json.Unmarshal(resp.Result, &message)
535
536 if bot.Debug {
537 log.Printf("sendAudio resp: %+v\n", message)
538 }
539
540 return message, nil
541}
542
543// SendDocument sends or uploads a document to a chat.
544//
545// Requires ChatID and FileID OR File.
546// ReplyToMessageID and ReplyMarkup are optional.
547// File should be either a string, FileBytes, or FileReader.
548func (bot *BotAPI) SendDocument(config DocumentConfig) (Message, error) {
549 if config.UseExistingDocument {
550 v := url.Values{}
551 v.Add("chat_id", strconv.Itoa(config.ChatID))
552 v.Add("document", config.FileID)
553 if config.ReplyToMessageID != 0 {
554 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
555 }
556 if config.ReplyMarkup != nil {
557 data, err := json.Marshal(config.ReplyMarkup)
558 if err != nil {
559 return Message{}, err
560 }
561
562 v.Add("reply_markup", string(data))
563 }
564
565 resp, err := bot.MakeRequest("sendDocument", v)
566 if err != nil {
567 return Message{}, err
568 }
569
570 var message Message
571 json.Unmarshal(resp.Result, &message)
572
573 if bot.Debug {
574 log.Printf("sendDocument req : %+v\n", v)
575 log.Printf("sendDocument resp: %+v\n", message)
576 }
577
578 return message, nil
579 }
580
581 params := make(map[string]string)
582
583 params["chat_id"] = strconv.Itoa(config.ChatID)
584 if config.ReplyToMessageID != 0 {
585 params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
586 }
587 if config.ReplyMarkup != nil {
588 data, err := json.Marshal(config.ReplyMarkup)
589 if err != nil {
590 return Message{}, err
591 }
592
593 params["reply_markup"] = string(data)
594 }
595
596 var file interface{}
597 if config.FilePath == "" {
598 file = config.File
599 } else {
600 file = config.FilePath
601 }
602
603 resp, err := bot.UploadFile("sendDocument", params, "document", file)
604 if err != nil {
605 return Message{}, err
606 }
607
608 var message Message
609 json.Unmarshal(resp.Result, &message)
610
611 if bot.Debug {
612 log.Printf("sendDocument resp: %+v\n", message)
613 }
614
615 return message, nil
616}
617
618// SendVoice sends or uploads a playable voice to a chat.
619// If using a file, the file must be encoded as an .ogg with OPUS.
620// You may use the tgutils.EncodeAudio func to assist you with this, if needed.
621//
622// Requires ChatID and FileID OR File.
623// ReplyToMessageID and ReplyMarkup are optional.
624// File should be either a string, FileBytes, or FileReader.
625func (bot *BotAPI) SendVoice(config VoiceConfig) (Message, error) {
626 if config.UseExistingVoice {
627 v := url.Values{}
628 v.Add("chat_id", strconv.Itoa(config.ChatID))
629 v.Add("voice", config.FileID)
630 if config.ReplyToMessageID != 0 {
631 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
632 }
633 if config.Duration != 0 {
634 v.Add("duration", strconv.Itoa(config.Duration))
635 }
636 if config.ReplyMarkup != nil {
637 data, err := json.Marshal(config.ReplyMarkup)
638 if err != nil {
639 return Message{}, err
640 }
641
642 v.Add("reply_markup", string(data))
643 }
644
645 resp, err := bot.MakeRequest("sendVoice", v)
646 if err != nil {
647 return Message{}, err
648 }
649
650 var message Message
651 json.Unmarshal(resp.Result, &message)
652
653 if bot.Debug {
654 log.Printf("SendVoice req : %+v\n", v)
655 log.Printf("SendVoice resp: %+v\n", message)
656 }
657
658 return message, nil
659 }
660
661 params := make(map[string]string)
662
663 params["chat_id"] = strconv.Itoa(config.ChatID)
664 if config.ReplyToMessageID != 0 {
665 params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
666 }
667 if config.Duration != 0 {
668 params["duration"] = strconv.Itoa(config.Duration)
669 }
670 if config.ReplyMarkup != nil {
671 data, err := json.Marshal(config.ReplyMarkup)
672 if err != nil {
673 return Message{}, err
674 }
675
676 params["reply_markup"] = string(data)
677 }
678
679 var file interface{}
680 if config.FilePath == "" {
681 file = config.File
682 } else {
683 file = config.FilePath
684 }
685
686 resp, err := bot.UploadFile("SendVoice", params, "voice", file)
687 if err != nil {
688 return Message{}, err
689 }
690
691 var message Message
692 json.Unmarshal(resp.Result, &message)
693
694 if bot.Debug {
695 log.Printf("SendVoice resp: %+v\n", message)
696 }
697
698 return message, nil
699}
700
701// SendSticker sends or uploads a sticker to a chat.
702//
703// Requires ChatID and FileID OR File.
704// ReplyToMessageID and ReplyMarkup are optional.
705// File should be either a string, FileBytes, or FileReader.
706func (bot *BotAPI) SendSticker(config StickerConfig) (Message, error) {
707 if config.UseExistingSticker {
708 v := url.Values{}
709 v.Add("chat_id", strconv.Itoa(config.ChatID))
710 v.Add("sticker", config.FileID)
711 if config.ReplyToMessageID != 0 {
712 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
713 }
714 if config.ReplyMarkup != nil {
715 data, err := json.Marshal(config.ReplyMarkup)
716 if err != nil {
717 return Message{}, err
718 }
719
720 v.Add("reply_markup", string(data))
721 }
722
723 resp, err := bot.MakeRequest("sendSticker", v)
724 if err != nil {
725 return Message{}, err
726 }
727
728 var message Message
729 json.Unmarshal(resp.Result, &message)
730
731 if bot.Debug {
732 log.Printf("sendSticker req : %+v\n", v)
733 log.Printf("sendSticker resp: %+v\n", message)
734 }
735
736 return message, nil
737 }
738
739 params := make(map[string]string)
740
741 params["chat_id"] = strconv.Itoa(config.ChatID)
742 if config.ReplyToMessageID != 0 {
743 params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
744 }
745 if config.ReplyMarkup != nil {
746 data, err := json.Marshal(config.ReplyMarkup)
747 if err != nil {
748 return Message{}, err
749 }
750
751 params["reply_markup"] = string(data)
752 }
753
754 var file interface{}
755 if config.FilePath == "" {
756 file = config.File
757 } else {
758 file = config.FilePath
759 }
760
761 resp, err := bot.UploadFile("sendSticker", params, "sticker", file)
762 if err != nil {
763 return Message{}, err
764 }
765
766 var message Message
767 json.Unmarshal(resp.Result, &message)
768
769 if bot.Debug {
770 log.Printf("sendSticker resp: %+v\n", message)
771 }
772
773 return message, nil
774}
775
776// SendVideo sends or uploads a video to a chat.
777//
778// Requires ChatID and FileID OR File.
779// ReplyToMessageID and ReplyMarkup are optional.
780// File should be either a string, FileBytes, or FileReader.
781func (bot *BotAPI) SendVideo(config VideoConfig) (Message, error) {
782 if config.UseExistingVideo {
783 v := url.Values{}
784 v.Add("chat_id", strconv.Itoa(config.ChatID))
785 v.Add("video", config.FileID)
786 if config.ReplyToMessageID != 0 {
787 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
788 }
789 if config.Duration != 0 {
790 v.Add("duration", strconv.Itoa(config.Duration))
791 }
792 if config.Caption != "" {
793 v.Add("caption", config.Caption)
794 }
795 if config.ReplyMarkup != nil {
796 data, err := json.Marshal(config.ReplyMarkup)
797 if err != nil {
798 return Message{}, err
799 }
800
801 v.Add("reply_markup", string(data))
802 }
803
804 resp, err := bot.MakeRequest("sendVideo", v)
805 if err != nil {
806 return Message{}, err
807 }
808
809 var message Message
810 json.Unmarshal(resp.Result, &message)
811
812 if bot.Debug {
813 log.Printf("sendVideo req : %+v\n", v)
814 log.Printf("sendVideo resp: %+v\n", message)
815 }
816
817 return message, nil
818 }
819
820 params := make(map[string]string)
821
822 params["chat_id"] = strconv.Itoa(config.ChatID)
823 if config.ReplyToMessageID != 0 {
824 params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
825 }
826 if config.ReplyMarkup != nil {
827 data, err := json.Marshal(config.ReplyMarkup)
828 if err != nil {
829 return Message{}, err
830 }
831
832 params["reply_markup"] = string(data)
833 }
834
835 var file interface{}
836 if config.FilePath == "" {
837 file = config.File
838 } else {
839 file = config.FilePath
840 }
841
842 resp, err := bot.UploadFile("sendVideo", params, "video", file)
843 if err != nil {
844 return Message{}, err
845 }
846
847 var message Message
848 json.Unmarshal(resp.Result, &message)
849
850 if bot.Debug {
851 log.Printf("sendVideo resp: %+v\n", message)
852 }
853
854 return message, nil
855}
856
857// SendLocation sends a location to a chat.
858//
859// Requires ChatID, Latitude, and Longitude.
860// ReplyToMessageID and ReplyMarkup are optional.
861func (bot *BotAPI) SendLocation(config LocationConfig) (Message, error) {
862 v := url.Values{}
863 v.Add("chat_id", strconv.Itoa(config.ChatID))
864 v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64))
865 v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64))
866 if config.ReplyToMessageID != 0 {
867 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
868 }
869 if config.ReplyMarkup != nil {
870 data, err := json.Marshal(config.ReplyMarkup)
871 if err != nil {
872 return Message{}, err
873 }
874
875 v.Add("reply_markup", string(data))
876 }
877
878 resp, err := bot.MakeRequest("sendLocation", v)
879 if err != nil {
880 return Message{}, err
881 }
882
883 var message Message
884 json.Unmarshal(resp.Result, &message)
885
886 if bot.Debug {
887 log.Printf("sendLocation req : %+v\n", v)
888 log.Printf("sendLocation resp: %+v\n", message)
889 }
890
891 return message, nil
892}
893
894// SendChatAction sets a current action in a chat.
895//
896// Requires ChatID and a valid Action (see Chat constants).
897func (bot *BotAPI) SendChatAction(config ChatActionConfig) error {
898 v := url.Values{}
899 v.Add("chat_id", strconv.Itoa(config.ChatID))
900 v.Add("action", config.Action)
901
902 _, err := bot.MakeRequest("sendChatAction", v)
903 if err != nil {
904 return err
905 }
906
907 return nil
908}
909
910// GetUserProfilePhotos gets a user's profile photos.
911//
912// Requires UserID.
913// Offset and Limit are optional.
914func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
915 v := url.Values{}
916 v.Add("user_id", strconv.Itoa(config.UserID))
917 if config.Offset != 0 {
918 v.Add("offset", strconv.Itoa(config.Offset))
919 }
920 if config.Limit != 0 {
921 v.Add("limit", strconv.Itoa(config.Limit))
922 }
923
924 resp, err := bot.MakeRequest("getUserProfilePhotos", v)
925 if err != nil {
926 return UserProfilePhotos{}, err
927 }
928
929 var profilePhotos UserProfilePhotos
930 json.Unmarshal(resp.Result, &profilePhotos)
931
932 if bot.Debug {
933 log.Printf("getUserProfilePhotos req : %+v\n", v)
934 log.Printf("getUserProfilePhotos resp: %+v\n", profilePhotos)
935 }
936
937 return profilePhotos, nil
938}
939
940// GetUpdates fetches updates.
941// If a WebHook is set, this will not return any data!
942//
943// Offset, Limit, and Timeout are optional.
944// To not get old items, set Offset to one higher than the previous item.
945// Set Timeout to a large number to reduce requests and get responses instantly.
946func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
947 v := url.Values{}
948 if config.Offset > 0 {
949 v.Add("offset", strconv.Itoa(config.Offset))
950 }
951 if config.Limit > 0 {
952 v.Add("limit", strconv.Itoa(config.Limit))
953 }
954 if config.Timeout > 0 {
955 v.Add("timeout", strconv.Itoa(config.Timeout))
956 }
957
958 resp, err := bot.MakeRequest("getUpdates", v)
959 if err != nil {
960 return []Update{}, err
961 }
962
963 var updates []Update
964 json.Unmarshal(resp.Result, &updates)
965
966 if bot.Debug {
967 log.Printf("getUpdates: %+v\n", updates)
968 }
969
970 return updates, nil
971}
972
973// SetWebhook sets a webhook.
974// If this is set, GetUpdates will not get any data!
975//
976// Requires Url OR to set Clear to true.
977func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
978 if config.Certificate == nil {
979 v := url.Values{}
980 if !config.Clear {
981 v.Add("url", config.URL.String())
982 }
983
984 return bot.MakeRequest("setWebhook", v)
985 }
986
987 params := make(map[string]string)
988 params["url"] = config.URL.String()
989
990 resp, err := bot.UploadFile("setWebhook", params, "certificate", config.Certificate)
991 if err != nil {
992 return APIResponse{}, err
993 }
994
995 var apiResp APIResponse
996 json.Unmarshal(resp.Result, &apiResp)
997
998 if bot.Debug {
999 log.Printf("setWebhook resp: %+v\n", apiResp)
1000 }
1001
1002 return apiResp, nil
1003}