all repos — telegram-bot-api @ 567a37868d87cbd50149f484f26cefd2859ecbfd

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
152	resp, err := bot.makeRequest("sendMessage", v)
153	if err != nil {
154		return Message{}, err
155	}
156
157	var message Message
158	json.Unmarshal(resp.Result, &message)
159
160	if bot.config.debug {
161		log.Printf("sendMessage req : %+v\n", v)
162		log.Printf("sendMessage resp: %+v\n", message)
163	}
164
165	return message, nil
166}
167
168func (bot *BotApi) forwardMessage(config ForwardConfig) (Message, error) {
169	v := url.Values{}
170	v.Add("chat_id", strconv.Itoa(config.ChatId))
171	v.Add("from_chat_id", strconv.Itoa(config.FromChatId))
172	v.Add("message_id", strconv.Itoa(config.MessageId))
173
174	resp, err := bot.makeRequest("forwardMessage", v)
175	if err != nil {
176		return Message{}, err
177	}
178
179	var message Message
180	json.Unmarshal(resp.Result, &message)
181
182	if bot.config.debug {
183		log.Printf("forwardMessage req : %+v\n", v)
184		log.Printf("forwardMessage resp: %+v\n", message)
185	}
186
187	return message, nil
188}
189
190func (bot *BotApi) sendPhoto(config PhotoConfig) (Message, error) {
191	if config.UseExistingPhoto {
192		v := url.Values{}
193		v.Add("chat_id", strconv.Itoa(config.ChatId))
194		v.Add("photo", config.FileId)
195		if config.Caption != "" {
196			v.Add("caption", config.Caption)
197		}
198		if config.ReplyToMessageId != 0 {
199			v.Add("reply_to_message_id", strconv.Itoa(config.ChatId))
200		}
201		if config.ReplyMarkup != nil {
202			data, err := json.Marshal(config.ReplyMarkup)
203			if err != nil {
204				return Message{}, err
205			}
206
207			v.Add("reply_markup", string(data))
208		}
209
210		resp, err := bot.makeRequest("sendPhoto", v)
211		if err != nil {
212			return Message{}, err
213		}
214
215		var message Message
216		json.Unmarshal(resp.Result, &message)
217
218		if bot.config.debug {
219			log.Printf("sendPhoto req : %+v\n", v)
220			log.Printf("sendPhoto resp: %+v\n", message)
221		}
222
223		return message, nil
224	}
225
226	params := make(map[string]string)
227	params["chat_id"] = strconv.Itoa(config.ChatId)
228	if config.Caption != "" {
229		params["caption"] = config.Caption
230	}
231	if config.ReplyToMessageId != 0 {
232		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageId)
233	}
234	if config.ReplyMarkup != nil {
235		data, err := json.Marshal(config.ReplyMarkup)
236		if err != nil {
237			return Message{}, err
238		}
239
240		params["reply_markup"] = string(data)
241	}
242
243	resp, err := bot.uploadFile("sendPhoto", params, "photo", config.FilePath)
244	if err != nil {
245		return Message{}, err
246	}
247
248	var message Message
249	json.Unmarshal(resp.Result, &message)
250
251	if bot.config.debug {
252		log.Printf("sendPhoto resp: %+v\n", message)
253	}
254
255	return message, nil
256}
257
258func (bot *BotApi) sendAudio(config AudioConfig) (Message, error) {
259	if config.UseExistingAudio {
260		v := url.Values{}
261		v.Add("chat_id", strconv.Itoa(config.ChatId))
262		v.Add("audio", config.FileId)
263		if config.ReplyToMessageId != 0 {
264			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
265		}
266		if config.ReplyMarkup != nil {
267			data, err := json.Marshal(config.ReplyMarkup)
268			if err != nil {
269				return Message{}, err
270			}
271
272			v.Add("reply_markup", string(data))
273		}
274
275		resp, err := bot.makeRequest("sendAudio", v)
276		if err != nil {
277			return Message{}, err
278		}
279
280		var message Message
281		json.Unmarshal(resp.Result, &message)
282
283		if bot.config.debug {
284			log.Printf("sendAudio req : %+v\n", v)
285			log.Printf("sendAudio resp: %+v\n", message)
286		}
287
288		return message, nil
289	}
290
291	params := make(map[string]string)
292
293	params["chat_id"] = strconv.Itoa(config.ChatId)
294	if config.ReplyToMessageId != 0 {
295		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageId)
296	}
297	if config.ReplyMarkup != nil {
298		data, err := json.Marshal(config.ReplyMarkup)
299		if err != nil {
300			return Message{}, err
301		}
302
303		params["reply_markup"] = string(data)
304	}
305
306	resp, err := bot.uploadFile("sendAudio", params, "audio", config.FilePath)
307	if err != nil {
308		return Message{}, err
309	}
310
311	var message Message
312	json.Unmarshal(resp.Result, &message)
313
314	if bot.config.debug {
315		log.Printf("sendAudio resp: %+v\n", message)
316	}
317
318	return message, nil
319}
320
321func (bot *BotApi) sendDocument(config DocumentConfig) (Message, error) {
322	if config.UseExistingDocument {
323		v := url.Values{}
324		v.Add("chat_id", strconv.Itoa(config.ChatId))
325		v.Add("document", config.FileId)
326		if config.ReplyToMessageId != 0 {
327			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
328		}
329		if config.ReplyMarkup != nil {
330			data, err := json.Marshal(config.ReplyMarkup)
331			if err != nil {
332				return Message{}, err
333			}
334
335			v.Add("reply_markup", string(data))
336		}
337
338		resp, err := bot.makeRequest("sendDocument", v)
339		if err != nil {
340			return Message{}, err
341		}
342
343		var message Message
344		json.Unmarshal(resp.Result, &message)
345
346		if bot.config.debug {
347			log.Printf("sendDocument req : %+v\n", v)
348			log.Printf("sendDocument resp: %+v\n", message)
349		}
350
351		return message, nil
352	}
353
354	params := make(map[string]string)
355
356	params["chat_id"] = strconv.Itoa(config.ChatId)
357	if config.ReplyToMessageId != 0 {
358		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageId)
359	}
360	if config.ReplyMarkup != nil {
361		data, err := json.Marshal(config.ReplyMarkup)
362		if err != nil {
363			return Message{}, err
364		}
365
366		params["reply_markup"] = string(data)
367	}
368
369	resp, err := bot.uploadFile("sendDocument", params, "document", config.FilePath)
370	if err != nil {
371		return Message{}, err
372	}
373
374	var message Message
375	json.Unmarshal(resp.Result, &message)
376
377	if bot.config.debug {
378		log.Printf("sendDocument resp: %+v\n", message)
379	}
380
381	return message, nil
382}
383
384func (bot *BotApi) sendSticker(config StickerConfig) (Message, error) {
385	if config.UseExistingSticker {
386		v := url.Values{}
387		v.Add("chat_id", strconv.Itoa(config.ChatId))
388		v.Add("sticker", config.FileId)
389		if config.ReplyToMessageId != 0 {
390			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
391		}
392		if config.ReplyMarkup != nil {
393			data, err := json.Marshal(config.ReplyMarkup)
394			if err != nil {
395				return Message{}, err
396			}
397
398			v.Add("reply_markup", string(data))
399		}
400
401		resp, err := bot.makeRequest("sendSticker", v)
402		if err != nil {
403			return Message{}, err
404		}
405
406		var message Message
407		json.Unmarshal(resp.Result, &message)
408
409		if bot.config.debug {
410			log.Printf("sendSticker req : %+v\n", v)
411			log.Printf("sendSticker resp: %+v\n", message)
412		}
413
414		return message, nil
415	}
416
417	params := make(map[string]string)
418
419	params["chat_id"] = strconv.Itoa(config.ChatId)
420	if config.ReplyToMessageId != 0 {
421		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageId)
422	}
423	if config.ReplyMarkup != nil {
424		data, err := json.Marshal(config.ReplyMarkup)
425		if err != nil {
426			return Message{}, err
427		}
428
429		params["reply_markup"] = string(data)
430	}
431
432	resp, err := bot.uploadFile("sendSticker", params, "sticker", config.FilePath)
433	if err != nil {
434		return Message{}, err
435	}
436
437	var message Message
438	json.Unmarshal(resp.Result, &message)
439
440	if bot.config.debug {
441		log.Printf("sendSticker resp: %+v\n", message)
442	}
443
444	return message, nil
445}
446
447func (bot *BotApi) sendVideo(config VideoConfig) (Message, error) {
448	if config.UseExistingVideo {
449		v := url.Values{}
450		v.Add("chat_id", strconv.Itoa(config.ChatId))
451		v.Add("video", config.FileId)
452		if config.ReplyToMessageId != 0 {
453			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
454		}
455		if config.ReplyMarkup != nil {
456			data, err := json.Marshal(config.ReplyMarkup)
457			if err != nil {
458				return Message{}, err
459			}
460
461			v.Add("reply_markup", string(data))
462		}
463
464		resp, err := bot.makeRequest("sendVideo", v)
465		if err != nil {
466			return Message{}, err
467		}
468
469		var message Message
470		json.Unmarshal(resp.Result, &message)
471
472		if bot.config.debug {
473			log.Printf("sendVideo req : %+v\n", v)
474			log.Printf("sendVideo resp: %+v\n", message)
475		}
476
477		return message, nil
478	}
479
480	params := make(map[string]string)
481
482	params["chat_id"] = strconv.Itoa(config.ChatId)
483	if config.ReplyToMessageId != 0 {
484		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageId)
485	}
486	if config.ReplyMarkup != nil {
487		data, err := json.Marshal(config.ReplyMarkup)
488		if err != nil {
489			return Message{}, err
490		}
491
492		params["reply_markup"] = string(data)
493	}
494
495	resp, err := bot.uploadFile("sendVideo", params, "video", config.FilePath)
496	if err != nil {
497		return Message{}, err
498	}
499
500	var message Message
501	json.Unmarshal(resp.Result, &message)
502
503	if bot.config.debug {
504		log.Printf("sendVideo resp: %+v\n", message)
505	}
506
507	return message, nil
508}
509
510func (bot *BotApi) sendLocation(config LocationConfig) (Message, error) {
511	v := url.Values{}
512	v.Add("chat_id", strconv.Itoa(config.ChatId))
513	v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64))
514	v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64))
515	if config.ReplyToMessageId != 0 {
516		v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
517	}
518	if config.ReplyMarkup != nil {
519		data, err := json.Marshal(config.ReplyMarkup)
520		if err != nil {
521			return Message{}, err
522		}
523
524		v.Add("reply_markup", string(data))
525	}
526
527	resp, err := bot.makeRequest("sendLocation", v)
528	if err != nil {
529		return Message{}, err
530	}
531
532	var message Message
533	json.Unmarshal(resp.Result, &message)
534
535	if bot.config.debug {
536		log.Printf("sendLocation req : %+v\n", v)
537		log.Printf("sendLocation resp: %+v\n", message)
538	}
539
540	return message, nil
541}
542
543func (bot *BotApi) sendChatAction(config ChatActionConfig) error {
544	v := url.Values{}
545	v.Add("chat_id", strconv.Itoa(config.ChatId))
546	v.Add("action", config.Action)
547
548	_, err := bot.makeRequest("sendChatAction", v)
549	if err != nil {
550		return err
551	}
552
553	return nil
554}
555
556func (bot *BotApi) getUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
557	v := url.Values{}
558	v.Add("user_id", strconv.Itoa(config.UserId))
559	if config.Offset != 0 {
560		v.Add("offset", strconv.Itoa(config.Offset))
561	}
562	if config.Limit != 0 {
563		v.Add("limit", strconv.Itoa(config.Limit))
564	}
565
566	resp, err := bot.makeRequest("getUserProfilePhotos", v)
567	if err != nil {
568		return UserProfilePhotos{}, err
569	}
570
571	var profilePhotos UserProfilePhotos
572	json.Unmarshal(resp.Result, &profilePhotos)
573
574	if bot.config.debug {
575		log.Printf("getUserProfilePhotos req : %+v\n", v)
576		log.Printf("getUserProfilePhotos resp: %+v\n", profilePhotos)
577	}
578
579	return profilePhotos, nil
580}
581
582func (bot *BotApi) getUpdates(config UpdateConfig) ([]Update, error) {
583	v := url.Values{}
584	if config.Offset > 0 {
585		v.Add("offset", strconv.Itoa(config.Offset))
586	}
587	if config.Limit > 0 {
588		v.Add("limit", strconv.Itoa(config.Limit))
589	}
590	if config.Timeout > 0 {
591		v.Add("timeout", strconv.Itoa(config.Timeout))
592	}
593
594	resp, err := bot.makeRequest("getUpdates", v)
595	if err != nil {
596		return []Update{}, err
597	}
598
599	var updates []Update
600	json.Unmarshal(resp.Result, &updates)
601
602	if bot.config.debug {
603		log.Printf("getUpdates: %+v\n", updates)
604	}
605
606	return updates, nil
607}
608
609func (bot *BotApi) setWebhook(v url.Values) error {
610	_, err := bot.makeRequest("setWebhook", v)
611
612	return err
613}
614
615type UpdateConfig struct {
616	Offset  int
617	Limit   int
618	Timeout int
619}
620
621type MessageConfig struct {
622	ChatId                int
623	Text                  string
624	DisableWebPagePreview bool
625	ReplyToMessageId      int
626}
627
628type ForwardConfig struct {
629	ChatId     int
630	FromChatId int
631	MessageId  int
632}
633
634type PhotoConfig struct {
635	ChatId           int
636	Caption          string
637	ReplyToMessageId int
638	ReplyMarkup      interface{}
639	UseExistingPhoto bool
640	FilePath         string
641	FileId           string
642}
643
644type AudioConfig struct {
645	ChatId           int
646	ReplyToMessageId int
647	ReplyMarkup      interface{}
648	UseExistingAudio bool
649	FilePath         string
650	FileId           string
651}
652
653type DocumentConfig struct {
654	ChatId              int
655	ReplyToMessageId    int
656	ReplyMarkup         interface{}
657	UseExistingDocument bool
658	FilePath            string
659	FileId              string
660}
661
662type StickerConfig struct {
663	ChatId             int
664	ReplyToMessageId   int
665	ReplyMarkup        interface{}
666	UseExistingSticker bool
667	FilePath           string
668	FileId             string
669}
670
671type VideoConfig struct {
672	ChatId           int
673	ReplyToMessageId int
674	ReplyMarkup      interface{}
675	UseExistingVideo bool
676	FilePath         string
677	FileId           string
678}
679
680type LocationConfig struct {
681	ChatId           int
682	Latitude         float64
683	Longitude        float64
684	ReplyToMessageId int
685	ReplyMarkup      interface{}
686}
687
688type ChatActionConfig struct {
689	ChatId int
690	Action string
691}
692
693type UserProfilePhotosConfig struct {
694	UserId int
695	Offset int
696	Limit  int
697}
698
699func NewMessage(chatId int, text string) MessageConfig {
700	return MessageConfig{
701		ChatId: chatId,
702		Text:   text,
703		DisableWebPagePreview: false,
704		ReplyToMessageId:      0,
705	}
706}
707
708func NewForward(chatId int, fromChatId int, messageId int) ForwardConfig {
709	return ForwardConfig{
710		ChatId:     chatId,
711		FromChatId: fromChatId,
712		MessageId:  messageId,
713	}
714}
715
716func NewPhotoUpload(chatId int, filename string) PhotoConfig {
717	return PhotoConfig{
718		ChatId:           chatId,
719		UseExistingPhoto: false,
720		FilePath:         filename,
721	}
722}
723
724func NewPhotoShare(chatId int, fileId string) PhotoConfig {
725	return PhotoConfig{
726		ChatId:           chatId,
727		UseExistingPhoto: true,
728		FileId:           fileId,
729	}
730}
731
732func NewAudioUpload(chatId int, filename string) AudioConfig {
733	return AudioConfig{
734		ChatId:           chatId,
735		UseExistingAudio: false,
736		FilePath:         filename,
737	}
738}
739
740func NewAudioShare(chatId int, fileId string) AudioConfig {
741	return AudioConfig{
742		ChatId:           chatId,
743		UseExistingAudio: true,
744		FileId:           fileId,
745	}
746}
747
748func NewDocumentUpload(chatId int, filename string) DocumentConfig {
749	return DocumentConfig{
750		ChatId:              chatId,
751		UseExistingDocument: false,
752		FilePath:            filename,
753	}
754}
755
756func NewDocumentShare(chatId int, fileId string) DocumentConfig {
757	return DocumentConfig{
758		ChatId:              chatId,
759		UseExistingDocument: true,
760		FileId:              fileId,
761	}
762}
763
764func NewStickerUpload(chatId int, filename string) StickerConfig {
765	return StickerConfig{
766		ChatId:             chatId,
767		UseExistingSticker: false,
768		FilePath:           filename,
769	}
770}
771
772func NewStickerShare(chatId int, fileId string) StickerConfig {
773	return StickerConfig{
774		ChatId:             chatId,
775		UseExistingSticker: true,
776		FileId:             fileId,
777	}
778}
779
780func NewVideoUpload(chatId int, filename string) VideoConfig {
781	return VideoConfig{
782		ChatId:           chatId,
783		UseExistingVideo: false,
784		FilePath:         filename,
785	}
786}
787
788func NewVideoShare(chatId int, fileId string) VideoConfig {
789	return VideoConfig{
790		ChatId:           chatId,
791		UseExistingVideo: true,
792		FileId:           fileId,
793	}
794}
795
796func NewLocation(chatId int, latitude float64, longitude float64) LocationConfig {
797	return LocationConfig{
798		ChatId:           chatId,
799		Latitude:         latitude,
800		Longitude:        longitude,
801		ReplyToMessageId: 0,
802		ReplyMarkup:      nil,
803	}
804}
805
806func NewChatAction(chatId int, action string) ChatActionConfig {
807	return ChatActionConfig{
808		ChatId: chatId,
809		Action: action,
810	}
811}
812
813func NewUserProfilePhotos(userId int) UserProfilePhotosConfig {
814	return UserProfilePhotosConfig{
815		UserId: userId,
816		Offset: 0,
817		Limit:  0,
818	}
819}
820
821func NewUpdate(offset int) UpdateConfig {
822	return UpdateConfig{
823		Offset:  offset,
824		Limit:   0,
825		Timeout: 0,
826	}
827}