Add support for file uploads
Norberto Lopes
9 years ago
0 | 0 | - Add more tests!!! |
1 | 1 | - Add timeouts |
2 | - Add examples | |
3 | 2 | - Fix the Websocket mess |
4 | - Implement Upload File | |
5 | 3 | - Add support to have markdown hints |
6 | 4 | - See section Message Formatting at https://api.slack.com/docs/formatting |
0 | Nan Nan Nan Nan Nan Nan Nan Nan Batman |
0 | package main | |
1 | ||
2 | import ( | |
3 | "fmt" | |
4 | ||
5 | "github.com/nlopes/slack" | |
6 | ) | |
7 | ||
8 | func main() { | |
9 | api := slack.New("YOUR_TOKEN_HERE") | |
10 | params := slack.FileUploadParameters{ | |
11 | Title: "Batman Example", | |
12 | //Filetype: "txt", | |
13 | File: "example.txt", | |
14 | //Content: "Nan Nan Nan Nan Nan Nan Nan Nan Batman", | |
15 | } | |
16 | file, err := api.UploadFile(params) | |
17 | if err != nil { | |
18 | fmt.Printf("%s\n", err) | |
19 | return | |
20 | } | |
21 | fmt.Printf("Name: %s, Url: %s\n", file.Name, file.Url) | |
22 | } |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "errors" |
4 | "log" | |
5 | 4 | "net/url" |
6 | 5 | "strconv" |
6 | "strings" | |
7 | 7 | ) |
8 | 8 | |
9 | 9 | const ( |
70 | 70 | IsStarred bool `json:"is_starred"` |
71 | 71 | } |
72 | 72 | |
73 | type FileUploadParameters struct { | |
74 | File string | |
75 | Content string | |
76 | Filetype string | |
77 | Filename string | |
78 | Title string | |
79 | InitialComment string | |
80 | Channels []string | |
81 | } | |
82 | ||
73 | 83 | type GetFilesParameters struct { |
74 | 84 | UserId string |
75 | 85 | TimestampFrom JSONTime |
155 | 165 | return response.Files, &response.Paging, nil |
156 | 166 | } |
157 | 167 | |
158 | func (api *Slack) UploadFile() { | |
159 | log.Fatal("Not implemented yet") | |
160 | } | |
168 | func (api *Slack) UploadFile(params FileUploadParameters) (file *File, err error) { | |
169 | // Test if user token is valid. This helps because client.Do doesn't like this for some reason. XXX: More | |
170 | // investigation needed, but for now this will do. | |
171 | _, err = api.AuthTest() | |
172 | if err != nil { | |
173 | return nil, err | |
174 | } | |
175 | response := &fileResponseFull{} | |
176 | values := url.Values{ | |
177 | "token": {api.config.token}, | |
178 | } | |
179 | if params.Filetype != "" { | |
180 | values.Add("filetype", params.Filetype) | |
181 | } | |
182 | if params.Filename != "" { | |
183 | values.Add("filename", params.Filename) | |
184 | } | |
185 | if params.Title != "" { | |
186 | values.Add("title", params.Title) | |
187 | } | |
188 | if params.InitialComment != "" { | |
189 | values.Add("initial_comment", params.InitialComment) | |
190 | } | |
191 | if len(params.Channels) != 0 { | |
192 | values.Add("channels", strings.Join(params.Channels, ",")) | |
193 | } | |
194 | if params.Content != "" { | |
195 | values.Add("content", params.Content) | |
196 | err = ParseResponse("files.upload", values, response, api.debug) | |
197 | } else if params.File != "" { | |
198 | err = parseResponseMultipart("files.upload", params.File, values, response, api.debug) | |
199 | } | |
200 | if err != nil { | |
201 | return nil, err | |
202 | } | |
203 | if !response.Ok { | |
204 | return nil, errors.New(response.Error) | |
205 | } | |
206 | return &response.File, nil | |
207 | } |
2 | 2 | import ( |
3 | 3 | "bytes" |
4 | 4 | "encoding/json" |
5 | "errors" | |
6 | "io" | |
5 | 7 | "io/ioutil" |
6 | 8 | "log" |
9 | "mime/multipart" | |
7 | 10 | "net/http" |
8 | 11 | "net/url" |
12 | "os" | |
13 | "path/filepath" | |
9 | 14 | ) |
15 | ||
16 | func fileUploadReq(path, fpath string, values url.Values) (*http.Request, error) { | |
17 | fullpath, err := filepath.Abs(fpath) | |
18 | if err != nil { | |
19 | return nil, err | |
20 | } | |
21 | file, err := os.Open(fullpath) | |
22 | if err != nil { | |
23 | return nil, err | |
24 | } | |
25 | defer file.Close() | |
26 | ||
27 | body := &bytes.Buffer{} | |
28 | wr := multipart.NewWriter(body) | |
29 | ||
30 | ioWriter, err := wr.CreateFormFile("file", filepath.Base(fullpath)) | |
31 | if err != nil { | |
32 | wr.Close() | |
33 | return nil, err | |
34 | } | |
35 | bytes, err := io.Copy(ioWriter, file) | |
36 | if err != nil { | |
37 | wr.Close() | |
38 | return nil, err | |
39 | } | |
40 | // Close the multipart writer or the footer won't be written | |
41 | wr.Close() | |
42 | stat, err := file.Stat() | |
43 | if err != nil { | |
44 | return nil, err | |
45 | } | |
46 | if bytes != stat.Size() { | |
47 | return nil, errors.New("could not read the whole file") | |
48 | } | |
49 | req, err := http.NewRequest("POST", path, body) | |
50 | if err != nil { | |
51 | return nil, err | |
52 | } | |
53 | req.Header.Add("Content-Type", wr.FormDataContentType()) | |
54 | req.URL.RawQuery = (values).Encode() | |
55 | return req, nil | |
56 | } | |
57 | ||
58 | func parseResponseBody(body io.ReadCloser, intf *interface{}, debug bool) error { | |
59 | var decoder *json.Decoder | |
60 | if debug { | |
61 | response, err := ioutil.ReadAll(body) | |
62 | if err != nil { | |
63 | return err | |
64 | } | |
65 | log.Println(string(response)) | |
66 | decoder = json.NewDecoder(bytes.NewReader(response)) | |
67 | } else { | |
68 | decoder = json.NewDecoder(body) | |
69 | } | |
70 | if err := decoder.Decode(&intf); err != nil { | |
71 | return err | |
72 | } | |
73 | return nil | |
74 | ||
75 | } | |
76 | ||
77 | func parseResponseMultipart(path string, filepath string, values url.Values, intf interface{}, debug bool) error { | |
78 | req, err := fileUploadReq(SLACK_API+path, filepath, values) | |
79 | client := &http.Client{} | |
80 | resp, err := client.Do(req) | |
81 | if err != nil { | |
82 | return err | |
83 | } | |
84 | defer resp.Body.Close() | |
85 | return parseResponseBody(resp.Body, &intf, debug) | |
86 | } | |
10 | 87 | |
11 | 88 | func ParseResponse(path string, values url.Values, intf interface{}, debug bool) error { |
12 | 89 | resp, err := http.PostForm(SLACK_API+path, values) |
14 | 91 | return err |
15 | 92 | } |
16 | 93 | defer resp.Body.Close() |
17 | ||
18 | var decoder *json.Decoder | |
19 | if debug { | |
20 | response, err := ioutil.ReadAll(resp.Body) | |
21 | if err != nil { | |
22 | return err | |
23 | } | |
24 | log.Println(string(response)) | |
25 | decoder = json.NewDecoder(bytes.NewReader(response)) | |
26 | } else { | |
27 | decoder = json.NewDecoder(resp.Body) | |
28 | } | |
29 | if err := decoder.Decode(&intf); err != nil { | |
30 | return err | |
31 | } | |
32 | return nil | |
94 | return parseResponseBody(resp.Body, &intf, debug) | |
33 | 95 | } |