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}