mirror of
https://github.com/valitydev/botkube.git
synced 2024-11-06 08:25:19 +00:00
Vendoring: Update nlopes/slack dep to master
Signed-off-by: Prasad Ghangal <prasad.ghangal@gmail.com>
This commit is contained in:
parent
a0e49d5bc2
commit
ec65f308d1
2
go.mod
2
go.mod
@ -28,7 +28,7 @@ require (
|
||||
github.com/mattermost/mattermost-server v5.11.1+incompatible
|
||||
github.com/mattn/go-sqlite3 v1.11.0 // indirect
|
||||
github.com/nicksnyder/go-i18n v1.10.1 // indirect
|
||||
github.com/nlopes/slack v0.4.0
|
||||
github.com/nlopes/slack v0.5.1-0.20190623232825-2891986e2a3e
|
||||
github.com/olivere/elastic v6.2.21+incompatible
|
||||
github.com/pborman/uuid v1.2.0 // indirect
|
||||
github.com/pkg/errors v0.8.1 // indirect
|
||||
|
4
go.sum
4
go.sum
@ -68,6 +68,7 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsC
|
||||
github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0=
|
||||
github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
|
||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
@ -125,6 +126,8 @@ github.com/nicksnyder/go-i18n v1.10.1 h1:isfg77E/aCD7+0lD/D00ebR2MV5vgeQ276WYyDa
|
||||
github.com/nicksnyder/go-i18n v1.10.1/go.mod h1:e4Di5xjP9oTVrC6y3C7C0HoSYXjSbhh/dU0eUV32nB4=
|
||||
github.com/nlopes/slack v0.4.0 h1:OVnHm7lv5gGT5gkcHsZAyw++oHVFihbjWbL3UceUpiA=
|
||||
github.com/nlopes/slack v0.4.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
|
||||
github.com/nlopes/slack v0.5.1-0.20190623232825-2891986e2a3e h1:4X/3/ywN6f+XITaDoI9GjXbF7MHk7fuunXsEWZX1GLc=
|
||||
github.com/nlopes/slack v0.5.1-0.20190623232825-2891986e2a3e/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk=
|
||||
github.com/olivere/elastic v6.2.21+incompatible h1:QnTuofzxOCV5FrYLywjkMxOmOWhAeild1VXxKRksK9Y=
|
||||
github.com/olivere/elastic v6.2.21+incompatible/go.mod h1:J+q1zQJTgAz9woqsbVRqGeB5G1iqDKVBWLNSYW8yfJ8=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@ -140,6 +143,7 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
14
vendor/github.com/nlopes/slack/.gometalinter.json
generated
vendored
Normal file
14
vendor/github.com/nlopes/slack/.gometalinter.json
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"DisableAll": true,
|
||||
"Enable": [
|
||||
"structcheck",
|
||||
"vet",
|
||||
"misspell",
|
||||
"unconvert",
|
||||
"interfacer",
|
||||
"goimports"
|
||||
],
|
||||
"Vendor": true,
|
||||
"Exclude": ["vendor"],
|
||||
"Deadline": "300s"
|
||||
}
|
32
vendor/github.com/nlopes/slack/.travis.yml
generated
vendored
32
vendor/github.com/nlopes/slack/.travis.yml
generated
vendored
@ -1,21 +1,35 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- tip
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
install: true
|
||||
|
||||
before_install:
|
||||
- export PATH=$HOME/gopath/bin:$PATH
|
||||
# install gometalinter
|
||||
- curl -L https://git.io/vp6lP | sh
|
||||
|
||||
script:
|
||||
- go test -race ./...
|
||||
- go test -cover ./...
|
||||
- PATH=$PWD/bin:$PATH gometalinter ./...
|
||||
- go test -race -cover ./...
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
allow_failures:
|
||||
- go: tip
|
||||
include:
|
||||
- go: "1.7.x"
|
||||
script: go test -v ./...
|
||||
- go: "1.8.x"
|
||||
script: go test -v ./...
|
||||
- go: "1.9.x"
|
||||
script: go test -v ./...
|
||||
- go: "1.10.x"
|
||||
script: go test -v ./...
|
||||
- go: "1.11.x"
|
||||
script: go test -v -mod=vendor ./...
|
||||
- go: "tip"
|
||||
script: go test -v -mod=vendor ./...
|
||||
|
||||
git:
|
||||
depth: 10
|
||||
|
5
vendor/github.com/nlopes/slack/CHANGELOG.md
generated
vendored
5
vendor/github.com/nlopes/slack/CHANGELOG.md
generated
vendored
@ -1,3 +1,8 @@
|
||||
### v0.5.0 - January 20, 2019
|
||||
full differences can be viewed using `git log --oneline --decorate --color v0.4.0..v0.5.0`
|
||||
- Breaking changes: various old struct fields have been removed or updated to match slack's api.
|
||||
- deadlock fix in RTM disconnect.
|
||||
|
||||
### v0.4.0 - October 06, 2018
|
||||
full differences can be viewed using `git log --oneline --decorate --color v0.3.0..v0.4.0`
|
||||
- Breaking Change: renamed ApplyMessageOption, to mark it as unsafe,
|
||||
|
39
vendor/github.com/nlopes/slack/Gopkg.lock
generated
vendored
39
vendor/github.com/nlopes/slack/Gopkg.lock
generated
vendored
@ -1,39 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
packages = ["."]
|
||||
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = ["assert"]
|
||||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
|
||||
version = "v1.2.2"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "596fa546322c2a1e9708a10c9f39aca2e04792b477fab86fb2899fbaab776070"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
17
vendor/github.com/nlopes/slack/Gopkg.toml
generated
vendored
17
vendor/github.com/nlopes/slack/Gopkg.toml
generated
vendored
@ -1,17 +0,0 @@
|
||||
ignored = ["github.com/lusis/slack-test"]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
version = "1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/errors"
|
||||
version = "0.8.0"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
14
vendor/github.com/nlopes/slack/README.md
generated
vendored
14
vendor/github.com/nlopes/slack/README.md
generated
vendored
@ -9,18 +9,10 @@ a fully managed way.
|
||||
|
||||
|
||||
|
||||
## Change log
|
||||
Support for the EventsAPI has recently been added. It is still in its early stages but nearly all events have been added and tested (except for those events in [Developer Preview](https://api.slack.com/slack-apps-preview) mode). API stability for events is not promised at this time.
|
||||
|
||||
### v0.2.0 - Feb 10, 2018
|
||||
## Changelog
|
||||
|
||||
Release adds a bunch of functionality and improvements, mainly to give people a recent version to vendor against.
|
||||
|
||||
Please check [0.2.0](https://github.com/nlopes/slack/releases/tag/v0.2.0)
|
||||
|
||||
### CHANGELOG.md
|
||||
|
||||
[CHANGELOG.md](https://github.com/nlopes/slack/blob/master/CHANGELOG.md) is available. Please visit it for updates.
|
||||
[CHANGELOG.md](https://github.com/nlopes/slack/blob/master/CHANGELOG.md) is available. Please visit it for updates.
|
||||
|
||||
## Installing
|
||||
|
||||
@ -43,7 +35,7 @@ func main() {
|
||||
api := slack.New("YOUR_TOKEN_HERE")
|
||||
// If you set debugging, it will log all requests to the console
|
||||
// Useful when encountering issues
|
||||
// api.SetDebug(true)
|
||||
// slack.New("YOUR_TOKEN_HERE", slack.OptionDebug(true))
|
||||
groups, err := api.GetGroups(false)
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
|
49
vendor/github.com/nlopes/slack/admin.go
generated
vendored
49
vendor/github.com/nlopes/slack/admin.go
generated
vendored
@ -2,28 +2,19 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type adminResponse struct {
|
||||
OK bool `json:"ok"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func adminRequest(ctx context.Context, client HTTPRequester, method string, teamName string, values url.Values, debug bool) (*adminResponse, error) {
|
||||
adminResponse := &adminResponse{}
|
||||
err := parseAdminResponse(ctx, client, method, teamName, values, adminResponse, debug)
|
||||
func (api *Client) adminRequest(ctx context.Context, method string, teamName string, values url.Values) error {
|
||||
resp := &SlackResponse{}
|
||||
err := parseAdminResponse(ctx, api.httpclient, method, teamName, values, resp, api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
if !adminResponse.OK {
|
||||
return nil, errors.New(adminResponse.Error)
|
||||
}
|
||||
|
||||
return adminResponse, nil
|
||||
return resp.Err()
|
||||
}
|
||||
|
||||
// DisableUser disabled a user account, given a user ID
|
||||
@ -40,9 +31,8 @@ func (api *Client) DisableUserContext(ctx context.Context, teamName string, uid
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, api.httpclient, "setInactive", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to disable user with id '%s': %s", uid, err)
|
||||
if err := api.adminRequest(ctx, "setInactive", teamName, values); err != nil {
|
||||
return fmt.Errorf("failed to disable user with id '%s': %s", uid, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -67,7 +57,7 @@ func (api *Client) InviteGuestContext(ctx context.Context, teamName, channel, fi
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug)
|
||||
err := api.adminRequest(ctx, "invite", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to invite single-channel guest: %s", err)
|
||||
}
|
||||
@ -94,7 +84,7 @@ func (api *Client) InviteRestrictedContext(ctx context.Context, teamName, channe
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug)
|
||||
err := api.adminRequest(ctx, "invite", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to restricted account: %s", err)
|
||||
}
|
||||
@ -118,7 +108,7 @@ func (api *Client) InviteToTeamContext(ctx context.Context, teamName, firstName,
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug)
|
||||
err := api.adminRequest(ctx, "invite", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to invite to team: %s", err)
|
||||
}
|
||||
@ -140,7 +130,7 @@ func (api *Client) SetRegularContext(ctx context.Context, teamName, user string)
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, api.httpclient, "setRegular", teamName, values, api.debug)
|
||||
err := api.adminRequest(ctx, "setRegular", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to change the user (%s) to a regular user: %s", user, err)
|
||||
}
|
||||
@ -162,7 +152,7 @@ func (api *Client) SendSSOBindingEmailContext(ctx context.Context, teamName, use
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, api.httpclient, "sendSSOBind", teamName, values, api.debug)
|
||||
err := api.adminRequest(ctx, "sendSSOBind", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to send SSO binding email for user (%s): %s", user, err)
|
||||
}
|
||||
@ -185,7 +175,7 @@ func (api *Client) SetUltraRestrictedContext(ctx context.Context, teamName, uid,
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, api.httpclient, "setUltraRestricted", teamName, values, api.debug)
|
||||
err := api.adminRequest(ctx, "setUltraRestricted", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to ultra-restrict account: %s", err)
|
||||
}
|
||||
@ -194,22 +184,23 @@ func (api *Client) SetUltraRestrictedContext(ctx context.Context, teamName, uid,
|
||||
}
|
||||
|
||||
// SetRestricted converts a user into a restricted account
|
||||
func (api *Client) SetRestricted(teamName, uid string) error {
|
||||
return api.SetRestrictedContext(context.Background(), teamName, uid)
|
||||
func (api *Client) SetRestricted(teamName, uid string, channelIds ...string) error {
|
||||
return api.SetRestrictedContext(context.Background(), teamName, uid, channelIds...)
|
||||
}
|
||||
|
||||
// SetRestrictedContext converts a user into a restricted account with a custom context
|
||||
func (api *Client) SetRestrictedContext(ctx context.Context, teamName, uid string) error {
|
||||
func (api *Client) SetRestrictedContext(ctx context.Context, teamName, uid string, channelIds ...string) error {
|
||||
values := url.Values{
|
||||
"user": {uid},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
"channels": {strings.Join(channelIds, ",")},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, api.httpclient, "setRestricted", teamName, values, api.debug)
|
||||
err := api.adminRequest(ctx, "setRestricted", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to restrict account: %s", err)
|
||||
return fmt.Errorf("failed to restrict account: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
28
vendor/github.com/nlopes/slack/attachments.go
generated
vendored
28
vendor/github.com/nlopes/slack/attachments.go
generated
vendored
@ -17,7 +17,7 @@ type AttachmentAction struct {
|
||||
Name string `json:"name"` // Required.
|
||||
Text string `json:"text"` // Required.
|
||||
Style string `json:"style,omitempty"` // Optional. Allowed values: "default", "primary", "danger".
|
||||
Type string `json:"type"` // Required. Must be set to "button" or "select".
|
||||
Type actionType `json:"type"` // Required. Must be set to "button" or "select".
|
||||
Value string `json:"value,omitempty"` // Optional.
|
||||
DataSource string `json:"data_source,omitempty"` // Optional.
|
||||
MinQueryLength int `json:"min_query_length,omitempty"` // Optional. Default value is 1.
|
||||
@ -28,6 +28,11 @@ type AttachmentAction struct {
|
||||
URL string `json:"url,omitempty"` // Optional.
|
||||
}
|
||||
|
||||
// actionType returns the type of the action
|
||||
func (a AttachmentAction) actionType() actionType {
|
||||
return a.Type
|
||||
}
|
||||
|
||||
// AttachmentActionOption the individual option to appear in action menu.
|
||||
type AttachmentActionOption struct {
|
||||
Text string `json:"text"` // Required.
|
||||
@ -42,25 +47,8 @@ type AttachmentActionOptionGroup struct {
|
||||
}
|
||||
|
||||
// AttachmentActionCallback is sent from Slack when a user clicks a button in an interactive message (aka AttachmentAction)
|
||||
type AttachmentActionCallback struct {
|
||||
Actions []AttachmentAction `json:"actions"`
|
||||
CallbackID string `json:"callback_id"`
|
||||
Team Team `json:"team"`
|
||||
Channel Channel `json:"channel"`
|
||||
User User `json:"user"`
|
||||
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
|
||||
OriginalMessage Message `json:"original_message"`
|
||||
|
||||
ActionTs string `json:"action_ts"`
|
||||
MessageTs string `json:"message_ts"`
|
||||
AttachmentID string `json:"attachment_id"`
|
||||
Token string `json:"token"`
|
||||
ResponseURL string `json:"response_url"`
|
||||
TriggerID string `json:"trigger_id"`
|
||||
}
|
||||
// DEPRECATED: use InteractionCallback
|
||||
type AttachmentActionCallback InteractionCallback
|
||||
|
||||
// ConfirmationField are used to ask users to confirm actions
|
||||
type ConfirmationField struct {
|
||||
|
40
vendor/github.com/nlopes/slack/auth.go
generated
vendored
Normal file
40
vendor/github.com/nlopes/slack/auth.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// AuthRevokeResponse contains our Auth response from the auth.revoke endpoint
|
||||
type AuthRevokeResponse struct {
|
||||
SlackResponse // Contains the "ok", and "Error", if any
|
||||
Revoked bool `json:"revoked,omitempty"`
|
||||
}
|
||||
|
||||
// authRequest sends the actual request, and unmarshals the response
|
||||
func (api *Client) authRequest(ctx context.Context, path string, values url.Values) (*AuthRevokeResponse, error) {
|
||||
response := &AuthRevokeResponse{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// SendAuthRevoke will send a revocation for our token
|
||||
func (api *Client) SendAuthRevoke(token string) (*AuthRevokeResponse, error) {
|
||||
return api.SendAuthRevokeContext(context.Background(), token)
|
||||
}
|
||||
|
||||
// SendAuthRevokeContext will retrieve the satus from api.test
|
||||
func (api *Client) SendAuthRevokeContext(ctx context.Context, token string) (*AuthRevokeResponse, error) {
|
||||
if token == "" {
|
||||
token = api.token
|
||||
}
|
||||
values := url.Values{
|
||||
"token": {token},
|
||||
}
|
||||
|
||||
return api.authRequest(ctx, "auth.revoke", values)
|
||||
}
|
50
vendor/github.com/nlopes/slack/backoff.go
generated
vendored
50
vendor/github.com/nlopes/slack/backoff.go
generated
vendored
@ -1,7 +1,6 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
@ -14,41 +13,42 @@ import (
|
||||
// conjunction with the time package.
|
||||
type backoff struct {
|
||||
attempts int
|
||||
//Factor is the multiplying factor for each increment step
|
||||
Factor float64
|
||||
//Jitter eases contention by randomizing backoff steps
|
||||
Jitter bool
|
||||
//Min and Max are the minimum and maximum values of the counter
|
||||
Min, Max time.Duration
|
||||
// Initial value to scale out
|
||||
Initial time.Duration
|
||||
// Jitter value randomizes an additional delay between 0 and Jitter
|
||||
Jitter time.Duration
|
||||
// Max maximum values of the backoff
|
||||
Max time.Duration
|
||||
}
|
||||
|
||||
// Returns the current value of the counter and then multiplies it
|
||||
// Factor
|
||||
func (b *backoff) Duration() time.Duration {
|
||||
//Zero-values are nonsensical, so we use
|
||||
//them to apply defaults
|
||||
if b.Min == 0 {
|
||||
b.Min = 100 * time.Millisecond
|
||||
}
|
||||
func (b *backoff) Duration() (dur time.Duration) {
|
||||
// Zero-values are nonsensical, so we use
|
||||
// them to apply defaults
|
||||
if b.Max == 0 {
|
||||
b.Max = 10 * time.Second
|
||||
}
|
||||
if b.Factor == 0 {
|
||||
b.Factor = 2
|
||||
|
||||
if b.Initial == 0 {
|
||||
b.Initial = 100 * time.Millisecond
|
||||
}
|
||||
//calculate this duration
|
||||
dur := float64(b.Min) * math.Pow(b.Factor, float64(b.attempts))
|
||||
if b.Jitter {
|
||||
dur = rand.Float64()*(dur-float64(b.Min)) + float64(b.Min)
|
||||
|
||||
// calculate this duration
|
||||
if dur = time.Duration(1 << uint(b.attempts)); dur > 0 {
|
||||
dur = dur * b.Initial
|
||||
} else {
|
||||
dur = b.Max
|
||||
}
|
||||
//cap!
|
||||
if dur > float64(b.Max) {
|
||||
return b.Max
|
||||
|
||||
if b.Jitter > 0 {
|
||||
dur = dur + time.Duration(rand.Intn(int(b.Jitter)))
|
||||
}
|
||||
//bump attempts count
|
||||
|
||||
// bump attempts count
|
||||
b.attempts++
|
||||
//return as a time.Duration
|
||||
return time.Duration(dur)
|
||||
|
||||
return dur
|
||||
}
|
||||
|
||||
//Resets the current value of the counter back to Min
|
||||
|
71
vendor/github.com/nlopes/slack/block.go
generated
vendored
Normal file
71
vendor/github.com/nlopes/slack/block.go
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
package slack
|
||||
|
||||
// @NOTE: Blocks are in beta and subject to change.
|
||||
|
||||
// More Information: https://api.slack.com/block-kit
|
||||
|
||||
// MessageBlockType defines a named string type to define each block type
|
||||
// as a constant for use within the package.
|
||||
type MessageBlockType string
|
||||
|
||||
const (
|
||||
MBTSection MessageBlockType = "section"
|
||||
MBTDivider MessageBlockType = "divider"
|
||||
MBTImage MessageBlockType = "image"
|
||||
MBTAction MessageBlockType = "actions"
|
||||
MBTContext MessageBlockType = "context"
|
||||
)
|
||||
|
||||
// Block defines an interface all block types should implement
|
||||
// to ensure consistency between blocks.
|
||||
type Block interface {
|
||||
BlockType() MessageBlockType
|
||||
}
|
||||
|
||||
// Blocks is a convenience struct defined to allow dynamic unmarshalling of
|
||||
// the "blocks" value in Slack's JSON response, which varies depending on block type
|
||||
type Blocks struct {
|
||||
BlockSet []Block `json:"blocks,omitempty"`
|
||||
}
|
||||
|
||||
// BlockAction is the action callback sent when a block is interacted with
|
||||
type BlockAction struct {
|
||||
ActionID string `json:"action_id"`
|
||||
BlockID string `json:"block_id"`
|
||||
Type actionType `json:"type"`
|
||||
Text TextBlockObject `json:"text"`
|
||||
Value string `json:"value"`
|
||||
ActionTs string `json:"action_ts"`
|
||||
SelectedOption OptionBlockObject `json:"selected_option"`
|
||||
SelectedUser string `json:"selected_user"`
|
||||
SelectedChannel string `json:"selected_channel"`
|
||||
SelectedConversation string `json:"selected_conversation"`
|
||||
SelectedDate string `json:"selected_date"`
|
||||
InitialOption OptionBlockObject `json:"initial_option"`
|
||||
InitialUser string `json:"initial_user"`
|
||||
InitialChannel string `json:"initial_channel"`
|
||||
InitialConversation string `json:"initial_conversation"`
|
||||
InitialDate string `json:"initial_date"`
|
||||
}
|
||||
|
||||
// actionType returns the type of the action
|
||||
func (b BlockAction) actionType() actionType {
|
||||
return b.Type
|
||||
}
|
||||
|
||||
// NewBlockMessage creates a new Message that contains one or more blocks to be displayed
|
||||
func NewBlockMessage(blocks ...Block) Message {
|
||||
return Message{
|
||||
Msg: Msg{
|
||||
Blocks: Blocks{
|
||||
BlockSet: blocks,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// AddBlockMessage appends a block to the end of the existing list of blocks
|
||||
func AddBlockMessage(message Message, newBlk Block) Message {
|
||||
message.Msg.Blocks.BlockSet = append(message.Msg.Blocks.BlockSet, newBlk)
|
||||
return message
|
||||
}
|
26
vendor/github.com/nlopes/slack/block_action.go
generated
vendored
Normal file
26
vendor/github.com/nlopes/slack/block_action.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package slack
|
||||
|
||||
// ActionBlock defines data that is used to hold interactive elements.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/blocks#actions
|
||||
type ActionBlock struct {
|
||||
Type MessageBlockType `json:"type"`
|
||||
BlockID string `json:"block_id,omitempty"`
|
||||
Elements BlockElements `json:"elements"`
|
||||
}
|
||||
|
||||
// BlockType returns the type of the block
|
||||
func (s ActionBlock) BlockType() MessageBlockType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// NewActionBlock returns a new instance of an Action Block
|
||||
func NewActionBlock(blockID string, elements ...BlockElement) *ActionBlock {
|
||||
return &ActionBlock{
|
||||
Type: MBTAction,
|
||||
BlockID: blockID,
|
||||
Elements: BlockElements{
|
||||
ElementSet: elements,
|
||||
},
|
||||
}
|
||||
}
|
32
vendor/github.com/nlopes/slack/block_context.go
generated
vendored
Normal file
32
vendor/github.com/nlopes/slack/block_context.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package slack
|
||||
|
||||
// ContextBlock defines data that is used to display message context, which can
|
||||
// include both images and text.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/blocks#actions
|
||||
type ContextBlock struct {
|
||||
Type MessageBlockType `json:"type"`
|
||||
BlockID string `json:"block_id,omitempty"`
|
||||
ContextElements ContextElements `json:"elements"`
|
||||
}
|
||||
|
||||
// BlockType returns the type of the block
|
||||
func (s ContextBlock) BlockType() MessageBlockType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
type ContextElements struct {
|
||||
Elements []MixedElement
|
||||
}
|
||||
|
||||
// NewContextBlock returns a new instance of a context block
|
||||
func NewContextBlock(blockID string, mixedElements ...MixedElement) *ContextBlock {
|
||||
elements := ContextElements{
|
||||
Elements: mixedElements,
|
||||
}
|
||||
return &ContextBlock{
|
||||
Type: MBTContext,
|
||||
BlockID: blockID,
|
||||
ContextElements: elements,
|
||||
}
|
||||
}
|
303
vendor/github.com/nlopes/slack/block_conv.go
generated
vendored
Normal file
303
vendor/github.com/nlopes/slack/block_conv.go
generated
vendored
Normal file
@ -0,0 +1,303 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type sumtype struct {
|
||||
TypeVal string `json:"type"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the Marshaller interface for Blocks so that any JSON
|
||||
// marshalling is delegated and proper type determination can be made before marshal
|
||||
func (b Blocks) MarshalJSON() ([]byte, error) {
|
||||
bytes, err := json.Marshal(b.BlockSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaller interface for Blocks, so that any JSON
|
||||
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
||||
func (b *Blocks) UnmarshalJSON(data []byte) error {
|
||||
var raw []json.RawMessage
|
||||
|
||||
if string(data) == "{}" {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := json.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var blocks Blocks
|
||||
for _, r := range raw {
|
||||
s := sumtype{}
|
||||
err := json.Unmarshal(r, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var blockType string
|
||||
if s.TypeVal != "" {
|
||||
blockType = s.TypeVal
|
||||
}
|
||||
|
||||
var block Block
|
||||
switch blockType {
|
||||
case "actions":
|
||||
block = &ActionBlock{}
|
||||
case "context":
|
||||
block = &ContextBlock{}
|
||||
case "divider":
|
||||
block = &DividerBlock{}
|
||||
case "image":
|
||||
block = &ImageBlock{}
|
||||
case "section":
|
||||
block = &SectionBlock{}
|
||||
default:
|
||||
return errors.New("unsupported block type")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(r, block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blocks.BlockSet = append(blocks.BlockSet, block)
|
||||
}
|
||||
|
||||
*b = blocks
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the Marshaller interface for BlockElements so that any JSON
|
||||
// marshalling is delegated and proper type determination can be made before marshal
|
||||
func (b *BlockElements) MarshalJSON() ([]byte, error) {
|
||||
bytes, err := json.Marshal(b.ElementSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaller interface for BlockElements, so that any JSON
|
||||
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
||||
func (b *BlockElements) UnmarshalJSON(data []byte) error {
|
||||
var raw []json.RawMessage
|
||||
|
||||
if string(data) == "{}" {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := json.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var blockElements BlockElements
|
||||
for _, r := range raw {
|
||||
s := sumtype{}
|
||||
err := json.Unmarshal(r, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var blockElementType string
|
||||
if s.TypeVal != "" {
|
||||
blockElementType = s.TypeVal
|
||||
}
|
||||
|
||||
var blockElement BlockElement
|
||||
switch blockElementType {
|
||||
case "image":
|
||||
blockElement = &ImageBlockElement{}
|
||||
case "button":
|
||||
blockElement = &ButtonBlockElement{}
|
||||
case "overflow":
|
||||
blockElement = &OverflowBlockElement{}
|
||||
case "datepicker":
|
||||
blockElement = &DatePickerBlockElement{}
|
||||
case "static_select", "external_select", "users_select", "conversations_select", "channels_select":
|
||||
blockElement = &SelectBlockElement{}
|
||||
default:
|
||||
return errors.New("unsupported block element type")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(r, blockElement)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockElements.ElementSet = append(blockElements.ElementSet, blockElement)
|
||||
}
|
||||
|
||||
*b = blockElements
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the Marshaller interface for Accessory so that any JSON
|
||||
// marshalling is delegated and proper type determination can be made before marshal
|
||||
func (a *Accessory) MarshalJSON() ([]byte, error) {
|
||||
bytes, err := json.Marshal(toBlockElement(a))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaller interface for Accessory, so that any JSON
|
||||
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
||||
func (a *Accessory) UnmarshalJSON(data []byte) error {
|
||||
var r json.RawMessage
|
||||
|
||||
if string(data) == "{\"accessory\":null}" {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := json.Unmarshal(data, &r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s := sumtype{}
|
||||
err = json.Unmarshal(r, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var blockElementType string
|
||||
if s.TypeVal != "" {
|
||||
blockElementType = s.TypeVal
|
||||
}
|
||||
|
||||
switch blockElementType {
|
||||
case "image":
|
||||
element, err := unmarshalBlockElement(r, &ImageBlockElement{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.ImageElement = element.(*ImageBlockElement)
|
||||
case "button":
|
||||
element, err := unmarshalBlockElement(r, &ButtonBlockElement{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.ButtonElement = element.(*ButtonBlockElement)
|
||||
case "overflow":
|
||||
element, err := unmarshalBlockElement(r, &OverflowBlockElement{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.OverflowElement = element.(*OverflowBlockElement)
|
||||
case "datepicker":
|
||||
element, err := unmarshalBlockElement(r, &DatePickerBlockElement{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.DatePickerElement = element.(*DatePickerBlockElement)
|
||||
case "static_select":
|
||||
element, err := unmarshalBlockElement(r, &SelectBlockElement{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.SelectElement = element.(*SelectBlockElement)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalBlockElement(r json.RawMessage, element BlockElement) (BlockElement, error) {
|
||||
err := json.Unmarshal(r, element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return element, nil
|
||||
}
|
||||
|
||||
func toBlockElement(element *Accessory) BlockElement {
|
||||
if element.ImageElement != nil {
|
||||
return element.ImageElement
|
||||
}
|
||||
if element.ButtonElement != nil {
|
||||
return element.ButtonElement
|
||||
}
|
||||
if element.OverflowElement != nil {
|
||||
return element.OverflowElement
|
||||
}
|
||||
if element.DatePickerElement != nil {
|
||||
return element.DatePickerElement
|
||||
}
|
||||
if element.SelectElement != nil {
|
||||
return element.SelectElement
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the Marshaller interface for ContextElements so that any JSON
|
||||
// marshalling is delegated and proper type determination can be made before marshal
|
||||
func (e *ContextElements) MarshalJSON() ([]byte, error) {
|
||||
bytes, err := json.Marshal(e.Elements)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaller interface for ContextElements, so that any JSON
|
||||
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
||||
func (e *ContextElements) UnmarshalJSON(data []byte) error {
|
||||
var raw []json.RawMessage
|
||||
|
||||
if string(data) == "{\"elements\":null}" {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := json.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range raw {
|
||||
s := sumtype{}
|
||||
err := json.Unmarshal(r, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var contextElementType string
|
||||
if s.TypeVal != "" {
|
||||
contextElementType = s.TypeVal
|
||||
}
|
||||
|
||||
switch contextElementType {
|
||||
case PlainTextType, MarkdownType:
|
||||
elem, err := unmarshalBlockObject(r, &TextBlockObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.Elements = append(e.Elements, elem.(*TextBlockObject))
|
||||
case "image":
|
||||
elem, err := unmarshalBlockElement(r, &ImageBlockElement{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.Elements = append(e.Elements, elem.(*ImageBlockElement))
|
||||
default:
|
||||
return errors.New("unsupported context element type")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
22
vendor/github.com/nlopes/slack/block_divider.go
generated
vendored
Normal file
22
vendor/github.com/nlopes/slack/block_divider.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
package slack
|
||||
|
||||
// DividerBlock for displaying a divider line between blocks (similar to <hr> tag in html)
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/blocks#divider
|
||||
type DividerBlock struct {
|
||||
Type MessageBlockType `json:"type"`
|
||||
BlockID string `json:"block_id,omitempty"`
|
||||
}
|
||||
|
||||
// BlockType returns the type of the block
|
||||
func (s DividerBlock) BlockType() MessageBlockType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// NewDividerBlock returns a new instance of a divider block
|
||||
func NewDividerBlock() *DividerBlock {
|
||||
return &DividerBlock{
|
||||
Type: MBTDivider,
|
||||
}
|
||||
|
||||
}
|
238
vendor/github.com/nlopes/slack/block_element.go
generated
vendored
Normal file
238
vendor/github.com/nlopes/slack/block_element.go
generated
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
package slack
|
||||
|
||||
// https://api.slack.com/reference/messaging/block-elements
|
||||
|
||||
const (
|
||||
METImage MessageElementType = "image"
|
||||
METButton MessageElementType = "button"
|
||||
METOverflow MessageElementType = "overflow"
|
||||
METDatepicker MessageElementType = "datepicker"
|
||||
|
||||
MixedElementImage MixedElementType = "mixed_image"
|
||||
MixedElementText MixedElementType = "mixed_text"
|
||||
|
||||
OptTypeStatic string = "static_select"
|
||||
OptTypeExternal string = "external_select"
|
||||
OptTypeUser string = "users_select"
|
||||
OptTypeConversations string = "conversations_select"
|
||||
OptTypeChannels string = "channels_select"
|
||||
)
|
||||
|
||||
type MessageElementType string
|
||||
type MixedElementType string
|
||||
|
||||
// BlockElement defines an interface that all block element types should implement.
|
||||
type BlockElement interface {
|
||||
ElementType() MessageElementType
|
||||
}
|
||||
|
||||
type MixedElement interface {
|
||||
MixedElementType() MixedElementType
|
||||
}
|
||||
|
||||
type Accessory struct {
|
||||
ImageElement *ImageBlockElement
|
||||
ButtonElement *ButtonBlockElement
|
||||
OverflowElement *OverflowBlockElement
|
||||
DatePickerElement *DatePickerBlockElement
|
||||
SelectElement *SelectBlockElement
|
||||
}
|
||||
|
||||
// NewAccessory returns a new Accessory for a given block element
|
||||
func NewAccessory(element BlockElement) *Accessory {
|
||||
switch element.(type) {
|
||||
case *ImageBlockElement:
|
||||
return &Accessory{ImageElement: element.(*ImageBlockElement)}
|
||||
case *ButtonBlockElement:
|
||||
return &Accessory{ButtonElement: element.(*ButtonBlockElement)}
|
||||
case *OverflowBlockElement:
|
||||
return &Accessory{OverflowElement: element.(*OverflowBlockElement)}
|
||||
case *DatePickerBlockElement:
|
||||
return &Accessory{DatePickerElement: element.(*DatePickerBlockElement)}
|
||||
case *SelectBlockElement:
|
||||
return &Accessory{SelectElement: element.(*SelectBlockElement)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BlockElements is a convenience struct defined to allow dynamic unmarshalling of
|
||||
// the "elements" value in Slack's JSON response, which varies depending on BlockElement type
|
||||
type BlockElements struct {
|
||||
ElementSet []BlockElement `json:"elements,omitempty"`
|
||||
}
|
||||
|
||||
// ImageBlockElement An element to insert an image - this element can be used
|
||||
// in section and context blocks only. If you want a block with only an image
|
||||
// in it, you're looking for the image block.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/block-elements#image
|
||||
type ImageBlockElement struct {
|
||||
Type MessageElementType `json:"type"`
|
||||
ImageURL string `json:"image_url"`
|
||||
AltText string `json:"alt_text"`
|
||||
}
|
||||
|
||||
// ElementType returns the type of the Element
|
||||
func (s ImageBlockElement) ElementType() MessageElementType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
func (s ImageBlockElement) MixedElementType() MixedElementType {
|
||||
return MixedElementImage
|
||||
}
|
||||
|
||||
// NewImageBlockElement returns a new instance of an image block element
|
||||
func NewImageBlockElement(imageURL, altText string) *ImageBlockElement {
|
||||
return &ImageBlockElement{
|
||||
Type: METImage,
|
||||
ImageURL: imageURL,
|
||||
AltText: altText,
|
||||
}
|
||||
}
|
||||
|
||||
type Style string
|
||||
|
||||
const (
|
||||
StyleDefault Style = "default"
|
||||
StylePrimary Style = "primary"
|
||||
StyleDanger Style = "danger"
|
||||
)
|
||||
|
||||
// ButtonBlockElement defines an interactive element that inserts a button. The
|
||||
// button can be a trigger for anything from opening a simple link to starting
|
||||
// a complex workflow.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/block-elements#button
|
||||
type ButtonBlockElement struct {
|
||||
Type MessageElementType `json:"type,omitempty"`
|
||||
Text *TextBlockObject `json:"text"`
|
||||
ActionID string `json:"action_id,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
|
||||
Style Style `json:"style,omitempty"`
|
||||
}
|
||||
|
||||
// ElementType returns the type of the element
|
||||
func (s ButtonBlockElement) ElementType() MessageElementType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// add styling to button object
|
||||
func (s *ButtonBlockElement) WithStyle(style Style) {
|
||||
s.Style = style
|
||||
}
|
||||
|
||||
// NewButtonBlockElement returns an instance of a new button element to be used within a block
|
||||
func NewButtonBlockElement(actionID, value string, text *TextBlockObject) *ButtonBlockElement {
|
||||
return &ButtonBlockElement{
|
||||
Type: METButton,
|
||||
ActionID: actionID,
|
||||
Text: text,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// SelectBlockElement defines the simplest form of select menu, with a static list
|
||||
// of options passed in when defining the element.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/block-elements#select
|
||||
type SelectBlockElement struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Placeholder *TextBlockObject `json:"placeholder,omitempty"`
|
||||
ActionID string `json:"action_id,omitempty"`
|
||||
Options []*OptionBlockObject `json:"options,omitempty"`
|
||||
OptionGroups []*OptionGroupBlockObject `json:"option_groups,omitempty"`
|
||||
InitialOption *OptionBlockObject `json:"initial_option,omitempty"`
|
||||
InitialUser string `json:"initial_user,omitempty"`
|
||||
InitialConversation string `json:"initial_conversation,omitempty"`
|
||||
InitialChannel string `json:"initial_channel,omitempty"`
|
||||
MinQueryLength int `json:"min_query_length,omitempty"`
|
||||
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
|
||||
}
|
||||
|
||||
// ElementType returns the type of the Element
|
||||
func (s SelectBlockElement) ElementType() MessageElementType {
|
||||
return MessageElementType(s.Type)
|
||||
}
|
||||
|
||||
// NewOptionsSelectBlockElement returns a new instance of SelectBlockElement for use with
|
||||
// the Options object only.
|
||||
func NewOptionsSelectBlockElement(optType string, placeholder *TextBlockObject, actionID string, options ...*OptionBlockObject) *SelectBlockElement {
|
||||
return &SelectBlockElement{
|
||||
Type: optType,
|
||||
Placeholder: placeholder,
|
||||
ActionID: actionID,
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// NewOptionsGroupSelectBlockElement returns a new instance of SelectBlockElement for use with
|
||||
// the Options object only.
|
||||
func NewOptionsGroupSelectBlockElement(
|
||||
optType string,
|
||||
placeholder *TextBlockObject,
|
||||
actionID string,
|
||||
optGroups ...*OptionGroupBlockObject,
|
||||
) *SelectBlockElement {
|
||||
return &SelectBlockElement{
|
||||
Type: optType,
|
||||
Placeholder: placeholder,
|
||||
ActionID: actionID,
|
||||
OptionGroups: optGroups,
|
||||
}
|
||||
}
|
||||
|
||||
// OverflowBlockElement defines the fields needed to use an overflow element.
|
||||
// And Overflow Element is like a cross between a button and a select menu -
|
||||
// when a user clicks on this overflow button, they will be presented with a
|
||||
// list of options to choose from.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/block-elements#overflow
|
||||
type OverflowBlockElement struct {
|
||||
Type MessageElementType `json:"type"`
|
||||
ActionID string `json:"action_id,omitempty"`
|
||||
Options []*OptionBlockObject `json:"options"`
|
||||
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
|
||||
}
|
||||
|
||||
// ElementType returns the type of the Element
|
||||
func (s OverflowBlockElement) ElementType() MessageElementType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// NewOverflowBlockElement returns an instance of a new Overflow Block Element
|
||||
func NewOverflowBlockElement(actionID string, options ...*OptionBlockObject) *OverflowBlockElement {
|
||||
return &OverflowBlockElement{
|
||||
Type: METOverflow,
|
||||
ActionID: actionID,
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// DatePickerBlockElement defines an element which lets users easily select a
|
||||
// date from a calendar style UI. Date picker elements can be used inside of
|
||||
// section and actions blocks.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/block-elements#datepicker
|
||||
type DatePickerBlockElement struct {
|
||||
Type MessageElementType `json:"type"`
|
||||
ActionID string `json:"action_id"`
|
||||
Placeholder *TextBlockObject `json:"placeholder,omitempty"`
|
||||
InitialDate string `json:"initial_date,omitempty"`
|
||||
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
|
||||
}
|
||||
|
||||
// ElementType returns the type of the Element
|
||||
func (s DatePickerBlockElement) ElementType() MessageElementType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// NewDatePickerBlockElement returns an instance of a date picker element
|
||||
func NewDatePickerBlockElement(actionID string) *DatePickerBlockElement {
|
||||
return &DatePickerBlockElement{
|
||||
Type: METDatepicker,
|
||||
ActionID: actionID,
|
||||
}
|
||||
}
|
28
vendor/github.com/nlopes/slack/block_image.go
generated
vendored
Normal file
28
vendor/github.com/nlopes/slack/block_image.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package slack
|
||||
|
||||
// ImageBlock defines data required to display an image as a block element
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/blocks#image
|
||||
type ImageBlock struct {
|
||||
Type MessageBlockType `json:"type"`
|
||||
ImageURL string `json:"image_url"`
|
||||
AltText string `json:"alt_text"`
|
||||
BlockID string `json:"block_id,omitempty"`
|
||||
Title *TextBlockObject `json:"title"`
|
||||
}
|
||||
|
||||
// BlockType returns the type of the block
|
||||
func (s ImageBlock) BlockType() MessageBlockType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// NewImageBlock returns an instance of a new Image Block type
|
||||
func NewImageBlock(imageURL, altText, blockID string, title *TextBlockObject) *ImageBlock {
|
||||
return &ImageBlock{
|
||||
Type: MBTImage,
|
||||
ImageURL: imageURL,
|
||||
AltText: altText,
|
||||
BlockID: blockID,
|
||||
Title: title,
|
||||
}
|
||||
}
|
215
vendor/github.com/nlopes/slack/block_object.go
generated
vendored
Normal file
215
vendor/github.com/nlopes/slack/block_object.go
generated
vendored
Normal file
@ -0,0 +1,215 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Block Objects are also known as Composition Objects
|
||||
//
|
||||
// For more information: https://api.slack.com/reference/messaging/composition-objects
|
||||
|
||||
// BlockObject defines an interface that all block object types should
|
||||
// implement.
|
||||
// @TODO: Is this interface needed?
|
||||
|
||||
// blockObject object types
|
||||
const (
|
||||
MarkdownType = "mrkdwn"
|
||||
PlainTextType = "plain_text"
|
||||
// The following objects don't actually have types and their corresponding
|
||||
// const values are just for internal use
|
||||
motConfirmation = "confirm"
|
||||
motOption = "option"
|
||||
motOptionGroup = "option_group"
|
||||
)
|
||||
|
||||
type MessageObjectType string
|
||||
|
||||
type blockObject interface {
|
||||
validateType() MessageObjectType
|
||||
}
|
||||
|
||||
type BlockObjects struct {
|
||||
TextObjects []*TextBlockObject
|
||||
ConfirmationObjects []*ConfirmationBlockObject
|
||||
OptionObjects []*OptionBlockObject
|
||||
OptionGroupObjects []*OptionGroupBlockObject
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaller interface for BlockObjects, so that any JSON
|
||||
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
||||
func (b *BlockObjects) UnmarshalJSON(data []byte) error {
|
||||
var raw []json.RawMessage
|
||||
err := json.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range raw {
|
||||
var obj map[string]interface{}
|
||||
err := json.Unmarshal(r, &obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockObjectType := getBlockObjectType(obj)
|
||||
|
||||
switch blockObjectType {
|
||||
case PlainTextType, MarkdownType:
|
||||
object, err := unmarshalBlockObject(r, &TextBlockObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.TextObjects = append(b.TextObjects, object.(*TextBlockObject))
|
||||
case motConfirmation:
|
||||
object, err := unmarshalBlockObject(r, &ConfirmationBlockObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.ConfirmationObjects = append(b.ConfirmationObjects, object.(*ConfirmationBlockObject))
|
||||
case motOption:
|
||||
object, err := unmarshalBlockObject(r, &OptionBlockObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.OptionObjects = append(b.OptionObjects, object.(*OptionBlockObject))
|
||||
case motOptionGroup:
|
||||
object, err := unmarshalBlockObject(r, &OptionGroupBlockObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.OptionGroupObjects = append(b.OptionGroupObjects, object.(*OptionGroupBlockObject))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ideally would have a better way to identify the block objects for
|
||||
// type casting at time of unmarshalling, should be adapted if possible
|
||||
// to accomplish in a more reliable manner.
|
||||
func getBlockObjectType(obj map[string]interface{}) string {
|
||||
if t, ok := obj["type"].(string); ok {
|
||||
return t
|
||||
}
|
||||
if _, ok := obj["confirm"].(string); ok {
|
||||
return "confirm"
|
||||
}
|
||||
if _, ok := obj["options"].(string); ok {
|
||||
return "option_group"
|
||||
}
|
||||
if _, ok := obj["text"].(string); ok {
|
||||
if _, ok := obj["value"].(string); ok {
|
||||
return "option"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func unmarshalBlockObject(r json.RawMessage, object blockObject) (blockObject, error) {
|
||||
err := json.Unmarshal(r, object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return object, nil
|
||||
}
|
||||
|
||||
// TextBlockObject defines a text element object to be used with blocks
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/composition-objects#text
|
||||
type TextBlockObject struct {
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text"`
|
||||
Emoji bool `json:"emoji,omitempty"`
|
||||
Verbatim bool `json:"verbatim,omitempty"`
|
||||
}
|
||||
|
||||
// validateType enforces block objects for element and block parameters
|
||||
func (s TextBlockObject) validateType() MessageObjectType {
|
||||
return MessageObjectType(s.Type)
|
||||
}
|
||||
|
||||
// validateType enforces block objects for element and block parameters
|
||||
func (s TextBlockObject) MixedElementType() MixedElementType {
|
||||
return MixedElementText
|
||||
}
|
||||
|
||||
// NewTextBlockObject returns an instance of a new Text Block Object
|
||||
func NewTextBlockObject(elementType, text string, emoji, verbatim bool) *TextBlockObject {
|
||||
return &TextBlockObject{
|
||||
Type: elementType,
|
||||
Text: text,
|
||||
Emoji: emoji,
|
||||
Verbatim: verbatim,
|
||||
}
|
||||
}
|
||||
|
||||
// ConfirmationBlockObject defines a dialog that provides a confirmation step to
|
||||
// any interactive element. This dialog will ask the user to confirm their action by
|
||||
// offering a confirm and deny buttons.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/composition-objects#confirm
|
||||
type ConfirmationBlockObject struct {
|
||||
Title *TextBlockObject `json:"title"`
|
||||
Text *TextBlockObject `json:"text"`
|
||||
Confirm *TextBlockObject `json:"confirm"`
|
||||
Deny *TextBlockObject `json:"deny"`
|
||||
}
|
||||
|
||||
// validateType enforces block objects for element and block parameters
|
||||
func (s ConfirmationBlockObject) validateType() MessageObjectType {
|
||||
return motConfirmation
|
||||
}
|
||||
|
||||
// NewConfirmationBlockObject returns an instance of a new Confirmation Block Object
|
||||
func NewConfirmationBlockObject(title, text, confirm, deny *TextBlockObject) *ConfirmationBlockObject {
|
||||
return &ConfirmationBlockObject{
|
||||
Title: title,
|
||||
Text: text,
|
||||
Confirm: confirm,
|
||||
Deny: deny,
|
||||
}
|
||||
}
|
||||
|
||||
// OptionBlockObject represents a single selectable item in a select menu
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/composition-objects#option
|
||||
type OptionBlockObject struct {
|
||||
Text *TextBlockObject `json:"text"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// NewOptionBlockObject returns an instance of a new Option Block Element
|
||||
func NewOptionBlockObject(value string, text *TextBlockObject) *OptionBlockObject {
|
||||
return &OptionBlockObject{
|
||||
Text: text,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// validateType enforces block objects for element and block parameters
|
||||
func (s OptionBlockObject) validateType() MessageObjectType {
|
||||
return motOption
|
||||
}
|
||||
|
||||
// OptionGroupBlockObject Provides a way to group options in a select menu.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/composition-objects#option-group
|
||||
type OptionGroupBlockObject struct {
|
||||
Label *TextBlockObject `json:"label,omitempty"`
|
||||
Options []*OptionBlockObject `json:"options"`
|
||||
}
|
||||
|
||||
// validateType enforces block objects for element and block parameters
|
||||
func (s OptionGroupBlockObject) validateType() MessageObjectType {
|
||||
return motOptionGroup
|
||||
}
|
||||
|
||||
// NewOptionGroupBlockElement returns an instance of a new option group block element
|
||||
func NewOptionGroupBlockElement(label *TextBlockObject, options ...*OptionBlockObject) *OptionGroupBlockObject {
|
||||
return &OptionGroupBlockObject{
|
||||
Label: label,
|
||||
Options: options,
|
||||
}
|
||||
}
|
42
vendor/github.com/nlopes/slack/block_section.go
generated
vendored
Normal file
42
vendor/github.com/nlopes/slack/block_section.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
package slack
|
||||
|
||||
// SectionBlock defines a new block of type section
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/blocks#section
|
||||
type SectionBlock struct {
|
||||
Type MessageBlockType `json:"type"`
|
||||
Text *TextBlockObject `json:"text,omitempty"`
|
||||
BlockID string `json:"block_id,omitempty"`
|
||||
Fields []*TextBlockObject `json:"fields,omitempty"`
|
||||
Accessory *Accessory `json:"accessory,omitempty"`
|
||||
}
|
||||
|
||||
// BlockType returns the type of the block
|
||||
func (s SectionBlock) BlockType() MessageBlockType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// SectionBlockOption allows configuration of options for a new section block
|
||||
type SectionBlockOption func(*SectionBlock)
|
||||
|
||||
func SectionBlockOptionBlockID(blockID string) SectionBlockOption {
|
||||
return func(block *SectionBlock) {
|
||||
block.BlockID = blockID
|
||||
}
|
||||
}
|
||||
|
||||
// NewSectionBlock returns a new instance of a section block to be rendered
|
||||
func NewSectionBlock(textObj *TextBlockObject, fields []*TextBlockObject, accessory *Accessory, options ...SectionBlockOption) *SectionBlock {
|
||||
block := SectionBlock{
|
||||
Type: MBTSection,
|
||||
Text: textObj,
|
||||
Fields: fields,
|
||||
Accessory: accessory,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(&block)
|
||||
}
|
||||
|
||||
return &block
|
||||
}
|
13
vendor/github.com/nlopes/slack/bots.go
generated
vendored
13
vendor/github.com/nlopes/slack/bots.go
generated
vendored
@ -2,7 +2,6 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
@ -19,15 +18,17 @@ type botResponseFull struct {
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func botRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*botResponseFull, error) {
|
||||
func (api *Client) botRequest(ctx context.Context, path string, values url.Values) (*botResponseFull, error) {
|
||||
response := &botResponseFull{}
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@ -43,7 +44,7 @@ func (api *Client) GetBotInfoContext(ctx context.Context, bot string) (*Bot, err
|
||||
"bot": {bot},
|
||||
}
|
||||
|
||||
response, err := botRequest(ctx, api.httpclient, "bots.info", values, api.debug)
|
||||
response, err := api.botRequest(ctx, "bots.info", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
93
vendor/github.com/nlopes/slack/channels.go
generated
vendored
93
vendor/github.com/nlopes/slack/channels.go
generated
vendored
@ -2,7 +2,6 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
@ -19,25 +18,50 @@ type channelResponseFull struct {
|
||||
|
||||
// Channel contains information about the channel
|
||||
type Channel struct {
|
||||
groupConversation
|
||||
GroupConversation
|
||||
IsChannel bool `json:"is_channel"`
|
||||
IsGeneral bool `json:"is_general"`
|
||||
IsMember bool `json:"is_member"`
|
||||
Locale string `json:"locale"`
|
||||
}
|
||||
|
||||
func channelRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*channelResponseFull, error) {
|
||||
func (api *Client) channelRequest(ctx context.Context, path string, values url.Values) (*channelResponseFull, error) {
|
||||
response := &channelResponseFull{}
|
||||
err := postForm(ctx, client, SLACK_API+path, values, response, debug)
|
||||
err := postForm(ctx, api.httpclient, api.endpoint+path, values, response, api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
type channelsConfig struct {
|
||||
values url.Values
|
||||
}
|
||||
|
||||
// GetChannelsOption option provided when getting channels.
|
||||
type GetChannelsOption func(*channelsConfig) error
|
||||
|
||||
// GetChannelsOptionExcludeMembers excludes the members collection from each channel.
|
||||
func GetChannelsOptionExcludeMembers() GetChannelsOption {
|
||||
return func(config *channelsConfig) error {
|
||||
config.values.Add("exclude_members", "true")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetChannelsOptionExcludeArchived excludes archived channels from results.
|
||||
func GetChannelsOptionExcludeArchived() GetChannelsOption {
|
||||
return func(config *channelsConfig) error {
|
||||
config.values.Add("exclude_archived", "true")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ArchiveChannel archives the given channel
|
||||
// see https://api.slack.com/methods/channels.archive
|
||||
func (api *Client) ArchiveChannel(channelID string) error {
|
||||
@ -52,7 +76,7 @@ func (api *Client) ArchiveChannelContext(ctx context.Context, channelID string)
|
||||
"channel": {channelID},
|
||||
}
|
||||
|
||||
_, err = channelRequest(ctx, api.httpclient, "channels.archive", values, api.debug)
|
||||
_, err = api.channelRequest(ctx, "channels.archive", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -70,7 +94,7 @@ func (api *Client) UnarchiveChannelContext(ctx context.Context, channelID string
|
||||
"channel": {channelID},
|
||||
}
|
||||
|
||||
_, err = channelRequest(ctx, api.httpclient, "channels.unarchive", values, api.debug)
|
||||
_, err = api.channelRequest(ctx, "channels.unarchive", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -88,7 +112,7 @@ func (api *Client) CreateChannelContext(ctx context.Context, channelName string)
|
||||
"name": {channelName},
|
||||
}
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.create", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "channels.create", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -133,7 +157,7 @@ func (api *Client) GetChannelHistoryContext(ctx context.Context, channelID strin
|
||||
}
|
||||
}
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.history", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "channels.history", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -150,11 +174,12 @@ func (api *Client) GetChannelInfo(channelID string) (*Channel, error) {
|
||||
// see https://api.slack.com/methods/channels.info
|
||||
func (api *Client) GetChannelInfoContext(ctx context.Context, channelID string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"include_locale": {strconv.FormatBool(true)},
|
||||
}
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.info", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "channels.info", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -167,7 +192,7 @@ func (api *Client) InviteUserToChannel(channelID, user string) (*Channel, error)
|
||||
return api.InviteUserToChannelContext(context.Background(), channelID, user)
|
||||
}
|
||||
|
||||
// InviteUserToChannelCustom invites a user to a given channel and returns a *Channel with a custom context
|
||||
// InviteUserToChannelContext invites a user to a given channel and returns a *Channel with a custom context
|
||||
// see https://api.slack.com/methods/channels.invite
|
||||
func (api *Client) InviteUserToChannelContext(ctx context.Context, channelID, user string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
@ -176,7 +201,7 @@ func (api *Client) InviteUserToChannelContext(ctx context.Context, channelID, us
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.invite", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "channels.invite", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -197,7 +222,7 @@ func (api *Client) JoinChannelContext(ctx context.Context, channelName string) (
|
||||
"name": {channelName},
|
||||
}
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.join", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "channels.join", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -218,7 +243,7 @@ func (api *Client) LeaveChannelContext(ctx context.Context, channelID string) (b
|
||||
"channel": {channelID},
|
||||
}
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.leave", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "channels.leave", values)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -241,27 +266,35 @@ func (api *Client) KickUserFromChannelContext(ctx context.Context, channelID, us
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
_, err = channelRequest(ctx, api.httpclient, "channels.kick", values, api.debug)
|
||||
_, err = api.channelRequest(ctx, "channels.kick", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetChannels retrieves all the channels
|
||||
// see https://api.slack.com/methods/channels.list
|
||||
func (api *Client) GetChannels(excludeArchived bool) ([]Channel, error) {
|
||||
return api.GetChannelsContext(context.Background(), excludeArchived)
|
||||
func (api *Client) GetChannels(excludeArchived bool, options ...GetChannelsOption) ([]Channel, error) {
|
||||
return api.GetChannelsContext(context.Background(), excludeArchived, options...)
|
||||
}
|
||||
|
||||
// GetChannelsContext retrieves all the channels with a custom context
|
||||
// see https://api.slack.com/methods/channels.list
|
||||
func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool) ([]Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool, options ...GetChannelsOption) ([]Channel, error) {
|
||||
config := channelsConfig{
|
||||
values: url.Values{
|
||||
"token": {api.token},
|
||||
},
|
||||
}
|
||||
if excludeArchived {
|
||||
values.Add("exclude_archived", "1")
|
||||
options = append(options, GetChannelsOptionExcludeArchived())
|
||||
}
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.list", values, api.debug)
|
||||
for _, opt := range options {
|
||||
if err := opt(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
response, err := api.channelRequest(ctx, "channels.list", config.values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -288,7 +321,7 @@ func (api *Client) SetChannelReadMarkContext(ctx context.Context, channelID, ts
|
||||
"ts": {ts},
|
||||
}
|
||||
|
||||
_, err = channelRequest(ctx, api.httpclient, "channels.mark", values, api.debug)
|
||||
_, err = api.channelRequest(ctx, "channels.mark", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -309,7 +342,7 @@ func (api *Client) RenameChannelContext(ctx context.Context, channelID, name str
|
||||
|
||||
// XXX: the created entry in this call returns a string instead of a number
|
||||
// so I may have to do some workaround to solve it.
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.rename", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "channels.rename", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -331,7 +364,7 @@ func (api *Client) SetChannelPurposeContext(ctx context.Context, channelID, purp
|
||||
"purpose": {purpose},
|
||||
}
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.setPurpose", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "channels.setPurpose", values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -353,7 +386,7 @@ func (api *Client) SetChannelTopicContext(ctx context.Context, channelID, topic
|
||||
"topic": {topic},
|
||||
}
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.setTopic", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "channels.setTopic", values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -374,7 +407,7 @@ func (api *Client) GetChannelRepliesContext(ctx context.Context, channelID, thre
|
||||
"channel": {channelID},
|
||||
"thread_ts": {thread_ts},
|
||||
}
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.replies", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "channels.replies", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
194
vendor/github.com/nlopes/slack/chat.go
generated
vendored
194
vendor/github.com/nlopes/slack/chat.go
generated
vendored
@ -43,19 +43,18 @@ func (c chatResponseFull) getMessageTimestamp() string {
|
||||
|
||||
// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
|
||||
type PostMessageParameters struct {
|
||||
Username string `json:"username"`
|
||||
AsUser bool `json:"as_user"`
|
||||
Parse string `json:"parse"`
|
||||
ThreadTimestamp string `json:"thread_ts"`
|
||||
ReplyBroadcast bool `json:"reply_broadcast"`
|
||||
LinkNames int `json:"link_names"`
|
||||
Attachments []Attachment `json:"attachments"`
|
||||
UnfurlLinks bool `json:"unfurl_links"`
|
||||
UnfurlMedia bool `json:"unfurl_media"`
|
||||
IconURL string `json:"icon_url"`
|
||||
IconEmoji string `json:"icon_emoji"`
|
||||
Markdown bool `json:"mrkdwn,omitempty"`
|
||||
EscapeText bool `json:"escape_text"`
|
||||
Username string `json:"username"`
|
||||
AsUser bool `json:"as_user"`
|
||||
Parse string `json:"parse"`
|
||||
ThreadTimestamp string `json:"thread_ts"`
|
||||
ReplyBroadcast bool `json:"reply_broadcast"`
|
||||
LinkNames int `json:"link_names"`
|
||||
UnfurlLinks bool `json:"unfurl_links"`
|
||||
UnfurlMedia bool `json:"unfurl_media"`
|
||||
IconURL string `json:"icon_url"`
|
||||
IconEmoji string `json:"icon_emoji"`
|
||||
Markdown bool `json:"mrkdwn,omitempty"`
|
||||
EscapeText bool `json:"escape_text"`
|
||||
|
||||
// chat.postEphemeral support
|
||||
Channel string `json:"channel"`
|
||||
@ -71,7 +70,6 @@ func NewPostMessageParameters() PostMessageParameters {
|
||||
Parse: DEFAULT_MESSAGE_PARSE,
|
||||
ThreadTimestamp: DEFAULT_MESSAGE_THREAD_TIMESTAMP,
|
||||
LinkNames: DEFAULT_MESSAGE_LINK_NAMES,
|
||||
Attachments: nil,
|
||||
UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
|
||||
UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
|
||||
IconURL: DEFAULT_MESSAGE_ICON_URL,
|
||||
@ -96,26 +94,24 @@ func (api *Client) DeleteMessageContext(ctx context.Context, channel, messageTim
|
||||
// PostMessage sends a message to a channel.
|
||||
// Message is escaped by default according to https://api.slack.com/docs/formatting
|
||||
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
|
||||
func (api *Client) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) {
|
||||
func (api *Client) PostMessage(channelID string, options ...MsgOption) (string, string, error) {
|
||||
respChannel, respTimestamp, _, err := api.SendMessageContext(
|
||||
context.Background(),
|
||||
channel,
|
||||
MsgOptionText(text, params.EscapeText),
|
||||
MsgOptionAttachments(params.Attachments...),
|
||||
MsgOptionPostMessageParameters(params),
|
||||
channelID,
|
||||
MsgOptionPost(),
|
||||
MsgOptionCompose(options...),
|
||||
)
|
||||
return respChannel, respTimestamp, err
|
||||
}
|
||||
|
||||
// PostMessageContext sends a message to a channel with a custom context
|
||||
// For more details, see PostMessage documentation
|
||||
func (api *Client) PostMessageContext(ctx context.Context, channel, text string, params PostMessageParameters) (string, string, error) {
|
||||
// For more details, see PostMessage documentation.
|
||||
func (api *Client) PostMessageContext(ctx context.Context, channelID string, options ...MsgOption) (string, string, error) {
|
||||
respChannel, respTimestamp, _, err := api.SendMessageContext(
|
||||
ctx,
|
||||
channel,
|
||||
MsgOptionText(text, params.EscapeText),
|
||||
MsgOptionAttachments(params.Attachments...),
|
||||
MsgOptionPostMessageParameters(params),
|
||||
channelID,
|
||||
MsgOptionPost(),
|
||||
MsgOptionCompose(options...),
|
||||
)
|
||||
return respChannel, respTimestamp, err
|
||||
}
|
||||
@ -135,18 +131,23 @@ func (api *Client) PostEphemeral(channelID, userID string, options ...MsgOption)
|
||||
// PostEphemeralContext sends an ephemeal message to a user in a channel with a custom context
|
||||
// For more details, see PostEphemeral documentation
|
||||
func (api *Client) PostEphemeralContext(ctx context.Context, channelID, userID string, options ...MsgOption) (timestamp string, err error) {
|
||||
_, timestamp, _, err = api.SendMessageContext(ctx, channelID, append(options, MsgOptionPostEphemeral2(userID))...)
|
||||
_, timestamp, _, err = api.SendMessageContext(ctx, channelID, MsgOptionPostEphemeral(userID), MsgOptionCompose(options...))
|
||||
return timestamp, err
|
||||
}
|
||||
|
||||
// UpdateMessage updates a message in a channel
|
||||
func (api *Client) UpdateMessage(channelID, timestamp, text string) (string, string, string, error) {
|
||||
return api.UpdateMessageContext(context.Background(), channelID, timestamp, text)
|
||||
func (api *Client) UpdateMessage(channelID, timestamp string, options ...MsgOption) (string, string, string, error) {
|
||||
return api.SendMessageContext(context.Background(), channelID, MsgOptionUpdate(timestamp), MsgOptionCompose(options...))
|
||||
}
|
||||
|
||||
// UpdateMessageContext updates a message in a channel
|
||||
func (api *Client) UpdateMessageContext(ctx context.Context, channelID, timestamp, text string) (string, string, string, error) {
|
||||
return api.SendMessageContext(ctx, channelID, MsgOptionUpdate(timestamp), MsgOptionText(text, true))
|
||||
func (api *Client) UpdateMessageContext(ctx context.Context, channelID, timestamp string, options ...MsgOption) (string, string, string, error) {
|
||||
return api.SendMessageContext(ctx, channelID, MsgOptionUpdate(timestamp), MsgOptionCompose(options...))
|
||||
}
|
||||
|
||||
// UnfurlMessage unfurls a message in a channel
|
||||
func (api *Client) UnfurlMessage(channelID, timestamp string, unfurls map[string]Attachment, options ...MsgOption) (string, string, string, error) {
|
||||
return api.SendMessageContext(context.Background(), channelID, MsgOptionUnfurl(timestamp, unfurls), MsgOptionCompose(options...))
|
||||
}
|
||||
|
||||
// SendMessage more flexible method for configuring messages.
|
||||
@ -161,11 +162,11 @@ func (api *Client) SendMessageContext(ctx context.Context, channelID string, opt
|
||||
response chatResponseFull
|
||||
)
|
||||
|
||||
if config, err = applyMsgOptions(api.token, channelID, options...); err != nil {
|
||||
if config, err = applyMsgOptions(api.token, channelID, api.endpoint, options...); err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
if err = postForm(ctx, api.httpclient, config.endpoint, config.values, &response, api.debug); err != nil {
|
||||
if err = postForm(ctx, api.httpclient, config.endpoint, config.values, &response, api); err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
@ -175,14 +176,15 @@ func (api *Client) SendMessageContext(ctx context.Context, channelID string, opt
|
||||
// UnsafeApplyMsgOptions utility function for debugging/testing chat requests.
|
||||
// NOTE: USE AT YOUR OWN RISK: No issues relating to the use of this function
|
||||
// will be supported by the library.
|
||||
func UnsafeApplyMsgOptions(token, channel string, options ...MsgOption) (string, url.Values, error) {
|
||||
config, err := applyMsgOptions(token, channel, options...)
|
||||
func UnsafeApplyMsgOptions(token, channel, apiurl string, options ...MsgOption) (string, url.Values, error) {
|
||||
config, err := applyMsgOptions(token, channel, apiurl, options...)
|
||||
return config.endpoint, config.values, err
|
||||
}
|
||||
|
||||
func applyMsgOptions(token, channel string, options ...MsgOption) (sendConfig, error) {
|
||||
func applyMsgOptions(token, channel, apiurl string, options ...MsgOption) (sendConfig, error) {
|
||||
config := sendConfig{
|
||||
endpoint: SLACK_API + string(chatPostMessage),
|
||||
apiurl: apiurl,
|
||||
endpoint: apiurl + string(chatPostMessage),
|
||||
values: url.Values{
|
||||
"token": {token},
|
||||
"channel": {channel},
|
||||
@ -206,9 +208,11 @@ const (
|
||||
chatDelete sendMode = "chat.delete"
|
||||
chatPostEphemeral sendMode = "chat.postEphemeral"
|
||||
chatMeMessage sendMode = "chat.meMessage"
|
||||
chatUnfurl sendMode = "chat.unfurl"
|
||||
)
|
||||
|
||||
type sendConfig struct {
|
||||
apiurl string
|
||||
endpoint string
|
||||
values url.Values
|
||||
}
|
||||
@ -219,26 +223,16 @@ type MsgOption func(*sendConfig) error
|
||||
// MsgOptionPost posts a messages, this is the default.
|
||||
func MsgOptionPost() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = SLACK_API + string(chatPostMessage)
|
||||
config.endpoint = config.apiurl + string(chatPostMessage)
|
||||
config.values.Del("ts")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionPostEphemeral - DEPRECATED: use MsgOptionPostEphemeral2
|
||||
// posts an ephemeral message.
|
||||
func MsgOptionPostEphemeral() MsgOption {
|
||||
// MsgOptionPostEphemeral - posts an ephemeral message to the provided user.
|
||||
func MsgOptionPostEphemeral(userID string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = SLACK_API + string(chatPostEphemeral)
|
||||
config.values.Del("ts")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionPostEphemeral2 - posts an ephemeral message to the provided user.
|
||||
func MsgOptionPostEphemeral2(userID string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = SLACK_API + string(chatPostEphemeral)
|
||||
config.endpoint = config.apiurl + string(chatPostEphemeral)
|
||||
MsgOptionUser(userID)(config)
|
||||
config.values.Del("ts")
|
||||
|
||||
@ -249,7 +243,7 @@ func MsgOptionPostEphemeral2(userID string) MsgOption {
|
||||
// MsgOptionMeMessage posts a "me message" type from the calling user
|
||||
func MsgOptionMeMessage() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = SLACK_API + string(chatMeMessage)
|
||||
config.endpoint = config.apiurl + string(chatMeMessage)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -257,7 +251,7 @@ func MsgOptionMeMessage() MsgOption {
|
||||
// MsgOptionUpdate updates a message based on the timestamp.
|
||||
func MsgOptionUpdate(timestamp string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = SLACK_API + string(chatUpdate)
|
||||
config.endpoint = config.apiurl + string(chatUpdate)
|
||||
config.values.Add("ts", timestamp)
|
||||
return nil
|
||||
}
|
||||
@ -266,12 +260,25 @@ func MsgOptionUpdate(timestamp string) MsgOption {
|
||||
// MsgOptionDelete deletes a message based on the timestamp.
|
||||
func MsgOptionDelete(timestamp string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = SLACK_API + string(chatDelete)
|
||||
config.endpoint = config.apiurl + string(chatDelete)
|
||||
config.values.Add("ts", timestamp)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionUnfurl unfurls a message based on the timestamp.
|
||||
func MsgOptionUnfurl(timestamp string, unfurls map[string]Attachment) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = config.apiurl + string(chatUnfurl)
|
||||
config.values.Add("ts", timestamp)
|
||||
unfurlsStr, err := json.Marshal(unfurls)
|
||||
if err == nil {
|
||||
config.values.Add("unfurls", string(unfurlsStr))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionAsUser whether or not to send the message as the user.
|
||||
func MsgOptionAsUser(b bool) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
@ -290,6 +297,14 @@ func MsgOptionUser(userID string) MsgOption {
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionUsername set the username for the message.
|
||||
func MsgOptionUsername(username string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.values.Set("username", username)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionText provide the text for the message, optionally escape the provided
|
||||
// text.
|
||||
func MsgOptionText(text string, escape bool) MsgOption {
|
||||
@ -317,6 +332,21 @@ func MsgOptionAttachments(attachments ...Attachment) MsgOption {
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionBlocks sets blocks for the message
|
||||
func MsgOptionBlocks(blocks ...Block) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
if blocks == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
blocks, err := json.Marshal(blocks)
|
||||
if err == nil {
|
||||
config.values.Set("blocks", string(blocks))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionEnableLinkUnfurl enables link unfurling
|
||||
func MsgOptionEnableLinkUnfurl() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
@ -365,7 +395,7 @@ func MsgOptionBroadcast() MsgOption {
|
||||
}
|
||||
}
|
||||
|
||||
// this function combines multiple options into a single option.
|
||||
// MsgOptionCompose combines multiple options into a single option.
|
||||
func MsgOptionCompose(options ...MsgOption) MsgOption {
|
||||
return func(c *sendConfig) error {
|
||||
for _, opt := range options {
|
||||
@ -377,19 +407,36 @@ func MsgOptionCompose(options ...MsgOption) MsgOption {
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionParse set parse option.
|
||||
func MsgOptionParse(b bool) MsgOption {
|
||||
return func(c *sendConfig) error {
|
||||
var v string
|
||||
if b {
|
||||
v = "1"
|
||||
v = "full"
|
||||
} else {
|
||||
v = "0"
|
||||
v = "none"
|
||||
}
|
||||
c.values.Set("parse", v)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionIconURL sets an icon URL
|
||||
func MsgOptionIconURL(iconURL string) MsgOption {
|
||||
return func(c *sendConfig) error {
|
||||
c.values.Set("icon_url", iconURL)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionIconEmoji sets an icon emoji
|
||||
func MsgOptionIconEmoji(iconEmoji string) MsgOption {
|
||||
return func(c *sendConfig) error {
|
||||
c.values.Set("icon_emoji", iconEmoji)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// UnsafeMsgOptionEndpoint deliver the message to the specified endpoint.
|
||||
// NOTE: USE AT YOUR OWN RISK: No issues relating to the use of this Option
|
||||
// will be supported by the library, it is subject to change without notice that
|
||||
@ -456,3 +503,38 @@ func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// PermalinkParameters are the parameters required to get a permalink to a
|
||||
// message. Slack documentation can be found here:
|
||||
// https://api.slack.com/methods/chat.getPermalink
|
||||
type PermalinkParameters struct {
|
||||
Channel string
|
||||
Ts string
|
||||
}
|
||||
|
||||
// GetPermalink returns the permalink for a message. It takes
|
||||
// PermalinkParameters and returns a string containing the permalink. It
|
||||
// returns an error if unable to retrieve the permalink.
|
||||
func (api *Client) GetPermalink(params *PermalinkParameters) (string, error) {
|
||||
return api.GetPermalinkContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetPermalinkContext returns the permalink for a message using a custom context.
|
||||
func (api *Client) GetPermalinkContext(ctx context.Context, params *PermalinkParameters) (string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {params.Channel},
|
||||
"message_ts": {params.Ts},
|
||||
}
|
||||
|
||||
response := struct {
|
||||
Channel string `json:"channel"`
|
||||
Permalink string `json:"permalink"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err := api.getMethod(ctx, "chat.getPermalink", values, &response)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return response.Permalink, response.Err()
|
||||
}
|
||||
|
81
vendor/github.com/nlopes/slack/conversation.go
generated
vendored
81
vendor/github.com/nlopes/slack/conversation.go
generated
vendored
@ -2,14 +2,13 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Conversation is the foundation for IM and BaseGroupConversation
|
||||
type conversation struct {
|
||||
type Conversation struct {
|
||||
ID string `json:"id"`
|
||||
Created JSONTime `json:"created"`
|
||||
IsOpen bool `json:"is_open"`
|
||||
@ -36,8 +35,8 @@ type conversation struct {
|
||||
}
|
||||
|
||||
// GroupConversation is the foundation for Group and Channel
|
||||
type groupConversation struct {
|
||||
conversation
|
||||
type GroupConversation struct {
|
||||
Conversation
|
||||
Name string `json:"name"`
|
||||
Creator string `json:"creator"`
|
||||
IsArchived bool `json:"is_archived"`
|
||||
@ -67,10 +66,11 @@ type GetUsersInConversationParameters struct {
|
||||
}
|
||||
|
||||
type GetConversationsForUserParameters struct {
|
||||
UserID string
|
||||
Cursor string
|
||||
Types []string
|
||||
Limit int
|
||||
UserID string
|
||||
Cursor string
|
||||
Types []string
|
||||
Limit int
|
||||
ExcludeArchived bool
|
||||
}
|
||||
|
||||
type responseMetaData struct {
|
||||
@ -99,13 +99,15 @@ func (api *Client) GetUsersInConversationContext(ctx context.Context, params *Ge
|
||||
ResponseMetaData responseMetaData `json:"response_metadata"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.members", values, &response, api.debug)
|
||||
err := api.postMethod(ctx, "conversations.members", values, &response)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, "", errors.New(response.Error)
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return response.Members, response.ResponseMetaData.NextCursor, nil
|
||||
}
|
||||
|
||||
@ -118,7 +120,9 @@ func (api *Client) GetConversationsForUser(params *GetConversationsForUserParame
|
||||
func (api *Client) GetConversationsForUserContext(ctx context.Context, params *GetConversationsForUserParameters) (channels []Channel, nextCursor string, err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"user": {params.UserID},
|
||||
}
|
||||
if params.UserID != "" {
|
||||
values.Add("user", params.UserID)
|
||||
}
|
||||
if params.Cursor != "" {
|
||||
values.Add("cursor", params.Cursor)
|
||||
@ -129,19 +133,20 @@ func (api *Client) GetConversationsForUserContext(ctx context.Context, params *G
|
||||
if params.Types != nil {
|
||||
values.Add("types", strings.Join(params.Types, ","))
|
||||
}
|
||||
if params.ExcludeArchived {
|
||||
values.Add("exclude_archived", "true")
|
||||
}
|
||||
response := struct {
|
||||
Channels []Channel `json:"channels"`
|
||||
ResponseMetaData responseMetaData `json:"response_metadata"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err = postSlackMethod(ctx, api.httpclient, "users.conversations", values, &response, api.debug)
|
||||
err = api.postMethod(ctx, "users.conversations", values, &response)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, "", errors.New(response.Error)
|
||||
}
|
||||
return response.Channels, response.ResponseMetaData.NextCursor, nil
|
||||
|
||||
return response.Channels, response.ResponseMetaData.NextCursor, response.Err()
|
||||
}
|
||||
|
||||
// ArchiveConversation archives a conversation
|
||||
@ -156,7 +161,7 @@ func (api *Client) ArchiveConversationContext(ctx context.Context, channelID str
|
||||
"channel": {channelID},
|
||||
}
|
||||
response := SlackResponse{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.archive", values, &response, api.debug)
|
||||
err := api.postMethod(ctx, "conversations.archive", values, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -176,7 +181,7 @@ func (api *Client) UnArchiveConversationContext(ctx context.Context, channelID s
|
||||
"channel": {channelID},
|
||||
}
|
||||
response := SlackResponse{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.unarchive", values, &response, api.debug)
|
||||
err := api.postMethod(ctx, "conversations.unarchive", values, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -200,7 +205,7 @@ func (api *Client) SetTopicOfConversationContext(ctx context.Context, channelID,
|
||||
SlackResponse
|
||||
Channel *Channel `json:"channel"`
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.setTopic", values, &response, api.debug)
|
||||
err := api.postMethod(ctx, "conversations.setTopic", values, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -224,7 +229,7 @@ func (api *Client) SetPurposeOfConversationContext(ctx context.Context, channelI
|
||||
SlackResponse
|
||||
Channel *Channel `json:"channel"`
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.setPurpose", values, &response, api.debug)
|
||||
err := api.postMethod(ctx, "conversations.setPurpose", values, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -248,7 +253,7 @@ func (api *Client) RenameConversationContext(ctx context.Context, channelID, cha
|
||||
SlackResponse
|
||||
Channel *Channel `json:"channel"`
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.rename", values, &response, api.debug)
|
||||
err := api.postMethod(ctx, "conversations.rename", values, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -272,7 +277,7 @@ func (api *Client) InviteUsersToConversationContext(ctx context.Context, channel
|
||||
SlackResponse
|
||||
Channel *Channel `json:"channel"`
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.invite", values, &response, api.debug)
|
||||
err := api.postMethod(ctx, "conversations.invite", values, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -293,7 +298,7 @@ func (api *Client) KickUserFromConversationContext(ctx context.Context, channelI
|
||||
"user": {user},
|
||||
}
|
||||
response := SlackResponse{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.kick", values, &response, api.debug)
|
||||
err := api.postMethod(ctx, "conversations.kick", values, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -318,7 +323,7 @@ func (api *Client) CloseConversationContext(ctx context.Context, channelID strin
|
||||
AlreadyClosed bool `json:"already_closed"`
|
||||
}{}
|
||||
|
||||
err = postSlackMethod(ctx, api.httpclient, "conversations.close", values, &response, api.debug)
|
||||
err = api.postMethod(ctx, "conversations.close", values, &response)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
@ -338,13 +343,12 @@ func (api *Client) CreateConversationContext(ctx context.Context, channelName st
|
||||
"name": {channelName},
|
||||
"is_private": {strconv.FormatBool(isPrivate)},
|
||||
}
|
||||
response, err := channelRequest(
|
||||
ctx, api.httpclient, "conversations.create", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "conversations.create", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.Channel, response.Err()
|
||||
return &response.Channel, nil
|
||||
}
|
||||
|
||||
// GetConversationInfo retrieves information about a conversation
|
||||
@ -359,8 +363,7 @@ func (api *Client) GetConversationInfoContext(ctx context.Context, channelID str
|
||||
"channel": {channelID},
|
||||
"include_locale": {strconv.FormatBool(includeLocale)},
|
||||
}
|
||||
response, err := channelRequest(
|
||||
ctx, api.httpclient, "conversations.info", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "conversations.info", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -380,7 +383,7 @@ func (api *Client) LeaveConversationContext(ctx context.Context, channelID strin
|
||||
"channel": {channelID},
|
||||
}
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "conversations.leave", values, api.debug)
|
||||
response, err := api.channelRequest(ctx, "conversations.leave", values)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -436,7 +439,7 @@ func (api *Client) GetConversationRepliesContext(ctx context.Context, params *Ge
|
||||
Messages []Message `json:"messages"`
|
||||
}{}
|
||||
|
||||
err = postSlackMethod(ctx, api.httpclient, "conversations.replies", values, &response, api.debug)
|
||||
err = api.postMethod(ctx, "conversations.replies", values, &response)
|
||||
if err != nil {
|
||||
return nil, false, "", err
|
||||
}
|
||||
@ -476,7 +479,7 @@ func (api *Client) GetConversationsContext(ctx context.Context, params *GetConve
|
||||
ResponseMetaData responseMetaData `json:"response_metadata"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err = postSlackMethod(ctx, api.httpclient, "conversations.list", values, &response, api.debug)
|
||||
err = api.postMethod(ctx, "conversations.list", values, &response)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
@ -513,7 +516,7 @@ func (api *Client) OpenConversationContext(ctx context.Context, params *OpenConv
|
||||
AlreadyOpen bool `json:"already_open"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.open", values, &response, api.debug)
|
||||
err := api.postMethod(ctx, "conversations.open", values, &response)
|
||||
if err != nil {
|
||||
return nil, false, false, err
|
||||
}
|
||||
@ -537,7 +540,7 @@ func (api *Client) JoinConversationContext(ctx context.Context, channelID string
|
||||
} `json:"response_metadata"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.join", values, &response, api.debug)
|
||||
err := api.postMethod(ctx, "conversations.join", values, &response)
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
@ -599,12 +602,10 @@ func (api *Client) GetConversationHistoryContext(ctx context.Context, params *Ge
|
||||
|
||||
response := GetConversationHistoryResponse{}
|
||||
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.history", values, &response, api.debug)
|
||||
err := api.postMethod(ctx, "conversations.history", values, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return &response, nil
|
||||
|
||||
return &response, response.Err()
|
||||
}
|
||||
|
61
vendor/github.com/nlopes/slack/dialog.go
generated
vendored
61
vendor/github.com/nlopes/slack/dialog.go
generated
vendored
@ -3,7 +3,7 @@ package slack
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// InputType is the type of the dialog input type
|
||||
@ -14,7 +14,7 @@ const (
|
||||
InputTypeText InputType = "text"
|
||||
// InputTypeTextArea textarea input
|
||||
InputTypeTextArea InputType = "textarea"
|
||||
// InputTypeSelect textfield input
|
||||
// InputTypeSelect select menus input
|
||||
InputTypeSelect InputType = "select"
|
||||
)
|
||||
|
||||
@ -25,6 +25,7 @@ type DialogInput struct {
|
||||
Name string `json:"name"`
|
||||
Placeholder string `json:"placeholder"`
|
||||
Optional bool `json:"optional"`
|
||||
Hint string `json:"hint"`
|
||||
}
|
||||
|
||||
// DialogTrigger ...
|
||||
@ -36,8 +37,9 @@ type DialogTrigger struct {
|
||||
// Dialog as in Slack dialogs
|
||||
// https://api.slack.com/dialogs#option_element_attributes#top-level_dialog_attributes
|
||||
type Dialog struct {
|
||||
TriggerID string `json:"trigger_id"` //Required
|
||||
CallbackID string `json:"callback_id"` //Required
|
||||
TriggerID string `json:"trigger_id"` // Required
|
||||
CallbackID string `json:"callback_id"` // Required
|
||||
State string `json:"state,omitempty"` // Optional
|
||||
Title string `json:"title"`
|
||||
SubmitLabel string `json:"submit_label,omitempty"`
|
||||
NotifyOnCancel bool `json:"notify_on_cancel"`
|
||||
@ -47,30 +49,13 @@ type Dialog struct {
|
||||
// DialogElement abstract type for dialogs.
|
||||
type DialogElement interface{}
|
||||
|
||||
// DialogCallback is sent from Slack when a user submits a form from within a dialog
|
||||
type DialogCallback struct {
|
||||
Type string `json:"type"`
|
||||
CallbackID string `json:"callback_id"`
|
||||
Team Team `json:"team"`
|
||||
Channel Channel `json:"channel"`
|
||||
User User `json:"user"`
|
||||
ActionTs string `json:"action_ts"`
|
||||
Token string `json:"token"`
|
||||
ResponseURL string `json:"response_url"`
|
||||
Submission map[string]string `json:"submission"`
|
||||
}
|
||||
// DialogCallback DEPRECATED use InteractionCallback
|
||||
type DialogCallback InteractionCallback
|
||||
|
||||
// DialogSuggestionCallback is sent from Slack when a user types in a select field with an external data source
|
||||
type DialogSuggestionCallback struct {
|
||||
Type string `json:"type"`
|
||||
Token string `json:"token"`
|
||||
ActionTs string `json:"action_ts"`
|
||||
Team Team `json:"team"`
|
||||
User User `json:"user"`
|
||||
Channel Channel `json:"channel"`
|
||||
ElementName string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
CallbackID string `json:"callback_id"`
|
||||
// DialogSubmissionCallback is sent from Slack when a user submits a form from within a dialog
|
||||
type DialogSubmissionCallback struct {
|
||||
State string `json:"state,omitempty"`
|
||||
Submission map[string]string `json:"submission"`
|
||||
}
|
||||
|
||||
// DialogOpenResponse response from `dialog.open`
|
||||
@ -84,6 +69,17 @@ type DialogResponseMetadata struct {
|
||||
Messages []string `json:"messages"`
|
||||
}
|
||||
|
||||
// DialogInputValidationError is an error when user inputs incorrect value to form from within a dialog
|
||||
type DialogInputValidationError struct {
|
||||
Name string `json:"name"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// DialogInputValidationErrors lists the name of field and that error messages
|
||||
type DialogInputValidationErrors struct {
|
||||
Errors []DialogInputValidationError `json:"errors"`
|
||||
}
|
||||
|
||||
// OpenDialog opens a dialog window where the triggerID originated from.
|
||||
// EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable.
|
||||
func (api *Client) OpenDialog(triggerID string, dialog Dialog) (err error) {
|
||||
@ -94,7 +90,7 @@ func (api *Client) OpenDialog(triggerID string, dialog Dialog) (err error) {
|
||||
// EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable.
|
||||
func (api *Client) OpenDialogContext(ctx context.Context, triggerID string, dialog Dialog) (err error) {
|
||||
if triggerID == "" {
|
||||
return errors.New("received empty parameters")
|
||||
return ErrParametersMissing
|
||||
}
|
||||
|
||||
req := DialogTrigger{
|
||||
@ -108,10 +104,15 @@ func (api *Client) OpenDialogContext(ctx context.Context, triggerID string, dial
|
||||
}
|
||||
|
||||
response := &DialogOpenResponse{}
|
||||
endpoint := SLACK_API + "dialog.open"
|
||||
if err := postJSON(ctx, api.httpclient, endpoint, api.token, encoded, response, api.debug); err != nil {
|
||||
endpoint := api.endpoint + "dialog.open"
|
||||
if err := postJSON(ctx, api.httpclient, endpoint, api.token, encoded, response, api); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(response.DialogResponseMetadata.Messages) > 0 {
|
||||
response.Ok = false
|
||||
response.Error += "\n" + strings.Join(response.DialogResponseMetadata.Messages, "\n")
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
42
vendor/github.com/nlopes/slack/dialog_select.go
generated
vendored
42
vendor/github.com/nlopes/slack/dialog_select.go
generated
vendored
@ -24,6 +24,8 @@ type DialogInputSelect struct {
|
||||
SelectedOptions string `json:"selected_options,omitempty"` //Optional. Default value for "external" only
|
||||
Options []DialogSelectOption `json:"options,omitempty"` //One of options or option_groups is required.
|
||||
OptionGroups []DialogOptionGroup `json:"option_groups,omitempty"` //Provide up to 100 options.
|
||||
MinQueryLength int `json:"min_query_length,omitempty"` //Optional. minimum characters before query is sent.
|
||||
Hint string `json:"hint,omitempty"` //Optional. Additional hint text.
|
||||
}
|
||||
|
||||
// DialogSelectOption is an option for the user to select from the menu
|
||||
@ -53,14 +55,7 @@ func NewStaticSelectDialogInput(name, label string, options []DialogSelectOption
|
||||
}
|
||||
|
||||
// NewGroupedSelectDialogInput creates grouped options select input for Dialogs.
|
||||
func NewGroupedSelectDialogInput(name, label string, groups map[string]map[string]string) *DialogInputSelect {
|
||||
optionGroups := []DialogOptionGroup{}
|
||||
for groupName, options := range groups {
|
||||
optionGroups = append(optionGroups, DialogOptionGroup{
|
||||
Label: groupName,
|
||||
Options: optionsFromMap(options),
|
||||
})
|
||||
}
|
||||
func NewGroupedSelectDialogInput(name, label string, options []DialogOptionGroup) *DialogInputSelect {
|
||||
return &DialogInputSelect{
|
||||
DialogInput: DialogInput{
|
||||
Type: InputTypeSelect,
|
||||
@ -68,34 +63,15 @@ func NewGroupedSelectDialogInput(name, label string, groups map[string]map[strin
|
||||
Label: label,
|
||||
},
|
||||
DataSource: DialogDataSourceStatic,
|
||||
OptionGroups: optionGroups,
|
||||
}
|
||||
OptionGroups: options}
|
||||
}
|
||||
|
||||
func optionsFromArray(options []string) []DialogSelectOption {
|
||||
selectOptions := make([]DialogSelectOption, len(options))
|
||||
for idx, value := range options {
|
||||
selectOptions[idx] = DialogSelectOption{
|
||||
Label: value,
|
||||
Value: value,
|
||||
}
|
||||
// NewDialogOptionGroup creates a DialogOptionGroup from several select options
|
||||
func NewDialogOptionGroup(label string, options ...DialogSelectOption) DialogOptionGroup {
|
||||
return DialogOptionGroup{
|
||||
Label: label,
|
||||
Options: options,
|
||||
}
|
||||
return selectOptions
|
||||
}
|
||||
|
||||
func optionsFromMap(options map[string]string) []DialogSelectOption {
|
||||
selectOptions := make([]DialogSelectOption, len(options))
|
||||
idx := 0
|
||||
var option DialogSelectOption
|
||||
for key, value := range options {
|
||||
option = DialogSelectOption{
|
||||
Label: key,
|
||||
Value: value,
|
||||
}
|
||||
selectOptions[idx] = option
|
||||
idx++
|
||||
}
|
||||
return selectOptions
|
||||
}
|
||||
|
||||
// NewConversationsSelect returns a `Conversations` select
|
||||
|
29
vendor/github.com/nlopes/slack/dnd.go
generated
vendored
29
vendor/github.com/nlopes/slack/dnd.go
generated
vendored
@ -2,7 +2,6 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -36,16 +35,14 @@ type dndTeamInfoResponse struct {
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func dndRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*dndResponseFull, error) {
|
||||
func (api *Client) dndRequest(ctx context.Context, path string, values url.Values) (*dndResponseFull, error) {
|
||||
response := &dndResponseFull{}
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response, nil
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// EndDND ends the user's scheduled Do Not Disturb session
|
||||
@ -61,7 +58,7 @@ func (api *Client) EndDNDContext(ctx context.Context) error {
|
||||
|
||||
response := &SlackResponse{}
|
||||
|
||||
if err := postSlackMethod(ctx, api.httpclient, "dnd.endDnd", values, response, api.debug); err != nil {
|
||||
if err := api.postMethod(ctx, "dnd.endDnd", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -79,7 +76,7 @@ func (api *Client) EndSnoozeContext(ctx context.Context) (*DNDStatus, error) {
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response, err := dndRequest(ctx, api.httpclient, "dnd.endSnooze", values, api.debug)
|
||||
response, err := api.dndRequest(ctx, "dnd.endSnooze", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -100,7 +97,7 @@ func (api *Client) GetDNDInfoContext(ctx context.Context, user *string) (*DNDSta
|
||||
values.Set("user", *user)
|
||||
}
|
||||
|
||||
response, err := dndRequest(ctx, api.httpclient, "dnd.info", values, api.debug)
|
||||
response, err := api.dndRequest(ctx, "dnd.info", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -120,12 +117,14 @@ func (api *Client) GetDNDTeamInfoContext(ctx context.Context, users []string) (m
|
||||
}
|
||||
response := &dndTeamInfoResponse{}
|
||||
|
||||
if err := postSlackMethod(ctx, api.httpclient, "dnd.teamInfo", values, response, api.debug); err != nil {
|
||||
if err := api.postMethod(ctx, "dnd.teamInfo", values, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
|
||||
if response.Err() != nil {
|
||||
return nil, response.Err()
|
||||
}
|
||||
|
||||
return response.Users, nil
|
||||
}
|
||||
|
||||
@ -136,7 +135,7 @@ func (api *Client) SetSnooze(minutes int) (*DNDStatus, error) {
|
||||
return api.SetSnoozeContext(context.Background(), minutes)
|
||||
}
|
||||
|
||||
// SetSnooze adjusts the snooze duration for a user's Do Not Disturb settings with a custom context.
|
||||
// SetSnoozeContext adjusts the snooze duration for a user's Do Not Disturb settings with a custom context.
|
||||
// For more information see the SetSnooze docs
|
||||
func (api *Client) SetSnoozeContext(ctx context.Context, minutes int) (*DNDStatus, error) {
|
||||
values := url.Values{
|
||||
@ -144,7 +143,7 @@ func (api *Client) SetSnoozeContext(ctx context.Context, minutes int) (*DNDStatu
|
||||
"num_minutes": {strconv.Itoa(minutes)},
|
||||
}
|
||||
|
||||
response, err := dndRequest(ctx, api.httpclient, "dnd.setSnooze", values, api.debug)
|
||||
response, err := api.dndRequest(ctx, "dnd.setSnooze", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
9
vendor/github.com/nlopes/slack/emoji.go
generated
vendored
9
vendor/github.com/nlopes/slack/emoji.go
generated
vendored
@ -2,7 +2,6 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
@ -23,12 +22,14 @@ func (api *Client) GetEmojiContext(ctx context.Context) (map[string]string, erro
|
||||
}
|
||||
response := &emojiResponseFull{}
|
||||
|
||||
err := postSlackMethod(ctx, api.httpclient, "emoji.list", values, response, api.debug)
|
||||
err := api.postMethod(ctx, "emoji.list", values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
|
||||
if response.Err() != nil {
|
||||
return nil, response.Err()
|
||||
}
|
||||
|
||||
return response.Emoji, nil
|
||||
}
|
||||
|
18
vendor/github.com/nlopes/slack/errors.go
generated
vendored
Normal file
18
vendor/github.com/nlopes/slack/errors.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package slack
|
||||
|
||||
import "github.com/nlopes/slack/internal/errorsx"
|
||||
|
||||
// Errors returned by various methods.
|
||||
const (
|
||||
ErrAlreadyDisconnected = errorsx.String("Invalid call to Disconnect - Slack API is already disconnected")
|
||||
ErrRTMDisconnected = errorsx.String("disconnect received while trying to connect")
|
||||
ErrParametersMissing = errorsx.String("received empty parameters")
|
||||
ErrInvalidConfiguration = errorsx.String("invalid configuration")
|
||||
ErrMissingHeaders = errorsx.String("missing headers")
|
||||
ErrExpiredTimestamp = errorsx.String("timestamp is too old")
|
||||
)
|
||||
|
||||
// internal errors
|
||||
const (
|
||||
errPaginationComplete = errorsx.String("pagination complete")
|
||||
)
|
112
vendor/github.com/nlopes/slack/files.go
generated
vendored
112
vendor/github.com/nlopes/slack/files.go
generated
vendored
@ -2,7 +2,7 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@ -86,12 +86,30 @@ type File struct {
|
||||
CommentsCount int `json:"comments_count"`
|
||||
NumStars int `json:"num_stars"`
|
||||
IsStarred bool `json:"is_starred"`
|
||||
Shares Share `json:"shares"`
|
||||
}
|
||||
|
||||
type Share struct {
|
||||
Public map[string][]ShareFileInfo `json:"public"`
|
||||
}
|
||||
|
||||
type ShareFileInfo struct {
|
||||
ReplyUsers []string `json:"reply_users"`
|
||||
ReplyUsersCount int `json:"reply_users_count"`
|
||||
ReplyCount int `json:"reply_count"`
|
||||
Ts string `json:"ts"`
|
||||
ThreadTs string `json:"thread_ts"`
|
||||
LatestReply string `json:"latest_reply"`
|
||||
ChannelName string `json:"channel_name"`
|
||||
TeamID string `json:"team_id"`
|
||||
}
|
||||
|
||||
// FileUploadParameters contains all the parameters necessary (including the optional ones) for an UploadFile() request.
|
||||
//
|
||||
// There are three ways to upload a file. You can either set Content if file is small, set Reader if file is large,
|
||||
// or provide a local file path in File to upload it from your filesystem.
|
||||
//
|
||||
// Note that when using the Reader option, you *must* specify the Filename, otherwise the Slack API isn't happy.
|
||||
type FileUploadParameters struct {
|
||||
File string
|
||||
Content string
|
||||
@ -115,11 +133,21 @@ type GetFilesParameters struct {
|
||||
Page int
|
||||
}
|
||||
|
||||
// ListFilesParameters contains all the parameters necessary (including the optional ones) for a ListFiles() request
|
||||
type ListFilesParameters struct {
|
||||
Limit int
|
||||
User string
|
||||
Channel string
|
||||
Types string
|
||||
Cursor string
|
||||
}
|
||||
|
||||
type fileResponseFull struct {
|
||||
File `json:"file"`
|
||||
Paging `json:"paging"`
|
||||
Comments []Comment `json:"comments"`
|
||||
Files []File `json:"files"`
|
||||
Comments []Comment `json:"comments"`
|
||||
Files []File `json:"files"`
|
||||
Metadata ResponseMetadata `json:"response_metadata"`
|
||||
|
||||
SlackResponse
|
||||
}
|
||||
@ -137,16 +165,14 @@ func NewGetFilesParameters() GetFilesParameters {
|
||||
}
|
||||
}
|
||||
|
||||
func fileRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*fileResponseFull, error) {
|
||||
func (api *Client) fileRequest(ctx context.Context, path string, values url.Values) (*fileResponseFull, error) {
|
||||
response := &fileResponseFull{}
|
||||
err := postForm(ctx, client, SLACK_API+path, values, response, debug)
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response, nil
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// GetFileInfo retrieves a file and related comments
|
||||
@ -163,18 +189,57 @@ func (api *Client) GetFileInfoContext(ctx context.Context, fileID string, count,
|
||||
"page": {strconv.Itoa(page)},
|
||||
}
|
||||
|
||||
response, err := fileRequest(ctx, api.httpclient, "files.info", values, api.debug)
|
||||
response, err := api.fileRequest(ctx, "files.info", values)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return &response.File, response.Comments, &response.Paging, nil
|
||||
}
|
||||
|
||||
// GetFile retreives a given file from its private download URL
|
||||
func (api *Client) GetFile(downloadURL string, writer io.Writer) error {
|
||||
return downloadFile(api.httpclient, api.token, downloadURL, writer, api)
|
||||
}
|
||||
|
||||
// GetFiles retrieves all files according to the parameters given
|
||||
func (api *Client) GetFiles(params GetFilesParameters) ([]File, *Paging, error) {
|
||||
return api.GetFilesContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// ListFiles retrieves all files according to the parameters given. Uses cursor based pagination.
|
||||
func (api *Client) ListFiles(params ListFilesParameters) ([]File, *ListFilesParameters, error) {
|
||||
return api.ListFilesContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// ListFilesContext retrieves all files according to the parameters given with a custom context. Uses cursor based pagination.
|
||||
func (api *Client) ListFilesContext(ctx context.Context, params ListFilesParameters) ([]File, *ListFilesParameters, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
if params.User != DEFAULT_FILES_USER {
|
||||
values.Add("user", params.User)
|
||||
}
|
||||
if params.Channel != DEFAULT_FILES_CHANNEL {
|
||||
values.Add("channel", params.Channel)
|
||||
}
|
||||
if params.Limit != DEFAULT_FILES_COUNT {
|
||||
values.Add("limit", strconv.Itoa(params.Limit))
|
||||
}
|
||||
if params.Cursor != "" {
|
||||
values.Add("cursor", params.Cursor)
|
||||
}
|
||||
|
||||
response, err := api.fileRequest(ctx, "files.list", values)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
params.Cursor = response.Metadata.Cursor
|
||||
|
||||
return response.Files, ¶ms, nil
|
||||
}
|
||||
|
||||
// GetFilesContext retrieves all files according to the parameters given with a custom context
|
||||
func (api *Client) GetFilesContext(ctx context.Context, params GetFilesParameters) ([]File, *Paging, error) {
|
||||
values := url.Values{
|
||||
@ -202,7 +267,7 @@ func (api *Client) GetFilesContext(ctx context.Context, params GetFilesParameter
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
|
||||
response, err := fileRequest(ctx, api.httpclient, "files.list", values, api.debug)
|
||||
response, err := api.fileRequest(ctx, "files.list", values)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -222,6 +287,9 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if params.Filename == "" {
|
||||
return nil, fmt.Errorf("files.upload: FileUploadParameters.Filename is mandatory")
|
||||
}
|
||||
response := &fileResponseFull{}
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
@ -246,19 +314,17 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam
|
||||
}
|
||||
if params.Content != "" {
|
||||
values.Add("content", params.Content)
|
||||
err = postForm(ctx, api.httpclient, SLACK_API+"files.upload", values, response, api.debug)
|
||||
err = api.postMethod(ctx, "files.upload", values, response)
|
||||
} else if params.File != "" {
|
||||
err = postLocalWithMultipartResponse(ctx, api.httpclient, "files.upload", params.File, "file", values, response, api.debug)
|
||||
err = postLocalWithMultipartResponse(ctx, api.httpclient, api.endpoint+"files.upload", params.File, "file", values, response, api)
|
||||
} else if params.Reader != nil {
|
||||
err = postWithMultipartResponse(ctx, api.httpclient, "files.upload", params.Filename, "file", values, params.Reader, response, api.debug)
|
||||
err = postWithMultipartResponse(ctx, api.httpclient, api.endpoint+"files.upload", params.Filename, "file", values, params.Reader, response, api)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return &response.File, nil
|
||||
|
||||
return &response.File, response.Err()
|
||||
}
|
||||
|
||||
// DeleteFileComment deletes a file's comment
|
||||
@ -269,7 +335,7 @@ func (api *Client) DeleteFileComment(commentID, fileID string) error {
|
||||
// DeleteFileCommentContext deletes a file's comment with a custom context
|
||||
func (api *Client) DeleteFileCommentContext(ctx context.Context, fileID, commentID string) (err error) {
|
||||
if fileID == "" || commentID == "" {
|
||||
return errors.New("received empty parameters")
|
||||
return ErrParametersMissing
|
||||
}
|
||||
|
||||
values := url.Values{
|
||||
@ -277,7 +343,7 @@ func (api *Client) DeleteFileCommentContext(ctx context.Context, fileID, comment
|
||||
"file": {fileID},
|
||||
"id": {commentID},
|
||||
}
|
||||
_, err = fileRequest(ctx, api.httpclient, "files.comments.delete", values, api.debug)
|
||||
_, err = api.fileRequest(ctx, "files.comments.delete", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -293,7 +359,7 @@ func (api *Client) DeleteFileContext(ctx context.Context, fileID string) (err er
|
||||
"file": {fileID},
|
||||
}
|
||||
|
||||
_, err = fileRequest(ctx, api.httpclient, "files.delete", values, api.debug)
|
||||
_, err = api.fileRequest(ctx, "files.delete", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -309,7 +375,7 @@ func (api *Client) RevokeFilePublicURLContext(ctx context.Context, fileID string
|
||||
"file": {fileID},
|
||||
}
|
||||
|
||||
response, err := fileRequest(ctx, api.httpclient, "files.revokePublicURL", values, api.debug)
|
||||
response, err := api.fileRequest(ctx, "files.revokePublicURL", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -328,7 +394,7 @@ func (api *Client) ShareFilePublicURLContext(ctx context.Context, fileID string)
|
||||
"file": {fileID},
|
||||
}
|
||||
|
||||
response, err := fileRequest(ctx, api.httpclient, "files.sharedPublicURL", values, api.debug)
|
||||
response, err := api.fileRequest(ctx, "files.sharedPublicURL", values)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
9
vendor/github.com/nlopes/slack/go.mod
generated
vendored
Normal file
9
vendor/github.com/nlopes/slack/go.mod
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
module github.com/nlopes/slack
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/gorilla/websocket v1.2.0
|
||||
github.com/pkg/errors v0.8.0
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
)
|
22
vendor/github.com/nlopes/slack/go.sum
generated
vendored
Normal file
22
vendor/github.com/nlopes/slack/go.sum
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
|
||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/nlopes/slack v0.1.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
|
||||
github.com/nlopes/slack v0.5.0 h1:NbIae8Kd0NpqaEI3iUrsuS0KbcEDhzhc939jLW5fNm0=
|
||||
github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/victorcoder/slack-test v0.0.0-20190131110821-6f9a569c10af h1:JFxr+No3ZWgCtxnnTWCybnB/z0Iy3qLmdj3u2NV5o48=
|
||||
github.com/victorcoder/slack-test v0.0.0-20190131110821-6f9a569c10af/go.mod h1:dStM4ShMus8J3hiq66ExbbzGLkwyZ+RQJePwFhWCCvQ=
|
||||
github.com/victorcoder/slack-test v0.0.0-20190131113129-a43b3bb77f43 h1:wtFekkaAAQibpy3iE4Hhx2Gi9pZAbITOSfVP7GXk5eM=
|
||||
github.com/victorcoder/slack-test v0.0.0-20190131113129-a43b3bb77f43/go.mod h1:dStM4ShMus8J3hiq66ExbbzGLkwyZ+RQJePwFhWCCvQ=
|
||||
golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37 h1:BkNcmLtAVeWe9h5k0jt24CQgaG5vb4x/doFbAiEC/Ho=
|
||||
golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
67
vendor/github.com/nlopes/slack/groups.go
generated
vendored
67
vendor/github.com/nlopes/slack/groups.go
generated
vendored
@ -2,14 +2,13 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Group contains all the information for a group
|
||||
type Group struct {
|
||||
groupConversation
|
||||
GroupConversation
|
||||
IsGroup bool `json:"is_group"`
|
||||
}
|
||||
|
||||
@ -28,16 +27,14 @@ type groupResponseFull struct {
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func groupRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*groupResponseFull, error) {
|
||||
func (api *Client) groupRequest(ctx context.Context, path string, values url.Values) (*groupResponseFull, error) {
|
||||
response := &groupResponseFull{}
|
||||
err := postForm(ctx, client, SLACK_API+path, values, response, debug)
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response, nil
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// ArchiveGroup archives a private group
|
||||
@ -52,7 +49,7 @@ func (api *Client) ArchiveGroupContext(ctx context.Context, group string) error
|
||||
"channel": {group},
|
||||
}
|
||||
|
||||
_, err := groupRequest(ctx, api.httpclient, "groups.archive", values, api.debug)
|
||||
_, err := api.groupRequest(ctx, "groups.archive", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -68,7 +65,7 @@ func (api *Client) UnarchiveGroupContext(ctx context.Context, group string) erro
|
||||
"channel": {group},
|
||||
}
|
||||
|
||||
_, err := groupRequest(ctx, api.httpclient, "groups.unarchive", values, api.debug)
|
||||
_, err := api.groupRequest(ctx, "groups.unarchive", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -84,7 +81,7 @@ func (api *Client) CreateGroupContext(ctx context.Context, group string) (*Group
|
||||
"name": {group},
|
||||
}
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.create", values, api.debug)
|
||||
response, err := api.groupRequest(ctx, "groups.create", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -109,32 +106,13 @@ func (api *Client) CreateChildGroupContext(ctx context.Context, group string) (*
|
||||
"channel": {group},
|
||||
}
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.createChild", values, api.debug)
|
||||
response, err := api.groupRequest(ctx, "groups.createChild", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Group, nil
|
||||
}
|
||||
|
||||
// CloseGroup closes a private group
|
||||
func (api *Client) CloseGroup(group string) (bool, bool, error) {
|
||||
return api.CloseGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// CloseGroupContext closes a private group with a custom context
|
||||
func (api *Client) CloseGroupContext(ctx context.Context, group string) (bool, bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "groups.close", values, api.debug)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
return response.NoOp, response.AlreadyClosed, nil
|
||||
}
|
||||
|
||||
// GetGroupHistory fetches all the history for a private group
|
||||
func (api *Client) GetGroupHistory(group string, params HistoryParameters) (*History, error) {
|
||||
return api.GetGroupHistoryContext(context.Background(), group, params)
|
||||
@ -170,7 +148,7 @@ func (api *Client) GetGroupHistoryContext(ctx context.Context, group string, par
|
||||
}
|
||||
}
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.history", values, api.debug)
|
||||
response, err := api.groupRequest(ctx, "groups.history", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -190,7 +168,7 @@ func (api *Client) InviteUserToGroupContext(ctx context.Context, group, user str
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.invite", values, api.debug)
|
||||
response, err := api.groupRequest(ctx, "groups.invite", values)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -209,7 +187,7 @@ func (api *Client) LeaveGroupContext(ctx context.Context, group string) (err err
|
||||
"channel": {group},
|
||||
}
|
||||
|
||||
_, err = groupRequest(ctx, api.httpclient, "groups.leave", values, api.debug)
|
||||
_, err = api.groupRequest(ctx, "groups.leave", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -226,7 +204,7 @@ func (api *Client) KickUserFromGroupContext(ctx context.Context, group, user str
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
_, err = groupRequest(ctx, api.httpclient, "groups.kick", values, api.debug)
|
||||
_, err = api.groupRequest(ctx, "groups.kick", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -244,7 +222,7 @@ func (api *Client) GetGroupsContext(ctx context.Context, excludeArchived bool) (
|
||||
values.Add("exclude_archived", "1")
|
||||
}
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.list", values, api.debug)
|
||||
response, err := api.groupRequest(ctx, "groups.list", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -259,11 +237,12 @@ func (api *Client) GetGroupInfo(group string) (*Group, error) {
|
||||
// GetGroupInfoContext retrieves the given group with a custom context
|
||||
func (api *Client) GetGroupInfoContext(ctx context.Context, group string) (*Group, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"include_locale": {strconv.FormatBool(true)},
|
||||
}
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.info", values, api.debug)
|
||||
response, err := api.groupRequest(ctx, "groups.info", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -288,7 +267,7 @@ func (api *Client) SetGroupReadMarkContext(ctx context.Context, group, ts string
|
||||
"ts": {ts},
|
||||
}
|
||||
|
||||
_, err = groupRequest(ctx, api.httpclient, "groups.mark", values, api.debug)
|
||||
_, err = api.groupRequest(ctx, "groups.mark", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -304,7 +283,7 @@ func (api *Client) OpenGroupContext(ctx context.Context, group string) (bool, bo
|
||||
"channel": {group},
|
||||
}
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.open", values, api.debug)
|
||||
response, err := api.groupRequest(ctx, "groups.open", values)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
@ -328,7 +307,7 @@ func (api *Client) RenameGroupContext(ctx context.Context, group, name string) (
|
||||
|
||||
// XXX: the created entry in this call returns a string instead of a number
|
||||
// so I may have to do some workaround to solve it.
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.rename", values, api.debug)
|
||||
response, err := api.groupRequest(ctx, "groups.rename", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -348,7 +327,7 @@ func (api *Client) SetGroupPurposeContext(ctx context.Context, group, purpose st
|
||||
"purpose": {purpose},
|
||||
}
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.setPurpose", values, api.debug)
|
||||
response, err := api.groupRequest(ctx, "groups.setPurpose", values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -368,7 +347,7 @@ func (api *Client) SetGroupTopicContext(ctx context.Context, group, topic string
|
||||
"topic": {topic},
|
||||
}
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.setTopic", values, api.debug)
|
||||
response, err := api.groupRequest(ctx, "groups.setTopic", values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
27
vendor/github.com/nlopes/slack/im.go
generated
vendored
27
vendor/github.com/nlopes/slack/im.go
generated
vendored
@ -2,7 +2,6 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
@ -23,22 +22,18 @@ type imResponseFull struct {
|
||||
|
||||
// IM contains information related to the Direct Message channel
|
||||
type IM struct {
|
||||
conversation
|
||||
IsIM bool `json:"is_im"`
|
||||
User string `json:"user"`
|
||||
IsUserDeleted bool `json:"is_user_deleted"`
|
||||
Conversation
|
||||
IsUserDeleted bool `json:"is_user_deleted"`
|
||||
}
|
||||
|
||||
func imRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*imResponseFull, error) {
|
||||
func (api *Client) imRequest(ctx context.Context, path string, values url.Values) (*imResponseFull, error) {
|
||||
response := &imResponseFull{}
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response, nil
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// CloseIMChannel closes the direct message channel
|
||||
@ -53,7 +48,7 @@ func (api *Client) CloseIMChannelContext(ctx context.Context, channel string) (b
|
||||
"channel": {channel},
|
||||
}
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "im.close", values, api.debug)
|
||||
response, err := api.imRequest(ctx, "im.close", values)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
@ -74,7 +69,7 @@ func (api *Client) OpenIMChannelContext(ctx context.Context, user string) (bool,
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "im.open", values, api.debug)
|
||||
response, err := api.imRequest(ctx, "im.open", values)
|
||||
if err != nil {
|
||||
return false, false, "", err
|
||||
}
|
||||
@ -94,7 +89,7 @@ func (api *Client) MarkIMChannelContext(ctx context.Context, channel, ts string)
|
||||
"ts": {ts},
|
||||
}
|
||||
|
||||
_, err := imRequest(ctx, api.httpclient, "im.mark", values, api.debug)
|
||||
_, err := api.imRequest(ctx, "im.mark", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -133,7 +128,7 @@ func (api *Client) GetIMHistoryContext(ctx context.Context, channel string, para
|
||||
}
|
||||
}
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "im.history", values, api.debug)
|
||||
response, err := api.imRequest(ctx, "im.history", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -151,7 +146,7 @@ func (api *Client) GetIMChannelsContext(ctx context.Context) ([]IM, error) {
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "im.list", values, api.debug)
|
||||
response, err := api.imRequest(ctx, "im.list", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
98
vendor/github.com/nlopes/slack/interactions.go
generated
vendored
Normal file
98
vendor/github.com/nlopes/slack/interactions.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// InteractionType type of interactions
|
||||
type InteractionType string
|
||||
|
||||
// ActionType type represents the type of action (attachment, block, etc.)
|
||||
type actionType string
|
||||
|
||||
// action is an interface that should be implemented by all callback action types
|
||||
type action interface {
|
||||
actionType() actionType
|
||||
}
|
||||
|
||||
// Types of interactions that can be received.
|
||||
const (
|
||||
InteractionTypeDialogCancellation = InteractionType("dialog_cancellation")
|
||||
InteractionTypeDialogSubmission = InteractionType("dialog_submission")
|
||||
InteractionTypeDialogSuggestion = InteractionType("dialog_suggestion")
|
||||
InteractionTypeInteractionMessage = InteractionType("interactive_message")
|
||||
InteractionTypeMessageAction = InteractionType("message_action")
|
||||
InteractionTypeBlockActions = InteractionType("block_actions")
|
||||
)
|
||||
|
||||
// InteractionCallback is sent from slack when a user interactions with a button or dialog.
|
||||
type InteractionCallback struct {
|
||||
Type InteractionType `json:"type"`
|
||||
Token string `json:"token"`
|
||||
CallbackID string `json:"callback_id"`
|
||||
ResponseURL string `json:"response_url"`
|
||||
TriggerID string `json:"trigger_id"`
|
||||
ActionTs string `json:"action_ts"`
|
||||
Team Team `json:"team"`
|
||||
Channel Channel `json:"channel"`
|
||||
User User `json:"user"`
|
||||
OriginalMessage Message `json:"original_message"`
|
||||
Message Message `json:"message"`
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
MessageTs string `json:"message_ts"`
|
||||
AttachmentID string `json:"attachment_id"`
|
||||
ActionCallback ActionCallbacks `json:"actions"`
|
||||
DialogSubmissionCallback
|
||||
}
|
||||
|
||||
// ActionCallback is a convenience struct defined to allow dynamic unmarshalling of
|
||||
// the "actions" value in Slack's JSON response, which varies depending on block type
|
||||
type ActionCallbacks struct {
|
||||
AttachmentActions []*AttachmentAction
|
||||
BlockActions []*BlockAction
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Marshaller interface in order to delegate
|
||||
// marshalling and allow for proper type assertion when decoding the response
|
||||
func (a *ActionCallbacks) UnmarshalJSON(data []byte) error {
|
||||
var raw []json.RawMessage
|
||||
err := json.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range raw {
|
||||
var obj map[string]interface{}
|
||||
err := json.Unmarshal(r, &obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := obj["block_id"].(string); ok {
|
||||
action, err := unmarshalAction(r, &BlockAction{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.BlockActions = append(a.BlockActions, action.(*BlockAction))
|
||||
return nil
|
||||
}
|
||||
|
||||
action, err := unmarshalAction(r, &AttachmentAction{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.AttachmentActions = append(a.AttachmentActions, action.(*AttachmentAction))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalAction(r json.RawMessage, callbackAction action) (action, error) {
|
||||
err := json.Unmarshal(r, callbackAction)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return callbackAction, nil
|
||||
}
|
8
vendor/github.com/nlopes/slack/internal/errorsx/errorsx.go
generated
vendored
Normal file
8
vendor/github.com/nlopes/slack/internal/errorsx/errorsx.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package errorsx
|
||||
|
||||
// String representing an error, useful for declaring string constants as errors.
|
||||
type String string
|
||||
|
||||
func (t String) Error() string {
|
||||
return string(t)
|
||||
}
|
18
vendor/github.com/nlopes/slack/internal/timex/timex.go
generated
vendored
Normal file
18
vendor/github.com/nlopes/slack/internal/timex/timex.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package timex
|
||||
|
||||
import "time"
|
||||
|
||||
// Max returns the maximum duration
|
||||
func Max(values ...time.Duration) time.Duration {
|
||||
var (
|
||||
max time.Duration
|
||||
)
|
||||
|
||||
for _, v := range values {
|
||||
if v > max {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
}
|
59
vendor/github.com/nlopes/slack/logger.go
generated
vendored
59
vendor/github.com/nlopes/slack/logger.go
generated
vendored
@ -2,52 +2,59 @@ package slack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// SetLogger let's library users supply a logger, so that api debugging
|
||||
// can be logged along with the application's debugging info.
|
||||
func SetLogger(l logProvider) {
|
||||
loggerMutex.Lock()
|
||||
logger = ilogger{logProvider: l}
|
||||
loggerMutex.Unlock()
|
||||
}
|
||||
|
||||
var (
|
||||
loggerMutex = new(sync.Mutex)
|
||||
logger logInternal // A logger that can be set by consumers
|
||||
)
|
||||
|
||||
// logProvider is a logger interface compatible with both stdlib and some
|
||||
// 3rd party loggers such as logrus.
|
||||
type logProvider interface {
|
||||
// logger is a logger interface compatible with both stdlib and some
|
||||
// 3rd party loggers.
|
||||
type logger interface {
|
||||
Output(int, string) error
|
||||
}
|
||||
|
||||
// logInternal represents the internal logging api we use.
|
||||
type logInternal interface {
|
||||
// ilogger represents the internal logging api we use.
|
||||
type ilogger interface {
|
||||
logger
|
||||
Print(...interface{})
|
||||
Printf(string, ...interface{})
|
||||
Println(...interface{})
|
||||
Output(int, string) error
|
||||
}
|
||||
|
||||
// ilogger implements the additional methods used by our internal logging.
|
||||
type ilogger struct {
|
||||
logProvider
|
||||
type debug interface {
|
||||
Debug() bool
|
||||
|
||||
// Debugf print a formatted debug line.
|
||||
Debugf(format string, v ...interface{})
|
||||
// Debugln print a debug line.
|
||||
Debugln(v ...interface{})
|
||||
}
|
||||
|
||||
// internalLog implements the additional methods used by our internal logging.
|
||||
type internalLog struct {
|
||||
logger
|
||||
}
|
||||
|
||||
// Println replicates the behaviour of the standard logger.
|
||||
func (t ilogger) Println(v ...interface{}) {
|
||||
func (t internalLog) Println(v ...interface{}) {
|
||||
t.Output(2, fmt.Sprintln(v...))
|
||||
}
|
||||
|
||||
// Printf replicates the behaviour of the standard logger.
|
||||
func (t ilogger) Printf(format string, v ...interface{}) {
|
||||
func (t internalLog) Printf(format string, v ...interface{}) {
|
||||
t.Output(2, fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// Print replicates the behaviour of the standard logger.
|
||||
func (t ilogger) Print(v ...interface{}) {
|
||||
func (t internalLog) Print(v ...interface{}) {
|
||||
t.Output(2, fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
type discard struct{}
|
||||
|
||||
func (t discard) Debug() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Debugf print a formatted debug line.
|
||||
func (t discard) Debugf(format string, v ...interface{}) {}
|
||||
|
||||
// Debugln print a debug line.
|
||||
func (t discard) Debugln(v ...interface{}) {}
|
||||
|
25
vendor/github.com/nlopes/slack/messages.go
generated
vendored
25
vendor/github.com/nlopes/slack/messages.go
generated
vendored
@ -4,17 +4,19 @@ package slack
|
||||
type OutgoingMessage struct {
|
||||
ID int `json:"id"`
|
||||
// channel ID
|
||||
Channel string `json:"channel,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
ThreadTimestamp string `json:"thread_ts,omitempty"`
|
||||
ThreadBroadcast bool `json:"reply_broadcast,omitempty"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
ThreadTimestamp string `json:"thread_ts,omitempty"`
|
||||
ThreadBroadcast bool `json:"reply_broadcast,omitempty"`
|
||||
IDs []string `json:"ids,omitempty"`
|
||||
}
|
||||
|
||||
// Message is an auxiliary type to allow us to have a message containing sub messages
|
||||
type Message struct {
|
||||
Msg
|
||||
SubMessage *Msg `json:"message,omitempty"`
|
||||
PreviousMessage *Msg `json:"previous_message,omitempty"`
|
||||
}
|
||||
|
||||
// Msg contains information about a slack message
|
||||
@ -91,6 +93,9 @@ type Msg struct {
|
||||
ResponseType string `json:"response_type,omitempty"`
|
||||
ReplaceOriginal bool `json:"replace_original"`
|
||||
DeleteOriginal bool `json:"delete_original"`
|
||||
|
||||
// Block type Message
|
||||
Blocks Blocks `json:"blocks,omitempty"`
|
||||
}
|
||||
|
||||
// Icon is used for bot messages
|
||||
@ -147,6 +152,15 @@ func (rtm *RTM) NewOutgoingMessage(text string, channelID string, options ...RTM
|
||||
return &msg
|
||||
}
|
||||
|
||||
// NewSubscribeUserPresence prepares an OutgoingMessage that the user can
|
||||
// use to subscribe presence events for the specified users.
|
||||
func (rtm *RTM) NewSubscribeUserPresence(ids []string) *OutgoingMessage {
|
||||
return &OutgoingMessage{
|
||||
Type: "presence_sub",
|
||||
IDs: ids,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTypingMessage prepares an OutgoingMessage that the user can
|
||||
// use to send as a typing indicator. Use this function to properly set the
|
||||
// messageID.
|
||||
@ -174,5 +188,4 @@ func RTMsgOptionBroadcast() RTMsgOption {
|
||||
return func(msg *OutgoingMessage) {
|
||||
msg.ThreadBroadcast = true
|
||||
}
|
||||
|
||||
}
|
||||
|
222
vendor/github.com/nlopes/slack/misc.go
generated
vendored
222
vendor/github.com/nlopes/slack/misc.go
generated
vendored
@ -19,6 +19,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// SlackResponse handles parsing out errors from the web api.
|
||||
type SlackResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Error string `json:"error"`
|
||||
@ -47,64 +48,89 @@ type statusCodeError struct {
|
||||
}
|
||||
|
||||
func (t statusCodeError) Error() string {
|
||||
// TODO: this is a bad error string, should clean it up with a breaking changes
|
||||
// merger.
|
||||
return fmt.Sprintf("Slack server error: %s.", t.Status)
|
||||
return fmt.Sprintf("slack server error: %s", t.Status)
|
||||
}
|
||||
|
||||
func (t statusCodeError) HTTPStatusCode() int {
|
||||
return t.Code
|
||||
}
|
||||
|
||||
func (t statusCodeError) Retryable() bool {
|
||||
if t.Code >= 500 || t.Code == http.StatusTooManyRequests {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RateLimitedError represents the rate limit respond from slack
|
||||
type RateLimitedError struct {
|
||||
RetryAfter time.Duration
|
||||
}
|
||||
|
||||
func (e *RateLimitedError) Error() string {
|
||||
return fmt.Sprintf("Slack rate limit exceeded, retry after %s", e.RetryAfter)
|
||||
return fmt.Sprintf("slack rate limit exceeded, retry after %s", e.RetryAfter)
|
||||
}
|
||||
|
||||
func fileUploadReq(ctx context.Context, path, fieldname, filename string, values url.Values, r io.Reader) (*http.Request, error) {
|
||||
body := &bytes.Buffer{}
|
||||
wr := multipart.NewWriter(body)
|
||||
func (e *RateLimitedError) Retryable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
ioWriter, err := wr.CreateFormFile(fieldname, filename)
|
||||
func fileUploadReq(ctx context.Context, path string, values url.Values, r io.Reader) (*http.Request, error) {
|
||||
req, err := http.NewRequest("POST", path, r)
|
||||
if err != nil {
|
||||
wr.Close()
|
||||
return nil, err
|
||||
}
|
||||
_, err = io.Copy(ioWriter, r)
|
||||
if err != nil {
|
||||
wr.Close()
|
||||
return nil, err
|
||||
}
|
||||
// Close the multipart writer or the footer won't be written
|
||||
wr.Close()
|
||||
req, err := http.NewRequest("POST", path, body)
|
||||
req = req.WithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Content-Type", wr.FormDataContentType())
|
||||
|
||||
req.URL.RawQuery = (values).Encode()
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func parseResponseBody(body io.ReadCloser, intf interface{}, debug bool) error {
|
||||
func downloadFile(client httpClient, token string, downloadURL string, writer io.Writer, d debug) error {
|
||||
if downloadURL == "" {
|
||||
return fmt.Errorf("received empty download URL")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", downloadURL, &bytes.Buffer{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var bearer = "Bearer " + token
|
||||
req.Header.Add("Authorization", bearer)
|
||||
req.WithContext(context.Background())
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = checkStatusCode(resp, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(writer, resp.Body)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func parseResponseBody(body io.ReadCloser, intf interface{}, d debug) error {
|
||||
response, err := ioutil.ReadAll(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME: will be api.Debugf
|
||||
if debug {
|
||||
logger.Printf("parseResponseBody: %s\n", string(response))
|
||||
if d.Debug() {
|
||||
d.Debugln("parseResponseBody", string(response))
|
||||
}
|
||||
|
||||
return json.Unmarshal(response, intf)
|
||||
}
|
||||
|
||||
func postLocalWithMultipartResponse(ctx context.Context, client HTTPRequester, path, fpath, fieldname string, values url.Values, intf interface{}, debug bool) error {
|
||||
func postLocalWithMultipartResponse(ctx context.Context, client httpClient, path, fpath, fieldname string, values url.Values, intf interface{}, d debug) error {
|
||||
fullpath, err := filepath.Abs(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -114,14 +140,57 @@ func postLocalWithMultipartResponse(ctx context.Context, client HTTPRequester, p
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return postWithMultipartResponse(ctx, client, path, filepath.Base(fpath), fieldname, values, file, intf, debug)
|
||||
return postWithMultipartResponse(ctx, client, path, filepath.Base(fpath), fieldname, values, file, intf, d)
|
||||
}
|
||||
|
||||
func postWithMultipartResponse(ctx context.Context, client HTTPRequester, path, name, fieldname string, values url.Values, r io.Reader, intf interface{}, debug bool) error {
|
||||
req, err := fileUploadReq(ctx, SLACK_API+path, fieldname, name, values, r)
|
||||
func postWithMultipartResponse(ctx context.Context, client httpClient, path, name, fieldname string, values url.Values, r io.Reader, intf interface{}, d debug) error {
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
wr := multipart.NewWriter(pipeWriter)
|
||||
errc := make(chan error)
|
||||
go func() {
|
||||
defer pipeWriter.Close()
|
||||
ioWriter, err := wr.CreateFormFile(fieldname, name)
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
_, err = io.Copy(ioWriter, r)
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
if err = wr.Close(); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
}()
|
||||
req, err := fileUploadReq(ctx, path, values, pipeReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Content-Type", wr.FormDataContentType())
|
||||
req = req.WithContext(ctx)
|
||||
resp, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = checkStatusCode(resp, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
select {
|
||||
case err = <-errc:
|
||||
return err
|
||||
default:
|
||||
return parseResponseBody(resp.Body, intf, d)
|
||||
}
|
||||
}
|
||||
|
||||
func doPost(ctx context.Context, client httpClient, req *http.Request, intf interface{}, d debug) error {
|
||||
req = req.WithContext(ctx)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
@ -129,50 +198,16 @@ func postWithMultipartResponse(ctx context.Context, client HTTPRequester, path,
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusTooManyRequests {
|
||||
retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return &RateLimitedError{time.Duration(retry) * time.Second}
|
||||
}
|
||||
|
||||
// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
logResponse(resp, debug)
|
||||
return statusCodeError{Code: resp.StatusCode, Status: resp.Status}
|
||||
}
|
||||
|
||||
return parseResponseBody(resp.Body, intf, debug)
|
||||
}
|
||||
|
||||
func doPost(ctx context.Context, client HTTPRequester, req *http.Request, intf interface{}, debug bool) error {
|
||||
req = req.WithContext(ctx)
|
||||
resp, err := client.Do(req)
|
||||
err = checkStatusCode(resp, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusTooManyRequests {
|
||||
retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return &RateLimitedError{time.Duration(retry) * time.Second}
|
||||
}
|
||||
|
||||
// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
logResponse(resp, debug)
|
||||
return statusCodeError{Code: resp.StatusCode, Status: resp.Status}
|
||||
}
|
||||
|
||||
return parseResponseBody(resp.Body, intf, debug)
|
||||
return parseResponseBody(resp.Body, intf, d)
|
||||
}
|
||||
|
||||
// post JSON.
|
||||
func postJSON(ctx context.Context, client HTTPRequester, endpoint, token string, json []byte, intf interface{}, debug bool) error {
|
||||
func postJSON(ctx context.Context, client httpClient, endpoint, token string, json []byte, intf interface{}, d debug) error {
|
||||
reqBody := bytes.NewBuffer(json)
|
||||
req, err := http.NewRequest("POST", endpoint, reqBody)
|
||||
if err != nil {
|
||||
@ -180,38 +215,43 @@ func postJSON(ctx context.Context, client HTTPRequester, endpoint, token string,
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
return doPost(ctx, client, req, intf, debug)
|
||||
return doPost(ctx, client, req, intf, d)
|
||||
}
|
||||
|
||||
// post a url encoded form.
|
||||
func postForm(ctx context.Context, client HTTPRequester, endpoint string, values url.Values, intf interface{}, debug bool) error {
|
||||
func postForm(ctx context.Context, client httpClient, endpoint string, values url.Values, intf interface{}, d debug) error {
|
||||
reqBody := strings.NewReader(values.Encode())
|
||||
req, err := http.NewRequest("POST", endpoint, reqBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
return doPost(ctx, client, req, intf, debug)
|
||||
return doPost(ctx, client, req, intf, d)
|
||||
}
|
||||
|
||||
// post to a slack web method.
|
||||
func postSlackMethod(ctx context.Context, client HTTPRequester, path string, values url.Values, intf interface{}, debug bool) error {
|
||||
return postForm(ctx, client, SLACK_API+path, values, intf, debug)
|
||||
func getResource(ctx context.Context, client httpClient, endpoint string, values url.Values, intf interface{}, d debug) error {
|
||||
req, err := http.NewRequest("GET", endpoint, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.URL.RawQuery = values.Encode()
|
||||
|
||||
return doPost(ctx, client, req, intf, d)
|
||||
}
|
||||
|
||||
func parseAdminResponse(ctx context.Context, client HTTPRequester, method string, teamName string, values url.Values, intf interface{}, debug bool) error {
|
||||
endpoint := fmt.Sprintf(SLACK_WEB_API_FORMAT, teamName, method, time.Now().Unix())
|
||||
return postForm(ctx, client, endpoint, values, intf, debug)
|
||||
func parseAdminResponse(ctx context.Context, client httpClient, method string, teamName string, values url.Values, intf interface{}, d debug) error {
|
||||
endpoint := fmt.Sprintf(WEBAPIURLFormat, teamName, method, time.Now().Unix())
|
||||
return postForm(ctx, client, endpoint, values, intf, d)
|
||||
}
|
||||
|
||||
func logResponse(resp *http.Response, debug bool) error {
|
||||
if debug {
|
||||
func logResponse(resp *http.Response, d debug) error {
|
||||
if d.Debug() {
|
||||
text, err := httputil.DumpResponse(resp, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Print(string(text))
|
||||
d.Debugln(string(text))
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -225,12 +265,6 @@ func okJSONHandler(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Write(response)
|
||||
}
|
||||
|
||||
type errorString string
|
||||
|
||||
func (t errorString) Error() string {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
// timerReset safely reset a timer, see time.Timer.Reset for details.
|
||||
func timerReset(t *time.Timer, d time.Duration) {
|
||||
if !t.Stop() {
|
||||
@ -238,3 +272,21 @@ func timerReset(t *time.Timer, d time.Duration) {
|
||||
}
|
||||
t.Reset(d)
|
||||
}
|
||||
|
||||
func checkStatusCode(resp *http.Response, d debug) error {
|
||||
if resp.StatusCode == http.StatusTooManyRequests {
|
||||
retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return &RateLimitedError{time.Duration(retry) * time.Second}
|
||||
}
|
||||
|
||||
// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
logResponse(resp, d)
|
||||
return statusCodeError{Code: resp.StatusCode, Status: resp.Status}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
26
vendor/github.com/nlopes/slack/oauth.go
generated
vendored
26
vendor/github.com/nlopes/slack/oauth.go
generated
vendored
@ -2,10 +2,10 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// OAuthResponseIncomingWebhook ...
|
||||
type OAuthResponseIncomingWebhook struct {
|
||||
URL string `json:"url"`
|
||||
Channel string `json:"channel"`
|
||||
@ -13,11 +13,13 @@ type OAuthResponseIncomingWebhook struct {
|
||||
ConfigurationURL string `json:"configuration_url"`
|
||||
}
|
||||
|
||||
// OAuthResponseBot ...
|
||||
type OAuthResponseBot struct {
|
||||
BotUserID string `json:"bot_user_id"`
|
||||
BotAccessToken string `json:"bot_access_token"`
|
||||
}
|
||||
|
||||
// OAuthResponse ...
|
||||
type OAuthResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
Scope string `json:"scope"`
|
||||
@ -30,24 +32,24 @@ type OAuthResponse struct {
|
||||
}
|
||||
|
||||
// GetOAuthToken retrieves an AccessToken
|
||||
func GetOAuthToken(clientID, clientSecret, code, redirectURI string, debug bool) (accessToken string, scope string, err error) {
|
||||
return GetOAuthTokenContext(context.Background(), clientID, clientSecret, code, redirectURI, debug)
|
||||
func GetOAuthToken(client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, err error) {
|
||||
return GetOAuthTokenContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
|
||||
}
|
||||
|
||||
// GetOAuthTokenContext retrieves an AccessToken with a custom context
|
||||
func GetOAuthTokenContext(ctx context.Context, clientID, clientSecret, code, redirectURI string, debug bool) (accessToken string, scope string, err error) {
|
||||
response, err := GetOAuthResponseContext(ctx, clientID, clientSecret, code, redirectURI, debug)
|
||||
func GetOAuthTokenContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, err error) {
|
||||
response, err := GetOAuthResponseContext(ctx, client, clientID, clientSecret, code, redirectURI)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return response.AccessToken, response.Scope, nil
|
||||
}
|
||||
|
||||
func GetOAuthResponse(clientID, clientSecret, code, redirectURI string, debug bool) (resp *OAuthResponse, err error) {
|
||||
return GetOAuthResponseContext(context.Background(), clientID, clientSecret, code, redirectURI, debug)
|
||||
func GetOAuthResponse(client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) {
|
||||
return GetOAuthResponseContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
|
||||
}
|
||||
|
||||
func GetOAuthResponseContext(ctx context.Context, clientID, clientSecret, code, redirectURI string, debug bool) (resp *OAuthResponse, err error) {
|
||||
func GetOAuthResponseContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) {
|
||||
values := url.Values{
|
||||
"client_id": {clientID},
|
||||
"client_secret": {clientSecret},
|
||||
@ -55,12 +57,8 @@ func GetOAuthResponseContext(ctx context.Context, clientID, clientSecret, code,
|
||||
"redirect_uri": {redirectURI},
|
||||
}
|
||||
response := &OAuthResponse{}
|
||||
err = postSlackMethod(ctx, customHTTPClient, "oauth.access", values, response, debug)
|
||||
if err != nil {
|
||||
if err = postForm(ctx, client, APIURL+"oauth.access", values, response, discard{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response, nil
|
||||
return response, response.Err()
|
||||
}
|
||||
|
6
vendor/github.com/nlopes/slack/pins.go
generated
vendored
6
vendor/github.com/nlopes/slack/pins.go
generated
vendored
@ -34,7 +34,7 @@ func (api *Client) AddPinContext(ctx context.Context, channel string, item ItemR
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := postSlackMethod(ctx, api.httpclient, "pins.add", values, response, api.debug); err != nil {
|
||||
if err := api.postMethod(ctx, "pins.add", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ func (api *Client) RemovePinContext(ctx context.Context, channel string, item It
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := postSlackMethod(ctx, api.httpclient, "pins.remove", values, response, api.debug); err != nil {
|
||||
if err := api.postMethod(ctx, "pins.remove", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@ func (api *Client) ListPinsContext(ctx context.Context, channel string) ([]Item,
|
||||
}
|
||||
|
||||
response := &listPinsResponseFull{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "pins.list", values, response, api.debug)
|
||||
err := api.postMethod(ctx, "pins.list", values, response)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
21
vendor/github.com/nlopes/slack/reactions.go
generated
vendored
21
vendor/github.com/nlopes/slack/reactions.go
generated
vendored
@ -2,7 +2,6 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
@ -155,7 +154,7 @@ func (api *Client) AddReactionContext(ctx context.Context, name string, item Ite
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := postSlackMethod(ctx, api.httpclient, "reactions.add", values, response, api.debug); err != nil {
|
||||
if err := api.postMethod(ctx, "reactions.add", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -189,7 +188,7 @@ func (api *Client) RemoveReactionContext(ctx context.Context, name string, item
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := postSlackMethod(ctx, api.httpclient, "reactions.remove", values, response, api.debug); err != nil {
|
||||
if err := api.postMethod(ctx, "reactions.remove", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -223,12 +222,14 @@ func (api *Client) GetReactionsContext(ctx context.Context, item ItemRef, params
|
||||
}
|
||||
|
||||
response := &getReactionsResponseFull{}
|
||||
if err := postSlackMethod(ctx, api.httpclient, "reactions.get", values, response, api.debug); err != nil {
|
||||
if err := api.postMethod(ctx, "reactions.get", values, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.extractReactions(), nil
|
||||
}
|
||||
|
||||
@ -256,12 +257,14 @@ func (api *Client) ListReactionsContext(ctx context.Context, params ListReaction
|
||||
}
|
||||
|
||||
response := &listReactionsResponseFull{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "reactions.list", values, response, api.debug)
|
||||
err := api.postMethod(ctx, "reactions.list", values, response)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, nil, errors.New(response.Error)
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return response.extractReactedItems(), &response.Paging, nil
|
||||
}
|
||||
|
75
vendor/github.com/nlopes/slack/reminders.go
generated
vendored
Normal file
75
vendor/github.com/nlopes/slack/reminders.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Reminder struct {
|
||||
ID string `json:"id"`
|
||||
Creator string `json:"creator"`
|
||||
User string `json:"user"`
|
||||
Text string `json:"text"`
|
||||
Recurring bool `json:"recurring"`
|
||||
Time time.Time `json:"time"`
|
||||
CompleteTS int `json:"complete_ts"`
|
||||
}
|
||||
|
||||
type reminderResp struct {
|
||||
SlackResponse
|
||||
Reminder Reminder `json:"reminder"`
|
||||
}
|
||||
|
||||
func (api *Client) doReminder(ctx context.Context, path string, values url.Values) (*Reminder, error) {
|
||||
response := &reminderResp{}
|
||||
if err := api.postMethod(ctx, path, values, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Reminder, response.Err()
|
||||
}
|
||||
|
||||
// AddChannelReminder adds a reminder for a channel.
|
||||
//
|
||||
// See https://api.slack.com/methods/reminders.add (NOTE: the ability to set
|
||||
// reminders on a channel is currently undocumented but has been tested to
|
||||
// work)
|
||||
func (api *Client) AddChannelReminder(channelID, text, time string) (*Reminder, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"text": {text},
|
||||
"time": {time},
|
||||
"channel": {channelID},
|
||||
}
|
||||
return api.doReminder(context.Background(), "reminders.add", values)
|
||||
}
|
||||
|
||||
// AddUserReminder adds a reminder for a user.
|
||||
//
|
||||
// See https://api.slack.com/methods/reminders.add (NOTE: the ability to set
|
||||
// reminders on a channel is currently undocumented but has been tested to
|
||||
// work)
|
||||
func (api *Client) AddUserReminder(userID, text, time string) (*Reminder, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"text": {text},
|
||||
"time": {time},
|
||||
"user": {userID},
|
||||
}
|
||||
return api.doReminder(context.Background(), "reminders.add", values)
|
||||
}
|
||||
|
||||
// DeleteReminder deletes an existing reminder.
|
||||
//
|
||||
// See https://api.slack.com/methods/reminders.delete
|
||||
func (api *Client) DeleteReminder(id string) error {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"reminder": {id},
|
||||
}
|
||||
response := &SlackResponse{}
|
||||
if err := api.postMethod(context.Background(), "reminders.delete", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Err()
|
||||
}
|
27
vendor/github.com/nlopes/slack/rtm.go
generated
vendored
27
vendor/github.com/nlopes/slack/rtm.go
generated
vendored
@ -38,7 +38,7 @@ func (api *Client) StartRTM() (info *Info, websocketURL string, err error) {
|
||||
// To have a fully managed Websocket connection, use `NewRTM`, and call `ManageConnection()` on it.
|
||||
func (api *Client) StartRTMContext(ctx context.Context) (info *Info, websocketURL string, err error) {
|
||||
response := &infoResponseFull{}
|
||||
err = postSlackMethod(ctx, api.httpclient, "rtm.start", url.Values{"token": {api.token}}, response, api.debug)
|
||||
err = api.postMethod(ctx, "rtm.start", url.Values{"token": {api.token}}, response)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
@ -63,7 +63,7 @@ func (api *Client) ConnectRTM() (info *Info, websocketURL string, err error) {
|
||||
// To have a fully managed Websocket connection, use `NewRTM`, and call `ManageConnection()` on it.
|
||||
func (api *Client) ConnectRTMContext(ctx context.Context) (info *Info, websocketURL string, err error) {
|
||||
response := &infoResponseFull{}
|
||||
err = postSlackMethod(ctx, api.httpclient, "rtm.connect", url.Values{"token": {api.token}}, response, api.debug)
|
||||
err = api.postMethod(ctx, "rtm.connect", url.Values{"token": {api.token}}, response)
|
||||
if err != nil {
|
||||
api.Debugf("Failed to connect to RTM: %s", err)
|
||||
return nil, "", err
|
||||
@ -100,6 +100,13 @@ func RTMOptionPingInterval(d time.Duration) RTMOption {
|
||||
}
|
||||
}
|
||||
|
||||
// RTMOptionConnParams installs parameters to embed into the connection URL.
|
||||
func RTMOptionConnParams(connParams url.Values) RTMOption {
|
||||
return func(rtm *RTM) {
|
||||
rtm.connParams = connParams
|
||||
}
|
||||
}
|
||||
|
||||
// NewRTM returns a RTM, which provides a fully managed connection to
|
||||
// Slack's websocket-based Real-Time Messaging protocol.
|
||||
func (api *Client) NewRTM(options ...RTMOption) *RTM {
|
||||
@ -109,10 +116,9 @@ func (api *Client) NewRTM(options ...RTMOption) *RTM {
|
||||
outgoingMessages: make(chan OutgoingMessage, 20),
|
||||
pingInterval: defaultPingInterval,
|
||||
pingDeadman: time.NewTimer(deadmanDuration(defaultPingInterval)),
|
||||
isConnected: false,
|
||||
wasIntentional: true,
|
||||
killChannel: make(chan bool),
|
||||
disconnected: make(chan struct{}, 1),
|
||||
disconnected: make(chan struct{}),
|
||||
disconnectedm: &sync.Once{},
|
||||
forcePing: make(chan bool),
|
||||
rawEvents: make(chan json.RawMessage),
|
||||
idGen: NewSafeID(1),
|
||||
@ -125,14 +131,3 @@ func (api *Client) NewRTM(options ...RTMOption) *RTM {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// NewRTMWithOptions Deprecated just use NewRTM(RTMOptionsUseStart(true))
|
||||
// returns a RTM, which provides a fully managed connection to
|
||||
// Slack's websocket-based Real-Time Messaging protocol.
|
||||
// This also allows to configure various options available for RTM API.
|
||||
func (api *Client) NewRTMWithOptions(options *RTMOptions) *RTM {
|
||||
if options != nil {
|
||||
return api.NewRTM(RTMOptionUseStart(options.UseRTMStart))
|
||||
}
|
||||
return api.NewRTM()
|
||||
}
|
||||
|
9
vendor/github.com/nlopes/slack/search.go
generated
vendored
9
vendor/github.com/nlopes/slack/search.go
generated
vendored
@ -2,7 +2,6 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
@ -104,14 +103,12 @@ func (api *Client) _search(ctx context.Context, path, query string, params Searc
|
||||
}
|
||||
|
||||
response = &searchResponseFull{}
|
||||
err := postSlackMethod(ctx, api.httpclient, path, values, response, api.debug)
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response, nil
|
||||
|
||||
return response, response.Err()
|
||||
|
||||
}
|
||||
|
||||
|
147
vendor/github.com/nlopes/slack/security.go
generated
vendored
147
vendor/github.com/nlopes/slack/security.go
generated
vendored
@ -1,47 +1,100 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// SecretsVerifier contains the information needed to verify that the request comes from Slack
|
||||
type SecretsVerifier struct {
|
||||
slackSig string
|
||||
timeStamp string
|
||||
hmac hash.Hash
|
||||
}
|
||||
|
||||
// NewSecretsVerifier returns a SecretsVerifier object in exchange for an http.Header object and signing secret
|
||||
func NewSecretsVerifier(header http.Header, signingSecret string) (SecretsVerifier, error) {
|
||||
if header["X-Slack-Signature"][0] == "" || header["X-Slack-Request-Timestamp"][0] == "" {
|
||||
return SecretsVerifier{}, errors.New("Headers are empty, cannot create SecretsVerifier")
|
||||
}
|
||||
|
||||
hash := hmac.New(sha256.New, []byte(signingSecret))
|
||||
hash.Write([]byte(fmt.Sprintf("v0:%s:", header["X-Slack-Request-Timestamp"][0])))
|
||||
return SecretsVerifier{
|
||||
slackSig: header["X-Slack-Signature"][0],
|
||||
timeStamp: header["X-Slack-Request-Timestamp"][0],
|
||||
hmac: hash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *SecretsVerifier) Write(body []byte) (n int, err error) {
|
||||
return v.hmac.Write(body)
|
||||
}
|
||||
|
||||
// Ensure compares the signature sent from Slack with the actual computed hash to judge validity
|
||||
func (v SecretsVerifier) Ensure() error {
|
||||
computed := "v0=" + string(hex.EncodeToString(v.hmac.Sum(nil)))
|
||||
if computed == v.slackSig {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Expected signing signature: %s, but computed: %s", v.slackSig, computed)
|
||||
}
|
||||
package slack
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Signature headers
|
||||
const (
|
||||
hSignature = "X-Slack-Signature"
|
||||
hTimestamp = "X-Slack-Request-Timestamp"
|
||||
)
|
||||
|
||||
// SecretsVerifier contains the information needed to verify that the request comes from Slack
|
||||
type SecretsVerifier struct {
|
||||
signature []byte
|
||||
hmac hash.Hash
|
||||
}
|
||||
|
||||
func unsafeSignatureVerifier(header http.Header, secret string) (_ SecretsVerifier, err error) {
|
||||
var (
|
||||
bsignature []byte
|
||||
)
|
||||
|
||||
signature := header.Get(hSignature)
|
||||
stimestamp := header.Get(hTimestamp)
|
||||
|
||||
if signature == "" || stimestamp == "" {
|
||||
return SecretsVerifier{}, ErrMissingHeaders
|
||||
}
|
||||
|
||||
if bsignature, err = hex.DecodeString(strings.TrimPrefix(signature, "v0=")); err != nil {
|
||||
return SecretsVerifier{}, err
|
||||
}
|
||||
|
||||
hash := hmac.New(sha256.New, []byte(secret))
|
||||
if _, err = hash.Write([]byte(fmt.Sprintf("v0:%s:", stimestamp))); err != nil {
|
||||
return SecretsVerifier{}, err
|
||||
}
|
||||
|
||||
return SecretsVerifier{
|
||||
signature: bsignature,
|
||||
hmac: hash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewSecretsVerifier returns a SecretsVerifier object in exchange for an http.Header object and signing secret
|
||||
func NewSecretsVerifier(header http.Header, secret string) (sv SecretsVerifier, err error) {
|
||||
var (
|
||||
timestamp int64
|
||||
)
|
||||
|
||||
stimestamp := header.Get(hTimestamp)
|
||||
|
||||
if sv, err = unsafeSignatureVerifier(header, secret); err != nil {
|
||||
return SecretsVerifier{}, err
|
||||
}
|
||||
|
||||
if timestamp, err = strconv.ParseInt(stimestamp, 10, 64); err != nil {
|
||||
return SecretsVerifier{}, err
|
||||
}
|
||||
|
||||
diff := absDuration(time.Since(time.Unix(timestamp, 0)))
|
||||
if diff > 5*time.Minute {
|
||||
return SecretsVerifier{}, ErrExpiredTimestamp
|
||||
}
|
||||
|
||||
return sv, err
|
||||
}
|
||||
|
||||
func (v *SecretsVerifier) Write(body []byte) (n int, err error) {
|
||||
return v.hmac.Write(body)
|
||||
}
|
||||
|
||||
// Ensure compares the signature sent from Slack with the actual computed hash to judge validity
|
||||
func (v SecretsVerifier) Ensure() error {
|
||||
computed := v.hmac.Sum(nil)
|
||||
// use hmac.Equal prevent leaking timing information.
|
||||
if hmac.Equal(computed, v.signature) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Expected signing signature: %s, but computed: %s", hex.EncodeToString(v.signature), hex.EncodeToString(computed))
|
||||
}
|
||||
|
||||
func abs64(n int64) int64 {
|
||||
y := n >> 63
|
||||
return (n ^ y) - y
|
||||
}
|
||||
|
||||
func absDuration(n time.Duration) time.Duration {
|
||||
return time.Duration(abs64(int64(n)))
|
||||
}
|
||||
|
109
vendor/github.com/nlopes/slack/slack.go
generated
vendored
109
vendor/github.com/nlopes/slack/slack.go
generated
vendored
@ -2,7 +2,6 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
@ -10,31 +9,18 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Added as a var so that we can change this for testing purposes
|
||||
var SLACK_API string = "https://slack.com/api/"
|
||||
var SLACK_WEB_API_FORMAT string = "https://%s.slack.com/api/users.admin.%s?t=%s"
|
||||
const (
|
||||
// APIURL of the slack api.
|
||||
APIURL = "https://slack.com/api/"
|
||||
// WEBAPIURLFormat ...
|
||||
WEBAPIURLFormat = "https://%s.slack.com/api/users.admin.%s?t=%d"
|
||||
)
|
||||
|
||||
// HTTPClient sets a custom http.Client
|
||||
// deprecated: in favor of SetHTTPClient()
|
||||
var HTTPClient = &http.Client{}
|
||||
|
||||
var customHTTPClient HTTPRequester = HTTPClient
|
||||
|
||||
// HTTPRequester defines the minimal interface needed for an http.Client to be implemented.
|
||||
//
|
||||
// Use it in conjunction with the SetHTTPClient function to allow for other capabilities
|
||||
// like a tracing http.Client
|
||||
type HTTPRequester interface {
|
||||
// httpClient defines the minimal interface needed for an http.Client to be implemented.
|
||||
type httpClient interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// SetHTTPClient allows you to specify a custom http.Client
|
||||
// Use this instead of the package level HTTPClient variable if you want to use a custom client like the
|
||||
// Stackdriver Trace HTTPClient https://godoc.org/cloud.google.com/go/trace#HTTPClient
|
||||
func SetHTTPClient(client HTTPRequester) {
|
||||
customHTTPClient = client
|
||||
}
|
||||
|
||||
// ResponseMetadata holds pagination metadata
|
||||
type ResponseMetadata struct {
|
||||
Cursor string `json:"next_cursor"`
|
||||
@ -48,12 +34,15 @@ func (t *ResponseMetadata) initialize() *ResponseMetadata {
|
||||
return &ResponseMetadata{}
|
||||
}
|
||||
|
||||
// AuthTestResponse ...
|
||||
type AuthTestResponse struct {
|
||||
URL string `json:"url"`
|
||||
Team string `json:"team"`
|
||||
User string `json:"user"`
|
||||
TeamID string `json:"team_id"`
|
||||
UserID string `json:"user_id"`
|
||||
// EnterpriseID is only returned when an enterprise id present
|
||||
EnterpriseID string `json:"enterprise_id,omitempty"`
|
||||
}
|
||||
|
||||
type authTestResponseFull struct {
|
||||
@ -61,28 +50,51 @@ type authTestResponseFull struct {
|
||||
AuthTestResponse
|
||||
}
|
||||
|
||||
// Client for the slack api.
|
||||
type Client struct {
|
||||
token string
|
||||
info Info
|
||||
endpoint string
|
||||
debug bool
|
||||
httpclient HTTPRequester
|
||||
log ilogger
|
||||
httpclient httpClient
|
||||
}
|
||||
|
||||
// Option defines an option for a Client
|
||||
type Option func(*Client)
|
||||
|
||||
// OptionHTTPClient - provide a custom http client to the slack client.
|
||||
func OptionHTTPClient(c HTTPRequester) func(*Client) {
|
||||
return func(s *Client) {
|
||||
s.httpclient = c
|
||||
func OptionHTTPClient(client httpClient) func(*Client) {
|
||||
return func(c *Client) {
|
||||
c.httpclient = client
|
||||
}
|
||||
}
|
||||
|
||||
// OptionDebug enable debugging for the client
|
||||
func OptionDebug(b bool) func(*Client) {
|
||||
return func(c *Client) {
|
||||
c.debug = b
|
||||
}
|
||||
}
|
||||
|
||||
// OptionLog set logging for client.
|
||||
func OptionLog(l logger) func(*Client) {
|
||||
return func(c *Client) {
|
||||
c.log = internalLog{logger: l}
|
||||
}
|
||||
}
|
||||
|
||||
// OptionAPIURL set the url for the client. only useful for testing.
|
||||
func OptionAPIURL(u string) func(*Client) {
|
||||
return func(c *Client) { c.endpoint = u }
|
||||
}
|
||||
|
||||
// New builds a slack client from the provided token and options.
|
||||
func New(token string, options ...Option) *Client {
|
||||
s := &Client{
|
||||
token: token,
|
||||
httpclient: customHTTPClient,
|
||||
endpoint: APIURL,
|
||||
httpclient: &http.Client{},
|
||||
log: log.New(os.Stderr, "nlopes/slack", log.LstdFlags|log.Lshortfile),
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
@ -98,43 +110,42 @@ func (api *Client) AuthTest() (response *AuthTestResponse, error error) {
|
||||
}
|
||||
|
||||
// AuthTestContext tests if the user is able to do authenticated requests or not with a custom context
|
||||
func (api *Client) AuthTestContext(ctx context.Context) (response *AuthTestResponse, error error) {
|
||||
func (api *Client) AuthTestContext(ctx context.Context) (response *AuthTestResponse, err error) {
|
||||
api.Debugf("Challenging auth...")
|
||||
responseFull := &authTestResponseFull{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "auth.test", url.Values{"token": {api.token}}, responseFull, api.debug)
|
||||
err = api.postMethod(ctx, "auth.test", url.Values{"token": {api.token}}, responseFull)
|
||||
if err != nil {
|
||||
api.Debugf("failed to test for auth: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
if !responseFull.Ok {
|
||||
api.Debugf("auth response was not Ok: %s", responseFull.Error)
|
||||
return nil, errors.New(responseFull.Error)
|
||||
}
|
||||
|
||||
api.Debugf("Auth challenge was successful with response %+v", responseFull.AuthTestResponse)
|
||||
return &responseFull.AuthTestResponse, nil
|
||||
}
|
||||
|
||||
// SetDebug switches the api into debug mode
|
||||
// When in debug mode, it logs various info about what its doing
|
||||
// If you ever use this in production, don't call SetDebug(true)
|
||||
func (api *Client) SetDebug(debug bool) {
|
||||
api.debug = debug
|
||||
if debug && logger == nil {
|
||||
SetLogger(log.New(os.Stdout, "nlopes/slack", log.LstdFlags|log.Lshortfile))
|
||||
}
|
||||
return &responseFull.AuthTestResponse, responseFull.Err()
|
||||
}
|
||||
|
||||
// Debugf print a formatted debug line.
|
||||
func (api *Client) Debugf(format string, v ...interface{}) {
|
||||
if api.debug {
|
||||
logger.Output(2, fmt.Sprintf(format, v...))
|
||||
api.log.Output(2, fmt.Sprintf(format, v...))
|
||||
}
|
||||
}
|
||||
|
||||
// Debugln print a debug line.
|
||||
func (api *Client) Debugln(v ...interface{}) {
|
||||
if api.debug {
|
||||
logger.Output(2, fmt.Sprintln(v...))
|
||||
api.log.Output(2, fmt.Sprintln(v...))
|
||||
}
|
||||
}
|
||||
|
||||
// Debug returns if debug is enabled.
|
||||
func (api *Client) Debug() bool {
|
||||
return api.debug
|
||||
}
|
||||
|
||||
// post to a slack web method.
|
||||
func (api *Client) postMethod(ctx context.Context, path string, values url.Values, intf interface{}) error {
|
||||
return postForm(ctx, api.httpclient, api.endpoint+path, values, intf, api)
|
||||
}
|
||||
|
||||
// get a slack web method.
|
||||
func (api *Client) getMethod(ctx context.Context, path string, values url.Values, intf interface{}) error {
|
||||
return getResource(ctx, api.httpclient, api.endpoint+path, values, intf, api)
|
||||
}
|
||||
|
5
vendor/github.com/nlopes/slack/slackutilsx/slackutilsx.go
generated
vendored
5
vendor/github.com/nlopes/slack/slackutilsx/slackutilsx.go
generated
vendored
@ -55,3 +55,8 @@ func EscapeMessage(message string) string {
|
||||
replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
|
||||
return replacer.Replace(message)
|
||||
}
|
||||
|
||||
// Retryable errors return true.
|
||||
type Retryable interface {
|
||||
Retryable() bool
|
||||
}
|
||||
|
13
vendor/github.com/nlopes/slack/stars.go
generated
vendored
13
vendor/github.com/nlopes/slack/stars.go
generated
vendored
@ -2,7 +2,6 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
@ -58,7 +57,7 @@ func (api *Client) AddStarContext(ctx context.Context, channel string, item Item
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := postSlackMethod(ctx, api.httpclient, "stars.add", values, response, api.debug); err != nil {
|
||||
if err := api.postMethod(ctx, "stars.add", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -87,7 +86,7 @@ func (api *Client) RemoveStarContext(ctx context.Context, channel string, item I
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := postSlackMethod(ctx, api.httpclient, "stars.remove", values, response, api.debug); err != nil {
|
||||
if err := api.postMethod(ctx, "stars.remove", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -115,13 +114,15 @@ func (api *Client) ListStarsContext(ctx context.Context, params StarsParameters)
|
||||
}
|
||||
|
||||
response := &listResponseFull{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "stars.list", values, response, api.debug)
|
||||
err := api.postMethod(ctx, "stars.list", values, response)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, nil, errors.New(response.Error)
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return response.Items, &response.Paging, nil
|
||||
}
|
||||
|
||||
|
40
vendor/github.com/nlopes/slack/team.go
generated
vendored
40
vendor/github.com/nlopes/slack/team.go
generated
vendored
@ -2,7 +2,6 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
@ -67,44 +66,33 @@ func NewAccessLogParameters() AccessLogParameters {
|
||||
}
|
||||
}
|
||||
|
||||
func teamRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*TeamResponse, error) {
|
||||
func (api *Client) teamRequest(ctx context.Context, path string, values url.Values) (*TeamResponse, error) {
|
||||
response := &TeamResponse{}
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
func billableInfoRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (map[string]BillingActive, error) {
|
||||
func (api *Client) billableInfoRequest(ctx context.Context, path string, values url.Values) (map[string]BillingActive, error) {
|
||||
response := &BillableInfoResponse{}
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
|
||||
return response.BillableInfo, nil
|
||||
return response.BillableInfo, response.Err()
|
||||
}
|
||||
|
||||
func accessLogsRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*LoginResponse, error) {
|
||||
func (api *Client) accessLogsRequest(ctx context.Context, path string, values url.Values) (*LoginResponse, error) {
|
||||
response := &LoginResponse{}
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response, nil
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// GetTeamInfo gets the Team Information of the user
|
||||
@ -118,7 +106,7 @@ func (api *Client) GetTeamInfoContext(ctx context.Context) (*TeamInfo, error) {
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response, err := teamRequest(ctx, api.httpclient, "team.info", values, api.debug)
|
||||
response, err := api.teamRequest(ctx, "team.info", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -142,24 +130,26 @@ func (api *Client) GetAccessLogsContext(ctx context.Context, params AccessLogPar
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
|
||||
response, err := accessLogsRequest(ctx, api.httpclient, "team.accessLogs", values, api.debug)
|
||||
response, err := api.accessLogsRequest(ctx, "team.accessLogs", values)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return response.Logins, &response.Paging, nil
|
||||
}
|
||||
|
||||
// GetBillableInfo ...
|
||||
func (api *Client) GetBillableInfo(user string) (map[string]BillingActive, error) {
|
||||
return api.GetBillableInfoContext(context.Background(), user)
|
||||
}
|
||||
|
||||
// GetBillableInfoContext ...
|
||||
func (api *Client) GetBillableInfoContext(ctx context.Context, user string) (map[string]BillingActive, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api.debug)
|
||||
return api.billableInfoRequest(ctx, "team.billableInfo", values)
|
||||
}
|
||||
|
||||
// GetBillableInfoForTeam returns the billing_active status of all users on the team.
|
||||
@ -173,5 +163,5 @@ func (api *Client) GetBillableInfoForTeamContext(ctx context.Context) (map[strin
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api.debug)
|
||||
return api.billableInfoRequest(ctx, "team.billableInfo", values)
|
||||
}
|
||||
|
29
vendor/github.com/nlopes/slack/usergroups.go
generated
vendored
29
vendor/github.com/nlopes/slack/usergroups.go
generated
vendored
@ -2,7 +2,6 @@ package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
@ -41,16 +40,14 @@ type userGroupResponseFull struct {
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func userGroupRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*userGroupResponseFull, error) {
|
||||
func (api *Client) userGroupRequest(ctx context.Context, path string, values url.Values) (*userGroupResponseFull, error) {
|
||||
response := &userGroupResponseFull{}
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response, nil
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// CreateUserGroup creates a new user group
|
||||
@ -77,7 +74,7 @@ func (api *Client) CreateUserGroupContext(ctx context.Context, userGroup UserGro
|
||||
values["channels"] = []string{strings.Join(userGroup.Prefs.Channels, ",")}
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.create", values, api.debug)
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.create", values)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
@ -96,7 +93,7 @@ func (api *Client) DisableUserGroupContext(ctx context.Context, userGroup string
|
||||
"usergroup": {userGroup},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.disable", values, api.debug)
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.disable", values)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
@ -115,7 +112,7 @@ func (api *Client) EnableUserGroupContext(ctx context.Context, userGroup string)
|
||||
"usergroup": {userGroup},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.enable", values, api.debug)
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.enable", values)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
@ -179,7 +176,7 @@ func (api *Client) GetUserGroupsContext(ctx context.Context, options ...GetUserG
|
||||
values.Add("include_users", "true")
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.list", values, api.debug)
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.list", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -209,8 +206,12 @@ func (api *Client) UpdateUserGroupContext(ctx context.Context, userGroup UserGro
|
||||
if userGroup.Description != "" {
|
||||
values["description"] = []string{userGroup.Description}
|
||||
}
|
||||
|
||||
if len(userGroup.Prefs.Channels) > 0 {
|
||||
values["channels"] = []string{strings.Join(userGroup.Prefs.Channels, ",")}
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.update", values, api.debug)
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.update", values)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
@ -229,7 +230,7 @@ func (api *Client) GetUserGroupMembersContext(ctx context.Context, userGroup str
|
||||
"usergroup": {userGroup},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.list", values, api.debug)
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.users.list", values)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
@ -249,7 +250,7 @@ func (api *Client) UpdateUserGroupMembersContext(ctx context.Context, userGroup
|
||||
"users": {members},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.update", values, api.debug)
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.users.update", values)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
|
145
vendor/github.com/nlopes/slack/users.go
generated
vendored
145
vendor/github.com/nlopes/slack/users.go
generated
vendored
@ -3,7 +3,6 @@ package slack
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
@ -12,7 +11,6 @@ const (
|
||||
DEFAULT_USER_PHOTO_CROP_X = -1
|
||||
DEFAULT_USER_PHOTO_CROP_Y = -1
|
||||
DEFAULT_USER_PHOTO_CROP_W = -1
|
||||
errPaginationComplete = errorString("pagination complete")
|
||||
)
|
||||
|
||||
// UserProfile contains all the information details of a given user
|
||||
@ -37,6 +35,7 @@ type UserProfile struct {
|
||||
ApiAppID string `json:"api_app_id,omitempty"`
|
||||
StatusText string `json:"status_text,omitempty"`
|
||||
StatusEmoji string `json:"status_emoji,omitempty"`
|
||||
StatusExpiration int `json:"status_expiration"`
|
||||
Team string `json:"team"`
|
||||
Fields UserProfileCustomFields `json:"fields"`
|
||||
}
|
||||
@ -100,28 +99,30 @@ type UserProfileCustomField struct {
|
||||
|
||||
// User contains all the information of a user
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
TeamID string `json:"team_id"`
|
||||
Name string `json:"name"`
|
||||
Deleted bool `json:"deleted"`
|
||||
Color string `json:"color"`
|
||||
RealName string `json:"real_name"`
|
||||
TZ string `json:"tz,omitempty"`
|
||||
TZLabel string `json:"tz_label"`
|
||||
TZOffset int `json:"tz_offset"`
|
||||
Profile UserProfile `json:"profile"`
|
||||
IsBot bool `json:"is_bot"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
IsOwner bool `json:"is_owner"`
|
||||
IsPrimaryOwner bool `json:"is_primary_owner"`
|
||||
IsRestricted bool `json:"is_restricted"`
|
||||
IsUltraRestricted bool `json:"is_ultra_restricted"`
|
||||
IsStranger bool `json:"is_stranger"`
|
||||
IsAppUser bool `json:"is_app_user"`
|
||||
Has2FA bool `json:"has_2fa"`
|
||||
HasFiles bool `json:"has_files"`
|
||||
Presence string `json:"presence"`
|
||||
Locale string `json:"locale"`
|
||||
ID string `json:"id"`
|
||||
TeamID string `json:"team_id"`
|
||||
Name string `json:"name"`
|
||||
Deleted bool `json:"deleted"`
|
||||
Color string `json:"color"`
|
||||
RealName string `json:"real_name"`
|
||||
TZ string `json:"tz,omitempty"`
|
||||
TZLabel string `json:"tz_label"`
|
||||
TZOffset int `json:"tz_offset"`
|
||||
Profile UserProfile `json:"profile"`
|
||||
IsBot bool `json:"is_bot"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
IsOwner bool `json:"is_owner"`
|
||||
IsPrimaryOwner bool `json:"is_primary_owner"`
|
||||
IsRestricted bool `json:"is_restricted"`
|
||||
IsUltraRestricted bool `json:"is_ultra_restricted"`
|
||||
IsStranger bool `json:"is_stranger"`
|
||||
IsAppUser bool `json:"is_app_user"`
|
||||
Has2FA bool `json:"has_2fa"`
|
||||
HasFiles bool `json:"has_files"`
|
||||
Presence string `json:"presence"`
|
||||
Locale string `json:"locale"`
|
||||
Updated JSONTime `json:"updated"`
|
||||
Enterprise EnterpriseUser `json:"enterprise_user,omitempty"`
|
||||
}
|
||||
|
||||
// UserPresence contains details about a user online status
|
||||
@ -152,6 +153,17 @@ type UserIdentity struct {
|
||||
Image512 string `json:"image_512"`
|
||||
}
|
||||
|
||||
// EnterpriseUser is present when a user is part of Slack Enterprise Grid
|
||||
// https://api.slack.com/types/user#enterprise_grid_user_objects
|
||||
type EnterpriseUser struct {
|
||||
ID string `json:"id"`
|
||||
EnterpriseID string `json:"enterprise_id"`
|
||||
EnterpriseName string `json:"enterprise_name"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
IsOwner bool `json:"is_owner"`
|
||||
Teams []string `json:"teams"`
|
||||
}
|
||||
|
||||
type TeamIdentity struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@ -189,16 +201,14 @@ func NewUserSetPhotoParams() UserSetPhotoParams {
|
||||
}
|
||||
}
|
||||
|
||||
func userRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*userResponseFull, error) {
|
||||
func (api *Client) userRequest(ctx context.Context, path string, values url.Values) (*userResponseFull, error) {
|
||||
response := &userResponseFull{}
|
||||
err := postForm(ctx, client, SLACK_API+path, values, response, debug)
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response, nil
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// GetUserPresence will retrieve the current presence status of given user.
|
||||
@ -213,7 +223,7 @@ func (api *Client) GetUserPresenceContext(ctx context.Context, user string) (*Us
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
response, err := userRequest(ctx, api.httpclient, "users.getPresence", values, api.debug)
|
||||
response, err := api.userRequest(ctx, "users.getPresence", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -228,11 +238,12 @@ func (api *Client) GetUserInfo(user string) (*User, error) {
|
||||
// GetUserInfoContext will retrieve the complete user information with a custom context
|
||||
func (api *Client) GetUserInfoContext(ctx context.Context, user string) (*User, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
"include_locale": {strconv.FormatBool(true)},
|
||||
}
|
||||
|
||||
response, err := userRequest(ctx, api.httpclient, "users.info", values, api.debug)
|
||||
response, err := api.userRequest(ctx, "users.info", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -304,13 +315,14 @@ func (t UserPagination) Next(ctx context.Context) (_ UserPagination, err error)
|
||||
t.previousResp = t.previousResp.initialize()
|
||||
|
||||
values := url.Values{
|
||||
"limit": {strconv.Itoa(t.limit)},
|
||||
"presence": {strconv.FormatBool(t.presence)},
|
||||
"token": {t.c.token},
|
||||
"cursor": {t.previousResp.Cursor},
|
||||
"limit": {strconv.Itoa(t.limit)},
|
||||
"presence": {strconv.FormatBool(t.presence)},
|
||||
"token": {t.c.token},
|
||||
"cursor": {t.previousResp.Cursor},
|
||||
"include_locale": {strconv.FormatBool(true)},
|
||||
}
|
||||
|
||||
if resp, err = userRequest(ctx, t.c.httpclient, "users.list", values, t.c.debug); err != nil {
|
||||
if resp, err = t.c.userRequest(ctx, "users.list", values); err != nil {
|
||||
return t, err
|
||||
}
|
||||
|
||||
@ -355,7 +367,7 @@ func (api *Client) GetUserByEmailContext(ctx context.Context, email string) (*Us
|
||||
"token": {api.token},
|
||||
"email": {email},
|
||||
}
|
||||
response, err := userRequest(ctx, api.httpclient, "users.lookupByEmail", values, api.debug)
|
||||
response, err := api.userRequest(ctx, "users.lookupByEmail", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -373,7 +385,7 @@ func (api *Client) SetUserAsActiveContext(ctx context.Context) (err error) {
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
_, err = userRequest(ctx, api.httpclient, "users.setActive", values, api.debug)
|
||||
_, err = api.userRequest(ctx, "users.setActive", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -389,7 +401,7 @@ func (api *Client) SetUserPresenceContext(ctx context.Context, presence string)
|
||||
"presence": {presence},
|
||||
}
|
||||
|
||||
_, err := userRequest(ctx, api.httpclient, "users.setPresence", values, api.debug)
|
||||
_, err := api.userRequest(ctx, "users.setPresence", values)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -405,13 +417,15 @@ func (api *Client) GetUserIdentityContext(ctx context.Context) (*UserIdentityRes
|
||||
}
|
||||
response := &UserIdentityResponse{}
|
||||
|
||||
err := postForm(ctx, api.httpclient, SLACK_API+"users.identity", values, response, api.debug)
|
||||
err := api.postMethod(ctx, "users.identity", values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@ -436,7 +450,7 @@ func (api *Client) SetUserPhotoContext(ctx context.Context, image string, params
|
||||
values.Add("crop_w", strconv.Itoa(params.CropW))
|
||||
}
|
||||
|
||||
err := postLocalWithMultipartResponse(ctx, api.httpclient, "users.setPhoto", image, "image", values, response, api.debug)
|
||||
err := postLocalWithMultipartResponse(ctx, api.httpclient, api.endpoint+"users.setPhoto", image, "image", values, response, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -456,7 +470,7 @@ func (api *Client) DeleteUserPhotoContext(ctx context.Context) error {
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
err := postForm(ctx, api.httpclient, SLACK_API+"users.deletePhoto", values, response, api.debug)
|
||||
err := api.postMethod(ctx, "users.deletePhoto", values, response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -467,15 +481,16 @@ func (api *Client) DeleteUserPhotoContext(ctx context.Context) error {
|
||||
// SetUserCustomStatus will set a custom status and emoji for the currently
|
||||
// authenticated user. If statusEmoji is "" and statusText is not, the Slack API
|
||||
// will automatically set it to ":speech_balloon:". Otherwise, if both are ""
|
||||
// the Slack API will unset the custom status/emoji.
|
||||
func (api *Client) SetUserCustomStatus(statusText, statusEmoji string) error {
|
||||
return api.SetUserCustomStatusContext(context.Background(), statusText, statusEmoji)
|
||||
// the Slack API will unset the custom status/emoji. If statusExpiration is set to 0
|
||||
// the status will not expire.
|
||||
func (api *Client) SetUserCustomStatus(statusText, statusEmoji string, statusExpiration int64) error {
|
||||
return api.SetUserCustomStatusContext(context.Background(), statusText, statusEmoji, statusExpiration)
|
||||
}
|
||||
|
||||
// SetUserCustomStatusContext will set a custom status and emoji for the currently authenticated user with a custom context
|
||||
//
|
||||
// For more information see SetUserCustomStatus
|
||||
func (api *Client) SetUserCustomStatusContext(ctx context.Context, statusText, statusEmoji string) error {
|
||||
func (api *Client) SetUserCustomStatusContext(ctx context.Context, statusText, statusEmoji string, statusExpiration int64) error {
|
||||
// XXX(theckman): this anonymous struct is for making requests to the Slack
|
||||
// API for setting and unsetting a User's Custom Status/Emoji. To change
|
||||
// these values we must provide a JSON document as the profile POST field.
|
||||
@ -488,11 +503,13 @@ func (api *Client) SetUserCustomStatusContext(ctx context.Context, statusText, s
|
||||
// - https://api.slack.com/docs/presence-and-status#custom_status
|
||||
profile, err := json.Marshal(
|
||||
&struct {
|
||||
StatusText string `json:"status_text"`
|
||||
StatusEmoji string `json:"status_emoji"`
|
||||
StatusText string `json:"status_text"`
|
||||
StatusEmoji string `json:"status_emoji"`
|
||||
StatusExpiration int64 `json:"status_expiration"`
|
||||
}{
|
||||
StatusText: statusText,
|
||||
StatusEmoji: statusEmoji,
|
||||
StatusText: statusText,
|
||||
StatusEmoji: statusEmoji,
|
||||
StatusExpiration: statusExpiration,
|
||||
},
|
||||
)
|
||||
|
||||
@ -506,15 +523,11 @@ func (api *Client) SetUserCustomStatusContext(ctx context.Context, statusText, s
|
||||
}
|
||||
|
||||
response := &userResponseFull{}
|
||||
if err = postForm(ctx, api.httpclient, SLACK_API+"users.profile.set", values, response, api.debug); err != nil {
|
||||
if err = api.postMethod(ctx, "users.profile.set", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// UnsetUserCustomStatus removes the custom status message for the currently
|
||||
@ -526,7 +539,7 @@ func (api *Client) UnsetUserCustomStatus() error {
|
||||
// UnsetUserCustomStatusContext removes the custom status message for the currently authenticated user
|
||||
// with a custom context. This is a convenience method that wraps (*Client).SetUserCustomStatus().
|
||||
func (api *Client) UnsetUserCustomStatusContext(ctx context.Context) error {
|
||||
return api.SetUserCustomStatusContext(ctx, "", "")
|
||||
return api.SetUserCustomStatusContext(ctx, "", "", 0)
|
||||
}
|
||||
|
||||
// GetUserProfile retrieves a user's profile information.
|
||||
@ -547,12 +560,14 @@ func (api *Client) GetUserProfileContext(ctx context.Context, userID string, inc
|
||||
}
|
||||
resp := &getUserProfileResponse{}
|
||||
|
||||
err := postSlackMethod(ctx, api.httpclient, "users.profile.get", values, &resp, api.debug)
|
||||
err := api.postMethod(ctx, "users.profile.get", values, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !resp.Ok {
|
||||
return nil, errors.New(resp.Error)
|
||||
|
||||
if err := resp.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Profile, nil
|
||||
}
|
||||
|
22
vendor/github.com/nlopes/slack/webhooks.go
generated
vendored
22
vendor/github.com/nlopes/slack/webhooks.go
generated
vendored
@ -9,26 +9,32 @@ import (
|
||||
)
|
||||
|
||||
type WebhookMessage struct {
|
||||
Text string `json:"text,omitempty"`
|
||||
Attachments []Attachment `json:"attachments,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
IconEmoji string `json:"icon_emoji,omitempty"`
|
||||
IconURL string `json:"icon_url,omitempty"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
ThreadTimestamp string `json:"thread_ts,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Attachments []Attachment `json:"attachments,omitempty"`
|
||||
Parse string `json:"parse,omitempty"`
|
||||
}
|
||||
|
||||
func PostWebhook(url string, msg *WebhookMessage) error {
|
||||
return PostWebhookCustomHTTP(url, http.DefaultClient, msg)
|
||||
}
|
||||
|
||||
func PostWebhookCustomHTTP(url string, httpClient *http.Client, msg *WebhookMessage) error {
|
||||
raw, err := json.Marshal(msg)
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal failed")
|
||||
}
|
||||
|
||||
response, err := http.Post(url, "application/json", bytes.NewReader(raw))
|
||||
response, err := httpClient.Post(url, "application/json", bytes.NewReader(raw))
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to post webhook")
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return statusCodeError{Code: response.StatusCode, Status: response.Status}
|
||||
}
|
||||
|
||||
return nil
|
||||
return checkStatusCode(response, discard{})
|
||||
}
|
||||
|
54
vendor/github.com/nlopes/slack/websocket.go
generated
vendored
54
vendor/github.com/nlopes/slack/websocket.go
generated
vendored
@ -2,7 +2,7 @@ package slack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -20,6 +20,9 @@ const (
|
||||
//
|
||||
// Create this element with Client's NewRTM() or NewRTMWithOptions(*RTMOptions)
|
||||
type RTM struct {
|
||||
// Client is the main API, embedded
|
||||
Client
|
||||
|
||||
idGen IDGenerator
|
||||
pingInterval time.Duration
|
||||
pingDeadman *time.Timer
|
||||
@ -29,15 +32,10 @@ type RTM struct {
|
||||
IncomingEvents chan RTMEvent
|
||||
outgoingMessages chan OutgoingMessage
|
||||
killChannel chan bool
|
||||
disconnected chan struct{} // disconnected is closed when Disconnect is invoked, regardless of connection state. Allows for ManagedConnection to not leak.
|
||||
disconnected chan struct{}
|
||||
disconnectedm *sync.Once
|
||||
forcePing chan bool
|
||||
rawEvents chan json.RawMessage
|
||||
wasIntentional bool
|
||||
isConnected bool
|
||||
|
||||
// Client is the main API, embedded
|
||||
Client
|
||||
websocketURL string
|
||||
|
||||
// UserDetails upon connection
|
||||
info *Info
|
||||
@ -53,40 +51,30 @@ type RTM struct {
|
||||
|
||||
// mu is mutex used to prevent RTM connection race conditions
|
||||
mu *sync.Mutex
|
||||
|
||||
// connParams is a map of flags for connection parameters.
|
||||
connParams url.Values
|
||||
}
|
||||
|
||||
// RTMOptions allows configuration of various options available for RTM messaging
|
||||
//
|
||||
// This structure will evolve in time so please make sure you are always using the
|
||||
// named keys for every entry available as per Go 1 compatibility promise adding fields
|
||||
// to this structure should not be considered a breaking change.
|
||||
type RTMOptions struct {
|
||||
// UseRTMStart set to true in order to use rtm.start or false to use rtm.connect
|
||||
// As of 11th July 2017 you should prefer setting this to false, see:
|
||||
// https://api.slack.com/changelog/2017-04-start-using-rtm-connect-and-stop-using-rtm-start
|
||||
UseRTMStart bool
|
||||
// signal that we are disconnected by closing the channel.
|
||||
// protect it with a mutex to ensure it only happens once.
|
||||
func (rtm *RTM) disconnect() {
|
||||
rtm.disconnectedm.Do(func() {
|
||||
close(rtm.disconnected)
|
||||
})
|
||||
}
|
||||
|
||||
// Disconnect and wait, blocking until a successful disconnection.
|
||||
func (rtm *RTM) Disconnect() error {
|
||||
// avoid RTM disconnect race conditions
|
||||
rtm.mu.Lock()
|
||||
defer rtm.mu.Unlock()
|
||||
|
||||
// always push into the disconnected channel when invoked,
|
||||
// always push into the kill channel when invoked,
|
||||
// this lets the ManagedConnection() function properly clean up.
|
||||
// if the buffer is full then just continue on.
|
||||
select {
|
||||
case rtm.disconnected <- struct{}{}:
|
||||
default:
|
||||
case rtm.killChannel <- true:
|
||||
return nil
|
||||
case <-rtm.disconnected:
|
||||
return ErrAlreadyDisconnected
|
||||
}
|
||||
|
||||
if !rtm.isConnected {
|
||||
return errors.New("Invalid call to Disconnect - Slack API is already disconnected")
|
||||
}
|
||||
|
||||
rtm.killChannel <- true
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetInfo returns the info structure received when calling
|
||||
@ -110,7 +98,7 @@ func (rtm *RTM) SendMessage(msg *OutgoingMessage) {
|
||||
}
|
||||
|
||||
func (rtm *RTM) resetDeadman() {
|
||||
timerReset(rtm.pingDeadman, deadmanDuration(rtm.pingInterval))
|
||||
rtm.pingDeadman.Reset(deadmanDuration(rtm.pingInterval))
|
||||
}
|
||||
|
||||
func deadmanDuration(d time.Duration) time.Duration {
|
||||
|
1
vendor/github.com/nlopes/slack/websocket_internals.go
generated
vendored
1
vendor/github.com/nlopes/slack/websocket_internals.go
generated
vendored
@ -18,6 +18,7 @@ type ConnectedEvent struct {
|
||||
// ConnectionErrorEvent contains information about a connection error
|
||||
type ConnectionErrorEvent struct {
|
||||
Attempt int
|
||||
Backoff time.Duration // how long we'll wait before the next attempt
|
||||
ErrorObj error
|
||||
}
|
||||
|
||||
|
148
vendor/github.com/nlopes/slack/websocket_managed_conn.go
generated
vendored
148
vendor/github.com/nlopes/slack/websocket_managed_conn.go
generated
vendored
@ -5,10 +5,12 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
stdurl "net/url"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/nlopes/slack/internal/timex"
|
||||
)
|
||||
|
||||
// ManageConnection can be called on a Slack RTM instance returned by the
|
||||
@ -37,6 +39,7 @@ func (rtm *RTM) ManageConnection() {
|
||||
if info, conn, err = rtm.connect(connectionCount, rtm.useRTMStart); err != nil {
|
||||
// when the connection is unsuccessful its fatal, and we need to bail out.
|
||||
rtm.Debugf("Failed to connect with RTM on try %d: %s", connectionCount, err)
|
||||
rtm.disconnect()
|
||||
return
|
||||
}
|
||||
|
||||
@ -44,7 +47,6 @@ func (rtm *RTM) ManageConnection() {
|
||||
// and conn.
|
||||
rtm.mu.Lock()
|
||||
rtm.conn = conn
|
||||
rtm.isConnected = true
|
||||
rtm.info = info
|
||||
rtm.mu.Unlock()
|
||||
|
||||
@ -55,20 +57,19 @@ func (rtm *RTM) ManageConnection() {
|
||||
|
||||
rtm.Debugf("RTM connection succeeded on try %d", connectionCount)
|
||||
|
||||
keepRunning := make(chan bool)
|
||||
// we're now connected (or have failed fatally) so we can set up
|
||||
// listeners
|
||||
go rtm.handleIncomingEvents(keepRunning)
|
||||
// we're now connected so we can set up listeners
|
||||
go rtm.handleIncomingEvents()
|
||||
|
||||
// this should be a blocking call until the connection has ended
|
||||
rtm.handleEvents(keepRunning)
|
||||
rtm.handleEvents()
|
||||
|
||||
// after being disconnected we need to check if it was intentional
|
||||
// if not then we should try to reconnect
|
||||
if rtm.wasIntentional {
|
||||
select {
|
||||
case <-rtm.disconnected:
|
||||
// after handle events returns we need to check if we're disconnected
|
||||
return
|
||||
default:
|
||||
// otherwise continue and run the loop again to reconnect
|
||||
}
|
||||
// else continue and run the loop again to connect
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,18 +88,20 @@ func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocke
|
||||
// used to provide exponential backoff wait time with jitter before trying
|
||||
// to connect to slack again
|
||||
boff := &backoff{
|
||||
Min: 100 * time.Millisecond,
|
||||
Max: 5 * time.Minute,
|
||||
Factor: 2,
|
||||
Jitter: true,
|
||||
Max: 5 * time.Minute,
|
||||
}
|
||||
|
||||
for {
|
||||
var (
|
||||
backoff time.Duration
|
||||
)
|
||||
|
||||
// send connecting event
|
||||
rtm.IncomingEvents <- RTMEvent{"connecting", &ConnectingEvent{
|
||||
Attempt: boff.attempts + 1,
|
||||
ConnectionCount: connectionCount,
|
||||
}}
|
||||
|
||||
// attempt to start the connection
|
||||
info, conn, err := rtm.startRTMAndDial(useRTMStart)
|
||||
if err == nil {
|
||||
@ -108,32 +111,48 @@ func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocke
|
||||
// check for fatal errors
|
||||
switch err.Error() {
|
||||
case errInvalidAuth, errInactiveAccount, errMissingAuthToken:
|
||||
rtm.Debugf("Invalid auth when connecting with RTM: %s", err)
|
||||
rtm.Debugf("invalid auth when connecting with RTM: %s", err)
|
||||
rtm.IncomingEvents <- RTMEvent{"invalid_auth", &InvalidAuthEvent{}}
|
||||
return nil, nil, err
|
||||
default:
|
||||
}
|
||||
|
||||
switch actual := err.(type) {
|
||||
case statusCodeError:
|
||||
if actual.Code == http.StatusNotFound {
|
||||
rtm.Debugf("invalid auth when connecting with RTM: %s", err)
|
||||
rtm.IncomingEvents <- RTMEvent{"invalid_auth", &InvalidAuthEvent{}}
|
||||
return nil, nil, err
|
||||
}
|
||||
case *RateLimitedError:
|
||||
backoff = actual.RetryAfter
|
||||
default:
|
||||
}
|
||||
|
||||
backoff = timex.Max(backoff, boff.Duration())
|
||||
// any other errors are treated as recoverable and we try again after
|
||||
// sending the event along the IncomingEvents channel
|
||||
rtm.IncomingEvents <- RTMEvent{"connection_error", &ConnectionErrorEvent{
|
||||
Attempt: boff.attempts,
|
||||
Backoff: backoff,
|
||||
ErrorObj: err,
|
||||
}}
|
||||
|
||||
// check if Disconnect() has been invoked.
|
||||
select {
|
||||
case intentional := <-rtm.killChannel:
|
||||
if intentional {
|
||||
rtm.killConnection(intentional)
|
||||
return nil, nil, ErrRTMDisconnected
|
||||
}
|
||||
case <-rtm.disconnected:
|
||||
rtm.IncomingEvents <- RTMEvent{"disconnected", &DisconnectedEvent{Intentional: true}}
|
||||
return nil, nil, fmt.Errorf("disconnect received while trying to connect")
|
||||
return nil, nil, ErrRTMDisconnected
|
||||
default:
|
||||
}
|
||||
|
||||
// get time we should wait before attempting to connect again
|
||||
dur := boff.Duration()
|
||||
rtm.Debugf("reconnection %d failed: %s", boff.attempts+1, err)
|
||||
rtm.Debugln(" -> reconnecting in", dur)
|
||||
time.Sleep(dur)
|
||||
rtm.Debugf("reconnection %d failed: %s reconnecting in %v\n", boff.attempts, err, backoff)
|
||||
time.Sleep(backoff)
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,6 +176,14 @@ func (rtm *RTM) startRTMAndDial(useRTMStart bool) (info *Info, _ *websocket.Conn
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// install connection parameters
|
||||
u, err := stdurl.Parse(url)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u.RawQuery = rtm.connParams.Encode()
|
||||
url = u.String()
|
||||
|
||||
rtm.Debugf("Dialing to websocket on url %s", url)
|
||||
// Only use HTTPS for connections to prevent MITM attacks on the connection.
|
||||
upgradeHeader := http.Header{}
|
||||
@ -178,15 +205,19 @@ func (rtm *RTM) startRTMAndDial(useRTMStart bool) (info *Info, _ *websocket.Conn
|
||||
//
|
||||
// This should not be called directly! Instead a boolean value (true for
|
||||
// intentional, false otherwise) should be sent to the killChannel on the RTM.
|
||||
func (rtm *RTM) killConnection(keepRunning chan bool, intentional bool) error {
|
||||
func (rtm *RTM) killConnection(intentional bool) (err error) {
|
||||
rtm.Debugln("killing connection")
|
||||
if rtm.isConnected {
|
||||
close(keepRunning)
|
||||
|
||||
if rtm.conn != nil {
|
||||
err = rtm.conn.Close()
|
||||
}
|
||||
rtm.isConnected = false
|
||||
rtm.wasIntentional = intentional
|
||||
err := rtm.conn.Close()
|
||||
|
||||
rtm.IncomingEvents <- RTMEvent{"disconnected", &DisconnectedEvent{intentional}}
|
||||
|
||||
if intentional {
|
||||
rtm.disconnect()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -195,31 +226,29 @@ func (rtm *RTM) killConnection(keepRunning chan bool, intentional bool) error {
|
||||
// interval. This also sends outgoing messages that are received from the RTM's
|
||||
// outgoingMessages channel. This also handles incoming raw events from the RTM
|
||||
// rawEvents channel.
|
||||
func (rtm *RTM) handleEvents(keepRunning chan bool) {
|
||||
func (rtm *RTM) handleEvents() {
|
||||
ticker := time.NewTicker(rtm.pingInterval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
// catch "stop" signal on channel close
|
||||
case intentional := <-rtm.killChannel:
|
||||
_ = rtm.killConnection(keepRunning, intentional)
|
||||
_ = rtm.killConnection(intentional)
|
||||
return
|
||||
|
||||
// detect when the connection is dead.
|
||||
case <-rtm.pingDeadman.C:
|
||||
rtm.Debugln("deadman switch trigger disconnecting")
|
||||
_ = rtm.killConnection(keepRunning, false)
|
||||
_ = rtm.killConnection(false)
|
||||
return
|
||||
// send pings on ticker interval
|
||||
case <-ticker.C:
|
||||
err := rtm.ping()
|
||||
if err != nil {
|
||||
_ = rtm.killConnection(keepRunning, false)
|
||||
if err := rtm.ping(); err != nil {
|
||||
_ = rtm.killConnection(false)
|
||||
return
|
||||
}
|
||||
case <-rtm.forcePing:
|
||||
err := rtm.ping()
|
||||
if err != nil {
|
||||
_ = rtm.killConnection(keepRunning, false)
|
||||
if err := rtm.ping(); err != nil {
|
||||
_ = rtm.killConnection(false)
|
||||
return
|
||||
}
|
||||
// listen for messages that need to be sent
|
||||
@ -229,7 +258,8 @@ func (rtm *RTM) handleEvents(keepRunning chan bool) {
|
||||
case rawEvent := <-rtm.rawEvents:
|
||||
switch rtm.handleRawEvent(rawEvent) {
|
||||
case rtmEventTypeGoodbye:
|
||||
_ = rtm.killConnection(keepRunning, false)
|
||||
_ = rtm.killConnection(false)
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
@ -241,17 +271,10 @@ func (rtm *RTM) handleEvents(keepRunning chan bool) {
|
||||
//
|
||||
// This will stop executing once the RTM's keepRunning channel has been closed
|
||||
// or has anything sent to it.
|
||||
func (rtm *RTM) handleIncomingEvents(keepRunning <-chan bool) {
|
||||
func (rtm *RTM) handleIncomingEvents() {
|
||||
for {
|
||||
// non-blocking listen to see if channel is closed
|
||||
select {
|
||||
// catch "stop" signal on channel close
|
||||
case <-keepRunning:
|
||||
if err := rtm.receiveIncomingEvent(); err != nil {
|
||||
return
|
||||
default:
|
||||
if err := rtm.receiveIncomingEvent(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -274,7 +297,7 @@ func (rtm *RTM) sendWithDeadline(msg interface{}) error {
|
||||
// and instead lets a future failed 'PING' detect the failed connection.
|
||||
func (rtm *RTM) sendOutgoingMessage(msg OutgoingMessage) {
|
||||
rtm.Debugln("Sending message:", msg)
|
||||
if len(msg.Text) > MaxMessageTextLength {
|
||||
if len([]rune(msg.Text)) > MaxMessageTextLength {
|
||||
rtm.IncomingEvents <- RTMEvent{"outgoing_error", &MessageTooLongEvent{
|
||||
Message: msg,
|
||||
MaxLength: MaxMessageTextLength,
|
||||
@ -323,20 +346,32 @@ func (rtm *RTM) receiveIncomingEvent() error {
|
||||
// 'PING' message
|
||||
|
||||
// trigger a 'PING' to detect potential websocket disconnect
|
||||
rtm.forcePing <- true
|
||||
select {
|
||||
case rtm.forcePing <- true:
|
||||
case <-rtm.disconnected:
|
||||
}
|
||||
case err != nil:
|
||||
// All other errors from ReadJSON come from NextReader, and should
|
||||
// kill the read loop and force a reconnect.
|
||||
rtm.IncomingEvents <- RTMEvent{"incoming_error", &IncomingEventError{
|
||||
ErrorObj: err,
|
||||
}}
|
||||
rtm.killChannel <- false
|
||||
|
||||
select {
|
||||
case rtm.killChannel <- false:
|
||||
case <-rtm.disconnected:
|
||||
}
|
||||
|
||||
return err
|
||||
case len(event) == 0:
|
||||
rtm.Debugln("Received empty event")
|
||||
default:
|
||||
rtm.Debugln("Incoming Event:", string(event[:]))
|
||||
rtm.rawEvents <- event
|
||||
rtm.Debugln("Incoming Event:", string(event))
|
||||
select {
|
||||
case rtm.rawEvents <- event:
|
||||
case <-rtm.disconnected:
|
||||
rtm.Debugln("disonnected while attempting to send raw event")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -405,8 +440,7 @@ func (rtm *RTM) handlePong(event json.RawMessage) {
|
||||
rtm.resetDeadman()
|
||||
|
||||
if err := json.Unmarshal(event, &p); err != nil {
|
||||
logger.Println("RTM Error unmarshalling 'pong' event:", err)
|
||||
rtm.Debugln(" -> Erroneous 'ping' event:", string(event))
|
||||
rtm.Client.log.Println("RTM Error unmarshalling 'pong' event:", err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -423,8 +457,8 @@ func (rtm *RTM) handlePong(event json.RawMessage) {
|
||||
func (rtm *RTM) handleEvent(typeStr string, event json.RawMessage) {
|
||||
v, exists := EventMapping[typeStr]
|
||||
if !exists {
|
||||
rtm.Debugf("RTM Error, received unmapped event %q: %s\n", typeStr, string(event))
|
||||
err := fmt.Errorf("RTM Error: Received unmapped event %q: %s\n", typeStr, string(event))
|
||||
rtm.Debugf("RTM Error - received unmapped event %q: %s\n", typeStr, string(event))
|
||||
err := fmt.Errorf("RTM Error: Received unmapped event %q: %s", typeStr, string(event))
|
||||
rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}}
|
||||
return
|
||||
}
|
||||
@ -433,7 +467,7 @@ func (rtm *RTM) handleEvent(typeStr string, event json.RawMessage) {
|
||||
err := json.Unmarshal(event, recvEvent)
|
||||
if err != nil {
|
||||
rtm.Debugf("RTM Error, could not unmarshall event %q: %s\n", typeStr, string(event))
|
||||
err := fmt.Errorf("RTM Error: Could not unmarshall event %q: %s\n", typeStr, string(event))
|
||||
err := fmt.Errorf("RTM Error: Could not unmarshall event %q: %s", typeStr, string(event))
|
||||
rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}}
|
||||
return
|
||||
}
|
||||
|
7
vendor/github.com/nlopes/slack/websocket_misc.go
generated
vendored
7
vendor/github.com/nlopes/slack/websocket_misc.go
generated
vendored
@ -43,9 +43,10 @@ type HelloEvent struct{}
|
||||
|
||||
// PresenceChangeEvent represents the presence change event
|
||||
type PresenceChangeEvent struct {
|
||||
Type string `json:"type"`
|
||||
Presence string `json:"presence"`
|
||||
User string `json:"user"`
|
||||
Type string `json:"type"`
|
||||
Presence string `json:"presence"`
|
||||
User string `json:"user"`
|
||||
Users []string `json:"users"`
|
||||
}
|
||||
|
||||
// UserTypingEvent represents the user typing event
|
||||
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@ -66,8 +66,10 @@ github.com/nicksnyder/go-i18n/i18n
|
||||
github.com/nicksnyder/go-i18n/i18n/bundle
|
||||
github.com/nicksnyder/go-i18n/i18n/language
|
||||
github.com/nicksnyder/go-i18n/i18n/translation
|
||||
# github.com/nlopes/slack v0.4.0
|
||||
# github.com/nlopes/slack v0.5.1-0.20190623232825-2891986e2a3e
|
||||
github.com/nlopes/slack
|
||||
github.com/nlopes/slack/internal/errorsx
|
||||
github.com/nlopes/slack/internal/timex
|
||||
github.com/nlopes/slack/slackutilsx
|
||||
# github.com/olivere/elastic v6.2.21+incompatible
|
||||
github.com/olivere/elastic
|
||||
|
Loading…
Reference in New Issue
Block a user