all repos — telegram-bot-api @ ae7c1b4e22f9071c5314a3d0e4c97df4b2743c32

Golang bindings for the Telegram Bot API

methods.go (view raw)

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