From 059baffbfbf2683405ac97a06a3592a203ed5a57 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Thu, 25 Jun 2015 11:25:02 -0500 Subject: [PATCH] working file uploads, starting to clean up code --- bot.go | 2 + methods.go | 363 +++++++++++++++++++++++++++------------------------ responses.go | 134 +++++++++++++++++++ 3 files changed, 331 insertions(+), 168 deletions(-) create mode 100644 responses.go diff --git a/bot.go b/bot.go index fd9f924..7cad09a 100644 --- a/bot.go +++ b/bot.go @@ -52,6 +52,8 @@ func (plugin *ColonThree) GotCommand(message Message, args []string) { } } else { bot.sendMessage(NewMessage(message.Chat.Id, ":3")) + + bot.sendPhoto(NewPhotoUpload(message.Chat.Id, "fox.png")) } } diff --git a/methods.go b/methods.go index a04c1d8..bc59238 100644 --- a/methods.go +++ b/methods.go @@ -1,11 +1,15 @@ package main import ( + "bytes" "encoding/json" + "io" "io/ioutil" "log" + "mime/multipart" "net/http" "net/url" + "os" "strconv" ) @@ -29,173 +33,6 @@ type BotApi struct { config BotConfig } -type ApiResponse struct { - Ok bool `json:"ok"` - Result json.RawMessage `json:"result"` -} - -type Update struct { - UpdateId int `json:"update_id"` - Message Message `json:"message"` -} - -type User struct { - Id int `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - UserName string `json:"username"` -} - -type GroupChat struct { - Id int `json:"id"` - Title string `json:"title"` -} - -type UserOrGroupChat struct { - Id int `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - UserName string `json:"username"` - Title string `json:"title"` -} - -type Message struct { - MessageId int `json:"message_id"` - From User `json:"from"` - Date int `json:"date"` - Chat UserOrGroupChat `json:"chat"` - ForwardFrom User `json:"forward_from"` - ForwardDate int `json:"forward_date"` - ReplyToMessage *Message `json:"reply_to_message"` - Text string `json:"text"` - Audio Audio `json:"audio"` - Document Document `json:"document"` - Photo []PhotoSize `json:"photo"` - Sticker Sticker `json:"sticker"` - Video Video `json:"video"` - Contact Contact `json:"contact"` - Location Location `json:"location"` - NewChatParticipant User `json:"new_chat_participant"` - LeftChatParticipant User `json:"left_chat_participant"` - NewChatTitle string `json:"new_chat_title"` - NewChatPhoto string `json:"new_chat_photo"` - DeleteChatPhoto bool `json:"delete_chat_photo"` - GroupChatCreated bool `json:"group_chat_created"` -} - -type PhotoSize struct { - FileId string `json:"file_id"` - Width int `json:"width"` - Height int `json:"height"` - FileSize int `json:"file_size"` -} - -type Audio struct { - FileId string `json:"file_id"` - Duration int `json:"duration"` - MimeType string `json:"mime_type"` - FileSize int `json:"file_size"` -} - -type Document struct { - FileId string `json:"file_id"` - Thumb PhotoSize `json:"thumb"` - FileName string `json:"file_name"` - MimeType string `json:"mime_type"` - FileSize int `json:"file_size"` -} - -type Sticker struct { - FileId string `json:"file_id"` - Width int `json:"width"` - Height int `json:"height"` - Thumb PhotoSize `json:"thumb"` - FileSize int `json:"file_size"` -} - -type Video struct { - FileId string `json:"file_id"` - Width int `json:"width"` - Height int `json:"height"` - Duration int `json:"duration"` - Thumb PhotoSize `json:"thumb"` - MimeType string `json:"mime_type"` - FileSize int `json:"file_size"` - Caption string `json:"caption"` -} - -type Contact struct { - PhoneNumber string `json:"phone_number"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - UserId string `json:"user_id"` -} - -type Location struct { - Longitude float32 `json:"longitude"` - Latitude float32 `json:"latitude"` -} - -type UserProfilePhotos struct { - TotalCount int `json:"total_count"` - Photos []PhotoSize `json:"photos"` -} - -type ReplyKeyboardMarkup struct { - Keyboard map[string]map[string]string `json:"keyboard"` - ResizeKeyboard bool `json:"resize_keyboard"` - OneTimeKeyboard bool `json:"one_time_keyboard"` - Selective bool `json:"selective"` -} - -type ReplyKeyboardHide struct { - HideKeyboard bool `json:"hide_keyboard"` - Selective bool `json:"selective"` -} - -type ForceReply struct { - ForceReply bool `json:"force_reply"` - Selective bool `json:"force_reply"` -} - -type UpdateConfig struct { - Offset int - Limit int - Timeout int -} - -type MessageConfig struct { - ChatId int - Text string - DisableWebPagePreview bool - ReplyToMessageId int -} - -type ForwardConfig struct { - ChatId int - FromChatId int - MessageId int -} - -type LocationConfig struct { - ChatId int - Latitude float64 - Longitude float64 - ReplyToMessageId int - ReplyMarkup interface{} -} - -type ChatActionConfig struct { - ChatId int - Action string -} - -type UserProfilePhotosConfig struct { - UserId int - Offset int - Limit int -} - func NewBotApi(config BotConfig) *BotApi { return &BotApi{ config: config, @@ -211,7 +48,65 @@ func (bot *BotApi) makeRequest(endpoint string, params url.Values) (ApiResponse, bytes, err := ioutil.ReadAll(resp.Body) if err != nil { - return ApiResponse{}, nil + return ApiResponse{}, err + } + + if bot.config.debug { + log.Println(string(bytes[:])) + } + + var apiResp ApiResponse + json.Unmarshal(bytes, &apiResp) + + return apiResp, nil +} + +func (bot *BotApi) uploadFile(endpoint string, params map[string]string, fieldname string, filename string) (ApiResponse, error) { + var b bytes.Buffer + w := multipart.NewWriter(&b) + + f, err := os.Open(filename) + if err != nil { + return ApiResponse{}, err + } + + fw, err := w.CreateFormFile(fieldname, filename) + if err != nil { + return ApiResponse{}, err + } + + if _, err = io.Copy(fw, f); err != nil { + return ApiResponse{}, err + } + + for key, val := range params { + if fw, err = w.CreateFormField(key); err != nil { + return ApiResponse{}, err + } + + if _, err = fw.Write([]byte(val)); err != nil { + return ApiResponse{}, err + } + } + + w.Close() + + req, err := http.NewRequest("POST", "https://api.telegram.org/bot"+bot.config.token+"/"+endpoint, &b) + if err != nil { + return ApiResponse{}, err + } + + req.Header.Set("Content-Type", w.FormDataContentType()) + + client := &http.Client{} + res, err := client.Do(req) + if err != nil { + return ApiResponse{}, err + } + + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return ApiResponse{}, err } if bot.config.debug { @@ -287,6 +182,74 @@ func (bot *BotApi) forwardMessage(config ForwardConfig) (Message, error) { return message, nil } +func (bot *BotApi) sendPhoto(config PhotoConfig) (Message, error) { + if config.UseExistingPhoto { + v := url.Values{} + v.Add("chat_id", strconv.Itoa(config.ChatId)) + v.Add("photo", config.FileId) + if config.Caption != "" { + v.Add("caption", config.Caption) + } + if config.ReplyToMessageId != 0 { + v.Add("reply_to_message_id", strconv.Itoa(config.ChatId)) + } + if config.ReplyMarkup != nil { + data, err := json.Marshal(config.ReplyMarkup) + if err != nil { + return Message{}, err + } + + v.Add("reply_markup", string(data)) + } + + resp, err := bot.makeRequest("sendPhoto", v) + if err != nil { + return Message{}, err + } + + var message Message + json.Unmarshal(resp.Result, &message) + + if bot.config.debug { + log.Printf("sendPhoto req : %+v\n", v) + log.Printf("sendPhoto resp: %+v\n", message) + } + + return message, nil + } + + params := make(map[string]string) + params["chat_id"] = strconv.Itoa(config.ChatId) + if config.Caption != "" { + params["caption"] = config.Caption + } + if config.ReplyToMessageId != 0 { + params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageId) + } + if config.ReplyMarkup != nil { + data, err := json.Marshal(config.ReplyMarkup) + if err != nil { + return Message{}, err + } + + params["reply_markup"] = string(data) + } + + resp, err := bot.uploadFile("sendPhoto", params, "photo", config.FilePath) + if err != nil { + return Message{}, err + } + + var message Message + json.Unmarshal(resp.Result, &message) + + if bot.config.debug { + log.Printf("sendPhoto resp: %+v\n", message) + } + + return message, nil +} + func (bot *BotApi) sendLocation(config LocationConfig) (Message, error) { v := url.Values{} v.Add("chat_id", strconv.Itoa(config.ChatId)) @@ -392,6 +355,54 @@ func (bot *BotApi) setWebhook(v url.Values) error { return err } +type UpdateConfig struct { + Offset int + Limit int + Timeout int +} + +type MessageConfig struct { + ChatId int + Text string + DisableWebPagePreview bool + ReplyToMessageId int +} + +type ForwardConfig struct { + ChatId int + FromChatId int + MessageId int +} + +type PhotoConfig struct { + ChatId int + Caption string + ReplyToMessageId int + ReplyMarkup interface{} + UseExistingPhoto bool + FilePath string + FileId string +} + +type LocationConfig struct { + ChatId int + Latitude float64 + Longitude float64 + ReplyToMessageId int + ReplyMarkup interface{} +} + +type ChatActionConfig struct { + ChatId int + Action string +} + +type UserProfilePhotosConfig struct { + UserId int + Offset int + Limit int +} + func NewMessage(chatId int, text string) MessageConfig { return MessageConfig{ ChatId: chatId, @@ -409,6 +420,22 @@ func NewForward(chatId int, fromChatId int, messageId int) ForwardConfig { } } +func NewPhotoUpload(chatId int, filename string) PhotoConfig { + return PhotoConfig{ + ChatId: chatId, + UseExistingPhoto: false, + FilePath: filename, + } +} + +func NewPhotoShare(chatId int, fileId string) PhotoConfig { + return PhotoConfig{ + ChatId: chatId, + UseExistingPhoto: true, + FileId: fileId, + } +} + func NewLocation(chatId int, latitude float64, longitude float64) LocationConfig { return LocationConfig{ ChatId: chatId, diff --git a/responses.go b/responses.go new file mode 100644 index 0000000..d2b4ce8 --- /dev/null +++ b/responses.go @@ -0,0 +1,134 @@ +package main + +import ( + "encoding/json" +) + +type ApiResponse struct { + Ok bool `json:"ok"` + Result json.RawMessage `json:"result"` +} + +type Update struct { + UpdateId int `json:"update_id"` + Message Message `json:"message"` +} + +type User struct { + Id int `json:"id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + UserName string `json:"username"` +} + +type GroupChat struct { + Id int `json:"id"` + Title string `json:"title"` +} + +type UserOrGroupChat struct { + Id int `json:"id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + UserName string `json:"username"` + Title string `json:"title"` +} + +type Message struct { + MessageId int `json:"message_id"` + From User `json:"from"` + Date int `json:"date"` + Chat UserOrGroupChat `json:"chat"` + ForwardFrom User `json:"forward_from"` + ForwardDate int `json:"forward_date"` + ReplyToMessage *Message `json:"reply_to_message"` + Text string `json:"text"` + Audio Audio `json:"audio"` + Document Document `json:"document"` + Photo []PhotoSize `json:"photo"` + Sticker Sticker `json:"sticker"` + Video Video `json:"video"` + Contact Contact `json:"contact"` + Location Location `json:"location"` + NewChatParticipant User `json:"new_chat_participant"` + LeftChatParticipant User `json:"left_chat_participant"` + NewChatTitle string `json:"new_chat_title"` + NewChatPhoto string `json:"new_chat_photo"` + DeleteChatPhoto bool `json:"delete_chat_photo"` + GroupChatCreated bool `json:"group_chat_created"` +} + +type PhotoSize struct { + FileId string `json:"file_id"` + Width int `json:"width"` + Height int `json:"height"` + FileSize int `json:"file_size"` +} + +type Audio struct { + FileId string `json:"file_id"` + Duration int `json:"duration"` + MimeType string `json:"mime_type"` + FileSize int `json:"file_size"` +} + +type Document struct { + FileId string `json:"file_id"` + Thumb PhotoSize `json:"thumb"` + FileName string `json:"file_name"` + MimeType string `json:"mime_type"` + FileSize int `json:"file_size"` +} + +type Sticker struct { + FileId string `json:"file_id"` + Width int `json:"width"` + Height int `json:"height"` + Thumb PhotoSize `json:"thumb"` + FileSize int `json:"file_size"` +} + +type Video struct { + FileId string `json:"file_id"` + Width int `json:"width"` + Height int `json:"height"` + Duration int `json:"duration"` + Thumb PhotoSize `json:"thumb"` + MimeType string `json:"mime_type"` + FileSize int `json:"file_size"` + Caption string `json:"caption"` +} + +type Contact struct { + PhoneNumber string `json:"phone_number"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + UserId string `json:"user_id"` +} + +type Location struct { + Longitude float32 `json:"longitude"` + Latitude float32 `json:"latitude"` +} + +type UserProfilePhotos struct { + TotalCount int `json:"total_count"` + Photos []PhotoSize `json:"photos"` +} + +type ReplyKeyboardMarkup struct { + Keyboard map[string]map[string]string `json:"keyboard"` + ResizeKeyboard bool `json:"resize_keyboard"` + OneTimeKeyboard bool `json:"one_time_keyboard"` + Selective bool `json:"selective"` +} + +type ReplyKeyboardHide struct { + HideKeyboard bool `json:"hide_keyboard"` + Selective bool `json:"selective"` +} + +type ForceReply struct { + ForceReply bool `json:"force_reply"` + Selective bool `json:"force_reply"` +}