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