mirror of
https://github.com/cwinfo/matterbridge.git
synced 2025-06-28 00:59:24 +00:00
Update nlopes/slack vendor
This commit is contained in:
1
vendor/github.com/nlopes/slack/.gitignore
generated
vendored
1
vendor/github.com/nlopes/slack/.gitignore
generated
vendored
@ -1,2 +1,3 @@
|
||||
*.test
|
||||
*~
|
||||
.idea/
|
||||
|
13
vendor/github.com/nlopes/slack/CHANGELOG.md
generated
vendored
13
vendor/github.com/nlopes/slack/CHANGELOG.md
generated
vendored
@ -1,3 +1,16 @@
|
||||
### v0.3.0 - July 30, 2018
|
||||
full differences can be viewed using `git log --oneline --decorate --color v0.2.0..v0.3.0`
|
||||
- slack events initial support added. (still considered experimental and undergoing changes, stability not promised)
|
||||
- vendored depedencies using dep, ensure using up to date tooling before filing issues.
|
||||
- RTM has improved its ability to identify dead connections and reconnect automatically (worth calling out in case it has unintended side effects).
|
||||
- bug fixes (various timestamp handling, error handling, RTM locking, etc).
|
||||
|
||||
### v0.2.0 - Feb 10, 2018
|
||||
|
||||
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)
|
||||
|
||||
### v0.1.0 - May 28, 2017
|
||||
|
||||
This is released before adding context support.
|
||||
|
33
vendor/github.com/nlopes/slack/Gopkg.lock
generated
vendored
Normal file
33
vendor/github.com/nlopes/slack/Gopkg.lock
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
# 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/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 = "888307bf47ee004aaaa4c45e6139929b4984f2253e48e382246bfb8c66f3cd65"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
18
vendor/github.com/nlopes/slack/README.md
generated
vendored
18
vendor/github.com/nlopes/slack/README.md
generated
vendored
@ -7,19 +7,20 @@ This library supports most if not all of the `api.slack.com` REST
|
||||
calls, as well as the Real-Time Messaging protocol over websocket, in
|
||||
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.1.0 - May 28, 2017
|
||||
### v0.2.0 - Feb 10, 2018
|
||||
|
||||
This is released before adding context support.
|
||||
As the used context package is the one from Go 1.7 this will be the last
|
||||
compatible with Go < 1.7.
|
||||
Release adds a bunch of functionality and improvements, mainly to give people a recent version to vendor against.
|
||||
|
||||
Please check [0.1.0](https://github.com/nlopes/slack/releases/tag/v0.1.0)
|
||||
Please check [0.2.0](https://github.com/nlopes/slack/releases/tag/v0.2.0)
|
||||
|
||||
### CHANGELOG.md
|
||||
|
||||
As of this version a [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
|
||||
|
||||
@ -79,6 +80,11 @@ func main() {
|
||||
See https://github.com/nlopes/slack/blob/master/examples/websocket/websocket.go
|
||||
|
||||
|
||||
## Minimal EventsAPI usage:
|
||||
|
||||
See https://github.com/nlopes/slack/blob/master/examples/eventsapi/events.go
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
You are more than welcome to contribute to this project. Fork and
|
||||
|
38
vendor/github.com/nlopes/slack/admin.go
generated
vendored
38
vendor/github.com/nlopes/slack/admin.go
generated
vendored
@ -12,9 +12,9 @@ type adminResponse struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func adminRequest(ctx context.Context, method string, teamName string, values url.Values, debug bool) (*adminResponse, error) {
|
||||
func adminRequest(ctx context.Context, client HTTPRequester, method string, teamName string, values url.Values, debug bool) (*adminResponse, error) {
|
||||
adminResponse := &adminResponse{}
|
||||
err := parseAdminResponse(ctx, method, teamName, values, adminResponse, debug)
|
||||
err := parseAdminResponse(ctx, client, method, teamName, values, adminResponse, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -35,12 +35,12 @@ func (api *Client) DisableUser(teamName string, uid string) error {
|
||||
func (api *Client) DisableUserContext(ctx context.Context, teamName string, uid string) error {
|
||||
values := url.Values{
|
||||
"user": {uid},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "setInactive", teamName, values, api.debug)
|
||||
_, 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)
|
||||
}
|
||||
@ -61,12 +61,13 @@ func (api *Client) InviteGuestContext(ctx context.Context, teamName, channel, fi
|
||||
"first_name": {firstName},
|
||||
"last_name": {lastName},
|
||||
"ultra_restricted": {"1"},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"resend": {"true"},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "invite", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to invite single-channel guest: %s", err)
|
||||
}
|
||||
@ -87,12 +88,13 @@ func (api *Client) InviteRestrictedContext(ctx context.Context, teamName, channe
|
||||
"first_name": {firstName},
|
||||
"last_name": {lastName},
|
||||
"restricted": {"1"},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"resend": {"true"},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "invite", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to restricted account: %s", err)
|
||||
}
|
||||
@ -111,12 +113,12 @@ func (api *Client) InviteToTeamContext(ctx context.Context, teamName, firstName,
|
||||
"email": {emailAddress},
|
||||
"first_name": {firstName},
|
||||
"last_name": {lastName},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "invite", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to invite to team: %s", err)
|
||||
}
|
||||
@ -133,12 +135,12 @@ func (api *Client) SetRegular(teamName, user string) error {
|
||||
func (api *Client) SetRegularContext(ctx context.Context, teamName, user string) error {
|
||||
values := url.Values{
|
||||
"user": {user},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "setRegular", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "setRegular", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to change the user (%s) to a regular user: %s", user, err)
|
||||
}
|
||||
@ -155,12 +157,12 @@ func (api *Client) SendSSOBindingEmail(teamName, user string) error {
|
||||
func (api *Client) SendSSOBindingEmailContext(ctx context.Context, teamName, user string) error {
|
||||
values := url.Values{
|
||||
"user": {user},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "sendSSOBind", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "sendSSOBind", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to send SSO binding email for user (%s): %s", user, err)
|
||||
}
|
||||
@ -178,12 +180,12 @@ func (api *Client) SetUltraRestrictedContext(ctx context.Context, teamName, uid,
|
||||
values := url.Values{
|
||||
"user": {uid},
|
||||
"channel": {channel},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "setUltraRestricted", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "setUltraRestricted", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to ultra-restrict account: %s", err)
|
||||
}
|
||||
@ -200,12 +202,12 @@ func (api *Client) SetRestricted(teamName, uid string) error {
|
||||
func (api *Client) SetRestrictedContext(ctx context.Context, teamName, uid string) error {
|
||||
values := url.Values{
|
||||
"user": {uid},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "setRestricted", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "setRestricted", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to restrict account: %s", err)
|
||||
}
|
||||
|
3
vendor/github.com/nlopes/slack/attachments.go
generated
vendored
3
vendor/github.com/nlopes/slack/attachments.go
generated
vendored
@ -59,6 +59,7 @@ type AttachmentActionCallback struct {
|
||||
AttachmentID string `json:"attachment_id"`
|
||||
Token string `json:"token"`
|
||||
ResponseURL string `json:"response_url"`
|
||||
TriggerID string `json:"trigger_id"`
|
||||
}
|
||||
|
||||
// ConfirmationField are used to ask users to confirm actions
|
||||
@ -75,7 +76,9 @@ type Attachment struct {
|
||||
Fallback string `json:"fallback"`
|
||||
|
||||
CallbackID string `json:"callback_id,omitempty"`
|
||||
ID int `json:"id,omitempty"`
|
||||
|
||||
AuthorID string `json:"author_id,omitempty"`
|
||||
AuthorName string `json:"author_name,omitempty"`
|
||||
AuthorSubname string `json:"author_subname,omitempty"`
|
||||
AuthorLink string `json:"author_link,omitempty"`
|
||||
|
2
vendor/github.com/nlopes/slack/backoff.go
generated
vendored
2
vendor/github.com/nlopes/slack/backoff.go
generated
vendored
@ -38,7 +38,7 @@ func (b *backoff) Duration() time.Duration {
|
||||
}
|
||||
//calculate this duration
|
||||
dur := float64(b.Min) * math.Pow(b.Factor, float64(b.attempts))
|
||||
if b.Jitter == true {
|
||||
if b.Jitter {
|
||||
dur = rand.Float64()*(dur-float64(b.Min)) + float64(b.Min)
|
||||
}
|
||||
//cap!
|
||||
|
9
vendor/github.com/nlopes/slack/bots.go
generated
vendored
9
vendor/github.com/nlopes/slack/bots.go
generated
vendored
@ -19,9 +19,9 @@ type botResponseFull struct {
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func botRequest(ctx context.Context, path string, values url.Values, debug bool) (*botResponseFull, error) {
|
||||
func botRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*botResponseFull, error) {
|
||||
response := &botResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -39,10 +39,11 @@ func (api *Client) GetBotInfo(bot string) (*Bot, error) {
|
||||
// GetBotInfoContext will retrieve the complete bot information using a custom context
|
||||
func (api *Client) GetBotInfoContext(ctx context.Context, bot string) (*Bot, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"bot": {bot},
|
||||
}
|
||||
response, err := botRequest(ctx, "bots.info", values, api.debug)
|
||||
|
||||
response, err := botRequest(ctx, api.httpclient, "bots.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
100
vendor/github.com/nlopes/slack/channels.go
generated
vendored
100
vendor/github.com/nlopes/slack/channels.go
generated
vendored
@ -20,14 +20,15 @@ type channelResponseFull struct {
|
||||
// Channel contains information about the channel
|
||||
type Channel struct {
|
||||
groupConversation
|
||||
IsChannel bool `json:"is_channel"`
|
||||
IsGeneral bool `json:"is_general"`
|
||||
IsMember bool `json:"is_member"`
|
||||
IsChannel bool `json:"is_channel"`
|
||||
IsGeneral bool `json:"is_general"`
|
||||
IsMember bool `json:"is_member"`
|
||||
Locale string `json:"locale"`
|
||||
}
|
||||
|
||||
func channelRequest(ctx context.Context, path string, values url.Values, debug bool) (*channelResponseFull, error) {
|
||||
func channelRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*channelResponseFull, error) {
|
||||
response := &channelResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postForm(ctx, client, SLACK_API+path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -45,12 +46,13 @@ func (api *Client) ArchiveChannel(channelID string) error {
|
||||
|
||||
// ArchiveChannelContext archives the given channel with a custom context
|
||||
// see https://api.slack.com/methods/channels.archive
|
||||
func (api *Client) ArchiveChannelContext(ctx context.Context, channelID string) error {
|
||||
func (api *Client) ArchiveChannelContext(ctx context.Context, channelID string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
_, err := channelRequest(ctx, "channels.archive", values, api.debug)
|
||||
|
||||
_, err = channelRequest(ctx, api.httpclient, "channels.archive", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -62,12 +64,13 @@ func (api *Client) UnarchiveChannel(channelID string) error {
|
||||
|
||||
// UnarchiveChannelContext unarchives the given channel with a custom context
|
||||
// see https://api.slack.com/methods/channels.unarchive
|
||||
func (api *Client) UnarchiveChannelContext(ctx context.Context, channelID string) error {
|
||||
func (api *Client) UnarchiveChannelContext(ctx context.Context, channelID string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
_, err := channelRequest(ctx, "channels.unarchive", values, api.debug)
|
||||
|
||||
_, err = channelRequest(ctx, api.httpclient, "channels.unarchive", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -81,10 +84,11 @@ func (api *Client) CreateChannel(channelName string) (*Channel, error) {
|
||||
// see https://api.slack.com/methods/channels.create
|
||||
func (api *Client) CreateChannelContext(ctx context.Context, channelName string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"name": {channelName},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.create", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.create", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -101,7 +105,7 @@ func (api *Client) GetChannelHistory(channelID string, params HistoryParameters)
|
||||
// see https://api.slack.com/methods/channels.history
|
||||
func (api *Client) GetChannelHistoryContext(ctx context.Context, channelID string, params HistoryParameters) (*History, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
if params.Latest != DEFAULT_HISTORY_LATEST {
|
||||
@ -120,6 +124,7 @@ func (api *Client) GetChannelHistoryContext(ctx context.Context, channelID strin
|
||||
values.Add("inclusive", "0")
|
||||
}
|
||||
}
|
||||
|
||||
if params.Unreads != DEFAULT_HISTORY_UNREADS {
|
||||
if params.Unreads {
|
||||
values.Add("unreads", "1")
|
||||
@ -127,7 +132,8 @@ func (api *Client) GetChannelHistoryContext(ctx context.Context, channelID strin
|
||||
values.Add("unreads", "0")
|
||||
}
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.history", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.history", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -144,10 +150,11 @@ 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.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.info", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -164,11 +171,12 @@ func (api *Client) InviteUserToChannel(channelID, user string) (*Channel, error)
|
||||
// see https://api.slack.com/methods/channels.invite
|
||||
func (api *Client) InviteUserToChannelContext(ctx context.Context, channelID, user string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"user": {user},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.invite", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.invite", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -185,10 +193,11 @@ func (api *Client) JoinChannel(channelName string) (*Channel, error) {
|
||||
// see https://api.slack.com/methods/channels.join
|
||||
func (api *Client) JoinChannelContext(ctx context.Context, channelName string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"name": {channelName},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.join", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.join", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -205,17 +214,16 @@ func (api *Client) LeaveChannel(channelID string) (bool, error) {
|
||||
// see https://api.slack.com/methods/channels.leave
|
||||
func (api *Client) LeaveChannelContext(ctx context.Context, channelID string) (bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.leave", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.leave", values, api.debug)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if response.NotInChannel {
|
||||
return response.NotInChannel, nil
|
||||
}
|
||||
return false, nil
|
||||
|
||||
return response.NotInChannel, nil
|
||||
}
|
||||
|
||||
// KickUserFromChannel kicks a user from a given channel
|
||||
@ -226,13 +234,14 @@ func (api *Client) KickUserFromChannel(channelID, user string) error {
|
||||
|
||||
// KickUserFromChannelContext kicks a user from a given channel with a custom context
|
||||
// see https://api.slack.com/methods/channels.kick
|
||||
func (api *Client) KickUserFromChannelContext(ctx context.Context, channelID, user string) error {
|
||||
func (api *Client) KickUserFromChannelContext(ctx context.Context, channelID, user string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"user": {user},
|
||||
}
|
||||
_, err := channelRequest(ctx, "channels.kick", values, api.debug)
|
||||
|
||||
_, err = channelRequest(ctx, api.httpclient, "channels.kick", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -246,12 +255,13 @@ func (api *Client) GetChannels(excludeArchived bool) ([]Channel, error) {
|
||||
// see https://api.slack.com/methods/channels.list
|
||||
func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool) ([]Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if excludeArchived {
|
||||
values.Add("exclude_archived", "1")
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.list", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.list", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -271,13 +281,14 @@ func (api *Client) SetChannelReadMark(channelID, ts string) error {
|
||||
// SetChannelReadMarkContext sets the read mark of a given channel to a specific point with a custom context
|
||||
// For more details see SetChannelReadMark documentation
|
||||
// see https://api.slack.com/methods/channels.mark
|
||||
func (api *Client) SetChannelReadMarkContext(ctx context.Context, channelID, ts string) error {
|
||||
func (api *Client) SetChannelReadMarkContext(ctx context.Context, channelID, ts string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"ts": {ts},
|
||||
}
|
||||
_, err := channelRequest(ctx, "channels.mark", values, api.debug)
|
||||
|
||||
_, err = channelRequest(ctx, api.httpclient, "channels.mark", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -291,13 +302,14 @@ func (api *Client) RenameChannel(channelID, name string) (*Channel, error) {
|
||||
// see https://api.slack.com/methods/channels.rename
|
||||
func (api *Client) RenameChannelContext(ctx context.Context, channelID, name string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"name": {name},
|
||||
}
|
||||
|
||||
// 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, "channels.rename", values, api.debug)
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.rename", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -314,11 +326,12 @@ func (api *Client) SetChannelPurpose(channelID, purpose string) (string, error)
|
||||
// see https://api.slack.com/methods/channels.setPurpose
|
||||
func (api *Client) SetChannelPurposeContext(ctx context.Context, channelID, purpose string) (string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"purpose": {purpose},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.setPurpose", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.setPurpose", values, api.debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -335,11 +348,12 @@ func (api *Client) SetChannelTopic(channelID, topic string) (string, error) {
|
||||
// see https://api.slack.com/methods/channels.setTopic
|
||||
func (api *Client) SetChannelTopicContext(ctx context.Context, channelID, topic string) (string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"topic": {topic},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.setTopic", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.setTopic", values, api.debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -356,11 +370,11 @@ func (api *Client) GetChannelReplies(channelID, thread_ts string) ([]Message, er
|
||||
// see https://api.slack.com/methods/channels.replies
|
||||
func (api *Client) GetChannelRepliesContext(ctx context.Context, channelID, thread_ts string) ([]Message, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"thread_ts": {thread_ts},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.replies", values, api.debug)
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.replies", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
200
vendor/github.com/nlopes/slack/chat.go
generated
vendored
200
vendor/github.com/nlopes/slack/chat.go
generated
vendored
@ -3,17 +3,16 @@ package slack
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_MESSAGE_USERNAME = ""
|
||||
DEFAULT_MESSAGE_THREAD_TIMESTAMP = ""
|
||||
DEFAULT_MESSAGE_REPLY_BROADCAST = false
|
||||
DEFAULT_MESSAGE_ASUSER = false
|
||||
DEFAULT_MESSAGE_PARSE = ""
|
||||
DEFAULT_MESSAGE_THREAD_TIMESTAMP = ""
|
||||
DEFAULT_MESSAGE_LINK_NAMES = 0
|
||||
DEFAULT_MESSAGE_UNFURL_LINKS = false
|
||||
DEFAULT_MESSAGE_UNFURL_MEDIA = true
|
||||
@ -24,16 +23,26 @@ const (
|
||||
)
|
||||
|
||||
type chatResponseFull struct {
|
||||
Channel string `json:"channel"`
|
||||
Timestamp string `json:"ts"`
|
||||
Text string `json:"text"`
|
||||
Channel string `json:"channel"`
|
||||
Timestamp string `json:"ts"` //Regualr message timestamp
|
||||
MessageTimeStamp string `json:"message_ts"` //Ephemeral message timestamp
|
||||
Text string `json:"text"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
// getMessageTimestamp will inspect the `chatResponseFull` to ruturn a timestamp value
|
||||
// in `chat.postMessage` its under `ts`
|
||||
// in `chat.postEphemeral` its under `message_ts`
|
||||
func (c chatResponseFull) getMessageTimestamp() string {
|
||||
if len(c.Timestamp) > 0 {
|
||||
return c.Timestamp
|
||||
}
|
||||
return c.MessageTimeStamp
|
||||
}
|
||||
|
||||
// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
|
||||
type PostMessageParameters struct {
|
||||
Text string `json:"text"`
|
||||
Username string `json:"user_name"`
|
||||
Username string `json:"username"`
|
||||
AsUser bool `json:"as_user"`
|
||||
Parse string `json:"parse"`
|
||||
ThreadTimestamp string `json:"thread_ts"`
|
||||
@ -55,18 +64,19 @@ type PostMessageParameters struct {
|
||||
// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
|
||||
func NewPostMessageParameters() PostMessageParameters {
|
||||
return PostMessageParameters{
|
||||
Username: DEFAULT_MESSAGE_USERNAME,
|
||||
User: DEFAULT_MESSAGE_USERNAME,
|
||||
AsUser: DEFAULT_MESSAGE_ASUSER,
|
||||
Parse: DEFAULT_MESSAGE_PARSE,
|
||||
LinkNames: DEFAULT_MESSAGE_LINK_NAMES,
|
||||
Attachments: nil,
|
||||
UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
|
||||
UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
|
||||
IconURL: DEFAULT_MESSAGE_ICON_URL,
|
||||
IconEmoji: DEFAULT_MESSAGE_ICON_EMOJI,
|
||||
Markdown: DEFAULT_MESSAGE_MARKDOWN,
|
||||
EscapeText: DEFAULT_MESSAGE_ESCAPE_TEXT,
|
||||
Username: DEFAULT_MESSAGE_USERNAME,
|
||||
User: DEFAULT_MESSAGE_USERNAME,
|
||||
AsUser: DEFAULT_MESSAGE_ASUSER,
|
||||
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,
|
||||
IconEmoji: DEFAULT_MESSAGE_ICON_EMOJI,
|
||||
Markdown: DEFAULT_MESSAGE_MARKDOWN,
|
||||
EscapeText: DEFAULT_MESSAGE_ESCAPE_TEXT,
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,11 +122,10 @@ func (api *Client) PostMessageContext(ctx context.Context, channel, text string,
|
||||
// PostEphemeral sends an ephemeral message to a user in 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) PostEphemeral(channel, userID string, options ...MsgOption) (string, error) {
|
||||
options = append(options, MsgOptionPostEphemeral())
|
||||
func (api *Client) PostEphemeral(channelID, userID string, options ...MsgOption) (string, error) {
|
||||
return api.PostEphemeralContext(
|
||||
context.Background(),
|
||||
channel,
|
||||
channelID,
|
||||
userID,
|
||||
options...,
|
||||
)
|
||||
@ -124,30 +133,19 @@ func (api *Client) PostEphemeral(channel, 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, channel, userID string, options ...MsgOption) (string, error) {
|
||||
path, values, err := ApplyMsgOptions(api.config.token, channel, options...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
values.Add("user", userID)
|
||||
|
||||
response, err := chatRequest(ctx, path, values, api.debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return response.Timestamp, nil
|
||||
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))...)
|
||||
return timestamp, err
|
||||
}
|
||||
|
||||
// UpdateMessage updates a message in a channel
|
||||
func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) {
|
||||
return api.UpdateMessageContext(context.Background(), channel, timestamp, text)
|
||||
func (api *Client) UpdateMessage(channelID, timestamp, text string) (string, string, string, error) {
|
||||
return api.UpdateMessageContext(context.Background(), channelID, timestamp, text)
|
||||
}
|
||||
|
||||
// UpdateMessage updates a message in a channel
|
||||
func (api *Client) UpdateMessageContext(ctx context.Context, channel, timestamp, text string) (string, string, string, error) {
|
||||
return api.SendMessageContext(ctx, channel, MsgOptionUpdate(timestamp), MsgOptionText(text, true))
|
||||
// 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))
|
||||
}
|
||||
|
||||
// SendMessage more flexible method for configuring messages.
|
||||
@ -156,22 +154,30 @@ func (api *Client) SendMessage(channel string, options ...MsgOption) (string, st
|
||||
}
|
||||
|
||||
// SendMessageContext more flexible method for configuring messages with a custom context.
|
||||
func (api *Client) SendMessageContext(ctx context.Context, channel string, options ...MsgOption) (string, string, string, error) {
|
||||
channel, values, err := ApplyMsgOptions(api.config.token, channel, options...)
|
||||
if err != nil {
|
||||
func (api *Client) SendMessageContext(ctx context.Context, channelID string, options ...MsgOption) (channel string, timestamp string, text string, err error) {
|
||||
var (
|
||||
config sendConfig
|
||||
response chatResponseFull
|
||||
)
|
||||
|
||||
if config, err = applyMsgOptions(api.token, channelID, options...); err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
response, err := chatRequest(ctx, channel, values, api.debug)
|
||||
if err != nil {
|
||||
if err = postSlackMethod(ctx, api.httpclient, string(config.mode), config.values, &response, api.debug); err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
return response.Channel, response.Timestamp, response.Text, nil
|
||||
return response.Channel, response.getMessageTimestamp(), response.Text, response.Err()
|
||||
}
|
||||
|
||||
// ApplyMsgOptions utility function for debugging/testing chat requests.
|
||||
func ApplyMsgOptions(token, channel string, options ...MsgOption) (string, url.Values, error) {
|
||||
config, err := applyMsgOptions(token, channel, options...)
|
||||
return string(config.mode), config.values, err
|
||||
}
|
||||
|
||||
func applyMsgOptions(token, channel string, options ...MsgOption) (sendConfig, error) {
|
||||
config := sendConfig{
|
||||
mode: chatPostMessage,
|
||||
values: url.Values{
|
||||
@ -182,11 +188,11 @@ func ApplyMsgOptions(token, channel string, options ...MsgOption) (string, url.V
|
||||
|
||||
for _, opt := range options {
|
||||
if err := opt(&config); err != nil {
|
||||
return string(config.mode), config.values, err
|
||||
return config, err
|
||||
}
|
||||
}
|
||||
|
||||
return string(config.mode), config.values, nil
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func escapeMessage(message string) string {
|
||||
@ -194,18 +200,6 @@ func escapeMessage(message string) string {
|
||||
return replacer.Replace(message)
|
||||
}
|
||||
|
||||
func chatRequest(ctx context.Context, path string, values url.Values, debug bool) (*chatResponseFull, error) {
|
||||
response := &chatResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
type sendMode string
|
||||
|
||||
const (
|
||||
@ -213,6 +207,7 @@ const (
|
||||
chatPostMessage sendMode = "chat.postMessage"
|
||||
chatDelete sendMode = "chat.delete"
|
||||
chatPostEphemeral sendMode = "chat.postEphemeral"
|
||||
chatMeMessage sendMode = "chat.meMessage"
|
||||
)
|
||||
|
||||
type sendConfig struct {
|
||||
@ -232,7 +227,8 @@ func MsgOptionPost() MsgOption {
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionPostEphemeral posts an ephemeral message
|
||||
// MsgOptionPostEphemeral - DEPRECATED: use MsgOptionPostEphemeral2
|
||||
// posts an ephemeral message.
|
||||
func MsgOptionPostEphemeral() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.mode = chatPostEphemeral
|
||||
@ -241,6 +237,25 @@ func MsgOptionPostEphemeral() MsgOption {
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionPostEphemeral2 - posts an ephemeral message to the provided user.
|
||||
func MsgOptionPostEphemeral2(userID string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.mode = chatPostEphemeral
|
||||
MsgOptionUser(userID)(config)
|
||||
config.values.Del("ts")
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionMeMessage posts a "me message" type from the calling user
|
||||
func MsgOptionMeMessage() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.mode = chatMeMessage
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionUpdate updates a message based on the timestamp.
|
||||
func MsgOptionUpdate(timestamp string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
@ -269,6 +284,14 @@ func MsgOptionAsUser(b bool) MsgOption {
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionUser set the user for the message.
|
||||
func MsgOptionUser(userID string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.values.Set("user", userID)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionText provide the text for the message, optionally escape the provided
|
||||
// text.
|
||||
func MsgOptionText(text string, escape bool) MsgOption {
|
||||
@ -304,6 +327,14 @@ func MsgOptionEnableLinkUnfurl() MsgOption {
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionDisableLinkUnfurl disables link unfurling
|
||||
func MsgOptionDisableLinkUnfurl() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.values.Set("unfurl_links", "false")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionDisableMediaUnfurl disables media unfurling.
|
||||
func MsgOptionDisableMediaUnfurl() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
@ -320,11 +351,52 @@ func MsgOptionDisableMarkdown() MsgOption {
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionTS sets the thread TS of the message to enable creating or replying to a thread
|
||||
func MsgOptionTS(ts string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.values.Set("thread_ts", ts)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionBroadcast sets reply_broadcast to true
|
||||
func MsgOptionBroadcast() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.values.Set("reply_broadcast", "true")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// this function combines multiple options into a single option.
|
||||
func MsgOptionCompose(options ...MsgOption) MsgOption {
|
||||
return func(c *sendConfig) error {
|
||||
for _, opt := range options {
|
||||
if err := opt(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func MsgOptionParse(b bool) MsgOption {
|
||||
return func(c *sendConfig) error {
|
||||
var v string
|
||||
if b {
|
||||
v = "1"
|
||||
} else {
|
||||
v = "0"
|
||||
}
|
||||
c.values.Set("parse", v)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionPostMessageParameters maintain backwards compatibility.
|
||||
func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
if params.Username != DEFAULT_MESSAGE_USERNAME {
|
||||
config.values.Set("username", string(params.Username))
|
||||
config.values.Set("username", params.Username)
|
||||
}
|
||||
|
||||
// chat.postEphemeral support
|
||||
@ -336,7 +408,7 @@ func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
|
||||
MsgOptionAsUser(params.AsUser)(config)
|
||||
|
||||
if params.Parse != DEFAULT_MESSAGE_PARSE {
|
||||
config.values.Set("parse", string(params.Parse))
|
||||
config.values.Set("parse", params.Parse)
|
||||
}
|
||||
if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
|
||||
config.values.Set("link_names", "1")
|
||||
|
529
vendor/github.com/nlopes/slack/conversation.go
generated
vendored
529
vendor/github.com/nlopes/slack/conversation.go
generated
vendored
@ -1,5 +1,13 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Conversation is the foundation for IM and BaseGroupConversation
|
||||
type conversation struct {
|
||||
ID string `json:"id"`
|
||||
@ -9,6 +17,20 @@ type conversation struct {
|
||||
Latest *Message `json:"latest,omitempty"`
|
||||
UnreadCount int `json:"unread_count,omitempty"`
|
||||
UnreadCountDisplay int `json:"unread_count_display,omitempty"`
|
||||
IsGroup bool `json:"is_group"`
|
||||
IsShared bool `json:"is_shared"`
|
||||
IsIM bool `json:"is_im"`
|
||||
IsExtShared bool `json:"is_ext_shared"`
|
||||
IsOrgShared bool `json:"is_org_shared"`
|
||||
IsPendingExtShared bool `json:"is_pending_ext_shared"`
|
||||
IsPrivate bool `json:"is_private"`
|
||||
IsMpIM bool `json:"is_mpim"`
|
||||
Unlinked int `json:"unlinked"`
|
||||
NameNormalized string `json:"name_normalized"`
|
||||
NumMembers int `json:"num_members"`
|
||||
Priority float64 `json:"priority"`
|
||||
// TODO support pending_shared
|
||||
// TODO support previous_names
|
||||
}
|
||||
|
||||
// GroupConversation is the foundation for Group and Channel
|
||||
@ -35,3 +57,510 @@ type Purpose struct {
|
||||
Creator string `json:"creator"`
|
||||
LastSet JSONTime `json:"last_set"`
|
||||
}
|
||||
|
||||
type GetUsersInConversationParameters struct {
|
||||
ChannelID string
|
||||
Cursor string
|
||||
Limit int
|
||||
}
|
||||
|
||||
type responseMetaData struct {
|
||||
NextCursor string `json:"next_cursor"`
|
||||
}
|
||||
|
||||
// GetUsersInConversation returns the list of users in a conversation
|
||||
func (api *Client) GetUsersInConversation(params *GetUsersInConversationParameters) ([]string, string, error) {
|
||||
return api.GetUsersInConversationContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetUsersInConversationContext returns the list of users in a conversation with a custom context
|
||||
func (api *Client) GetUsersInConversationContext(ctx context.Context, params *GetUsersInConversationParameters) ([]string, string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {params.ChannelID},
|
||||
}
|
||||
if params.Cursor != "" {
|
||||
values.Add("cursor", params.Cursor)
|
||||
}
|
||||
if params.Limit != 0 {
|
||||
values.Add("limit", strconv.Itoa(params.Limit))
|
||||
}
|
||||
response := struct {
|
||||
Members []string `json:"members"`
|
||||
ResponseMetaData responseMetaData `json:"response_metadata"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.members", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, "", errors.New(response.Error)
|
||||
}
|
||||
return response.Members, response.ResponseMetaData.NextCursor, nil
|
||||
}
|
||||
|
||||
// ArchiveConversation archives a conversation
|
||||
func (api *Client) ArchiveConversation(channelID string) error {
|
||||
return api.ArchiveConversationContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// ArchiveConversationContext archives a conversation with a custom context
|
||||
func (api *Client) ArchiveConversationContext(ctx context.Context, channelID string) error {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
response := SlackResponse{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.archive", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// UnArchiveConversation reverses conversation archival
|
||||
func (api *Client) UnArchiveConversation(channelID string) error {
|
||||
return api.UnArchiveConversationContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// UnArchiveConversationContext reverses conversation archival with a custom context
|
||||
func (api *Client) UnArchiveConversationContext(ctx context.Context, channelID string) error {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
response := SlackResponse{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.unarchive", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// SetTopicOfConversation sets the topic for a conversation
|
||||
func (api *Client) SetTopicOfConversation(channelID, topic string) (*Channel, error) {
|
||||
return api.SetTopicOfConversationContext(context.Background(), channelID, topic)
|
||||
}
|
||||
|
||||
// SetTopicOfConversationContext sets the topic for a conversation with a custom context
|
||||
func (api *Client) SetTopicOfConversationContext(ctx context.Context, channelID, topic string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"topic": {topic},
|
||||
}
|
||||
response := struct {
|
||||
SlackResponse
|
||||
Channel *Channel `json:"channel"`
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.setTopic", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.Channel, response.Err()
|
||||
}
|
||||
|
||||
// SetPurposeOfConversation sets the purpose for a conversation
|
||||
func (api *Client) SetPurposeOfConversation(channelID, purpose string) (*Channel, error) {
|
||||
return api.SetPurposeOfConversationContext(context.Background(), channelID, purpose)
|
||||
}
|
||||
|
||||
// SetPurposeOfConversationContext sets the purpose for a conversation with a custom context
|
||||
func (api *Client) SetPurposeOfConversationContext(ctx context.Context, channelID, purpose string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"purpose": {purpose},
|
||||
}
|
||||
response := struct {
|
||||
SlackResponse
|
||||
Channel *Channel `json:"channel"`
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.setPurpose", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.Channel, response.Err()
|
||||
}
|
||||
|
||||
// RenameConversation renames a conversation
|
||||
func (api *Client) RenameConversation(channelID, channelName string) (*Channel, error) {
|
||||
return api.RenameConversationContext(context.Background(), channelID, channelName)
|
||||
}
|
||||
|
||||
// RenameConversationContext renames a conversation with a custom context
|
||||
func (api *Client) RenameConversationContext(ctx context.Context, channelID, channelName string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"name": {channelName},
|
||||
}
|
||||
response := struct {
|
||||
SlackResponse
|
||||
Channel *Channel `json:"channel"`
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.rename", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.Channel, response.Err()
|
||||
}
|
||||
|
||||
// InviteUsersToConversation invites users to a channel
|
||||
func (api *Client) InviteUsersToConversation(channelID string, users ...string) (*Channel, error) {
|
||||
return api.InviteUsersToConversationContext(context.Background(), channelID, users...)
|
||||
}
|
||||
|
||||
// InviteUsersToConversationContext invites users to a channel with a custom context
|
||||
func (api *Client) InviteUsersToConversationContext(ctx context.Context, channelID string, users ...string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"users": {strings.Join(users, ",")},
|
||||
}
|
||||
response := struct {
|
||||
SlackResponse
|
||||
Channel *Channel `json:"channel"`
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.invite", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.Channel, response.Err()
|
||||
}
|
||||
|
||||
// KickUserFromConversation removes a user from a conversation
|
||||
func (api *Client) KickUserFromConversation(channelID string, user string) error {
|
||||
return api.KickUserFromConversationContext(context.Background(), channelID, user)
|
||||
}
|
||||
|
||||
// KickUserFromConversationContext removes a user from a conversation with a custom context
|
||||
func (api *Client) KickUserFromConversationContext(ctx context.Context, channelID string, user string) error {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"user": {user},
|
||||
}
|
||||
response := SlackResponse{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.kick", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// CloseConversation closes a direct message or multi-person direct message
|
||||
func (api *Client) CloseConversation(channelID string) (noOp bool, alreadyClosed bool, err error) {
|
||||
return api.CloseConversationContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// CloseConversationContext closes a direct message or multi-person direct message with a custom context
|
||||
func (api *Client) CloseConversationContext(ctx context.Context, channelID string) (noOp bool, alreadyClosed bool, err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
response := struct {
|
||||
SlackResponse
|
||||
NoOp bool `json:"no_op"`
|
||||
AlreadyClosed bool `json:"already_closed"`
|
||||
}{}
|
||||
|
||||
err = postSlackMethod(ctx, api.httpclient, "conversations.close", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
return response.NoOp, response.AlreadyClosed, response.Err()
|
||||
}
|
||||
|
||||
// CreateConversation initiates a public or private channel-based conversation
|
||||
func (api *Client) CreateConversation(channelName string, isPrivate bool) (*Channel, error) {
|
||||
return api.CreateConversationContext(context.Background(), channelName, isPrivate)
|
||||
}
|
||||
|
||||
// CreateConversationContext initiates a public or private channel-based conversation with a custom context
|
||||
func (api *Client) CreateConversationContext(ctx context.Context, channelName string, isPrivate bool) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"name": {channelName},
|
||||
"is_private": {strconv.FormatBool(isPrivate)},
|
||||
}
|
||||
response, err := channelRequest(
|
||||
ctx, api.httpclient, "conversations.create", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.Channel, response.Err()
|
||||
}
|
||||
|
||||
// GetConversationInfo retrieves information about a conversation
|
||||
func (api *Client) GetConversationInfo(channelID string, includeLocale bool) (*Channel, error) {
|
||||
return api.GetConversationInfoContext(context.Background(), channelID, includeLocale)
|
||||
}
|
||||
|
||||
// GetConversationInfoContext retrieves information about a conversation with a custom context
|
||||
func (api *Client) GetConversationInfoContext(ctx context.Context, channelID string, includeLocale bool) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"include_locale": {strconv.FormatBool(includeLocale)},
|
||||
}
|
||||
response, err := channelRequest(
|
||||
ctx, api.httpclient, "conversations.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.Channel, response.Err()
|
||||
}
|
||||
|
||||
// LeaveConversation leaves a conversation
|
||||
func (api *Client) LeaveConversation(channelID string) (bool, error) {
|
||||
return api.LeaveConversationContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// LeaveConversationContext leaves a conversation with a custom context
|
||||
func (api *Client) LeaveConversationContext(ctx context.Context, channelID string) (bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "conversations.leave", values, api.debug)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return response.NotInChannel, err
|
||||
}
|
||||
|
||||
type GetConversationRepliesParameters struct {
|
||||
ChannelID string
|
||||
Timestamp string
|
||||
Cursor string
|
||||
Inclusive bool
|
||||
Latest string
|
||||
Limit int
|
||||
Oldest string
|
||||
}
|
||||
|
||||
// GetConversationReplies retrieves a thread of messages posted to a conversation
|
||||
func (api *Client) GetConversationReplies(params *GetConversationRepliesParameters) (msgs []Message, hasMore bool, nextCursor string, err error) {
|
||||
return api.GetConversationRepliesContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetConversationRepliesContext retrieves a thread of messages posted to a conversation with a custom context
|
||||
func (api *Client) GetConversationRepliesContext(ctx context.Context, params *GetConversationRepliesParameters) (msgs []Message, hasMore bool, nextCursor string, err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {params.ChannelID},
|
||||
"ts": {params.Timestamp},
|
||||
}
|
||||
if params.Cursor != "" {
|
||||
values.Add("cursor", params.Cursor)
|
||||
}
|
||||
if params.Latest != "" {
|
||||
values.Add("latest", params.Latest)
|
||||
}
|
||||
if params.Limit != 0 {
|
||||
values.Add("limit", strconv.Itoa(params.Limit))
|
||||
}
|
||||
if params.Oldest != "" {
|
||||
values.Add("oldest", params.Oldest)
|
||||
}
|
||||
if params.Inclusive {
|
||||
values.Add("inclusive", "1")
|
||||
} else {
|
||||
values.Add("inclusive", "0")
|
||||
}
|
||||
response := struct {
|
||||
SlackResponse
|
||||
HasMore bool `json:"has_more"`
|
||||
ResponseMetaData struct {
|
||||
NextCursor string `json:"next_cursor"`
|
||||
} `json:"response_metadata"`
|
||||
Messages []Message `json:"messages"`
|
||||
}{}
|
||||
|
||||
err = postSlackMethod(ctx, api.httpclient, "conversations.replies", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, false, "", err
|
||||
}
|
||||
|
||||
return response.Messages, response.HasMore, response.ResponseMetaData.NextCursor, response.Err()
|
||||
}
|
||||
|
||||
type GetConversationsParameters struct {
|
||||
Cursor string
|
||||
ExcludeArchived string
|
||||
Limit int
|
||||
Types []string
|
||||
}
|
||||
|
||||
// GetConversations returns the list of channels in a Slack team
|
||||
func (api *Client) GetConversations(params *GetConversationsParameters) (channels []Channel, nextCursor string, err error) {
|
||||
return api.GetConversationsContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetConversationsContext returns the list of channels in a Slack team with a custom context
|
||||
func (api *Client) GetConversationsContext(ctx context.Context, params *GetConversationsParameters) (channels []Channel, nextCursor string, err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"exclude_archived": {params.ExcludeArchived},
|
||||
}
|
||||
if params.Cursor != "" {
|
||||
values.Add("cursor", params.Cursor)
|
||||
}
|
||||
if params.Limit != 0 {
|
||||
values.Add("limit", strconv.Itoa(params.Limit))
|
||||
}
|
||||
if params.Types != nil {
|
||||
values.Add("types", strings.Join(params.Types, ","))
|
||||
}
|
||||
response := struct {
|
||||
Channels []Channel `json:"channels"`
|
||||
ResponseMetaData responseMetaData `json:"response_metadata"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err = postSlackMethod(ctx, api.httpclient, "conversations.list", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return response.Channels, response.ResponseMetaData.NextCursor, response.Err()
|
||||
}
|
||||
|
||||
type OpenConversationParameters struct {
|
||||
ChannelID string
|
||||
ReturnIM bool
|
||||
Users []string
|
||||
}
|
||||
|
||||
// OpenConversation opens or resumes a direct message or multi-person direct message
|
||||
func (api *Client) OpenConversation(params *OpenConversationParameters) (*Channel, bool, bool, error) {
|
||||
return api.OpenConversationContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// OpenConversationContext opens or resumes a direct message or multi-person direct message with a custom context
|
||||
func (api *Client) OpenConversationContext(ctx context.Context, params *OpenConversationParameters) (*Channel, bool, bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"return_im": {strconv.FormatBool(params.ReturnIM)},
|
||||
}
|
||||
if params.ChannelID != "" {
|
||||
values.Add("channel", params.ChannelID)
|
||||
}
|
||||
if params.Users != nil {
|
||||
values.Add("users", strings.Join(params.Users, ","))
|
||||
}
|
||||
response := struct {
|
||||
Channel *Channel `json:"channel"`
|
||||
NoOp bool `json:"no_op"`
|
||||
AlreadyOpen bool `json:"already_open"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.open", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, false, false, err
|
||||
}
|
||||
|
||||
return response.Channel, response.NoOp, response.AlreadyOpen, response.Err()
|
||||
}
|
||||
|
||||
// JoinConversation joins an existing conversation
|
||||
func (api *Client) JoinConversation(channelID string) (*Channel, string, []string, error) {
|
||||
return api.JoinConversationContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// JoinConversationContext joins an existing conversation with a custom context
|
||||
func (api *Client) JoinConversationContext(ctx context.Context, channelID string) (*Channel, string, []string, error) {
|
||||
values := url.Values{"token": {api.token}, "channel": {channelID}}
|
||||
response := struct {
|
||||
Channel *Channel `json:"channel"`
|
||||
Warning string `json:"warning"`
|
||||
ResponseMetaData *struct {
|
||||
Warnings []string `json:"warnings"`
|
||||
} `json:"response_metadata"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.join", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
if response.Err() != nil {
|
||||
return nil, "", nil, response.Err()
|
||||
}
|
||||
var warnings []string
|
||||
if response.ResponseMetaData != nil {
|
||||
warnings = response.ResponseMetaData.Warnings
|
||||
}
|
||||
return response.Channel, response.Warning, warnings, nil
|
||||
}
|
||||
|
||||
type GetConversationHistoryParameters struct {
|
||||
ChannelID string
|
||||
Cursor string
|
||||
Inclusive bool
|
||||
Latest string
|
||||
Limit int
|
||||
Oldest string
|
||||
}
|
||||
|
||||
type GetConversationHistoryResponse struct {
|
||||
SlackResponse
|
||||
HasMore bool `json:"has_more"`
|
||||
PinCount int `json:"pin_count"`
|
||||
Latest string `json:"latest"`
|
||||
ResponseMetaData struct {
|
||||
NextCursor string `json:"next_cursor"`
|
||||
} `json:"response_metadata"`
|
||||
Messages []Message `json:"messages"`
|
||||
}
|
||||
|
||||
// GetConversationHistory joins an existing conversation
|
||||
func (api *Client) GetConversationHistory(params *GetConversationHistoryParameters) (*GetConversationHistoryResponse, error) {
|
||||
return api.GetConversationHistoryContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetConversationHistoryContext joins an existing conversation with a custom context
|
||||
func (api *Client) GetConversationHistoryContext(ctx context.Context, params *GetConversationHistoryParameters) (*GetConversationHistoryResponse, error) {
|
||||
values := url.Values{"token": {api.token}, "channel": {params.ChannelID}}
|
||||
if params.Cursor != "" {
|
||||
values.Add("cursor", params.Cursor)
|
||||
}
|
||||
if params.Inclusive {
|
||||
values.Add("inclusive", "1")
|
||||
} else {
|
||||
values.Add("inclusive", "0")
|
||||
}
|
||||
if params.Latest != "" {
|
||||
values.Add("latest", params.Latest)
|
||||
}
|
||||
if params.Limit != 0 {
|
||||
values.Add("limit", strconv.Itoa(params.Limit))
|
||||
}
|
||||
if params.Oldest != "" {
|
||||
values.Add("oldest", params.Oldest)
|
||||
}
|
||||
|
||||
response := GetConversationHistoryResponse{}
|
||||
|
||||
err := postSlackMethod(ctx, api.httpclient, "conversations.history", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
107
vendor/github.com/nlopes/slack/dialog.go
generated
vendored
Normal file
107
vendor/github.com/nlopes/slack/dialog.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type DialogTrigger struct {
|
||||
TriggerId string `json:"trigger_id"` //Required. Must respond within 3 seconds.
|
||||
Dialog Dialog `json:"dialog"` //Required.
|
||||
}
|
||||
|
||||
type Dialog struct {
|
||||
CallbackId string `json:"callback_id"` //Required.
|
||||
Title string `json:"title"` //Required.
|
||||
SubmitLabel string `json:"submit_label,omitempty"` //Optional. Default value is 'Submit'
|
||||
NotifyOnCancel bool `json:"notify_on_cancel,omitempty"` //Optional. Default value is false
|
||||
Elements []DialogElement `json:"elements"` //Required.
|
||||
}
|
||||
|
||||
type DialogElement interface{}
|
||||
|
||||
type DialogTextElement struct {
|
||||
Label string `json:"label"` //Required.
|
||||
Name string `json:"name"` //Required.
|
||||
Type string `json:"type"` //Required. Allowed values: "text", "textarea", "select".
|
||||
Placeholder string `json:"placeholder,omitempty"` //Optional.
|
||||
Optional bool `json:"optional,omitempty"` //Optional. Default value is false
|
||||
Value string `json:"value,omitempty"` //Optional.
|
||||
MaxLength int `json:"max_length,omitempty"` //Optional.
|
||||
MinLength int `json:"min_length,omitempty"` //Optional,. Default value is 0
|
||||
Hint string `json:"hint,omitempty"` //Optional.
|
||||
Subtype string `json:"subtype,omitempty"` //Optional. Allowed values: "email", "number", "tel", "url".
|
||||
}
|
||||
|
||||
type DialogSelectElement struct {
|
||||
Label string `json:"label"` //Required.
|
||||
Name string `json:"name"` //Required.
|
||||
Type string `json:"type"` //Required. Allowed values: "text", "textarea", "select".
|
||||
Placeholder string `json:"placeholder,omitempty"` //Optional.
|
||||
Optional bool `json:"optional,omitempty"` //Optional. Default value is false
|
||||
Value string `json:"value,omitempty"` //Optional.
|
||||
DataSource string `json:"data_source,omitempty"` //Optional. Allowed values: "users", "channels", "conversations", "external".
|
||||
SelectedOptions string `json:"selected_options,omitempty"` //Optional. Default value for "external" only
|
||||
Options []DialogElementOption `json:"options,omitempty"` //One of options or option_groups is required.
|
||||
OptionGroups []DialogElementOption `json:"option_groups,omitempty"` //Provide up to 100 options.
|
||||
}
|
||||
|
||||
type DialogElementOption struct {
|
||||
Label string `json:"label"` //Required.
|
||||
Value string `json:"value"` //Required.
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// OpenDialog opens a dialog window where the triggerId originated from
|
||||
func (api *Client) OpenDialog(triggerId string, dialog Dialog) (err error) {
|
||||
return api.OpenDialogContext(context.Background(), triggerId, dialog)
|
||||
}
|
||||
|
||||
// OpenDialogContext opens a dialog window where the triggerId originated from with a custom context
|
||||
func (api *Client) OpenDialogContext(ctx context.Context, triggerId string, dialog Dialog) (err error) {
|
||||
if triggerId == "" {
|
||||
return errors.New("received empty parameters")
|
||||
}
|
||||
|
||||
resp := DialogTrigger{
|
||||
TriggerId: triggerId,
|
||||
Dialog: dialog,
|
||||
}
|
||||
jsonResp, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response := &SlackResponse{}
|
||||
endpoint := SLACK_API + "dialog.open"
|
||||
if err := postJSON(ctx, api.httpclient, endpoint, api.token, jsonResp, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
34
vendor/github.com/nlopes/slack/dnd.go
generated
vendored
34
vendor/github.com/nlopes/slack/dnd.go
generated
vendored
@ -36,9 +36,9 @@ type dndTeamInfoResponse struct {
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func dndRequest(ctx context.Context, path string, values url.Values, debug bool) (*dndResponseFull, error) {
|
||||
func dndRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*dndResponseFull, error) {
|
||||
response := &dndResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -56,17 +56,16 @@ func (api *Client) EndDND() error {
|
||||
// EndDNDContext ends the user's scheduled Do Not Disturb session with a custom context
|
||||
func (api *Client) EndDNDContext(ctx context.Context) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "dnd.endDnd", values, response, api.debug); err != nil {
|
||||
|
||||
if err := postSlackMethod(ctx, api.httpclient, "dnd.endDnd", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
return nil
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// EndSnooze ends the current user's snooze mode
|
||||
@ -77,10 +76,10 @@ func (api *Client) EndSnooze() (*DNDStatus, error) {
|
||||
// EndSnoozeContext ends the current user's snooze mode with a custom context
|
||||
func (api *Client) EndSnoozeContext(ctx context.Context) (*DNDStatus, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response, err := dndRequest(ctx, "dnd.endSnooze", values, api.debug)
|
||||
response, err := dndRequest(ctx, api.httpclient, "dnd.endSnooze", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -95,12 +94,13 @@ func (api *Client) GetDNDInfo(user *string) (*DNDStatus, error) {
|
||||
// GetDNDInfoContext provides information about a user's current Do Not Disturb settings with a custom context.
|
||||
func (api *Client) GetDNDInfoContext(ctx context.Context, user *string) (*DNDStatus, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if user != nil {
|
||||
values.Set("user", *user)
|
||||
}
|
||||
response, err := dndRequest(ctx, "dnd.info", values, api.debug)
|
||||
|
||||
response, err := dndRequest(ctx, api.httpclient, "dnd.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -115,11 +115,12 @@ func (api *Client) GetDNDTeamInfo(users []string) (map[string]DNDStatus, error)
|
||||
// GetDNDTeamInfoContext provides information about a user's current Do Not Disturb settings with a custom context.
|
||||
func (api *Client) GetDNDTeamInfoContext(ctx context.Context, users []string) (map[string]DNDStatus, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"users": {strings.Join(users, ",")},
|
||||
}
|
||||
response := &dndTeamInfoResponse{}
|
||||
if err := post(ctx, "dnd.teamInfo", values, response, api.debug); err != nil {
|
||||
|
||||
if err := postSlackMethod(ctx, api.httpclient, "dnd.teamInfo", values, response, api.debug); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
@ -139,10 +140,11 @@ func (api *Client) SetSnooze(minutes int) (*DNDStatus, error) {
|
||||
// For more information see the SetSnooze docs
|
||||
func (api *Client) SetSnoozeContext(ctx context.Context, minutes int) (*DNDStatus, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"num_minutes": {strconv.Itoa(minutes)},
|
||||
}
|
||||
response, err := dndRequest(ctx, "dnd.setSnooze", values, api.debug)
|
||||
|
||||
response, err := dndRequest(ctx, api.httpclient, "dnd.setSnooze", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
5
vendor/github.com/nlopes/slack/emoji.go
generated
vendored
5
vendor/github.com/nlopes/slack/emoji.go
generated
vendored
@ -19,10 +19,11 @@ func (api *Client) GetEmoji() (map[string]string, error) {
|
||||
// GetEmojiContext retrieves all the emojis with a custom context
|
||||
func (api *Client) GetEmojiContext(ctx context.Context) (map[string]string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
response := &emojiResponseFull{}
|
||||
err := post(ctx, "emoji.list", values, response, api.debug)
|
||||
|
||||
err := postSlackMethod(ctx, api.httpclient, "emoji.list", values, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
60
vendor/github.com/nlopes/slack/files.go
generated
vendored
60
vendor/github.com/nlopes/slack/files.go
generated
vendored
@ -136,9 +136,9 @@ func NewGetFilesParameters() GetFilesParameters {
|
||||
}
|
||||
}
|
||||
|
||||
func fileRequest(ctx context.Context, path string, values url.Values, debug bool) (*fileResponseFull, error) {
|
||||
func fileRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*fileResponseFull, error) {
|
||||
response := &fileResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postForm(ctx, client, SLACK_API+path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -156,12 +156,13 @@ func (api *Client) GetFileInfo(fileID string, count, page int) (*File, []Comment
|
||||
// GetFileInfoContext retrieves a file and related comments with a custom context
|
||||
func (api *Client) GetFileInfoContext(ctx context.Context, fileID string, count, page int) (*File, []Comment, *Paging, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"file": {fileID},
|
||||
"count": {strconv.Itoa(count)},
|
||||
"page": {strconv.Itoa(page)},
|
||||
}
|
||||
response, err := fileRequest(ctx, "files.info", values, api.debug)
|
||||
|
||||
response, err := fileRequest(ctx, api.httpclient, "files.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
@ -176,7 +177,7 @@ func (api *Client) GetFiles(params GetFilesParameters) ([]File, *Paging, error)
|
||||
// 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{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.User != DEFAULT_FILES_USER {
|
||||
values.Add("user", params.User)
|
||||
@ -199,7 +200,8 @@ func (api *Client) GetFilesContext(ctx context.Context, params GetFilesParameter
|
||||
if params.Page != DEFAULT_FILES_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
response, err := fileRequest(ctx, "files.list", values, api.debug)
|
||||
|
||||
response, err := fileRequest(ctx, api.httpclient, "files.list", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -221,7 +223,7 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam
|
||||
}
|
||||
response := &fileResponseFull{}
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.Filetype != "" {
|
||||
values.Add("filetype", params.Filetype)
|
||||
@ -240,11 +242,11 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam
|
||||
}
|
||||
if params.Content != "" {
|
||||
values.Add("content", params.Content)
|
||||
err = post(ctx, "files.upload", values, response, api.debug)
|
||||
err = postForm(ctx, api.httpclient, SLACK_API+"files.upload", values, response, api.debug)
|
||||
} else if params.File != "" {
|
||||
err = postLocalWithMultipartResponse(ctx, "files.upload", params.File, "file", values, response, api.debug)
|
||||
err = postLocalWithMultipartResponse(ctx, api.httpclient, "files.upload", params.File, "file", values, response, api.debug)
|
||||
} else if params.Reader != nil {
|
||||
err = postWithMultipartResponse(ctx, "files.upload", params.Filename, "file", values, params.Reader, response, api.debug)
|
||||
err = postWithMultipartResponse(ctx, api.httpclient, "files.upload", params.Filename, "file", values, params.Reader, response, api.debug)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -255,20 +257,40 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam
|
||||
return &response.File, nil
|
||||
}
|
||||
|
||||
// DeleteFileComment deletes a file's comment
|
||||
func (api *Client) DeleteFileComment(commentID, fileID string) error {
|
||||
return api.DeleteFileCommentContext(context.Background(), fileID, commentID)
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"file": {fileID},
|
||||
"id": {commentID},
|
||||
}
|
||||
_, err = fileRequest(ctx, api.httpclient, "files.comments.delete", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteFile deletes a file
|
||||
func (api *Client) DeleteFile(fileID string) error {
|
||||
return api.DeleteFileContext(context.Background(), fileID)
|
||||
}
|
||||
|
||||
// DeleteFileContext deletes a file with a custom context
|
||||
func (api *Client) DeleteFileContext(ctx context.Context, fileID string) error {
|
||||
func (api *Client) DeleteFileContext(ctx context.Context, fileID string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"file": {fileID},
|
||||
}
|
||||
_, err := fileRequest(ctx, "files.delete", values, api.debug)
|
||||
return err
|
||||
|
||||
_, err = fileRequest(ctx, api.httpclient, "files.delete", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
// RevokeFilePublicURL disables public/external sharing for a file
|
||||
@ -279,10 +301,11 @@ func (api *Client) RevokeFilePublicURL(fileID string) (*File, error) {
|
||||
// RevokeFilePublicURLContext disables public/external sharing for a file with a custom context
|
||||
func (api *Client) RevokeFilePublicURLContext(ctx context.Context, fileID string) (*File, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"file": {fileID},
|
||||
}
|
||||
response, err := fileRequest(ctx, "files.revokePublicURL", values, api.debug)
|
||||
|
||||
response, err := fileRequest(ctx, api.httpclient, "files.revokePublicURL", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -297,10 +320,11 @@ func (api *Client) ShareFilePublicURL(fileID string) (*File, []Comment, *Paging,
|
||||
// ShareFilePublicURLContext enabled public/external sharing for a file with a custom context
|
||||
func (api *Client) ShareFilePublicURLContext(ctx context.Context, fileID string) (*File, []Comment, *Paging, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"file": {fileID},
|
||||
}
|
||||
response, err := fileRequest(ctx, "files.sharedPublicURL", values, api.debug)
|
||||
|
||||
response, err := fileRequest(ctx, api.httpclient, "files.sharedPublicURL", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
108
vendor/github.com/nlopes/slack/groups.go
generated
vendored
108
vendor/github.com/nlopes/slack/groups.go
generated
vendored
@ -28,9 +28,9 @@ type groupResponseFull struct {
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func groupRequest(ctx context.Context, path string, values url.Values, debug bool) (*groupResponseFull, error) {
|
||||
func groupRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*groupResponseFull, error) {
|
||||
response := &groupResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postForm(ctx, client, SLACK_API+path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -45,17 +45,15 @@ func (api *Client) ArchiveGroup(group string) error {
|
||||
return api.ArchiveGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// ArchiveGroup archives a private group
|
||||
// ArchiveGroupContext archives a private group
|
||||
func (api *Client) ArchiveGroupContext(ctx context.Context, group string) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
_, err := groupRequest(ctx, "groups.archive", values, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
_, err := groupRequest(ctx, api.httpclient, "groups.archive", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
// UnarchiveGroup unarchives a private group
|
||||
@ -63,17 +61,15 @@ func (api *Client) UnarchiveGroup(group string) error {
|
||||
return api.UnarchiveGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// UnarchiveGroup unarchives a private group
|
||||
// UnarchiveGroupContext unarchives a private group
|
||||
func (api *Client) UnarchiveGroupContext(ctx context.Context, group string) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
_, err := groupRequest(ctx, "groups.unarchive", values, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
_, err := groupRequest(ctx, api.httpclient, "groups.unarchive", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateGroup creates a private group
|
||||
@ -81,13 +77,14 @@ func (api *Client) CreateGroup(group string) (*Group, error) {
|
||||
return api.CreateGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// CreateGroup creates a private group
|
||||
// CreateGroupContext creates a private group
|
||||
func (api *Client) CreateGroupContext(ctx context.Context, group string) (*Group, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"name": {group},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.create", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.create", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -104,14 +101,15 @@ func (api *Client) CreateChildGroup(group string) (*Group, error) {
|
||||
return api.CreateChildGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// CreateChildGroup creates a new private group archiving the old one with a custom context
|
||||
// CreateChildGroupContext creates a new private group archiving the old one with a custom context
|
||||
// For more information see CreateChildGroup
|
||||
func (api *Client) CreateChildGroupContext(ctx context.Context, group string) (*Group, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.createChild", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.createChild", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -126,10 +124,11 @@ func (api *Client) CloseGroup(group string) (bool, bool, error) {
|
||||
// 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.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
response, err := imRequest(ctx, "groups.close", values, api.debug)
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "groups.close", values, api.debug)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
@ -144,7 +143,7 @@ func (api *Client) GetGroupHistory(group string, params HistoryParameters) (*His
|
||||
// GetGroupHistoryContext fetches all the history for a private group with a custom context
|
||||
func (api *Client) GetGroupHistoryContext(ctx context.Context, group string, params HistoryParameters) (*History, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
if params.Latest != DEFAULT_HISTORY_LATEST {
|
||||
@ -170,7 +169,8 @@ func (api *Client) GetGroupHistoryContext(ctx context.Context, group string, par
|
||||
values.Add("unreads", "0")
|
||||
}
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.history", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.history", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -185,11 +185,12 @@ func (api *Client) InviteUserToGroup(group, user string) (*Group, bool, error) {
|
||||
// InviteUserToGroupContext invites a specific user to a private group with a custom context
|
||||
func (api *Client) InviteUserToGroupContext(ctx context.Context, group, user string) (*Group, bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"user": {user},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.invite", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.invite", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -202,12 +203,13 @@ func (api *Client) LeaveGroup(group string) error {
|
||||
}
|
||||
|
||||
// LeaveGroupContext makes authenticated user leave the group with a custom context
|
||||
func (api *Client) LeaveGroupContext(ctx context.Context, group string) error {
|
||||
func (api *Client) LeaveGroupContext(ctx context.Context, group string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
_, err := groupRequest(ctx, "groups.leave", values, api.debug)
|
||||
|
||||
_, err = groupRequest(ctx, api.httpclient, "groups.leave", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -217,13 +219,14 @@ func (api *Client) KickUserFromGroup(group, user string) error {
|
||||
}
|
||||
|
||||
// KickUserFromGroupContext kicks a user from a group with a custom context
|
||||
func (api *Client) KickUserFromGroupContext(ctx context.Context, group, user string) error {
|
||||
func (api *Client) KickUserFromGroupContext(ctx context.Context, group, user string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"user": {user},
|
||||
}
|
||||
_, err := groupRequest(ctx, "groups.kick", values, api.debug)
|
||||
|
||||
_, err = groupRequest(ctx, api.httpclient, "groups.kick", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -235,12 +238,13 @@ func (api *Client) GetGroups(excludeArchived bool) ([]Group, error) {
|
||||
// GetGroupsContext retrieves all groups with a custom context
|
||||
func (api *Client) GetGroupsContext(ctx context.Context, excludeArchived bool) ([]Group, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if excludeArchived {
|
||||
values.Add("exclude_archived", "1")
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.list", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.list", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -255,10 +259,11 @@ 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.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.info", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -276,13 +281,14 @@ func (api *Client) SetGroupReadMark(group, ts string) error {
|
||||
|
||||
// SetGroupReadMarkContext sets the read mark on a private group with a custom context
|
||||
// For more details see SetGroupReadMark
|
||||
func (api *Client) SetGroupReadMarkContext(ctx context.Context, group, ts string) error {
|
||||
func (api *Client) SetGroupReadMarkContext(ctx context.Context, group, ts string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"ts": {ts},
|
||||
}
|
||||
_, err := groupRequest(ctx, "groups.mark", values, api.debug)
|
||||
|
||||
_, err = groupRequest(ctx, api.httpclient, "groups.mark", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -294,10 +300,11 @@ func (api *Client) OpenGroup(group string) (bool, bool, error) {
|
||||
// OpenGroupContext opens a private group with a custom context
|
||||
func (api *Client) OpenGroupContext(ctx context.Context, group string) (bool, bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.open", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.open", values, api.debug)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
@ -314,13 +321,14 @@ func (api *Client) RenameGroup(group, name string) (*Channel, error) {
|
||||
// RenameGroupContext renames a group with a custom context
|
||||
func (api *Client) RenameGroupContext(ctx context.Context, group, name string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"name": {name},
|
||||
}
|
||||
|
||||
// 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, "groups.rename", values, api.debug)
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.rename", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -335,11 +343,12 @@ func (api *Client) SetGroupPurpose(group, purpose string) (string, error) {
|
||||
// SetGroupPurposeContext sets the group purpose with a custom context
|
||||
func (api *Client) SetGroupPurposeContext(ctx context.Context, group, purpose string) (string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"purpose": {purpose},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.setPurpose", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.setPurpose", values, api.debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -354,11 +363,12 @@ func (api *Client) SetGroupTopic(group, topic string) (string, error) {
|
||||
// SetGroupTopicContext sets the group topic with a custom context
|
||||
func (api *Client) SetGroupTopicContext(ctx context.Context, group, topic string) (string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"topic": {topic},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.setTopic", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.setTopic", values, api.debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
36
vendor/github.com/nlopes/slack/im.go
generated
vendored
36
vendor/github.com/nlopes/slack/im.go
generated
vendored
@ -29,9 +29,9 @@ type IM struct {
|
||||
IsUserDeleted bool `json:"is_user_deleted"`
|
||||
}
|
||||
|
||||
func imRequest(ctx context.Context, path string, values url.Values, debug bool) (*imResponseFull, error) {
|
||||
func imRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*imResponseFull, error) {
|
||||
response := &imResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -49,10 +49,11 @@ func (api *Client) CloseIMChannel(channel string) (bool, bool, error) {
|
||||
// CloseIMChannelContext closes the direct message channel with a custom context
|
||||
func (api *Client) CloseIMChannelContext(ctx context.Context, channel string) (bool, bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channel},
|
||||
}
|
||||
response, err := imRequest(ctx, "im.close", values, api.debug)
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "im.close", values, api.debug)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
@ -69,10 +70,11 @@ func (api *Client) OpenIMChannel(user string) (bool, bool, string, error) {
|
||||
// Returns some status and the channel ID
|
||||
func (api *Client) OpenIMChannelContext(ctx context.Context, user string) (bool, bool, string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
}
|
||||
response, err := imRequest(ctx, "im.open", values, api.debug)
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "im.open", values, api.debug)
|
||||
if err != nil {
|
||||
return false, false, "", err
|
||||
}
|
||||
@ -85,17 +87,15 @@ func (api *Client) MarkIMChannel(channel, ts string) (err error) {
|
||||
}
|
||||
|
||||
// MarkIMChannelContext sets the read mark of a direct message channel to a specific point with a custom context
|
||||
func (api *Client) MarkIMChannelContext(ctx context.Context, channel, ts string) (err error) {
|
||||
func (api *Client) MarkIMChannelContext(ctx context.Context, channel, ts string) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channel},
|
||||
"ts": {ts},
|
||||
}
|
||||
_, err = imRequest(ctx, "im.mark", values, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
|
||||
_, err := imRequest(ctx, api.httpclient, "im.mark", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetIMHistory retrieves the direct message channel history
|
||||
@ -106,7 +106,7 @@ func (api *Client) GetIMHistory(channel string, params HistoryParameters) (*Hist
|
||||
// GetIMHistoryContext retrieves the direct message channel history with a custom context
|
||||
func (api *Client) GetIMHistoryContext(ctx context.Context, channel string, params HistoryParameters) (*History, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channel},
|
||||
}
|
||||
if params.Latest != DEFAULT_HISTORY_LATEST {
|
||||
@ -132,7 +132,8 @@ func (api *Client) GetIMHistoryContext(ctx context.Context, channel string, para
|
||||
values.Add("unreads", "0")
|
||||
}
|
||||
}
|
||||
response, err := imRequest(ctx, "im.history", values, api.debug)
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "im.history", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -147,9 +148,10 @@ func (api *Client) GetIMChannels() ([]IM, error) {
|
||||
// GetIMChannelsContext returns the list of direct message channels with a custom context
|
||||
func (api *Client) GetIMChannelsContext(ctx context.Context) ([]IM, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
response, err := imRequest(ctx, "im.list", values, api.debug)
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "im.list", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
17
vendor/github.com/nlopes/slack/info.go
generated
vendored
17
vendor/github.com/nlopes/slack/info.go
generated
vendored
@ -1,7 +1,9 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -127,6 +129,19 @@ func (t JSONTime) Time() time.Time {
|
||||
return time.Unix(int64(t), 0)
|
||||
}
|
||||
|
||||
// UnmarshalJSON will unmarshal both string and int JSON values
|
||||
func (t *JSONTime) UnmarshalJSON(buf []byte) error {
|
||||
s := bytes.Trim(buf, `"`)
|
||||
|
||||
v, err := strconv.Atoi(string(s))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*t = JSONTime(int64(v))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Team contains details about a team
|
||||
type Team struct {
|
||||
ID string `json:"id"`
|
||||
@ -156,7 +171,7 @@ type Info struct {
|
||||
|
||||
type infoResponseFull struct {
|
||||
Info
|
||||
WebResponse
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
// GetBotByID returns a bot given a bot id
|
||||
|
53
vendor/github.com/nlopes/slack/logger.go
generated
vendored
Normal file
53
vendor/github.com/nlopes/slack/logger.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
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 {
|
||||
Output(int, string) error
|
||||
}
|
||||
|
||||
// logInternal represents the internal logging api we use.
|
||||
type logInternal interface {
|
||||
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
|
||||
}
|
||||
|
||||
// Println replicates the behaviour of the standard logger.
|
||||
func (t ilogger) 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{}) {
|
||||
t.Output(2, fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// Print replicates the behaviour of the standard logger.
|
||||
func (t ilogger) Print(v ...interface{}) {
|
||||
t.Output(2, fmt.Sprint(v...))
|
||||
}
|
51
vendor/github.com/nlopes/slack/messages.go
generated
vendored
51
vendor/github.com/nlopes/slack/messages.go
generated
vendored
@ -2,12 +2,13 @@ package slack
|
||||
|
||||
// OutgoingMessage is used for the realtime API, and seems incomplete.
|
||||
type OutgoingMessage struct {
|
||||
ID int `json:"id"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// Message is an auxiliary type to allow us to have a message containing sub messages
|
||||
@ -26,9 +27,12 @@ type Msg struct {
|
||||
Timestamp string `json:"ts,omitempty"`
|
||||
ThreadTimestamp string `json:"thread_ts,omitempty"`
|
||||
IsStarred bool `json:"is_starred,omitempty"`
|
||||
PinnedTo []string `json:"pinned_to, omitempty"`
|
||||
PinnedTo []string `json:"pinned_to,omitempty"`
|
||||
Attachments []Attachment `json:"attachments,omitempty"`
|
||||
Edited *Edited `json:"edited,omitempty"`
|
||||
LastRead string `json:"last_read,omitempty"`
|
||||
Subscribed bool `json:"subscribed,omitempty"`
|
||||
UnreadCount int `json:"unread_count,omitempty"`
|
||||
|
||||
// Message Subtypes
|
||||
SubType string `json:"subtype,omitempty"`
|
||||
@ -65,7 +69,7 @@ type Msg struct {
|
||||
ParentUserId string `json:"parent_user_id,omitempty"`
|
||||
|
||||
// file_share, file_comment, file_mention
|
||||
File *File `json:"file,omitempty"`
|
||||
Files []File `json:"files,omitempty"`
|
||||
|
||||
// file_share
|
||||
Upload bool `json:"upload,omitempty"`
|
||||
@ -82,6 +86,11 @@ type Msg struct {
|
||||
|
||||
// reactions
|
||||
Reactions []ItemReaction `json:"reactions,omitempty"`
|
||||
|
||||
// slash commands and interactive messages
|
||||
ResponseType string `json:"response_type,omitempty"`
|
||||
ReplaceOriginal bool `json:"replace_original"`
|
||||
DeleteOriginal bool `json:"delete_original"`
|
||||
}
|
||||
|
||||
// Icon is used for bot messages
|
||||
@ -109,27 +118,33 @@ type Event struct {
|
||||
|
||||
// Ping contains information about a Ping Event
|
||||
type Ping struct {
|
||||
ID int `json:"id"`
|
||||
Type string `json:"type"`
|
||||
ID int `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
// Pong contains information about a Pong Event
|
||||
type Pong struct {
|
||||
Type string `json:"type"`
|
||||
ReplyTo int `json:"reply_to"`
|
||||
Type string `json:"type"`
|
||||
ReplyTo int `json:"reply_to"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
// NewOutgoingMessage prepares an OutgoingMessage that the user can
|
||||
// use to send a message. Use this function to properly set the
|
||||
// messageID.
|
||||
func (rtm *RTM) NewOutgoingMessage(text string, channelID string) *OutgoingMessage {
|
||||
func (rtm *RTM) NewOutgoingMessage(text string, channelID string, options ...RTMsgOption) *OutgoingMessage {
|
||||
id := rtm.idGen.Next()
|
||||
return &OutgoingMessage{
|
||||
msg := OutgoingMessage{
|
||||
ID: id,
|
||||
Type: "message",
|
||||
Channel: channelID,
|
||||
Text: text,
|
||||
}
|
||||
for _, option := range options {
|
||||
option(&msg)
|
||||
}
|
||||
return &msg
|
||||
}
|
||||
|
||||
// NewTypingMessage prepares an OutgoingMessage that the user can
|
||||
@ -143,3 +158,21 @@ func (rtm *RTM) NewTypingMessage(channelID string) *OutgoingMessage {
|
||||
Channel: channelID,
|
||||
}
|
||||
}
|
||||
|
||||
// RTMsgOption allows configuration of various options available for sending an RTM message
|
||||
type RTMsgOption func(*OutgoingMessage)
|
||||
|
||||
// RTMsgOptionTS sets thead timestamp of an outgoing message in order to respond to a thread
|
||||
func RTMsgOptionTS(threadTimestamp string) RTMsgOption {
|
||||
return func(msg *OutgoingMessage) {
|
||||
msg.ThreadTimestamp = threadTimestamp
|
||||
}
|
||||
}
|
||||
|
||||
// RTMsgOptionBroadcast sets broadcast reply to channel to "true"
|
||||
func RTMsgOptionBroadcast() RTMsgOption {
|
||||
return func(msg *OutgoingMessage) {
|
||||
msg.ThreadBroadcast = true
|
||||
}
|
||||
|
||||
}
|
||||
|
165
vendor/github.com/nlopes/slack/misc.go
generated
vendored
165
vendor/github.com/nlopes/slack/misc.go
generated
vendored
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@ -18,29 +19,41 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
type SlackResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
var customHTTPClient HTTPRequester
|
||||
func (t SlackResponse) Err() error {
|
||||
if t.Ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HTTPClient sets a custom http.Client
|
||||
// deprecated: in favor of SetHTTPClient()
|
||||
var HTTPClient = &http.Client{}
|
||||
// handle pure text based responses like chat.post
|
||||
// which while they have a slack response in their data structure
|
||||
// it doesn't actually get set during parsing.
|
||||
if strings.TrimSpace(t.Error) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Error *WebError `json:"error"`
|
||||
return errors.New(t.Error)
|
||||
}
|
||||
|
||||
type WebError string
|
||||
// StatusCodeError represents an http response error.
|
||||
// type httpStatusCode interface { HTTPStatusCode() int } to handle it.
|
||||
type statusCodeError struct {
|
||||
Code int
|
||||
Status string
|
||||
}
|
||||
|
||||
func (s WebError) Error() string {
|
||||
return string(s)
|
||||
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)
|
||||
}
|
||||
|
||||
func (t statusCodeError) HTTPStatusCode() int {
|
||||
return t.Code
|
||||
}
|
||||
|
||||
type RateLimitedError struct {
|
||||
@ -77,7 +90,7 @@ func fileUploadReq(ctx context.Context, path, fieldname, filename string, values
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func parseResponseBody(body io.ReadCloser, intf *interface{}, debug bool) error {
|
||||
func parseResponseBody(body io.ReadCloser, intf interface{}, debug bool) error {
|
||||
response, err := ioutil.ReadAll(body)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -88,10 +101,10 @@ func parseResponseBody(body io.ReadCloser, intf *interface{}, debug bool) error
|
||||
logger.Printf("parseResponseBody: %s\n", string(response))
|
||||
}
|
||||
|
||||
return json.Unmarshal(response, &intf)
|
||||
return json.Unmarshal(response, intf)
|
||||
}
|
||||
|
||||
func postLocalWithMultipartResponse(ctx context.Context, path, fpath, fieldname string, values url.Values, intf interface{}, debug bool) error {
|
||||
func postLocalWithMultipartResponse(ctx context.Context, client HTTPRequester, path, fpath, fieldname string, values url.Values, intf interface{}, debug bool) error {
|
||||
fullpath, err := filepath.Abs(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -101,16 +114,16 @@ func postLocalWithMultipartResponse(ctx context.Context, path, fpath, fieldname
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return postWithMultipartResponse(ctx, path, filepath.Base(fpath), fieldname, values, file, intf, debug)
|
||||
return postWithMultipartResponse(ctx, client, path, filepath.Base(fpath), fieldname, values, file, intf, debug)
|
||||
}
|
||||
|
||||
func postWithMultipartResponse(ctx context.Context, path, name, fieldname string, values url.Values, r io.Reader, intf interface{}, debug bool) error {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
resp, err := getHTTPClient().Do(req)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -127,51 +140,68 @@ func postWithMultipartResponse(ctx context.Context, path, name, fieldname string
|
||||
// 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 fmt.Errorf("Slack server error: %s.", resp.Status)
|
||||
return statusCodeError{Code: resp.StatusCode, Status: resp.Status}
|
||||
}
|
||||
|
||||
return parseResponseBody(resp.Body, &intf, debug)
|
||||
return parseResponseBody(resp.Body, intf, debug)
|
||||
}
|
||||
|
||||
func postForm(ctx context.Context, endpoint string, values url.Values, intf interface{}, debug bool) error {
|
||||
func doPost(ctx context.Context, client HTTPRequester, req *http.Request, intf interface{}, debug bool) error {
|
||||
req = req.WithContext(ctx)
|
||||
resp, err := client.Do(req)
|
||||
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)
|
||||
}
|
||||
|
||||
// post JSON.
|
||||
func postJSON(ctx context.Context, client HTTPRequester, endpoint, token string, json []byte, intf interface{}, debug bool) error {
|
||||
reqBody := bytes.NewBuffer(json)
|
||||
req, err := http.NewRequest("POST", endpoint, reqBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
return doPost(ctx, client, req, intf, debug)
|
||||
}
|
||||
|
||||
// post a url encoded form.
|
||||
func postForm(ctx context.Context, client HTTPRequester, endpoint string, values url.Values, intf interface{}, debug bool) 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")
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
resp, err := getHTTPClient().Do(req)
|
||||
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 fmt.Errorf("Slack server error: %s.", resp.Status)
|
||||
}
|
||||
|
||||
return parseResponseBody(resp.Body, &intf, debug)
|
||||
return doPost(ctx, client, req, intf, debug)
|
||||
}
|
||||
|
||||
func post(ctx context.Context, path string, values url.Values, intf interface{}, debug bool) error {
|
||||
return postForm(ctx, SLACK_API+path, values, intf, debug)
|
||||
// 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 parseAdminResponse(ctx context.Context, method string, teamName string, values url.Values, intf interface{}, debug bool) error {
|
||||
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, endpoint, values, intf, debug)
|
||||
return postForm(ctx, client, endpoint, values, intf, debug)
|
||||
}
|
||||
|
||||
func logResponse(resp *http.Response, debug bool) error {
|
||||
@ -187,17 +217,24 @@ func logResponse(resp *http.Response, debug bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHTTPClient() HTTPRequester {
|
||||
if customHTTPClient != nil {
|
||||
return customHTTPClient
|
||||
func okJSONHandler(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
response, _ := json.Marshal(SlackResponse{
|
||||
Ok: true,
|
||||
})
|
||||
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() {
|
||||
<-t.C
|
||||
}
|
||||
|
||||
return HTTPClient
|
||||
}
|
||||
|
||||
// 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
|
||||
t.Reset(d)
|
||||
}
|
||||
|
2
vendor/github.com/nlopes/slack/oauth.go
generated
vendored
2
vendor/github.com/nlopes/slack/oauth.go
generated
vendored
@ -55,7 +55,7 @@ func GetOAuthResponseContext(ctx context.Context, clientID, clientSecret, code,
|
||||
"redirect_uri": {redirectURI},
|
||||
}
|
||||
response := &OAuthResponse{}
|
||||
err = post(ctx, "oauth.access", values, response, debug)
|
||||
err = postSlackMethod(ctx, customHTTPClient, "oauth.access", values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
39
vendor/github.com/nlopes/slack/pins.go
generated
vendored
39
vendor/github.com/nlopes/slack/pins.go
generated
vendored
@ -21,25 +21,24 @@ func (api *Client) AddPin(channel string, item ItemRef) error {
|
||||
func (api *Client) AddPinContext(ctx context.Context, channel string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", string(item.Timestamp))
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", string(item.File))
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "pins.add", values, response, api.debug); err != nil {
|
||||
if err := postSlackMethod(ctx, api.httpclient, "pins.add", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
return nil
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// RemovePin un-pins an item from a channel
|
||||
@ -51,25 +50,24 @@ func (api *Client) RemovePin(channel string, item ItemRef) error {
|
||||
func (api *Client) RemovePinContext(ctx context.Context, channel string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", string(item.Timestamp))
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", string(item.File))
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "pins.remove", values, response, api.debug); err != nil {
|
||||
if err := postSlackMethod(ctx, api.httpclient, "pins.remove", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
return nil
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// ListPins returns information about the items a user reacted to.
|
||||
@ -81,10 +79,11 @@ func (api *Client) ListPins(channel string) ([]Item, *Paging, error) {
|
||||
func (api *Client) ListPinsContext(ctx context.Context, channel string) ([]Item, *Paging, error) {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response := &listPinsResponseFull{}
|
||||
err := post(ctx, "pins.list", values, response, api.debug)
|
||||
err := postSlackMethod(ctx, api.httpclient, "pins.list", values, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
56
vendor/github.com/nlopes/slack/reactions.go
generated
vendored
56
vendor/github.com/nlopes/slack/reactions.go
generated
vendored
@ -136,31 +136,30 @@ func (api *Client) AddReaction(name string, item ItemRef) error {
|
||||
// AddReactionContext adds a reaction emoji to a message, file or file comment with a custom context.
|
||||
func (api *Client) AddReactionContext(ctx context.Context, name string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if name != "" {
|
||||
values.Set("name", name)
|
||||
}
|
||||
if item.Channel != "" {
|
||||
values.Set("channel", string(item.Channel))
|
||||
values.Set("channel", item.Channel)
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", string(item.Timestamp))
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", string(item.File))
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "reactions.add", values, response, api.debug); err != nil {
|
||||
if err := postSlackMethod(ctx, api.httpclient, "reactions.add", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
return nil
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// RemoveReaction removes a reaction emoji from a message, file or file comment.
|
||||
@ -171,31 +170,30 @@ func (api *Client) RemoveReaction(name string, item ItemRef) error {
|
||||
// RemoveReactionContext removes a reaction emoji from a message, file or file comment with a custom context.
|
||||
func (api *Client) RemoveReactionContext(ctx context.Context, name string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if name != "" {
|
||||
values.Set("name", name)
|
||||
}
|
||||
if item.Channel != "" {
|
||||
values.Set("channel", string(item.Channel))
|
||||
values.Set("channel", item.Channel)
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", string(item.Timestamp))
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", string(item.File))
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "reactions.remove", values, response, api.debug); err != nil {
|
||||
if err := postSlackMethod(ctx, api.httpclient, "reactions.remove", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
return nil
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// GetReactions returns details about the reactions on an item.
|
||||
@ -206,25 +204,26 @@ func (api *Client) GetReactions(item ItemRef, params GetReactionsParameters) ([]
|
||||
// GetReactionsContext returns details about the reactions on an item with a custom context
|
||||
func (api *Client) GetReactionsContext(ctx context.Context, item ItemRef, params GetReactionsParameters) ([]ItemReaction, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Channel != "" {
|
||||
values.Set("channel", string(item.Channel))
|
||||
values.Set("channel", item.Channel)
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", string(item.Timestamp))
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", string(item.File))
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
if params.Full != DEFAULT_REACTIONS_FULL {
|
||||
values.Set("full", strconv.FormatBool(params.Full))
|
||||
}
|
||||
|
||||
response := &getReactionsResponseFull{}
|
||||
if err := post(ctx, "reactions.get", values, response, api.debug); err != nil {
|
||||
if err := postSlackMethod(ctx, api.httpclient, "reactions.get", values, response, api.debug); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
@ -241,7 +240,7 @@ func (api *Client) ListReactions(params ListReactionsParameters) ([]ReactedItem,
|
||||
// ListReactionsContext returns information about the items a user reacted to with a custom context.
|
||||
func (api *Client) ListReactionsContext(ctx context.Context, params ListReactionsParameters) ([]ReactedItem, *Paging, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.User != DEFAULT_REACTIONS_USER {
|
||||
values.Add("user", params.User)
|
||||
@ -255,8 +254,9 @@ func (api *Client) ListReactionsContext(ctx context.Context, params ListReaction
|
||||
if params.Full != DEFAULT_REACTIONS_FULL {
|
||||
values.Add("full", strconv.FormatBool(params.Full))
|
||||
}
|
||||
|
||||
response := &listReactionsResponseFull{}
|
||||
err := post(ctx, "reactions.list", values, response, api.debug)
|
||||
err := postSlackMethod(ctx, api.httpclient, "reactions.list", values, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
110
vendor/github.com/nlopes/slack/rtm.go
generated
vendored
110
vendor/github.com/nlopes/slack/rtm.go
generated
vendored
@ -3,16 +3,34 @@ package slack
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
websocketDefaultTimeout = 10 * time.Second
|
||||
defaultPingInterval = 30 * time.Second
|
||||
)
|
||||
|
||||
const (
|
||||
rtmEventTypeAck = ""
|
||||
rtmEventTypeHello = "hello"
|
||||
rtmEventTypeGoodbye = "goodbye"
|
||||
rtmEventTypePong = "pong"
|
||||
rtmEventTypeDesktopNotification = "desktop_notification"
|
||||
)
|
||||
|
||||
// StartRTM calls the "rtm.start" endpoint and returns the provided URL and the full Info block.
|
||||
//
|
||||
// To have a fully managed Websocket connection, use `NewRTM`, and call `ManageConnection()` on it.
|
||||
func (api *Client) StartRTM() (info *Info, websocketURL string, err error) {
|
||||
return api.StartRTMContext(context.Background())
|
||||
ctx, cancel := context.WithTimeout(context.Background(), websocketDefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
return api.StartRTMContext(ctx)
|
||||
}
|
||||
|
||||
// StartRTMContext calls the "rtm.start" endpoint and returns the provided URL and the full Info block with a custom context.
|
||||
@ -20,69 +38,101 @@ 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 = post(ctx, "rtm.start", url.Values{"token": {api.config.token}}, response, api.debug)
|
||||
err = postSlackMethod(ctx, api.httpclient, "rtm.start", url.Values{"token": {api.token}}, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("post: %s", err)
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, "", response.Error
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
api.Debugln("Using URL:", response.Info.URL)
|
||||
return &response.Info, response.Info.URL, nil
|
||||
return &response.Info, response.Info.URL, response.Err()
|
||||
}
|
||||
|
||||
// ConnectRTM calls the "rtm.connect" endpoint and returns the provided URL and the compact Info block.
|
||||
//
|
||||
// To have a fully managed Websocket connection, use `NewRTM`, and call `ManageConnection()` on it.
|
||||
func (api *Client) ConnectRTM() (info *Info, websocketURL string, err error) {
|
||||
return api.ConnectRTMContext(context.Background())
|
||||
ctx, cancel := context.WithTimeout(context.Background(), websocketDefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
return api.ConnectRTMContext(ctx)
|
||||
}
|
||||
|
||||
// ConnectRTM calls the "rtm.connect" endpoint and returns the provided URL and the compact Info block with a custom context.
|
||||
// ConnectRTMContext calls the "rtm.connect" endpoint and returns the
|
||||
// provided URL and the compact Info block with a custom context.
|
||||
//
|
||||
// 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 = post(ctx, "rtm.connect", url.Values{"token": {api.config.token}}, response, api.debug)
|
||||
err = postSlackMethod(ctx, api.httpclient, "rtm.connect", url.Values{"token": {api.token}}, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("post: %s", err)
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, "", response.Error
|
||||
api.Debugf("Failed to connect to RTM: %s", err)
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
api.Debugln("Using URL:", response.Info.URL)
|
||||
return &response.Info, response.Info.URL, nil
|
||||
return &response.Info, response.Info.URL, response.Err()
|
||||
}
|
||||
|
||||
// RTMOption options for the managed RTM.
|
||||
type RTMOption func(*RTM)
|
||||
|
||||
// RTMOptionUseStart 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
|
||||
func RTMOptionUseStart(b bool) RTMOption {
|
||||
return func(rtm *RTM) {
|
||||
rtm.useRTMStart = b
|
||||
}
|
||||
}
|
||||
|
||||
// RTMOptionDialer takes a gorilla websocket Dialer and uses it as the
|
||||
// Dialer when opening the websocket for the RTM connection.
|
||||
func RTMOptionDialer(d *websocket.Dialer) RTMOption {
|
||||
return func(rtm *RTM) {
|
||||
rtm.dialer = d
|
||||
}
|
||||
}
|
||||
|
||||
// RTMOptionPingInterval determines how often to deliver a ping message to slack.
|
||||
func RTMOptionPingInterval(d time.Duration) RTMOption {
|
||||
return func(rtm *RTM) {
|
||||
rtm.pingInterval = d
|
||||
rtm.resetDeadman()
|
||||
}
|
||||
}
|
||||
|
||||
// NewRTM returns a RTM, which provides a fully managed connection to
|
||||
// Slack's websocket-based Real-Time Messaging protocol.
|
||||
func (api *Client) NewRTM() *RTM {
|
||||
return api.NewRTMWithOptions(nil)
|
||||
}
|
||||
|
||||
// NewRTMWithOptions 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 {
|
||||
func (api *Client) NewRTM(options ...RTMOption) *RTM {
|
||||
result := &RTM{
|
||||
Client: *api,
|
||||
IncomingEvents: make(chan RTMEvent, 50),
|
||||
outgoingMessages: make(chan OutgoingMessage, 20),
|
||||
pings: make(map[int]time.Time),
|
||||
pingInterval: defaultPingInterval,
|
||||
pingDeadman: time.NewTimer(deadmanDuration(defaultPingInterval)),
|
||||
isConnected: false,
|
||||
wasIntentional: true,
|
||||
killChannel: make(chan bool),
|
||||
disconnected: make(chan struct{}),
|
||||
disconnected: make(chan struct{}, 1),
|
||||
forcePing: make(chan bool),
|
||||
rawEvents: make(chan json.RawMessage),
|
||||
idGen: NewSafeID(1),
|
||||
mu: &sync.Mutex{},
|
||||
}
|
||||
|
||||
if options != nil {
|
||||
result.useRTMStart = options.UseRTMStart
|
||||
} else {
|
||||
result.useRTMStart = true
|
||||
for _, opt := range options {
|
||||
opt(result)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
30
vendor/github.com/nlopes/slack/search.go
generated
vendored
30
vendor/github.com/nlopes/slack/search.go
generated
vendored
@ -11,7 +11,7 @@ const (
|
||||
DEFAULT_SEARCH_SORT = "score"
|
||||
DEFAULT_SEARCH_SORT_DIR = "desc"
|
||||
DEFAULT_SEARCH_HIGHLIGHT = false
|
||||
DEFAULT_SEARCH_COUNT = 100
|
||||
DEFAULT_SEARCH_COUNT = 20
|
||||
DEFAULT_SEARCH_PAGE = 1
|
||||
)
|
||||
|
||||
@ -37,17 +37,18 @@ type CtxMessage struct {
|
||||
}
|
||||
|
||||
type SearchMessage struct {
|
||||
Type string `json:"type"`
|
||||
Channel CtxChannel `json:"channel"`
|
||||
User string `json:"user"`
|
||||
Username string `json:"username"`
|
||||
Timestamp string `json:"ts"`
|
||||
Text string `json:"text"`
|
||||
Permalink string `json:"permalink"`
|
||||
Previous CtxMessage `json:"previous"`
|
||||
Previous2 CtxMessage `json:"previous_2"`
|
||||
Next CtxMessage `json:"next"`
|
||||
Next2 CtxMessage `json:"next_2"`
|
||||
Type string `json:"type"`
|
||||
Channel CtxChannel `json:"channel"`
|
||||
User string `json:"user"`
|
||||
Username string `json:"username"`
|
||||
Timestamp string `json:"ts"`
|
||||
Text string `json:"text"`
|
||||
Permalink string `json:"permalink"`
|
||||
Attachments []Attachment `json:"attachments"`
|
||||
Previous CtxMessage `json:"previous"`
|
||||
Previous2 CtxMessage `json:"previous_2"`
|
||||
Next CtxMessage `json:"next"`
|
||||
Next2 CtxMessage `json:"next_2"`
|
||||
}
|
||||
|
||||
type SearchMessages struct {
|
||||
@ -83,7 +84,7 @@ func NewSearchParameters() SearchParameters {
|
||||
|
||||
func (api *Client) _search(ctx context.Context, path, query string, params SearchParameters, files, messages bool) (response *searchResponseFull, error error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"query": {query},
|
||||
}
|
||||
if params.Sort != DEFAULT_SEARCH_SORT {
|
||||
@ -101,8 +102,9 @@ func (api *Client) _search(ctx context.Context, path, query string, params Searc
|
||||
if params.Page != DEFAULT_SEARCH_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
|
||||
response = &searchResponseFull{}
|
||||
err := post(ctx, path, values, response, api.debug)
|
||||
err := postSlackMethod(ctx, api.httpclient, path, values, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
104
vendor/github.com/nlopes/slack/slack.go
generated
vendored
104
vendor/github.com/nlopes/slack/slack.go
generated
vendored
@ -5,20 +5,47 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
var logger stdLogger // A logger that can be set by consumers
|
||||
/*
|
||||
Added as a var so that we can change this for testing purposes
|
||||
*/
|
||||
// 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"
|
||||
|
||||
type SlackResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Error string `json:"error"`
|
||||
// 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 {
|
||||
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"`
|
||||
}
|
||||
|
||||
func (t *ResponseMetadata) initialize() *ResponseMetadata {
|
||||
if t != nil {
|
||||
return t
|
||||
}
|
||||
|
||||
return &ResponseMetadata{}
|
||||
}
|
||||
|
||||
type AuthTestResponse struct {
|
||||
@ -35,41 +62,33 @@ type authTestResponseFull struct {
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
config struct {
|
||||
token string
|
||||
token string
|
||||
info Info
|
||||
debug bool
|
||||
httpclient HTTPRequester
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
info Info
|
||||
debug bool
|
||||
}
|
||||
|
||||
// stdLogger is a logger interface compatible with both stdlib and some
|
||||
// 3rd party loggers such as logrus.
|
||||
type stdLogger interface {
|
||||
Print(...interface{})
|
||||
Printf(string, ...interface{})
|
||||
Println(...interface{})
|
||||
// New builds a slack client from the provided token and options.
|
||||
func New(token string, options ...Option) *Client {
|
||||
s := &Client{
|
||||
token: token,
|
||||
httpclient: customHTTPClient,
|
||||
}
|
||||
|
||||
Fatal(...interface{})
|
||||
Fatalf(string, ...interface{})
|
||||
Fatalln(...interface{})
|
||||
for _, opt := range options {
|
||||
opt(s)
|
||||
}
|
||||
|
||||
Panic(...interface{})
|
||||
Panicf(string, ...interface{})
|
||||
Panicln(...interface{})
|
||||
|
||||
Output(int, string) error
|
||||
}
|
||||
|
||||
// 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 stdLogger) {
|
||||
logger = l
|
||||
}
|
||||
|
||||
// New creates new Client.
|
||||
func New(token string) *Client {
|
||||
s := &Client{}
|
||||
s.config.token = token
|
||||
return s
|
||||
}
|
||||
|
||||
@ -80,14 +99,19 @@ 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) {
|
||||
api.Debugf("Challenging auth...")
|
||||
responseFull := &authTestResponseFull{}
|
||||
err := post(ctx, "auth.test", url.Values{"token": {api.config.token}}, responseFull, api.debug)
|
||||
err := postSlackMethod(ctx, api.httpclient, "auth.test", url.Values{"token": {api.token}}, responseFull, api.debug)
|
||||
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
|
||||
}
|
||||
|
||||
@ -97,16 +121,18 @@ func (api *Client) AuthTestContext(ctx context.Context) (response *AuthTestRespo
|
||||
func (api *Client) SetDebug(debug bool) {
|
||||
api.debug = debug
|
||||
if debug && logger == nil {
|
||||
logger = log.New(os.Stdout, "nlopes/slack", log.LstdFlags|log.Lshortfile)
|
||||
SetLogger(log.New(os.Stdout, "nlopes/slack", log.LstdFlags|log.Lshortfile))
|
||||
}
|
||||
}
|
||||
|
||||
// Debugf print a formatted debug line.
|
||||
func (api *Client) Debugf(format string, v ...interface{}) {
|
||||
if api.debug {
|
||||
logger.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...))
|
||||
|
53
vendor/github.com/nlopes/slack/slash.go
generated
vendored
Normal file
53
vendor/github.com/nlopes/slack/slash.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// SlashCommand contains information about a request of the slash command
|
||||
type SlashCommand struct {
|
||||
Token string `json:"token"`
|
||||
TeamID string `json:"team_id"`
|
||||
TeamDomain string `json:"team_domain"`
|
||||
EnterpriseID string `json:"enterprise_id,omitempty"`
|
||||
EnterpriseName string `json:"enterprise_name,omitempty"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
ChannelName string `json:"channel_name"`
|
||||
UserID string `json:"user_id"`
|
||||
UserName string `json:"user_name"`
|
||||
Command string `json:"command"`
|
||||
Text string `json:"text"`
|
||||
ResponseURL string `json:"response_url"`
|
||||
TriggerID string `json:"trigger_id"`
|
||||
}
|
||||
|
||||
// SlashCommandParse will parse the request of the slash command
|
||||
func SlashCommandParse(r *http.Request) (s SlashCommand, err error) {
|
||||
if err = r.ParseForm(); err != nil {
|
||||
return s, err
|
||||
}
|
||||
s.Token = r.PostForm.Get("token")
|
||||
s.TeamID = r.PostForm.Get("team_id")
|
||||
s.TeamDomain = r.PostForm.Get("team_domain")
|
||||
s.EnterpriseID = r.PostForm.Get("enterprise_id")
|
||||
s.EnterpriseName = r.PostForm.Get("enterprise_name")
|
||||
s.ChannelID = r.PostForm.Get("channel_id")
|
||||
s.ChannelName = r.PostForm.Get("channel_name")
|
||||
s.UserID = r.PostForm.Get("user_id")
|
||||
s.UserName = r.PostForm.Get("user_name")
|
||||
s.Command = r.PostForm.Get("command")
|
||||
s.Text = r.PostForm.Get("text")
|
||||
s.ResponseURL = r.PostForm.Get("response_url")
|
||||
s.TriggerID = r.PostForm.Get("trigger_id")
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// ValidateToken validates verificationTokens
|
||||
func (s SlashCommand) ValidateToken(verificationTokens ...string) bool {
|
||||
for _, token := range verificationTokens {
|
||||
if s.Token == token {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
39
vendor/github.com/nlopes/slack/stars.go
generated
vendored
39
vendor/github.com/nlopes/slack/stars.go
generated
vendored
@ -45,25 +45,24 @@ func (api *Client) AddStar(channel string, item ItemRef) error {
|
||||
func (api *Client) AddStarContext(ctx context.Context, channel string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", string(item.Timestamp))
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", string(item.File))
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "stars.add", values, response, api.debug); err != nil {
|
||||
if err := postSlackMethod(ctx, api.httpclient, "stars.add", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
return nil
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// RemoveStar removes a starred item from a channel
|
||||
@ -75,25 +74,24 @@ func (api *Client) RemoveStar(channel string, item ItemRef) error {
|
||||
func (api *Client) RemoveStarContext(ctx context.Context, channel string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", string(item.Timestamp))
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", string(item.File))
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "stars.remove", values, response, api.debug); err != nil {
|
||||
if err := postSlackMethod(ctx, api.httpclient, "stars.remove", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
return nil
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// ListStars returns information about the stars a user added
|
||||
@ -104,7 +102,7 @@ func (api *Client) ListStars(params StarsParameters) ([]Item, *Paging, error) {
|
||||
// ListStarsContext returns information about the stars a user added with a custom context
|
||||
func (api *Client) ListStarsContext(ctx context.Context, params StarsParameters) ([]Item, *Paging, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.User != DEFAULT_STARS_USER {
|
||||
values.Add("user", params.User)
|
||||
@ -115,8 +113,9 @@ func (api *Client) ListStarsContext(ctx context.Context, params StarsParameters)
|
||||
if params.Page != DEFAULT_STARS_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
|
||||
response := &listResponseFull{}
|
||||
err := post(ctx, "stars.list", values, response, api.debug)
|
||||
err := postSlackMethod(ctx, api.httpclient, "stars.list", values, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
29
vendor/github.com/nlopes/slack/team.go
generated
vendored
29
vendor/github.com/nlopes/slack/team.go
generated
vendored
@ -67,9 +67,9 @@ func NewAccessLogParameters() AccessLogParameters {
|
||||
}
|
||||
}
|
||||
|
||||
func teamRequest(ctx context.Context, path string, values url.Values, debug bool) (*TeamResponse, error) {
|
||||
func teamRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*TeamResponse, error) {
|
||||
response := &TeamResponse{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -81,9 +81,9 @@ func teamRequest(ctx context.Context, path string, values url.Values, debug bool
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func billableInfoRequest(ctx context.Context, path string, values url.Values, debug bool) (map[string]BillingActive, error) {
|
||||
func billableInfoRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (map[string]BillingActive, error) {
|
||||
response := &BillableInfoResponse{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -95,9 +95,9 @@ func billableInfoRequest(ctx context.Context, path string, values url.Values, de
|
||||
return response.BillableInfo, nil
|
||||
}
|
||||
|
||||
func accessLogsRequest(ctx context.Context, path string, values url.Values, debug bool) (*LoginResponse, error) {
|
||||
func accessLogsRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*LoginResponse, error) {
|
||||
response := &LoginResponse{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -115,10 +115,10 @@ func (api *Client) GetTeamInfo() (*TeamInfo, error) {
|
||||
// GetTeamInfoContext gets the Team Information of the user with a custom context
|
||||
func (api *Client) GetTeamInfoContext(ctx context.Context) (*TeamInfo, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response, err := teamRequest(ctx, "team.info", values, api.debug)
|
||||
response, err := teamRequest(ctx, api.httpclient, "team.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -133,7 +133,7 @@ func (api *Client) GetAccessLogs(params AccessLogParameters) ([]Login, *Paging,
|
||||
// GetAccessLogsContext retrieves a page of logins according to the parameters given with a custom context
|
||||
func (api *Client) GetAccessLogsContext(ctx context.Context, params AccessLogParameters) ([]Login, *Paging, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.Count != DEFAULT_LOGINS_COUNT {
|
||||
values.Add("count", strconv.Itoa(params.Count))
|
||||
@ -141,7 +141,8 @@ func (api *Client) GetAccessLogsContext(ctx context.Context, params AccessLogPar
|
||||
if params.Page != DEFAULT_LOGINS_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
response, err := accessLogsRequest(ctx, "team.accessLogs", values, api.debug)
|
||||
|
||||
response, err := accessLogsRequest(ctx, api.httpclient, "team.accessLogs", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -154,11 +155,11 @@ func (api *Client) GetBillableInfo(user string) (map[string]BillingActive, error
|
||||
|
||||
func (api *Client) GetBillableInfoContext(ctx context.Context, user string) (map[string]BillingActive, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
return billableInfoRequest(ctx, "team.billableInfo", values, api.debug)
|
||||
return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api.debug)
|
||||
}
|
||||
|
||||
// GetBillableInfoForTeam returns the billing_active status of all users on the team.
|
||||
@ -169,8 +170,8 @@ func (api *Client) GetBillableInfoForTeam() (map[string]BillingActive, error) {
|
||||
// GetBillableInfoForTeamContext returns the billing_active status of all users on the team with a custom context
|
||||
func (api *Client) GetBillableInfoForTeamContext(ctx context.Context) (map[string]BillingActive, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
return billableInfoRequest(ctx, "team.billableInfo", values, api.debug)
|
||||
return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api.debug)
|
||||
}
|
||||
|
32
vendor/github.com/nlopes/slack/usergroups.go
generated
vendored
32
vendor/github.com/nlopes/slack/usergroups.go
generated
vendored
@ -40,9 +40,9 @@ type userGroupResponseFull struct {
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func userGroupRequest(ctx context.Context, path string, values url.Values, debug bool) (*userGroupResponseFull, error) {
|
||||
func userGroupRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*userGroupResponseFull, error) {
|
||||
response := &userGroupResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postSlackMethod(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -60,7 +60,7 @@ func (api *Client) CreateUserGroup(userGroup UserGroup) (UserGroup, error) {
|
||||
// CreateUserGroupContext creates a new user group with a custom context
|
||||
func (api *Client) CreateUserGroupContext(ctx context.Context, userGroup UserGroup) (UserGroup, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"name": {userGroup.Name},
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ func (api *Client) CreateUserGroupContext(ctx context.Context, userGroup UserGro
|
||||
values["channels"] = []string{strings.Join(userGroup.Prefs.Channels, ",")}
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.create", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.create", values, api.debug)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
@ -91,11 +91,11 @@ func (api *Client) DisableUserGroup(userGroup string) (UserGroup, error) {
|
||||
// DisableUserGroupContext disables an existing user group with a custom context
|
||||
func (api *Client) DisableUserGroupContext(ctx context.Context, userGroup string) (UserGroup, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"usergroup": {userGroup},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.disable", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.disable", values, api.debug)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
@ -110,11 +110,11 @@ func (api *Client) EnableUserGroup(userGroup string) (UserGroup, error) {
|
||||
// EnableUserGroupContext enables an existing user group with a custom context
|
||||
func (api *Client) EnableUserGroupContext(ctx context.Context, userGroup string) (UserGroup, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"usergroup": {userGroup},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.enable", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.enable", values, api.debug)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
@ -129,10 +129,10 @@ func (api *Client) GetUserGroups() ([]UserGroup, error) {
|
||||
// GetUserGroupsContext returns a list of user groups for the team with a custom context
|
||||
func (api *Client) GetUserGroupsContext(ctx context.Context) ([]UserGroup, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.list", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.list", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -147,7 +147,7 @@ func (api *Client) UpdateUserGroup(userGroup UserGroup) (UserGroup, error) {
|
||||
// UpdateUserGroupContext will update an existing user group with a custom context
|
||||
func (api *Client) UpdateUserGroupContext(ctx context.Context, userGroup UserGroup) (UserGroup, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"usergroup": {userGroup.ID},
|
||||
}
|
||||
|
||||
@ -163,7 +163,7 @@ func (api *Client) UpdateUserGroupContext(ctx context.Context, userGroup UserGro
|
||||
values["description"] = []string{userGroup.Description}
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.update", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.update", values, api.debug)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
@ -178,11 +178,11 @@ func (api *Client) GetUserGroupMembers(userGroup string) ([]string, error) {
|
||||
// GetUserGroupMembersContext will retrieve the current list of users in a group with a custom context
|
||||
func (api *Client) GetUserGroupMembersContext(ctx context.Context, userGroup string) ([]string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"usergroup": {userGroup},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.users.list", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.list", values, api.debug)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
@ -197,12 +197,12 @@ func (api *Client) UpdateUserGroupMembers(userGroup string, members string) (Use
|
||||
// UpdateUserGroupMembersContext will update the members of an existing user group with a custom context
|
||||
func (api *Client) UpdateUserGroupMembersContext(ctx context.Context, userGroup string, members string) (UserGroup, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"usergroup": {userGroup},
|
||||
"users": {members},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.users.update", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.update", values, api.debug)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
|
325
vendor/github.com/nlopes/slack/users.go
generated
vendored
325
vendor/github.com/nlopes/slack/users.go
generated
vendored
@ -5,41 +5,103 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
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
|
||||
type UserProfile struct {
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
RealName string `json:"real_name"`
|
||||
RealNameNormalized string `json:"real_name_normalized"`
|
||||
DisplayName string `json:"display_name"`
|
||||
DisplayNameNormalized string `json:"display_name_normalized"`
|
||||
Email string `json:"email"`
|
||||
Skype string `json:"skype"`
|
||||
Phone string `json:"phone"`
|
||||
Image24 string `json:"image_24"`
|
||||
Image32 string `json:"image_32"`
|
||||
Image48 string `json:"image_48"`
|
||||
Image72 string `json:"image_72"`
|
||||
Image192 string `json:"image_192"`
|
||||
ImageOriginal string `json:"image_original"`
|
||||
Title string `json:"title"`
|
||||
BotID string `json:"bot_id,omitempty"`
|
||||
ApiAppID string `json:"api_app_id,omitempty"`
|
||||
StatusText string `json:"status_text,omitempty"`
|
||||
StatusEmoji string `json:"status_emoji,omitempty"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
RealName string `json:"real_name"`
|
||||
RealNameNormalized string `json:"real_name_normalized"`
|
||||
DisplayName string `json:"display_name"`
|
||||
DisplayNameNormalized string `json:"display_name_normalized"`
|
||||
Email string `json:"email"`
|
||||
Skype string `json:"skype"`
|
||||
Phone string `json:"phone"`
|
||||
Image24 string `json:"image_24"`
|
||||
Image32 string `json:"image_32"`
|
||||
Image48 string `json:"image_48"`
|
||||
Image72 string `json:"image_72"`
|
||||
Image192 string `json:"image_192"`
|
||||
ImageOriginal string `json:"image_original"`
|
||||
Title string `json:"title"`
|
||||
BotID string `json:"bot_id,omitempty"`
|
||||
ApiAppID string `json:"api_app_id,omitempty"`
|
||||
StatusText string `json:"status_text,omitempty"`
|
||||
StatusEmoji string `json:"status_emoji,omitempty"`
|
||||
Team string `json:"team"`
|
||||
Fields UserProfileCustomFields `json:"fields"`
|
||||
}
|
||||
|
||||
// UserProfileCustomFields represents user profile's custom fields.
|
||||
// Slack API's response data type is inconsistent so we use the struct.
|
||||
// For detail, please see below.
|
||||
// https://github.com/nlopes/slack/pull/298#discussion_r185159233
|
||||
type UserProfileCustomFields struct {
|
||||
fields map[string]UserProfileCustomField
|
||||
}
|
||||
|
||||
// UnmarshalJSON is the implementation of the json.Unmarshaler interface.
|
||||
func (fields *UserProfileCustomFields) UnmarshalJSON(b []byte) error {
|
||||
// https://github.com/nlopes/slack/pull/298#discussion_r185159233
|
||||
if string(b) == "[]" {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(b, &fields.fields)
|
||||
}
|
||||
|
||||
// MarshalJSON is the implementation of the json.Marshaler interface.
|
||||
func (fields UserProfileCustomFields) MarshalJSON() ([]byte, error) {
|
||||
if len(fields.fields) == 0 {
|
||||
return []byte("[]"), nil
|
||||
}
|
||||
return json.Marshal(fields.fields)
|
||||
}
|
||||
|
||||
// ToMap returns a map of custom fields.
|
||||
func (fields *UserProfileCustomFields) ToMap() map[string]UserProfileCustomField {
|
||||
return fields.fields
|
||||
}
|
||||
|
||||
// Len returns the number of custom fields.
|
||||
func (fields *UserProfileCustomFields) Len() int {
|
||||
return len(fields.fields)
|
||||
}
|
||||
|
||||
// SetMap sets a map of custom fields.
|
||||
func (fields *UserProfileCustomFields) SetMap(m map[string]UserProfileCustomField) {
|
||||
fields.fields = m
|
||||
}
|
||||
|
||||
// FieldsMap returns a map of custom fields.
|
||||
func (profile *UserProfile) FieldsMap() map[string]UserProfileCustomField {
|
||||
return profile.Fields.ToMap()
|
||||
}
|
||||
|
||||
// SetFieldsMap sets a map of custom fields.
|
||||
func (profile *UserProfile) SetFieldsMap(m map[string]UserProfileCustomField) {
|
||||
profile.Fields.SetMap(m)
|
||||
}
|
||||
|
||||
// UserProfileCustomField represents a custom user profile field
|
||||
type UserProfileCustomField struct {
|
||||
Value string `json:"value"`
|
||||
Alt string `json:"alt"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
@ -54,9 +116,12 @@ type User struct {
|
||||
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"`
|
||||
}
|
||||
|
||||
// UserPresence contains details about a user online status
|
||||
@ -103,10 +168,11 @@ type TeamIdentity struct {
|
||||
}
|
||||
|
||||
type userResponseFull struct {
|
||||
Members []User `json:"members,omitempty"` // ListUsers
|
||||
User `json:"user,omitempty"` // GetUserInfo
|
||||
UserPresence // GetUserPresence
|
||||
Members []User `json:"members,omitempty"`
|
||||
User `json:"user,omitempty"`
|
||||
UserPresence
|
||||
SlackResponse
|
||||
Metadata ResponseMetadata `json:"response_metadata"`
|
||||
}
|
||||
|
||||
type UserSetPhotoParams struct {
|
||||
@ -123,9 +189,9 @@ func NewUserSetPhotoParams() UserSetPhotoParams {
|
||||
}
|
||||
}
|
||||
|
||||
func userRequest(ctx context.Context, path string, values url.Values, debug bool) (*userResponseFull, error) {
|
||||
func userRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*userResponseFull, error) {
|
||||
response := &userResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postForm(ctx, client, SLACK_API+path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -143,10 +209,11 @@ func (api *Client) GetUserPresence(user string) (*UserPresence, error) {
|
||||
// GetUserPresenceContext will retrieve the current presence status of given user with a custom context.
|
||||
func (api *Client) GetUserPresenceContext(ctx context.Context, user string) (*UserPresence, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
}
|
||||
response, err := userRequest(ctx, "users.getPresence", values, api.debug)
|
||||
|
||||
response, err := userRequest(ctx, api.httpclient, "users.getPresence", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -161,32 +228,138 @@ 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.config.token},
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
}
|
||||
response, err := userRequest(ctx, "users.info", values, api.debug)
|
||||
|
||||
response, err := userRequest(ctx, api.httpclient, "users.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.User, nil
|
||||
}
|
||||
|
||||
// GetUsersOption options for the GetUsers method call.
|
||||
type GetUsersOption func(*UserPagination)
|
||||
|
||||
// GetUsersOptionLimit limit the number of users returned
|
||||
func GetUsersOptionLimit(n int) GetUsersOption {
|
||||
return func(p *UserPagination) {
|
||||
p.limit = n
|
||||
}
|
||||
}
|
||||
|
||||
// GetUsersOptionPresence include user presence
|
||||
func GetUsersOptionPresence(n bool) GetUsersOption {
|
||||
return func(p *UserPagination) {
|
||||
p.presence = n
|
||||
}
|
||||
}
|
||||
|
||||
func newUserPagination(c *Client, options ...GetUsersOption) (up UserPagination) {
|
||||
up = UserPagination{
|
||||
c: c,
|
||||
limit: 200, // per slack api documentation.
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
opt(&up)
|
||||
}
|
||||
|
||||
return up
|
||||
}
|
||||
|
||||
// UserPagination allows for paginating over the users
|
||||
type UserPagination struct {
|
||||
Users []User
|
||||
limit int
|
||||
presence bool
|
||||
previousResp *ResponseMetadata
|
||||
c *Client
|
||||
}
|
||||
|
||||
// Done checks if the pagination has completed
|
||||
func (UserPagination) Done(err error) bool {
|
||||
return err == errPaginationComplete
|
||||
}
|
||||
|
||||
// Failure checks if pagination failed.
|
||||
func (t UserPagination) Failure(err error) error {
|
||||
if t.Done(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (t UserPagination) Next(ctx context.Context) (_ UserPagination, err error) {
|
||||
var (
|
||||
resp *userResponseFull
|
||||
)
|
||||
|
||||
if t.c == nil || (t.previousResp != nil && t.previousResp.Cursor == "") {
|
||||
return t, errPaginationComplete
|
||||
}
|
||||
|
||||
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},
|
||||
}
|
||||
|
||||
if resp, err = userRequest(ctx, t.c.httpclient, "users.list", values, t.c.debug); err != nil {
|
||||
return t, err
|
||||
}
|
||||
|
||||
t.c.Debugf("GetUsersContext: got %d users; metadata %v", len(resp.Members), resp.Metadata)
|
||||
t.Users = resp.Members
|
||||
t.previousResp = &resp.Metadata
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// GetUsersPaginated fetches users in a paginated fashion, see GetUsersContext for usage.
|
||||
func (api *Client) GetUsersPaginated(options ...GetUsersOption) UserPagination {
|
||||
return newUserPagination(api, options...)
|
||||
}
|
||||
|
||||
// GetUsers returns the list of users (with their detailed information)
|
||||
func (api *Client) GetUsers() ([]User, error) {
|
||||
return api.GetUsersContext(context.Background())
|
||||
}
|
||||
|
||||
// GetUsersContext returns the list of users (with their detailed information) with a custom context
|
||||
func (api *Client) GetUsersContext(ctx context.Context) ([]User, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"presence": {"1"},
|
||||
func (api *Client) GetUsersContext(ctx context.Context) (results []User, err error) {
|
||||
var (
|
||||
p UserPagination
|
||||
)
|
||||
|
||||
for p = api.GetUsersPaginated(); !p.Done(err); p, err = p.Next(ctx) {
|
||||
results = append(results, p.Users...)
|
||||
}
|
||||
response, err := userRequest(ctx, "users.list", values, api.debug)
|
||||
|
||||
return results, p.Failure(err)
|
||||
}
|
||||
|
||||
// GetUserByEmail will retrieve the complete user information by email
|
||||
func (api *Client) GetUserByEmail(email string) (*User, error) {
|
||||
return api.GetUserByEmailContext(context.Background(), email)
|
||||
}
|
||||
|
||||
// GetUserByEmailContext will retrieve the complete user information by email with a custom context
|
||||
func (api *Client) GetUserByEmailContext(ctx context.Context, email string) (*User, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"email": {email},
|
||||
}
|
||||
response, err := userRequest(ctx, api.httpclient, "users.lookupByEmail", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.Members, nil
|
||||
return &response.User, nil
|
||||
}
|
||||
|
||||
// SetUserAsActive marks the currently authenticated user as active
|
||||
@ -195,11 +368,12 @@ func (api *Client) SetUserAsActive() error {
|
||||
}
|
||||
|
||||
// SetUserAsActiveContext marks the currently authenticated user as active with a custom context
|
||||
func (api *Client) SetUserAsActiveContext(ctx context.Context) error {
|
||||
func (api *Client) SetUserAsActiveContext(ctx context.Context) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
_, err := userRequest(ctx, "users.setActive", values, api.debug)
|
||||
|
||||
_, err = userRequest(ctx, api.httpclient, "users.setActive", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -211,15 +385,12 @@ func (api *Client) SetUserPresence(presence string) error {
|
||||
// SetUserPresenceContext changes the currently authenticated user presence with a custom context
|
||||
func (api *Client) SetUserPresenceContext(ctx context.Context, presence string) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"presence": {presence},
|
||||
}
|
||||
_, err := userRequest(ctx, "users.setPresence", values, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
_, err := userRequest(ctx, api.httpclient, "users.setPresence", values, api.debug)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetUserIdentity will retrieve user info available per identity scopes
|
||||
@ -230,10 +401,11 @@ func (api *Client) GetUserIdentity() (*UserIdentityResponse, error) {
|
||||
// GetUserIdentityContext will retrieve user info available per identity scopes with a custom context
|
||||
func (api *Client) GetUserIdentityContext(ctx context.Context) (*UserIdentityResponse, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
response := &UserIdentityResponse{}
|
||||
err := post(ctx, "users.identity", values, response, api.debug)
|
||||
|
||||
err := postForm(ctx, api.httpclient, SLACK_API+"users.identity", values, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -252,25 +424,24 @@ func (api *Client) SetUserPhoto(image string, params UserSetPhotoParams) error {
|
||||
func (api *Client) SetUserPhotoContext(ctx context.Context, image string, params UserSetPhotoParams) error {
|
||||
response := &SlackResponse{}
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.CropX != DEFAULT_USER_PHOTO_CROP_X {
|
||||
values.Add("crop_x", string(params.CropX))
|
||||
values.Add("crop_x", strconv.Itoa(params.CropX))
|
||||
}
|
||||
if params.CropY != DEFAULT_USER_PHOTO_CROP_Y {
|
||||
values.Add("crop_y", string(params.CropY))
|
||||
values.Add("crop_y", strconv.Itoa(params.CropX))
|
||||
}
|
||||
if params.CropW != DEFAULT_USER_PHOTO_CROP_W {
|
||||
values.Add("crop_w", string(params.CropW))
|
||||
values.Add("crop_w", strconv.Itoa(params.CropW))
|
||||
}
|
||||
err := postLocalWithMultipartResponse(ctx, "users.setPhoto", image, "image", values, response, api.debug)
|
||||
|
||||
err := postLocalWithMultipartResponse(ctx, api.httpclient, "users.setPhoto", image, "image", values, response, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
return nil
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// DeleteUserPhoto deletes the current authenticated user's profile image
|
||||
@ -282,16 +453,15 @@ func (api *Client) DeleteUserPhoto() error {
|
||||
func (api *Client) DeleteUserPhotoContext(ctx context.Context) error {
|
||||
response := &SlackResponse{}
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
err := post(ctx, "users.deletePhoto", values, response, api.debug)
|
||||
|
||||
err := postForm(ctx, api.httpclient, SLACK_API+"users.deletePhoto", values, response, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
return nil
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// SetUserCustomStatus will set a custom status and emoji for the currently
|
||||
@ -331,13 +501,12 @@ func (api *Client) SetUserCustomStatusContext(ctx context.Context, statusText, s
|
||||
}
|
||||
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"profile": {string(profile)},
|
||||
}
|
||||
|
||||
response := &userResponseFull{}
|
||||
|
||||
if err = post(ctx, "users.profile.set", values, response, api.debug); err != nil {
|
||||
if err = postForm(ctx, api.httpclient, SLACK_API+"users.profile.set", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -359,3 +528,31 @@ func (api *Client) UnsetUserCustomStatus() error {
|
||||
func (api *Client) UnsetUserCustomStatusContext(ctx context.Context) error {
|
||||
return api.SetUserCustomStatusContext(ctx, "", "")
|
||||
}
|
||||
|
||||
// GetUserProfile retrieves a user's profile information.
|
||||
func (api *Client) GetUserProfile(userID string, includeLabels bool) (*UserProfile, error) {
|
||||
return api.GetUserProfileContext(context.Background(), userID, includeLabels)
|
||||
}
|
||||
|
||||
type getUserProfileResponse struct {
|
||||
SlackResponse
|
||||
Profile *UserProfile `json:"profile"`
|
||||
}
|
||||
|
||||
// GetUserProfileContext retrieves a user's profile information with a context.
|
||||
func (api *Client) GetUserProfileContext(ctx context.Context, userID string, includeLabels bool) (*UserProfile, error) {
|
||||
values := url.Values{"token": {api.token}, "user": {userID}}
|
||||
if includeLabels {
|
||||
values.Add("include_labels", "true")
|
||||
}
|
||||
resp := &getUserProfileResponse{}
|
||||
|
||||
err := postSlackMethod(ctx, api.httpclient, "users.profile.get", values, &resp, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !resp.Ok {
|
||||
return nil, errors.New(resp.Error)
|
||||
}
|
||||
return resp.Profile, nil
|
||||
}
|
||||
|
33
vendor/github.com/nlopes/slack/webhooks.go
generated
vendored
Normal file
33
vendor/github.com/nlopes/slack/webhooks.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"net/http"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type WebhookMessage struct {
|
||||
Text string `json:"text,omitempty"`
|
||||
Attachments []Attachment `json:"attachments,omitempty"`
|
||||
}
|
||||
|
||||
func PostWebhook(url string, 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));
|
||||
|
||||
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
|
||||
}
|
43
vendor/github.com/nlopes/slack/websocket.go
generated
vendored
43
vendor/github.com/nlopes/slack/websocket.go
generated
vendored
@ -3,9 +3,10 @@ package slack
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -19,8 +20,9 @@ const (
|
||||
//
|
||||
// Create this element with Client's NewRTM() or NewRTMWithOptions(*RTMOptions)
|
||||
type RTM struct {
|
||||
idGen IDGenerator
|
||||
pings map[int]time.Time
|
||||
idGen IDGenerator
|
||||
pingInterval time.Duration
|
||||
pingDeadman *time.Timer
|
||||
|
||||
// Connection life-cycle
|
||||
conn *websocket.Conn
|
||||
@ -44,6 +46,13 @@ type RTM struct {
|
||||
// rtm.start to connect to Slack, otherwise it will use
|
||||
// rtm.connect
|
||||
useRTMStart bool
|
||||
|
||||
// dialer is a gorilla/websocket Dialer. If nil, use the default
|
||||
// Dialer.
|
||||
dialer *websocket.Dialer
|
||||
|
||||
// mu is mutex used to prevent RTM connection race conditions
|
||||
mu *sync.Mutex
|
||||
}
|
||||
|
||||
// RTMOptions allows configuration of various options available for RTM messaging
|
||||
@ -60,9 +69,17 @@ type RTMOptions struct {
|
||||
|
||||
// Disconnect and wait, blocking until a successful disconnection.
|
||||
func (rtm *RTM) Disconnect() error {
|
||||
// this channel is always closed on disconnect. lets the ManagedConnection() function
|
||||
// properly clean up.
|
||||
close(rtm.disconnected)
|
||||
// avoid RTM disconnect race conditions
|
||||
rtm.mu.Lock()
|
||||
defer rtm.mu.Unlock()
|
||||
|
||||
// always push into the disconnected 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:
|
||||
}
|
||||
|
||||
if !rtm.isConnected {
|
||||
return errors.New("Invalid call to Disconnect - Slack API is already disconnected")
|
||||
@ -72,12 +89,6 @@ func (rtm *RTM) Disconnect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reconnect only makes sense if you've successfully disconnectd with Disconnect().
|
||||
func (rtm *RTM) Reconnect() error {
|
||||
logger.Println("RTM::Reconnect not implemented!")
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetInfo returns the info structure received when calling
|
||||
// "startrtm", holding all channels, groups and other metadata needed
|
||||
// to implement a full chat client. It will be non-nil after a call to
|
||||
@ -97,3 +108,11 @@ func (rtm *RTM) SendMessage(msg *OutgoingMessage) {
|
||||
|
||||
rtm.outgoingMessages <- *msg
|
||||
}
|
||||
|
||||
func (rtm *RTM) resetDeadman() {
|
||||
timerReset(rtm.pingDeadman, deadmanDuration(rtm.pingInterval))
|
||||
}
|
||||
|
||||
func deadmanDuration(d time.Duration) time.Duration {
|
||||
return d * 4
|
||||
}
|
||||
|
7
vendor/github.com/nlopes/slack/websocket_internals.go
generated
vendored
7
vendor/github.com/nlopes/slack/websocket_internals.go
generated
vendored
@ -63,6 +63,13 @@ func (m *MessageTooLongEvent) Error() string {
|
||||
return fmt.Sprintf("Message too long (max %d characters)", m.MaxLength)
|
||||
}
|
||||
|
||||
// RateLimitEvent is used when Slack warns that rate-limits are being hit.
|
||||
type RateLimitEvent struct{}
|
||||
|
||||
func (e *RateLimitEvent) Error() string {
|
||||
return "Messages are being sent too fast."
|
||||
}
|
||||
|
||||
// OutgoingErrorEvent contains information in case there were errors sending messages
|
||||
type OutgoingErrorEvent struct {
|
||||
Message OutgoingMessage
|
||||
|
183
vendor/github.com/nlopes/slack/websocket_managed_conn.go
generated
vendored
183
vendor/github.com/nlopes/slack/websocket_managed_conn.go
generated
vendored
@ -4,10 +4,11 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// ManageConnection can be called on a Slack RTM instance returned by the
|
||||
@ -24,25 +25,35 @@ import (
|
||||
//
|
||||
// The defined error events are located in websocket_internals.go.
|
||||
func (rtm *RTM) ManageConnection() {
|
||||
var connectionCount int
|
||||
for {
|
||||
connectionCount++
|
||||
var (
|
||||
err error
|
||||
info *Info
|
||||
conn *websocket.Conn
|
||||
)
|
||||
|
||||
for connectionCount := 0; ; connectionCount++ {
|
||||
// start trying to connect
|
||||
// the returned err is already passed onto the IncomingEvents channel
|
||||
info, conn, err := rtm.connect(connectionCount, rtm.useRTMStart)
|
||||
// if err != nil then the connection is sucessful - otherwise it is
|
||||
// fatal
|
||||
if err != nil {
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
// lock to prevent data races with Disconnect particularly around isConnected
|
||||
// and conn.
|
||||
rtm.mu.Lock()
|
||||
rtm.conn = conn
|
||||
rtm.isConnected = true
|
||||
rtm.info = info
|
||||
rtm.mu.Unlock()
|
||||
|
||||
rtm.IncomingEvents <- RTMEvent{"connected", &ConnectedEvent{
|
||||
ConnectionCount: connectionCount,
|
||||
Info: info,
|
||||
}}
|
||||
|
||||
rtm.conn = conn
|
||||
rtm.isConnected = true
|
||||
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
|
||||
@ -50,7 +61,7 @@ func (rtm *RTM) ManageConnection() {
|
||||
go rtm.handleIncomingEvents(keepRunning)
|
||||
|
||||
// this should be a blocking call until the connection has ended
|
||||
rtm.handleEvents(keepRunning, 30*time.Second)
|
||||
rtm.handleEvents(keepRunning)
|
||||
|
||||
// after being disconnected we need to check if it was intentional
|
||||
// if not then we should try to reconnect
|
||||
@ -67,6 +78,12 @@ func (rtm *RTM) ManageConnection() {
|
||||
// If useRTMStart is false then it uses rtm.connect to create the connection,
|
||||
// otherwise it uses rtm.start.
|
||||
func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocket.Conn, error) {
|
||||
const (
|
||||
errInvalidAuth = "invalid_auth"
|
||||
errInactiveAccount = "account_inactive"
|
||||
errMissingAuthToken = "not_authed"
|
||||
)
|
||||
|
||||
// used to provide exponential backoff wait time with jitter before trying
|
||||
// to connect to slack again
|
||||
boff := &backoff{
|
||||
@ -87,10 +104,14 @@ func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocke
|
||||
if err == nil {
|
||||
return info, conn, nil
|
||||
}
|
||||
// check for fatal errors - currently only invalid_auth
|
||||
if sErr, ok := err.(*WebError); ok && (sErr.Error() == "invalid_auth" || sErr.Error() == "account_inactive") {
|
||||
|
||||
// check for fatal errors
|
||||
switch err.Error() {
|
||||
case errInvalidAuth, errInactiveAccount, errMissingAuthToken:
|
||||
rtm.Debugf("Invalid auth when connecting with RTM: %s", err)
|
||||
rtm.IncomingEvents <- RTMEvent{"invalid_auth", &InvalidAuthEvent{}}
|
||||
return nil, nil, sErr
|
||||
return nil, nil, err
|
||||
default:
|
||||
}
|
||||
|
||||
// any other errors are treated as recoverable and we try again after
|
||||
@ -102,7 +123,7 @@ func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocke
|
||||
|
||||
// check if Disconnect() has been invoked.
|
||||
select {
|
||||
case _ = <-rtm.disconnected:
|
||||
case <-rtm.disconnected:
|
||||
rtm.IncomingEvents <- RTMEvent{"disconnected", &DisconnectedEvent{Intentional: true}}
|
||||
return nil, nil, fmt.Errorf("disconnect received while trying to connect")
|
||||
default:
|
||||
@ -119,23 +140,34 @@ func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocke
|
||||
// startRTMAndDial attempts to connect to the slack websocket. If useRTMStart is true,
|
||||
// then it returns the full information returned by the "rtm.start" method on the
|
||||
// slack API. Else it uses the "rtm.connect" method to connect
|
||||
func (rtm *RTM) startRTMAndDial(useRTMStart bool) (*Info, *websocket.Conn, error) {
|
||||
var info *Info
|
||||
var url string
|
||||
var err error
|
||||
func (rtm *RTM) startRTMAndDial(useRTMStart bool) (info *Info, _ *websocket.Conn, err error) {
|
||||
var (
|
||||
url string
|
||||
)
|
||||
|
||||
if useRTMStart {
|
||||
rtm.Debugf("Starting RTM")
|
||||
info, url, err = rtm.StartRTM()
|
||||
} else {
|
||||
rtm.Debugf("Connecting to RTM")
|
||||
info, url, err = rtm.ConnectRTM()
|
||||
}
|
||||
if err != nil {
|
||||
rtm.Debugf("Failed to start or connect to RTM: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rtm.Debugf("Dialing to websocket on url %s", url)
|
||||
// Only use HTTPS for connections to prevent MITM attacks on the connection.
|
||||
conn, err := websocketProxyDial(url, "https://api.slack.com")
|
||||
upgradeHeader := http.Header{}
|
||||
upgradeHeader.Add("Origin", "https://api.slack.com")
|
||||
dialer := websocket.DefaultDialer
|
||||
if rtm.dialer != nil {
|
||||
dialer = rtm.dialer
|
||||
}
|
||||
conn, _, err := dialer.Dial(url, upgradeHeader)
|
||||
if err != nil {
|
||||
rtm.Debugf("Failed to dial to the websocket: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
return info, conn, err
|
||||
@ -163,8 +195,8 @@ 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, interval time.Duration) {
|
||||
ticker := time.NewTicker(interval)
|
||||
func (rtm *RTM) handleEvents(keepRunning chan bool) {
|
||||
ticker := time.NewTicker(rtm.pingInterval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
@ -172,7 +204,12 @@ func (rtm *RTM) handleEvents(keepRunning chan bool, interval time.Duration) {
|
||||
case intentional := <-rtm.killChannel:
|
||||
_ = rtm.killConnection(keepRunning, intentional)
|
||||
return
|
||||
// send pings on ticker interval
|
||||
|
||||
// detect when the connection is dead.
|
||||
case <-rtm.pingDeadman.C:
|
||||
rtm.Debugln("deadman switch trigger disconnecting")
|
||||
_ = rtm.killConnection(keepRunning, false)
|
||||
// send pings on ticker interval
|
||||
case <-ticker.C:
|
||||
err := rtm.ping()
|
||||
if err != nil {
|
||||
@ -190,7 +227,11 @@ func (rtm *RTM) handleEvents(keepRunning chan bool, interval time.Duration) {
|
||||
rtm.sendOutgoingMessage(msg)
|
||||
// listen for incoming messages that need to be parsed
|
||||
case rawEvent := <-rtm.rawEvents:
|
||||
rtm.handleRawEvent(rawEvent)
|
||||
switch rtm.handleRawEvent(rawEvent) {
|
||||
case rtmEventTypeGoodbye:
|
||||
_ = rtm.killConnection(keepRunning, false)
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -208,7 +249,9 @@ func (rtm *RTM) handleIncomingEvents(keepRunning <-chan bool) {
|
||||
case <-keepRunning:
|
||||
return
|
||||
default:
|
||||
rtm.receiveIncomingEvent()
|
||||
if err := rtm.receiveIncomingEvent(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -218,7 +261,7 @@ func (rtm *RTM) sendWithDeadline(msg interface{}) error {
|
||||
if err := rtm.conn.SetWriteDeadline(time.Now().Add(10 * time.Second)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := websocket.JSON.Send(rtm.conn, msg); err != nil {
|
||||
if err := rtm.conn.WriteJSON(msg); err != nil {
|
||||
return err
|
||||
}
|
||||
// remove write deadline
|
||||
@ -258,9 +301,7 @@ func (rtm *RTM) sendOutgoingMessage(msg OutgoingMessage) {
|
||||
func (rtm *RTM) ping() error {
|
||||
id := rtm.idGen.Next()
|
||||
rtm.Debugln("Sending PING ", id)
|
||||
rtm.pings[id] = time.Now()
|
||||
|
||||
msg := &Ping{ID: id, Type: "ping"}
|
||||
msg := &Ping{ID: id, Type: "ping", Timestamp: time.Now().Unix()}
|
||||
|
||||
if err := rtm.sendWithDeadline(msg); err != nil {
|
||||
rtm.Debugf("RTM Error sending 'PING %d': %s", id, err.Error())
|
||||
@ -271,52 +312,62 @@ func (rtm *RTM) ping() error {
|
||||
|
||||
// receiveIncomingEvent attempts to receive an event from the RTM's websocket.
|
||||
// This will block until a frame is available from the websocket.
|
||||
func (rtm *RTM) receiveIncomingEvent() {
|
||||
// If the read from the websocket results in a fatal error, this function will return non-nil.
|
||||
func (rtm *RTM) receiveIncomingEvent() error {
|
||||
event := json.RawMessage{}
|
||||
err := websocket.JSON.Receive(rtm.conn, &event)
|
||||
if err == io.EOF {
|
||||
err := rtm.conn.ReadJSON(&event)
|
||||
switch {
|
||||
case err == io.ErrUnexpectedEOF:
|
||||
// EOF's don't seem to signify a failed connection so instead we ignore
|
||||
// them here and detect a failed connection upon attempting to send a
|
||||
// 'PING' message
|
||||
|
||||
// trigger a 'PING' to detect pontential websocket disconnect
|
||||
// trigger a 'PING' to detect potential websocket disconnect
|
||||
rtm.forcePing <- true
|
||||
return
|
||||
} else if err != nil {
|
||||
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,
|
||||
}}
|
||||
// force a ping here too?
|
||||
return
|
||||
} else if len(event) == 0 {
|
||||
rtm.killChannel <- false
|
||||
return err
|
||||
case len(event) == 0:
|
||||
rtm.Debugln("Received empty event")
|
||||
return
|
||||
default:
|
||||
rtm.Debugln("Incoming Event:", string(event[:]))
|
||||
rtm.rawEvents <- event
|
||||
}
|
||||
rtm.Debugln("Incoming Event:", string(event[:]))
|
||||
rtm.rawEvents <- event
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleRawEvent takes a raw JSON message received from the slack websocket
|
||||
// and handles the encoded event.
|
||||
func (rtm *RTM) handleRawEvent(rawEvent json.RawMessage) {
|
||||
// returns the event type of the message.
|
||||
func (rtm *RTM) handleRawEvent(rawEvent json.RawMessage) string {
|
||||
event := &Event{}
|
||||
err := json.Unmarshal(rawEvent, event)
|
||||
if err != nil {
|
||||
rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}}
|
||||
return
|
||||
return ""
|
||||
}
|
||||
|
||||
switch event.Type {
|
||||
case "":
|
||||
case rtmEventTypeAck:
|
||||
rtm.handleAck(rawEvent)
|
||||
case "hello":
|
||||
case rtmEventTypeHello:
|
||||
rtm.IncomingEvents <- RTMEvent{"hello", &HelloEvent{}}
|
||||
case "pong":
|
||||
case rtmEventTypePong:
|
||||
rtm.handlePong(rawEvent)
|
||||
case "desktop_notification":
|
||||
case rtmEventTypeGoodbye:
|
||||
// just return the event type up for goodbye, will be handled by caller.
|
||||
case rtmEventTypeDesktopNotification:
|
||||
rtm.Debugln("Received desktop notification, ignoring")
|
||||
default:
|
||||
rtm.handleEvent(event.Type, rawEvent)
|
||||
}
|
||||
|
||||
return event.Type
|
||||
}
|
||||
|
||||
// handleAck handles an incoming 'ACK' message.
|
||||
@ -331,7 +382,13 @@ func (rtm *RTM) handleAck(event json.RawMessage) {
|
||||
if ack.Ok {
|
||||
rtm.IncomingEvents <- RTMEvent{"ack", ack}
|
||||
} else if ack.RTMResponse.Error != nil {
|
||||
rtm.IncomingEvents <- RTMEvent{"ack_error", &AckErrorEvent{ack.Error}}
|
||||
// As there is no documentation for RTM error-codes, this
|
||||
// identification of a rate-limit warning is very brittle.
|
||||
if ack.RTMResponse.Error.Code == -1 && ack.RTMResponse.Error.Msg == "slow down, too many messages..." {
|
||||
rtm.IncomingEvents <- RTMEvent{"ack_error", &RateLimitEvent{}}
|
||||
} else {
|
||||
rtm.IncomingEvents <- RTMEvent{"ack_error", &AckErrorEvent{ack.Error}}
|
||||
}
|
||||
} else {
|
||||
rtm.IncomingEvents <- RTMEvent{"ack_error", &AckErrorEvent{fmt.Errorf("ack decode failure")}}
|
||||
}
|
||||
@ -341,19 +398,20 @@ func (rtm *RTM) handleAck(event json.RawMessage) {
|
||||
// a previously sent 'PING' message. This is then used to compute the
|
||||
// connection's latency.
|
||||
func (rtm *RTM) handlePong(event json.RawMessage) {
|
||||
pong := &Pong{}
|
||||
if err := json.Unmarshal(event, pong); err != nil {
|
||||
rtm.Debugln("RTM Error unmarshalling 'pong' event:", err)
|
||||
var (
|
||||
p Pong
|
||||
)
|
||||
|
||||
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))
|
||||
return
|
||||
}
|
||||
if pingTime, exists := rtm.pings[pong.ReplyTo]; exists {
|
||||
latency := time.Since(pingTime)
|
||||
rtm.IncomingEvents <- RTMEvent{"latency_report", &LatencyReport{Value: latency}}
|
||||
delete(rtm.pings, pong.ReplyTo)
|
||||
} else {
|
||||
rtm.Debugln("RTM Error - unmatched 'pong' event:", string(event))
|
||||
}
|
||||
|
||||
latency := time.Since(time.Unix(p.Timestamp, 0))
|
||||
rtm.IncomingEvents <- RTMEvent{"latency_report", &LatencyReport{Value: latency}}
|
||||
}
|
||||
|
||||
// handleEvent is the "default" response to an event that does not have a
|
||||
@ -363,7 +421,7 @@ func (rtm *RTM) handlePong(event json.RawMessage) {
|
||||
// correct struct then this sends an UnmarshallingErrorEvent to the
|
||||
// IncomingEvents channel.
|
||||
func (rtm *RTM) handleEvent(typeStr string, event json.RawMessage) {
|
||||
v, exists := eventMapping[typeStr]
|
||||
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))
|
||||
@ -382,10 +440,10 @@ func (rtm *RTM) handleEvent(typeStr string, event json.RawMessage) {
|
||||
rtm.IncomingEvents <- RTMEvent{typeStr, recvEvent}
|
||||
}
|
||||
|
||||
// eventMapping holds a mapping of event names to their corresponding struct
|
||||
// EventMapping holds a mapping of event names to their corresponding struct
|
||||
// implementations. The structs should be instances of the unmarshalling
|
||||
// target for the matching event type.
|
||||
var eventMapping = map[string]interface{}{
|
||||
var EventMapping = map[string]interface{}{
|
||||
"message": MessageEvent{},
|
||||
"presence_change": PresenceChangeEvent{},
|
||||
"user_typing": UserTypingEvent{},
|
||||
@ -463,4 +521,7 @@ var eventMapping = map[string]interface{}{
|
||||
"accounts_changed": AccountsChangedEvent{},
|
||||
|
||||
"reconnect_url": ReconnectUrlEvent{},
|
||||
|
||||
"member_joined_channel": MemberJoinedChannelEvent{},
|
||||
"member_left_channel": MemberLeftChannelEvent{},
|
||||
}
|
||||
|
21
vendor/github.com/nlopes/slack/websocket_misc.go
generated
vendored
21
vendor/github.com/nlopes/slack/websocket_misc.go
generated
vendored
@ -80,7 +80,7 @@ type EmojiChangedEvent struct {
|
||||
SubType string `json:"subtype"`
|
||||
Name string `json:"name"`
|
||||
Names []string `json:"names"`
|
||||
Value string `json:"value"`
|
||||
Value string `json:"value"`
|
||||
EventTimestamp string `json:"event_ts"`
|
||||
}
|
||||
|
||||
@ -119,3 +119,22 @@ type ReconnectUrlEvent struct {
|
||||
Type string `json:"type"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// MemberJoinedChannelEvent, a user joined a public or private channel
|
||||
type MemberJoinedChannelEvent struct {
|
||||
Type string `json:"type"`
|
||||
User string `json:"user"`
|
||||
Channel string `json:"channel"`
|
||||
ChannelType string `json:"channel_type"`
|
||||
Team string `json:"team"`
|
||||
Inviter string `json:"inviter"`
|
||||
}
|
||||
|
||||
// MemberJoinedChannelEvent, a user left a public or private channel
|
||||
type MemberLeftChannelEvent struct {
|
||||
Type string `json:"type"`
|
||||
User string `json:"user"`
|
||||
Channel string `json:"channel"`
|
||||
ChannelType string `json:"channel_type"`
|
||||
Team string `json:"team"`
|
||||
}
|
||||
|
82
vendor/github.com/nlopes/slack/websocket_proxy.go
generated
vendored
82
vendor/github.com/nlopes/slack/websocket_proxy.go
generated
vendored
@ -1,82 +0,0 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
// Taken and reworked from: https://gist.github.com/madmo/8548738
|
||||
func websocketHTTPConnect(proxy, urlString string) (net.Conn, error) {
|
||||
p, err := net.Dial("tcp", proxy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
turl, err := url.Parse(urlString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := http.Request{
|
||||
Method: "CONNECT",
|
||||
URL: &url.URL{},
|
||||
Host: turl.Host,
|
||||
}
|
||||
|
||||
cc := httputil.NewProxyClientConn(p, nil)
|
||||
if _, err := cc.Do(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rwc, _ := cc.Hijack()
|
||||
|
||||
return rwc, nil
|
||||
}
|
||||
|
||||
func websocketProxyDial(urlString, origin string) (ws *websocket.Conn, err error) {
|
||||
if os.Getenv("HTTP_PROXY") == "" {
|
||||
return websocket.Dial(urlString, "", origin)
|
||||
}
|
||||
|
||||
purl, err := url.Parse(os.Getenv("HTTP_PROXY"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config, err := websocket.NewConfig(urlString, origin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := websocketHTTPConnect(purl.Host, urlString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch config.Location.Scheme {
|
||||
case "ws":
|
||||
case "wss":
|
||||
tlsClient := tls.Client(client, &tls.Config{
|
||||
ServerName: strings.Split(config.Location.Host, ":")[0],
|
||||
})
|
||||
err := tlsClient.Handshake()
|
||||
if err != nil {
|
||||
tlsClient.Close()
|
||||
return nil, err
|
||||
}
|
||||
client = tlsClient
|
||||
|
||||
default:
|
||||
return nil, errors.New("invalid websocket schema")
|
||||
}
|
||||
|
||||
return websocket.NewClient(config, client)
|
||||
}
|
Reference in New Issue
Block a user