1package slack 2 3import ( 4 "context" 5 "net/url" 6 "strconv" 7) 8 9// Group contains all the information for a group 10type Group struct { 11 GroupConversation 12 IsGroup bool `json:"is_group"` 13} 14 15type groupResponseFull struct { 16 Group Group `json:"group"` 17 Groups []Group `json:"groups"` 18 Purpose string `json:"purpose"` 19 Topic string `json:"topic"` 20 NotInGroup bool `json:"not_in_group"` 21 NoOp bool `json:"no_op"` 22 AlreadyClosed bool `json:"already_closed"` 23 AlreadyOpen bool `json:"already_open"` 24 AlreadyInGroup bool `json:"already_in_group"` 25 Channel Channel `json:"channel"` 26 History 27 SlackResponse 28} 29 30func (api *Client) groupRequest(ctx context.Context, path string, values url.Values) (*groupResponseFull, error) { 31 response := &groupResponseFull{} 32 err := api.postMethod(ctx, path, values, response) 33 if err != nil { 34 return nil, err 35 } 36 37 return response, response.Err() 38} 39 40// ArchiveGroup archives a private group 41func (api *Client) ArchiveGroup(group string) error { 42 return api.ArchiveGroupContext(context.Background(), group) 43} 44 45// ArchiveGroupContext archives a private group 46func (api *Client) ArchiveGroupContext(ctx context.Context, group string) error { 47 values := url.Values{ 48 "token": {api.token}, 49 "channel": {group}, 50 } 51 52 _, err := api.groupRequest(ctx, "groups.archive", values) 53 return err 54} 55 56// UnarchiveGroup unarchives a private group 57func (api *Client) UnarchiveGroup(group string) error { 58 return api.UnarchiveGroupContext(context.Background(), group) 59} 60 61// UnarchiveGroupContext unarchives a private group 62func (api *Client) UnarchiveGroupContext(ctx context.Context, group string) error { 63 values := url.Values{ 64 "token": {api.token}, 65 "channel": {group}, 66 } 67 68 _, err := api.groupRequest(ctx, "groups.unarchive", values) 69 return err 70} 71 72// CreateGroup creates a private group 73func (api *Client) CreateGroup(group string) (*Group, error) { 74 return api.CreateGroupContext(context.Background(), group) 75} 76 77// CreateGroupContext creates a private group 78func (api *Client) CreateGroupContext(ctx context.Context, group string) (*Group, error) { 79 values := url.Values{ 80 "token": {api.token}, 81 "name": {group}, 82 } 83 84 response, err := api.groupRequest(ctx, "groups.create", values) 85 if err != nil { 86 return nil, err 87 } 88 return &response.Group, nil 89} 90 91// CreateChildGroup creates a new private group archiving the old one 92// This method takes an existing private group and performs the following steps: 93// 1. Renames the existing group (from "example" to "example-archived"). 94// 2. Archives the existing group. 95// 3. Creates a new group with the name of the existing group. 96// 4. Adds all members of the existing group to the new group. 97func (api *Client) CreateChildGroup(group string) (*Group, error) { 98 return api.CreateChildGroupContext(context.Background(), group) 99} 100 101// CreateChildGroupContext creates a new private group archiving the old one with a custom context 102// For more information see CreateChildGroup 103func (api *Client) CreateChildGroupContext(ctx context.Context, group string) (*Group, error) { 104 values := url.Values{ 105 "token": {api.token}, 106 "channel": {group}, 107 } 108 109 response, err := api.groupRequest(ctx, "groups.createChild", values) 110 if err != nil { 111 return nil, err 112 } 113 return &response.Group, nil 114} 115 116// GetGroupHistory fetches all the history for a private group 117func (api *Client) GetGroupHistory(group string, params HistoryParameters) (*History, error) { 118 return api.GetGroupHistoryContext(context.Background(), group, params) 119} 120 121// GetGroupHistoryContext fetches all the history for a private group with a custom context 122func (api *Client) GetGroupHistoryContext(ctx context.Context, group string, params HistoryParameters) (*History, error) { 123 values := url.Values{ 124 "token": {api.token}, 125 "channel": {group}, 126 } 127 if params.Latest != DEFAULT_HISTORY_LATEST { 128 values.Add("latest", params.Latest) 129 } 130 if params.Oldest != DEFAULT_HISTORY_OLDEST { 131 values.Add("oldest", params.Oldest) 132 } 133 if params.Count != DEFAULT_HISTORY_COUNT { 134 values.Add("count", strconv.Itoa(params.Count)) 135 } 136 if params.Inclusive != DEFAULT_HISTORY_INCLUSIVE { 137 if params.Inclusive { 138 values.Add("inclusive", "1") 139 } else { 140 values.Add("inclusive", "0") 141 } 142 } 143 if params.Unreads != DEFAULT_HISTORY_UNREADS { 144 if params.Unreads { 145 values.Add("unreads", "1") 146 } else { 147 values.Add("unreads", "0") 148 } 149 } 150 151 response, err := api.groupRequest(ctx, "groups.history", values) 152 if err != nil { 153 return nil, err 154 } 155 return &response.History, nil 156} 157 158// InviteUserToGroup invites a specific user to a private group 159func (api *Client) InviteUserToGroup(group, user string) (*Group, bool, error) { 160 return api.InviteUserToGroupContext(context.Background(), group, user) 161} 162 163// InviteUserToGroupContext invites a specific user to a private group with a custom context 164func (api *Client) InviteUserToGroupContext(ctx context.Context, group, user string) (*Group, bool, error) { 165 values := url.Values{ 166 "token": {api.token}, 167 "channel": {group}, 168 "user": {user}, 169 } 170 171 response, err := api.groupRequest(ctx, "groups.invite", values) 172 if err != nil { 173 return nil, false, err 174 } 175 return &response.Group, response.AlreadyInGroup, nil 176} 177 178// LeaveGroup makes authenticated user leave the group 179func (api *Client) LeaveGroup(group string) error { 180 return api.LeaveGroupContext(context.Background(), group) 181} 182 183// LeaveGroupContext makes authenticated user leave the group with a custom context 184func (api *Client) LeaveGroupContext(ctx context.Context, group string) (err error) { 185 values := url.Values{ 186 "token": {api.token}, 187 "channel": {group}, 188 } 189 190 _, err = api.groupRequest(ctx, "groups.leave", values) 191 return err 192} 193 194// KickUserFromGroup kicks a user from a group 195func (api *Client) KickUserFromGroup(group, user string) error { 196 return api.KickUserFromGroupContext(context.Background(), group, user) 197} 198 199// KickUserFromGroupContext kicks a user from a group with a custom context 200func (api *Client) KickUserFromGroupContext(ctx context.Context, group, user string) (err error) { 201 values := url.Values{ 202 "token": {api.token}, 203 "channel": {group}, 204 "user": {user}, 205 } 206 207 _, err = api.groupRequest(ctx, "groups.kick", values) 208 return err 209} 210 211// GetGroups retrieves all groups 212func (api *Client) GetGroups(excludeArchived bool) ([]Group, error) { 213 return api.GetGroupsContext(context.Background(), excludeArchived) 214} 215 216// GetGroupsContext retrieves all groups with a custom context 217func (api *Client) GetGroupsContext(ctx context.Context, excludeArchived bool) ([]Group, error) { 218 values := url.Values{ 219 "token": {api.token}, 220 } 221 if excludeArchived { 222 values.Add("exclude_archived", "1") 223 } 224 225 response, err := api.groupRequest(ctx, "groups.list", values) 226 if err != nil { 227 return nil, err 228 } 229 return response.Groups, nil 230} 231 232// GetGroupInfo retrieves the given group 233func (api *Client) GetGroupInfo(group string) (*Group, error) { 234 return api.GetGroupInfoContext(context.Background(), group) 235} 236 237// GetGroupInfoContext retrieves the given group with a custom context 238func (api *Client) GetGroupInfoContext(ctx context.Context, group string) (*Group, error) { 239 values := url.Values{ 240 "token": {api.token}, 241 "channel": {group}, 242 "include_locale": {strconv.FormatBool(true)}, 243 } 244 245 response, err := api.groupRequest(ctx, "groups.info", values) 246 if err != nil { 247 return nil, err 248 } 249 return &response.Group, nil 250} 251 252// SetGroupReadMark sets the read mark on a private group 253// Clients should try to avoid making this call too often. When needing to mark a read position, a client should set a 254// timer before making the call. In this way, any further updates needed during the timeout will not generate extra 255// calls (just one per channel). This is useful for when reading scroll-back history, or following a busy live 256// channel. A timeout of 5 seconds is a good starting point. Be sure to flush these calls on shutdown/logout. 257func (api *Client) SetGroupReadMark(group, ts string) error { 258 return api.SetGroupReadMarkContext(context.Background(), group, ts) 259} 260 261// SetGroupReadMarkContext sets the read mark on a private group with a custom context 262// For more details see SetGroupReadMark 263func (api *Client) SetGroupReadMarkContext(ctx context.Context, group, ts string) (err error) { 264 values := url.Values{ 265 "token": {api.token}, 266 "channel": {group}, 267 "ts": {ts}, 268 } 269 270 _, err = api.groupRequest(ctx, "groups.mark", values) 271 return err 272} 273 274// OpenGroup opens a private group 275func (api *Client) OpenGroup(group string) (bool, bool, error) { 276 return api.OpenGroupContext(context.Background(), group) 277} 278 279// OpenGroupContext opens a private group with a custom context 280func (api *Client) OpenGroupContext(ctx context.Context, group string) (bool, bool, error) { 281 values := url.Values{ 282 "token": {api.token}, 283 "channel": {group}, 284 } 285 286 response, err := api.groupRequest(ctx, "groups.open", values) 287 if err != nil { 288 return false, false, err 289 } 290 return response.NoOp, response.AlreadyOpen, nil 291} 292 293// RenameGroup renames a group 294// XXX: They return a channel, not a group. What is this crap? :( 295// Inconsistent api it seems. 296func (api *Client) RenameGroup(group, name string) (*Channel, error) { 297 return api.RenameGroupContext(context.Background(), group, name) 298} 299 300// RenameGroupContext renames a group with a custom context 301func (api *Client) RenameGroupContext(ctx context.Context, group, name string) (*Channel, error) { 302 values := url.Values{ 303 "token": {api.token}, 304 "channel": {group}, 305 "name": {name}, 306 } 307 308 // XXX: the created entry in this call returns a string instead of a number 309 // so I may have to do some workaround to solve it. 310 response, err := api.groupRequest(ctx, "groups.rename", values) 311 if err != nil { 312 return nil, err 313 } 314 return &response.Channel, nil 315} 316 317// SetGroupPurpose sets the group purpose 318func (api *Client) SetGroupPurpose(group, purpose string) (string, error) { 319 return api.SetGroupPurposeContext(context.Background(), group, purpose) 320} 321 322// SetGroupPurposeContext sets the group purpose with a custom context 323func (api *Client) SetGroupPurposeContext(ctx context.Context, group, purpose string) (string, error) { 324 values := url.Values{ 325 "token": {api.token}, 326 "channel": {group}, 327 "purpose": {purpose}, 328 } 329 330 response, err := api.groupRequest(ctx, "groups.setPurpose", values) 331 if err != nil { 332 return "", err 333 } 334 return response.Purpose, nil 335} 336 337// SetGroupTopic sets the group topic 338func (api *Client) SetGroupTopic(group, topic string) (string, error) { 339 return api.SetGroupTopicContext(context.Background(), group, topic) 340} 341 342// SetGroupTopicContext sets the group topic with a custom context 343func (api *Client) SetGroupTopicContext(ctx context.Context, group, topic string) (string, error) { 344 values := url.Values{ 345 "token": {api.token}, 346 "channel": {group}, 347 "topic": {topic}, 348 } 349 350 response, err := api.groupRequest(ctx, "groups.setTopic", values) 351 if err != nil { 352 return "", err 353 } 354 return response.Topic, nil 355} 356