all repos — telegram-bot-api @ 86e4fadcb08f7b67df0f7913f06ee13c59dd67b7

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