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