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}