├── LICENSE ├── README.md ├── api.go ├── attachments.go ├── auth.go ├── channels.go ├── channels_test.go ├── chat.go ├── commons.go ├── endpoints.go ├── examples ├── assets │ └── test.txt ├── auth_test.go ├── channels_history.go ├── channels_join.go ├── channels_list.go ├── chat_post_attachments.go ├── chat_post_message.go ├── groups_create.go ├── groups_invite.go ├── groups_list.go ├── post_group_message.go ├── upload_file.go ├── upload_file_info.go ├── users_info.go ├── users_list.go └── webhook_post.go ├── files.go ├── groups.go ├── ims.go ├── mpims.go ├── slack.go ├── team.go ├── users.go └── webhook.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jun Kimura 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Slack [![GoDoc](https://godoc.org/github.com/bluele/slack?status.png)](https://godoc.org/github.com/bluele/slack) 2 | 3 | Golang client for the Slack API. Include the example code using each slack api. 4 | 5 | ## Currently supports: 6 | 7 | Method | Description | Example 8 | --- | --- | --- 9 | channels.history | Fetches history of messages and events from a channel. | [#link](https://github.com/bluele/slack/blob/master/examples/channels_history.go) 10 | channels.join | Joins a channel, creating it if needed. | [#link](https://github.com/bluele/slack/blob/master/examples/channels_join.go) 11 | channels.list | Lists all channels in a Slack team. | [#link](https://github.com/bluele/slack/blob/master/examples/channels_list.go) 12 | chat.postMessage | Sends a message to a channel. | [#link](https://github.com/bluele/slack/blob/master/examples/chat_post_message.go) 13 | files.upload | Upload an image/file | [#link](https://github.com/bluele/slack/blob/master/examples/upload_file.go) 14 | files.info | Retrieves the information about an uploaded file | [#link](https://github.com/bluele/slack/blob/master/examples/upload_file_info.go) 15 | groups.invite | Invites a user to a private group. | [#link](https://github.com/bluele/slack/blob/master/examples/groups_invite.go) 16 | groups.create | Creates a private group. | [#link](https://github.com/bluele/slack/blob/master/examples/groups_create.go) 17 | groups.list | Lists private groups that the calling user has access to. | [#link](https://github.com/bluele/slack/blob/master/examples/groups_list.go) 18 | users.info | Gets information about a channel. | [#link](https://github.com/bluele/slack/blob/master/examples/users_info.go) 19 | users.list | Lists all users in a Slack team. | [#link](https://github.com/bluele/slack/blob/master/examples/users_list.go) 20 | 21 | 22 | ## Example 23 | 24 | ```go 25 | package main 26 | 27 | import ( 28 | "github.com/bluele/slack" 29 | ) 30 | 31 | const ( 32 | token = "your-api-token" 33 | channelName = "general" 34 | ) 35 | 36 | func main() { 37 | api := slack.New(token) 38 | err := api.ChatPostMessage(channelName, "Hello, world!", nil) 39 | if err != nil { 40 | panic(err) 41 | } 42 | } 43 | ``` 44 | 45 | ## Command line tool 46 | 47 | If you are looking for slack commandline utility, [vektorlab/slackcat](https://github.com/vektorlab/slackcat) probably suits you. 48 | 49 | # Author 50 | 51 | **Jun Kimura** 52 | 53 | * 54 | * 55 | -------------------------------------------------------------------------------- /api.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "net/http" 7 | "net/url" 8 | ) 9 | 10 | var httpClient *http.Client 11 | 12 | func init() { 13 | httpClient = &http.Client{} 14 | } 15 | 16 | func (sl *Slack) request(req *http.Request) ([]byte, error) { 17 | res, err := httpClient.Do(req) 18 | if err != nil { 19 | return nil, err 20 | } 21 | defer res.Body.Close() 22 | return ioutil.ReadAll(res.Body) 23 | } 24 | 25 | func (sl *Slack) GetRequest(endpoint string, uv *url.Values) ([]byte, error) { 26 | ul := apiBaseUrl + endpoint 27 | req, err := http.NewRequest("GET", ul, nil) 28 | if err != nil { 29 | return nil, err 30 | } 31 | req.URL.RawQuery = (*uv).Encode() 32 | return sl.request(req) 33 | } 34 | 35 | func (sl *Slack) PostRequest(endpoint string, uv *url.Values, body *bytes.Buffer) ([]byte, error) { 36 | ul := apiBaseUrl + endpoint 37 | req, err := http.NewRequest("POST", ul, body) 38 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 39 | if err != nil { 40 | return nil, err 41 | } 42 | req.URL.RawQuery = (*uv).Encode() 43 | return sl.request(req) 44 | } 45 | 46 | func (sl *Slack) DoRequest(req *http.Request) ([]byte, error) { 47 | return sl.request(req) 48 | } 49 | -------------------------------------------------------------------------------- /attachments.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | // https://api.slack.com/docs/attachments 4 | // It is possible to create more richly-formatted messages using Attachments. 5 | type AttachmentField struct { 6 | Title string `json:"title"` 7 | Value string `json:"value"` 8 | Short bool `json:"short"` 9 | } 10 | 11 | type Attachment struct { 12 | Color string `json:"color,omitempty"` 13 | Fallback string `json:"fallback"` 14 | 15 | AuthorName string `json:"author_name,omitempty"` 16 | AuthorSubname string `json:"author_subname,omitempty"` 17 | AuthorLink string `json:"author_link,omitempty"` 18 | AuthorIcon string `json:"author_icon,omitempty"` 19 | 20 | Title string `json:"title,omitempty"` 21 | TitleLink string `json:"title_link,omitempty"` 22 | Pretext string `json:"pretext,omitempty"` 23 | Text string `json:"text"` 24 | 25 | ImageURL string `json:"image_url,omitempty"` 26 | ThumbURL string `json:"thumb_url,omitempty"` 27 | 28 | Footer string `json:"footer,omitempty"` 29 | FooterIcon string `json:"footer_icon,omitempty"` 30 | TimeStamp int64 `json:"ts,omitempty"` 31 | 32 | Fields []*AttachmentField `json:"fields,omitempty"` 33 | MarkdownIn []string `json:"mrkdwn_in,omitempty"` 34 | } 35 | -------------------------------------------------------------------------------- /auth.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | ) 7 | 8 | // API auth.test: Checks authentication and tells you who you are. 9 | func (sl *Slack) AuthTest() (*AuthTestApiResponse, error) { 10 | uv := sl.urlValues() 11 | body, err := sl.GetRequest(authTestApiEndpoint, uv) 12 | if err != nil { 13 | return nil, err 14 | } 15 | res := new(AuthTestApiResponse) 16 | err = json.Unmarshal(body, res) 17 | if err != nil { 18 | return nil, err 19 | } 20 | if !res.Ok { 21 | return nil, errors.New(res.Error) 22 | } 23 | return res, nil 24 | } 25 | 26 | // response type for `auth.test` api 27 | type AuthTestApiResponse struct { 28 | BaseAPIResponse 29 | Url string `json:"url"` 30 | Team string `json:"team"` 31 | User string `json:"user"` 32 | TeamId string `json:"team_id"` 33 | UserId string `json:"user_id"` 34 | } 35 | -------------------------------------------------------------------------------- /channels.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "net/url" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | // API channels.list: Lists all channels in a Slack team. 12 | func (sl *Slack) ChannelsList() ([]*Channel, error) { 13 | uv := sl.urlValues() 14 | body, err := sl.GetRequest(channelsListApiEndpoint, uv) 15 | if err != nil { 16 | return nil, err 17 | } 18 | res := new(ChannelsListAPIResponse) 19 | err = json.Unmarshal(body, res) 20 | if err != nil { 21 | return nil, err 22 | } 23 | if !res.Ok { 24 | return nil, errors.New(res.Error) 25 | } 26 | return res.Channels() 27 | } 28 | 29 | // response type for `channels.list` api 30 | type ChannelsListAPIResponse struct { 31 | BaseAPIResponse 32 | RawChannels json.RawMessage `json:"channels"` 33 | } 34 | 35 | // slack channel type 36 | type Channel struct { 37 | Id string `json:"id"` 38 | Name string `json:"name"` 39 | IsChannel bool `json:"is_channel"` 40 | Created int `json:"created"` 41 | Creator string `json:"creator"` 42 | IsArchived bool `json:"is_archived"` 43 | IsGeneral bool `json:"is_general"` 44 | IsMember bool `json:"is_member"` 45 | Members []string `json:"members"` 46 | RawTopic json.RawMessage `json:"topic"` 47 | RawPurpose json.RawMessage `json:"purpose"` 48 | NumMembers int `json:"num_members"` 49 | } 50 | 51 | // Channels returns a slice of channel object from a response of `channels.list` api. 52 | func (res *ChannelsListAPIResponse) Channels() ([]*Channel, error) { 53 | var chs []*Channel 54 | err := json.Unmarshal(res.RawChannels, &chs) 55 | if err != nil { 56 | return nil, err 57 | } 58 | return chs, nil 59 | } 60 | 61 | func (ch *Channel) Topic() (*Topic, error) { 62 | tp := new(Topic) 63 | err := json.Unmarshal(ch.RawTopic, tp) 64 | if err != nil { 65 | return nil, err 66 | } 67 | return tp, nil 68 | } 69 | 70 | func (ch *Channel) Purpose() (*Purpose, error) { 71 | pp := new(Purpose) 72 | err := json.Unmarshal(ch.RawPurpose, pp) 73 | if err != nil { 74 | return nil, err 75 | } 76 | return pp, nil 77 | } 78 | 79 | // FindChannel returns a channel object that satisfy conditions specified. 80 | func (sl *Slack) FindChannel(cb func(*Channel) bool) (*Channel, error) { 81 | channels, err := sl.ChannelsList() 82 | if err != nil { 83 | return nil, err 84 | } 85 | for _, channel := range channels { 86 | if cb(channel) { 87 | return channel, nil 88 | } 89 | } 90 | return nil, errors.New("No such channel.") 91 | } 92 | 93 | // FindChannelByName returns a channel object that matches name specified. 94 | func (sl *Slack) FindChannelByName(name string) (*Channel, error) { 95 | return sl.FindChannel(func(channel *Channel) bool { 96 | return channel.Name == name 97 | }) 98 | } 99 | 100 | // API channels.join: Joins a channel, creating it if needed. 101 | func (sl *Slack) JoinChannel(name string) error { 102 | uv := sl.urlValues() 103 | uv.Add("name", name) 104 | 105 | _, err := sl.GetRequest(channelsJoinApiEndpoint, uv) 106 | if err != nil { 107 | return err 108 | } 109 | return nil 110 | } 111 | 112 | type Message struct { 113 | Type string `json:"type"` 114 | Ts string `json:"ts"` 115 | UserId string `json:"user"` 116 | Text string `json:"text"` 117 | Subtype string `json:"subtype"` 118 | } 119 | 120 | func (msg *Message) Timestamp() *time.Time { 121 | seconds, _ := strconv.ParseInt(msg.Ts[0:10], 10, 64) 122 | microseconds, _ := strconv.ParseInt(msg.Ts[11:17], 10, 64) 123 | ts := time.Unix(seconds, microseconds*1e3) 124 | return &ts 125 | } 126 | 127 | // option type for `channels.history` api 128 | type ChannelsHistoryOpt struct { 129 | Channel string `json:"channel"` 130 | Latest float64 `json:"latest"` 131 | Oldest float64 `json:"oldest"` 132 | Inclusive int `json:"inclusive"` 133 | Count int `json:"count"` 134 | UnReads int `json:"unreads,omitempty"` 135 | } 136 | 137 | func (opt *ChannelsHistoryOpt) Bind(uv *url.Values) error { 138 | uv.Add("channel", opt.Channel) 139 | if opt.Latest != 0.0 { 140 | uv.Add("lastest", strconv.FormatFloat(opt.Latest, 'f', 6, 64)) 141 | } 142 | if opt.Oldest != 0.0 { 143 | uv.Add("oldest", strconv.FormatFloat(opt.Oldest, 'f', 6, 64)) 144 | } 145 | uv.Add("inclusive", strconv.Itoa(opt.Inclusive)) 146 | if opt.Count != 0 { 147 | uv.Add("count", strconv.Itoa(opt.Count)) 148 | } 149 | uv.Add("unreads", strconv.Itoa(opt.UnReads)) 150 | return nil 151 | } 152 | 153 | // response type for `channels.history` api 154 | type ChannelsHistoryResponse struct { 155 | BaseAPIResponse 156 | Latest float64 `json:"latest"` 157 | Messages []*Message `json:"messages"` 158 | HasMore bool `json:"has_more"` 159 | UnReadCountDisplay int `json:"unread_count_display"` 160 | } 161 | 162 | // API channels.history: Fetches history of messages and events from a channel. 163 | func (sl *Slack) ChannelsHistory(opt *ChannelsHistoryOpt) (*ChannelsHistoryResponse, error) { 164 | uv := sl.urlValues() 165 | err := opt.Bind(uv) 166 | if err != nil { 167 | return nil, err 168 | } 169 | body, err := sl.GetRequest(channelsHistoryApiEndpoint, uv) 170 | if err != nil { 171 | return nil, err 172 | } 173 | res := new(ChannelsHistoryResponse) 174 | err = json.Unmarshal(body, res) 175 | if err != nil { 176 | return nil, err 177 | } 178 | if !res.Ok { 179 | return nil, errors.New(res.Error) 180 | } 181 | return res, nil 182 | } 183 | 184 | func (sl *Slack) ChannelsHistoryMessages(opt *ChannelsHistoryOpt) ([]*Message, error) { 185 | res, err := sl.ChannelsHistory(opt) 186 | if err != nil { 187 | return nil, err 188 | } 189 | return res.Messages, nil 190 | } 191 | -------------------------------------------------------------------------------- /channels_test.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestMessageTimestamp(t *testing.T) { 10 | ts := time.Date(2012, 11, 6, 10, 23, 42, 123456000, time.UTC) 11 | var msg Message 12 | msg.Ts = fmt.Sprintf("%d.%s", ts.Unix(), "123456") 13 | if msg.Timestamp().UnixNano() != ts.UnixNano() { 14 | t.Errorf("%v != %v", ts, msg.Timestamp()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /chat.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "net/url" 8 | ) 9 | 10 | // API chat.postMessage: Sends a message to a channel. 11 | func (sl *Slack) ChatPostMessage(channelId string, text string, opt *ChatPostMessageOpt) error { 12 | uv, err := sl.buildChatPostMessageUrlValues(opt) 13 | if err != nil { 14 | return err 15 | } 16 | uv.Add("channel", channelId) 17 | 18 | body, err := sl.PostRequest(chatPostMessageApiEndpoint, uv, sl.buildRequestBodyForm(text)) 19 | if err != nil { 20 | return err 21 | } 22 | res := new(ChatPostMessageAPIResponse) 23 | err = json.Unmarshal(body, res) 24 | if err != nil { 25 | return err 26 | } 27 | if !res.Ok { 28 | return errors.New(res.Error) 29 | } 30 | return nil 31 | } 32 | 33 | // option type for `chat.postMessage` api 34 | type ChatPostMessageOpt struct { 35 | AsUser bool 36 | Username string 37 | Parse string 38 | LinkNames string 39 | Attachments []*Attachment 40 | UnfurlLinks string 41 | UnfurlMedia string 42 | IconUrl string 43 | IconEmoji string 44 | } 45 | 46 | // response type for `chat.postMessage` api 47 | type ChatPostMessageAPIResponse struct { 48 | BaseAPIResponse 49 | Channel string `json:"channel"` 50 | Ts string `json:"ts"` 51 | } 52 | 53 | func (sl *Slack) buildRequestBodyForm(text string) *bytes.Buffer { 54 | return bytes.NewBuffer([]byte("text=" + url.QueryEscape(text))) 55 | } 56 | 57 | func (sl *Slack) buildChatPostMessageUrlValues(opt *ChatPostMessageOpt) (*url.Values, error) { 58 | uv := sl.urlValues() 59 | if opt == nil { 60 | return uv, nil 61 | } 62 | if opt.AsUser { 63 | uv.Add("as_user", "true") 64 | } else if opt.Username != "" { 65 | uv.Add("username", opt.Username) 66 | } 67 | if opt.Parse != "" { 68 | uv.Add("parse", opt.Parse) 69 | } 70 | if opt.LinkNames != "" { 71 | uv.Add("link_names", opt.LinkNames) 72 | } 73 | if opt.UnfurlLinks != "" { 74 | uv.Add("unfurl_links", opt.UnfurlLinks) 75 | } 76 | if opt.UnfurlMedia != "" { 77 | uv.Add("unfurl_media", opt.UnfurlMedia) 78 | } 79 | if opt.IconUrl != "" { 80 | uv.Add("icon_url", opt.IconUrl) 81 | } 82 | if opt.IconEmoji != "" { 83 | uv.Add("icon_emoji", opt.IconEmoji) 84 | } 85 | if opt.Attachments != nil && len(opt.Attachments) > 0 { 86 | ats, err := json.Marshal(opt.Attachments) 87 | if err != nil { 88 | return nil, err 89 | } 90 | uv.Add("attachments", string(ats)) 91 | } 92 | return uv, nil 93 | } 94 | -------------------------------------------------------------------------------- /commons.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | type Topic struct { 4 | Value string `json:"value"` 5 | Creator string `json:"creator"` 6 | LastSet int `json:"last_set"` 7 | } 8 | 9 | type Purpose struct { 10 | Value string `json:"value"` 11 | Creator string `json:"creator"` 12 | LastSet int `json:"last_set"` 13 | } 14 | 15 | type BaseAPIResponse struct { 16 | Ok bool `json:"ok"` 17 | Error string `json:"error"` 18 | } 19 | -------------------------------------------------------------------------------- /endpoints.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | const ( 4 | apiBaseUrl = "https://slack.com/api/" 5 | 6 | authTestApiEndpoint = "auth.test" 7 | 8 | channelsListApiEndpoint = "channels.list" 9 | channelsJoinApiEndpoint = "channels.join" 10 | channelsHistoryApiEndpoint = "channels.history" 11 | 12 | chatPostMessageApiEndpoint = "chat.postMessage" 13 | 14 | groupsInviteApiEndpoint = "groups.invite" 15 | groupsListApiEndpoint = "groups.list" 16 | groupsCreateApiEndpoint = "groups.create" 17 | 18 | imListApiEndpoint = "im.list" 19 | mpimListApiEndpoint = "mpim.list" 20 | 21 | filesUploadApiEndpoint = "files.upload" 22 | filesInfoApiEndpoint = "files.info" 23 | 24 | usersInfoApiEndpoint = "users.info" 25 | usersListApiEndpoint = "users.list" 26 | 27 | teamInfoApiEndpoint = "team.info" 28 | ) 29 | -------------------------------------------------------------------------------- /examples/assets/test.txt: -------------------------------------------------------------------------------- 1 | file upload test. -------------------------------------------------------------------------------- /examples/auth_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bluele/slack" 6 | ) 7 | 8 | const ( 9 | token = "your-api-token" 10 | ) 11 | 12 | func main() { 13 | api := slack.New(token) 14 | auth, err := api.AuthTest() 15 | if err != nil { 16 | panic(err) 17 | } 18 | fmt.Println(auth.Url) 19 | fmt.Println(auth.Team) 20 | fmt.Println(auth.User) 21 | } 22 | -------------------------------------------------------------------------------- /examples/channels_history.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bluele/slack" 6 | ) 7 | 8 | // Please change these values to suit your environment 9 | const ( 10 | token = "your-api-token" 11 | channelName = "general" 12 | ) 13 | 14 | func main() { 15 | api := slack.New(token) 16 | channel, err := api.FindChannelByName(channelName) 17 | if err != nil { 18 | panic(err) 19 | } 20 | msgs, err := api.ChannelsHistoryMessages(&slack.ChannelsHistoryOpt{ 21 | Channel: channel.Id, 22 | }) 23 | if err != nil { 24 | panic(err) 25 | } 26 | for _, msg := range msgs { 27 | fmt.Println(msg.UserId, msg.Text) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/channels_join.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bluele/slack" 5 | ) 6 | 7 | // Please change these values to suit your environment 8 | const ( 9 | token = "your-api-token" 10 | channelName = "general" 11 | ) 12 | 13 | func main() { 14 | api := slack.New(token) 15 | err := api.JoinChannel(channelName) 16 | if err != nil { 17 | panic(err) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/channels_list.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bluele/slack" 6 | ) 7 | 8 | const ( 9 | token = "your-api-token" 10 | ) 11 | 12 | func main() { 13 | api := slack.New(token) 14 | channels, err := api.ChannelsList() 15 | if err != nil { 16 | panic(err) 17 | } 18 | for _, channel := range channels { 19 | fmt.Println(channel.Id, channel.Name) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/chat_post_attachments.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bluele/slack" 5 | ) 6 | 7 | const ( 8 | token = "your-api-token" 9 | channelName = "general" 10 | ) 11 | 12 | func main() { 13 | api := slack.New(token) 14 | channel, err := api.FindChannelByName(channelName) 15 | if err != nil { 16 | panic(err) 17 | } 18 | err = api.ChatPostMessage(channel.Id, "Hello, world!", &slack.ChatPostMessageOpt{ 19 | Attachments: []*slack.Attachment{ 20 | {Text: "danger", Color: "danger"}, 21 | }, 22 | }) 23 | if err != nil { 24 | panic(err) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/chat_post_message.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bluele/slack" 5 | ) 6 | 7 | const ( 8 | token = "your-api-token" 9 | channelName = "general" 10 | ) 11 | 12 | func main() { 13 | api := slack.New(token) 14 | err := api.ChatPostMessage(channelName, "Hello, world!", nil) 15 | if err != nil { 16 | panic(err) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/groups_create.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bluele/slack" 5 | ) 6 | 7 | // Please change these values to suit your environment 8 | const ( 9 | token = "your-api-token" 10 | groupName = "create-group" 11 | ) 12 | 13 | func main() { 14 | api := slack.New(token) 15 | err := api.CreateGroup(groupName) 16 | if err != nil { 17 | panic(err) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/groups_invite.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bluele/slack" 5 | ) 6 | 7 | const ( 8 | token = "your-api-token" 9 | inviteUserName = "your-team-member" 10 | groupName = "your-group-name" 11 | ) 12 | 13 | func main() { 14 | api := slack.New(token) 15 | group, err := api.FindGroupByName(groupName) 16 | if err != nil { 17 | panic(err) 18 | } 19 | 20 | user, err := api.FindUser(func(user *slack.User) bool { 21 | return user.Name == inviteUserName 22 | }) 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | err = api.InviteGroup(group.Id, user.Id) 28 | if err != nil { 29 | panic(err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/groups_list.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bluele/slack" 6 | ) 7 | 8 | const ( 9 | token = "your-api-token" 10 | ) 11 | 12 | func main() { 13 | api := slack.New(token) 14 | groups, err := api.GroupsList() 15 | if err != nil { 16 | panic(err) 17 | } 18 | for _, group := range groups { 19 | fmt.Println(group.Id, group.Name) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/post_group_message.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bluele/slack" 5 | ) 6 | 7 | // Please change these values to suit your environment 8 | const ( 9 | token = "your-api-token" 10 | groupName = "group-name" 11 | ) 12 | 13 | func main() { 14 | api := slack.New(token) 15 | err := api.ChatPostMessage(groupName, "Hello, world!", &slack.ChatPostMessageOpt{AsUser: true}) 16 | if err != nil { 17 | panic(err) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/upload_file.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | 7 | "github.com/bluele/slack" 8 | ) 9 | 10 | // Please change these values to suit your environment 11 | const ( 12 | token = "your-api-token" 13 | channelName = "general" 14 | uploadFilePath = "./assets/test.txt" 15 | ) 16 | 17 | func main() { 18 | api := slack.New(token) 19 | channel, err := api.FindChannelByName(channelName) 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | info, err := api.FilesUpload(&slack.FilesUploadOpt{ 25 | Filepath: uploadFilePath, 26 | Filetype: "text", 27 | Filename: filepath.Base(uploadFilePath), 28 | Title: "upload test", 29 | Channels: []string{channel.Id}, 30 | }) 31 | 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | fmt.Println(fmt.Sprintf("Completed file upload with the ID: '%s'.", info.ID)) 37 | } 38 | -------------------------------------------------------------------------------- /examples/upload_file_info.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/bluele/slack" 7 | ) 8 | 9 | // Please change these values to suit your environment 10 | const ( 11 | token = "your-api-token" 12 | uploadedFileId = "previous-uploaded-file-id" 13 | ) 14 | 15 | func main() { 16 | api := slack.New(token) 17 | file, err := api.FindFile(uploadedFileId) 18 | 19 | if err != nil { 20 | panic(err) 21 | } 22 | 23 | fmt.Println("Information for uploaded file retrieved!") 24 | fmt.Println(file) 25 | } 26 | -------------------------------------------------------------------------------- /examples/users_info.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bluele/slack" 6 | ) 7 | 8 | // Please change these values to suit your environment 9 | const ( 10 | token = "your-api-token" 11 | userID = "userID" 12 | ) 13 | 14 | func main() { 15 | api := slack.New(token) 16 | user, err := api.UsersInfo(userID) 17 | if err != nil { 18 | panic(err) 19 | } 20 | fmt.Println(user.Name, user.Profile.Email) 21 | } 22 | -------------------------------------------------------------------------------- /examples/users_list.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bluele/slack" 6 | ) 7 | 8 | const ( 9 | token = "your-api-token" 10 | ) 11 | 12 | func main() { 13 | api := slack.New(token) 14 | users, err := api.UsersList() 15 | if err != nil { 16 | panic(err) 17 | } 18 | for _, user := range users { 19 | fmt.Println(user.Id, user.Name) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/webhook_post.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bluele/slack" 5 | ) 6 | 7 | // Please change these values to suit your environment 8 | const ( 9 | hookURL = "https://hooks.slack.com/services/xxxxxx/xxxxxx/xxxxxxxxxxxxxx" 10 | ) 11 | 12 | func main() { 13 | hook := slack.NewWebHook(hookURL) 14 | err := hook.PostMessage(&slack.WebHookPostPayload{ 15 | Text: "hello!", 16 | // Channel: "#test-channel", 17 | Attachments: []*slack.Attachment{ 18 | {Text: "danger", Color: "danger"}, 19 | }, 20 | }) 21 | if err != nil { 22 | panic(err) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /files.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "io" 8 | "mime/multipart" 9 | "net/http" 10 | "net/url" 11 | "os" 12 | "path/filepath" 13 | "strings" 14 | ) 15 | 16 | // API files.upload: Uploads or creates a file. 17 | func (sl *Slack) FilesUpload(opt *FilesUploadOpt) (file *UploadedFile, err error) { 18 | req, err := sl.createFilesUploadRequest(opt) 19 | 20 | if err != nil { 21 | return 22 | } 23 | 24 | body, err := sl.DoRequest(req) 25 | 26 | if err != nil { 27 | return 28 | } 29 | 30 | res := new(FilesUploadAPIResponse) 31 | err = json.Unmarshal(body, res) 32 | 33 | if err != nil { 34 | return 35 | } 36 | 37 | if res.Ok { 38 | file = &res.File 39 | } else { 40 | err = errors.New(res.Error) 41 | } 42 | 43 | return 44 | } 45 | 46 | // API files.info: Retrieves information about a specified uploaded file. 47 | func (sl *Slack) FindFile(id string) (file *UploadedFile, err error) { 48 | uv := sl.urlValues() 49 | uv.Add("file", id) 50 | 51 | body, err := sl.GetRequest(filesInfoApiEndpoint, uv) 52 | 53 | if err != nil { 54 | return 55 | } 56 | 57 | res := new(FilesUploadAPIResponse) 58 | err = json.Unmarshal(body, res) 59 | 60 | if err != nil { 61 | return 62 | } 63 | 64 | if res.Ok { 65 | file = &res.File 66 | } else { 67 | err = errors.New("File not found") 68 | } 69 | 70 | return 71 | } 72 | 73 | // option type for `files.upload` api 74 | type FilesUploadOpt struct { 75 | Content string 76 | Filepath string 77 | Filetype string 78 | Filename string 79 | Title string 80 | InitialComment string 81 | Channels []string 82 | } 83 | 84 | // response of `files.upload` api 85 | type FilesUploadAPIResponse struct { 86 | Ok bool `json:"ok"` 87 | Error string `json:"error"` 88 | File UploadedFile `json:"file"` 89 | } 90 | 91 | type UploadedFile struct { 92 | ID string `json:"id"` 93 | Title string `json:"title"` 94 | Name string `json:"name"` 95 | MimeType string `json:"mimetype"` 96 | FileType string `json:"filetype"` 97 | User string `json:"user"` 98 | PrivateUrl string `json:"url_private"` 99 | PrivateDownloadUrl string `json:"url_private_download"` 100 | Permalink string `json:"permalink"` 101 | PublicPermalink string `json:"permalink_public"` 102 | } 103 | 104 | func (sl *Slack) createFilesUploadRequest(opt *FilesUploadOpt) (*http.Request, error) { 105 | var body io.Reader 106 | 107 | uv := sl.urlValues() 108 | if opt == nil { 109 | return nil, errors.New("`opt *FilesUploadOpt` argument must be specified.") 110 | } 111 | contentType := "application/x-www-form-urlencoded" 112 | 113 | if opt.Filetype != "" { 114 | uv.Add("filetype", opt.Filetype) 115 | } 116 | if opt.Filename != "" { 117 | uv.Add("filename", opt.Filename) 118 | } 119 | if opt.Title != "" { 120 | uv.Add("title", opt.Title) 121 | } 122 | if opt.InitialComment != "" { 123 | uv.Add("initial_comment", opt.InitialComment) 124 | } 125 | if len(opt.Channels) != 0 { 126 | uv.Add("channels", strings.Join(opt.Channels, ",")) 127 | } 128 | 129 | if opt.Filepath != "" { 130 | var err error 131 | body, contentType, err = createFileParam("file", opt.Filepath) 132 | if err != nil { 133 | return nil, err 134 | } 135 | } else if opt.Content != "" { 136 | body = strings.NewReader(url.Values{"content": []string{opt.Content}}.Encode()) 137 | } 138 | 139 | req, err := http.NewRequest("POST", apiBaseUrl+filesUploadApiEndpoint, body) 140 | if err != nil { 141 | return nil, err 142 | } 143 | req.Header.Set("Content-Type", contentType) 144 | req.URL.RawQuery = (*uv).Encode() 145 | return req, nil 146 | } 147 | 148 | func createFileParam(param, path string) (*bytes.Buffer, string, error) { 149 | body := &bytes.Buffer{} 150 | writer := multipart.NewWriter(body) 151 | defer writer.Close() 152 | 153 | p, err := filepath.Abs(path) 154 | if err != nil { 155 | return nil, "", err 156 | } 157 | file, err := os.Open(p) 158 | if err != nil { 159 | return nil, "", err 160 | } 161 | defer file.Close() 162 | part, err := writer.CreateFormFile(param, filepath.Base(path)) 163 | if err != nil { 164 | return nil, "", err 165 | } 166 | 167 | _, err = io.Copy(part, file) 168 | if err != nil { 169 | return nil, "", err 170 | } 171 | 172 | return body, writer.FormDataContentType(), nil 173 | } 174 | -------------------------------------------------------------------------------- /groups.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | ) 7 | 8 | // API groups.list: Lists private groups that the calling user has access to. 9 | func (sl *Slack) GroupsList() ([]*Group, error) { 10 | uv := sl.urlValues() 11 | body, err := sl.GetRequest(groupsListApiEndpoint, uv) 12 | if err != nil { 13 | return nil, err 14 | } 15 | res := new(GroupsListAPIResponse) 16 | err = json.Unmarshal(body, res) 17 | if err != nil { 18 | return nil, err 19 | } 20 | if !res.Ok { 21 | return nil, errors.New(res.Error) 22 | } 23 | return res.Groups() 24 | } 25 | 26 | // API groups.create: Creates a private group. 27 | func (sl *Slack) CreateGroup(name string) error { 28 | uv := sl.urlValues() 29 | uv.Add("name", name) 30 | 31 | _, err := sl.GetRequest(groupsCreateApiEndpoint, uv) 32 | if err != nil { 33 | return err 34 | } 35 | return nil 36 | } 37 | 38 | // slack group type 39 | type Group struct { 40 | Id string `json:"id"` 41 | Name string `json:"name"` 42 | Created int `json:"created"` 43 | Creator string `json:"creator"` 44 | IsArchived bool `json:"is_archived"` 45 | Members []string `json:"members"` 46 | RawTopic json.RawMessage `json:"topic"` 47 | RawPurpose json.RawMessage `json:"purpose"` 48 | } 49 | 50 | // response type for `groups.list` api 51 | type GroupsListAPIResponse struct { 52 | BaseAPIResponse 53 | RawGroups json.RawMessage `json:"groups"` 54 | } 55 | 56 | // Groups returns a slice of group object from `groups.list` api. 57 | func (res *GroupsListAPIResponse) Groups() ([]*Group, error) { 58 | var groups []*Group 59 | err := json.Unmarshal(res.RawGroups, &groups) 60 | if err != nil { 61 | return nil, err 62 | } 63 | return groups, nil 64 | } 65 | 66 | // response type for `groups.create` api 67 | type GroupsCreateAPIResponse struct { 68 | BaseAPIResponse 69 | RawGroup json.RawMessage `json:"group"` 70 | } 71 | 72 | func (res *GroupsCreateAPIResponse) Group() (*Group, error) { 73 | group := Group{} 74 | err := json.Unmarshal(res.RawGroup, &group) 75 | if err != nil { 76 | return nil, err 77 | } 78 | return &group, nil 79 | } 80 | 81 | // FindGroup returns a group object that satisfy conditions specified. 82 | func (sl *Slack) FindGroup(cb func(*Group) bool) (*Group, error) { 83 | groups, err := sl.GroupsList() 84 | if err != nil { 85 | return nil, err 86 | } 87 | for _, group := range groups { 88 | if cb(group) { 89 | return group, nil 90 | } 91 | } 92 | return nil, errors.New("No such group.") 93 | } 94 | 95 | // FindGroupByName returns a group object that matches name specified. 96 | func (sl *Slack) FindGroupByName(name string) (*Group, error) { 97 | return sl.FindGroup(func(group *Group) bool { 98 | return group.Name == name 99 | }) 100 | } 101 | 102 | // API groups.invite: Invites a user to a private group. 103 | func (sl *Slack) InviteGroup(channelId, userId string) error { 104 | uv := sl.urlValues() 105 | uv.Add("channel", channelId) 106 | uv.Add("user", userId) 107 | 108 | _, err := sl.GetRequest(channelsJoinApiEndpoint, uv) 109 | if err != nil { 110 | return err 111 | } 112 | return nil 113 | } 114 | -------------------------------------------------------------------------------- /ims.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | ) 7 | 8 | // API im.list: Lists direct message channels for the calling user. 9 | func (sl *Slack) ImList() ([]*Im, error) { 10 | uv := sl.urlValues() 11 | body, err := sl.GetRequest(imListApiEndpoint, uv) 12 | if err != nil { 13 | return nil, err 14 | } 15 | res := new(ImListAPIResponse) 16 | err = json.Unmarshal(body, res) 17 | if err != nil { 18 | return nil, err 19 | } 20 | if !res.Ok { 21 | return nil, errors.New(res.Error) 22 | } 23 | return res.Ims() 24 | } 25 | 26 | // slack im type 27 | type Im struct { 28 | Id string `json:"id"` 29 | Isim bool `json:"is_im"` 30 | User string `json:"user"` 31 | Created int `json:"created"` 32 | IsUserDeleted bool `json:"is_user_deleted"` 33 | } 34 | 35 | // response type for `im.list` api 36 | type ImListAPIResponse struct { 37 | BaseAPIResponse 38 | RawIms json.RawMessage `json:"ims"` 39 | } 40 | 41 | // Ims returns a slice of im object from `im.list` api. 42 | func (res *ImListAPIResponse) Ims() ([]*Im, error) { 43 | var im []*Im 44 | err := json.Unmarshal(res.RawIms, &im) 45 | if err != nil { 46 | return nil, err 47 | } 48 | return im, nil 49 | } 50 | 51 | // FindIm returns a im object that satisfy conditions specified. 52 | func (sl *Slack) FindIm(cb func(*Im) bool) (*Im, error) { 53 | ims, err := sl.ImList() 54 | if err != nil { 55 | return nil, err 56 | } 57 | for _, im := range ims { 58 | if cb(im) { 59 | return im, nil 60 | } 61 | } 62 | return nil, errors.New("No such im.") 63 | } 64 | 65 | // FindImByName returns a im object that matches name specified. 66 | func (sl *Slack) FindImByName(name string) (*Im, error) { 67 | user, err := sl.FindUserByName(name) 68 | if err != nil { 69 | return nil, err 70 | } 71 | id := user.Id 72 | return sl.FindIm(func(im *Im) bool { 73 | return im.User == id 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /mpims.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | ) 7 | 8 | // API mpim.list: Lists multiparty direct message channels for the calling user. 9 | func (sl *Slack) MpImList() ([]*MpIm, error) { 10 | uv := sl.urlValues() 11 | body, err := sl.GetRequest(mpimListApiEndpoint, uv) 12 | if err != nil { 13 | return nil, err 14 | } 15 | res := new(MpImListAPIResponse) 16 | err = json.Unmarshal(body, res) 17 | if err != nil { 18 | return nil, err 19 | } 20 | if !res.Ok { 21 | return nil, errors.New(res.Error) 22 | } 23 | return res.MpIms() 24 | } 25 | 26 | // slack mpim type 27 | type MpIm struct { 28 | Id string `json:"id"` 29 | Name string `json:"name"` 30 | Created int64 `json:"created"` 31 | Creator string `json:"creator"` 32 | IsArchived bool `json:"is_archived"` 33 | IsMpim bool `json:"is_mpim"` 34 | Members []string `json:"members"` 35 | RawTopic json.RawMessage `json:"topic"` 36 | RawPurpose json.RawMessage `json:"purpose"` 37 | } 38 | 39 | // response type for `im.list` api 40 | type MpImListAPIResponse struct { 41 | BaseAPIResponse 42 | RawMpIms json.RawMessage `json:"groups"` 43 | } 44 | 45 | // MpIms returns a slice of mpim object from `mpim.list` api. 46 | func (res *MpImListAPIResponse) MpIms() ([]*MpIm, error) { 47 | var mpim []*MpIm 48 | err := json.Unmarshal(res.RawMpIms, &mpim) 49 | if err != nil { 50 | return nil, err 51 | } 52 | return mpim, nil 53 | } 54 | 55 | // FindMpIm returns a mpim object that satisfy conditions specified. 56 | func (sl *Slack) FindMpIm(cb func(*MpIm) bool) (*MpIm, error) { 57 | mpims, err := sl.MpImList() 58 | if err != nil { 59 | return nil, err 60 | } 61 | for _, mpim := range mpims { 62 | if cb(mpim) { 63 | return mpim, nil 64 | } 65 | } 66 | return nil, errors.New("No such mpim.") 67 | } 68 | -------------------------------------------------------------------------------- /slack.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "net/url" 5 | ) 6 | 7 | type Slack struct { 8 | token string 9 | } 10 | 11 | // Create a slack client with an API token. 12 | func New(token string) *Slack { 13 | return &Slack{ 14 | token: token, 15 | } 16 | } 17 | 18 | func (sl *Slack) urlValues() *url.Values { 19 | uv := url.Values{} 20 | uv.Add("token", sl.token) 21 | return &uv 22 | } 23 | -------------------------------------------------------------------------------- /team.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | ) 7 | 8 | type TeamInfo struct { 9 | Id string `json:"id"` 10 | Name string `json:"name"` 11 | Domain string `json:"domain"` 12 | EmailDomain string `json:"email_domain"` 13 | Icon *Icon `json:"icon"` 14 | } 15 | 16 | type Icon struct { 17 | Image34 string `json:"image_34"` 18 | Image44 string `json:"image_44"` 19 | Image68 string `json:"image_68"` 20 | Image88 string `json:"image_88"` 21 | Image102 string `json:"image_102"` 22 | Image132 string `json:"image_132"` 23 | ImageDefault bool `json:"image_default"` 24 | } 25 | 26 | type TeamInfoResponse struct { 27 | BaseAPIResponse 28 | *TeamInfo `json:"team"` 29 | } 30 | 31 | func (sl *Slack) TeamInfo() (*TeamInfo, error) { 32 | uv := sl.urlValues() 33 | body, err := sl.GetRequest(teamInfoApiEndpoint, uv) 34 | if err != nil { 35 | return nil, err 36 | } 37 | res := new(TeamInfoResponse) 38 | err = json.Unmarshal(body, res) 39 | if err != nil { 40 | return nil, err 41 | } 42 | if !res.Ok { 43 | return nil, errors.New(res.Error) 44 | } 45 | return res.TeamInfo, nil 46 | } 47 | -------------------------------------------------------------------------------- /users.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | ) 7 | 8 | // slack user type 9 | type User struct { 10 | Id string `json:"id"` 11 | Name string `json:"name"` 12 | Deleted bool `json:"deleted"` 13 | Color string `json:"color"` 14 | Profile *ProfileInfo 15 | IsAdmin bool `json:"is_admin"` 16 | IsOwner bool `json:"is_owner"` 17 | Has2fa bool `json:"has_2fa"` 18 | HasFiles bool `json:"has_files"` 19 | } 20 | 21 | // slack user profile type 22 | type ProfileInfo struct { 23 | FirstName string `json:"first_name"` 24 | LastName string `json:"last_name"` 25 | RealName string `json:"real_name"` 26 | Email string `json:"email"` 27 | Skype string `json:"skype"` 28 | Phone string `json:"phone"` 29 | Image24 string `json:"image_24"` 30 | Image32 string `json:"image_32"` 31 | Image48 string `json:"image_48"` 32 | Image72 string `json:"image_72"` 33 | Image192 string `json:"image_192"` 34 | } 35 | 36 | // API users.list: Lists all users in a Slack team. 37 | func (sl *Slack) UsersList() ([]*User, error) { 38 | uv := sl.urlValues() 39 | body, err := sl.GetRequest(usersListApiEndpoint, uv) 40 | if err != nil { 41 | return nil, err 42 | } 43 | res := new(UsersListAPIResponse) 44 | err = json.Unmarshal(body, res) 45 | if err != nil { 46 | return nil, err 47 | } 48 | if !res.Ok { 49 | return nil, errors.New(res.Error) 50 | } 51 | return res.Members() 52 | } 53 | 54 | // response type of `users.list` api 55 | type UsersListAPIResponse struct { 56 | BaseAPIResponse 57 | RawMembers json.RawMessage `json:"members"` 58 | } 59 | 60 | func (res *UsersListAPIResponse) Members() ([]*User, error) { 61 | var members []*User 62 | err := json.Unmarshal(res.RawMembers, &members) 63 | if err != nil { 64 | return nil, err 65 | } 66 | return members, nil 67 | } 68 | 69 | // FindUser returns a user object that satisfy conditions specified. 70 | func (sl *Slack) FindUser(cb func(*User) bool) (*User, error) { 71 | members, err := sl.UsersList() 72 | if err != nil { 73 | return nil, err 74 | } 75 | for _, member := range members { 76 | if cb(member) { 77 | return member, nil 78 | } 79 | } 80 | return nil, errors.New("No such user.") 81 | } 82 | 83 | // FindUserByName returns a user object that matches name specified. 84 | func (sl *Slack) FindUserByName(name string) (*User, error) { 85 | return sl.FindUser(func(user *User) bool { 86 | return user.Name == name 87 | }) 88 | } 89 | 90 | // response type of `users.info` api 91 | type UsersInfoAPIResponse struct { 92 | BaseAPIResponse 93 | User *User `json:"user"` 94 | } 95 | 96 | // API users.info: Gets information about a user. 97 | func (sl *Slack) UsersInfo(userId string) (*User, error) { 98 | uv := sl.urlValues() 99 | uv.Add("user", userId) 100 | 101 | body, err := sl.GetRequest(usersInfoApiEndpoint, uv) 102 | if err != nil { 103 | return nil, err 104 | } 105 | res := new(UsersInfoAPIResponse) 106 | err = json.Unmarshal(body, res) 107 | if err != nil { 108 | return nil, err 109 | } 110 | if !res.Ok { 111 | return nil, errors.New(res.Error) 112 | } 113 | return res.User, nil 114 | } 115 | -------------------------------------------------------------------------------- /webhook.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "io/ioutil" 8 | "net/http" 9 | ) 10 | 11 | type WebHook struct { 12 | hookURL string 13 | } 14 | 15 | type WebHookPostPayload struct { 16 | Text string `json:"text,omitempty"` 17 | Channel string `json:"channel,omitempty"` 18 | Username string `json:"username,omitempty"` 19 | IconUrl string `json:"icon_url,omitempty"` 20 | IconEmoji string `json:"icon_emoji,omitempty"` 21 | UnfurlLinks bool `json:"unfurl_links,omitempty"` 22 | LinkNames string `json:"link_names,omitempty"` 23 | Attachments []*Attachment `json:"attachments,omitempty"` 24 | } 25 | 26 | func NewWebHook(hookURL string) *WebHook { 27 | return &WebHook{hookURL} 28 | } 29 | 30 | func (hk *WebHook) PostMessage(payload *WebHookPostPayload) error { 31 | body, err := json.Marshal(payload) 32 | if err != nil { 33 | return err 34 | } 35 | resp, err := http.Post(hk.hookURL, "application/json", bytes.NewReader(body)) 36 | if err != nil { 37 | return err 38 | } 39 | defer resp.Body.Close() 40 | 41 | if resp.StatusCode != 200 { 42 | t, _ := ioutil.ReadAll(resp.Body) 43 | return errors.New(string(t)) 44 | } 45 | 46 | return nil 47 | } 48 | --------------------------------------------------------------------------------