working file uploads, starting to clean up code
Syfaro syfaro@foxpaw.in
Thu, 25 Jun 2015 11:25:02 -0500
3 files changed,
324 insertions(+),
161 deletions(-)
M
methods.go
→
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,189 +33,80 @@ type BotApi struct {
config BotConfig } -type ApiResponse struct { - Ok bool `json:"ok"` - Result json.RawMessage `json:"result"` +func NewBotApi(config BotConfig) *BotApi { + return &BotApi{ + config: config, + } } -type Update struct { - UpdateId int `json:"update_id"` - Message Message `json:"message"` -} +func (bot *BotApi) makeRequest(endpoint string, params url.Values) (ApiResponse, error) { + resp, err := http.PostForm("https://api.telegram.org/bot"+bot.config.token+"/"+endpoint, params) + defer resp.Body.Close() + if err != nil { + return ApiResponse{}, err + } -type User struct { - Id int `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - UserName string `json:"username"` -} + bytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ApiResponse{}, err + } -type GroupChat struct { - Id int `json:"id"` - Title string `json:"title"` -} + if bot.config.debug { + log.Println(string(bytes[:])) + } -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"` -} + var apiResp ApiResponse + json.Unmarshal(bytes, &apiResp) -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"` + return apiResp, nil } -type PhotoSize struct { - FileId string `json:"file_id"` - Width int `json:"width"` - Height int `json:"height"` - FileSize int `json:"file_size"` -} +func (bot *BotApi) uploadFile(endpoint string, params map[string]string, fieldname string, filename string) (ApiResponse, error) { + var b bytes.Buffer + w := multipart.NewWriter(&b) -type Audio struct { - FileId string `json:"file_id"` - Duration int `json:"duration"` - MimeType string `json:"mime_type"` - FileSize int `json:"file_size"` -} + f, err := os.Open(filename) + if err != nil { + return ApiResponse{}, err + } -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"` -} + fw, err := w.CreateFormFile(fieldname, filename) + if err != nil { + return ApiResponse{}, err + } -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"` -} + if _, err = io.Copy(fw, f); err != nil { + return ApiResponse{}, err + } -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"` -} + for key, val := range params { + if fw, err = w.CreateFormField(key); err != nil { + return ApiResponse{}, err + } -type Contact struct { - PhoneNumber string `json:"phone_number"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - UserId string `json:"user_id"` -} + if _, err = fw.Write([]byte(val)); err != nil { + return ApiResponse{}, err + } + } -type Location struct { - Longitude float32 `json:"longitude"` - Latitude float32 `json:"latitude"` -} + w.Close() -type UserProfilePhotos struct { - TotalCount int `json:"total_count"` - Photos []PhotoSize `json:"photos"` -} + req, err := http.NewRequest("POST", "https://api.telegram.org/bot"+bot.config.token+"/"+endpoint, &b) + if err != nil { + return ApiResponse{}, err + } -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"` -} + req.Header.Set("Content-Type", w.FormDataContentType()) -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, - } -} - -func (bot *BotApi) makeRequest(endpoint string, params url.Values) (ApiResponse, error) { - resp, err := http.PostForm("https://api.telegram.org/bot"+bot.config.token+"/"+endpoint, params) - defer resp.Body.Close() + client := &http.Client{} + res, err := client.Do(req) if err != nil { return ApiResponse{}, err } - bytes, err := ioutil.ReadAll(resp.Body) + bytes, err := ioutil.ReadAll(res.Body) if err != nil { - return ApiResponse{}, nil + return ApiResponse{}, err } if bot.config.debug {@@ -287,6 +182,74 @@
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 @@
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,@@ -406,6 +417,22 @@ return ForwardConfig{
ChatId: chatId, FromChatId: fromChatId, MessageId: messageId, + } +} + +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, } }
A
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"` +}