all repos — telegram-bot-api @ 3940cb5953c71f8c558bb19752ec374785fccf87

Golang bindings for the Telegram Bot API

methods.go (view raw)

  1package main
  2
  3import (
  4	"bytes"
  5	"encoding/json"
  6	"errors"
  7	"io"
  8	"io/ioutil"
  9	"log"
 10	"mime/multipart"
 11	"net/http"
 12	"net/url"
 13	"os"
 14	"strconv"
 15)
 16
 17const (
 18	CHAT_TYPING          = "typing"
 19	CHAT_UPLOAD_PHOTO    = "upload_photo"
 20	CHAT_RECORD_VIDEO    = "record_video"
 21	CHAT_UPLOAD_VIDEO    = "upload_video"
 22	CHAT_RECORD_AUDIO    = "record_audio"
 23	CHAT_UPLOAD_AUDIO    = "upload_audio"
 24	CHAT_UPLOAD_DOCUMENT = "upload_document"
 25	CHAT_FIND_LOCATION   = "find_location"
 26)
 27
 28type BotConfig struct {
 29	token string
 30	debug bool
 31}
 32
 33type BotApi struct {
 34	config BotConfig
 35}
 36
 37func NewBotApi(config BotConfig) *BotApi {
 38	return &BotApi{
 39		config: config,
 40	}
 41}
 42
 43func (bot *BotApi) makeRequest(endpoint string, params url.Values) (ApiResponse, error) {
 44	resp, err := http.PostForm("https://api.telegram.org/bot"+bot.config.token+"/"+endpoint, params)
 45	defer resp.Body.Close()
 46	if err != nil {
 47		return ApiResponse{}, err
 48	}
 49
 50	bytes, err := ioutil.ReadAll(resp.Body)
 51	if err != nil {
 52		return ApiResponse{}, err
 53	}
 54
 55	if bot.config.debug {
 56		log.Println(endpoint, string(bytes))
 57	}
 58
 59	var apiResp ApiResponse
 60	json.Unmarshal(bytes, &apiResp)
 61
 62	if !apiResp.Ok {
 63		return ApiResponse{}, errors.New(apiResp.Description)
 64	}
 65
 66	return apiResp, nil
 67}
 68
 69func (bot *BotApi) uploadFile(endpoint string, params map[string]string, fieldname string, filename string) (ApiResponse, error) {
 70	var b bytes.Buffer
 71	w := multipart.NewWriter(&b)
 72
 73	f, err := os.Open(filename)
 74	if err != nil {
 75		return ApiResponse{}, err
 76	}
 77
 78	fw, err := w.CreateFormFile(fieldname, filename)
 79	if err != nil {
 80		return ApiResponse{}, err
 81	}
 82
 83	if _, err = io.Copy(fw, f); err != nil {
 84		return ApiResponse{}, err
 85	}
 86
 87	for key, val := range params {
 88		if fw, err = w.CreateFormField(key); err != nil {
 89			return ApiResponse{}, err
 90		}
 91
 92		if _, err = fw.Write([]byte(val)); err != nil {
 93			return ApiResponse{}, err
 94		}
 95	}
 96
 97	w.Close()
 98
 99	req, err := http.NewRequest("POST", "https://api.telegram.org/bot"+bot.config.token+"/"+endpoint, &b)
100	if err != nil {
101		return ApiResponse{}, err
102	}
103
104	req.Header.Set("Content-Type", w.FormDataContentType())
105
106	client := &http.Client{}
107	res, err := client.Do(req)
108	if err != nil {
109		return ApiResponse{}, err
110	}
111
112	bytes, err := ioutil.ReadAll(res.Body)
113	if err != nil {
114		return ApiResponse{}, err
115	}
116
117	if bot.config.debug {
118		log.Println(string(bytes[:]))
119	}
120
121	var apiResp ApiResponse
122	json.Unmarshal(bytes, &apiResp)
123
124	return apiResp, nil
125}
126
127func (bot *BotApi) getMe() (User, error) {
128	resp, err := bot.makeRequest("getMe", nil)
129	if err != nil {
130		return User{}, err
131	}
132
133	var user User
134	json.Unmarshal(resp.Result, &user)
135
136	if bot.config.debug {
137		log.Printf("getMe: %+v\n", user)
138	}
139
140	return user, nil
141}
142
143func (bot *BotApi) sendMessage(config MessageConfig) (Message, error) {
144	v := url.Values{}
145	v.Add("chat_id", strconv.Itoa(config.ChatId))
146	v.Add("text", config.Text)
147	v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview))
148	if config.ReplyToMessageId != 0 {
149		v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
150	}
151	if config.ReplyMarkup != nil {
152		data, err := json.Marshal(config.ReplyMarkup)
153		if err != nil {
154			return Message{}, err
155		}
156
157		v.Add("reply_markup", string(data))
158	}
159
160	resp, err := bot.makeRequest("sendMessage", v)
161	if err != nil {
162		return Message{}, err
163	}
164
165	var message Message
166	json.Unmarshal(resp.Result, &message)
167
168	if bot.config.debug {
169		log.Printf("sendMessage req : %+v\n", v)
170		log.Printf("sendMessage resp: %+v\n", message)
171	}
172
173	return message, nil
174}
175
176func (bot *BotApi) forwardMessage(config ForwardConfig) (Message, error) {
177	v := url.Values{}
178	v.Add("chat_id", strconv.Itoa(config.ChatId))
179	v.Add("from_chat_id", strconv.Itoa(config.FromChatId))
180	v.Add("message_id", strconv.Itoa(config.MessageId))
181
182	resp, err := bot.makeRequest("forwardMessage", v)
183	if err != nil {
184		return Message{}, err
185	}
186
187	var message Message
188	json.Unmarshal(resp.Result, &message)
189
190	if bot.config.debug {
191		log.Printf("forwardMessage req : %+v\n", v)
192		log.Printf("forwardMessage resp: %+v\n", message)
193	}
194
195	return message, nil
196}
197
198func (bot *BotApi) sendPhoto(config PhotoConfig) (Message, error) {
199	if config.UseExistingPhoto {
200		v := url.Values{}
201		v.Add("chat_id", strconv.Itoa(config.ChatId))
202		v.Add("photo", config.FileId)
203		if config.Caption != "" {
204			v.Add("caption", config.Caption)
205		}
206		if config.ReplyToMessageId != 0 {
207			v.Add("reply_to_message_id", strconv.Itoa(config.ChatId))
208		}
209		if config.ReplyMarkup != nil {
210			data, err := json.Marshal(config.ReplyMarkup)
211			if err != nil {
212				return Message{}, err
213			}
214
215			v.Add("reply_markup", string(data))
216		}
217
218		resp, err := bot.makeRequest("sendPhoto", v)
219		if err != nil {
220			return Message{}, err
221		}
222
223		var message Message
224		json.Unmarshal(resp.Result, &message)
225
226		if bot.config.debug {
227			log.Printf("sendPhoto req : %+v\n", v)
228			log.Printf("sendPhoto resp: %+v\n", message)
229		}
230
231		return message, nil
232	}
233
234	params := make(map[string]string)
235	params["chat_id"] = strconv.Itoa(config.ChatId)
236	if config.Caption != "" {
237		params["caption"] = config.Caption
238	}
239	if config.ReplyToMessageId != 0 {
240		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageId)
241	}
242	if config.ReplyMarkup != nil {
243		data, err := json.Marshal(config.ReplyMarkup)
244		if err != nil {
245			return Message{}, err
246		}
247
248		params["reply_markup"] = string(data)
249	}
250
251	resp, err := bot.uploadFile("sendPhoto", params, "photo", config.FilePath)
252	if err != nil {
253		return Message{}, err
254	}
255
256	var message Message
257	json.Unmarshal(resp.Result, &message)
258
259	if bot.config.debug {
260		log.Printf("sendPhoto resp: %+v\n", message)
261	}
262
263	return message, nil
264}
265
266func (bot *BotApi) sendAudio(config AudioConfig) (Message, error) {
267	if config.UseExistingAudio {
268		v := url.Values{}
269		v.Add("chat_id", strconv.Itoa(config.ChatId))
270		v.Add("audio", config.FileId)
271		if config.ReplyToMessageId != 0 {
272			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
273		}
274		if config.ReplyMarkup != nil {
275			data, err := json.Marshal(config.ReplyMarkup)
276			if err != nil {
277				return Message{}, err
278			}
279
280			v.Add("reply_markup", string(data))
281		}
282
283		resp, err := bot.makeRequest("sendAudio", v)
284		if err != nil {
285			return Message{}, err
286		}
287
288		var message Message
289		json.Unmarshal(resp.Result, &message)
290
291		if bot.config.debug {
292			log.Printf("sendAudio req : %+v\n", v)
293			log.Printf("sendAudio resp: %+v\n", message)
294		}
295
296		return message, nil
297	}
298
299	params := make(map[string]string)
300
301	params["chat_id"] = strconv.Itoa(config.ChatId)
302	if config.ReplyToMessageId != 0 {
303		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageId)
304	}
305	if config.ReplyMarkup != nil {
306		data, err := json.Marshal(config.ReplyMarkup)
307		if err != nil {
308			return Message{}, err
309		}
310
311		params["reply_markup"] = string(data)
312	}
313
314	resp, err := bot.uploadFile("sendAudio", params, "audio", config.FilePath)
315	if err != nil {
316		return Message{}, err
317	}
318
319	var message Message
320	json.Unmarshal(resp.Result, &message)
321
322	if bot.config.debug {
323		log.Printf("sendAudio resp: %+v\n", message)
324	}
325
326	return message, nil
327}
328
329func (bot *BotApi) sendDocument(config DocumentConfig) (Message, error) {
330	if config.UseExistingDocument {
331		v := url.Values{}
332		v.Add("chat_id", strconv.Itoa(config.ChatId))
333		v.Add("document", config.FileId)
334		if config.ReplyToMessageId != 0 {
335			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
336		}
337		if config.ReplyMarkup != nil {
338			data, err := json.Marshal(config.ReplyMarkup)
339			if err != nil {
340				return Message{}, err
341			}
342
343			v.Add("reply_markup", string(data))
344		}
345
346		resp, err := bot.makeRequest("sendDocument", v)
347		if err != nil {
348			return Message{}, err
349		}
350
351		var message Message
352		json.Unmarshal(resp.Result, &message)
353
354		if bot.config.debug {
355			log.Printf("sendDocument req : %+v\n", v)
356			log.Printf("sendDocument resp: %+v\n", message)
357		}
358
359		return message, nil
360	}
361
362	params := make(map[string]string)
363
364	params["chat_id"] = strconv.Itoa(config.ChatId)
365	if config.ReplyToMessageId != 0 {
366		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageId)
367	}
368	if config.ReplyMarkup != nil {
369		data, err := json.Marshal(config.ReplyMarkup)
370		if err != nil {
371			return Message{}, err
372		}
373
374		params["reply_markup"] = string(data)
375	}
376
377	resp, err := bot.uploadFile("sendDocument", params, "document", config.FilePath)
378	if err != nil {
379		return Message{}, err
380	}
381
382	var message Message
383	json.Unmarshal(resp.Result, &message)
384
385	if bot.config.debug {
386		log.Printf("sendDocument resp: %+v\n", message)
387	}
388
389	return message, nil
390}
391
392func (bot *BotApi) sendSticker(config StickerConfig) (Message, error) {
393	if config.UseExistingSticker {
394		v := url.Values{}
395		v.Add("chat_id", strconv.Itoa(config.ChatId))
396		v.Add("sticker", config.FileId)
397		if config.ReplyToMessageId != 0 {
398			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
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("sendSticker", 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.config.debug {
418			log.Printf("sendSticker req : %+v\n", v)
419			log.Printf("sendSticker resp: %+v\n", message)
420		}
421
422		return message, nil
423	}
424
425	params := make(map[string]string)
426
427	params["chat_id"] = strconv.Itoa(config.ChatId)
428	if config.ReplyToMessageId != 0 {
429		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageId)
430	}
431	if config.ReplyMarkup != nil {
432		data, err := json.Marshal(config.ReplyMarkup)
433		if err != nil {
434			return Message{}, err
435		}
436
437		params["reply_markup"] = string(data)
438	}
439
440	resp, err := bot.uploadFile("sendSticker", params, "sticker", config.FilePath)
441	if err != nil {
442		return Message{}, err
443	}
444
445	var message Message
446	json.Unmarshal(resp.Result, &message)
447
448	if bot.config.debug {
449		log.Printf("sendSticker resp: %+v\n", message)
450	}
451
452	return message, nil
453}
454
455func (bot *BotApi) sendVideo(config VideoConfig) (Message, error) {
456	if config.UseExistingVideo {
457		v := url.Values{}
458		v.Add("chat_id", strconv.Itoa(config.ChatId))
459		v.Add("video", config.FileId)
460		if config.ReplyToMessageId != 0 {
461			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
462		}
463		if config.ReplyMarkup != nil {
464			data, err := json.Marshal(config.ReplyMarkup)
465			if err != nil {
466				return Message{}, err
467			}
468
469			v.Add("reply_markup", string(data))
470		}
471
472		resp, err := bot.makeRequest("sendVideo", v)
473		if err != nil {
474			return Message{}, err
475		}
476
477		var message Message
478		json.Unmarshal(resp.Result, &message)
479
480		if bot.config.debug {
481			log.Printf("sendVideo req : %+v\n", v)
482			log.Printf("sendVideo resp: %+v\n", message)
483		}
484
485		return message, nil
486	}
487
488	params := make(map[string]string)
489
490	params["chat_id"] = strconv.Itoa(config.ChatId)
491	if config.ReplyToMessageId != 0 {
492		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageId)
493	}
494	if config.ReplyMarkup != nil {
495		data, err := json.Marshal(config.ReplyMarkup)
496		if err != nil {
497			return Message{}, err
498		}
499
500		params["reply_markup"] = string(data)
501	}
502
503	resp, err := bot.uploadFile("sendVideo", params, "video", config.FilePath)
504	if err != nil {
505		return Message{}, err
506	}
507
508	var message Message
509	json.Unmarshal(resp.Result, &message)
510
511	if bot.config.debug {
512		log.Printf("sendVideo resp: %+v\n", message)
513	}
514
515	return message, nil
516}
517
518func (bot *BotApi) sendLocation(config LocationConfig) (Message, error) {
519	v := url.Values{}
520	v.Add("chat_id", strconv.Itoa(config.ChatId))
521	v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64))
522	v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64))
523	if config.ReplyToMessageId != 0 {
524		v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
525	}
526	if config.ReplyMarkup != nil {
527		data, err := json.Marshal(config.ReplyMarkup)
528		if err != nil {
529			return Message{}, err
530		}
531
532		v.Add("reply_markup", string(data))
533	}
534
535	resp, err := bot.makeRequest("sendLocation", v)
536	if err != nil {
537		return Message{}, err
538	}
539
540	var message Message
541	json.Unmarshal(resp.Result, &message)
542
543	if bot.config.debug {
544		log.Printf("sendLocation req : %+v\n", v)
545		log.Printf("sendLocation resp: %+v\n", message)
546	}
547
548	return message, nil
549}
550
551func (bot *BotApi) sendChatAction(config ChatActionConfig) error {
552	v := url.Values{}
553	v.Add("chat_id", strconv.Itoa(config.ChatId))
554	v.Add("action", config.Action)
555
556	_, err := bot.makeRequest("sendChatAction", v)
557	if err != nil {
558		return err
559	}
560
561	return nil
562}
563
564func (bot *BotApi) getUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
565	v := url.Values{}
566	v.Add("user_id", strconv.Itoa(config.UserId))
567	if config.Offset != 0 {
568		v.Add("offset", strconv.Itoa(config.Offset))
569	}
570	if config.Limit != 0 {
571		v.Add("limit", strconv.Itoa(config.Limit))
572	}
573
574	resp, err := bot.makeRequest("getUserProfilePhotos", v)
575	if err != nil {
576		return UserProfilePhotos{}, err
577	}
578
579	var profilePhotos UserProfilePhotos
580	json.Unmarshal(resp.Result, &profilePhotos)
581
582	if bot.config.debug {
583		log.Printf("getUserProfilePhotos req : %+v\n", v)
584		log.Printf("getUserProfilePhotos resp: %+v\n", profilePhotos)
585	}
586
587	return profilePhotos, nil
588}
589
590func (bot *BotApi) getUpdates(config UpdateConfig) ([]Update, error) {
591	v := url.Values{}
592	if config.Offset > 0 {
593		v.Add("offset", strconv.Itoa(config.Offset))
594	}
595	if config.Limit > 0 {
596		v.Add("limit", strconv.Itoa(config.Limit))
597	}
598	if config.Timeout > 0 {
599		v.Add("timeout", strconv.Itoa(config.Timeout))
600	}
601
602	resp, err := bot.makeRequest("getUpdates", v)
603	if err != nil {
604		return []Update{}, err
605	}
606
607	var updates []Update
608	json.Unmarshal(resp.Result, &updates)
609
610	if bot.config.debug {
611		log.Printf("getUpdates: %+v\n", updates)
612	}
613
614	return updates, nil
615}
616
617func (bot *BotApi) setWebhook(v url.Values) error {
618	_, err := bot.makeRequest("setWebhook", v)
619
620	return err
621}
622
623type UpdateConfig struct {
624	Offset  int
625	Limit   int
626	Timeout int
627}
628
629type MessageConfig struct {
630	ChatId                int
631	Text                  string
632	DisableWebPagePreview bool
633	ReplyToMessageId      int
634	ReplyMarkup           interface{}
635}
636
637type ForwardConfig struct {
638	ChatId     int
639	FromChatId int
640	MessageId  int
641}
642
643type PhotoConfig struct {
644	ChatId           int
645	Caption          string
646	ReplyToMessageId int
647	ReplyMarkup      interface{}
648	UseExistingPhoto bool
649	FilePath         string
650	FileId           string
651}
652
653type AudioConfig struct {
654	ChatId           int
655	ReplyToMessageId int
656	ReplyMarkup      interface{}
657	UseExistingAudio bool
658	FilePath         string
659	FileId           string
660}
661
662type DocumentConfig struct {
663	ChatId              int
664	ReplyToMessageId    int
665	ReplyMarkup         interface{}
666	UseExistingDocument bool
667	FilePath            string
668	FileId              string
669}
670
671type StickerConfig struct {
672	ChatId             int
673	ReplyToMessageId   int
674	ReplyMarkup        interface{}
675	UseExistingSticker bool
676	FilePath           string
677	FileId             string
678}
679
680type VideoConfig struct {
681	ChatId           int
682	ReplyToMessageId int
683	ReplyMarkup      interface{}
684	UseExistingVideo bool
685	FilePath         string
686	FileId           string
687}
688
689type LocationConfig struct {
690	ChatId           int
691	Latitude         float64
692	Longitude        float64
693	ReplyToMessageId int
694	ReplyMarkup      interface{}
695}
696
697type ChatActionConfig struct {
698	ChatId int
699	Action string
700}
701
702type UserProfilePhotosConfig struct {
703	UserId int
704	Offset int
705	Limit  int
706}
707
708func NewMessage(chatId int, text string) MessageConfig {
709	return MessageConfig{
710		ChatId: chatId,
711		Text:   text,
712		DisableWebPagePreview: false,
713		ReplyToMessageId:      0,
714	}
715}
716
717func NewForward(chatId int, fromChatId int, messageId int) ForwardConfig {
718	return ForwardConfig{
719		ChatId:     chatId,
720		FromChatId: fromChatId,
721		MessageId:  messageId,
722	}
723}
724
725func NewPhotoUpload(chatId int, filename string) PhotoConfig {
726	return PhotoConfig{
727		ChatId:           chatId,
728		UseExistingPhoto: false,
729		FilePath:         filename,
730	}
731}
732
733func NewPhotoShare(chatId int, fileId string) PhotoConfig {
734	return PhotoConfig{
735		ChatId:           chatId,
736		UseExistingPhoto: true,
737		FileId:           fileId,
738	}
739}
740
741func NewAudioUpload(chatId int, filename string) AudioConfig {
742	return AudioConfig{
743		ChatId:           chatId,
744		UseExistingAudio: false,
745		FilePath:         filename,
746	}
747}
748
749func NewAudioShare(chatId int, fileId string) AudioConfig {
750	return AudioConfig{
751		ChatId:           chatId,
752		UseExistingAudio: true,
753		FileId:           fileId,
754	}
755}
756
757func NewDocumentUpload(chatId int, filename string) DocumentConfig {
758	return DocumentConfig{
759		ChatId:              chatId,
760		UseExistingDocument: false,
761		FilePath:            filename,
762	}
763}
764
765func NewDocumentShare(chatId int, fileId string) DocumentConfig {
766	return DocumentConfig{
767		ChatId:              chatId,
768		UseExistingDocument: true,
769		FileId:              fileId,
770	}
771}
772
773func NewStickerUpload(chatId int, filename string) StickerConfig {
774	return StickerConfig{
775		ChatId:             chatId,
776		UseExistingSticker: false,
777		FilePath:           filename,
778	}
779}
780
781func NewStickerShare(chatId int, fileId string) StickerConfig {
782	return StickerConfig{
783		ChatId:             chatId,
784		UseExistingSticker: true,
785		FileId:             fileId,
786	}
787}
788
789func NewVideoUpload(chatId int, filename string) VideoConfig {
790	return VideoConfig{
791		ChatId:           chatId,
792		UseExistingVideo: false,
793		FilePath:         filename,
794	}
795}
796
797func NewVideoShare(chatId int, fileId string) VideoConfig {
798	return VideoConfig{
799		ChatId:           chatId,
800		UseExistingVideo: true,
801		FileId:           fileId,
802	}
803}
804
805func NewLocation(chatId int, latitude float64, longitude float64) LocationConfig {
806	return LocationConfig{
807		ChatId:           chatId,
808		Latitude:         latitude,
809		Longitude:        longitude,
810		ReplyToMessageId: 0,
811		ReplyMarkup:      nil,
812	}
813}
814
815func NewChatAction(chatId int, action string) ChatActionConfig {
816	return ChatActionConfig{
817		ChatId: chatId,
818		Action: action,
819	}
820}
821
822func NewUserProfilePhotos(userId int) UserProfilePhotosConfig {
823	return UserProfilePhotosConfig{
824		UserId: userId,
825		Offset: 0,
826		Limit:  0,
827	}
828}
829
830func NewUpdate(offset int) UpdateConfig {
831	return UpdateConfig{
832		Offset:  offset,
833		Limit:   0,
834		Timeout: 0,
835	}
836}