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