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