From d427667b299b5366dd63824d51e21fb756ce1fb1 Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Wed, 26 Dec 2018 17:38:41 +0300 Subject: [PATCH] add file upload and download methods --- v1/client.go | 110 +++++++++++++++++++++++++++++++++++++++++++++- v1/client_test.go | 57 ++++++++++++++++++++++-- v1/request.go | 9 ++-- v1/types.go | 36 +++++++++++++++ 4 files changed, 204 insertions(+), 8 deletions(-) diff --git a/v1/client.go b/v1/client.go index 673b136..ca96014 100644 --- a/v1/client.go +++ b/v1/client.go @@ -1,9 +1,11 @@ package v1 import ( + "bytes" "encoding/json" "errors" "fmt" + "io" "net/http" "strings" "time" @@ -437,7 +439,7 @@ func (c *MgClient) MessageSend(request MessageSendRequest) (MessageSendResponse, var resp MessageSendResponse outgoing, _ := json.Marshal(&request) - data, status, err := c.PostRequest("/messages", []byte(outgoing)) + data, status, err := c.PostRequest("/messages", bytes.NewBuffer(outgoing)) if err != nil { return resp, status, err } @@ -677,6 +679,112 @@ func (c *MgClient) CommandDelete(request string) (map[string]interface{}, int, e return resp, status, err } +// GetFile implement get file url +// +// Example: +// +// var client = v1.New("https://token.url", "cb8ccf05e38a47543ad8477d4999be73bff503ea6") +// +// data, status, err := client.GetFile("file_ID") +// +// if err != nil { +// fmt.Printf("%v", err) +// } +// +// fmt.Printf("%s\n", data.ID) +func (c *MgClient) GetFile(request string) (FullFileResponse, int, error) { + var resp FullFileResponse + var b []byte + + data, status, err := c.GetRequest(fmt.Sprintf("/files/%s", request), b) + + if err != nil { + return resp, status, err + } + + if e := json.Unmarshal(data, &resp); e != nil { + return resp, status, e + } + + if status != http.StatusOK { + return resp, status, c.Error(data) + } + + return resp, status, err +} + +// UploadFile upload file +// +// Example: +// +// resp, err := http.Get("https://via.placeholder.com/300") +// if err != nil { +// fmt.Printf("%v", err) +// } +// +// defer resp.Body.Close() +// +// var client = v1.New("https://token.url", "cb8ccf05e38a47543ad8477d4999be73bff503ea6") +// +// data, status, err := client.UploadFile(resp.Body) +// +// if err != nil { +// fmt.Printf("%v", err) +// } +// +// fmt.Printf("%s\n%s", data.ID, status) +func (c *MgClient) UploadFile(request io.Reader) (UploadFileResponse, int, error) { + var resp UploadFileResponse + + data, status, err := c.PostRequest("/files/upload", request) + if err != nil { + return resp, status, err + } + + if e := json.Unmarshal(data, &resp); e != nil { + return resp, status, e + } + + if status != http.StatusOK { + return resp, status, c.Error(data) + } + + return resp, status, err +} + +// UploadFileByURL upload file by url +// +// Example: +// +// uploadFileResponse, st, err := c.UploadFileByURL(UploadFileByUrlRequest{ +// Url: "https://via.placeholder.com/300", +// }) +// +// if err != nil { +// fmt.Printf("%v", err) +// } +// +// fmt.Printf("%s\n%s", uploadFileResponse.ID, status) +func (c *MgClient) UploadFileByURL(request UploadFileByUrlRequest) (UploadFileResponse, int, error) { + var resp UploadFileResponse + outgoing, _ := json.Marshal(&request) + + data, status, err := c.PostRequest("/files/upload_by_url", bytes.NewBuffer(outgoing)) + if err != nil { + return resp, status, err + } + + if e := json.Unmarshal(data, &resp); e != nil { + return resp, status, e + } + + if status != http.StatusOK { + return resp, status, c.Error(data) + } + + return resp, status, err +} + // WsMeta let you receive url & headers to open web socket connection func (c *MgClient) WsMeta(events []string) (string, http.Header, error) { var url string diff --git a/v1/client_test.go b/v1/client_test.go index 42b0caa..9d643d6 100644 --- a/v1/client_test.go +++ b/v1/client_test.go @@ -12,7 +12,6 @@ import ( "time" "github.com/joho/godotenv" - "github.com/stretchr/testify/assert" ) @@ -42,7 +41,10 @@ var ( ) func client() *MgClient { - return New(mgURL, mgToken) + c := New(mgURL, mgToken) + c.Debug = true + + return c } func TestMgClient_Bots(t *testing.T) { @@ -197,7 +199,7 @@ func TestMgClient_Messages(t *testing.T) { assert.NotEmpty(t, data) for _, message := range data { - assert.NotEmpty(t, message.Content) + assert.NotEmpty(t, message.ID) } } @@ -433,6 +435,55 @@ func TestMgClient_WsMeta(t *testing.T) { assert.Equal(t, resToken, headers["X-Bot-Token"][0]) } +func TestMgClient_UploadFile(t *testing.T) { + c := client() + + resp, err := http.Get("https://via.placeholder.com/300") + if err != nil { + t.Errorf("%v", err) + } + + defer resp.Body.Close() + + data, status, err := c.UploadFile(resp.Body) + + if status != http.StatusOK { + t.Errorf("%v", err) + } + + t.Logf("File %+v is upload", data) +} + +func TestMgClient_ImageMessages(t *testing.T) { + c := client() + + uploadFileResponse, st, err := c.UploadFileByURL(UploadFileByUrlRequest{ + Url: "https://via.placeholder.com/300", + }) + + if st != http.StatusOK { + t.Errorf("%v", err) + } + + t.Logf("File %+v is upload", uploadFileResponse.ID) + + i, err := strconv.ParseUint(os.Getenv("MG_BOT_CHAT"), 10, 64) + message := MessageSendRequest{ + Type: MsgTypeImage, + Scope: MessageScopePublic, + Items: []Item{{ID: uploadFileResponse.ID}}, + ChatID: i, + } + + data, status, err := c.MessageSend(message) + if err != nil { + t.Errorf("%d %v", status, err) + } + + assert.NoError(t, err) + assert.NotEmpty(t, data.MessageID) +} + func RandStringBytesMaskImprSrc(n int) string { b := make([]byte, n) // A src.Int63() generates 63 random bits, enough for letterIdxMax characters! diff --git a/v1/request.go b/v1/request.go index 54f298e..26f0da2 100644 --- a/v1/request.go +++ b/v1/request.go @@ -3,6 +3,7 @@ package v1 import ( "bytes" "fmt" + "io" "io/ioutil" "log" "net/http" @@ -21,11 +22,11 @@ func (c *MgClient) GetRequest(url string, parameters []byte) ([]byte, int, error } // PostRequest implements POST Request -func (c *MgClient) PostRequest(url string, parameters []byte) ([]byte, int, error) { +func (c *MgClient) PostRequest(url string, parameters io.Reader) ([]byte, int, error) { return makeRequest( "POST", fmt.Sprintf("%s%s%s", c.URL, prefix, url), - bytes.NewBuffer(parameters), + parameters, c, ) } @@ -60,7 +61,7 @@ func (c *MgClient) DeleteRequest(url string, parameters []byte) ([]byte, int, er ) } -func makeRequest(reqType, url string, buf *bytes.Buffer, c *MgClient) ([]byte, int, error) { +func makeRequest(reqType, url string, buf io.Reader, c *MgClient) ([]byte, int, error) { var res []byte req, err := http.NewRequest(reqType, url, buf) if err != nil { @@ -71,7 +72,7 @@ func makeRequest(reqType, url string, buf *bytes.Buffer, c *MgClient) ([]byte, i req.Header.Set("X-Bot-Token", c.Token) if c.Debug { - log.Printf("MG BOT API Request: %s %s %s %s", reqType, url, c.Token, buf.String()) + log.Printf("MG BOT API Request: %s %s %s %+v", reqType, url, c.Token, buf) } resp, err := c.httpClient.Do(req) diff --git a/v1/types.go b/v1/types.go index 2bed805..160d3fe 100644 --- a/v1/types.go +++ b/v1/types.go @@ -3,6 +3,7 @@ package v1 import ( "encoding/json" "net/http" + "time" ) const ( @@ -50,6 +51,8 @@ const ( MsgTypeCommand string = "command" MsgTypeOrder string = "order" MsgTypeProduct string = "product" + MsgTypeFile string = "file" + MsgTypeImage string = "image" MsgOrderStatusCodeNew = "new" MsgOrderStatusCodeApproval = "approval" @@ -162,6 +165,7 @@ type ( Content string `url:"content,omitempty" json:"content"` Product *MessageProduct `url:"product,omitempty" json:"product"` Order *MessageOrder `url:"order,omitempty" json:"order"` + Items []Item `url:"order,omitempty" json:"items"` Scope string `url:"scope,omitempty" json:"scope"` ChatID uint64 `url:"chat_id,omitempty" json:"chat_id"` QuoteMessageId uint64 `url:"quote_message_id,omitempty" json:"quote_message_id"` @@ -189,6 +193,10 @@ type ( Name string `url:"name,omitempty" json:"name"` Description string `url:"description,omitempty" json:"description"` } + + UploadFileByUrlRequest struct { + Url string `json:"url"` + } ) // Response types @@ -314,6 +322,24 @@ type ( CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at,omitempty"` } + + FullFileResponse struct { + ID string `json:"id,omitempty"` + Type string `json:"type,omitempty"` + Size int `json:"size,omitempty"` + Url string `json:"url,omitempty"` + } + + UploadFileResponse struct { + ID string `json:"id"` + Hash string `json:"hash"` + Type string `json:"type"` + Meta FileMeta `json:"meta"` + MimeType string `json:"mime_type"` + Size int `json:"size"` + Url *string `json:"source_url"` + CreatedAt time.Time `json:"created_at"` + } ) // WS event types @@ -499,6 +525,16 @@ type ( CreatedAt string `json:"created_at"` ClosedAt *string `json:"closed_at"` } + + FileMeta struct { + Width *int `json:"width,omitempty"` + Height *int `json:"height,omitempty"` + } + + Item struct { + ID string `json:"id"` + Caption string `json:"caption"` + } ) // Channel settings