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 if !apiResp.Ok {
294 return APIResponse{}, errors.New(apiResp.Description)
295 }
296
297 return apiResp, nil
298}
299
300// GetMe fetches the currently authenticated bot.
301//
302// There are no parameters for this method.
303func (bot *BotAPI) GetMe() (User, error) {
304 resp, err := bot.MakeRequest("getMe", nil)
305 if err != nil {
306 return User{}, err
307 }
308
309 var user User
310 json.Unmarshal(resp.Result, &user)
311
312 if bot.Debug {
313 log.Printf("getMe: %+v\n", user)
314 }
315
316 return user, nil
317}
318
319// SendMessage sends a Message to a chat.
320//
321// Requires ChatID and Text.
322// DisableWebPagePreview, ReplyToMessageID, and ReplyMarkup are optional.
323func (bot *BotAPI) SendMessage(config MessageConfig) (Message, error) {
324 v := url.Values{}
325 v.Add("chat_id", strconv.Itoa(config.ChatID))
326 v.Add("text", config.Text)
327 v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview))
328 if config.ParseMode != "" {
329 v.Add("parse_mode", config.ParseMode)
330 }
331 if config.ReplyToMessageID != 0 {
332 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
333 }
334 if config.ReplyMarkup != nil {
335 data, err := json.Marshal(config.ReplyMarkup)
336 if err != nil {
337 return Message{}, err
338 }
339
340 v.Add("reply_markup", string(data))
341 }
342
343 resp, err := bot.MakeRequest("SendMessage", v)
344 if err != nil {
345 return Message{}, err
346 }
347
348 var message Message
349 json.Unmarshal(resp.Result, &message)
350
351 if bot.Debug {
352 log.Printf("SendMessage req : %+v\n", v)
353 log.Printf("SendMessage resp: %+v\n", message)
354 }
355
356 return message, nil
357}
358
359// ForwardMessage forwards a message from one chat to another.
360//
361// Requires ChatID (destination), FromChatID (source), and MessageID.
362func (bot *BotAPI) ForwardMessage(config ForwardConfig) (Message, error) {
363 v := url.Values{}
364 v.Add("chat_id", strconv.Itoa(config.ChatID))
365 v.Add("from_chat_id", strconv.Itoa(config.FromChatID))
366 v.Add("message_id", strconv.Itoa(config.MessageID))
367
368 resp, err := bot.MakeRequest("forwardMessage", v)
369 if err != nil {
370 return Message{}, err
371 }
372
373 var message Message
374 json.Unmarshal(resp.Result, &message)
375
376 if bot.Debug {
377 log.Printf("forwardMessage req : %+v\n", v)
378 log.Printf("forwardMessage resp: %+v\n", message)
379 }
380
381 return message, nil
382}
383
384// SendPhoto sends or uploads a photo to a chat.
385//
386// Requires ChatID and FileID OR File.
387// Caption, ReplyToMessageID, and ReplyMarkup are optional.
388// File should be either a string, FileBytes, or FileReader.
389func (bot *BotAPI) SendPhoto(config PhotoConfig) (Message, error) {
390 if config.UseExistingPhoto {
391 v := url.Values{}
392 v.Add("chat_id", strconv.Itoa(config.ChatID))
393 v.Add("photo", config.FileID)
394 if config.Caption != "" {
395 v.Add("caption", config.Caption)
396 }
397 if config.ReplyToMessageID != 0 {
398 v.Add("reply_to_message_id", strconv.Itoa(config.ChatID))
399 }
400 if config.ReplyMarkup != nil {
401 data, err := json.Marshal(config.ReplyMarkup)
402 if err != nil {
403 return Message{}, err
404 }
405
406 v.Add("reply_markup", string(data))
407 }
408
409 resp, err := bot.MakeRequest("SendPhoto", v)
410 if err != nil {
411 return Message{}, err
412 }
413
414 var message Message
415 json.Unmarshal(resp.Result, &message)
416
417 if bot.Debug {
418 log.Printf("SendPhoto req : %+v\n", v)
419 log.Printf("SendPhoto resp: %+v\n", message)
420 }
421
422 return message, nil
423 }
424
425 params := make(map[string]string)
426 params["chat_id"] = strconv.Itoa(config.ChatID)
427 if config.Caption != "" {
428 params["caption"] = config.Caption
429 }
430 if config.ReplyToMessageID != 0 {
431 params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
432 }
433 if config.ReplyMarkup != nil {
434 data, err := json.Marshal(config.ReplyMarkup)
435 if err != nil {
436 return Message{}, err
437 }
438
439 params["reply_markup"] = string(data)
440 }
441
442 var file interface{}
443 if config.FilePath == "" {
444 file = config.File
445 } else {
446 file = config.FilePath
447 }
448
449 resp, err := bot.UploadFile("SendPhoto", params, "photo", file)
450 if err != nil {
451 return Message{}, err
452 }
453
454 var message Message
455 json.Unmarshal(resp.Result, &message)
456
457 if bot.Debug {
458 log.Printf("SendPhoto resp: %+v\n", message)
459 }
460
461 return message, nil
462}
463
464// SendAudio sends or uploads an audio clip to a chat.
465// If using a file, the file must be in the .mp3 format.
466//
467// When the fields title and performer are both empty and
468// the mime-type of the file to be sent is not audio/mpeg,
469// the file must be an .ogg file encoded with OPUS.
470// You may use the tgutils.EncodeAudio func to assist you with this, if needed.
471//
472// Requires ChatID and FileID OR File.
473// ReplyToMessageID and ReplyMarkup are optional.
474// File should be either a string, FileBytes, or FileReader.
475func (bot *BotAPI) SendAudio(config AudioConfig) (Message, error) {
476 if config.UseExistingAudio {
477 v := url.Values{}
478 v.Add("chat_id", strconv.Itoa(config.ChatID))
479 v.Add("audio", config.FileID)
480 if config.ReplyToMessageID != 0 {
481 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
482 }
483 if config.Duration != 0 {
484 v.Add("duration", strconv.Itoa(config.Duration))
485 }
486 if config.ReplyMarkup != nil {
487 data, err := json.Marshal(config.ReplyMarkup)
488 if err != nil {
489 return Message{}, err
490 }
491
492 v.Add("reply_markup", string(data))
493 }
494 if config.Performer != "" {
495 v.Add("performer", config.Performer)
496 }
497 if config.Title != "" {
498 v.Add("title", config.Title)
499 }
500
501 resp, err := bot.MakeRequest("sendAudio", v)
502 if err != nil {
503 return Message{}, err
504 }
505
506 var message Message
507 json.Unmarshal(resp.Result, &message)
508
509 if bot.Debug {
510 log.Printf("sendAudio req : %+v\n", v)
511 log.Printf("sendAudio resp: %+v\n", message)
512 }
513
514 return message, nil
515 }
516
517 params := make(map[string]string)
518
519 params["chat_id"] = strconv.Itoa(config.ChatID)
520 if config.ReplyToMessageID != 0 {
521 params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
522 }
523 if config.Duration != 0 {
524 params["duration"] = strconv.Itoa(config.Duration)
525 }
526 if config.ReplyMarkup != nil {
527 data, err := json.Marshal(config.ReplyMarkup)
528 if err != nil {
529 return Message{}, err
530 }
531
532 params["reply_markup"] = string(data)
533 }
534 if config.Performer != "" {
535 params["performer"] = config.Performer
536 }
537 if config.Title != "" {
538 params["title"] = config.Title
539 }
540
541 var file interface{}
542 if config.FilePath == "" {
543 file = config.File
544 } else {
545 file = config.FilePath
546 }
547
548 resp, err := bot.UploadFile("sendAudio", params, "audio", file)
549 if err != nil {
550 return Message{}, err
551 }
552
553 var message Message
554 json.Unmarshal(resp.Result, &message)
555
556 if bot.Debug {
557 log.Printf("sendAudio resp: %+v\n", message)
558 }
559
560 return message, nil
561}
562
563// SendDocument sends or uploads a document to a chat.
564//
565// Requires ChatID and FileID OR File.
566// ReplyToMessageID and ReplyMarkup are optional.
567// File should be either a string, FileBytes, or FileReader.
568func (bot *BotAPI) SendDocument(config DocumentConfig) (Message, error) {
569 if config.UseExistingDocument {
570 v := url.Values{}
571 v.Add("chat_id", strconv.Itoa(config.ChatID))
572 v.Add("document", config.FileID)
573 if config.ReplyToMessageID != 0 {
574 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
575 }
576 if config.ReplyMarkup != nil {
577 data, err := json.Marshal(config.ReplyMarkup)
578 if err != nil {
579 return Message{}, err
580 }
581
582 v.Add("reply_markup", string(data))
583 }
584
585 resp, err := bot.MakeRequest("sendDocument", v)
586 if err != nil {
587 return Message{}, err
588 }
589
590 var message Message
591 json.Unmarshal(resp.Result, &message)
592
593 if bot.Debug {
594 log.Printf("sendDocument req : %+v\n", v)
595 log.Printf("sendDocument resp: %+v\n", message)
596 }
597
598 return message, nil
599 }
600
601 params := make(map[string]string)
602
603 params["chat_id"] = strconv.Itoa(config.ChatID)
604 if config.ReplyToMessageID != 0 {
605 params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
606 }
607 if config.ReplyMarkup != nil {
608 data, err := json.Marshal(config.ReplyMarkup)
609 if err != nil {
610 return Message{}, err
611 }
612
613 params["reply_markup"] = string(data)
614 }
615
616 var file interface{}
617 if config.FilePath == "" {
618 file = config.File
619 } else {
620 file = config.FilePath
621 }
622
623 resp, err := bot.UploadFile("sendDocument", params, "document", file)
624 if err != nil {
625 return Message{}, err
626 }
627
628 var message Message
629 json.Unmarshal(resp.Result, &message)
630
631 if bot.Debug {
632 log.Printf("sendDocument resp: %+v\n", message)
633 }
634
635 return message, nil
636}
637
638// SendVoice sends or uploads a playable voice to a chat.
639// If using a file, the file must be encoded as an .ogg with OPUS.
640// You may use the tgutils.EncodeAudio func to assist you with this, if needed.
641//
642// Requires ChatID and FileID OR File.
643// ReplyToMessageID and ReplyMarkup are optional.
644// File should be either a string, FileBytes, or FileReader.
645func (bot *BotAPI) SendVoice(config VoiceConfig) (Message, error) {
646 if config.UseExistingVoice {
647 v := url.Values{}
648 v.Add("chat_id", strconv.Itoa(config.ChatID))
649 v.Add("voice", config.FileID)
650 if config.ReplyToMessageID != 0 {
651 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
652 }
653 if config.Duration != 0 {
654 v.Add("duration", strconv.Itoa(config.Duration))
655 }
656 if config.ReplyMarkup != nil {
657 data, err := json.Marshal(config.ReplyMarkup)
658 if err != nil {
659 return Message{}, err
660 }
661
662 v.Add("reply_markup", string(data))
663 }
664
665 resp, err := bot.MakeRequest("sendVoice", v)
666 if err != nil {
667 return Message{}, err
668 }
669
670 var message Message
671 json.Unmarshal(resp.Result, &message)
672
673 if bot.Debug {
674 log.Printf("SendVoice req : %+v\n", v)
675 log.Printf("SendVoice resp: %+v\n", message)
676 }
677
678 return message, nil
679 }
680
681 params := make(map[string]string)
682
683 params["chat_id"] = strconv.Itoa(config.ChatID)
684 if config.ReplyToMessageID != 0 {
685 params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
686 }
687 if config.Duration != 0 {
688 params["duration"] = strconv.Itoa(config.Duration)
689 }
690 if config.ReplyMarkup != nil {
691 data, err := json.Marshal(config.ReplyMarkup)
692 if err != nil {
693 return Message{}, err
694 }
695
696 params["reply_markup"] = string(data)
697 }
698
699 var file interface{}
700 if config.FilePath == "" {
701 file = config.File
702 } else {
703 file = config.FilePath
704 }
705
706 resp, err := bot.UploadFile("SendVoice", params, "voice", file)
707 if err != nil {
708 return Message{}, err
709 }
710
711 var message Message
712 json.Unmarshal(resp.Result, &message)
713
714 if bot.Debug {
715 log.Printf("SendVoice resp: %+v\n", message)
716 }
717
718 return message, nil
719}
720
721// SendSticker sends or uploads a sticker to a chat.
722//
723// Requires ChatID and FileID OR File.
724// ReplyToMessageID and ReplyMarkup are optional.
725// File should be either a string, FileBytes, or FileReader.
726func (bot *BotAPI) SendSticker(config StickerConfig) (Message, error) {
727 if config.UseExistingSticker {
728 v := url.Values{}
729 v.Add("chat_id", strconv.Itoa(config.ChatID))
730 v.Add("sticker", config.FileID)
731 if config.ReplyToMessageID != 0 {
732 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
733 }
734 if config.ReplyMarkup != nil {
735 data, err := json.Marshal(config.ReplyMarkup)
736 if err != nil {
737 return Message{}, err
738 }
739
740 v.Add("reply_markup", string(data))
741 }
742
743 resp, err := bot.MakeRequest("sendSticker", v)
744 if err != nil {
745 return Message{}, err
746 }
747
748 var message Message
749 json.Unmarshal(resp.Result, &message)
750
751 if bot.Debug {
752 log.Printf("sendSticker req : %+v\n", v)
753 log.Printf("sendSticker resp: %+v\n", message)
754 }
755
756 return message, nil
757 }
758
759 params := make(map[string]string)
760
761 params["chat_id"] = strconv.Itoa(config.ChatID)
762 if config.ReplyToMessageID != 0 {
763 params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
764 }
765 if config.ReplyMarkup != nil {
766 data, err := json.Marshal(config.ReplyMarkup)
767 if err != nil {
768 return Message{}, err
769 }
770
771 params["reply_markup"] = string(data)
772 }
773
774 var file interface{}
775 if config.FilePath == "" {
776 file = config.File
777 } else {
778 file = config.FilePath
779 }
780
781 resp, err := bot.UploadFile("sendSticker", params, "sticker", file)
782 if err != nil {
783 return Message{}, err
784 }
785
786 var message Message
787 json.Unmarshal(resp.Result, &message)
788
789 if bot.Debug {
790 log.Printf("sendSticker resp: %+v\n", message)
791 }
792
793 return message, nil
794}
795
796// SendVideo sends or uploads a video to a chat.
797//
798// Requires ChatID and FileID OR File.
799// ReplyToMessageID and ReplyMarkup are optional.
800// File should be either a string, FileBytes, or FileReader.
801func (bot *BotAPI) SendVideo(config VideoConfig) (Message, error) {
802 if config.UseExistingVideo {
803 v := url.Values{}
804 v.Add("chat_id", strconv.Itoa(config.ChatID))
805 v.Add("video", config.FileID)
806 if config.ReplyToMessageID != 0 {
807 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
808 }
809 if config.Duration != 0 {
810 v.Add("duration", strconv.Itoa(config.Duration))
811 }
812 if config.Caption != "" {
813 v.Add("caption", config.Caption)
814 }
815 if config.ReplyMarkup != nil {
816 data, err := json.Marshal(config.ReplyMarkup)
817 if err != nil {
818 return Message{}, err
819 }
820
821 v.Add("reply_markup", string(data))
822 }
823
824 resp, err := bot.MakeRequest("sendVideo", v)
825 if err != nil {
826 return Message{}, err
827 }
828
829 var message Message
830 json.Unmarshal(resp.Result, &message)
831
832 if bot.Debug {
833 log.Printf("sendVideo req : %+v\n", v)
834 log.Printf("sendVideo resp: %+v\n", message)
835 }
836
837 return message, nil
838 }
839
840 params := make(map[string]string)
841
842 params["chat_id"] = strconv.Itoa(config.ChatID)
843 if config.ReplyToMessageID != 0 {
844 params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
845 }
846 if config.ReplyMarkup != nil {
847 data, err := json.Marshal(config.ReplyMarkup)
848 if err != nil {
849 return Message{}, err
850 }
851
852 params["reply_markup"] = string(data)
853 }
854
855 var file interface{}
856 if config.FilePath == "" {
857 file = config.File
858 } else {
859 file = config.FilePath
860 }
861
862 resp, err := bot.UploadFile("sendVideo", params, "video", file)
863 if err != nil {
864 return Message{}, err
865 }
866
867 var message Message
868 json.Unmarshal(resp.Result, &message)
869
870 if bot.Debug {
871 log.Printf("sendVideo resp: %+v\n", message)
872 }
873
874 return message, nil
875}
876
877// SendLocation sends a location to a chat.
878//
879// Requires ChatID, Latitude, and Longitude.
880// ReplyToMessageID and ReplyMarkup are optional.
881func (bot *BotAPI) SendLocation(config LocationConfig) (Message, error) {
882 v := url.Values{}
883 v.Add("chat_id", strconv.Itoa(config.ChatID))
884 v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64))
885 v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64))
886 if config.ReplyToMessageID != 0 {
887 v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
888 }
889 if config.ReplyMarkup != nil {
890 data, err := json.Marshal(config.ReplyMarkup)
891 if err != nil {
892 return Message{}, err
893 }
894
895 v.Add("reply_markup", string(data))
896 }
897
898 resp, err := bot.MakeRequest("sendLocation", v)
899 if err != nil {
900 return Message{}, err
901 }
902
903 var message Message
904 json.Unmarshal(resp.Result, &message)
905
906 if bot.Debug {
907 log.Printf("sendLocation req : %+v\n", v)
908 log.Printf("sendLocation resp: %+v\n", message)
909 }
910
911 return message, nil
912}
913
914// SendChatAction sets a current action in a chat.
915//
916// Requires ChatID and a valid Action (see Chat constants).
917func (bot *BotAPI) SendChatAction(config ChatActionConfig) error {
918 v := url.Values{}
919 v.Add("chat_id", strconv.Itoa(config.ChatID))
920 v.Add("action", config.Action)
921
922 _, err := bot.MakeRequest("sendChatAction", v)
923 if err != nil {
924 return err
925 }
926
927 return nil
928}
929
930// GetUserProfilePhotos gets a user's profile photos.
931//
932// Requires UserID.
933// Offset and Limit are optional.
934func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
935 v := url.Values{}
936 v.Add("user_id", strconv.Itoa(config.UserID))
937 if config.Offset != 0 {
938 v.Add("offset", strconv.Itoa(config.Offset))
939 }
940 if config.Limit != 0 {
941 v.Add("limit", strconv.Itoa(config.Limit))
942 }
943
944 resp, err := bot.MakeRequest("getUserProfilePhotos", v)
945 if err != nil {
946 return UserProfilePhotos{}, err
947 }
948
949 var profilePhotos UserProfilePhotos
950 json.Unmarshal(resp.Result, &profilePhotos)
951
952 if bot.Debug {
953 log.Printf("getUserProfilePhotos req : %+v\n", v)
954 log.Printf("getUserProfilePhotos resp: %+v\n", profilePhotos)
955 }
956
957 return profilePhotos, nil
958}
959
960// GetFile returns a file_id required to download a file.
961//
962// Requires FileID.
963func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
964 v := url.Values{}
965 v.Add("file_id", config.FileID)
966
967 resp, err := bot.MakeRequest("getFile", v)
968 if err != nil {
969 return File{}, err
970 }
971
972 var file File
973 json.Unmarshal(resp.Result, &file)
974
975 if bot.Debug {
976 log.Printf("getFile req : %+v\n", v)
977 log.Printf("getFile resp: %+v\n", file)
978 }
979
980 return file, nil
981}
982
983// GetUpdates fetches updates.
984// If a WebHook is set, this will not return any data!
985//
986// Offset, Limit, and Timeout are optional.
987// To not get old items, set Offset to one higher than the previous item.
988// Set Timeout to a large number to reduce requests and get responses instantly.
989func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
990 v := url.Values{}
991 if config.Offset > 0 {
992 v.Add("offset", strconv.Itoa(config.Offset))
993 }
994 if config.Limit > 0 {
995 v.Add("limit", strconv.Itoa(config.Limit))
996 }
997 if config.Timeout > 0 {
998 v.Add("timeout", strconv.Itoa(config.Timeout))
999 }
1000
1001 resp, err := bot.MakeRequest("getUpdates", v)
1002 if err != nil {
1003 return []Update{}, err
1004 }
1005
1006 var updates []Update
1007 json.Unmarshal(resp.Result, &updates)
1008
1009 if bot.Debug {
1010 log.Printf("getUpdates: %+v\n", updates)
1011 }
1012
1013 return updates, nil
1014}
1015
1016// SetWebhook sets a webhook.
1017// If this is set, GetUpdates will not get any data!
1018//
1019// Requires Url OR to set Clear to true.
1020func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
1021 if config.Certificate == nil {
1022 v := url.Values{}
1023 if !config.Clear {
1024 v.Add("url", config.URL.String())
1025 }
1026
1027 return bot.MakeRequest("setWebhook", v)
1028 }
1029
1030 params := make(map[string]string)
1031 params["url"] = config.URL.String()
1032
1033 resp, err := bot.UploadFile("setWebhook", params, "certificate", config.Certificate)
1034 if err != nil {
1035 return APIResponse{}, err
1036 }
1037
1038 var apiResp APIResponse
1039 json.Unmarshal(resp.Result, &apiResp)
1040
1041 if bot.Debug {
1042 log.Printf("setWebhook resp: %+v\n", apiResp)
1043 }
1044
1045 return apiResp, nil
1046}