mirror of
https://github.com/cwinfo/matterbridge.git
synced 2025-07-03 04:57:44 +00:00
Use upstream slack-go/slack again (#1018)
This commit is contained in:
3
vendor/github.com/slack-go/slack/.gitignore
generated
vendored
Normal file
3
vendor/github.com/slack-go/slack/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.test
|
||||
*~
|
||||
.idea/
|
14
vendor/github.com/slack-go/slack/.gometalinter.json
generated
vendored
Normal file
14
vendor/github.com/slack-go/slack/.gometalinter.json
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"DisableAll": true,
|
||||
"Enable": [
|
||||
"structcheck",
|
||||
"vet",
|
||||
"misspell",
|
||||
"unconvert",
|
||||
"interfacer",
|
||||
"goimports"
|
||||
],
|
||||
"Vendor": true,
|
||||
"Exclude": ["vendor"],
|
||||
"Deadline": "300s"
|
||||
}
|
39
vendor/github.com/slack-go/slack/.travis.yml
generated
vendored
Normal file
39
vendor/github.com/slack-go/slack/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
language: go
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
install: true
|
||||
|
||||
before_install:
|
||||
- export PATH=$HOME/gopath/bin:$PATH
|
||||
# install gometalinter
|
||||
- curl -L https://git.io/vp6lP | sh
|
||||
|
||||
script:
|
||||
- PATH=$PWD/bin:$PATH gometalinter ./...
|
||||
- go test -race -cover ./...
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
include:
|
||||
- go: "1.7.x"
|
||||
script: go test -v ./...
|
||||
- go: "1.8.x"
|
||||
script: go test -v ./...
|
||||
- go: "1.9.x"
|
||||
script: go test -v ./...
|
||||
- go: "1.10.x"
|
||||
script: go test -v ./...
|
||||
- go: "1.11.x"
|
||||
script: go test -v -mod=vendor ./...
|
||||
- go: "1.12.x"
|
||||
script: go test -v -mod=vendor ./...
|
||||
- go: "1.13.x"
|
||||
script: go test -v -mod=vendor ./...
|
||||
- go: "tip"
|
||||
script: go test -v -mod=vendor ./...
|
||||
|
||||
git:
|
||||
depth: 10
|
59
vendor/github.com/slack-go/slack/CHANGELOG.md
generated
vendored
Normal file
59
vendor/github.com/slack-go/slack/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
### v0.6.0 - August 31, 2019
|
||||
full differences can be viewed using `git log --oneline --decorate --color v0.5.0..v0.6.0`
|
||||
thanks to everyone who has contributed since January!
|
||||
|
||||
|
||||
#### Breaking Changes:
|
||||
- Info struct has had fields removed related to deprecated functionality by slack.
|
||||
- minor adjustments to some structs.
|
||||
- some internal default values have changed, usually to be more inline with slack defaults or to correct inability to set a particular value. (Message Parse for example.)
|
||||
|
||||
##### Highlights:
|
||||
- new slacktest package easy mocking for slack client. use, enjoy, please submit PRs for improvements and default behaviours! shamelessly taken from the [slack-test repo](https://github.com/lusis/slack-test) thank you lusis for letting us use it and bring it into the slack repo.
|
||||
- blocks, blocks, blocks.
|
||||
- RTM ManagedConnection has undergone a significant cleanup.
|
||||
in particular handles backoffs gracefully, removed many deadlocks,
|
||||
and Disconnect is now much more responsive.
|
||||
|
||||
### v0.5.0 - January 20, 2019
|
||||
full differences can be viewed using `git log --oneline --decorate --color v0.4.0..v0.5.0`
|
||||
- Breaking changes: various old struct fields have been removed or updated to match slack's api.
|
||||
- deadlock fix in RTM disconnect.
|
||||
|
||||
### v0.4.0 - October 06, 2018
|
||||
full differences can be viewed using `git log --oneline --decorate --color v0.3.0..v0.4.0`
|
||||
- Breaking Change: renamed ApplyMessageOption, to mark it as unsafe,
|
||||
this means it may break without warning in the future.
|
||||
- Breaking: Msg structure files field changed to an array.
|
||||
- General: implementation for new security headers.
|
||||
- RTM: deadlock fix between connect/disconnect.
|
||||
- Events: various new fields added.
|
||||
- Web: various fixes, new fields exposed, new methods added.
|
||||
- Interactions: minor additions expect breaking changes in next release for dialogs/button clicks.
|
||||
- Utils: new methods added.
|
||||
|
||||
### 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.
|
||||
As the used context package is the one from Go 1.7 this will be the last
|
||||
compatible with Go < 1.7.
|
||||
|
||||
Please check [0.1.0](https://github.com/nlopes/slack/releases/tag/v0.1.0)
|
||||
|
||||
### v0.0.1 - Jul 26, 2015
|
||||
|
||||
If you just updated from master and it broke your implementation, please
|
||||
check [0.0.1](https://github.com/nlopes/slack/releases/tag/v0.0.1)
|
23
vendor/github.com/slack-go/slack/LICENSE
generated
vendored
Normal file
23
vendor/github.com/slack-go/slack/LICENSE
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
Copyright (c) 2015, Norberto Lopes
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
36
vendor/github.com/slack-go/slack/Makefile
generated
vendored
Normal file
36
vendor/github.com/slack-go/slack/Makefile
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
.PHONY: help deps fmt lint test test-race test-integration
|
||||
|
||||
help:
|
||||
@echo ""
|
||||
@echo "Welcome to slack-go/slack make."
|
||||
@echo "The following commands are available:"
|
||||
@echo ""
|
||||
@echo " make deps : Fetch all dependencies"
|
||||
@echo " make fmt : Run go fmt to fix any formatting issues"
|
||||
@echo " make lint : Use go vet to check for linting issues"
|
||||
@echo " make test : Run all short tests"
|
||||
@echo " make test-race : Run all tests with race condition checking"
|
||||
@echo " make test-integration : Run all tests without limiting to short"
|
||||
@echo ""
|
||||
@echo " make pr-prep : Run this before making a PR to run fmt, lint and tests"
|
||||
@echo ""
|
||||
|
||||
deps:
|
||||
@go mod tidy
|
||||
|
||||
fmt:
|
||||
@go fmt .
|
||||
|
||||
lint:
|
||||
@go vet .
|
||||
|
||||
test:
|
||||
@go test -count=1 -timeout 300s -short .
|
||||
|
||||
test-race:
|
||||
@go test -count=1 -timeout 300s -short -race .
|
||||
|
||||
test-integration:
|
||||
@go test -count=1 -timeout 600s .
|
||||
|
||||
pr-prep: fmt lint test-race test-integration
|
96
vendor/github.com/slack-go/slack/README.md
generated
vendored
Normal file
96
vendor/github.com/slack-go/slack/README.md
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
Slack API in Go [](https://godoc.org/github.com/slack-go/slack) [](https://travis-ci.org/slack-go/slack)
|
||||
===============
|
||||
This is the original Slack library for Go created by Norberto Lopez, transferred to a Github organization.
|
||||
|
||||
[](https://gitter.im/go-slack/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Changelog
|
||||
|
||||
[CHANGELOG.md](https://github.com/slack-go/slack/blob/master/CHANGELOG.md) is available. Please visit it for updates.
|
||||
|
||||
## Installing
|
||||
|
||||
### *go get*
|
||||
|
||||
$ go get -u github.com/slack-go/slack
|
||||
|
||||
## Example
|
||||
|
||||
### Getting all groups
|
||||
|
||||
```golang
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/slack-go/slack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
api := slack.New("YOUR_TOKEN_HERE")
|
||||
// If you set debugging, it will log all requests to the console
|
||||
// Useful when encountering issues
|
||||
// slack.New("YOUR_TOKEN_HERE", slack.OptionDebug(true))
|
||||
groups, err := api.GetGroups(false)
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
return
|
||||
}
|
||||
for _, group := range groups {
|
||||
fmt.Printf("ID: %s, Name: %s\n", group.ID, group.Name)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Getting User Information
|
||||
|
||||
```golang
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/slack-go/slack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
api := slack.New("YOUR_TOKEN_HERE")
|
||||
user, err := api.GetUserInfo("U023BECGF")
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("ID: %s, Fullname: %s, Email: %s\n", user.ID, user.Profile.RealName, user.Profile.Email)
|
||||
}
|
||||
```
|
||||
|
||||
## Minimal RTM usage:
|
||||
|
||||
See https://github.com/slack-go/slack/blob/master/examples/websocket/websocket.go
|
||||
|
||||
|
||||
## Minimal EventsAPI usage:
|
||||
|
||||
See https://github.com/slack-go/slack/blob/master/examples/eventsapi/events.go
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
You are more than welcome to contribute to this project. Fork and
|
||||
make a Pull Request, or create an Issue if you see any problem.
|
||||
|
||||
Before making any Pull Request please run the following:
|
||||
|
||||
```
|
||||
make pr-prep
|
||||
```
|
||||
|
||||
This will check/update code formatting, linting and then run all tests
|
||||
|
||||
## License
|
||||
|
||||
BSD 2 Clause license
|
3
vendor/github.com/slack-go/slack/TODO.txt
generated
vendored
Normal file
3
vendor/github.com/slack-go/slack/TODO.txt
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
- Add more tests!!!
|
||||
- Add support to have markdown hints
|
||||
- See section Message Formatting at https://api.slack.com/docs/formatting
|
207
vendor/github.com/slack-go/slack/admin.go
generated
vendored
Normal file
207
vendor/github.com/slack-go/slack/admin.go
generated
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (api *Client) adminRequest(ctx context.Context, method string, teamName string, values url.Values) error {
|
||||
resp := &SlackResponse{}
|
||||
err := parseAdminResponse(ctx, api.httpclient, method, teamName, values, resp, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return resp.Err()
|
||||
}
|
||||
|
||||
// DisableUser disabled a user account, given a user ID
|
||||
func (api *Client) DisableUser(teamName string, uid string) error {
|
||||
return api.DisableUserContext(context.Background(), teamName, uid)
|
||||
}
|
||||
|
||||
// DisableUserContext disabled a user account, given a user ID with a custom context
|
||||
func (api *Client) DisableUserContext(ctx context.Context, teamName string, uid string) error {
|
||||
values := url.Values{
|
||||
"user": {uid},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
if err := api.adminRequest(ctx, "setInactive", teamName, values); err != nil {
|
||||
return fmt.Errorf("failed to disable user with id '%s': %s", uid, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InviteGuest invites a user to Slack as a single-channel guest
|
||||
func (api *Client) InviteGuest(teamName, channel, firstName, lastName, emailAddress string) error {
|
||||
return api.InviteGuestContext(context.Background(), teamName, channel, firstName, lastName, emailAddress)
|
||||
}
|
||||
|
||||
// InviteGuestContext invites a user to Slack as a single-channel guest with a custom context
|
||||
func (api *Client) InviteGuestContext(ctx context.Context, teamName, channel, firstName, lastName, emailAddress string) error {
|
||||
values := url.Values{
|
||||
"email": {emailAddress},
|
||||
"channels": {channel},
|
||||
"first_name": {firstName},
|
||||
"last_name": {lastName},
|
||||
"ultra_restricted": {"1"},
|
||||
"token": {api.token},
|
||||
"resend": {"true"},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
err := api.adminRequest(ctx, "invite", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to invite single-channel guest: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InviteRestricted invites a user to Slack as a restricted account
|
||||
func (api *Client) InviteRestricted(teamName, channel, firstName, lastName, emailAddress string) error {
|
||||
return api.InviteRestrictedContext(context.Background(), teamName, channel, firstName, lastName, emailAddress)
|
||||
}
|
||||
|
||||
// InviteRestrictedContext invites a user to Slack as a restricted account with a custom context
|
||||
func (api *Client) InviteRestrictedContext(ctx context.Context, teamName, channel, firstName, lastName, emailAddress string) error {
|
||||
values := url.Values{
|
||||
"email": {emailAddress},
|
||||
"channels": {channel},
|
||||
"first_name": {firstName},
|
||||
"last_name": {lastName},
|
||||
"restricted": {"1"},
|
||||
"token": {api.token},
|
||||
"resend": {"true"},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
err := api.adminRequest(ctx, "invite", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to restricted account: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InviteToTeam invites a user to a Slack team
|
||||
func (api *Client) InviteToTeam(teamName, firstName, lastName, emailAddress string) error {
|
||||
return api.InviteToTeamContext(context.Background(), teamName, firstName, lastName, emailAddress)
|
||||
}
|
||||
|
||||
// InviteToTeamContext invites a user to a Slack team with a custom context
|
||||
func (api *Client) InviteToTeamContext(ctx context.Context, teamName, firstName, lastName, emailAddress string) error {
|
||||
values := url.Values{
|
||||
"email": {emailAddress},
|
||||
"first_name": {firstName},
|
||||
"last_name": {lastName},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
err := api.adminRequest(ctx, "invite", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to invite to team: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetRegular enables the specified user
|
||||
func (api *Client) SetRegular(teamName, user string) error {
|
||||
return api.SetRegularContext(context.Background(), teamName, user)
|
||||
}
|
||||
|
||||
// SetRegularContext enables the specified user with a custom context
|
||||
func (api *Client) SetRegularContext(ctx context.Context, teamName, user string) error {
|
||||
values := url.Values{
|
||||
"user": {user},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
err := api.adminRequest(ctx, "setRegular", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to change the user (%s) to a regular user: %s", user, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendSSOBindingEmail sends an SSO binding email to the specified user
|
||||
func (api *Client) SendSSOBindingEmail(teamName, user string) error {
|
||||
return api.SendSSOBindingEmailContext(context.Background(), teamName, user)
|
||||
}
|
||||
|
||||
// SendSSOBindingEmailContext sends an SSO binding email to the specified user with a custom context
|
||||
func (api *Client) SendSSOBindingEmailContext(ctx context.Context, teamName, user string) error {
|
||||
values := url.Values{
|
||||
"user": {user},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
err := api.adminRequest(ctx, "sendSSOBind", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to send SSO binding email for user (%s): %s", user, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUltraRestricted converts a user into a single-channel guest
|
||||
func (api *Client) SetUltraRestricted(teamName, uid, channel string) error {
|
||||
return api.SetUltraRestrictedContext(context.Background(), teamName, uid, channel)
|
||||
}
|
||||
|
||||
// SetUltraRestrictedContext converts a user into a single-channel guest with a custom context
|
||||
func (api *Client) SetUltraRestrictedContext(ctx context.Context, teamName, uid, channel string) error {
|
||||
values := url.Values{
|
||||
"user": {uid},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
err := api.adminRequest(ctx, "setUltraRestricted", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to ultra-restrict account: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetRestricted converts a user into a restricted account
|
||||
func (api *Client) SetRestricted(teamName, uid string, channelIds ...string) error {
|
||||
return api.SetRestrictedContext(context.Background(), teamName, uid, channelIds...)
|
||||
}
|
||||
|
||||
// SetRestrictedContext converts a user into a restricted account with a custom context
|
||||
func (api *Client) SetRestrictedContext(ctx context.Context, teamName, uid string, channelIds ...string) error {
|
||||
values := url.Values{
|
||||
"user": {uid},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
"channels": {strings.Join(channelIds, ",")},
|
||||
}
|
||||
|
||||
err := api.adminRequest(ctx, "setRestricted", teamName, values)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to restrict account: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
93
vendor/github.com/slack-go/slack/attachments.go
generated
vendored
Normal file
93
vendor/github.com/slack-go/slack/attachments.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
package slack
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// AttachmentField contains information for an attachment field
|
||||
// An Attachment can contain multiple of these
|
||||
type AttachmentField struct {
|
||||
Title string `json:"title"`
|
||||
Value string `json:"value"`
|
||||
Short bool `json:"short"`
|
||||
}
|
||||
|
||||
// AttachmentAction is a button or menu to be included in the attachment. Required when
|
||||
// using message buttons or menus and otherwise not useful. A maximum of 5 actions may be
|
||||
// provided per attachment.
|
||||
type AttachmentAction struct {
|
||||
Name string `json:"name"` // Required.
|
||||
Text string `json:"text"` // Required.
|
||||
Style string `json:"style,omitempty"` // Optional. Allowed values: "default", "primary", "danger".
|
||||
Type actionType `json:"type"` // Required. Must be set to "button" or "select".
|
||||
Value string `json:"value,omitempty"` // Optional.
|
||||
DataSource string `json:"data_source,omitempty"` // Optional.
|
||||
MinQueryLength int `json:"min_query_length,omitempty"` // Optional. Default value is 1.
|
||||
Options []AttachmentActionOption `json:"options,omitempty"` // Optional. Maximum of 100 options can be provided in each menu.
|
||||
SelectedOptions []AttachmentActionOption `json:"selected_options,omitempty"` // Optional. The first element of this array will be set as the pre-selected option for this menu.
|
||||
OptionGroups []AttachmentActionOptionGroup `json:"option_groups,omitempty"` // Optional.
|
||||
Confirm *ConfirmationField `json:"confirm,omitempty"` // Optional.
|
||||
URL string `json:"url,omitempty"` // Optional.
|
||||
}
|
||||
|
||||
// actionType returns the type of the action
|
||||
func (a AttachmentAction) actionType() actionType {
|
||||
return a.Type
|
||||
}
|
||||
|
||||
// AttachmentActionOption the individual option to appear in action menu.
|
||||
type AttachmentActionOption struct {
|
||||
Text string `json:"text"` // Required.
|
||||
Value string `json:"value"` // Required.
|
||||
Description string `json:"description,omitempty"` // Optional. Up to 30 characters.
|
||||
}
|
||||
|
||||
// AttachmentActionOptionGroup is a semi-hierarchal way to list available options to appear in action menu.
|
||||
type AttachmentActionOptionGroup struct {
|
||||
Text string `json:"text"` // Required.
|
||||
Options []AttachmentActionOption `json:"options"` // Required.
|
||||
}
|
||||
|
||||
// AttachmentActionCallback is sent from Slack when a user clicks a button in an interactive message (aka AttachmentAction)
|
||||
// DEPRECATED: use InteractionCallback
|
||||
type AttachmentActionCallback InteractionCallback
|
||||
|
||||
// ConfirmationField are used to ask users to confirm actions
|
||||
type ConfirmationField struct {
|
||||
Title string `json:"title,omitempty"` // Optional.
|
||||
Text string `json:"text"` // Required.
|
||||
OkText string `json:"ok_text,omitempty"` // Optional. Defaults to "Okay"
|
||||
DismissText string `json:"dismiss_text,omitempty"` // Optional. Defaults to "Cancel"
|
||||
}
|
||||
|
||||
// Attachment contains all the information for an attachment
|
||||
type Attachment struct {
|
||||
Color string `json:"color,omitempty"`
|
||||
Fallback string `json:"fallback,omitempty"`
|
||||
|
||||
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"`
|
||||
AuthorIcon string `json:"author_icon,omitempty"`
|
||||
|
||||
Title string `json:"title,omitempty"`
|
||||
TitleLink string `json:"title_link,omitempty"`
|
||||
Pretext string `json:"pretext,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
|
||||
ImageURL string `json:"image_url,omitempty"`
|
||||
ThumbURL string `json:"thumb_url,omitempty"`
|
||||
|
||||
Fields []AttachmentField `json:"fields,omitempty"`
|
||||
Actions []AttachmentAction `json:"actions,omitempty"`
|
||||
MarkdownIn []string `json:"mrkdwn_in,omitempty"`
|
||||
|
||||
Blocks Blocks `json:"blocks,omitempty"`
|
||||
|
||||
Footer string `json:"footer,omitempty"`
|
||||
FooterIcon string `json:"footer_icon,omitempty"`
|
||||
|
||||
Ts json.Number `json:"ts,omitempty"`
|
||||
}
|
40
vendor/github.com/slack-go/slack/auth.go
generated
vendored
Normal file
40
vendor/github.com/slack-go/slack/auth.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// AuthRevokeResponse contains our Auth response from the auth.revoke endpoint
|
||||
type AuthRevokeResponse struct {
|
||||
SlackResponse // Contains the "ok", and "Error", if any
|
||||
Revoked bool `json:"revoked,omitempty"`
|
||||
}
|
||||
|
||||
// authRequest sends the actual request, and unmarshals the response
|
||||
func (api *Client) authRequest(ctx context.Context, path string, values url.Values) (*AuthRevokeResponse, error) {
|
||||
response := &AuthRevokeResponse{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// SendAuthRevoke will send a revocation for our token
|
||||
func (api *Client) SendAuthRevoke(token string) (*AuthRevokeResponse, error) {
|
||||
return api.SendAuthRevokeContext(context.Background(), token)
|
||||
}
|
||||
|
||||
// SendAuthRevokeContext will retrieve the satus from api.test
|
||||
func (api *Client) SendAuthRevokeContext(ctx context.Context, token string) (*AuthRevokeResponse, error) {
|
||||
if token == "" {
|
||||
token = api.token
|
||||
}
|
||||
values := url.Values{
|
||||
"token": {token},
|
||||
}
|
||||
|
||||
return api.authRequest(ctx, "auth.revoke", values)
|
||||
}
|
57
vendor/github.com/slack-go/slack/backoff.go
generated
vendored
Normal file
57
vendor/github.com/slack-go/slack/backoff.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This one was ripped from https://github.com/jpillora/backoff/blob/master/backoff.go
|
||||
|
||||
// Backoff is a time.Duration counter. It starts at Min. After every
|
||||
// call to Duration() it is multiplied by Factor. It is capped at
|
||||
// Max. It returns to Min on every call to Reset(). Used in
|
||||
// conjunction with the time package.
|
||||
type backoff struct {
|
||||
attempts int
|
||||
// Initial value to scale out
|
||||
Initial time.Duration
|
||||
// Jitter value randomizes an additional delay between 0 and Jitter
|
||||
Jitter time.Duration
|
||||
// Max maximum values of the backoff
|
||||
Max time.Duration
|
||||
}
|
||||
|
||||
// Returns the current value of the counter and then multiplies it
|
||||
// Factor
|
||||
func (b *backoff) Duration() (dur time.Duration) {
|
||||
// Zero-values are nonsensical, so we use
|
||||
// them to apply defaults
|
||||
if b.Max == 0 {
|
||||
b.Max = 10 * time.Second
|
||||
}
|
||||
|
||||
if b.Initial == 0 {
|
||||
b.Initial = 100 * time.Millisecond
|
||||
}
|
||||
|
||||
// calculate this duration
|
||||
if dur = time.Duration(1 << uint(b.attempts)); dur > 0 {
|
||||
dur = dur * b.Initial
|
||||
} else {
|
||||
dur = b.Max
|
||||
}
|
||||
|
||||
if b.Jitter > 0 {
|
||||
dur = dur + time.Duration(rand.Intn(int(b.Jitter)))
|
||||
}
|
||||
|
||||
// bump attempts count
|
||||
b.attempts++
|
||||
|
||||
return dur
|
||||
}
|
||||
|
||||
//Resets the current value of the counter back to Min
|
||||
func (b *backoff) Reset() {
|
||||
b.attempts = 0
|
||||
}
|
73
vendor/github.com/slack-go/slack/block.go
generated
vendored
Normal file
73
vendor/github.com/slack-go/slack/block.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
package slack
|
||||
|
||||
// @NOTE: Blocks are in beta and subject to change.
|
||||
|
||||
// More Information: https://api.slack.com/block-kit
|
||||
|
||||
// MessageBlockType defines a named string type to define each block type
|
||||
// as a constant for use within the package.
|
||||
type MessageBlockType string
|
||||
|
||||
const (
|
||||
MBTSection MessageBlockType = "section"
|
||||
MBTDivider MessageBlockType = "divider"
|
||||
MBTImage MessageBlockType = "image"
|
||||
MBTAction MessageBlockType = "actions"
|
||||
MBTContext MessageBlockType = "context"
|
||||
MBTInput MessageBlockType = "input"
|
||||
)
|
||||
|
||||
// Block defines an interface all block types should implement
|
||||
// to ensure consistency between blocks.
|
||||
type Block interface {
|
||||
BlockType() MessageBlockType
|
||||
}
|
||||
|
||||
// Blocks is a convenience struct defined to allow dynamic unmarshalling of
|
||||
// the "blocks" value in Slack's JSON response, which varies depending on block type
|
||||
type Blocks struct {
|
||||
BlockSet []Block `json:"blocks,omitempty"`
|
||||
}
|
||||
|
||||
// BlockAction is the action callback sent when a block is interacted with
|
||||
type BlockAction struct {
|
||||
ActionID string `json:"action_id"`
|
||||
BlockID string `json:"block_id"`
|
||||
Type actionType `json:"type"`
|
||||
Text TextBlockObject `json:"text"`
|
||||
Value string `json:"value"`
|
||||
ActionTs string `json:"action_ts"`
|
||||
SelectedOption OptionBlockObject `json:"selected_option"`
|
||||
SelectedOptions []OptionBlockObject `json:"selected_options"`
|
||||
SelectedUser string `json:"selected_user"`
|
||||
SelectedChannel string `json:"selected_channel"`
|
||||
SelectedConversation string `json:"selected_conversation"`
|
||||
SelectedDate string `json:"selected_date"`
|
||||
InitialOption OptionBlockObject `json:"initial_option"`
|
||||
InitialUser string `json:"initial_user"`
|
||||
InitialChannel string `json:"initial_channel"`
|
||||
InitialConversation string `json:"initial_conversation"`
|
||||
InitialDate string `json:"initial_date"`
|
||||
}
|
||||
|
||||
// actionType returns the type of the action
|
||||
func (b BlockAction) actionType() actionType {
|
||||
return b.Type
|
||||
}
|
||||
|
||||
// NewBlockMessage creates a new Message that contains one or more blocks to be displayed
|
||||
func NewBlockMessage(blocks ...Block) Message {
|
||||
return Message{
|
||||
Msg: Msg{
|
||||
Blocks: Blocks{
|
||||
BlockSet: blocks,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// AddBlockMessage appends a block to the end of the existing list of blocks
|
||||
func AddBlockMessage(message Message, newBlk Block) Message {
|
||||
message.Msg.Blocks.BlockSet = append(message.Msg.Blocks.BlockSet, newBlk)
|
||||
return message
|
||||
}
|
26
vendor/github.com/slack-go/slack/block_action.go
generated
vendored
Normal file
26
vendor/github.com/slack-go/slack/block_action.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package slack
|
||||
|
||||
// ActionBlock defines data that is used to hold interactive elements.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/blocks#actions
|
||||
type ActionBlock struct {
|
||||
Type MessageBlockType `json:"type"`
|
||||
BlockID string `json:"block_id,omitempty"`
|
||||
Elements BlockElements `json:"elements"`
|
||||
}
|
||||
|
||||
// BlockType returns the type of the block
|
||||
func (s ActionBlock) BlockType() MessageBlockType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// NewActionBlock returns a new instance of an Action Block
|
||||
func NewActionBlock(blockID string, elements ...BlockElement) *ActionBlock {
|
||||
return &ActionBlock{
|
||||
Type: MBTAction,
|
||||
BlockID: blockID,
|
||||
Elements: BlockElements{
|
||||
ElementSet: elements,
|
||||
},
|
||||
}
|
||||
}
|
32
vendor/github.com/slack-go/slack/block_context.go
generated
vendored
Normal file
32
vendor/github.com/slack-go/slack/block_context.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package slack
|
||||
|
||||
// ContextBlock defines data that is used to display message context, which can
|
||||
// include both images and text.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/blocks#actions
|
||||
type ContextBlock struct {
|
||||
Type MessageBlockType `json:"type"`
|
||||
BlockID string `json:"block_id,omitempty"`
|
||||
ContextElements ContextElements `json:"elements"`
|
||||
}
|
||||
|
||||
// BlockType returns the type of the block
|
||||
func (s ContextBlock) BlockType() MessageBlockType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
type ContextElements struct {
|
||||
Elements []MixedElement
|
||||
}
|
||||
|
||||
// NewContextBlock returns a new instance of a context block
|
||||
func NewContextBlock(blockID string, mixedElements ...MixedElement) *ContextBlock {
|
||||
elements := ContextElements{
|
||||
Elements: mixedElements,
|
||||
}
|
||||
return &ContextBlock{
|
||||
Type: MBTContext,
|
||||
BlockID: blockID,
|
||||
ContextElements: elements,
|
||||
}
|
||||
}
|
353
vendor/github.com/slack-go/slack/block_conv.go
generated
vendored
Normal file
353
vendor/github.com/slack-go/slack/block_conv.go
generated
vendored
Normal file
@ -0,0 +1,353 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type sumtype struct {
|
||||
TypeVal string `json:"type"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the Marshaller interface for Blocks so that any JSON
|
||||
// marshalling is delegated and proper type determination can be made before marshal
|
||||
func (b Blocks) MarshalJSON() ([]byte, error) {
|
||||
bytes, err := json.Marshal(b.BlockSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaller interface for Blocks, so that any JSON
|
||||
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
||||
func (b *Blocks) UnmarshalJSON(data []byte) error {
|
||||
var raw []json.RawMessage
|
||||
|
||||
if string(data) == "{}" {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := json.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var blocks Blocks
|
||||
for _, r := range raw {
|
||||
s := sumtype{}
|
||||
err := json.Unmarshal(r, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var blockType string
|
||||
if s.TypeVal != "" {
|
||||
blockType = s.TypeVal
|
||||
}
|
||||
|
||||
var block Block
|
||||
switch blockType {
|
||||
case "actions":
|
||||
block = &ActionBlock{}
|
||||
case "context":
|
||||
block = &ContextBlock{}
|
||||
case "divider":
|
||||
block = &DividerBlock{}
|
||||
case "image":
|
||||
block = &ImageBlock{}
|
||||
case "input":
|
||||
block = &InputBlock{}
|
||||
case "section":
|
||||
block = &SectionBlock{}
|
||||
case "rich_text":
|
||||
// for now ignore the (complex) content of rich_text blocks until we can fully support it
|
||||
continue
|
||||
case "file":
|
||||
// for now ignore the file blocks until we can fully support it
|
||||
continue
|
||||
default:
|
||||
return errors.New("unsupported block type")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(r, block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blocks.BlockSet = append(blocks.BlockSet, block)
|
||||
}
|
||||
|
||||
*b = blocks
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaller interface for InputBlock, so that any JSON
|
||||
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
||||
func (b *InputBlock) UnmarshalJSON(data []byte) error {
|
||||
type alias InputBlock
|
||||
a := struct {
|
||||
Element json.RawMessage `json:"element"`
|
||||
*alias
|
||||
}{
|
||||
alias: (*alias)(b),
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &a); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s := sumtype{}
|
||||
if err := json.Unmarshal(a.Element, &s); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var e BlockElement
|
||||
switch s.TypeVal {
|
||||
case "datepicker":
|
||||
e = &DatePickerBlockElement{}
|
||||
case "plain_text_input":
|
||||
e = &PlainTextInputBlockElement{}
|
||||
case "static_select", "external_select", "users_select", "conversations_select", "channels_select":
|
||||
e = &SelectBlockElement{}
|
||||
default:
|
||||
return errors.New("unsupported block element type")
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(a.Element, e); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Element = e
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the Marshaller interface for BlockElements so that any JSON
|
||||
// marshalling is delegated and proper type determination can be made before marshal
|
||||
func (b *BlockElements) MarshalJSON() ([]byte, error) {
|
||||
bytes, err := json.Marshal(b.ElementSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaller interface for BlockElements, so that any JSON
|
||||
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
||||
func (b *BlockElements) UnmarshalJSON(data []byte) error {
|
||||
var raw []json.RawMessage
|
||||
|
||||
if string(data) == "{}" {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := json.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var blockElements BlockElements
|
||||
for _, r := range raw {
|
||||
s := sumtype{}
|
||||
err := json.Unmarshal(r, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var blockElementType string
|
||||
if s.TypeVal != "" {
|
||||
blockElementType = s.TypeVal
|
||||
}
|
||||
|
||||
var blockElement BlockElement
|
||||
switch blockElementType {
|
||||
case "image":
|
||||
blockElement = &ImageBlockElement{}
|
||||
case "button":
|
||||
blockElement = &ButtonBlockElement{}
|
||||
case "overflow":
|
||||
blockElement = &OverflowBlockElement{}
|
||||
case "datepicker":
|
||||
blockElement = &DatePickerBlockElement{}
|
||||
case "plain_text_input":
|
||||
blockElement = &PlainTextInputBlockElement{}
|
||||
case "static_select", "external_select", "users_select", "conversations_select", "channels_select":
|
||||
blockElement = &SelectBlockElement{}
|
||||
default:
|
||||
return errors.New("unsupported block element type")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(r, blockElement)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockElements.ElementSet = append(blockElements.ElementSet, blockElement)
|
||||
}
|
||||
|
||||
*b = blockElements
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the Marshaller interface for Accessory so that any JSON
|
||||
// marshalling is delegated and proper type determination can be made before marshal
|
||||
func (a *Accessory) MarshalJSON() ([]byte, error) {
|
||||
bytes, err := json.Marshal(toBlockElement(a))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaller interface for Accessory, so that any JSON
|
||||
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
||||
func (a *Accessory) UnmarshalJSON(data []byte) error {
|
||||
var r json.RawMessage
|
||||
|
||||
if string(data) == "{\"accessory\":null}" {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := json.Unmarshal(data, &r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s := sumtype{}
|
||||
err = json.Unmarshal(r, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var blockElementType string
|
||||
if s.TypeVal != "" {
|
||||
blockElementType = s.TypeVal
|
||||
}
|
||||
|
||||
switch blockElementType {
|
||||
case "image":
|
||||
element, err := unmarshalBlockElement(r, &ImageBlockElement{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.ImageElement = element.(*ImageBlockElement)
|
||||
case "button":
|
||||
element, err := unmarshalBlockElement(r, &ButtonBlockElement{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.ButtonElement = element.(*ButtonBlockElement)
|
||||
case "overflow":
|
||||
element, err := unmarshalBlockElement(r, &OverflowBlockElement{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.OverflowElement = element.(*OverflowBlockElement)
|
||||
case "datepicker":
|
||||
element, err := unmarshalBlockElement(r, &DatePickerBlockElement{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.DatePickerElement = element.(*DatePickerBlockElement)
|
||||
case "static_select":
|
||||
element, err := unmarshalBlockElement(r, &SelectBlockElement{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.SelectElement = element.(*SelectBlockElement)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalBlockElement(r json.RawMessage, element BlockElement) (BlockElement, error) {
|
||||
err := json.Unmarshal(r, element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return element, nil
|
||||
}
|
||||
|
||||
func toBlockElement(element *Accessory) BlockElement {
|
||||
if element.ImageElement != nil {
|
||||
return element.ImageElement
|
||||
}
|
||||
if element.ButtonElement != nil {
|
||||
return element.ButtonElement
|
||||
}
|
||||
if element.OverflowElement != nil {
|
||||
return element.OverflowElement
|
||||
}
|
||||
if element.DatePickerElement != nil {
|
||||
return element.DatePickerElement
|
||||
}
|
||||
if element.SelectElement != nil {
|
||||
return element.SelectElement
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the Marshaller interface for ContextElements so that any JSON
|
||||
// marshalling is delegated and proper type determination can be made before marshal
|
||||
func (e *ContextElements) MarshalJSON() ([]byte, error) {
|
||||
bytes, err := json.Marshal(e.Elements)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaller interface for ContextElements, so that any JSON
|
||||
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
||||
func (e *ContextElements) UnmarshalJSON(data []byte) error {
|
||||
var raw []json.RawMessage
|
||||
|
||||
if string(data) == "{\"elements\":null}" {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := json.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range raw {
|
||||
s := sumtype{}
|
||||
err := json.Unmarshal(r, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var contextElementType string
|
||||
if s.TypeVal != "" {
|
||||
contextElementType = s.TypeVal
|
||||
}
|
||||
|
||||
switch contextElementType {
|
||||
case PlainTextType, MarkdownType:
|
||||
elem, err := unmarshalBlockObject(r, &TextBlockObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.Elements = append(e.Elements, elem.(*TextBlockObject))
|
||||
case "image":
|
||||
elem, err := unmarshalBlockElement(r, &ImageBlockElement{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.Elements = append(e.Elements, elem.(*ImageBlockElement))
|
||||
default:
|
||||
return errors.New("unsupported context element type")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
22
vendor/github.com/slack-go/slack/block_divider.go
generated
vendored
Normal file
22
vendor/github.com/slack-go/slack/block_divider.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
package slack
|
||||
|
||||
// DividerBlock for displaying a divider line between blocks (similar to <hr> tag in html)
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/blocks#divider
|
||||
type DividerBlock struct {
|
||||
Type MessageBlockType `json:"type"`
|
||||
BlockID string `json:"block_id,omitempty"`
|
||||
}
|
||||
|
||||
// BlockType returns the type of the block
|
||||
func (s DividerBlock) BlockType() MessageBlockType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// NewDividerBlock returns a new instance of a divider block
|
||||
func NewDividerBlock() *DividerBlock {
|
||||
return &DividerBlock{
|
||||
Type: MBTDivider,
|
||||
}
|
||||
|
||||
}
|
267
vendor/github.com/slack-go/slack/block_element.go
generated
vendored
Normal file
267
vendor/github.com/slack-go/slack/block_element.go
generated
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
package slack
|
||||
|
||||
// https://api.slack.com/reference/messaging/block-elements
|
||||
|
||||
const (
|
||||
METImage MessageElementType = "image"
|
||||
METButton MessageElementType = "button"
|
||||
METOverflow MessageElementType = "overflow"
|
||||
METDatepicker MessageElementType = "datepicker"
|
||||
METPlainTextInput MessageElementType = "plain_text_input"
|
||||
|
||||
MixedElementImage MixedElementType = "mixed_image"
|
||||
MixedElementText MixedElementType = "mixed_text"
|
||||
|
||||
OptTypeStatic string = "static_select"
|
||||
OptTypeExternal string = "external_select"
|
||||
OptTypeUser string = "users_select"
|
||||
OptTypeConversations string = "conversations_select"
|
||||
OptTypeChannels string = "channels_select"
|
||||
)
|
||||
|
||||
type MessageElementType string
|
||||
type MixedElementType string
|
||||
|
||||
// BlockElement defines an interface that all block element types should implement.
|
||||
type BlockElement interface {
|
||||
ElementType() MessageElementType
|
||||
}
|
||||
|
||||
type MixedElement interface {
|
||||
MixedElementType() MixedElementType
|
||||
}
|
||||
|
||||
type Accessory struct {
|
||||
ImageElement *ImageBlockElement
|
||||
ButtonElement *ButtonBlockElement
|
||||
OverflowElement *OverflowBlockElement
|
||||
DatePickerElement *DatePickerBlockElement
|
||||
SelectElement *SelectBlockElement
|
||||
}
|
||||
|
||||
// NewAccessory returns a new Accessory for a given block element
|
||||
func NewAccessory(element BlockElement) *Accessory {
|
||||
switch element.(type) {
|
||||
case *ImageBlockElement:
|
||||
return &Accessory{ImageElement: element.(*ImageBlockElement)}
|
||||
case *ButtonBlockElement:
|
||||
return &Accessory{ButtonElement: element.(*ButtonBlockElement)}
|
||||
case *OverflowBlockElement:
|
||||
return &Accessory{OverflowElement: element.(*OverflowBlockElement)}
|
||||
case *DatePickerBlockElement:
|
||||
return &Accessory{DatePickerElement: element.(*DatePickerBlockElement)}
|
||||
case *SelectBlockElement:
|
||||
return &Accessory{SelectElement: element.(*SelectBlockElement)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BlockElements is a convenience struct defined to allow dynamic unmarshalling of
|
||||
// the "elements" value in Slack's JSON response, which varies depending on BlockElement type
|
||||
type BlockElements struct {
|
||||
ElementSet []BlockElement `json:"elements,omitempty"`
|
||||
}
|
||||
|
||||
// ImageBlockElement An element to insert an image - this element can be used
|
||||
// in section and context blocks only. If you want a block with only an image
|
||||
// in it, you're looking for the image block.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/block-elements#image
|
||||
type ImageBlockElement struct {
|
||||
Type MessageElementType `json:"type"`
|
||||
ImageURL string `json:"image_url"`
|
||||
AltText string `json:"alt_text"`
|
||||
}
|
||||
|
||||
// ElementType returns the type of the Element
|
||||
func (s ImageBlockElement) ElementType() MessageElementType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
func (s ImageBlockElement) MixedElementType() MixedElementType {
|
||||
return MixedElementImage
|
||||
}
|
||||
|
||||
// NewImageBlockElement returns a new instance of an image block element
|
||||
func NewImageBlockElement(imageURL, altText string) *ImageBlockElement {
|
||||
return &ImageBlockElement{
|
||||
Type: METImage,
|
||||
ImageURL: imageURL,
|
||||
AltText: altText,
|
||||
}
|
||||
}
|
||||
|
||||
type Style string
|
||||
|
||||
const (
|
||||
StyleDefault Style = "default"
|
||||
StylePrimary Style = "primary"
|
||||
StyleDanger Style = "danger"
|
||||
)
|
||||
|
||||
// ButtonBlockElement defines an interactive element that inserts a button. The
|
||||
// button can be a trigger for anything from opening a simple link to starting
|
||||
// a complex workflow.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/block-elements#button
|
||||
type ButtonBlockElement struct {
|
||||
Type MessageElementType `json:"type,omitempty"`
|
||||
Text *TextBlockObject `json:"text"`
|
||||
ActionID string `json:"action_id,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
|
||||
Style Style `json:"style,omitempty"`
|
||||
}
|
||||
|
||||
// ElementType returns the type of the element
|
||||
func (s ButtonBlockElement) ElementType() MessageElementType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// add styling to button object
|
||||
func (s *ButtonBlockElement) WithStyle(style Style) {
|
||||
s.Style = style
|
||||
}
|
||||
|
||||
// NewButtonBlockElement returns an instance of a new button element to be used within a block
|
||||
func NewButtonBlockElement(actionID, value string, text *TextBlockObject) *ButtonBlockElement {
|
||||
return &ButtonBlockElement{
|
||||
Type: METButton,
|
||||
ActionID: actionID,
|
||||
Text: text,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// SelectBlockElement defines the simplest form of select menu, with a static list
|
||||
// of options passed in when defining the element.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/block-elements#select
|
||||
type SelectBlockElement struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Placeholder *TextBlockObject `json:"placeholder,omitempty"`
|
||||
ActionID string `json:"action_id,omitempty"`
|
||||
Options []*OptionBlockObject `json:"options,omitempty"`
|
||||
OptionGroups []*OptionGroupBlockObject `json:"option_groups,omitempty"`
|
||||
InitialOption *OptionBlockObject `json:"initial_option,omitempty"`
|
||||
InitialUser string `json:"initial_user,omitempty"`
|
||||
InitialConversation string `json:"initial_conversation,omitempty"`
|
||||
InitialChannel string `json:"initial_channel,omitempty"`
|
||||
MinQueryLength int `json:"min_query_length,omitempty"`
|
||||
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
|
||||
}
|
||||
|
||||
// ElementType returns the type of the Element
|
||||
func (s SelectBlockElement) ElementType() MessageElementType {
|
||||
return MessageElementType(s.Type)
|
||||
}
|
||||
|
||||
// NewOptionsSelectBlockElement returns a new instance of SelectBlockElement for use with
|
||||
// the Options object only.
|
||||
func NewOptionsSelectBlockElement(optType string, placeholder *TextBlockObject, actionID string, options ...*OptionBlockObject) *SelectBlockElement {
|
||||
return &SelectBlockElement{
|
||||
Type: optType,
|
||||
Placeholder: placeholder,
|
||||
ActionID: actionID,
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// NewOptionsGroupSelectBlockElement returns a new instance of SelectBlockElement for use with
|
||||
// the Options object only.
|
||||
func NewOptionsGroupSelectBlockElement(
|
||||
optType string,
|
||||
placeholder *TextBlockObject,
|
||||
actionID string,
|
||||
optGroups ...*OptionGroupBlockObject,
|
||||
) *SelectBlockElement {
|
||||
return &SelectBlockElement{
|
||||
Type: optType,
|
||||
Placeholder: placeholder,
|
||||
ActionID: actionID,
|
||||
OptionGroups: optGroups,
|
||||
}
|
||||
}
|
||||
|
||||
// OverflowBlockElement defines the fields needed to use an overflow element.
|
||||
// And Overflow Element is like a cross between a button and a select menu -
|
||||
// when a user clicks on this overflow button, they will be presented with a
|
||||
// list of options to choose from.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/block-elements#overflow
|
||||
type OverflowBlockElement struct {
|
||||
Type MessageElementType `json:"type"`
|
||||
ActionID string `json:"action_id,omitempty"`
|
||||
Options []*OptionBlockObject `json:"options"`
|
||||
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
|
||||
}
|
||||
|
||||
// ElementType returns the type of the Element
|
||||
func (s OverflowBlockElement) ElementType() MessageElementType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// NewOverflowBlockElement returns an instance of a new Overflow Block Element
|
||||
func NewOverflowBlockElement(actionID string, options ...*OptionBlockObject) *OverflowBlockElement {
|
||||
return &OverflowBlockElement{
|
||||
Type: METOverflow,
|
||||
ActionID: actionID,
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// DatePickerBlockElement defines an element which lets users easily select a
|
||||
// date from a calendar style UI. Date picker elements can be used inside of
|
||||
// section and actions blocks.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/block-elements#datepicker
|
||||
type DatePickerBlockElement struct {
|
||||
Type MessageElementType `json:"type"`
|
||||
ActionID string `json:"action_id"`
|
||||
Placeholder *TextBlockObject `json:"placeholder,omitempty"`
|
||||
InitialDate string `json:"initial_date,omitempty"`
|
||||
Confirm *ConfirmationBlockObject `json:"confirm,omitempty"`
|
||||
}
|
||||
|
||||
// ElementType returns the type of the Element
|
||||
func (s DatePickerBlockElement) ElementType() MessageElementType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// NewDatePickerBlockElement returns an instance of a date picker element
|
||||
func NewDatePickerBlockElement(actionID string) *DatePickerBlockElement {
|
||||
return &DatePickerBlockElement{
|
||||
Type: METDatepicker,
|
||||
ActionID: actionID,
|
||||
}
|
||||
}
|
||||
|
||||
// PlainTextInputBlockElement creates a field where a user can enter freeform data.
|
||||
// Plain-text input elements are currently only available in modals.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/block-elements#input
|
||||
type PlainTextInputBlockElement struct {
|
||||
Type MessageElementType `json:"type"`
|
||||
ActionID string `json:"action_id"`
|
||||
Placeholder *TextBlockObject `json:"placeholder,omitempty"`
|
||||
InitialValue string `json:"initial_value,omitempty"`
|
||||
Multiline bool `json:"multiline,omitempty"`
|
||||
MinLength int `json:"min_length,omitempty"`
|
||||
MaxLength int `json:"max_length,omitempty"`
|
||||
}
|
||||
|
||||
// ElementType returns the type of the Element
|
||||
func (s PlainTextInputBlockElement) ElementType() MessageElementType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// NewPlainTextInputBlockElement returns an instance of a plain-text input element
|
||||
func NewPlainTextInputBlockElement(placeholder *TextBlockObject, actionID string) *PlainTextInputBlockElement {
|
||||
return &PlainTextInputBlockElement{
|
||||
Type: METPlainTextInput,
|
||||
ActionID: actionID,
|
||||
Placeholder: placeholder,
|
||||
}
|
||||
}
|
28
vendor/github.com/slack-go/slack/block_image.go
generated
vendored
Normal file
28
vendor/github.com/slack-go/slack/block_image.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package slack
|
||||
|
||||
// ImageBlock defines data required to display an image as a block element
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/blocks#image
|
||||
type ImageBlock struct {
|
||||
Type MessageBlockType `json:"type"`
|
||||
ImageURL string `json:"image_url"`
|
||||
AltText string `json:"alt_text"`
|
||||
BlockID string `json:"block_id,omitempty"`
|
||||
Title *TextBlockObject `json:"title"`
|
||||
}
|
||||
|
||||
// BlockType returns the type of the block
|
||||
func (s ImageBlock) BlockType() MessageBlockType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// NewImageBlock returns an instance of a new Image Block type
|
||||
func NewImageBlock(imageURL, altText, blockID string, title *TextBlockObject) *ImageBlock {
|
||||
return &ImageBlock{
|
||||
Type: MBTImage,
|
||||
ImageURL: imageURL,
|
||||
AltText: altText,
|
||||
BlockID: blockID,
|
||||
Title: title,
|
||||
}
|
||||
}
|
30
vendor/github.com/slack-go/slack/block_input.go
generated
vendored
Normal file
30
vendor/github.com/slack-go/slack/block_input.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package slack
|
||||
|
||||
// InputBlock defines data that is used to collect information from users -
|
||||
// it can hold a plain-text input element, a select menu element,
|
||||
// a multi-select menu element, or a datepicker.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/blocks#input
|
||||
type InputBlock struct {
|
||||
Type MessageBlockType `json:"type"`
|
||||
BlockID string `json:"block_id,omitempty"`
|
||||
Label *TextBlockObject `json:"label"`
|
||||
Element BlockElement `json:"element"`
|
||||
Hint *TextBlockObject `json:"hint,omitempty"`
|
||||
Optional bool `json:"optional,omitempty"`
|
||||
}
|
||||
|
||||
// BlockType returns the type of the block
|
||||
func (s InputBlock) BlockType() MessageBlockType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// NewInputBlock returns a new instance of an Input Block
|
||||
func NewInputBlock(blockID string, label *TextBlockObject, element BlockElement) *InputBlock {
|
||||
return &InputBlock{
|
||||
Type: MBTInput,
|
||||
BlockID: blockID,
|
||||
Label: label,
|
||||
Element: element,
|
||||
}
|
||||
}
|
216
vendor/github.com/slack-go/slack/block_object.go
generated
vendored
Normal file
216
vendor/github.com/slack-go/slack/block_object.go
generated
vendored
Normal file
@ -0,0 +1,216 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Block Objects are also known as Composition Objects
|
||||
//
|
||||
// For more information: https://api.slack.com/reference/messaging/composition-objects
|
||||
|
||||
// BlockObject defines an interface that all block object types should
|
||||
// implement.
|
||||
// @TODO: Is this interface needed?
|
||||
|
||||
// blockObject object types
|
||||
const (
|
||||
MarkdownType = "mrkdwn"
|
||||
PlainTextType = "plain_text"
|
||||
// The following objects don't actually have types and their corresponding
|
||||
// const values are just for internal use
|
||||
motConfirmation = "confirm"
|
||||
motOption = "option"
|
||||
motOptionGroup = "option_group"
|
||||
)
|
||||
|
||||
type MessageObjectType string
|
||||
|
||||
type blockObject interface {
|
||||
validateType() MessageObjectType
|
||||
}
|
||||
|
||||
type BlockObjects struct {
|
||||
TextObjects []*TextBlockObject
|
||||
ConfirmationObjects []*ConfirmationBlockObject
|
||||
OptionObjects []*OptionBlockObject
|
||||
OptionGroupObjects []*OptionGroupBlockObject
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaller interface for BlockObjects, so that any JSON
|
||||
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
||||
func (b *BlockObjects) UnmarshalJSON(data []byte) error {
|
||||
var raw []json.RawMessage
|
||||
err := json.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range raw {
|
||||
var obj map[string]interface{}
|
||||
err := json.Unmarshal(r, &obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockObjectType := getBlockObjectType(obj)
|
||||
|
||||
switch blockObjectType {
|
||||
case PlainTextType, MarkdownType:
|
||||
object, err := unmarshalBlockObject(r, &TextBlockObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.TextObjects = append(b.TextObjects, object.(*TextBlockObject))
|
||||
case motConfirmation:
|
||||
object, err := unmarshalBlockObject(r, &ConfirmationBlockObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.ConfirmationObjects = append(b.ConfirmationObjects, object.(*ConfirmationBlockObject))
|
||||
case motOption:
|
||||
object, err := unmarshalBlockObject(r, &OptionBlockObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.OptionObjects = append(b.OptionObjects, object.(*OptionBlockObject))
|
||||
case motOptionGroup:
|
||||
object, err := unmarshalBlockObject(r, &OptionGroupBlockObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.OptionGroupObjects = append(b.OptionGroupObjects, object.(*OptionGroupBlockObject))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ideally would have a better way to identify the block objects for
|
||||
// type casting at time of unmarshalling, should be adapted if possible
|
||||
// to accomplish in a more reliable manner.
|
||||
func getBlockObjectType(obj map[string]interface{}) string {
|
||||
if t, ok := obj["type"].(string); ok {
|
||||
return t
|
||||
}
|
||||
if _, ok := obj["confirm"].(string); ok {
|
||||
return "confirm"
|
||||
}
|
||||
if _, ok := obj["options"].(string); ok {
|
||||
return "option_group"
|
||||
}
|
||||
if _, ok := obj["text"].(string); ok {
|
||||
if _, ok := obj["value"].(string); ok {
|
||||
return "option"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func unmarshalBlockObject(r json.RawMessage, object blockObject) (blockObject, error) {
|
||||
err := json.Unmarshal(r, object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return object, nil
|
||||
}
|
||||
|
||||
// TextBlockObject defines a text element object to be used with blocks
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/composition-objects#text
|
||||
type TextBlockObject struct {
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text"`
|
||||
Emoji bool `json:"emoji,omitempty"`
|
||||
Verbatim bool `json:"verbatim,omitempty"`
|
||||
}
|
||||
|
||||
// validateType enforces block objects for element and block parameters
|
||||
func (s TextBlockObject) validateType() MessageObjectType {
|
||||
return MessageObjectType(s.Type)
|
||||
}
|
||||
|
||||
// validateType enforces block objects for element and block parameters
|
||||
func (s TextBlockObject) MixedElementType() MixedElementType {
|
||||
return MixedElementText
|
||||
}
|
||||
|
||||
// NewTextBlockObject returns an instance of a new Text Block Object
|
||||
func NewTextBlockObject(elementType, text string, emoji, verbatim bool) *TextBlockObject {
|
||||
return &TextBlockObject{
|
||||
Type: elementType,
|
||||
Text: text,
|
||||
Emoji: emoji,
|
||||
Verbatim: verbatim,
|
||||
}
|
||||
}
|
||||
|
||||
// ConfirmationBlockObject defines a dialog that provides a confirmation step to
|
||||
// any interactive element. This dialog will ask the user to confirm their action by
|
||||
// offering a confirm and deny buttons.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/composition-objects#confirm
|
||||
type ConfirmationBlockObject struct {
|
||||
Title *TextBlockObject `json:"title"`
|
||||
Text *TextBlockObject `json:"text"`
|
||||
Confirm *TextBlockObject `json:"confirm"`
|
||||
Deny *TextBlockObject `json:"deny"`
|
||||
}
|
||||
|
||||
// validateType enforces block objects for element and block parameters
|
||||
func (s ConfirmationBlockObject) validateType() MessageObjectType {
|
||||
return motConfirmation
|
||||
}
|
||||
|
||||
// NewConfirmationBlockObject returns an instance of a new Confirmation Block Object
|
||||
func NewConfirmationBlockObject(title, text, confirm, deny *TextBlockObject) *ConfirmationBlockObject {
|
||||
return &ConfirmationBlockObject{
|
||||
Title: title,
|
||||
Text: text,
|
||||
Confirm: confirm,
|
||||
Deny: deny,
|
||||
}
|
||||
}
|
||||
|
||||
// OptionBlockObject represents a single selectable item in a select menu
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/composition-objects#option
|
||||
type OptionBlockObject struct {
|
||||
Text *TextBlockObject `json:"text"`
|
||||
Value string `json:"value"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
// NewOptionBlockObject returns an instance of a new Option Block Element
|
||||
func NewOptionBlockObject(value string, text *TextBlockObject) *OptionBlockObject {
|
||||
return &OptionBlockObject{
|
||||
Text: text,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// validateType enforces block objects for element and block parameters
|
||||
func (s OptionBlockObject) validateType() MessageObjectType {
|
||||
return motOption
|
||||
}
|
||||
|
||||
// OptionGroupBlockObject Provides a way to group options in a select menu.
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/composition-objects#option-group
|
||||
type OptionGroupBlockObject struct {
|
||||
Label *TextBlockObject `json:"label,omitempty"`
|
||||
Options []*OptionBlockObject `json:"options"`
|
||||
}
|
||||
|
||||
// validateType enforces block objects for element and block parameters
|
||||
func (s OptionGroupBlockObject) validateType() MessageObjectType {
|
||||
return motOptionGroup
|
||||
}
|
||||
|
||||
// NewOptionGroupBlockElement returns an instance of a new option group block element
|
||||
func NewOptionGroupBlockElement(label *TextBlockObject, options ...*OptionBlockObject) *OptionGroupBlockObject {
|
||||
return &OptionGroupBlockObject{
|
||||
Label: label,
|
||||
Options: options,
|
||||
}
|
||||
}
|
42
vendor/github.com/slack-go/slack/block_section.go
generated
vendored
Normal file
42
vendor/github.com/slack-go/slack/block_section.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
package slack
|
||||
|
||||
// SectionBlock defines a new block of type section
|
||||
//
|
||||
// More Information: https://api.slack.com/reference/messaging/blocks#section
|
||||
type SectionBlock struct {
|
||||
Type MessageBlockType `json:"type"`
|
||||
Text *TextBlockObject `json:"text,omitempty"`
|
||||
BlockID string `json:"block_id,omitempty"`
|
||||
Fields []*TextBlockObject `json:"fields,omitempty"`
|
||||
Accessory *Accessory `json:"accessory,omitempty"`
|
||||
}
|
||||
|
||||
// BlockType returns the type of the block
|
||||
func (s SectionBlock) BlockType() MessageBlockType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// SectionBlockOption allows configuration of options for a new section block
|
||||
type SectionBlockOption func(*SectionBlock)
|
||||
|
||||
func SectionBlockOptionBlockID(blockID string) SectionBlockOption {
|
||||
return func(block *SectionBlock) {
|
||||
block.BlockID = blockID
|
||||
}
|
||||
}
|
||||
|
||||
// NewSectionBlock returns a new instance of a section block to be rendered
|
||||
func NewSectionBlock(textObj *TextBlockObject, fields []*TextBlockObject, accessory *Accessory, options ...SectionBlockOption) *SectionBlock {
|
||||
block := SectionBlock{
|
||||
Type: MBTSection,
|
||||
Text: textObj,
|
||||
Fields: fields,
|
||||
Accessory: accessory,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(&block)
|
||||
}
|
||||
|
||||
return &block
|
||||
}
|
58
vendor/github.com/slack-go/slack/bots.go
generated
vendored
Normal file
58
vendor/github.com/slack-go/slack/bots.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Bot contains information about a bot
|
||||
type Bot struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Deleted bool `json:"deleted"`
|
||||
UserID string `json:"user_id"`
|
||||
AppID string `json:"app_id"`
|
||||
Updated JSONTime `json:"updated"`
|
||||
Icons Icons `json:"icons"`
|
||||
}
|
||||
|
||||
type botResponseFull struct {
|
||||
Bot `json:"bot,omitempty"` // GetBotInfo
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func (api *Client) botRequest(ctx context.Context, path string, values url.Values) (*botResponseFull, error) {
|
||||
response := &botResponseFull{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// GetBotInfo will retrieve the complete bot information
|
||||
func (api *Client) GetBotInfo(bot string) (*Bot, error) {
|
||||
return api.GetBotInfoContext(context.Background(), bot)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
|
||||
if bot != "" {
|
||||
values.Add("bot", bot)
|
||||
}
|
||||
|
||||
response, err := api.botRequest(ctx, "bots.info", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Bot, nil
|
||||
}
|
412
vendor/github.com/slack-go/slack/channels.go
generated
vendored
Normal file
412
vendor/github.com/slack-go/slack/channels.go
generated
vendored
Normal file
@ -0,0 +1,412 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type channelResponseFull struct {
|
||||
Channel Channel `json:"channel"`
|
||||
Channels []Channel `json:"channels"`
|
||||
Purpose string `json:"purpose"`
|
||||
Topic string `json:"topic"`
|
||||
NotInChannel bool `json:"not_in_channel"`
|
||||
History
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
// 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"`
|
||||
Locale string `json:"locale"`
|
||||
}
|
||||
|
||||
func (api *Client) channelRequest(ctx context.Context, path string, values url.Values) (*channelResponseFull, error) {
|
||||
response := &channelResponseFull{}
|
||||
err := postForm(ctx, api.httpclient, api.endpoint+path, values, response, api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
type channelsConfig struct {
|
||||
values url.Values
|
||||
}
|
||||
|
||||
// GetChannelsOption option provided when getting channels.
|
||||
type GetChannelsOption func(*channelsConfig) error
|
||||
|
||||
// GetChannelsOptionExcludeMembers excludes the members collection from each channel.
|
||||
func GetChannelsOptionExcludeMembers() GetChannelsOption {
|
||||
return func(config *channelsConfig) error {
|
||||
config.values.Add("exclude_members", "true")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetChannelsOptionExcludeArchived excludes archived channels from results.
|
||||
func GetChannelsOptionExcludeArchived() GetChannelsOption {
|
||||
return func(config *channelsConfig) error {
|
||||
config.values.Add("exclude_archived", "true")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ArchiveChannel archives the given channel
|
||||
// see https://api.slack.com/methods/channels.archive
|
||||
func (api *Client) ArchiveChannel(channelID string) error {
|
||||
return api.ArchiveChannelContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// 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) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
|
||||
_, err = api.channelRequest(ctx, "channels.archive", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// UnarchiveChannel unarchives the given channel
|
||||
// see https://api.slack.com/methods/channels.unarchive
|
||||
func (api *Client) UnarchiveChannel(channelID string) error {
|
||||
return api.UnarchiveChannelContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// 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) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
|
||||
_, err = api.channelRequest(ctx, "channels.unarchive", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateChannel creates a channel with the given name and returns a *Channel
|
||||
// see https://api.slack.com/methods/channels.create
|
||||
func (api *Client) CreateChannel(channelName string) (*Channel, error) {
|
||||
return api.CreateChannelContext(context.Background(), channelName)
|
||||
}
|
||||
|
||||
// CreateChannelContext creates a channel with the given name and returns a *Channel with a custom context
|
||||
// see https://api.slack.com/methods/channels.create
|
||||
func (api *Client) CreateChannelContext(ctx context.Context, channelName string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"name": {channelName},
|
||||
}
|
||||
|
||||
response, err := api.channelRequest(ctx, "channels.create", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Channel, nil
|
||||
}
|
||||
|
||||
// GetChannelHistory retrieves the channel history
|
||||
// see https://api.slack.com/methods/channels.history
|
||||
func (api *Client) GetChannelHistory(channelID string, params HistoryParameters) (*History, error) {
|
||||
return api.GetChannelHistoryContext(context.Background(), channelID, params)
|
||||
}
|
||||
|
||||
// GetChannelHistoryContext retrieves the channel history with a custom context
|
||||
// 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.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
if params.Latest != DEFAULT_HISTORY_LATEST {
|
||||
values.Add("latest", params.Latest)
|
||||
}
|
||||
if params.Oldest != DEFAULT_HISTORY_OLDEST {
|
||||
values.Add("oldest", params.Oldest)
|
||||
}
|
||||
if params.Count != DEFAULT_HISTORY_COUNT {
|
||||
values.Add("count", strconv.Itoa(params.Count))
|
||||
}
|
||||
if params.Inclusive != DEFAULT_HISTORY_INCLUSIVE {
|
||||
if params.Inclusive {
|
||||
values.Add("inclusive", "1")
|
||||
} else {
|
||||
values.Add("inclusive", "0")
|
||||
}
|
||||
}
|
||||
|
||||
if params.Unreads != DEFAULT_HISTORY_UNREADS {
|
||||
if params.Unreads {
|
||||
values.Add("unreads", "1")
|
||||
} else {
|
||||
values.Add("unreads", "0")
|
||||
}
|
||||
}
|
||||
|
||||
response, err := api.channelRequest(ctx, "channels.history", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.History, nil
|
||||
}
|
||||
|
||||
// GetChannelInfo retrieves the given channel
|
||||
// see https://api.slack.com/methods/channels.info
|
||||
func (api *Client) GetChannelInfo(channelID string) (*Channel, error) {
|
||||
return api.GetChannelInfoContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// GetChannelInfoContext retrieves the given channel with a custom context
|
||||
// see https://api.slack.com/methods/channels.info
|
||||
func (api *Client) GetChannelInfoContext(ctx context.Context, channelID string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"include_locale": {strconv.FormatBool(true)},
|
||||
}
|
||||
|
||||
response, err := api.channelRequest(ctx, "channels.info", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Channel, nil
|
||||
}
|
||||
|
||||
// InviteUserToChannel invites a user to a given channel and returns a *Channel
|
||||
// see https://api.slack.com/methods/channels.invite
|
||||
func (api *Client) InviteUserToChannel(channelID, user string) (*Channel, error) {
|
||||
return api.InviteUserToChannelContext(context.Background(), channelID, user)
|
||||
}
|
||||
|
||||
// InviteUserToChannelContext invites a user to a given channel and returns a *Channel with a custom context
|
||||
// see https://api.slack.com/methods/channels.invite
|
||||
func (api *Client) InviteUserToChannelContext(ctx context.Context, channelID, user string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
response, err := api.channelRequest(ctx, "channels.invite", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Channel, nil
|
||||
}
|
||||
|
||||
// JoinChannel joins the currently authenticated user to a channel
|
||||
// see https://api.slack.com/methods/channels.join
|
||||
func (api *Client) JoinChannel(channelName string) (*Channel, error) {
|
||||
return api.JoinChannelContext(context.Background(), channelName)
|
||||
}
|
||||
|
||||
// JoinChannelContext joins the currently authenticated user to a channel with a custom context
|
||||
// see https://api.slack.com/methods/channels.join
|
||||
func (api *Client) JoinChannelContext(ctx context.Context, channelName string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"name": {channelName},
|
||||
}
|
||||
|
||||
response, err := api.channelRequest(ctx, "channels.join", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Channel, nil
|
||||
}
|
||||
|
||||
// LeaveChannel makes the authenticated user leave the given channel
|
||||
// see https://api.slack.com/methods/channels.leave
|
||||
func (api *Client) LeaveChannel(channelID string) (bool, error) {
|
||||
return api.LeaveChannelContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// LeaveChannelContext makes the authenticated user leave the given channel with a custom context
|
||||
// see https://api.slack.com/methods/channels.leave
|
||||
func (api *Client) LeaveChannelContext(ctx context.Context, channelID string) (bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
|
||||
response, err := api.channelRequest(ctx, "channels.leave", values)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return response.NotInChannel, nil
|
||||
}
|
||||
|
||||
// KickUserFromChannel kicks a user from a given channel
|
||||
// see https://api.slack.com/methods/channels.kick
|
||||
func (api *Client) KickUserFromChannel(channelID, user string) error {
|
||||
return api.KickUserFromChannelContext(context.Background(), channelID, user)
|
||||
}
|
||||
|
||||
// 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) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
_, err = api.channelRequest(ctx, "channels.kick", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetChannels retrieves all the channels
|
||||
// see https://api.slack.com/methods/channels.list
|
||||
func (api *Client) GetChannels(excludeArchived bool, options ...GetChannelsOption) ([]Channel, error) {
|
||||
return api.GetChannelsContext(context.Background(), excludeArchived, options...)
|
||||
}
|
||||
|
||||
// GetChannelsContext retrieves all the channels with a custom context
|
||||
// see https://api.slack.com/methods/channels.list
|
||||
func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool, options ...GetChannelsOption) ([]Channel, error) {
|
||||
config := channelsConfig{
|
||||
values: url.Values{
|
||||
"token": {api.token},
|
||||
},
|
||||
}
|
||||
|
||||
if excludeArchived {
|
||||
options = append(options, GetChannelsOptionExcludeArchived())
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
if err := opt(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
response, err := api.channelRequest(ctx, "channels.list", config.values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.Channels, nil
|
||||
}
|
||||
|
||||
// SetChannelReadMark sets the read mark of a given channel to a specific point
|
||||
// Clients should try to avoid making this call too often. When needing to mark a read position, a client should set a
|
||||
// timer before making the call. In this way, any further updates needed during the timeout will not generate extra calls
|
||||
// (just one per channel). This is useful for when reading scroll-back history, or following a busy live channel. A
|
||||
// timeout of 5 seconds is a good starting point. Be sure to flush these calls on shutdown/logout.
|
||||
// see https://api.slack.com/methods/channels.mark
|
||||
func (api *Client) SetChannelReadMark(channelID, ts string) error {
|
||||
return api.SetChannelReadMarkContext(context.Background(), channelID, ts)
|
||||
}
|
||||
|
||||
// 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) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"ts": {ts},
|
||||
}
|
||||
|
||||
_, err = api.channelRequest(ctx, "channels.mark", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// RenameChannel renames a given channel
|
||||
// see https://api.slack.com/methods/channels.rename
|
||||
func (api *Client) RenameChannel(channelID, name string) (*Channel, error) {
|
||||
return api.RenameChannelContext(context.Background(), channelID, name)
|
||||
}
|
||||
|
||||
// RenameChannelContext renames a given channel with a custom context
|
||||
// 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.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 := api.channelRequest(ctx, "channels.rename", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Channel, nil
|
||||
}
|
||||
|
||||
// SetChannelPurpose sets the channel purpose and returns the purpose that was successfully set
|
||||
// see https://api.slack.com/methods/channels.setPurpose
|
||||
func (api *Client) SetChannelPurpose(channelID, purpose string) (string, error) {
|
||||
return api.SetChannelPurposeContext(context.Background(), channelID, purpose)
|
||||
}
|
||||
|
||||
// SetChannelPurposeContext sets the channel purpose and returns the purpose that was successfully set with a custom context
|
||||
// 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.token},
|
||||
"channel": {channelID},
|
||||
"purpose": {purpose},
|
||||
}
|
||||
|
||||
response, err := api.channelRequest(ctx, "channels.setPurpose", values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return response.Purpose, nil
|
||||
}
|
||||
|
||||
// SetChannelTopic sets the channel topic and returns the topic that was successfully set
|
||||
// see https://api.slack.com/methods/channels.setTopic
|
||||
func (api *Client) SetChannelTopic(channelID, topic string) (string, error) {
|
||||
return api.SetChannelTopicContext(context.Background(), channelID, topic)
|
||||
}
|
||||
|
||||
// SetChannelTopicContext sets the channel topic and returns the topic that was successfully set with a custom context
|
||||
// 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.token},
|
||||
"channel": {channelID},
|
||||
"topic": {topic},
|
||||
}
|
||||
|
||||
response, err := api.channelRequest(ctx, "channels.setTopic", values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return response.Topic, nil
|
||||
}
|
||||
|
||||
// GetChannelReplies gets an entire thread (a message plus all the messages in reply to it).
|
||||
// see https://api.slack.com/methods/channels.replies
|
||||
func (api *Client) GetChannelReplies(channelID, thread_ts string) ([]Message, error) {
|
||||
return api.GetChannelRepliesContext(context.Background(), channelID, thread_ts)
|
||||
}
|
||||
|
||||
// GetChannelRepliesContext gets an entire thread (a message plus all the messages in reply to it) with a custom context
|
||||
// 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.token},
|
||||
"channel": {channelID},
|
||||
"thread_ts": {thread_ts},
|
||||
}
|
||||
response, err := api.channelRequest(ctx, "channels.replies", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.History.Messages, nil
|
||||
}
|
627
vendor/github.com/slack-go/slack/chat.go
generated
vendored
Normal file
627
vendor/github.com/slack-go/slack/chat.go
generated
vendored
Normal file
@ -0,0 +1,627 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/slack-go/slack/slackutilsx"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_MESSAGE_USERNAME = ""
|
||||
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
|
||||
DEFAULT_MESSAGE_ICON_URL = ""
|
||||
DEFAULT_MESSAGE_ICON_EMOJI = ""
|
||||
DEFAULT_MESSAGE_MARKDOWN = true
|
||||
DEFAULT_MESSAGE_ESCAPE_TEXT = true
|
||||
)
|
||||
|
||||
type chatResponseFull struct {
|
||||
Channel string `json:"channel"`
|
||||
Timestamp string `json:"ts"` //Regular 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 {
|
||||
Username string `json:"username"`
|
||||
AsUser bool `json:"as_user"`
|
||||
Parse string `json:"parse"`
|
||||
ThreadTimestamp string `json:"thread_ts"`
|
||||
ReplyBroadcast bool `json:"reply_broadcast"`
|
||||
LinkNames int `json:"link_names"`
|
||||
UnfurlLinks bool `json:"unfurl_links"`
|
||||
UnfurlMedia bool `json:"unfurl_media"`
|
||||
IconURL string `json:"icon_url"`
|
||||
IconEmoji string `json:"icon_emoji"`
|
||||
Markdown bool `json:"mrkdwn,omitempty"`
|
||||
EscapeText bool `json:"escape_text"`
|
||||
|
||||
// chat.postEphemeral support
|
||||
Channel string `json:"channel"`
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
// 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,
|
||||
ThreadTimestamp: DEFAULT_MESSAGE_THREAD_TIMESTAMP,
|
||||
LinkNames: DEFAULT_MESSAGE_LINK_NAMES,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteMessage deletes a message in a channel
|
||||
func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) {
|
||||
respChannel, respTimestamp, _, err := api.SendMessageContext(context.Background(), channel, MsgOptionDelete(messageTimestamp))
|
||||
return respChannel, respTimestamp, err
|
||||
}
|
||||
|
||||
// DeleteMessageContext deletes a message in a channel with a custom context
|
||||
func (api *Client) DeleteMessageContext(ctx context.Context, channel, messageTimestamp string) (string, string, error) {
|
||||
respChannel, respTimestamp, _, err := api.SendMessageContext(ctx, channel, MsgOptionDelete(messageTimestamp))
|
||||
return respChannel, respTimestamp, err
|
||||
}
|
||||
|
||||
// PostMessage sends a message to a channel.
|
||||
// Message is escaped by default according to https://api.slack.com/docs/formatting
|
||||
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
|
||||
func (api *Client) PostMessage(channelID string, options ...MsgOption) (string, string, error) {
|
||||
respChannel, respTimestamp, _, err := api.SendMessageContext(
|
||||
context.Background(),
|
||||
channelID,
|
||||
MsgOptionPost(),
|
||||
MsgOptionCompose(options...),
|
||||
)
|
||||
return respChannel, respTimestamp, err
|
||||
}
|
||||
|
||||
// PostMessageContext sends a message to a channel with a custom context
|
||||
// For more details, see PostMessage documentation.
|
||||
func (api *Client) PostMessageContext(ctx context.Context, channelID string, options ...MsgOption) (string, string, error) {
|
||||
respChannel, respTimestamp, _, err := api.SendMessageContext(
|
||||
ctx,
|
||||
channelID,
|
||||
MsgOptionPost(),
|
||||
MsgOptionCompose(options...),
|
||||
)
|
||||
return respChannel, respTimestamp, err
|
||||
}
|
||||
|
||||
// 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(channelID, userID string, options ...MsgOption) (string, error) {
|
||||
return api.PostEphemeralContext(
|
||||
context.Background(),
|
||||
channelID,
|
||||
userID,
|
||||
options...,
|
||||
)
|
||||
}
|
||||
|
||||
// PostEphemeralContext sends an ephemeal message to a user in a channel with a custom context
|
||||
// For more details, see PostEphemeral documentation
|
||||
func (api *Client) PostEphemeralContext(ctx context.Context, channelID, userID string, options ...MsgOption) (timestamp string, err error) {
|
||||
_, timestamp, _, err = api.SendMessageContext(ctx, channelID, MsgOptionPostEphemeral(userID), MsgOptionCompose(options...))
|
||||
return timestamp, err
|
||||
}
|
||||
|
||||
// UpdateMessage updates a message in a channel
|
||||
func (api *Client) UpdateMessage(channelID, timestamp string, options ...MsgOption) (string, string, string, error) {
|
||||
return api.SendMessageContext(context.Background(), channelID, MsgOptionUpdate(timestamp), MsgOptionCompose(options...))
|
||||
}
|
||||
|
||||
// UpdateMessageContext updates a message in a channel
|
||||
func (api *Client) UpdateMessageContext(ctx context.Context, channelID, timestamp string, options ...MsgOption) (string, string, string, error) {
|
||||
return api.SendMessageContext(ctx, channelID, MsgOptionUpdate(timestamp), MsgOptionCompose(options...))
|
||||
}
|
||||
|
||||
// UnfurlMessage unfurls a message in a channel
|
||||
func (api *Client) UnfurlMessage(channelID, timestamp string, unfurls map[string]Attachment, options ...MsgOption) (string, string, string, error) {
|
||||
return api.SendMessageContext(context.Background(), channelID, MsgOptionUnfurl(timestamp, unfurls), MsgOptionCompose(options...))
|
||||
}
|
||||
|
||||
// SendMessage more flexible method for configuring messages.
|
||||
func (api *Client) SendMessage(channel string, options ...MsgOption) (string, string, string, error) {
|
||||
return api.SendMessageContext(context.Background(), channel, options...)
|
||||
}
|
||||
|
||||
// SendMessageContext more flexible method for configuring messages with a custom context.
|
||||
func (api *Client) SendMessageContext(ctx context.Context, channelID string, options ...MsgOption) (_channel string, _timestamp string, _text string, err error) {
|
||||
var (
|
||||
req *http.Request
|
||||
parser func(*chatResponseFull) responseParser
|
||||
response chatResponseFull
|
||||
)
|
||||
|
||||
if req, parser, err = buildSender(api.endpoint, options...).BuildRequest(api.token, channelID); err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
if err = doPost(ctx, api.httpclient, req, parser(&response), api); err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
return response.Channel, response.getMessageTimestamp(), response.Text, response.Err()
|
||||
}
|
||||
|
||||
// UnsafeApplyMsgOptions utility function for debugging/testing chat requests.
|
||||
// NOTE: USE AT YOUR OWN RISK: No issues relating to the use of this function
|
||||
// will be supported by the library.
|
||||
func UnsafeApplyMsgOptions(token, channel, apiurl string, options ...MsgOption) (string, url.Values, error) {
|
||||
config, err := applyMsgOptions(token, channel, apiurl, options...)
|
||||
return config.endpoint, config.values, err
|
||||
}
|
||||
|
||||
func applyMsgOptions(token, channel, apiurl string, options ...MsgOption) (sendConfig, error) {
|
||||
config := sendConfig{
|
||||
apiurl: apiurl,
|
||||
endpoint: apiurl + string(chatPostMessage),
|
||||
values: url.Values{
|
||||
"token": {token},
|
||||
"channel": {channel},
|
||||
},
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
if err := opt(&config); err != nil {
|
||||
return config, err
|
||||
}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func buildSender(apiurl string, options ...MsgOption) sendConfig {
|
||||
return sendConfig{
|
||||
apiurl: apiurl,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
type sendMode string
|
||||
|
||||
const (
|
||||
chatUpdate sendMode = "chat.update"
|
||||
chatPostMessage sendMode = "chat.postMessage"
|
||||
chatDelete sendMode = "chat.delete"
|
||||
chatPostEphemeral sendMode = "chat.postEphemeral"
|
||||
chatResponse sendMode = "chat.responseURL"
|
||||
chatMeMessage sendMode = "chat.meMessage"
|
||||
chatUnfurl sendMode = "chat.unfurl"
|
||||
)
|
||||
|
||||
type sendConfig struct {
|
||||
apiurl string
|
||||
options []MsgOption
|
||||
mode sendMode
|
||||
endpoint string
|
||||
values url.Values
|
||||
attachments []Attachment
|
||||
blocks Blocks
|
||||
responseType string
|
||||
}
|
||||
|
||||
func (t sendConfig) BuildRequest(token, channelID string) (req *http.Request, _ func(*chatResponseFull) responseParser, err error) {
|
||||
if t, err = applyMsgOptions(token, channelID, t.apiurl, t.options...); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
switch t.mode {
|
||||
case chatResponse:
|
||||
return responseURLSender{
|
||||
endpoint: t.endpoint,
|
||||
values: t.values,
|
||||
attachments: t.attachments,
|
||||
blocks: t.blocks,
|
||||
responseType: t.responseType,
|
||||
}.BuildRequest()
|
||||
default:
|
||||
return formSender{endpoint: t.endpoint, values: t.values}.BuildRequest()
|
||||
}
|
||||
}
|
||||
|
||||
type formSender struct {
|
||||
endpoint string
|
||||
values url.Values
|
||||
}
|
||||
|
||||
func (t formSender) BuildRequest() (*http.Request, func(*chatResponseFull) responseParser, error) {
|
||||
req, err := formReq(t.endpoint, t.values)
|
||||
return req, func(resp *chatResponseFull) responseParser {
|
||||
return newJSONParser(resp)
|
||||
}, err
|
||||
}
|
||||
|
||||
type responseURLSender struct {
|
||||
endpoint string
|
||||
values url.Values
|
||||
attachments []Attachment
|
||||
blocks Blocks
|
||||
responseType string
|
||||
}
|
||||
|
||||
func (t responseURLSender) BuildRequest() (*http.Request, func(*chatResponseFull) responseParser, error) {
|
||||
req, err := jsonReq(t.endpoint, Msg{
|
||||
Text: t.values.Get("text"),
|
||||
Timestamp: t.values.Get("ts"),
|
||||
Attachments: t.attachments,
|
||||
Blocks: t.blocks,
|
||||
ResponseType: t.responseType,
|
||||
})
|
||||
return req, func(resp *chatResponseFull) responseParser {
|
||||
return newContentTypeParser(resp)
|
||||
}, err
|
||||
}
|
||||
|
||||
// MsgOption option provided when sending a message.
|
||||
type MsgOption func(*sendConfig) error
|
||||
|
||||
// MsgOptionPost posts a messages, this is the default.
|
||||
func MsgOptionPost() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = config.apiurl + string(chatPostMessage)
|
||||
config.values.Del("ts")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionPostEphemeral - posts an ephemeral message to the provided user.
|
||||
func MsgOptionPostEphemeral(userID string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = config.apiurl + string(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.endpoint = config.apiurl + string(chatMeMessage)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionUpdate updates a message based on the timestamp.
|
||||
func MsgOptionUpdate(timestamp string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = config.apiurl + string(chatUpdate)
|
||||
config.values.Add("ts", timestamp)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionDelete deletes a message based on the timestamp.
|
||||
func MsgOptionDelete(timestamp string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = config.apiurl + string(chatDelete)
|
||||
config.values.Add("ts", timestamp)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionUnfurl unfurls a message based on the timestamp.
|
||||
func MsgOptionUnfurl(timestamp string, unfurls map[string]Attachment) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = config.apiurl + string(chatUnfurl)
|
||||
config.values.Add("ts", timestamp)
|
||||
unfurlsStr, err := json.Marshal(unfurls)
|
||||
if err == nil {
|
||||
config.values.Add("unfurls", string(unfurlsStr))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionResponseURL supplies a url to use as the endpoint.
|
||||
func MsgOptionResponseURL(url string, responseType string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.mode = chatResponse
|
||||
config.endpoint = url
|
||||
config.responseType = responseType
|
||||
config.values.Del("ts")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionAsUser whether or not to send the message as the user.
|
||||
func MsgOptionAsUser(b bool) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
if b != DEFAULT_MESSAGE_ASUSER {
|
||||
config.values.Set("as_user", "true")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionUser set the user for the message.
|
||||
func MsgOptionUser(userID string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.values.Set("user", userID)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionUsername set the username for the message.
|
||||
func MsgOptionUsername(username string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.values.Set("username", username)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionText provide the text for the message, optionally escape the provided
|
||||
// text.
|
||||
func MsgOptionText(text string, escape bool) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
if escape {
|
||||
text = slackutilsx.EscapeMessage(text)
|
||||
}
|
||||
config.values.Add("text", text)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionAttachments provide attachments for the message.
|
||||
func MsgOptionAttachments(attachments ...Attachment) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
if attachments == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
config.attachments = attachments
|
||||
|
||||
// FIXME: We are setting the attachments on the message twice: above for
|
||||
// the json version, and below for the html version. The marshalled bytes
|
||||
// we put into config.values below don't work directly in the Msg version.
|
||||
|
||||
attachmentBytes, err := json.Marshal(attachments)
|
||||
if err == nil {
|
||||
config.values.Set("attachments", string(attachmentBytes))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionBlocks sets blocks for the message
|
||||
func MsgOptionBlocks(blocks ...Block) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
if blocks == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
config.blocks.BlockSet = append(config.blocks.BlockSet, blocks...)
|
||||
|
||||
blocks, err := json.Marshal(blocks)
|
||||
if err == nil {
|
||||
config.values.Set("blocks", string(blocks))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionEnableLinkUnfurl enables link unfurling
|
||||
func MsgOptionEnableLinkUnfurl() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.values.Set("unfurl_links", "true")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
config.values.Set("unfurl_media", "false")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionDisableMarkdown disables markdown.
|
||||
func MsgOptionDisableMarkdown() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.values.Set("mrkdwn", "false")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionCompose 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
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionParse set parse option.
|
||||
func MsgOptionParse(b bool) MsgOption {
|
||||
return func(c *sendConfig) error {
|
||||
var v string
|
||||
if b {
|
||||
v = "full"
|
||||
} else {
|
||||
v = "none"
|
||||
}
|
||||
c.values.Set("parse", v)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionIconURL sets an icon URL
|
||||
func MsgOptionIconURL(iconURL string) MsgOption {
|
||||
return func(c *sendConfig) error {
|
||||
c.values.Set("icon_url", iconURL)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionIconEmoji sets an icon emoji
|
||||
func MsgOptionIconEmoji(iconEmoji string) MsgOption {
|
||||
return func(c *sendConfig) error {
|
||||
c.values.Set("icon_emoji", iconEmoji)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// UnsafeMsgOptionEndpoint deliver the message to the specified endpoint.
|
||||
// NOTE: USE AT YOUR OWN RISK: No issues relating to the use of this Option
|
||||
// will be supported by the library, it is subject to change without notice that
|
||||
// may result in compilation errors or runtime behaviour changes.
|
||||
func UnsafeMsgOptionEndpoint(endpoint string, update func(url.Values)) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.endpoint = endpoint
|
||||
update(config.values)
|
||||
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", params.Username)
|
||||
}
|
||||
|
||||
// chat.postEphemeral support
|
||||
if params.User != DEFAULT_MESSAGE_USERNAME {
|
||||
config.values.Set("user", params.User)
|
||||
}
|
||||
|
||||
// never generates an error.
|
||||
MsgOptionAsUser(params.AsUser)(config)
|
||||
|
||||
if params.Parse != DEFAULT_MESSAGE_PARSE {
|
||||
config.values.Set("parse", params.Parse)
|
||||
}
|
||||
if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
|
||||
config.values.Set("link_names", "1")
|
||||
}
|
||||
|
||||
if params.UnfurlLinks != DEFAULT_MESSAGE_UNFURL_LINKS {
|
||||
config.values.Set("unfurl_links", "true")
|
||||
}
|
||||
|
||||
// I want to send a message with explicit `as_user` `true` and `unfurl_links` `false` in request.
|
||||
// Because setting `as_user` to `true` will change the default value for `unfurl_links` to `true` on Slack API side.
|
||||
if params.AsUser != DEFAULT_MESSAGE_ASUSER && params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
|
||||
config.values.Set("unfurl_links", "false")
|
||||
}
|
||||
if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
|
||||
config.values.Set("unfurl_media", "false")
|
||||
}
|
||||
if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
|
||||
config.values.Set("icon_url", params.IconURL)
|
||||
}
|
||||
if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
|
||||
config.values.Set("icon_emoji", params.IconEmoji)
|
||||
}
|
||||
if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
|
||||
config.values.Set("mrkdwn", "false")
|
||||
}
|
||||
|
||||
if params.ThreadTimestamp != DEFAULT_MESSAGE_THREAD_TIMESTAMP {
|
||||
config.values.Set("thread_ts", params.ThreadTimestamp)
|
||||
}
|
||||
if params.ReplyBroadcast != DEFAULT_MESSAGE_REPLY_BROADCAST {
|
||||
config.values.Set("reply_broadcast", "true")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// PermalinkParameters are the parameters required to get a permalink to a
|
||||
// message. Slack documentation can be found here:
|
||||
// https://api.slack.com/methods/chat.getPermalink
|
||||
type PermalinkParameters struct {
|
||||
Channel string
|
||||
Ts string
|
||||
}
|
||||
|
||||
// GetPermalink returns the permalink for a message. It takes
|
||||
// PermalinkParameters and returns a string containing the permalink. It
|
||||
// returns an error if unable to retrieve the permalink.
|
||||
func (api *Client) GetPermalink(params *PermalinkParameters) (string, error) {
|
||||
return api.GetPermalinkContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetPermalinkContext returns the permalink for a message using a custom context.
|
||||
func (api *Client) GetPermalinkContext(ctx context.Context, params *PermalinkParameters) (string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {params.Channel},
|
||||
"message_ts": {params.Ts},
|
||||
}
|
||||
|
||||
response := struct {
|
||||
Channel string `json:"channel"`
|
||||
Permalink string `json:"permalink"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err := api.getMethod(ctx, "chat.getPermalink", values, &response)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return response.Permalink, response.Err()
|
||||
}
|
10
vendor/github.com/slack-go/slack/comment.go
generated
vendored
Normal file
10
vendor/github.com/slack-go/slack/comment.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
package slack
|
||||
|
||||
// Comment contains all the information relative to a comment
|
||||
type Comment struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Created JSONTime `json:"created,omitempty"`
|
||||
Timestamp JSONTime `json:"timestamp,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
}
|
620
vendor/github.com/slack-go/slack/conversation.go
generated
vendored
Normal file
620
vendor/github.com/slack-go/slack/conversation.go
generated
vendored
Normal file
@ -0,0 +1,620 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Conversation is the foundation for IM and BaseGroupConversation
|
||||
type Conversation struct {
|
||||
ID string `json:"id"`
|
||||
Created JSONTime `json:"created"`
|
||||
IsOpen bool `json:"is_open"`
|
||||
LastRead string `json:"last_read,omitempty"`
|
||||
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"`
|
||||
User string `json:"user"`
|
||||
|
||||
// TODO support pending_shared
|
||||
// TODO support previous_names
|
||||
}
|
||||
|
||||
// GroupConversation is the foundation for Group and Channel
|
||||
type GroupConversation struct {
|
||||
Conversation
|
||||
Name string `json:"name"`
|
||||
Creator string `json:"creator"`
|
||||
IsArchived bool `json:"is_archived"`
|
||||
Members []string `json:"members"`
|
||||
Topic Topic `json:"topic"`
|
||||
Purpose Purpose `json:"purpose"`
|
||||
}
|
||||
|
||||
// Topic contains information about the topic
|
||||
type Topic struct {
|
||||
Value string `json:"value"`
|
||||
Creator string `json:"creator"`
|
||||
LastSet JSONTime `json:"last_set"`
|
||||
}
|
||||
|
||||
// Purpose contains information about the purpose
|
||||
type Purpose struct {
|
||||
Value string `json:"value"`
|
||||
Creator string `json:"creator"`
|
||||
LastSet JSONTime `json:"last_set"`
|
||||
}
|
||||
|
||||
type GetUsersInConversationParameters struct {
|
||||
ChannelID string
|
||||
Cursor string
|
||||
Limit int
|
||||
}
|
||||
|
||||
type GetConversationsForUserParameters struct {
|
||||
UserID string
|
||||
Cursor string
|
||||
Types []string
|
||||
Limit int
|
||||
ExcludeArchived bool
|
||||
}
|
||||
|
||||
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 := api.postMethod(ctx, "conversations.members", values, &response)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return response.Members, response.ResponseMetaData.NextCursor, nil
|
||||
}
|
||||
|
||||
// GetConversationsForUser returns the list conversations for a given user
|
||||
func (api *Client) GetConversationsForUser(params *GetConversationsForUserParameters) (channels []Channel, nextCursor string, err error) {
|
||||
return api.GetConversationsForUserContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetConversationsForUserContext returns the list conversations for a given user with a custom context
|
||||
func (api *Client) GetConversationsForUserContext(ctx context.Context, params *GetConversationsForUserParameters) (channels []Channel, nextCursor string, err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.UserID != "" {
|
||||
values.Add("user", params.UserID)
|
||||
}
|
||||
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, ","))
|
||||
}
|
||||
if params.ExcludeArchived {
|
||||
values.Add("exclude_archived", "true")
|
||||
}
|
||||
response := struct {
|
||||
Channels []Channel `json:"channels"`
|
||||
ResponseMetaData responseMetaData `json:"response_metadata"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err = api.postMethod(ctx, "users.conversations", values, &response)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return response.Channels, response.ResponseMetaData.NextCursor, response.Err()
|
||||
}
|
||||
|
||||
// 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 := api.postMethod(ctx, "conversations.archive", values, &response)
|
||||
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 := api.postMethod(ctx, "conversations.unarchive", values, &response)
|
||||
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 := api.postMethod(ctx, "conversations.setTopic", values, &response)
|
||||
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 := api.postMethod(ctx, "conversations.setPurpose", values, &response)
|
||||
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 := api.postMethod(ctx, "conversations.rename", values, &response)
|
||||
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 := api.postMethod(ctx, "conversations.invite", values, &response)
|
||||
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 := api.postMethod(ctx, "conversations.kick", values, &response)
|
||||
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 = api.postMethod(ctx, "conversations.close", values, &response)
|
||||
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 := api.channelRequest(ctx, "conversations.create", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.Channel, nil
|
||||
}
|
||||
|
||||
// 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 := api.channelRequest(ctx, "conversations.info", values)
|
||||
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 := api.channelRequest(ctx, "conversations.leave", values)
|
||||
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 = api.postMethod(ctx, "conversations.replies", values, &response)
|
||||
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 = api.postMethod(ctx, "conversations.list", values, &response)
|
||||
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 := api.postMethod(ctx, "conversations.open", values, &response)
|
||||
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 := api.postMethod(ctx, "conversations.join", values, &response)
|
||||
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 := api.postMethod(ctx, "conversations.history", values, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response, response.Err()
|
||||
}
|
118
vendor/github.com/slack-go/slack/dialog.go
generated
vendored
Normal file
118
vendor/github.com/slack-go/slack/dialog.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// InputType is the type of the dialog input type
|
||||
type InputType string
|
||||
|
||||
const (
|
||||
// InputTypeText textfield input
|
||||
InputTypeText InputType = "text"
|
||||
// InputTypeTextArea textarea input
|
||||
InputTypeTextArea InputType = "textarea"
|
||||
// InputTypeSelect select menus input
|
||||
InputTypeSelect InputType = "select"
|
||||
)
|
||||
|
||||
// DialogInput for dialogs input type text or menu
|
||||
type DialogInput struct {
|
||||
Type InputType `json:"type"`
|
||||
Label string `json:"label"`
|
||||
Name string `json:"name"`
|
||||
Placeholder string `json:"placeholder"`
|
||||
Optional bool `json:"optional"`
|
||||
Hint string `json:"hint"`
|
||||
}
|
||||
|
||||
// DialogTrigger ...
|
||||
type DialogTrigger struct {
|
||||
TriggerID string `json:"trigger_id"` //Required. Must respond within 3 seconds.
|
||||
Dialog Dialog `json:"dialog"` //Required.
|
||||
}
|
||||
|
||||
// Dialog as in Slack dialogs
|
||||
// https://api.slack.com/dialogs#option_element_attributes#top-level_dialog_attributes
|
||||
type Dialog struct {
|
||||
TriggerID string `json:"trigger_id"` // Required
|
||||
CallbackID string `json:"callback_id"` // Required
|
||||
State string `json:"state,omitempty"` // Optional
|
||||
Title string `json:"title"`
|
||||
SubmitLabel string `json:"submit_label,omitempty"`
|
||||
NotifyOnCancel bool `json:"notify_on_cancel"`
|
||||
Elements []DialogElement `json:"elements"`
|
||||
}
|
||||
|
||||
// DialogElement abstract type for dialogs.
|
||||
type DialogElement interface{}
|
||||
|
||||
// DialogCallback DEPRECATED use InteractionCallback
|
||||
type DialogCallback InteractionCallback
|
||||
|
||||
// DialogSubmissionCallback is sent from Slack when a user submits a form from within a dialog
|
||||
type DialogSubmissionCallback struct {
|
||||
State string `json:"state,omitempty"`
|
||||
Submission map[string]string `json:"submission"`
|
||||
}
|
||||
|
||||
// DialogOpenResponse response from `dialog.open`
|
||||
type DialogOpenResponse struct {
|
||||
SlackResponse
|
||||
DialogResponseMetadata DialogResponseMetadata `json:"response_metadata"`
|
||||
}
|
||||
|
||||
// DialogResponseMetadata lists the error messages
|
||||
type DialogResponseMetadata struct {
|
||||
Messages []string `json:"messages"`
|
||||
}
|
||||
|
||||
// DialogInputValidationError is an error when user inputs incorrect value to form from within a dialog
|
||||
type DialogInputValidationError struct {
|
||||
Name string `json:"name"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// DialogInputValidationErrors lists the name of field and that error messages
|
||||
type DialogInputValidationErrors struct {
|
||||
Errors []DialogInputValidationError `json:"errors"`
|
||||
}
|
||||
|
||||
// OpenDialog opens a dialog window where the triggerID originated from.
|
||||
// EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable.
|
||||
func (api *Client) OpenDialog(triggerID string, dialog Dialog) (err error) {
|
||||
return api.OpenDialogContext(context.Background(), triggerID, dialog)
|
||||
}
|
||||
|
||||
// OpenDialogContext opens a dialog window where the triggerId originated from with a custom context
|
||||
// EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable.
|
||||
func (api *Client) OpenDialogContext(ctx context.Context, triggerID string, dialog Dialog) (err error) {
|
||||
if triggerID == "" {
|
||||
return ErrParametersMissing
|
||||
}
|
||||
|
||||
req := DialogTrigger{
|
||||
TriggerID: triggerID,
|
||||
Dialog: dialog,
|
||||
}
|
||||
|
||||
encoded, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response := &DialogOpenResponse{}
|
||||
endpoint := api.endpoint + "dialog.open"
|
||||
if err := postJSON(ctx, api.httpclient, endpoint, api.token, encoded, response, api); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(response.DialogResponseMetadata.Messages) > 0 {
|
||||
response.Ok = false
|
||||
response.Error += "\n" + strings.Join(response.DialogResponseMetadata.Messages, "\n")
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
101
vendor/github.com/slack-go/slack/dialog_select.go
generated
vendored
Normal file
101
vendor/github.com/slack-go/slack/dialog_select.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
package slack
|
||||
|
||||
// SelectDataSource types of select datasource
|
||||
type SelectDataSource string
|
||||
|
||||
const (
|
||||
// DialogDataSourceStatic menu with static Options/OptionGroups
|
||||
DialogDataSourceStatic SelectDataSource = "static"
|
||||
// DialogDataSourceExternal dynamic datasource
|
||||
DialogDataSourceExternal SelectDataSource = "external"
|
||||
// DialogDataSourceConversations provides a list of conversations
|
||||
DialogDataSourceConversations SelectDataSource = "conversations"
|
||||
// DialogDataSourceChannels provides a list of channels
|
||||
DialogDataSourceChannels SelectDataSource = "channels"
|
||||
// DialogDataSourceUsers provides a list of users
|
||||
DialogDataSourceUsers SelectDataSource = "users"
|
||||
)
|
||||
|
||||
// DialogInputSelect dialog support for select boxes.
|
||||
type DialogInputSelect struct {
|
||||
DialogInput
|
||||
Value string `json:"value,omitempty"` //Optional.
|
||||
DataSource SelectDataSource `json:"data_source,omitempty"` //Optional. Allowed values: "users", "channels", "conversations", "external".
|
||||
SelectedOptions []DialogSelectOption `json:"selected_options,omitempty"` //Optional. May hold at most one element, for use with "external" only.
|
||||
Options []DialogSelectOption `json:"options,omitempty"` //One of options or option_groups is required.
|
||||
OptionGroups []DialogOptionGroup `json:"option_groups,omitempty"` //Provide up to 100 options.
|
||||
MinQueryLength int `json:"min_query_length,omitempty"` //Optional. minimum characters before query is sent.
|
||||
Hint string `json:"hint,omitempty"` //Optional. Additional hint text.
|
||||
}
|
||||
|
||||
// DialogSelectOption is an option for the user to select from the menu
|
||||
type DialogSelectOption struct {
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// DialogOptionGroup is a collection of options for creating a segmented table
|
||||
type DialogOptionGroup struct {
|
||||
Label string `json:"label"`
|
||||
Options []DialogSelectOption `json:"options"`
|
||||
}
|
||||
|
||||
// NewStaticSelectDialogInput constructor for a `static` datasource menu input
|
||||
func NewStaticSelectDialogInput(name, label string, options []DialogSelectOption) *DialogInputSelect {
|
||||
return &DialogInputSelect{
|
||||
DialogInput: DialogInput{
|
||||
Type: InputTypeSelect,
|
||||
Name: name,
|
||||
Label: label,
|
||||
Optional: true,
|
||||
},
|
||||
DataSource: DialogDataSourceStatic,
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// NewGroupedSelectDialogInput creates grouped options select input for Dialogs.
|
||||
func NewGroupedSelectDialogInput(name, label string, options []DialogOptionGroup) *DialogInputSelect {
|
||||
return &DialogInputSelect{
|
||||
DialogInput: DialogInput{
|
||||
Type: InputTypeSelect,
|
||||
Name: name,
|
||||
Label: label,
|
||||
},
|
||||
DataSource: DialogDataSourceStatic,
|
||||
OptionGroups: options}
|
||||
}
|
||||
|
||||
// NewDialogOptionGroup creates a DialogOptionGroup from several select options
|
||||
func NewDialogOptionGroup(label string, options ...DialogSelectOption) DialogOptionGroup {
|
||||
return DialogOptionGroup{
|
||||
Label: label,
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// NewConversationsSelect returns a `Conversations` select
|
||||
func NewConversationsSelect(name, label string) *DialogInputSelect {
|
||||
return newPresetSelect(name, label, DialogDataSourceConversations)
|
||||
}
|
||||
|
||||
// NewChannelsSelect returns a `Channels` select
|
||||
func NewChannelsSelect(name, label string) *DialogInputSelect {
|
||||
return newPresetSelect(name, label, DialogDataSourceChannels)
|
||||
}
|
||||
|
||||
// NewUsersSelect returns a `Users` select
|
||||
func NewUsersSelect(name, label string) *DialogInputSelect {
|
||||
return newPresetSelect(name, label, DialogDataSourceUsers)
|
||||
}
|
||||
|
||||
func newPresetSelect(name, label string, dataSourceType SelectDataSource) *DialogInputSelect {
|
||||
return &DialogInputSelect{
|
||||
DialogInput: DialogInput{
|
||||
Type: InputTypeSelect,
|
||||
Label: label,
|
||||
Name: name,
|
||||
},
|
||||
DataSource: dataSourceType,
|
||||
}
|
||||
}
|
59
vendor/github.com/slack-go/slack/dialog_text.go
generated
vendored
Normal file
59
vendor/github.com/slack-go/slack/dialog_text.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
package slack
|
||||
|
||||
// TextInputSubtype Accepts email, number, tel, or url. In some form factors, optimized input is provided for this subtype.
|
||||
type TextInputSubtype string
|
||||
|
||||
// TextInputOption handle to extra inputs options.
|
||||
type TextInputOption func(*TextInputElement)
|
||||
|
||||
const (
|
||||
// InputSubtypeEmail email keyboard
|
||||
InputSubtypeEmail TextInputSubtype = "email"
|
||||
// InputSubtypeNumber numeric keyboard
|
||||
InputSubtypeNumber TextInputSubtype = "number"
|
||||
// InputSubtypeTel Phone keyboard
|
||||
InputSubtypeTel TextInputSubtype = "tel"
|
||||
// InputSubtypeURL Phone keyboard
|
||||
InputSubtypeURL TextInputSubtype = "url"
|
||||
)
|
||||
|
||||
// TextInputElement subtype of DialogInput
|
||||
// https://api.slack.com/dialogs#option_element_attributes#text_element_attributes
|
||||
type TextInputElement struct {
|
||||
DialogInput
|
||||
MaxLength int `json:"max_length,omitempty"`
|
||||
MinLength int `json:"min_length,omitempty"`
|
||||
Hint string `json:"hint,omitempty"`
|
||||
Subtype TextInputSubtype `json:"subtype"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// NewTextInput constructor for a `text` input
|
||||
func NewTextInput(name, label, text string, options ...TextInputOption) *TextInputElement {
|
||||
t := &TextInputElement{
|
||||
DialogInput: DialogInput{
|
||||
Type: InputTypeText,
|
||||
Name: name,
|
||||
Label: label,
|
||||
},
|
||||
Value: text,
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
opt(t)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// NewTextAreaInput constructor for a `textarea` input
|
||||
func NewTextAreaInput(name, label, text string) *TextInputElement {
|
||||
return &TextInputElement{
|
||||
DialogInput: DialogInput{
|
||||
Type: InputTypeTextArea,
|
||||
Name: name,
|
||||
Label: label,
|
||||
},
|
||||
Value: text,
|
||||
}
|
||||
}
|
151
vendor/github.com/slack-go/slack/dnd.go
generated
vendored
Normal file
151
vendor/github.com/slack-go/slack/dnd.go
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SnoozeDebug struct {
|
||||
SnoozeEndDate string `json:"snooze_end_date"`
|
||||
}
|
||||
|
||||
type SnoozeInfo struct {
|
||||
SnoozeEnabled bool `json:"snooze_enabled,omitempty"`
|
||||
SnoozeEndTime int `json:"snooze_endtime,omitempty"`
|
||||
SnoozeRemaining int `json:"snooze_remaining,omitempty"`
|
||||
SnoozeDebug SnoozeDebug `json:"snooze_debug,omitempty"`
|
||||
}
|
||||
|
||||
type DNDStatus struct {
|
||||
Enabled bool `json:"dnd_enabled"`
|
||||
NextStartTimestamp int `json:"next_dnd_start_ts"`
|
||||
NextEndTimestamp int `json:"next_dnd_end_ts"`
|
||||
SnoozeInfo
|
||||
}
|
||||
|
||||
type dndResponseFull struct {
|
||||
DNDStatus
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
type dndTeamInfoResponse struct {
|
||||
Users map[string]DNDStatus `json:"users"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func (api *Client) dndRequest(ctx context.Context, path string, values url.Values) (*dndResponseFull, error) {
|
||||
response := &dndResponseFull{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// EndDND ends the user's scheduled Do Not Disturb session
|
||||
func (api *Client) EndDND() error {
|
||||
return api.EndDNDContext(context.Background())
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
|
||||
if err := api.postMethod(ctx, "dnd.endDnd", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// EndSnooze ends the current user's snooze mode
|
||||
func (api *Client) EndSnooze() (*DNDStatus, error) {
|
||||
return api.EndSnoozeContext(context.Background())
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
|
||||
response, err := api.dndRequest(ctx, "dnd.endSnooze", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.DNDStatus, nil
|
||||
}
|
||||
|
||||
// GetDNDInfo provides information about a user's current Do Not Disturb settings.
|
||||
func (api *Client) GetDNDInfo(user *string) (*DNDStatus, error) {
|
||||
return api.GetDNDInfoContext(context.Background(), user)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
if user != nil {
|
||||
values.Set("user", *user)
|
||||
}
|
||||
|
||||
response, err := api.dndRequest(ctx, "dnd.info", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.DNDStatus, nil
|
||||
}
|
||||
|
||||
// GetDNDTeamInfo provides information about a user's current Do Not Disturb settings.
|
||||
func (api *Client) GetDNDTeamInfo(users []string) (map[string]DNDStatus, error) {
|
||||
return api.GetDNDTeamInfoContext(context.Background(), users)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"users": {strings.Join(users, ",")},
|
||||
}
|
||||
response := &dndTeamInfoResponse{}
|
||||
|
||||
if err := api.postMethod(ctx, "dnd.teamInfo", values, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.Err() != nil {
|
||||
return nil, response.Err()
|
||||
}
|
||||
|
||||
return response.Users, nil
|
||||
}
|
||||
|
||||
// SetSnooze adjusts the snooze duration for a user's Do Not Disturb
|
||||
// settings. If a snooze session is not already active for the user, invoking
|
||||
// this method will begin one for the specified duration.
|
||||
func (api *Client) SetSnooze(minutes int) (*DNDStatus, error) {
|
||||
return api.SetSnoozeContext(context.Background(), minutes)
|
||||
}
|
||||
|
||||
// SetSnoozeContext adjusts the snooze duration for a user's Do Not Disturb settings with a custom context.
|
||||
// For more information see the SetSnooze docs
|
||||
func (api *Client) SetSnoozeContext(ctx context.Context, minutes int) (*DNDStatus, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"num_minutes": {strconv.Itoa(minutes)},
|
||||
}
|
||||
|
||||
response, err := api.dndRequest(ctx, "dnd.setSnooze", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.DNDStatus, nil
|
||||
}
|
35
vendor/github.com/slack-go/slack/emoji.go
generated
vendored
Normal file
35
vendor/github.com/slack-go/slack/emoji.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type emojiResponseFull struct {
|
||||
Emoji map[string]string `json:"emoji"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
// GetEmoji retrieves all the emojis
|
||||
func (api *Client) GetEmoji() (map[string]string, error) {
|
||||
return api.GetEmojiContext(context.Background())
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
response := &emojiResponseFull{}
|
||||
|
||||
err := api.postMethod(ctx, "emoji.list", values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.Err() != nil {
|
||||
return nil, response.Err()
|
||||
}
|
||||
|
||||
return response.Emoji, nil
|
||||
}
|
20
vendor/github.com/slack-go/slack/errors.go
generated
vendored
Normal file
20
vendor/github.com/slack-go/slack/errors.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
package slack
|
||||
|
||||
import "github.com/slack-go/slack/internal/errorsx"
|
||||
|
||||
// Errors returned by various methods.
|
||||
const (
|
||||
ErrAlreadyDisconnected = errorsx.String("Invalid call to Disconnect - Slack API is already disconnected")
|
||||
ErrRTMDisconnected = errorsx.String("disconnect received while trying to connect")
|
||||
ErrRTMGoodbye = errorsx.String("goodbye detected")
|
||||
ErrRTMDeadman = errorsx.String("deadman switch triggered")
|
||||
ErrParametersMissing = errorsx.String("received empty parameters")
|
||||
ErrInvalidConfiguration = errorsx.String("invalid configuration")
|
||||
ErrMissingHeaders = errorsx.String("missing headers")
|
||||
ErrExpiredTimestamp = errorsx.String("timestamp is too old")
|
||||
)
|
||||
|
||||
// internal errors
|
||||
const (
|
||||
errPaginationComplete = errorsx.String("pagination complete")
|
||||
)
|
404
vendor/github.com/slack-go/slack/files.go
generated
vendored
Normal file
404
vendor/github.com/slack-go/slack/files.go
generated
vendored
Normal file
@ -0,0 +1,404 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// Add here the defaults in the siten
|
||||
DEFAULT_FILES_USER = ""
|
||||
DEFAULT_FILES_CHANNEL = ""
|
||||
DEFAULT_FILES_TS_FROM = 0
|
||||
DEFAULT_FILES_TS_TO = -1
|
||||
DEFAULT_FILES_TYPES = "all"
|
||||
DEFAULT_FILES_COUNT = 100
|
||||
DEFAULT_FILES_PAGE = 1
|
||||
)
|
||||
|
||||
// File contains all the information for a file
|
||||
type File struct {
|
||||
ID string `json:"id"`
|
||||
Created JSONTime `json:"created"`
|
||||
Timestamp JSONTime `json:"timestamp"`
|
||||
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Mimetype string `json:"mimetype"`
|
||||
ImageExifRotation int `json:"image_exif_rotation"`
|
||||
Filetype string `json:"filetype"`
|
||||
PrettyType string `json:"pretty_type"`
|
||||
User string `json:"user"`
|
||||
|
||||
Mode string `json:"mode"`
|
||||
Editable bool `json:"editable"`
|
||||
IsExternal bool `json:"is_external"`
|
||||
ExternalType string `json:"external_type"`
|
||||
|
||||
Size int `json:"size"`
|
||||
|
||||
URL string `json:"url"` // Deprecated - never set
|
||||
URLDownload string `json:"url_download"` // Deprecated - never set
|
||||
URLPrivate string `json:"url_private"`
|
||||
URLPrivateDownload string `json:"url_private_download"`
|
||||
|
||||
OriginalH int `json:"original_h"`
|
||||
OriginalW int `json:"original_w"`
|
||||
Thumb64 string `json:"thumb_64"`
|
||||
Thumb80 string `json:"thumb_80"`
|
||||
Thumb160 string `json:"thumb_160"`
|
||||
Thumb360 string `json:"thumb_360"`
|
||||
Thumb360Gif string `json:"thumb_360_gif"`
|
||||
Thumb360W int `json:"thumb_360_w"`
|
||||
Thumb360H int `json:"thumb_360_h"`
|
||||
Thumb480 string `json:"thumb_480"`
|
||||
Thumb480W int `json:"thumb_480_w"`
|
||||
Thumb480H int `json:"thumb_480_h"`
|
||||
Thumb720 string `json:"thumb_720"`
|
||||
Thumb720W int `json:"thumb_720_w"`
|
||||
Thumb720H int `json:"thumb_720_h"`
|
||||
Thumb960 string `json:"thumb_960"`
|
||||
Thumb960W int `json:"thumb_960_w"`
|
||||
Thumb960H int `json:"thumb_960_h"`
|
||||
Thumb1024 string `json:"thumb_1024"`
|
||||
Thumb1024W int `json:"thumb_1024_w"`
|
||||
Thumb1024H int `json:"thumb_1024_h"`
|
||||
|
||||
Permalink string `json:"permalink"`
|
||||
PermalinkPublic string `json:"permalink_public"`
|
||||
|
||||
EditLink string `json:"edit_link"`
|
||||
Preview string `json:"preview"`
|
||||
PreviewHighlight string `json:"preview_highlight"`
|
||||
Lines int `json:"lines"`
|
||||
LinesMore int `json:"lines_more"`
|
||||
|
||||
IsPublic bool `json:"is_public"`
|
||||
PublicURLShared bool `json:"public_url_shared"`
|
||||
Channels []string `json:"channels"`
|
||||
Groups []string `json:"groups"`
|
||||
IMs []string `json:"ims"`
|
||||
InitialComment Comment `json:"initial_comment"`
|
||||
CommentsCount int `json:"comments_count"`
|
||||
NumStars int `json:"num_stars"`
|
||||
IsStarred bool `json:"is_starred"`
|
||||
Shares Share `json:"shares"`
|
||||
}
|
||||
|
||||
type Share struct {
|
||||
Public map[string][]ShareFileInfo `json:"public"`
|
||||
Private map[string][]ShareFileInfo `json:"private"`
|
||||
}
|
||||
|
||||
type ShareFileInfo struct {
|
||||
ReplyUsers []string `json:"reply_users"`
|
||||
ReplyUsersCount int `json:"reply_users_count"`
|
||||
ReplyCount int `json:"reply_count"`
|
||||
Ts string `json:"ts"`
|
||||
ThreadTs string `json:"thread_ts"`
|
||||
LatestReply string `json:"latest_reply"`
|
||||
ChannelName string `json:"channel_name"`
|
||||
TeamID string `json:"team_id"`
|
||||
}
|
||||
|
||||
// FileUploadParameters contains all the parameters necessary (including the optional ones) for an UploadFile() request.
|
||||
//
|
||||
// There are three ways to upload a file. You can either set Content if file is small, set Reader if file is large,
|
||||
// or provide a local file path in File to upload it from your filesystem.
|
||||
//
|
||||
// Note that when using the Reader option, you *must* specify the Filename, otherwise the Slack API isn't happy.
|
||||
type FileUploadParameters struct {
|
||||
File string
|
||||
Content string
|
||||
Reader io.Reader
|
||||
Filetype string
|
||||
Filename string
|
||||
Title string
|
||||
InitialComment string
|
||||
Channels []string
|
||||
ThreadTimestamp string
|
||||
}
|
||||
|
||||
// GetFilesParameters contains all the parameters necessary (including the optional ones) for a GetFiles() request
|
||||
type GetFilesParameters struct {
|
||||
User string
|
||||
Channel string
|
||||
TimestampFrom JSONTime
|
||||
TimestampTo JSONTime
|
||||
Types string
|
||||
Count int
|
||||
Page int
|
||||
}
|
||||
|
||||
// ListFilesParameters contains all the parameters necessary (including the optional ones) for a ListFiles() request
|
||||
type ListFilesParameters struct {
|
||||
Limit int
|
||||
User string
|
||||
Channel string
|
||||
Types string
|
||||
Cursor string
|
||||
}
|
||||
|
||||
type fileResponseFull struct {
|
||||
File `json:"file"`
|
||||
Paging `json:"paging"`
|
||||
Comments []Comment `json:"comments"`
|
||||
Files []File `json:"files"`
|
||||
Metadata ResponseMetadata `json:"response_metadata"`
|
||||
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
// NewGetFilesParameters provides an instance of GetFilesParameters with all the sane default values set
|
||||
func NewGetFilesParameters() GetFilesParameters {
|
||||
return GetFilesParameters{
|
||||
User: DEFAULT_FILES_USER,
|
||||
Channel: DEFAULT_FILES_CHANNEL,
|
||||
TimestampFrom: DEFAULT_FILES_TS_FROM,
|
||||
TimestampTo: DEFAULT_FILES_TS_TO,
|
||||
Types: DEFAULT_FILES_TYPES,
|
||||
Count: DEFAULT_FILES_COUNT,
|
||||
Page: DEFAULT_FILES_PAGE,
|
||||
}
|
||||
}
|
||||
|
||||
func (api *Client) fileRequest(ctx context.Context, path string, values url.Values) (*fileResponseFull, error) {
|
||||
response := &fileResponseFull{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// GetFileInfo retrieves a file and related comments
|
||||
func (api *Client) GetFileInfo(fileID string, count, page int) (*File, []Comment, *Paging, error) {
|
||||
return api.GetFileInfoContext(context.Background(), fileID, count, page)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"file": {fileID},
|
||||
"count": {strconv.Itoa(count)},
|
||||
"page": {strconv.Itoa(page)},
|
||||
}
|
||||
|
||||
response, err := api.fileRequest(ctx, "files.info", values)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return &response.File, response.Comments, &response.Paging, nil
|
||||
}
|
||||
|
||||
// GetFile retreives a given file from its private download URL
|
||||
func (api *Client) GetFile(downloadURL string, writer io.Writer) error {
|
||||
return downloadFile(api.httpclient, api.token, downloadURL, writer, api)
|
||||
}
|
||||
|
||||
// GetFiles retrieves all files according to the parameters given
|
||||
func (api *Client) GetFiles(params GetFilesParameters) ([]File, *Paging, error) {
|
||||
return api.GetFilesContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// ListFiles retrieves all files according to the parameters given. Uses cursor based pagination.
|
||||
func (api *Client) ListFiles(params ListFilesParameters) ([]File, *ListFilesParameters, error) {
|
||||
return api.ListFilesContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// ListFilesContext retrieves all files according to the parameters given with a custom context. Uses cursor based pagination.
|
||||
func (api *Client) ListFilesContext(ctx context.Context, params ListFilesParameters) ([]File, *ListFilesParameters, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
if params.User != DEFAULT_FILES_USER {
|
||||
values.Add("user", params.User)
|
||||
}
|
||||
if params.Channel != DEFAULT_FILES_CHANNEL {
|
||||
values.Add("channel", params.Channel)
|
||||
}
|
||||
if params.Limit != DEFAULT_FILES_COUNT {
|
||||
values.Add("limit", strconv.Itoa(params.Limit))
|
||||
}
|
||||
if params.Cursor != "" {
|
||||
values.Add("cursor", params.Cursor)
|
||||
}
|
||||
|
||||
response, err := api.fileRequest(ctx, "files.list", values)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
params.Cursor = response.Metadata.Cursor
|
||||
|
||||
return response.Files, ¶ms, nil
|
||||
}
|
||||
|
||||
// GetFilesContext retrieves all files according to the parameters given with a custom context
|
||||
func (api *Client) GetFilesContext(ctx context.Context, params GetFilesParameters) ([]File, *Paging, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.User != DEFAULT_FILES_USER {
|
||||
values.Add("user", params.User)
|
||||
}
|
||||
if params.Channel != DEFAULT_FILES_CHANNEL {
|
||||
values.Add("channel", params.Channel)
|
||||
}
|
||||
if params.TimestampFrom != DEFAULT_FILES_TS_FROM {
|
||||
values.Add("ts_from", strconv.FormatInt(int64(params.TimestampFrom), 10))
|
||||
}
|
||||
if params.TimestampTo != DEFAULT_FILES_TS_TO {
|
||||
values.Add("ts_to", strconv.FormatInt(int64(params.TimestampTo), 10))
|
||||
}
|
||||
if params.Types != DEFAULT_FILES_TYPES {
|
||||
values.Add("types", params.Types)
|
||||
}
|
||||
if params.Count != DEFAULT_FILES_COUNT {
|
||||
values.Add("count", strconv.Itoa(params.Count))
|
||||
}
|
||||
if params.Page != DEFAULT_FILES_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
|
||||
response, err := api.fileRequest(ctx, "files.list", values)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return response.Files, &response.Paging, nil
|
||||
}
|
||||
|
||||
// UploadFile uploads a file
|
||||
func (api *Client) UploadFile(params FileUploadParameters) (file *File, err error) {
|
||||
return api.UploadFileContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// UploadFileContext uploads a file and setting a custom context
|
||||
func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParameters) (file *File, err error) {
|
||||
// Test if user token is valid. This helps because client.Do doesn't like this for some reason. XXX: More
|
||||
// investigation needed, but for now this will do.
|
||||
_, err = api.AuthTest()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response := &fileResponseFull{}
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.Filetype != "" {
|
||||
values.Add("filetype", params.Filetype)
|
||||
}
|
||||
if params.Filename != "" {
|
||||
values.Add("filename", params.Filename)
|
||||
}
|
||||
if params.Title != "" {
|
||||
values.Add("title", params.Title)
|
||||
}
|
||||
if params.InitialComment != "" {
|
||||
values.Add("initial_comment", params.InitialComment)
|
||||
}
|
||||
if params.ThreadTimestamp != "" {
|
||||
values.Add("thread_ts", params.ThreadTimestamp)
|
||||
}
|
||||
if len(params.Channels) != 0 {
|
||||
values.Add("channels", strings.Join(params.Channels, ","))
|
||||
}
|
||||
if params.Content != "" {
|
||||
values.Add("content", params.Content)
|
||||
err = api.postMethod(ctx, "files.upload", values, response)
|
||||
} else if params.File != "" {
|
||||
err = postLocalWithMultipartResponse(ctx, api.httpclient, api.endpoint+"files.upload", params.File, "file", values, response, api)
|
||||
} else if params.Reader != nil {
|
||||
if params.Filename == "" {
|
||||
return nil, fmt.Errorf("files.upload: FileUploadParameters.Filename is mandatory when using FileUploadParameters.Reader")
|
||||
}
|
||||
err = postWithMultipartResponse(ctx, api.httpclient, api.endpoint+"files.upload", params.Filename, "file", values, params.Reader, response, api)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.File, response.Err()
|
||||
}
|
||||
|
||||
// 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 ErrParametersMissing
|
||||
}
|
||||
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"file": {fileID},
|
||||
"id": {commentID},
|
||||
}
|
||||
_, err = api.fileRequest(ctx, "files.comments.delete", values)
|
||||
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) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"file": {fileID},
|
||||
}
|
||||
|
||||
_, err = api.fileRequest(ctx, "files.delete", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// RevokeFilePublicURL disables public/external sharing for a file
|
||||
func (api *Client) RevokeFilePublicURL(fileID string) (*File, error) {
|
||||
return api.RevokeFilePublicURLContext(context.Background(), fileID)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"file": {fileID},
|
||||
}
|
||||
|
||||
response, err := api.fileRequest(ctx, "files.revokePublicURL", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.File, nil
|
||||
}
|
||||
|
||||
// ShareFilePublicURL enabled public/external sharing for a file
|
||||
func (api *Client) ShareFilePublicURL(fileID string) (*File, []Comment, *Paging, error) {
|
||||
return api.ShareFilePublicURLContext(context.Background(), fileID)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"file": {fileID},
|
||||
}
|
||||
|
||||
response, err := api.fileRequest(ctx, "files.sharedPublicURL", values)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return &response.File, response.Comments, &response.Paging, nil
|
||||
}
|
12
vendor/github.com/slack-go/slack/go.mod
generated
vendored
Normal file
12
vendor/github.com/slack-go/slack/go.mod
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
module github.com/slack-go/slack
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-test/deep v1.0.4
|
||||
github.com/gorilla/websocket v1.2.0
|
||||
github.com/pkg/errors v0.8.0
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
)
|
12
vendor/github.com/slack-go/slack/go.sum
generated
vendored
Normal file
12
vendor/github.com/slack-go/slack/go.sum
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
|
||||
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
|
||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
355
vendor/github.com/slack-go/slack/groups.go
generated
vendored
Normal file
355
vendor/github.com/slack-go/slack/groups.go
generated
vendored
Normal file
@ -0,0 +1,355 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Group contains all the information for a group
|
||||
type Group struct {
|
||||
GroupConversation
|
||||
IsGroup bool `json:"is_group"`
|
||||
}
|
||||
|
||||
type groupResponseFull struct {
|
||||
Group Group `json:"group"`
|
||||
Groups []Group `json:"groups"`
|
||||
Purpose string `json:"purpose"`
|
||||
Topic string `json:"topic"`
|
||||
NotInGroup bool `json:"not_in_group"`
|
||||
NoOp bool `json:"no_op"`
|
||||
AlreadyClosed bool `json:"already_closed"`
|
||||
AlreadyOpen bool `json:"already_open"`
|
||||
AlreadyInGroup bool `json:"already_in_group"`
|
||||
Channel Channel `json:"channel"`
|
||||
History
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func (api *Client) groupRequest(ctx context.Context, path string, values url.Values) (*groupResponseFull, error) {
|
||||
response := &groupResponseFull{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// ArchiveGroup archives a private group
|
||||
func (api *Client) ArchiveGroup(group string) error {
|
||||
return api.ArchiveGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// ArchiveGroupContext archives a private group
|
||||
func (api *Client) ArchiveGroupContext(ctx context.Context, group string) error {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
|
||||
_, err := api.groupRequest(ctx, "groups.archive", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// UnarchiveGroup unarchives a private group
|
||||
func (api *Client) UnarchiveGroup(group string) error {
|
||||
return api.UnarchiveGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// UnarchiveGroupContext unarchives a private group
|
||||
func (api *Client) UnarchiveGroupContext(ctx context.Context, group string) error {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
|
||||
_, err := api.groupRequest(ctx, "groups.unarchive", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateGroup creates a private group
|
||||
func (api *Client) CreateGroup(group string) (*Group, error) {
|
||||
return api.CreateGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// CreateGroupContext creates a private group
|
||||
func (api *Client) CreateGroupContext(ctx context.Context, group string) (*Group, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"name": {group},
|
||||
}
|
||||
|
||||
response, err := api.groupRequest(ctx, "groups.create", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Group, nil
|
||||
}
|
||||
|
||||
// CreateChildGroup creates a new private group archiving the old one
|
||||
// This method takes an existing private group and performs the following steps:
|
||||
// 1. Renames the existing group (from "example" to "example-archived").
|
||||
// 2. Archives the existing group.
|
||||
// 3. Creates a new group with the name of the existing group.
|
||||
// 4. Adds all members of the existing group to the new group.
|
||||
func (api *Client) CreateChildGroup(group string) (*Group, error) {
|
||||
return api.CreateChildGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"channel": {group},
|
||||
}
|
||||
|
||||
response, err := api.groupRequest(ctx, "groups.createChild", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Group, nil
|
||||
}
|
||||
|
||||
// GetGroupHistory fetches all the history for a private group
|
||||
func (api *Client) GetGroupHistory(group string, params HistoryParameters) (*History, error) {
|
||||
return api.GetGroupHistoryContext(context.Background(), group, params)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"channel": {group},
|
||||
}
|
||||
if params.Latest != DEFAULT_HISTORY_LATEST {
|
||||
values.Add("latest", params.Latest)
|
||||
}
|
||||
if params.Oldest != DEFAULT_HISTORY_OLDEST {
|
||||
values.Add("oldest", params.Oldest)
|
||||
}
|
||||
if params.Count != DEFAULT_HISTORY_COUNT {
|
||||
values.Add("count", strconv.Itoa(params.Count))
|
||||
}
|
||||
if params.Inclusive != DEFAULT_HISTORY_INCLUSIVE {
|
||||
if params.Inclusive {
|
||||
values.Add("inclusive", "1")
|
||||
} else {
|
||||
values.Add("inclusive", "0")
|
||||
}
|
||||
}
|
||||
if params.Unreads != DEFAULT_HISTORY_UNREADS {
|
||||
if params.Unreads {
|
||||
values.Add("unreads", "1")
|
||||
} else {
|
||||
values.Add("unreads", "0")
|
||||
}
|
||||
}
|
||||
|
||||
response, err := api.groupRequest(ctx, "groups.history", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.History, nil
|
||||
}
|
||||
|
||||
// InviteUserToGroup invites a specific user to a private group
|
||||
func (api *Client) InviteUserToGroup(group, user string) (*Group, bool, error) {
|
||||
return api.InviteUserToGroupContext(context.Background(), group, user)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"channel": {group},
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
response, err := api.groupRequest(ctx, "groups.invite", values)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return &response.Group, response.AlreadyInGroup, nil
|
||||
}
|
||||
|
||||
// LeaveGroup makes authenticated user leave the group
|
||||
func (api *Client) LeaveGroup(group string) error {
|
||||
return api.LeaveGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// LeaveGroupContext makes authenticated user leave the group with a custom context
|
||||
func (api *Client) LeaveGroupContext(ctx context.Context, group string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
|
||||
_, err = api.groupRequest(ctx, "groups.leave", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// KickUserFromGroup kicks a user from a group
|
||||
func (api *Client) KickUserFromGroup(group, user string) error {
|
||||
return api.KickUserFromGroupContext(context.Background(), group, user)
|
||||
}
|
||||
|
||||
// KickUserFromGroupContext kicks a user from a group with a custom context
|
||||
func (api *Client) KickUserFromGroupContext(ctx context.Context, group, user string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
_, err = api.groupRequest(ctx, "groups.kick", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetGroups retrieves all groups
|
||||
func (api *Client) GetGroups(excludeArchived bool) ([]Group, error) {
|
||||
return api.GetGroupsContext(context.Background(), excludeArchived)
|
||||
}
|
||||
|
||||
// GetGroupsContext retrieves all groups with a custom context
|
||||
func (api *Client) GetGroupsContext(ctx context.Context, excludeArchived bool) ([]Group, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
}
|
||||
if excludeArchived {
|
||||
values.Add("exclude_archived", "1")
|
||||
}
|
||||
|
||||
response, err := api.groupRequest(ctx, "groups.list", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.Groups, nil
|
||||
}
|
||||
|
||||
// GetGroupInfo retrieves the given group
|
||||
func (api *Client) GetGroupInfo(group string) (*Group, error) {
|
||||
return api.GetGroupInfoContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// GetGroupInfoContext retrieves the given group with a custom context
|
||||
func (api *Client) GetGroupInfoContext(ctx context.Context, group string) (*Group, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"include_locale": {strconv.FormatBool(true)},
|
||||
}
|
||||
|
||||
response, err := api.groupRequest(ctx, "groups.info", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Group, nil
|
||||
}
|
||||
|
||||
// SetGroupReadMark sets the read mark on a private group
|
||||
// Clients should try to avoid making this call too often. When needing to mark a read position, a client should set a
|
||||
// timer before making the call. In this way, any further updates needed during the timeout will not generate extra
|
||||
// calls (just one per channel). This is useful for when reading scroll-back history, or following a busy live
|
||||
// channel. A timeout of 5 seconds is a good starting point. Be sure to flush these calls on shutdown/logout.
|
||||
func (api *Client) SetGroupReadMark(group, ts string) error {
|
||||
return api.SetGroupReadMarkContext(context.Background(), group, ts)
|
||||
}
|
||||
|
||||
// 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) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"ts": {ts},
|
||||
}
|
||||
|
||||
_, err = api.groupRequest(ctx, "groups.mark", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// OpenGroup opens a private group
|
||||
func (api *Client) OpenGroup(group string) (bool, bool, error) {
|
||||
return api.OpenGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"channel": {group},
|
||||
}
|
||||
|
||||
response, err := api.groupRequest(ctx, "groups.open", values)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
return response.NoOp, response.AlreadyOpen, nil
|
||||
}
|
||||
|
||||
// RenameGroup renames a group
|
||||
// XXX: They return a channel, not a group. What is this crap? :(
|
||||
// Inconsistent api it seems.
|
||||
func (api *Client) RenameGroup(group, name string) (*Channel, error) {
|
||||
return api.RenameGroupContext(context.Background(), group, name)
|
||||
}
|
||||
|
||||
// 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.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 := api.groupRequest(ctx, "groups.rename", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Channel, nil
|
||||
}
|
||||
|
||||
// SetGroupPurpose sets the group purpose
|
||||
func (api *Client) SetGroupPurpose(group, purpose string) (string, error) {
|
||||
return api.SetGroupPurposeContext(context.Background(), group, purpose)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"channel": {group},
|
||||
"purpose": {purpose},
|
||||
}
|
||||
|
||||
response, err := api.groupRequest(ctx, "groups.setPurpose", values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return response.Purpose, nil
|
||||
}
|
||||
|
||||
// SetGroupTopic sets the group topic
|
||||
func (api *Client) SetGroupTopic(group, topic string) (string, error) {
|
||||
return api.SetGroupTopicContext(context.Background(), group, topic)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"channel": {group},
|
||||
"topic": {topic},
|
||||
}
|
||||
|
||||
response, err := api.groupRequest(ctx, "groups.setTopic", values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return response.Topic, nil
|
||||
}
|
36
vendor/github.com/slack-go/slack/history.go
generated
vendored
Normal file
36
vendor/github.com/slack-go/slack/history.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package slack
|
||||
|
||||
const (
|
||||
DEFAULT_HISTORY_LATEST = ""
|
||||
DEFAULT_HISTORY_OLDEST = "0"
|
||||
DEFAULT_HISTORY_COUNT = 100
|
||||
DEFAULT_HISTORY_INCLUSIVE = false
|
||||
DEFAULT_HISTORY_UNREADS = false
|
||||
)
|
||||
|
||||
// HistoryParameters contains all the necessary information to help in the retrieval of history for Channels/Groups/DMs
|
||||
type HistoryParameters struct {
|
||||
Latest string
|
||||
Oldest string
|
||||
Count int
|
||||
Inclusive bool
|
||||
Unreads bool
|
||||
}
|
||||
|
||||
// History contains message history information needed to navigate a Channel / Group / DM history
|
||||
type History struct {
|
||||
Latest string `json:"latest"`
|
||||
Messages []Message `json:"messages"`
|
||||
HasMore bool `json:"has_more"`
|
||||
}
|
||||
|
||||
// NewHistoryParameters provides an instance of HistoryParameters with all the sane default values set
|
||||
func NewHistoryParameters() HistoryParameters {
|
||||
return HistoryParameters{
|
||||
Latest: DEFAULT_HISTORY_LATEST,
|
||||
Oldest: DEFAULT_HISTORY_OLDEST,
|
||||
Count: DEFAULT_HISTORY_COUNT,
|
||||
Inclusive: DEFAULT_HISTORY_INCLUSIVE,
|
||||
Unreads: DEFAULT_HISTORY_UNREADS,
|
||||
}
|
||||
}
|
154
vendor/github.com/slack-go/slack/im.go
generated
vendored
Normal file
154
vendor/github.com/slack-go/slack/im.go
generated
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type imChannel struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type imResponseFull struct {
|
||||
NoOp bool `json:"no_op"`
|
||||
AlreadyClosed bool `json:"already_closed"`
|
||||
AlreadyOpen bool `json:"already_open"`
|
||||
Channel imChannel `json:"channel"`
|
||||
IMs []IM `json:"ims"`
|
||||
History
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
// IM contains information related to the Direct Message channel
|
||||
type IM struct {
|
||||
Conversation
|
||||
IsUserDeleted bool `json:"is_user_deleted"`
|
||||
}
|
||||
|
||||
func (api *Client) imRequest(ctx context.Context, path string, values url.Values) (*imResponseFull, error) {
|
||||
response := &imResponseFull{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// CloseIMChannel closes the direct message channel
|
||||
func (api *Client) CloseIMChannel(channel string) (bool, bool, error) {
|
||||
return api.CloseIMChannelContext(context.Background(), channel)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"channel": {channel},
|
||||
}
|
||||
|
||||
response, err := api.imRequest(ctx, "im.close", values)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
return response.NoOp, response.AlreadyClosed, nil
|
||||
}
|
||||
|
||||
// OpenIMChannel opens a direct message channel to the user provided as argument
|
||||
// Returns some status and the channel ID
|
||||
func (api *Client) OpenIMChannel(user string) (bool, bool, string, error) {
|
||||
return api.OpenIMChannelContext(context.Background(), user)
|
||||
}
|
||||
|
||||
// OpenIMChannelContext opens a direct message channel to the user provided as argument with a custom context
|
||||
// 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.token},
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
response, err := api.imRequest(ctx, "im.open", values)
|
||||
if err != nil {
|
||||
return false, false, "", err
|
||||
}
|
||||
return response.NoOp, response.AlreadyOpen, response.Channel.ID, nil
|
||||
}
|
||||
|
||||
// MarkIMChannel sets the read mark of a direct message channel to a specific point
|
||||
func (api *Client) MarkIMChannel(channel, ts string) (err error) {
|
||||
return api.MarkIMChannelContext(context.Background(), channel, ts)
|
||||
}
|
||||
|
||||
// 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) error {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channel},
|
||||
"ts": {ts},
|
||||
}
|
||||
|
||||
_, err := api.imRequest(ctx, "im.mark", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetIMHistory retrieves the direct message channel history
|
||||
func (api *Client) GetIMHistory(channel string, params HistoryParameters) (*History, error) {
|
||||
return api.GetIMHistoryContext(context.Background(), channel, params)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"channel": {channel},
|
||||
}
|
||||
if params.Latest != DEFAULT_HISTORY_LATEST {
|
||||
values.Add("latest", params.Latest)
|
||||
}
|
||||
if params.Oldest != DEFAULT_HISTORY_OLDEST {
|
||||
values.Add("oldest", params.Oldest)
|
||||
}
|
||||
if params.Count != DEFAULT_HISTORY_COUNT {
|
||||
values.Add("count", strconv.Itoa(params.Count))
|
||||
}
|
||||
if params.Inclusive != DEFAULT_HISTORY_INCLUSIVE {
|
||||
if params.Inclusive {
|
||||
values.Add("inclusive", "1")
|
||||
} else {
|
||||
values.Add("inclusive", "0")
|
||||
}
|
||||
}
|
||||
if params.Unreads != DEFAULT_HISTORY_UNREADS {
|
||||
if params.Unreads {
|
||||
values.Add("unreads", "1")
|
||||
} else {
|
||||
values.Add("unreads", "0")
|
||||
}
|
||||
}
|
||||
|
||||
response, err := api.imRequest(ctx, "im.history", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.History, nil
|
||||
}
|
||||
|
||||
// GetIMChannels returns the list of direct message channels
|
||||
func (api *Client) GetIMChannels() ([]IM, error) {
|
||||
return api.GetIMChannelsContext(context.Background())
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
|
||||
response, err := api.imRequest(ctx, "im.list", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.IMs, nil
|
||||
}
|
195
vendor/github.com/slack-go/slack/info.go
generated
vendored
Normal file
195
vendor/github.com/slack-go/slack/info.go
generated
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UserPrefs needs to be implemented
|
||||
type UserPrefs struct {
|
||||
// "highlight_words":"",
|
||||
// "user_colors":"",
|
||||
// "color_names_in_list":true,
|
||||
// "growls_enabled":true,
|
||||
// "tz":"Europe\/London",
|
||||
// "push_dm_alert":true,
|
||||
// "push_mention_alert":true,
|
||||
// "push_everything":true,
|
||||
// "push_idle_wait":2,
|
||||
// "push_sound":"b2.mp3",
|
||||
// "push_loud_channels":"",
|
||||
// "push_mention_channels":"",
|
||||
// "push_loud_channels_set":"",
|
||||
// "email_alerts":"instant",
|
||||
// "email_alerts_sleep_until":0,
|
||||
// "email_misc":false,
|
||||
// "email_weekly":true,
|
||||
// "welcome_message_hidden":false,
|
||||
// "all_channels_loud":true,
|
||||
// "loud_channels":"",
|
||||
// "never_channels":"",
|
||||
// "loud_channels_set":"",
|
||||
// "show_member_presence":true,
|
||||
// "search_sort":"timestamp",
|
||||
// "expand_inline_imgs":true,
|
||||
// "expand_internal_inline_imgs":true,
|
||||
// "expand_snippets":false,
|
||||
// "posts_formatting_guide":true,
|
||||
// "seen_welcome_2":true,
|
||||
// "seen_ssb_prompt":false,
|
||||
// "search_only_my_channels":false,
|
||||
// "emoji_mode":"default",
|
||||
// "has_invited":true,
|
||||
// "has_uploaded":false,
|
||||
// "has_created_channel":true,
|
||||
// "search_exclude_channels":"",
|
||||
// "messages_theme":"default",
|
||||
// "webapp_spellcheck":true,
|
||||
// "no_joined_overlays":false,
|
||||
// "no_created_overlays":true,
|
||||
// "dropbox_enabled":false,
|
||||
// "seen_user_menu_tip_card":true,
|
||||
// "seen_team_menu_tip_card":true,
|
||||
// "seen_channel_menu_tip_card":true,
|
||||
// "seen_message_input_tip_card":true,
|
||||
// "seen_channels_tip_card":true,
|
||||
// "seen_domain_invite_reminder":false,
|
||||
// "seen_member_invite_reminder":false,
|
||||
// "seen_flexpane_tip_card":true,
|
||||
// "seen_search_input_tip_card":true,
|
||||
// "mute_sounds":false,
|
||||
// "arrow_history":false,
|
||||
// "tab_ui_return_selects":true,
|
||||
// "obey_inline_img_limit":true,
|
||||
// "new_msg_snd":"knock_brush.mp3",
|
||||
// "collapsible":false,
|
||||
// "collapsible_by_click":true,
|
||||
// "require_at":false,
|
||||
// "mac_ssb_bounce":"",
|
||||
// "mac_ssb_bullet":true,
|
||||
// "win_ssb_bullet":true,
|
||||
// "expand_non_media_attachments":true,
|
||||
// "show_typing":true,
|
||||
// "pagekeys_handled":true,
|
||||
// "last_snippet_type":"",
|
||||
// "display_real_names_override":0,
|
||||
// "time24":false,
|
||||
// "enter_is_special_in_tbt":false,
|
||||
// "graphic_emoticons":false,
|
||||
// "convert_emoticons":true,
|
||||
// "autoplay_chat_sounds":true,
|
||||
// "ss_emojis":true,
|
||||
// "sidebar_behavior":"",
|
||||
// "mark_msgs_read_immediately":true,
|
||||
// "start_scroll_at_oldest":true,
|
||||
// "snippet_editor_wrap_long_lines":false,
|
||||
// "ls_disabled":false,
|
||||
// "sidebar_theme":"default",
|
||||
// "sidebar_theme_custom_values":"",
|
||||
// "f_key_search":false,
|
||||
// "k_key_omnibox":true,
|
||||
// "speak_growls":false,
|
||||
// "mac_speak_voice":"com.apple.speech.synthesis.voice.Alex",
|
||||
// "mac_speak_speed":250,
|
||||
// "comma_key_prefs":false,
|
||||
// "at_channel_suppressed_channels":"",
|
||||
// "push_at_channel_suppressed_channels":"",
|
||||
// "prompted_for_email_disabling":false,
|
||||
// "full_text_extracts":false,
|
||||
// "no_text_in_notifications":false,
|
||||
// "muted_channels":"",
|
||||
// "no_macssb1_banner":false,
|
||||
// "privacy_policy_seen":true,
|
||||
// "search_exclude_bots":false,
|
||||
// "fuzzy_matching":false
|
||||
}
|
||||
|
||||
// UserDetails contains user details coming in the initial response from StartRTM
|
||||
type UserDetails struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Created JSONTime `json:"created"`
|
||||
ManualPresence string `json:"manual_presence"`
|
||||
Prefs UserPrefs `json:"prefs"`
|
||||
}
|
||||
|
||||
// JSONTime exists so that we can have a String method converting the date
|
||||
type JSONTime int64
|
||||
|
||||
// String converts the unix timestamp into a string
|
||||
func (t JSONTime) String() string {
|
||||
tm := t.Time()
|
||||
return fmt.Sprintf("\"%s\"", tm.Format("Mon Jan _2"))
|
||||
}
|
||||
|
||||
// Time returns a `time.Time` representation of this value.
|
||||
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"`
|
||||
Name string `json:"name"`
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
// Icons XXX: needs further investigation
|
||||
type Icons struct {
|
||||
Image36 string `json:"image_36,omitempty"`
|
||||
Image48 string `json:"image_48,omitempty"`
|
||||
Image72 string `json:"image_72,omitempty"`
|
||||
}
|
||||
|
||||
// Info contains various details about the authenticated user and team.
|
||||
// It is returned by StartRTM or included in the "ConnectedEvent" RTM event.
|
||||
type Info struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
User *UserDetails `json:"self,omitempty"`
|
||||
Team *Team `json:"team,omitempty"`
|
||||
}
|
||||
|
||||
type infoResponseFull struct {
|
||||
Info
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
// GetBotByID is deprecated and returns nil
|
||||
func (info Info) GetBotByID(botID string) *Bot {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserByID is deprecated and returns nil
|
||||
func (info Info) GetUserByID(userID string) *User {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetChannelByID is deprecated and returns nil
|
||||
func (info Info) GetChannelByID(channelID string) *Channel {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetGroupByID is deprecated and returns nil
|
||||
func (info Info) GetGroupByID(groupID string) *Group {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetIMByID is deprecated and returns nil
|
||||
func (info Info) GetIMByID(imID string) *IM {
|
||||
return nil
|
||||
}
|
142
vendor/github.com/slack-go/slack/interactions.go
generated
vendored
Normal file
142
vendor/github.com/slack-go/slack/interactions.go
generated
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// InteractionType type of interactions
|
||||
type InteractionType string
|
||||
|
||||
// ActionType type represents the type of action (attachment, block, etc.)
|
||||
type actionType string
|
||||
|
||||
// action is an interface that should be implemented by all callback action types
|
||||
type action interface {
|
||||
actionType() actionType
|
||||
}
|
||||
|
||||
// Types of interactions that can be received.
|
||||
const (
|
||||
InteractionTypeDialogCancellation = InteractionType("dialog_cancellation")
|
||||
InteractionTypeDialogSubmission = InteractionType("dialog_submission")
|
||||
InteractionTypeDialogSuggestion = InteractionType("dialog_suggestion")
|
||||
InteractionTypeInteractionMessage = InteractionType("interactive_message")
|
||||
InteractionTypeMessageAction = InteractionType("message_action")
|
||||
InteractionTypeBlockActions = InteractionType("block_actions")
|
||||
)
|
||||
|
||||
// InteractionCallback is sent from slack when a user interactions with a button or dialog.
|
||||
type InteractionCallback struct {
|
||||
Type InteractionType `json:"type"`
|
||||
Token string `json:"token"`
|
||||
CallbackID string `json:"callback_id"`
|
||||
ResponseURL string `json:"response_url"`
|
||||
TriggerID string `json:"trigger_id"`
|
||||
ActionTs string `json:"action_ts"`
|
||||
Team Team `json:"team"`
|
||||
Channel Channel `json:"channel"`
|
||||
User User `json:"user"`
|
||||
OriginalMessage Message `json:"original_message"`
|
||||
Message Message `json:"message"`
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
MessageTs string `json:"message_ts"`
|
||||
AttachmentID string `json:"attachment_id"`
|
||||
ActionCallback ActionCallbacks `json:"actions"`
|
||||
APIAppID string `json:"api_app_id"`
|
||||
DialogSubmissionCallback
|
||||
}
|
||||
|
||||
// ActionCallback is a convenience struct defined to allow dynamic unmarshalling of
|
||||
// the "actions" value in Slack's JSON response, which varies depending on block type
|
||||
type ActionCallbacks struct {
|
||||
AttachmentActions []*AttachmentAction
|
||||
BlockActions []*BlockAction
|
||||
}
|
||||
|
||||
// MarshalJSON implements the Marshaller interface in order to combine both
|
||||
// action callback types back into a single array, like how the api responds.
|
||||
// This makes Marshaling and Unmarshaling an InteractionCallback symmetrical
|
||||
func (a ActionCallbacks) MarshalJSON() ([]byte, error) {
|
||||
count := 0
|
||||
length := len(a.AttachmentActions) + len(a.BlockActions)
|
||||
buffer := bytes.NewBufferString("[")
|
||||
|
||||
f := func(obj interface{}) error {
|
||||
js, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = buffer.Write(js)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
count++
|
||||
if count < length {
|
||||
_, err = buffer.WriteString(",")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, act := range a.AttachmentActions {
|
||||
err := f(act)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, blk := range a.BlockActions {
|
||||
err := f(blk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
buffer.WriteString("]")
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Marshaller interface in order to delegate
|
||||
// marshalling and allow for proper type assertion when decoding the response
|
||||
func (a *ActionCallbacks) UnmarshalJSON(data []byte) error {
|
||||
var raw []json.RawMessage
|
||||
err := json.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range raw {
|
||||
var obj map[string]interface{}
|
||||
err := json.Unmarshal(r, &obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := obj["block_id"].(string); ok {
|
||||
action, err := unmarshalAction(r, &BlockAction{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.BlockActions = append(a.BlockActions, action.(*BlockAction))
|
||||
return nil
|
||||
}
|
||||
|
||||
action, err := unmarshalAction(r, &AttachmentAction{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.AttachmentActions = append(a.AttachmentActions, action.(*AttachmentAction))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalAction(r json.RawMessage, callbackAction action) (action, error) {
|
||||
err := json.Unmarshal(r, callbackAction)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return callbackAction, nil
|
||||
}
|
8
vendor/github.com/slack-go/slack/internal/errorsx/errorsx.go
generated
vendored
Normal file
8
vendor/github.com/slack-go/slack/internal/errorsx/errorsx.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package errorsx
|
||||
|
||||
// String representing an error, useful for declaring string constants as errors.
|
||||
type String string
|
||||
|
||||
func (t String) Error() string {
|
||||
return string(t)
|
||||
}
|
18
vendor/github.com/slack-go/slack/internal/timex/timex.go
generated
vendored
Normal file
18
vendor/github.com/slack-go/slack/internal/timex/timex.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package timex
|
||||
|
||||
import "time"
|
||||
|
||||
// Max returns the maximum duration
|
||||
func Max(values ...time.Duration) time.Duration {
|
||||
var (
|
||||
max time.Duration
|
||||
)
|
||||
|
||||
for _, v := range values {
|
||||
if v > max {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
}
|
75
vendor/github.com/slack-go/slack/item.go
generated
vendored
Normal file
75
vendor/github.com/slack-go/slack/item.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
package slack
|
||||
|
||||
const (
|
||||
TYPE_MESSAGE = "message"
|
||||
TYPE_FILE = "file"
|
||||
TYPE_FILE_COMMENT = "file_comment"
|
||||
TYPE_CHANNEL = "channel"
|
||||
TYPE_IM = "im"
|
||||
TYPE_GROUP = "group"
|
||||
)
|
||||
|
||||
// Item is any type of slack message - message, file, or file comment.
|
||||
type Item struct {
|
||||
Type string `json:"type"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
Message *Message `json:"message,omitempty"`
|
||||
File *File `json:"file,omitempty"`
|
||||
Comment *Comment `json:"comment,omitempty"`
|
||||
Timestamp string `json:"ts,omitempty"`
|
||||
}
|
||||
|
||||
// NewMessageItem turns a message on a channel into a typed message struct.
|
||||
func NewMessageItem(ch string, m *Message) Item {
|
||||
return Item{Type: TYPE_MESSAGE, Channel: ch, Message: m}
|
||||
}
|
||||
|
||||
// NewFileItem turns a file into a typed file struct.
|
||||
func NewFileItem(f *File) Item {
|
||||
return Item{Type: TYPE_FILE, File: f}
|
||||
}
|
||||
|
||||
// NewFileCommentItem turns a file and comment into a typed file_comment struct.
|
||||
func NewFileCommentItem(f *File, c *Comment) Item {
|
||||
return Item{Type: TYPE_FILE_COMMENT, File: f, Comment: c}
|
||||
}
|
||||
|
||||
// NewChannelItem turns a channel id into a typed channel struct.
|
||||
func NewChannelItem(ch string) Item {
|
||||
return Item{Type: TYPE_CHANNEL, Channel: ch}
|
||||
}
|
||||
|
||||
// NewIMItem turns a channel id into a typed im struct.
|
||||
func NewIMItem(ch string) Item {
|
||||
return Item{Type: TYPE_IM, Channel: ch}
|
||||
}
|
||||
|
||||
// NewGroupItem turns a channel id into a typed group struct.
|
||||
func NewGroupItem(ch string) Item {
|
||||
return Item{Type: TYPE_GROUP, Channel: ch}
|
||||
}
|
||||
|
||||
// ItemRef is a reference to a message of any type. One of FileID,
|
||||
// CommentId, or the combination of ChannelId and Timestamp must be
|
||||
// specified.
|
||||
type ItemRef struct {
|
||||
Channel string `json:"channel"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
File string `json:"file"`
|
||||
Comment string `json:"file_comment"`
|
||||
}
|
||||
|
||||
// NewRefToMessage initializes a reference to to a message.
|
||||
func NewRefToMessage(channel, timestamp string) ItemRef {
|
||||
return ItemRef{Channel: channel, Timestamp: timestamp}
|
||||
}
|
||||
|
||||
// NewRefToFile initializes a reference to a file.
|
||||
func NewRefToFile(file string) ItemRef {
|
||||
return ItemRef{File: file}
|
||||
}
|
||||
|
||||
// NewRefToComment initializes a reference to a file comment.
|
||||
func NewRefToComment(comment string) ItemRef {
|
||||
return ItemRef{Comment: comment}
|
||||
}
|
60
vendor/github.com/slack-go/slack/logger.go
generated
vendored
Normal file
60
vendor/github.com/slack-go/slack/logger.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// logger is a logger interface compatible with both stdlib and some
|
||||
// 3rd party loggers.
|
||||
type logger interface {
|
||||
Output(int, string) error
|
||||
}
|
||||
|
||||
// ilogger represents the internal logging api we use.
|
||||
type ilogger interface {
|
||||
logger
|
||||
Print(...interface{})
|
||||
Printf(string, ...interface{})
|
||||
Println(...interface{})
|
||||
}
|
||||
|
||||
type debug interface {
|
||||
Debug() bool
|
||||
|
||||
// Debugf print a formatted debug line.
|
||||
Debugf(format string, v ...interface{})
|
||||
// Debugln print a debug line.
|
||||
Debugln(v ...interface{})
|
||||
}
|
||||
|
||||
// internalLog implements the additional methods used by our internal logging.
|
||||
type internalLog struct {
|
||||
logger
|
||||
}
|
||||
|
||||
// Println replicates the behaviour of the standard logger.
|
||||
func (t internalLog) Println(v ...interface{}) {
|
||||
t.Output(2, fmt.Sprintln(v...))
|
||||
}
|
||||
|
||||
// Printf replicates the behaviour of the standard logger.
|
||||
func (t internalLog) Printf(format string, v ...interface{}) {
|
||||
t.Output(2, fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// Print replicates the behaviour of the standard logger.
|
||||
func (t internalLog) Print(v ...interface{}) {
|
||||
t.Output(2, fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
type discard struct{}
|
||||
|
||||
func (t discard) Debug() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Debugf print a formatted debug line.
|
||||
func (t discard) Debugf(format string, v ...interface{}) {}
|
||||
|
||||
// Debugln print a debug line.
|
||||
func (t discard) Debugln(v ...interface{}) {}
|
30
vendor/github.com/slack-go/slack/messageID.go
generated
vendored
Normal file
30
vendor/github.com/slack-go/slack/messageID.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package slack
|
||||
|
||||
import "sync"
|
||||
|
||||
// IDGenerator provides an interface for generating integer ID values.
|
||||
type IDGenerator interface {
|
||||
Next() int
|
||||
}
|
||||
|
||||
// NewSafeID returns a new instance of an IDGenerator which is safe for
|
||||
// concurrent use by multiple goroutines.
|
||||
func NewSafeID(startID int) IDGenerator {
|
||||
return &safeID{
|
||||
nextID: startID,
|
||||
mutex: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
type safeID struct {
|
||||
nextID int
|
||||
mutex *sync.Mutex
|
||||
}
|
||||
|
||||
func (s *safeID) Next() int {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
id := s.nextID
|
||||
s.nextID++
|
||||
return id
|
||||
}
|
199
vendor/github.com/slack-go/slack/messages.go
generated
vendored
Normal file
199
vendor/github.com/slack-go/slack/messages.go
generated
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
package slack
|
||||
|
||||
// OutgoingMessage is used for the realtime API, and seems incomplete.
|
||||
type OutgoingMessage struct {
|
||||
ID int `json:"id"`
|
||||
// channel ID
|
||||
Channel string `json:"channel,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
ThreadTimestamp string `json:"thread_ts,omitempty"`
|
||||
ThreadBroadcast bool `json:"reply_broadcast,omitempty"`
|
||||
IDs []string `json:"ids,omitempty"`
|
||||
}
|
||||
|
||||
// Message is an auxiliary type to allow us to have a message containing sub messages
|
||||
type Message struct {
|
||||
Msg
|
||||
SubMessage *Msg `json:"message,omitempty"`
|
||||
PreviousMessage *Msg `json:"previous_message,omitempty"`
|
||||
}
|
||||
|
||||
// Msg contains information about a slack message
|
||||
type Msg struct {
|
||||
// Basic Message
|
||||
ClientMsgID string `json:"client_msg_id"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Timestamp string `json:"ts,omitempty"`
|
||||
ThreadTimestamp string `json:"thread_ts,omitempty"`
|
||||
IsStarred bool `json:"is_starred,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"`
|
||||
|
||||
// Hidden Subtypes
|
||||
Hidden bool `json:"hidden,omitempty"` // message_changed, message_deleted, unpinned_item
|
||||
DeletedTimestamp string `json:"deleted_ts,omitempty"` // message_deleted
|
||||
EventTimestamp string `json:"event_ts,omitempty"`
|
||||
|
||||
// bot_message (https://api.slack.com/events/message/bot_message)
|
||||
BotID string `json:"bot_id,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Icons *Icon `json:"icons,omitempty"`
|
||||
|
||||
// channel_join, group_join
|
||||
Inviter string `json:"inviter,omitempty"`
|
||||
|
||||
// channel_topic, group_topic
|
||||
Topic string `json:"topic,omitempty"`
|
||||
|
||||
// channel_purpose, group_purpose
|
||||
Purpose string `json:"purpose,omitempty"`
|
||||
|
||||
// channel_name, group_name
|
||||
Name string `json:"name,omitempty"`
|
||||
OldName string `json:"old_name,omitempty"`
|
||||
|
||||
// channel_archive, group_archive
|
||||
Members []string `json:"members,omitempty"`
|
||||
|
||||
// channels.replies, groups.replies, im.replies, mpim.replies
|
||||
ReplyCount int `json:"reply_count,omitempty"`
|
||||
Replies []Reply `json:"replies,omitempty"`
|
||||
ParentUserId string `json:"parent_user_id,omitempty"`
|
||||
|
||||
// file_share, file_comment, file_mention
|
||||
Files []File `json:"files,omitempty"`
|
||||
|
||||
// file_share
|
||||
Upload bool `json:"upload,omitempty"`
|
||||
|
||||
// file_comment
|
||||
Comment *Comment `json:"comment,omitempty"`
|
||||
|
||||
// pinned_item
|
||||
ItemType string `json:"item_type,omitempty"`
|
||||
|
||||
// https://api.slack.com/rtm
|
||||
ReplyTo int `json:"reply_to,omitempty"`
|
||||
Team string `json:"team,omitempty"`
|
||||
|
||||
// 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"`
|
||||
|
||||
// Block type Message
|
||||
Blocks Blocks `json:"blocks,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
// ResponseTypeInChannel in channel response for slash commands.
|
||||
ResponseTypeInChannel = "in_channel"
|
||||
// ResponseTypeEphemeral ephemeral response for slash commands.
|
||||
ResponseTypeEphemeral = "ephemeral"
|
||||
)
|
||||
|
||||
// Icon is used for bot messages
|
||||
type Icon struct {
|
||||
IconURL string `json:"icon_url,omitempty"`
|
||||
IconEmoji string `json:"icon_emoji,omitempty"`
|
||||
}
|
||||
|
||||
// Edited indicates that a message has been edited.
|
||||
type Edited struct {
|
||||
User string `json:"user,omitempty"`
|
||||
Timestamp string `json:"ts,omitempty"`
|
||||
}
|
||||
|
||||
// Reply contains information about a reply for a thread
|
||||
type Reply struct {
|
||||
User string `json:"user,omitempty"`
|
||||
Timestamp string `json:"ts,omitempty"`
|
||||
}
|
||||
|
||||
// Event contains the event type
|
||||
type Event struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// Ping contains information about a Ping Event
|
||||
type Ping struct {
|
||||
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"`
|
||||
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, options ...RTMsgOption) *OutgoingMessage {
|
||||
id := rtm.idGen.Next()
|
||||
msg := OutgoingMessage{
|
||||
ID: id,
|
||||
Type: "message",
|
||||
Channel: channelID,
|
||||
Text: text,
|
||||
}
|
||||
for _, option := range options {
|
||||
option(&msg)
|
||||
}
|
||||
return &msg
|
||||
}
|
||||
|
||||
// NewSubscribeUserPresence prepares an OutgoingMessage that the user can
|
||||
// use to subscribe presence events for the specified users.
|
||||
func (rtm *RTM) NewSubscribeUserPresence(ids []string) *OutgoingMessage {
|
||||
return &OutgoingMessage{
|
||||
Type: "presence_sub",
|
||||
IDs: ids,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTypingMessage prepares an OutgoingMessage that the user can
|
||||
// use to send as a typing indicator. Use this function to properly set the
|
||||
// messageID.
|
||||
func (rtm *RTM) NewTypingMessage(channelID string) *OutgoingMessage {
|
||||
id := rtm.idGen.Next()
|
||||
return &OutgoingMessage{
|
||||
ID: id,
|
||||
Type: "typing",
|
||||
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
|
||||
}
|
||||
}
|
360
vendor/github.com/slack-go/slack/misc.go
generated
vendored
Normal file
360
vendor/github.com/slack-go/slack/misc.go
generated
vendored
Normal file
@ -0,0 +1,360 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SlackResponse handles parsing out errors from the web api.
|
||||
type SlackResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func (t SlackResponse) Err() error {
|
||||
if t.Ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return errors.New(t.Error)
|
||||
}
|
||||
|
||||
// StatusCodeError represents an http response error.
|
||||
// type httpStatusCode interface { HTTPStatusCode() int } to handle it.
|
||||
type statusCodeError struct {
|
||||
Code int
|
||||
Status string
|
||||
}
|
||||
|
||||
func (t statusCodeError) Error() string {
|
||||
return fmt.Sprintf("slack server error: %s", t.Status)
|
||||
}
|
||||
|
||||
func (t statusCodeError) HTTPStatusCode() int {
|
||||
return t.Code
|
||||
}
|
||||
|
||||
func (t statusCodeError) Retryable() bool {
|
||||
if t.Code >= 500 || t.Code == http.StatusTooManyRequests {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RateLimitedError represents the rate limit respond from slack
|
||||
type RateLimitedError struct {
|
||||
RetryAfter time.Duration
|
||||
}
|
||||
|
||||
func (e *RateLimitedError) Error() string {
|
||||
return fmt.Sprintf("slack rate limit exceeded, retry after %s", e.RetryAfter)
|
||||
}
|
||||
|
||||
func (e *RateLimitedError) Retryable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func fileUploadReq(ctx context.Context, path string, values url.Values, r io.Reader) (*http.Request, error) {
|
||||
req, err := http.NewRequest("POST", path, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
req.URL.RawQuery = (values).Encode()
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func downloadFile(client httpClient, token string, downloadURL string, writer io.Writer, d debug) error {
|
||||
if downloadURL == "" {
|
||||
return fmt.Errorf("received empty download URL")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", downloadURL, &bytes.Buffer{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var bearer = "Bearer " + token
|
||||
req.Header.Add("Authorization", bearer)
|
||||
req.WithContext(context.Background())
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = checkStatusCode(resp, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(writer, resp.Body)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func formReq(endpoint string, values url.Values) (req *http.Request, err error) {
|
||||
if req, err = http.NewRequest("POST", endpoint, strings.NewReader(values.Encode())); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func jsonReq(endpoint string, body interface{}) (req *http.Request, err error) {
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
if err = json.NewEncoder(buffer).Encode(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req, err = http.NewRequest("POST", endpoint, buffer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func parseResponseBody(body io.ReadCloser, intf interface{}, d debug) error {
|
||||
response, err := ioutil.ReadAll(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.Debug() {
|
||||
d.Debugln("parseResponseBody", string(response))
|
||||
}
|
||||
|
||||
return json.Unmarshal(response, intf)
|
||||
}
|
||||
|
||||
func postLocalWithMultipartResponse(ctx context.Context, client httpClient, method, fpath, fieldname string, values url.Values, intf interface{}, d debug) error {
|
||||
fullpath, err := filepath.Abs(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.Open(fullpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return postWithMultipartResponse(ctx, client, method, filepath.Base(fpath), fieldname, values, file, intf, d)
|
||||
}
|
||||
|
||||
func postWithMultipartResponse(ctx context.Context, client httpClient, path, name, fieldname string, values url.Values, r io.Reader, intf interface{}, d debug) error {
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
wr := multipart.NewWriter(pipeWriter)
|
||||
errc := make(chan error)
|
||||
go func() {
|
||||
defer pipeWriter.Close()
|
||||
ioWriter, err := wr.CreateFormFile(fieldname, name)
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
_, err = io.Copy(ioWriter, r)
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
if err = wr.Close(); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
}()
|
||||
req, err := fileUploadReq(ctx, path, values, pipeReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Content-Type", wr.FormDataContentType())
|
||||
req = req.WithContext(ctx)
|
||||
resp, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = checkStatusCode(resp, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
select {
|
||||
case err = <-errc:
|
||||
return err
|
||||
default:
|
||||
return newJSONParser(intf)(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func doPost(ctx context.Context, client httpClient, req *http.Request, parser responseParser, d debug) error {
|
||||
req = req.WithContext(ctx)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = checkStatusCode(resp, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return parser(resp)
|
||||
}
|
||||
|
||||
// post JSON.
|
||||
func postJSON(ctx context.Context, client httpClient, endpoint, token string, json []byte, intf interface{}, d debug) error {
|
||||
reqBody := bytes.NewBuffer(json)
|
||||
req, err := http.NewRequest("POST", endpoint, reqBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
|
||||
return doPost(ctx, client, req, newJSONParser(intf), d)
|
||||
}
|
||||
|
||||
// post a url encoded form.
|
||||
func postForm(ctx context.Context, client httpClient, endpoint string, values url.Values, intf interface{}, d debug) error {
|
||||
reqBody := strings.NewReader(values.Encode())
|
||||
req, err := http.NewRequest("POST", endpoint, reqBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
return doPost(ctx, client, req, newJSONParser(intf), d)
|
||||
}
|
||||
|
||||
func getResource(ctx context.Context, client httpClient, endpoint string, values url.Values, intf interface{}, d debug) error {
|
||||
req, err := http.NewRequest("GET", endpoint, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.URL.RawQuery = values.Encode()
|
||||
|
||||
return doPost(ctx, client, req, newJSONParser(intf), d)
|
||||
}
|
||||
|
||||
func parseAdminResponse(ctx context.Context, client httpClient, method string, teamName string, values url.Values, intf interface{}, d debug) error {
|
||||
endpoint := fmt.Sprintf(WEBAPIURLFormat, teamName, method, time.Now().Unix())
|
||||
return postForm(ctx, client, endpoint, values, intf, d)
|
||||
}
|
||||
|
||||
func logResponse(resp *http.Response, d debug) error {
|
||||
if d.Debug() {
|
||||
text, err := httputil.DumpResponse(resp, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Debugln(string(text))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func okJSONHandler(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
response, _ := json.Marshal(SlackResponse{
|
||||
Ok: true,
|
||||
})
|
||||
rw.Write(response)
|
||||
}
|
||||
|
||||
// timerReset safely reset a timer, see time.Timer.Reset for details.
|
||||
func timerReset(t *time.Timer, d time.Duration) {
|
||||
if !t.Stop() {
|
||||
<-t.C
|
||||
}
|
||||
t.Reset(d)
|
||||
}
|
||||
|
||||
func checkStatusCode(resp *http.Response, d debug) error {
|
||||
if resp.StatusCode == http.StatusTooManyRequests {
|
||||
retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return &RateLimitedError{time.Duration(retry) * time.Second}
|
||||
}
|
||||
|
||||
// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
logResponse(resp, d)
|
||||
return statusCodeError{Code: resp.StatusCode, Status: resp.Status}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type responseParser func(*http.Response) error
|
||||
|
||||
func newJSONParser(dst interface{}) responseParser {
|
||||
return func(resp *http.Response) error {
|
||||
return json.NewDecoder(resp.Body).Decode(dst)
|
||||
}
|
||||
}
|
||||
|
||||
func newTextParser(dst interface{}) responseParser {
|
||||
return func(resp *http.Response) error {
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(b, []byte("ok")) {
|
||||
return errors.New(string(b))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func newContentTypeParser(dst interface{}) responseParser {
|
||||
return func(req *http.Response) (err error) {
|
||||
var (
|
||||
ctype string
|
||||
)
|
||||
|
||||
if ctype, _, err = mime.ParseMediaType(req.Header.Get("Content-Type")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch ctype {
|
||||
case "application/json":
|
||||
return newJSONParser(dst)(req)
|
||||
default:
|
||||
return newTextParser(dst)(req)
|
||||
}
|
||||
}
|
||||
}
|
64
vendor/github.com/slack-go/slack/oauth.go
generated
vendored
Normal file
64
vendor/github.com/slack-go/slack/oauth.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// OAuthResponseIncomingWebhook ...
|
||||
type OAuthResponseIncomingWebhook struct {
|
||||
URL string `json:"url"`
|
||||
Channel string `json:"channel"`
|
||||
ChannelID string `json:"channel_id,omitempty"`
|
||||
ConfigurationURL string `json:"configuration_url"`
|
||||
}
|
||||
|
||||
// OAuthResponseBot ...
|
||||
type OAuthResponseBot struct {
|
||||
BotUserID string `json:"bot_user_id"`
|
||||
BotAccessToken string `json:"bot_access_token"`
|
||||
}
|
||||
|
||||
// OAuthResponse ...
|
||||
type OAuthResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
Scope string `json:"scope"`
|
||||
TeamName string `json:"team_name"`
|
||||
TeamID string `json:"team_id"`
|
||||
IncomingWebhook OAuthResponseIncomingWebhook `json:"incoming_webhook"`
|
||||
Bot OAuthResponseBot `json:"bot"`
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
// GetOAuthToken retrieves an AccessToken
|
||||
func GetOAuthToken(client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, err error) {
|
||||
return GetOAuthTokenContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
|
||||
}
|
||||
|
||||
// GetOAuthTokenContext retrieves an AccessToken with a custom context
|
||||
func GetOAuthTokenContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, err error) {
|
||||
response, err := GetOAuthResponseContext(ctx, client, clientID, clientSecret, code, redirectURI)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return response.AccessToken, response.Scope, nil
|
||||
}
|
||||
|
||||
func GetOAuthResponse(client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) {
|
||||
return GetOAuthResponseContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
|
||||
}
|
||||
|
||||
func GetOAuthResponseContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) {
|
||||
values := url.Values{
|
||||
"client_id": {clientID},
|
||||
"client_secret": {clientSecret},
|
||||
"code": {code},
|
||||
"redirect_uri": {redirectURI},
|
||||
}
|
||||
response := &OAuthResponse{}
|
||||
if err = postForm(ctx, client, APIURL+"oauth.access", values, response, discard{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, response.Err()
|
||||
}
|
20
vendor/github.com/slack-go/slack/pagination.go
generated
vendored
Normal file
20
vendor/github.com/slack-go/slack/pagination.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
package slack
|
||||
|
||||
// Paging contains paging information
|
||||
type Paging struct {
|
||||
Count int `json:"count"`
|
||||
Total int `json:"total"`
|
||||
Page int `json:"page"`
|
||||
Pages int `json:"pages"`
|
||||
}
|
||||
|
||||
// Pagination contains pagination information
|
||||
// This is different from Paging in that it contains additional details
|
||||
type Pagination struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"per_page"`
|
||||
PageCount int `json:"page_count"`
|
||||
First int `json:"first"`
|
||||
Last int `json:"last"`
|
||||
}
|
94
vendor/github.com/slack-go/slack/pins.go
generated
vendored
Normal file
94
vendor/github.com/slack-go/slack/pins.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type listPinsResponseFull struct {
|
||||
Items []Item
|
||||
Paging `json:"paging"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
// AddPin pins an item in a channel
|
||||
func (api *Client) AddPin(channel string, item ItemRef) error {
|
||||
return api.AddPinContext(context.Background(), channel, item)
|
||||
}
|
||||
|
||||
// AddPinContext pins an item in a channel with a custom context
|
||||
func (api *Client) AddPinContext(ctx context.Context, channel string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := api.postMethod(ctx, "pins.add", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// RemovePin un-pins an item from a channel
|
||||
func (api *Client) RemovePin(channel string, item ItemRef) error {
|
||||
return api.RemovePinContext(context.Background(), channel, item)
|
||||
}
|
||||
|
||||
// RemovePinContext un-pins an item from a channel with a custom context
|
||||
func (api *Client) RemovePinContext(ctx context.Context, channel string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := api.postMethod(ctx, "pins.remove", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// ListPins returns information about the items a user reacted to.
|
||||
func (api *Client) ListPins(channel string) ([]Item, *Paging, error) {
|
||||
return api.ListPinsContext(context.Background(), channel)
|
||||
}
|
||||
|
||||
// ListPinsContext returns information about the items a user reacted to with a custom context.
|
||||
func (api *Client) ListPinsContext(ctx context.Context, channel string) ([]Item, *Paging, error) {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response := &listPinsResponseFull{}
|
||||
err := api.postMethod(ctx, "pins.list", values, response)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, nil, errors.New(response.Error)
|
||||
}
|
||||
return response.Items, &response.Paging, nil
|
||||
}
|
270
vendor/github.com/slack-go/slack/reactions.go
generated
vendored
Normal file
270
vendor/github.com/slack-go/slack/reactions.go
generated
vendored
Normal file
@ -0,0 +1,270 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ItemReaction is the reactions that have happened on an item.
|
||||
type ItemReaction struct {
|
||||
Name string `json:"name"`
|
||||
Count int `json:"count"`
|
||||
Users []string `json:"users"`
|
||||
}
|
||||
|
||||
// ReactedItem is an item that was reacted to, and the details of the
|
||||
// reactions.
|
||||
type ReactedItem struct {
|
||||
Item
|
||||
Reactions []ItemReaction
|
||||
}
|
||||
|
||||
// GetReactionsParameters is the inputs to get reactions to an item.
|
||||
type GetReactionsParameters struct {
|
||||
Full bool
|
||||
}
|
||||
|
||||
// NewGetReactionsParameters initializes the inputs to get reactions to an item.
|
||||
func NewGetReactionsParameters() GetReactionsParameters {
|
||||
return GetReactionsParameters{
|
||||
Full: false,
|
||||
}
|
||||
}
|
||||
|
||||
type getReactionsResponseFull struct {
|
||||
Type string
|
||||
M struct {
|
||||
Reactions []ItemReaction
|
||||
} `json:"message"`
|
||||
F struct {
|
||||
Reactions []ItemReaction
|
||||
} `json:"file"`
|
||||
FC struct {
|
||||
Reactions []ItemReaction
|
||||
} `json:"comment"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func (res getReactionsResponseFull) extractReactions() []ItemReaction {
|
||||
switch res.Type {
|
||||
case "message":
|
||||
return res.M.Reactions
|
||||
case "file":
|
||||
return res.F.Reactions
|
||||
case "file_comment":
|
||||
return res.FC.Reactions
|
||||
}
|
||||
return []ItemReaction{}
|
||||
}
|
||||
|
||||
const (
|
||||
DEFAULT_REACTIONS_USER = ""
|
||||
DEFAULT_REACTIONS_COUNT = 100
|
||||
DEFAULT_REACTIONS_PAGE = 1
|
||||
DEFAULT_REACTIONS_FULL = false
|
||||
)
|
||||
|
||||
// ListReactionsParameters is the inputs to find all reactions by a user.
|
||||
type ListReactionsParameters struct {
|
||||
User string
|
||||
Count int
|
||||
Page int
|
||||
Full bool
|
||||
}
|
||||
|
||||
// NewListReactionsParameters initializes the inputs to find all reactions
|
||||
// performed by a user.
|
||||
func NewListReactionsParameters() ListReactionsParameters {
|
||||
return ListReactionsParameters{
|
||||
User: DEFAULT_REACTIONS_USER,
|
||||
Count: DEFAULT_REACTIONS_COUNT,
|
||||
Page: DEFAULT_REACTIONS_PAGE,
|
||||
Full: DEFAULT_REACTIONS_FULL,
|
||||
}
|
||||
}
|
||||
|
||||
type listReactionsResponseFull struct {
|
||||
Items []struct {
|
||||
Type string
|
||||
Channel string
|
||||
M struct {
|
||||
*Message
|
||||
} `json:"message"`
|
||||
F struct {
|
||||
*File
|
||||
Reactions []ItemReaction
|
||||
} `json:"file"`
|
||||
FC struct {
|
||||
*Comment
|
||||
Reactions []ItemReaction
|
||||
} `json:"comment"`
|
||||
}
|
||||
Paging `json:"paging"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func (res listReactionsResponseFull) extractReactedItems() []ReactedItem {
|
||||
items := make([]ReactedItem, len(res.Items))
|
||||
for i, input := range res.Items {
|
||||
item := ReactedItem{}
|
||||
item.Type = input.Type
|
||||
switch input.Type {
|
||||
case "message":
|
||||
item.Channel = input.Channel
|
||||
item.Message = input.M.Message
|
||||
item.Reactions = input.M.Reactions
|
||||
case "file":
|
||||
item.File = input.F.File
|
||||
item.Reactions = input.F.Reactions
|
||||
case "file_comment":
|
||||
item.File = input.F.File
|
||||
item.Comment = input.FC.Comment
|
||||
item.Reactions = input.FC.Reactions
|
||||
}
|
||||
items[i] = item
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// AddReaction adds a reaction emoji to a message, file or file comment.
|
||||
func (api *Client) AddReaction(name string, item ItemRef) error {
|
||||
return api.AddReactionContext(context.Background(), name, item)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
if name != "" {
|
||||
values.Set("name", name)
|
||||
}
|
||||
if item.Channel != "" {
|
||||
values.Set("channel", item.Channel)
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := api.postMethod(ctx, "reactions.add", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// RemoveReaction removes a reaction emoji from a message, file or file comment.
|
||||
func (api *Client) RemoveReaction(name string, item ItemRef) error {
|
||||
return api.RemoveReactionContext(context.Background(), name, item)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
if name != "" {
|
||||
values.Set("name", name)
|
||||
}
|
||||
if item.Channel != "" {
|
||||
values.Set("channel", item.Channel)
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := api.postMethod(ctx, "reactions.remove", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// GetReactions returns details about the reactions on an item.
|
||||
func (api *Client) GetReactions(item ItemRef, params GetReactionsParameters) ([]ItemReaction, error) {
|
||||
return api.GetReactionsContext(context.Background(), item, params)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
if item.Channel != "" {
|
||||
values.Set("channel", item.Channel)
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if 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 := api.postMethod(ctx, "reactions.get", values, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.extractReactions(), nil
|
||||
}
|
||||
|
||||
// ListReactions returns information about the items a user reacted to.
|
||||
func (api *Client) ListReactions(params ListReactionsParameters) ([]ReactedItem, *Paging, error) {
|
||||
return api.ListReactionsContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
if params.User != DEFAULT_REACTIONS_USER {
|
||||
values.Add("user", params.User)
|
||||
}
|
||||
if params.Count != DEFAULT_REACTIONS_COUNT {
|
||||
values.Add("count", strconv.Itoa(params.Count))
|
||||
}
|
||||
if params.Page != DEFAULT_REACTIONS_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
if params.Full != DEFAULT_REACTIONS_FULL {
|
||||
values.Add("full", strconv.FormatBool(params.Full))
|
||||
}
|
||||
|
||||
response := &listReactionsResponseFull{}
|
||||
err := api.postMethod(ctx, "reactions.list", values, response)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return response.extractReactedItems(), &response.Paging, nil
|
||||
}
|
75
vendor/github.com/slack-go/slack/reminders.go
generated
vendored
Normal file
75
vendor/github.com/slack-go/slack/reminders.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Reminder struct {
|
||||
ID string `json:"id"`
|
||||
Creator string `json:"creator"`
|
||||
User string `json:"user"`
|
||||
Text string `json:"text"`
|
||||
Recurring bool `json:"recurring"`
|
||||
Time time.Time `json:"time"`
|
||||
CompleteTS int `json:"complete_ts"`
|
||||
}
|
||||
|
||||
type reminderResp struct {
|
||||
SlackResponse
|
||||
Reminder Reminder `json:"reminder"`
|
||||
}
|
||||
|
||||
func (api *Client) doReminder(ctx context.Context, path string, values url.Values) (*Reminder, error) {
|
||||
response := &reminderResp{}
|
||||
if err := api.postMethod(ctx, path, values, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Reminder, response.Err()
|
||||
}
|
||||
|
||||
// AddChannelReminder adds a reminder for a channel.
|
||||
//
|
||||
// See https://api.slack.com/methods/reminders.add (NOTE: the ability to set
|
||||
// reminders on a channel is currently undocumented but has been tested to
|
||||
// work)
|
||||
func (api *Client) AddChannelReminder(channelID, text, time string) (*Reminder, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"text": {text},
|
||||
"time": {time},
|
||||
"channel": {channelID},
|
||||
}
|
||||
return api.doReminder(context.Background(), "reminders.add", values)
|
||||
}
|
||||
|
||||
// AddUserReminder adds a reminder for a user.
|
||||
//
|
||||
// See https://api.slack.com/methods/reminders.add (NOTE: the ability to set
|
||||
// reminders on a channel is currently undocumented but has been tested to
|
||||
// work)
|
||||
func (api *Client) AddUserReminder(userID, text, time string) (*Reminder, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"text": {text},
|
||||
"time": {time},
|
||||
"user": {userID},
|
||||
}
|
||||
return api.doReminder(context.Background(), "reminders.add", values)
|
||||
}
|
||||
|
||||
// DeleteReminder deletes an existing reminder.
|
||||
//
|
||||
// See https://api.slack.com/methods/reminders.delete
|
||||
func (api *Client) DeleteReminder(id string) error {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"reminder": {id},
|
||||
}
|
||||
response := &SlackResponse{}
|
||||
if err := api.postMethod(context.Background(), "reminders.delete", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Err()
|
||||
}
|
131
vendor/github.com/slack-go/slack/rtm.go
generated
vendored
Normal file
131
vendor/github.com/slack-go/slack/rtm.go
generated
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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) {
|
||||
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.
|
||||
//
|
||||
// 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 = api.postMethod(ctx, "rtm.start", url.Values{"token": {api.token}}, response)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
api.Debugln("Using URL:", response.Info.URL)
|
||||
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) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), websocketDefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
return api.ConnectRTMContext(ctx)
|
||||
}
|
||||
|
||||
// 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 = api.postMethod(ctx, "rtm.connect", url.Values{"token": {api.token}}, response)
|
||||
if err != nil {
|
||||
api.Debugf("Failed to connect to RTM: %s", err)
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
api.Debugln("Using URL:", response.Info.URL)
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
// RTMOptionConnParams installs parameters to embed into the connection URL.
|
||||
func RTMOptionConnParams(connParams url.Values) RTMOption {
|
||||
return func(rtm *RTM) {
|
||||
rtm.connParams = connParams
|
||||
}
|
||||
}
|
||||
|
||||
// NewRTM returns a RTM, which provides a fully managed connection to
|
||||
// Slack's websocket-based Real-Time Messaging protocol.
|
||||
func (api *Client) NewRTM(options ...RTMOption) *RTM {
|
||||
result := &RTM{
|
||||
Client: *api,
|
||||
IncomingEvents: make(chan RTMEvent, 50),
|
||||
outgoingMessages: make(chan OutgoingMessage, 20),
|
||||
pingInterval: defaultPingInterval,
|
||||
pingDeadman: time.NewTimer(deadmanDuration(defaultPingInterval)),
|
||||
killChannel: make(chan bool),
|
||||
disconnected: make(chan struct{}),
|
||||
disconnectedm: &sync.Once{},
|
||||
forcePing: make(chan bool),
|
||||
idGen: NewSafeID(1),
|
||||
mu: &sync.Mutex{},
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
opt(result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
156
vendor/github.com/slack-go/slack/search.go
generated
vendored
Normal file
156
vendor/github.com/slack-go/slack/search.go
generated
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_SEARCH_SORT = "score"
|
||||
DEFAULT_SEARCH_SORT_DIR = "desc"
|
||||
DEFAULT_SEARCH_HIGHLIGHT = false
|
||||
DEFAULT_SEARCH_COUNT = 20
|
||||
DEFAULT_SEARCH_PAGE = 1
|
||||
)
|
||||
|
||||
type SearchParameters struct {
|
||||
Sort string
|
||||
SortDirection string
|
||||
Highlight bool
|
||||
Count int
|
||||
Page int
|
||||
}
|
||||
|
||||
type CtxChannel struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
IsExtShared bool `json:"is_ext_shared"`
|
||||
IsMPIM bool `json:"is_mpim"`
|
||||
ISOrgShared bool `json:"is_org_shared"`
|
||||
IsPendingExtShared bool `json:"is_pending_ext_shared"`
|
||||
IsPrivate bool `json:"is_private"`
|
||||
IsShared bool `json:"is_shared"`
|
||||
}
|
||||
|
||||
type CtxMessage struct {
|
||||
User string `json:"user"`
|
||||
Username string `json:"username"`
|
||||
Text string `json:"text"`
|
||||
Timestamp string `json:"ts"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type SearchMessage struct {
|
||||
Type string `json:"type"`
|
||||
Channel CtxChannel `json:"channel"`
|
||||
User string `json:"user"`
|
||||
Username string `json:"username"`
|
||||
Timestamp string `json:"ts"`
|
||||
Blocks Blocks `json:"blocks,omitempty"`
|
||||
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 {
|
||||
Matches []SearchMessage `json:"matches"`
|
||||
Paging `json:"paging"`
|
||||
Pagination `json:"pagination"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type SearchFiles struct {
|
||||
Matches []File `json:"matches"`
|
||||
Paging `json:"paging"`
|
||||
Pagination `json:"pagination"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type searchResponseFull struct {
|
||||
Query string `json:"query"`
|
||||
SearchMessages `json:"messages"`
|
||||
SearchFiles `json:"files"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func NewSearchParameters() SearchParameters {
|
||||
return SearchParameters{
|
||||
Sort: DEFAULT_SEARCH_SORT,
|
||||
SortDirection: DEFAULT_SEARCH_SORT_DIR,
|
||||
Highlight: DEFAULT_SEARCH_HIGHLIGHT,
|
||||
Count: DEFAULT_SEARCH_COUNT,
|
||||
Page: DEFAULT_SEARCH_PAGE,
|
||||
}
|
||||
}
|
||||
|
||||
func (api *Client) _search(ctx context.Context, path, query string, params SearchParameters, files, messages bool) (response *searchResponseFull, error error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"query": {query},
|
||||
}
|
||||
if params.Sort != DEFAULT_SEARCH_SORT {
|
||||
values.Add("sort", params.Sort)
|
||||
}
|
||||
if params.SortDirection != DEFAULT_SEARCH_SORT_DIR {
|
||||
values.Add("sort_dir", params.SortDirection)
|
||||
}
|
||||
if params.Highlight != DEFAULT_SEARCH_HIGHLIGHT {
|
||||
values.Add("highlight", strconv.Itoa(1))
|
||||
}
|
||||
if params.Count != DEFAULT_SEARCH_COUNT {
|
||||
values.Add("count", strconv.Itoa(params.Count))
|
||||
}
|
||||
if params.Page != DEFAULT_SEARCH_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
|
||||
response = &searchResponseFull{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, response.Err()
|
||||
|
||||
}
|
||||
|
||||
func (api *Client) Search(query string, params SearchParameters) (*SearchMessages, *SearchFiles, error) {
|
||||
return api.SearchContext(context.Background(), query, params)
|
||||
}
|
||||
|
||||
func (api *Client) SearchContext(ctx context.Context, query string, params SearchParameters) (*SearchMessages, *SearchFiles, error) {
|
||||
response, err := api._search(ctx, "search.all", query, params, true, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &response.SearchMessages, &response.SearchFiles, nil
|
||||
}
|
||||
|
||||
func (api *Client) SearchFiles(query string, params SearchParameters) (*SearchFiles, error) {
|
||||
return api.SearchFilesContext(context.Background(), query, params)
|
||||
}
|
||||
|
||||
func (api *Client) SearchFilesContext(ctx context.Context, query string, params SearchParameters) (*SearchFiles, error) {
|
||||
response, err := api._search(ctx, "search.files", query, params, true, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.SearchFiles, nil
|
||||
}
|
||||
|
||||
func (api *Client) SearchMessages(query string, params SearchParameters) (*SearchMessages, error) {
|
||||
return api.SearchMessagesContext(context.Background(), query, params)
|
||||
}
|
||||
|
||||
func (api *Client) SearchMessagesContext(ctx context.Context, query string, params SearchParameters) (*SearchMessages, error) {
|
||||
response, err := api._search(ctx, "search.messages", query, params, false, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.SearchMessages, nil
|
||||
}
|
100
vendor/github.com/slack-go/slack/security.go
generated
vendored
Normal file
100
vendor/github.com/slack-go/slack/security.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Signature headers
|
||||
const (
|
||||
hSignature = "X-Slack-Signature"
|
||||
hTimestamp = "X-Slack-Request-Timestamp"
|
||||
)
|
||||
|
||||
// SecretsVerifier contains the information needed to verify that the request comes from Slack
|
||||
type SecretsVerifier struct {
|
||||
signature []byte
|
||||
hmac hash.Hash
|
||||
}
|
||||
|
||||
func unsafeSignatureVerifier(header http.Header, secret string) (_ SecretsVerifier, err error) {
|
||||
var (
|
||||
bsignature []byte
|
||||
)
|
||||
|
||||
signature := header.Get(hSignature)
|
||||
stimestamp := header.Get(hTimestamp)
|
||||
|
||||
if signature == "" || stimestamp == "" {
|
||||
return SecretsVerifier{}, ErrMissingHeaders
|
||||
}
|
||||
|
||||
if bsignature, err = hex.DecodeString(strings.TrimPrefix(signature, "v0=")); err != nil {
|
||||
return SecretsVerifier{}, err
|
||||
}
|
||||
|
||||
hash := hmac.New(sha256.New, []byte(secret))
|
||||
if _, err = hash.Write([]byte(fmt.Sprintf("v0:%s:", stimestamp))); err != nil {
|
||||
return SecretsVerifier{}, err
|
||||
}
|
||||
|
||||
return SecretsVerifier{
|
||||
signature: bsignature,
|
||||
hmac: hash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewSecretsVerifier returns a SecretsVerifier object in exchange for an http.Header object and signing secret
|
||||
func NewSecretsVerifier(header http.Header, secret string) (sv SecretsVerifier, err error) {
|
||||
var (
|
||||
timestamp int64
|
||||
)
|
||||
|
||||
stimestamp := header.Get(hTimestamp)
|
||||
|
||||
if sv, err = unsafeSignatureVerifier(header, secret); err != nil {
|
||||
return SecretsVerifier{}, err
|
||||
}
|
||||
|
||||
if timestamp, err = strconv.ParseInt(stimestamp, 10, 64); err != nil {
|
||||
return SecretsVerifier{}, err
|
||||
}
|
||||
|
||||
diff := absDuration(time.Since(time.Unix(timestamp, 0)))
|
||||
if diff > 5*time.Minute {
|
||||
return SecretsVerifier{}, ErrExpiredTimestamp
|
||||
}
|
||||
|
||||
return sv, err
|
||||
}
|
||||
|
||||
func (v *SecretsVerifier) Write(body []byte) (n int, err error) {
|
||||
return v.hmac.Write(body)
|
||||
}
|
||||
|
||||
// Ensure compares the signature sent from Slack with the actual computed hash to judge validity
|
||||
func (v SecretsVerifier) Ensure() error {
|
||||
computed := v.hmac.Sum(nil)
|
||||
// use hmac.Equal prevent leaking timing information.
|
||||
if hmac.Equal(computed, v.signature) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Expected signing signature: %s, but computed: %s", hex.EncodeToString(v.signature), hex.EncodeToString(computed))
|
||||
}
|
||||
|
||||
func abs64(n int64) int64 {
|
||||
y := n >> 63
|
||||
return (n ^ y) - y
|
||||
}
|
||||
|
||||
func absDuration(n time.Duration) time.Duration {
|
||||
return time.Duration(abs64(int64(n)))
|
||||
}
|
153
vendor/github.com/slack-go/slack/slack.go
generated
vendored
Normal file
153
vendor/github.com/slack-go/slack/slack.go
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
// APIURL of the slack api.
|
||||
APIURL = "https://slack.com/api/"
|
||||
// WEBAPIURLFormat ...
|
||||
WEBAPIURLFormat = "https://%s.slack.com/api/users.admin.%s?t=%d"
|
||||
)
|
||||
|
||||
// httpClient defines the minimal interface needed for an http.Client to be implemented.
|
||||
type httpClient interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// ResponseMetadata holds pagination metadata
|
||||
type ResponseMetadata struct {
|
||||
Cursor string `json:"next_cursor"`
|
||||
}
|
||||
|
||||
func (t *ResponseMetadata) initialize() *ResponseMetadata {
|
||||
if t != nil {
|
||||
return t
|
||||
}
|
||||
|
||||
return &ResponseMetadata{}
|
||||
}
|
||||
|
||||
// AuthTestResponse ...
|
||||
type AuthTestResponse struct {
|
||||
URL string `json:"url"`
|
||||
Team string `json:"team"`
|
||||
User string `json:"user"`
|
||||
TeamID string `json:"team_id"`
|
||||
UserID string `json:"user_id"`
|
||||
// EnterpriseID is only returned when an enterprise id present
|
||||
EnterpriseID string `json:"enterprise_id,omitempty"`
|
||||
}
|
||||
|
||||
type authTestResponseFull struct {
|
||||
SlackResponse
|
||||
AuthTestResponse
|
||||
}
|
||||
|
||||
// Client for the slack api.
|
||||
type ParamOption func(*url.Values)
|
||||
|
||||
type Client struct {
|
||||
token string
|
||||
endpoint string
|
||||
debug bool
|
||||
log ilogger
|
||||
httpclient httpClient
|
||||
}
|
||||
|
||||
// Option defines an option for a Client
|
||||
type Option func(*Client)
|
||||
|
||||
// OptionHTTPClient - provide a custom http client to the slack client.
|
||||
func OptionHTTPClient(client httpClient) func(*Client) {
|
||||
return func(c *Client) {
|
||||
c.httpclient = client
|
||||
}
|
||||
}
|
||||
|
||||
// OptionDebug enable debugging for the client
|
||||
func OptionDebug(b bool) func(*Client) {
|
||||
return func(c *Client) {
|
||||
c.debug = b
|
||||
}
|
||||
}
|
||||
|
||||
// OptionLog set logging for client.
|
||||
func OptionLog(l logger) func(*Client) {
|
||||
return func(c *Client) {
|
||||
c.log = internalLog{logger: l}
|
||||
}
|
||||
}
|
||||
|
||||
// OptionAPIURL set the url for the client. only useful for testing.
|
||||
func OptionAPIURL(u string) func(*Client) {
|
||||
return func(c *Client) { c.endpoint = u }
|
||||
}
|
||||
|
||||
// New builds a slack client from the provided token and options.
|
||||
func New(token string, options ...Option) *Client {
|
||||
s := &Client{
|
||||
token: token,
|
||||
endpoint: APIURL,
|
||||
httpclient: &http.Client{},
|
||||
log: log.New(os.Stderr, "slack-go/slack", log.LstdFlags|log.Lshortfile),
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
opt(s)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// AuthTest tests if the user is able to do authenticated requests or not
|
||||
func (api *Client) AuthTest() (response *AuthTestResponse, error error) {
|
||||
return api.AuthTestContext(context.Background())
|
||||
}
|
||||
|
||||
// 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, err error) {
|
||||
api.Debugf("Challenging auth...")
|
||||
responseFull := &authTestResponseFull{}
|
||||
err = api.postMethod(ctx, "auth.test", url.Values{"token": {api.token}}, responseFull)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &responseFull.AuthTestResponse, responseFull.Err()
|
||||
}
|
||||
|
||||
// Debugf print a formatted debug line.
|
||||
func (api *Client) Debugf(format string, v ...interface{}) {
|
||||
if api.debug {
|
||||
api.log.Output(2, fmt.Sprintf(format, v...))
|
||||
}
|
||||
}
|
||||
|
||||
// Debugln print a debug line.
|
||||
func (api *Client) Debugln(v ...interface{}) {
|
||||
if api.debug {
|
||||
api.log.Output(2, fmt.Sprintln(v...))
|
||||
}
|
||||
}
|
||||
|
||||
// Debug returns if debug is enabled.
|
||||
func (api *Client) Debug() bool {
|
||||
return api.debug
|
||||
}
|
||||
|
||||
// post to a slack web method.
|
||||
func (api *Client) postMethod(ctx context.Context, path string, values url.Values, intf interface{}) error {
|
||||
return postForm(ctx, api.httpclient, api.endpoint+path, values, intf, api)
|
||||
}
|
||||
|
||||
// get a slack web method.
|
||||
func (api *Client) getMethod(ctx context.Context, path string, values url.Values, intf interface{}) error {
|
||||
return getResource(ctx, api.httpclient, api.endpoint+path, values, intf, api)
|
||||
}
|
62
vendor/github.com/slack-go/slack/slackutilsx/slackutilsx.go
generated
vendored
Normal file
62
vendor/github.com/slack-go/slack/slackutilsx/slackutilsx.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
// Package slackutilsx is a utility package that doesn't promise API stability.
|
||||
// its for experimental functionality and utilities.
|
||||
package slackutilsx
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// ChannelType the type of channel based on the channelID
|
||||
type ChannelType int
|
||||
|
||||
func (t ChannelType) String() string {
|
||||
switch t {
|
||||
case CTypeDM:
|
||||
return "Direct"
|
||||
case CTypeGroup:
|
||||
return "Group"
|
||||
case CTypeChannel:
|
||||
return "Channel"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// CTypeUnknown represents channels we cannot properly detect.
|
||||
CTypeUnknown ChannelType = iota
|
||||
// CTypeDM is a private channel between two slack users.
|
||||
CTypeDM
|
||||
// CTypeGroup is a group channel.
|
||||
CTypeGroup
|
||||
// CTypeChannel is a public channel.
|
||||
CTypeChannel
|
||||
)
|
||||
|
||||
// DetectChannelType converts a channelID to a ChannelType.
|
||||
// channelID must not be empty. However, if it is empty, the channel type will default to Unknown.
|
||||
func DetectChannelType(channelID string) ChannelType {
|
||||
// intentionally ignore the error and just default to CTypeUnknown
|
||||
switch r, _ := utf8.DecodeRuneInString(channelID); r {
|
||||
case 'C':
|
||||
return CTypeChannel
|
||||
case 'G':
|
||||
return CTypeGroup
|
||||
case 'D':
|
||||
return CTypeDM
|
||||
default:
|
||||
return CTypeUnknown
|
||||
}
|
||||
}
|
||||
|
||||
// EscapeMessage text
|
||||
func EscapeMessage(message string) string {
|
||||
replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
|
||||
return replacer.Replace(message)
|
||||
}
|
||||
|
||||
// Retryable errors return true.
|
||||
type Retryable interface {
|
||||
Retryable() bool
|
||||
}
|
53
vendor/github.com/slack-go/slack/slash.go
generated
vendored
Normal file
53
vendor/github.com/slack-go/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
|
||||
}
|
263
vendor/github.com/slack-go/slack/stars.go
generated
vendored
Normal file
263
vendor/github.com/slack-go/slack/stars.go
generated
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_STARS_USER = ""
|
||||
DEFAULT_STARS_COUNT = 100
|
||||
DEFAULT_STARS_PAGE = 1
|
||||
)
|
||||
|
||||
type StarsParameters struct {
|
||||
User string
|
||||
Count int
|
||||
Page int
|
||||
}
|
||||
|
||||
type StarredItem Item
|
||||
|
||||
type listResponseFull struct {
|
||||
Items []Item `json:"items"`
|
||||
Paging `json:"paging"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
// NewStarsParameters initialises StarsParameters with default values
|
||||
func NewStarsParameters() StarsParameters {
|
||||
return StarsParameters{
|
||||
User: DEFAULT_STARS_USER,
|
||||
Count: DEFAULT_STARS_COUNT,
|
||||
Page: DEFAULT_STARS_PAGE,
|
||||
}
|
||||
}
|
||||
|
||||
// AddStar stars an item in a channel
|
||||
func (api *Client) AddStar(channel string, item ItemRef) error {
|
||||
return api.AddStarContext(context.Background(), channel, item)
|
||||
}
|
||||
|
||||
// AddStarContext stars an item in a channel with a custom context
|
||||
func (api *Client) AddStarContext(ctx context.Context, channel string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := api.postMethod(ctx, "stars.add", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// RemoveStar removes a starred item from a channel
|
||||
func (api *Client) RemoveStar(channel string, item ItemRef) error {
|
||||
return api.RemoveStarContext(context.Background(), channel, item)
|
||||
}
|
||||
|
||||
// RemoveStarContext removes a starred item from a channel with a custom context
|
||||
func (api *Client) RemoveStarContext(ctx context.Context, channel string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", item.Timestamp)
|
||||
}
|
||||
if item.File != "" {
|
||||
values.Set("file", item.File)
|
||||
}
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", item.Comment)
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := api.postMethod(ctx, "stars.remove", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// ListStars returns information about the stars a user added
|
||||
func (api *Client) ListStars(params StarsParameters) ([]Item, *Paging, error) {
|
||||
return api.ListStarsContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
if params.User != DEFAULT_STARS_USER {
|
||||
values.Add("user", params.User)
|
||||
}
|
||||
if params.Count != DEFAULT_STARS_COUNT {
|
||||
values.Add("count", strconv.Itoa(params.Count))
|
||||
}
|
||||
if params.Page != DEFAULT_STARS_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
|
||||
response := &listResponseFull{}
|
||||
err := api.postMethod(ctx, "stars.list", values, response)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return response.Items, &response.Paging, nil
|
||||
}
|
||||
|
||||
// GetStarred returns a list of StarredItem items.
|
||||
//
|
||||
// The user then has to iterate over them and figure out what they should
|
||||
// be looking at according to what is in the Type.
|
||||
// for _, item := range items {
|
||||
// switch c.Type {
|
||||
// case "file_comment":
|
||||
// log.Println(c.Comment)
|
||||
// case "file":
|
||||
// ...
|
||||
//
|
||||
// }
|
||||
// This function still exists to maintain backwards compatibility.
|
||||
// I exposed it as returning []StarredItem, so it shall stay as StarredItem
|
||||
func (api *Client) GetStarred(params StarsParameters) ([]StarredItem, *Paging, error) {
|
||||
return api.GetStarredContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetStarredContext returns a list of StarredItem items with a custom context
|
||||
//
|
||||
// For more details see GetStarred
|
||||
func (api *Client) GetStarredContext(ctx context.Context, params StarsParameters) ([]StarredItem, *Paging, error) {
|
||||
items, paging, err := api.ListStarsContext(ctx, params)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
starredItems := make([]StarredItem, len(items))
|
||||
for i, item := range items {
|
||||
starredItems[i] = StarredItem(item)
|
||||
}
|
||||
return starredItems, paging, nil
|
||||
}
|
||||
|
||||
type listResponsePaginated struct {
|
||||
Items []Item `json:"items"`
|
||||
SlackResponse
|
||||
Metadata ResponseMetadata `json:"response_metadata"`
|
||||
}
|
||||
|
||||
// StarredItemPagination allows for paginating over the starred items
|
||||
type StarredItemPagination struct {
|
||||
Items []Item
|
||||
limit int
|
||||
previousResp *ResponseMetadata
|
||||
c *Client
|
||||
}
|
||||
|
||||
// ListStarsOption options for the GetUsers method call.
|
||||
type ListStarsOption func(*StarredItemPagination)
|
||||
|
||||
// ListAllStars returns the complete list of starred items
|
||||
func (api *Client) ListAllStars() ([]Item, error) {
|
||||
return api.ListAllStarsContext(context.Background())
|
||||
}
|
||||
|
||||
// ListAllStarsContext returns the list of users (with their detailed information) with a custom context
|
||||
func (api *Client) ListAllStarsContext(ctx context.Context) (results []Item, err error) {
|
||||
p := api.ListStarsPaginated()
|
||||
for err == nil {
|
||||
p, err = p.next(ctx)
|
||||
if err == nil {
|
||||
results = append(results, p.Items...)
|
||||
} else if rateLimitedError, ok := err.(*RateLimitedError); ok {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
case <-time.After(rateLimitedError.RetryAfter):
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results, p.failure(err)
|
||||
}
|
||||
|
||||
// ListStarsPaginated fetches users in a paginated fashion, see ListStarsPaginationContext for usage.
|
||||
func (api *Client) ListStarsPaginated(options ...ListStarsOption) StarredItemPagination {
|
||||
return newStarPagination(api, options...)
|
||||
}
|
||||
|
||||
func newStarPagination(c *Client, options ...ListStarsOption) (sip StarredItemPagination) {
|
||||
sip = StarredItemPagination{
|
||||
c: c,
|
||||
limit: 200, // per slack api documentation.
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
opt(&sip)
|
||||
}
|
||||
|
||||
return sip
|
||||
}
|
||||
|
||||
// done checks if the pagination has completed
|
||||
func (StarredItemPagination) done(err error) bool {
|
||||
return err == errPaginationComplete
|
||||
}
|
||||
|
||||
// done checks if pagination failed.
|
||||
func (t StarredItemPagination) failure(err error) error {
|
||||
if t.done(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// next gets the next list of starred items based on the cursor value
|
||||
func (t StarredItemPagination) next(ctx context.Context) (_ StarredItemPagination, err error) {
|
||||
var (
|
||||
resp *listResponsePaginated
|
||||
)
|
||||
|
||||
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)},
|
||||
"token": {t.c.token},
|
||||
"cursor": {t.previousResp.Cursor},
|
||||
}
|
||||
|
||||
if err = t.c.postMethod(ctx, "stars.list", values, &resp); err != nil {
|
||||
return t, err
|
||||
}
|
||||
|
||||
t.previousResp = &resp.Metadata
|
||||
t.Items = resp.Items
|
||||
|
||||
return t, nil
|
||||
}
|
167
vendor/github.com/slack-go/slack/team.go
generated
vendored
Normal file
167
vendor/github.com/slack-go/slack/team.go
generated
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_LOGINS_COUNT = 100
|
||||
DEFAULT_LOGINS_PAGE = 1
|
||||
)
|
||||
|
||||
type TeamResponse struct {
|
||||
Team TeamInfo `json:"team"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
type TeamInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Domain string `json:"domain"`
|
||||
EmailDomain string `json:"email_domain"`
|
||||
Icon map[string]interface{} `json:"icon"`
|
||||
}
|
||||
|
||||
type LoginResponse struct {
|
||||
Logins []Login `json:"logins"`
|
||||
Paging `json:"paging"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
type Login struct {
|
||||
UserID string `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
DateFirst int `json:"date_first"`
|
||||
DateLast int `json:"date_last"`
|
||||
Count int `json:"count"`
|
||||
IP string `json:"ip"`
|
||||
UserAgent string `json:"user_agent"`
|
||||
ISP string `json:"isp"`
|
||||
Country string `json:"country"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type BillableInfoResponse struct {
|
||||
BillableInfo map[string]BillingActive `json:"billable_info"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
type BillingActive struct {
|
||||
BillingActive bool `json:"billing_active"`
|
||||
}
|
||||
|
||||
// AccessLogParameters contains all the parameters necessary (including the optional ones) for a GetAccessLogs() request
|
||||
type AccessLogParameters struct {
|
||||
Count int
|
||||
Page int
|
||||
}
|
||||
|
||||
// NewAccessLogParameters provides an instance of AccessLogParameters with all the sane default values set
|
||||
func NewAccessLogParameters() AccessLogParameters {
|
||||
return AccessLogParameters{
|
||||
Count: DEFAULT_LOGINS_COUNT,
|
||||
Page: DEFAULT_LOGINS_PAGE,
|
||||
}
|
||||
}
|
||||
|
||||
func (api *Client) teamRequest(ctx context.Context, path string, values url.Values) (*TeamResponse, error) {
|
||||
response := &TeamResponse{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
func (api *Client) billableInfoRequest(ctx context.Context, path string, values url.Values) (map[string]BillingActive, error) {
|
||||
response := &BillableInfoResponse{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.BillableInfo, response.Err()
|
||||
}
|
||||
|
||||
func (api *Client) accessLogsRequest(ctx context.Context, path string, values url.Values) (*LoginResponse, error) {
|
||||
response := &LoginResponse{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// GetTeamInfo gets the Team Information of the user
|
||||
func (api *Client) GetTeamInfo() (*TeamInfo, error) {
|
||||
return api.GetTeamInfoContext(context.Background())
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
|
||||
response, err := api.teamRequest(ctx, "team.info", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Team, nil
|
||||
}
|
||||
|
||||
// GetAccessLogs retrieves a page of logins according to the parameters given
|
||||
func (api *Client) GetAccessLogs(params AccessLogParameters) ([]Login, *Paging, error) {
|
||||
return api.GetAccessLogsContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
if params.Count != DEFAULT_LOGINS_COUNT {
|
||||
values.Add("count", strconv.Itoa(params.Count))
|
||||
}
|
||||
if params.Page != DEFAULT_LOGINS_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
|
||||
response, err := api.accessLogsRequest(ctx, "team.accessLogs", values)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return response.Logins, &response.Paging, nil
|
||||
}
|
||||
|
||||
// GetBillableInfo ...
|
||||
func (api *Client) GetBillableInfo(user string) (map[string]BillingActive, error) {
|
||||
return api.GetBillableInfoContext(context.Background(), user)
|
||||
}
|
||||
|
||||
// GetBillableInfoContext ...
|
||||
func (api *Client) GetBillableInfoContext(ctx context.Context, user string) (map[string]BillingActive, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
return api.billableInfoRequest(ctx, "team.billableInfo", values)
|
||||
}
|
||||
|
||||
// GetBillableInfoForTeam returns the billing_active status of all users on the team.
|
||||
func (api *Client) GetBillableInfoForTeam() (map[string]BillingActive, error) {
|
||||
return api.GetBillableInfoForTeamContext(context.Background())
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
}
|
||||
|
||||
return api.billableInfoRequest(ctx, "team.billableInfo", values)
|
||||
}
|
258
vendor/github.com/slack-go/slack/usergroups.go
generated
vendored
Normal file
258
vendor/github.com/slack-go/slack/usergroups.go
generated
vendored
Normal file
@ -0,0 +1,258 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// UserGroup contains all the information of a user group
|
||||
type UserGroup struct {
|
||||
ID string `json:"id"`
|
||||
TeamID string `json:"team_id"`
|
||||
IsUserGroup bool `json:"is_usergroup"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Handle string `json:"handle"`
|
||||
IsExternal bool `json:"is_external"`
|
||||
DateCreate JSONTime `json:"date_create"`
|
||||
DateUpdate JSONTime `json:"date_update"`
|
||||
DateDelete JSONTime `json:"date_delete"`
|
||||
AutoType string `json:"auto_type"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
UpdatedBy string `json:"updated_by"`
|
||||
DeletedBy string `json:"deleted_by"`
|
||||
Prefs UserGroupPrefs `json:"prefs"`
|
||||
UserCount int `json:"user_count"`
|
||||
Users []string `json:"users"`
|
||||
}
|
||||
|
||||
// UserGroupPrefs contains default channels and groups (private channels)
|
||||
type UserGroupPrefs struct {
|
||||
Channels []string `json:"channels"`
|
||||
Groups []string `json:"groups"`
|
||||
}
|
||||
|
||||
type userGroupResponseFull struct {
|
||||
UserGroups []UserGroup `json:"usergroups"`
|
||||
UserGroup UserGroup `json:"usergroup"`
|
||||
Users []string `json:"users"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func (api *Client) userGroupRequest(ctx context.Context, path string, values url.Values) (*userGroupResponseFull, error) {
|
||||
response := &userGroupResponseFull{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// CreateUserGroup creates a new user group
|
||||
func (api *Client) CreateUserGroup(userGroup UserGroup) (UserGroup, error) {
|
||||
return api.CreateUserGroupContext(context.Background(), userGroup)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"name": {userGroup.Name},
|
||||
}
|
||||
|
||||
if userGroup.Handle != "" {
|
||||
values["handle"] = []string{userGroup.Handle}
|
||||
}
|
||||
|
||||
if userGroup.Description != "" {
|
||||
values["description"] = []string{userGroup.Description}
|
||||
}
|
||||
|
||||
if len(userGroup.Prefs.Channels) > 0 {
|
||||
values["channels"] = []string{strings.Join(userGroup.Prefs.Channels, ",")}
|
||||
}
|
||||
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.create", values)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
return response.UserGroup, nil
|
||||
}
|
||||
|
||||
// DisableUserGroup disables an existing user group
|
||||
func (api *Client) DisableUserGroup(userGroup string) (UserGroup, error) {
|
||||
return api.DisableUserGroupContext(context.Background(), userGroup)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"usergroup": {userGroup},
|
||||
}
|
||||
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.disable", values)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
return response.UserGroup, nil
|
||||
}
|
||||
|
||||
// EnableUserGroup enables an existing user group
|
||||
func (api *Client) EnableUserGroup(userGroup string) (UserGroup, error) {
|
||||
return api.EnableUserGroupContext(context.Background(), userGroup)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"usergroup": {userGroup},
|
||||
}
|
||||
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.enable", values)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
return response.UserGroup, nil
|
||||
}
|
||||
|
||||
// GetUserGroupsOption options for the GetUserGroups method call.
|
||||
type GetUserGroupsOption func(*GetUserGroupsParams)
|
||||
|
||||
// GetUserGroupsOptionIncludeCount include the number of users in each User Group (default: false)
|
||||
func GetUserGroupsOptionIncludeCount(b bool) GetUserGroupsOption {
|
||||
return func(params *GetUserGroupsParams) {
|
||||
params.IncludeCount = b
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserGroupsOptionIncludeDisabled include disabled User Groups (default: false)
|
||||
func GetUserGroupsOptionIncludeDisabled(b bool) GetUserGroupsOption {
|
||||
return func(params *GetUserGroupsParams) {
|
||||
params.IncludeDisabled = b
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserGroupsOptionIncludeUsers include the list of users for each User Group (default: false)
|
||||
func GetUserGroupsOptionIncludeUsers(b bool) GetUserGroupsOption {
|
||||
return func(params *GetUserGroupsParams) {
|
||||
params.IncludeUsers = b
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserGroupsParams contains arguments for GetUserGroups method call
|
||||
type GetUserGroupsParams struct {
|
||||
IncludeCount bool
|
||||
IncludeDisabled bool
|
||||
IncludeUsers bool
|
||||
}
|
||||
|
||||
// GetUserGroups returns a list of user groups for the team
|
||||
func (api *Client) GetUserGroups(options ...GetUserGroupsOption) ([]UserGroup, error) {
|
||||
return api.GetUserGroupsContext(context.Background(), options...)
|
||||
}
|
||||
|
||||
// GetUserGroupsContext returns a list of user groups for the team with a custom context
|
||||
func (api *Client) GetUserGroupsContext(ctx context.Context, options ...GetUserGroupsOption) ([]UserGroup, error) {
|
||||
params := GetUserGroupsParams{}
|
||||
|
||||
for _, opt := range options {
|
||||
opt(¶ms)
|
||||
}
|
||||
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.IncludeCount {
|
||||
values.Add("include_count", "true")
|
||||
}
|
||||
if params.IncludeDisabled {
|
||||
values.Add("include_disabled", "true")
|
||||
}
|
||||
if params.IncludeUsers {
|
||||
values.Add("include_users", "true")
|
||||
}
|
||||
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.list", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.UserGroups, nil
|
||||
}
|
||||
|
||||
// UpdateUserGroup will update an existing user group
|
||||
func (api *Client) UpdateUserGroup(userGroup UserGroup) (UserGroup, error) {
|
||||
return api.UpdateUserGroupContext(context.Background(), userGroup)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"usergroup": {userGroup.ID},
|
||||
}
|
||||
|
||||
if userGroup.Name != "" {
|
||||
values["name"] = []string{userGroup.Name}
|
||||
}
|
||||
|
||||
if userGroup.Handle != "" {
|
||||
values["handle"] = []string{userGroup.Handle}
|
||||
}
|
||||
|
||||
if userGroup.Description != "" {
|
||||
values["description"] = []string{userGroup.Description}
|
||||
}
|
||||
|
||||
if len(userGroup.Prefs.Channels) > 0 {
|
||||
values["channels"] = []string{strings.Join(userGroup.Prefs.Channels, ",")}
|
||||
}
|
||||
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.update", values)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
return response.UserGroup, nil
|
||||
}
|
||||
|
||||
// GetUserGroupMembers will retrieve the current list of users in a group
|
||||
func (api *Client) GetUserGroupMembers(userGroup string) ([]string, error) {
|
||||
return api.GetUserGroupMembersContext(context.Background(), userGroup)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"usergroup": {userGroup},
|
||||
}
|
||||
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.users.list", values)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
return response.Users, nil
|
||||
}
|
||||
|
||||
// UpdateUserGroupMembers will update the members of an existing user group
|
||||
func (api *Client) UpdateUserGroupMembers(userGroup string, members string) (UserGroup, error) {
|
||||
return api.UpdateUserGroupMembersContext(context.Background(), userGroup, members)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"usergroup": {userGroup},
|
||||
"users": {members},
|
||||
}
|
||||
|
||||
response, err := api.userGroupRequest(ctx, "usergroups.users.update", values)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
return response.UserGroup, nil
|
||||
}
|
597
vendor/github.com/slack-go/slack/users.go
generated
vendored
Normal file
597
vendor/github.com/slack-go/slack/users.go
generated
vendored
Normal file
@ -0,0 +1,597 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_USER_PHOTO_CROP_X = -1
|
||||
DEFAULT_USER_PHOTO_CROP_Y = -1
|
||||
DEFAULT_USER_PHOTO_CROP_W = -1
|
||||
)
|
||||
|
||||
// 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"`
|
||||
StatusExpiration int `json:"status_expiration"`
|
||||
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/slack-go/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/slack-go/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"`
|
||||
RealName string `json:"real_name"`
|
||||
TZ string `json:"tz,omitempty"`
|
||||
TZLabel string `json:"tz_label"`
|
||||
TZOffset int `json:"tz_offset"`
|
||||
Profile UserProfile `json:"profile"`
|
||||
IsBot bool `json:"is_bot"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
IsOwner bool `json:"is_owner"`
|
||||
IsPrimaryOwner bool `json:"is_primary_owner"`
|
||||
IsRestricted bool `json:"is_restricted"`
|
||||
IsUltraRestricted bool `json:"is_ultra_restricted"`
|
||||
IsStranger bool `json:"is_stranger"`
|
||||
IsAppUser bool `json:"is_app_user"`
|
||||
IsInvitedUser bool `json:"is_invited_user"`
|
||||
Has2FA bool `json:"has_2fa"`
|
||||
HasFiles bool `json:"has_files"`
|
||||
Presence string `json:"presence"`
|
||||
Locale string `json:"locale"`
|
||||
Updated JSONTime `json:"updated"`
|
||||
Enterprise EnterpriseUser `json:"enterprise_user,omitempty"`
|
||||
}
|
||||
|
||||
// UserPresence contains details about a user online status
|
||||
type UserPresence struct {
|
||||
Presence string `json:"presence,omitempty"`
|
||||
Online bool `json:"online,omitempty"`
|
||||
AutoAway bool `json:"auto_away,omitempty"`
|
||||
ManualAway bool `json:"manual_away,omitempty"`
|
||||
ConnectionCount int `json:"connection_count,omitempty"`
|
||||
LastActivity JSONTime `json:"last_activity,omitempty"`
|
||||
}
|
||||
|
||||
type UserIdentityResponse struct {
|
||||
User UserIdentity `json:"user"`
|
||||
Team TeamIdentity `json:"team"`
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
type UserIdentity struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
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"`
|
||||
Image512 string `json:"image_512"`
|
||||
}
|
||||
|
||||
// EnterpriseUser is present when a user is part of Slack Enterprise Grid
|
||||
// https://api.slack.com/types/user#enterprise_grid_user_objects
|
||||
type EnterpriseUser struct {
|
||||
ID string `json:"id"`
|
||||
EnterpriseID string `json:"enterprise_id"`
|
||||
EnterpriseName string `json:"enterprise_name"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
IsOwner bool `json:"is_owner"`
|
||||
Teams []string `json:"teams"`
|
||||
}
|
||||
|
||||
type TeamIdentity struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Domain string `json:"domain"`
|
||||
Image34 string `json:"image_34"`
|
||||
Image44 string `json:"image_44"`
|
||||
Image68 string `json:"image_68"`
|
||||
Image88 string `json:"image_88"`
|
||||
Image102 string `json:"image_102"`
|
||||
Image132 string `json:"image_132"`
|
||||
Image230 string `json:"image_230"`
|
||||
ImageDefault bool `json:"image_default"`
|
||||
ImageOriginal string `json:"image_original"`
|
||||
}
|
||||
|
||||
type userResponseFull struct {
|
||||
Members []User `json:"members,omitempty"`
|
||||
User `json:"user,omitempty"`
|
||||
UserPresence
|
||||
SlackResponse
|
||||
Metadata ResponseMetadata `json:"response_metadata"`
|
||||
}
|
||||
|
||||
type UserSetPhotoParams struct {
|
||||
CropX int
|
||||
CropY int
|
||||
CropW int
|
||||
}
|
||||
|
||||
func NewUserSetPhotoParams() UserSetPhotoParams {
|
||||
return UserSetPhotoParams{
|
||||
CropX: DEFAULT_USER_PHOTO_CROP_X,
|
||||
CropY: DEFAULT_USER_PHOTO_CROP_Y,
|
||||
CropW: DEFAULT_USER_PHOTO_CROP_W,
|
||||
}
|
||||
}
|
||||
|
||||
func (api *Client) userRequest(ctx context.Context, path string, values url.Values) (*userResponseFull, error) {
|
||||
response := &userResponseFull{}
|
||||
err := api.postMethod(ctx, path, values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, response.Err()
|
||||
}
|
||||
|
||||
// GetUserPresence will retrieve the current presence status of given user.
|
||||
func (api *Client) GetUserPresence(user string) (*UserPresence, error) {
|
||||
return api.GetUserPresenceContext(context.Background(), user)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
response, err := api.userRequest(ctx, "users.getPresence", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.UserPresence, nil
|
||||
}
|
||||
|
||||
// GetUserInfo will retrieve the complete user information
|
||||
func (api *Client) GetUserInfo(user string) (*User, error) {
|
||||
return api.GetUserInfoContext(context.Background(), user)
|
||||
}
|
||||
|
||||
// GetUserInfoContext will retrieve the complete user information with a custom context
|
||||
func (api *Client) GetUserInfoContext(ctx context.Context, user string) (*User, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
"include_locale": {strconv.FormatBool(true)},
|
||||
}
|
||||
|
||||
response, err := api.userRequest(ctx, "users.info", values)
|
||||
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},
|
||||
"include_locale": {strconv.FormatBool(true)},
|
||||
}
|
||||
|
||||
if resp, err = t.c.userRequest(ctx, "users.list", values); 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) (results []User, err error) {
|
||||
p := api.GetUsersPaginated()
|
||||
for err == nil {
|
||||
p, err = p.Next(ctx)
|
||||
if err == nil {
|
||||
results = append(results, p.Users...)
|
||||
} else if rateLimitedError, ok := err.(*RateLimitedError); ok {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
case <-time.After(rateLimitedError.RetryAfter):
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 := api.userRequest(ctx, "users.lookupByEmail", values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.User, nil
|
||||
}
|
||||
|
||||
// SetUserAsActive marks the currently authenticated user as active
|
||||
func (api *Client) SetUserAsActive() error {
|
||||
return api.SetUserAsActiveContext(context.Background())
|
||||
}
|
||||
|
||||
// SetUserAsActiveContext marks the currently authenticated user as active with a custom context
|
||||
func (api *Client) SetUserAsActiveContext(ctx context.Context) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
_, err = api.userRequest(ctx, "users.setActive", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetUserPresence changes the currently authenticated user presence
|
||||
func (api *Client) SetUserPresence(presence string) error {
|
||||
return api.SetUserPresenceContext(context.Background(), presence)
|
||||
}
|
||||
|
||||
// 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.token},
|
||||
"presence": {presence},
|
||||
}
|
||||
|
||||
_, err := api.userRequest(ctx, "users.setPresence", values)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetUserIdentity will retrieve user info available per identity scopes
|
||||
func (api *Client) GetUserIdentity() (*UserIdentityResponse, error) {
|
||||
return api.GetUserIdentityContext(context.Background())
|
||||
}
|
||||
|
||||
// GetUserIdentityContext will retrieve user info available per identity scopes with a custom context
|
||||
func (api *Client) GetUserIdentityContext(ctx context.Context) (response *UserIdentityResponse, err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
}
|
||||
response = &UserIdentityResponse{}
|
||||
|
||||
err = api.postMethod(ctx, "users.identity", values, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := response.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// SetUserPhoto changes the currently authenticated user's profile image
|
||||
func (api *Client) SetUserPhoto(image string, params UserSetPhotoParams) error {
|
||||
return api.SetUserPhotoContext(context.Background(), image, params)
|
||||
}
|
||||
|
||||
// SetUserPhotoContext changes the currently authenticated user's profile image using a custom context
|
||||
func (api *Client) SetUserPhotoContext(ctx context.Context, image string, params UserSetPhotoParams) (err error) {
|
||||
response := &SlackResponse{}
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.CropX != DEFAULT_USER_PHOTO_CROP_X {
|
||||
values.Add("crop_x", strconv.Itoa(params.CropX))
|
||||
}
|
||||
if params.CropY != DEFAULT_USER_PHOTO_CROP_Y {
|
||||
values.Add("crop_y", strconv.Itoa(params.CropX))
|
||||
}
|
||||
if params.CropW != DEFAULT_USER_PHOTO_CROP_W {
|
||||
values.Add("crop_w", strconv.Itoa(params.CropW))
|
||||
}
|
||||
|
||||
err = postLocalWithMultipartResponse(ctx, api.httpclient, api.endpoint+"users.setPhoto", image, "image", values, response, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// DeleteUserPhoto deletes the current authenticated user's profile image
|
||||
func (api *Client) DeleteUserPhoto() error {
|
||||
return api.DeleteUserPhotoContext(context.Background())
|
||||
}
|
||||
|
||||
// DeleteUserPhotoContext deletes the current authenticated user's profile image with a custom context
|
||||
func (api *Client) DeleteUserPhotoContext(ctx context.Context) (err error) {
|
||||
response := &SlackResponse{}
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
err = api.postMethod(ctx, "users.deletePhoto", values, response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// SetUserCustomStatus will set a custom status and emoji for the currently
|
||||
// authenticated user. If statusEmoji is "" and statusText is not, the Slack API
|
||||
// will automatically set it to ":speech_balloon:". Otherwise, if both are ""
|
||||
// the Slack API will unset the custom status/emoji. If statusExpiration is set to 0
|
||||
// the status will not expire.
|
||||
func (api *Client) SetUserCustomStatus(statusText, statusEmoji string, statusExpiration int64) error {
|
||||
return api.SetUserCustomStatusContextWithUser(context.Background(), "", statusText, statusEmoji, statusExpiration)
|
||||
}
|
||||
|
||||
// SetUserCustomStatusContext will set a custom status and emoji for the currently authenticated user with a custom context
|
||||
//
|
||||
// For more information see SetUserCustomStatus
|
||||
func (api *Client) SetUserCustomStatusContext(ctx context.Context, statusText, statusEmoji string, statusExpiration int64) error {
|
||||
return api.SetUserCustomStatusContextWithUser(context.Background(), "", statusText, statusEmoji, statusExpiration)
|
||||
}
|
||||
|
||||
// SetUserCustomStatusWithUser will set a custom status and emoji for the provided user.
|
||||
//
|
||||
// For more information see SetUserCustomStatus
|
||||
func (api *Client) SetUserCustomStatusWithUser(user, statusText, statusEmoji string, statusExpiration int64) error {
|
||||
return api.SetUserCustomStatusContextWithUser(context.Background(), user, statusText, statusEmoji, statusExpiration)
|
||||
}
|
||||
|
||||
// SetUserCustomStatusContextWithUser will set a custom status and emoji for the provided user with a custom context
|
||||
//
|
||||
// For more information see SetUserCustomStatus
|
||||
func (api *Client) SetUserCustomStatusContextWithUser(ctx context.Context, user, statusText, statusEmoji string, statusExpiration int64) error {
|
||||
// XXX(theckman): this anonymous struct is for making requests to the Slack
|
||||
// API for setting and unsetting a User's Custom Status/Emoji. To change
|
||||
// these values we must provide a JSON document as the profile POST field.
|
||||
//
|
||||
// We use an anonymous struct over UserProfile because to unset the values
|
||||
// on the User's profile we cannot use the `json:"omitempty"` tag. This is
|
||||
// because an empty string ("") is what's used to unset the values. Check
|
||||
// out the API docs for more details:
|
||||
//
|
||||
// - https://api.slack.com/docs/presence-and-status#custom_status
|
||||
profile, err := json.Marshal(
|
||||
&struct {
|
||||
StatusText string `json:"status_text"`
|
||||
StatusEmoji string `json:"status_emoji"`
|
||||
StatusExpiration int64 `json:"status_expiration"`
|
||||
}{
|
||||
StatusText: statusText,
|
||||
StatusEmoji: statusEmoji,
|
||||
StatusExpiration: statusExpiration,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
values := url.Values{
|
||||
"user": {user},
|
||||
"token": {api.token},
|
||||
"profile": {string(profile)},
|
||||
}
|
||||
|
||||
response := &userResponseFull{}
|
||||
if err = api.postMethod(ctx, "users.profile.set", values, response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Err()
|
||||
}
|
||||
|
||||
// UnsetUserCustomStatus removes the custom status message for the currently
|
||||
// authenticated user. This is a convenience method that wraps (*Client).SetUserCustomStatus().
|
||||
func (api *Client) UnsetUserCustomStatus() error {
|
||||
return api.UnsetUserCustomStatusContext(context.Background())
|
||||
}
|
||||
|
||||
// UnsetUserCustomStatusContext removes the custom status message for the currently authenticated user
|
||||
// with a custom context. This is a convenience method that wraps (*Client).SetUserCustomStatus().
|
||||
func (api *Client) UnsetUserCustomStatusContext(ctx context.Context) error {
|
||||
return api.SetUserCustomStatusContext(ctx, "", "", 0)
|
||||
}
|
||||
|
||||
// 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 := api.postMethod(ctx, "users.profile.get", values, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := resp.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Profile, nil
|
||||
}
|
221
vendor/github.com/slack-go/slack/views.go
generated
vendored
Normal file
221
vendor/github.com/slack-go/slack/views.go
generated
vendored
Normal file
@ -0,0 +1,221 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
const (
|
||||
VTModal ViewType = "modal"
|
||||
VTHomeTab ViewType = "home"
|
||||
)
|
||||
|
||||
type ViewType string
|
||||
|
||||
type View struct {
|
||||
SlackResponse
|
||||
ID string `json:"id"`
|
||||
TeamID string `json:"team_id"`
|
||||
Type ViewType `json:"type"`
|
||||
Title *TextBlockObject `json:"title"`
|
||||
Close *TextBlockObject `json:"close"`
|
||||
Submit *TextBlockObject `json:"submit"`
|
||||
Blocks Blocks `json:"blocks"`
|
||||
PrivateMetadata string `json:"private_metadata"`
|
||||
CallbackID string `json:"callback_id"`
|
||||
State interface{} `json:"state"`
|
||||
Hash string `json:"hash"`
|
||||
ClearOnClose bool `json:"clear_on_close"`
|
||||
NotifyOnClose bool `json:"notify_on_close"`
|
||||
RootViewID string `json:"root_view_id"`
|
||||
PreviousViewID string `json:"previous_view_id"`
|
||||
AppID string `json:"app_id"`
|
||||
ExternalID string `json:"external_id"`
|
||||
BotID string `json:"bot_id"`
|
||||
}
|
||||
|
||||
type ModalViewRequest struct {
|
||||
Type ViewType `json:"type"`
|
||||
Title *TextBlockObject `json:"title"`
|
||||
Blocks Blocks `json:"blocks"`
|
||||
Close *TextBlockObject `json:"close"`
|
||||
Submit *TextBlockObject `json:"submit"`
|
||||
PrivateMetadata string `json:"private_metadata"`
|
||||
CallbackID string `json:"callback_id"`
|
||||
ClearOnClose bool `json:"clear_on_close"`
|
||||
NotifyOnClose bool `json:"notify_on_close"`
|
||||
ExternalID string `json:"external_id"`
|
||||
}
|
||||
|
||||
func (v *ModalViewRequest) ViewType() ViewType {
|
||||
return v.Type
|
||||
}
|
||||
|
||||
type HomeTabViewRequest struct {
|
||||
Type ViewType `json:"type"`
|
||||
Blocks Blocks `json:"blocks"`
|
||||
PrivateMetadata string `json:"private_metadata"`
|
||||
CallbackID string `json:"callback_id"`
|
||||
ExternalID string `json:"external_id"`
|
||||
}
|
||||
|
||||
func (v *HomeTabViewRequest) ViewType() ViewType {
|
||||
return v.Type
|
||||
}
|
||||
|
||||
type openViewRequest struct {
|
||||
TriggerID string `json:"trigger_id"`
|
||||
View ModalViewRequest `json:"view"`
|
||||
}
|
||||
|
||||
type publishViewRequest struct {
|
||||
UserID string `json:"user_id"`
|
||||
View HomeTabViewRequest `json:"view"`
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
|
||||
type pushViewRequest struct {
|
||||
TriggerID string `json:"trigger_id"`
|
||||
View ModalViewRequest `json:"view"`
|
||||
}
|
||||
|
||||
type updateViewRequest struct {
|
||||
View ModalViewRequest `json:"view"`
|
||||
ExternalID string `json:"external_id"`
|
||||
Hash string `json:"hash"`
|
||||
ViewID string `json:"view_id"`
|
||||
}
|
||||
|
||||
type ViewResponse struct {
|
||||
SlackResponse
|
||||
View `json:"view"`
|
||||
}
|
||||
|
||||
// OpenView opens a view for a user.
|
||||
func (api *Client) OpenView(triggerID string, view ModalViewRequest) (*ViewResponse, error) {
|
||||
return api.OpenViewContext(context.Background(), triggerID, view)
|
||||
}
|
||||
|
||||
// OpenViewContext opens a view for a user with a custom context.
|
||||
func (api *Client) OpenViewContext(
|
||||
ctx context.Context,
|
||||
triggerID string,
|
||||
view ModalViewRequest,
|
||||
) (*ViewResponse, error) {
|
||||
if triggerID == "" {
|
||||
return nil, ErrParametersMissing
|
||||
}
|
||||
req := openViewRequest{
|
||||
TriggerID: triggerID,
|
||||
View: view,
|
||||
}
|
||||
encoded, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endpoint := api.endpoint + "views.open"
|
||||
resp := &ViewResponse{}
|
||||
err = postJSON(ctx, api.httpclient, endpoint, api.token, encoded, resp, api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, resp.Err()
|
||||
}
|
||||
|
||||
// PublishView publishes a static view for a user.
|
||||
func (api *Client) PublishView(userID string, view HomeTabViewRequest, hash string) (*ViewResponse, error) {
|
||||
return api.PublishViewContext(context.Background(), userID, view, hash)
|
||||
}
|
||||
|
||||
// PublishViewContext publishes a static view for a user with a custom context.
|
||||
func (api *Client) PublishViewContext(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
view HomeTabViewRequest,
|
||||
hash string,
|
||||
) (*ViewResponse, error) {
|
||||
if userID == "" {
|
||||
return nil, ErrParametersMissing
|
||||
}
|
||||
req := publishViewRequest{
|
||||
UserID: userID,
|
||||
View: view,
|
||||
Hash: hash,
|
||||
}
|
||||
encoded, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endpoint := api.endpoint + "views.publish"
|
||||
resp := &ViewResponse{}
|
||||
err = postJSON(ctx, api.httpclient, endpoint, api.token, encoded, resp, api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, resp.Err()
|
||||
}
|
||||
|
||||
// PushView pushes a view onto the stack of a root view.
|
||||
func (api *Client) PushView(triggerID string, view ModalViewRequest) (*ViewResponse, error) {
|
||||
return api.PushViewContext(context.Background(), triggerID, view)
|
||||
}
|
||||
|
||||
// PublishViewContext pushes a view onto the stack of a root view with a custom context.
|
||||
func (api *Client) PushViewContext(
|
||||
ctx context.Context,
|
||||
triggerID string,
|
||||
view ModalViewRequest,
|
||||
) (*ViewResponse, error) {
|
||||
if triggerID == "" {
|
||||
return nil, ErrParametersMissing
|
||||
}
|
||||
req := pushViewRequest{
|
||||
TriggerID: triggerID,
|
||||
View: view,
|
||||
}
|
||||
encoded, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endpoint := api.endpoint + "views.push"
|
||||
resp := &ViewResponse{}
|
||||
err = postJSON(ctx, api.httpclient, endpoint, api.token, encoded, resp, api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, resp.Err()
|
||||
}
|
||||
|
||||
// UpdateView updates an existing view.
|
||||
func (api *Client) UpdateView(view ModalViewRequest, externalID, hash, viewID string) (*ViewResponse, error) {
|
||||
return api.UpdateViewContext(context.Background(), view, externalID, hash, viewID)
|
||||
}
|
||||
|
||||
// UpdateViewContext updates an existing view with a custom context.
|
||||
func (api *Client) UpdateViewContext(
|
||||
ctx context.Context,
|
||||
view ModalViewRequest,
|
||||
externalID, hash,
|
||||
viewID string,
|
||||
) (*ViewResponse, error) {
|
||||
if externalID == "" && viewID == "" {
|
||||
return nil, ErrParametersMissing
|
||||
}
|
||||
req := updateViewRequest{
|
||||
View: view,
|
||||
ExternalID: externalID,
|
||||
Hash: hash,
|
||||
ViewID: viewID,
|
||||
}
|
||||
encoded, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endpoint := api.endpoint + "views.update"
|
||||
resp := &ViewResponse{}
|
||||
err = postJSON(ctx, api.httpclient, endpoint, api.token, encoded, resp, api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, resp.Err()
|
||||
}
|
29
vendor/github.com/slack-go/slack/webhooks.go
generated
vendored
Normal file
29
vendor/github.com/slack-go/slack/webhooks.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WebhookMessage struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
IconEmoji string `json:"icon_emoji,omitempty"`
|
||||
IconURL string `json:"icon_url,omitempty"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
ThreadTimestamp string `json:"thread_ts,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Attachments []Attachment `json:"attachments,omitempty"`
|
||||
Parse string `json:"parse,omitempty"`
|
||||
}
|
||||
|
||||
func PostWebhook(url string, msg *WebhookMessage) error {
|
||||
return PostWebhookCustomHTTPContext(context.Background(), url, http.DefaultClient, msg)
|
||||
}
|
||||
|
||||
func PostWebhookContext(ctx context.Context, url string, msg *WebhookMessage) error {
|
||||
return PostWebhookCustomHTTPContext(ctx, url, http.DefaultClient, msg)
|
||||
}
|
||||
|
||||
func PostWebhookCustomHTTP(url string, httpClient *http.Client, msg *WebhookMessage) error {
|
||||
return PostWebhookCustomHTTPContext(context.Background(), url, httpClient, msg)
|
||||
}
|
34
vendor/github.com/slack-go/slack/webhooks_go112.go
generated
vendored
Normal file
34
vendor/github.com/slack-go/slack/webhooks_go112.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
// +build !go1.13
|
||||
|
||||
package slack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func PostWebhookCustomHTTPContext(ctx context.Context, url string, httpClient *http.Client, msg *WebhookMessage) error {
|
||||
raw, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal failed")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(raw))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed new request")
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to post webhook")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return checkStatusCode(resp, discard{})
|
||||
}
|
33
vendor/github.com/slack-go/slack/webhooks_go113.go
generated
vendored
Normal file
33
vendor/github.com/slack-go/slack/webhooks_go113.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// +build go1.13
|
||||
|
||||
package slack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func PostWebhookCustomHTTPContext(ctx context.Context, url string, httpClient *http.Client, msg *WebhookMessage) error {
|
||||
raw, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal failed")
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(raw))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed new request")
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to post webhook")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return checkStatusCode(resp, discard{})
|
||||
}
|
103
vendor/github.com/slack-go/slack/websocket.go
generated
vendored
Normal file
103
vendor/github.com/slack-go/slack/websocket.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxMessageTextLength is the current maximum message length in number of characters as defined here
|
||||
// https://api.slack.com/rtm#limits
|
||||
MaxMessageTextLength = 4000
|
||||
)
|
||||
|
||||
// RTM represents a managed websocket connection. It also supports
|
||||
// all the methods of the `Client` type.
|
||||
//
|
||||
// Create this element with Client's NewRTM() or NewRTMWithOptions(*RTMOptions)
|
||||
type RTM struct {
|
||||
// Client is the main API, embedded
|
||||
Client
|
||||
|
||||
idGen IDGenerator
|
||||
pingInterval time.Duration
|
||||
pingDeadman *time.Timer
|
||||
|
||||
// Connection life-cycle
|
||||
conn *websocket.Conn
|
||||
IncomingEvents chan RTMEvent
|
||||
outgoingMessages chan OutgoingMessage
|
||||
killChannel chan bool
|
||||
disconnected chan struct{}
|
||||
disconnectedm *sync.Once
|
||||
forcePing chan bool
|
||||
|
||||
// UserDetails upon connection
|
||||
info *Info
|
||||
|
||||
// useRTMStart should be set to true if you want to use
|
||||
// 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
|
||||
|
||||
// connParams is a map of flags for connection parameters.
|
||||
connParams url.Values
|
||||
}
|
||||
|
||||
// signal that we are disconnected by closing the channel.
|
||||
// protect it with a mutex to ensure it only happens once.
|
||||
func (rtm *RTM) disconnect() {
|
||||
rtm.disconnectedm.Do(func() {
|
||||
close(rtm.disconnected)
|
||||
})
|
||||
}
|
||||
|
||||
// Disconnect and wait, blocking until a successful disconnection.
|
||||
func (rtm *RTM) Disconnect() error {
|
||||
// always push into the kill channel when invoked,
|
||||
// this lets the ManagedConnection() function properly clean up.
|
||||
// if the buffer is full then just continue on.
|
||||
select {
|
||||
case rtm.killChannel <- true:
|
||||
return nil
|
||||
case <-rtm.disconnected:
|
||||
return ErrAlreadyDisconnected
|
||||
}
|
||||
}
|
||||
|
||||
// GetInfo returns the info structure received when calling
|
||||
// "startrtm", holding metadata needed to implement a full
|
||||
// chat client. It will be non-nil after a call to StartRTM().
|
||||
func (rtm *RTM) GetInfo() *Info {
|
||||
return rtm.info
|
||||
}
|
||||
|
||||
// SendMessage submits a simple message through the websocket. For
|
||||
// more complicated messages, use `rtm.PostMessage` with a complete
|
||||
// struct describing your attachments and all.
|
||||
func (rtm *RTM) SendMessage(msg *OutgoingMessage) {
|
||||
if msg == nil {
|
||||
rtm.Debugln("Error: Attempted to SendMessage(nil)")
|
||||
return
|
||||
}
|
||||
|
||||
rtm.outgoingMessages <- *msg
|
||||
}
|
||||
|
||||
func (rtm *RTM) resetDeadman() {
|
||||
rtm.pingDeadman.Reset(deadmanDuration(rtm.pingInterval))
|
||||
}
|
||||
|
||||
func deadmanDuration(d time.Duration) time.Duration {
|
||||
return d * 4
|
||||
}
|
72
vendor/github.com/slack-go/slack/websocket_channels.go
generated
vendored
Normal file
72
vendor/github.com/slack-go/slack/websocket_channels.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
package slack
|
||||
|
||||
// ChannelCreatedEvent represents the Channel created event
|
||||
type ChannelCreatedEvent struct {
|
||||
Type string `json:"type"`
|
||||
Channel ChannelCreatedInfo `json:"channel"`
|
||||
EventTimestamp string `json:"event_ts"`
|
||||
}
|
||||
|
||||
// ChannelCreatedInfo represents the information associated with the Channel created event
|
||||
type ChannelCreatedInfo struct {
|
||||
ID string `json:"id"`
|
||||
IsChannel bool `json:"is_channel"`
|
||||
Name string `json:"name"`
|
||||
Created int `json:"created"`
|
||||
Creator string `json:"creator"`
|
||||
}
|
||||
|
||||
// ChannelJoinedEvent represents the Channel joined event
|
||||
type ChannelJoinedEvent struct {
|
||||
Type string `json:"type"`
|
||||
Channel Channel `json:"channel"`
|
||||
}
|
||||
|
||||
// ChannelInfoEvent represents the Channel info event
|
||||
type ChannelInfoEvent struct {
|
||||
// channel_left
|
||||
// channel_deleted
|
||||
// channel_archive
|
||||
// channel_unarchive
|
||||
Type string `json:"type"`
|
||||
Channel string `json:"channel"`
|
||||
User string `json:"user,omitempty"`
|
||||
Timestamp string `json:"ts,omitempty"`
|
||||
}
|
||||
|
||||
// ChannelRenameEvent represents the Channel rename event
|
||||
type ChannelRenameEvent struct {
|
||||
Type string `json:"type"`
|
||||
Channel ChannelRenameInfo `json:"channel"`
|
||||
Timestamp string `json:"event_ts"`
|
||||
}
|
||||
|
||||
// ChannelRenameInfo represents the information associated with a Channel rename event
|
||||
type ChannelRenameInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Created string `json:"created"`
|
||||
}
|
||||
|
||||
// ChannelHistoryChangedEvent represents the Channel history changed event
|
||||
type ChannelHistoryChangedEvent struct {
|
||||
Type string `json:"type"`
|
||||
Latest string `json:"latest"`
|
||||
Timestamp string `json:"ts"`
|
||||
EventTimestamp string `json:"event_ts"`
|
||||
}
|
||||
|
||||
// ChannelMarkedEvent represents the Channel marked event
|
||||
type ChannelMarkedEvent ChannelInfoEvent
|
||||
|
||||
// ChannelLeftEvent represents the Channel left event
|
||||
type ChannelLeftEvent ChannelInfoEvent
|
||||
|
||||
// ChannelDeletedEvent represents the Channel deleted event
|
||||
type ChannelDeletedEvent ChannelInfoEvent
|
||||
|
||||
// ChannelArchiveEvent represents the Channel archive event
|
||||
type ChannelArchiveEvent ChannelInfoEvent
|
||||
|
||||
// ChannelUnarchiveEvent represents the Channel unarchive event
|
||||
type ChannelUnarchiveEvent ChannelInfoEvent
|
19
vendor/github.com/slack-go/slack/websocket_desktop_notification.go
generated
vendored
Normal file
19
vendor/github.com/slack-go/slack/websocket_desktop_notification.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
package slack
|
||||
|
||||
// DesktopNotificationEvent represents the update event for Desktop Notification.
|
||||
type DesktopNotificationEvent struct {
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Subtitle string `json:"subtitle"`
|
||||
Message string `json:"msg"`
|
||||
Timestamp string `json:"ts"`
|
||||
Content string `json:"content"`
|
||||
Channel string `json:"channel"`
|
||||
LaunchURI string `json:"launchUri"`
|
||||
AvatarImage string `json:"avatarImage"`
|
||||
SsbFilename string `json:"ssbFilename"`
|
||||
ImageURI string `json:"imageUri"`
|
||||
IsShared bool `json:"is_shared"`
|
||||
IsChannelInvite bool `json:"is_channel_invite"`
|
||||
EventTimestamp string `json:"event_ts"`
|
||||
}
|
23
vendor/github.com/slack-go/slack/websocket_dm.go
generated
vendored
Normal file
23
vendor/github.com/slack-go/slack/websocket_dm.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
package slack
|
||||
|
||||
// IMCreatedEvent represents the IM created event
|
||||
type IMCreatedEvent struct {
|
||||
Type string `json:"type"`
|
||||
User string `json:"user"`
|
||||
Channel ChannelCreatedInfo `json:"channel"`
|
||||
}
|
||||
|
||||
// IMHistoryChangedEvent represents the IM history changed event
|
||||
type IMHistoryChangedEvent ChannelHistoryChangedEvent
|
||||
|
||||
// IMOpenEvent represents the IM open event
|
||||
type IMOpenEvent ChannelInfoEvent
|
||||
|
||||
// IMCloseEvent represents the IM close event
|
||||
type IMCloseEvent ChannelInfoEvent
|
||||
|
||||
// IMMarkedEvent represents the IM marked event
|
||||
type IMMarkedEvent ChannelInfoEvent
|
||||
|
||||
// IMMarkedHistoryChanged represents the IM marked history changed event
|
||||
type IMMarkedHistoryChanged ChannelInfoEvent
|
8
vendor/github.com/slack-go/slack/websocket_dnd.go
generated
vendored
Normal file
8
vendor/github.com/slack-go/slack/websocket_dnd.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package slack
|
||||
|
||||
// DNDUpdatedEvent represents the update event for Do Not Disturb
|
||||
type DNDUpdatedEvent struct {
|
||||
Type string `json:"type"`
|
||||
User string `json:"user"`
|
||||
Status DNDStatus `json:"dnd_status"`
|
||||
}
|
49
vendor/github.com/slack-go/slack/websocket_files.go
generated
vendored
Normal file
49
vendor/github.com/slack-go/slack/websocket_files.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
package slack
|
||||
|
||||
// FileActionEvent represents the File action event
|
||||
type fileActionEvent struct {
|
||||
Type string `json:"type"`
|
||||
EventTimestamp string `json:"event_ts"`
|
||||
File File `json:"file"`
|
||||
// FileID is used for FileDeletedEvent
|
||||
FileID string `json:"file_id,omitempty"`
|
||||
}
|
||||
|
||||
// FileCreatedEvent represents the File created event
|
||||
type FileCreatedEvent fileActionEvent
|
||||
|
||||
// FileSharedEvent represents the File shared event
|
||||
type FileSharedEvent fileActionEvent
|
||||
|
||||
// FilePublicEvent represents the File public event
|
||||
type FilePublicEvent fileActionEvent
|
||||
|
||||
// FileUnsharedEvent represents the File unshared event
|
||||
type FileUnsharedEvent fileActionEvent
|
||||
|
||||
// FileChangeEvent represents the File change event
|
||||
type FileChangeEvent fileActionEvent
|
||||
|
||||
// FileDeletedEvent represents the File deleted event
|
||||
type FileDeletedEvent fileActionEvent
|
||||
|
||||
// FilePrivateEvent represents the File private event
|
||||
type FilePrivateEvent fileActionEvent
|
||||
|
||||
// FileCommentAddedEvent represents the File comment added event
|
||||
type FileCommentAddedEvent struct {
|
||||
fileActionEvent
|
||||
Comment Comment `json:"comment"`
|
||||
}
|
||||
|
||||
// FileCommentEditedEvent represents the File comment edited event
|
||||
type FileCommentEditedEvent struct {
|
||||
fileActionEvent
|
||||
Comment Comment `json:"comment"`
|
||||
}
|
||||
|
||||
// FileCommentDeletedEvent represents the File comment deleted event
|
||||
type FileCommentDeletedEvent struct {
|
||||
fileActionEvent
|
||||
Comment string `json:"comment"`
|
||||
}
|
49
vendor/github.com/slack-go/slack/websocket_groups.go
generated
vendored
Normal file
49
vendor/github.com/slack-go/slack/websocket_groups.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
package slack
|
||||
|
||||
// GroupCreatedEvent represents the Group created event
|
||||
type GroupCreatedEvent struct {
|
||||
Type string `json:"type"`
|
||||
User string `json:"user"`
|
||||
Channel ChannelCreatedInfo `json:"channel"`
|
||||
}
|
||||
|
||||
// XXX: Should we really do this? event.Group is probably nicer than event.Channel
|
||||
// even though the api returns "channel"
|
||||
|
||||
// GroupMarkedEvent represents the Group marked event
|
||||
type GroupMarkedEvent ChannelInfoEvent
|
||||
|
||||
// GroupOpenEvent represents the Group open event
|
||||
type GroupOpenEvent ChannelInfoEvent
|
||||
|
||||
// GroupCloseEvent represents the Group close event
|
||||
type GroupCloseEvent ChannelInfoEvent
|
||||
|
||||
// GroupArchiveEvent represents the Group archive event
|
||||
type GroupArchiveEvent ChannelInfoEvent
|
||||
|
||||
// GroupUnarchiveEvent represents the Group unarchive event
|
||||
type GroupUnarchiveEvent ChannelInfoEvent
|
||||
|
||||
// GroupLeftEvent represents the Group left event
|
||||
type GroupLeftEvent ChannelInfoEvent
|
||||
|
||||
// GroupJoinedEvent represents the Group joined event
|
||||
type GroupJoinedEvent ChannelJoinedEvent
|
||||
|
||||
// GroupRenameEvent represents the Group rename event
|
||||
type GroupRenameEvent struct {
|
||||
Type string `json:"type"`
|
||||
Group GroupRenameInfo `json:"channel"`
|
||||
Timestamp string `json:"ts"`
|
||||
}
|
||||
|
||||
// GroupRenameInfo represents the group info related to the renamed group
|
||||
type GroupRenameInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Created string `json:"created"`
|
||||
}
|
||||
|
||||
// GroupHistoryChangedEvent represents the Group history changed event
|
||||
type GroupHistoryChangedEvent ChannelHistoryChangedEvent
|
101
vendor/github.com/slack-go/slack/websocket_internals.go
generated
vendored
Normal file
101
vendor/github.com/slack-go/slack/websocket_internals.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
/**
|
||||
* Internal events, created by this lib and not mapped to Slack APIs.
|
||||
*/
|
||||
|
||||
// ConnectedEvent is used for when we connect to Slack
|
||||
type ConnectedEvent struct {
|
||||
ConnectionCount int // 1 = first time, 2 = second time
|
||||
Info *Info
|
||||
}
|
||||
|
||||
// ConnectionErrorEvent contains information about a connection error
|
||||
type ConnectionErrorEvent struct {
|
||||
Attempt int
|
||||
Backoff time.Duration // how long we'll wait before the next attempt
|
||||
ErrorObj error
|
||||
}
|
||||
|
||||
func (c *ConnectionErrorEvent) Error() string {
|
||||
return c.ErrorObj.Error()
|
||||
}
|
||||
|
||||
// ConnectingEvent contains information about our connection attempt
|
||||
type ConnectingEvent struct {
|
||||
Attempt int // 1 = first attempt, 2 = second attempt
|
||||
ConnectionCount int
|
||||
}
|
||||
|
||||
// DisconnectedEvent contains information about how we disconnected
|
||||
type DisconnectedEvent struct {
|
||||
Intentional bool
|
||||
Cause error
|
||||
}
|
||||
|
||||
// LatencyReport contains information about connection latency
|
||||
type LatencyReport struct {
|
||||
Value time.Duration
|
||||
}
|
||||
|
||||
// InvalidAuthEvent is used in case we can't even authenticate with the API
|
||||
type InvalidAuthEvent struct{}
|
||||
|
||||
// UnmarshallingErrorEvent is used when there are issues deconstructing a response
|
||||
type UnmarshallingErrorEvent struct {
|
||||
ErrorObj error
|
||||
}
|
||||
|
||||
func (u UnmarshallingErrorEvent) Error() string {
|
||||
return u.ErrorObj.Error()
|
||||
}
|
||||
|
||||
// MessageTooLongEvent is used when sending a message that is too long
|
||||
type MessageTooLongEvent struct {
|
||||
Message OutgoingMessage
|
||||
MaxLength int
|
||||
}
|
||||
|
||||
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
|
||||
ErrorObj error
|
||||
}
|
||||
|
||||
func (o OutgoingErrorEvent) Error() string {
|
||||
return o.ErrorObj.Error()
|
||||
}
|
||||
|
||||
// IncomingEventError contains information about an unexpected error receiving a websocket event
|
||||
type IncomingEventError struct {
|
||||
ErrorObj error
|
||||
}
|
||||
|
||||
func (i *IncomingEventError) Error() string {
|
||||
return i.ErrorObj.Error()
|
||||
}
|
||||
|
||||
// AckErrorEvent i
|
||||
type AckErrorEvent struct {
|
||||
ErrorObj error
|
||||
}
|
||||
|
||||
func (a *AckErrorEvent) Error() string {
|
||||
return a.ErrorObj.Error()
|
||||
}
|
581
vendor/github.com/slack-go/slack/websocket_managed_conn.go
generated
vendored
Normal file
581
vendor/github.com/slack-go/slack/websocket_managed_conn.go
generated
vendored
Normal file
@ -0,0 +1,581 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
stdurl "net/url"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/slack-go/slack/internal/errorsx"
|
||||
"github.com/slack-go/slack/internal/timex"
|
||||
)
|
||||
|
||||
// ManageConnection can be called on a Slack RTM instance returned by the
|
||||
// NewRTM method. It will connect to the slack RTM API and handle all incoming
|
||||
// and outgoing events. If a connection fails then it will attempt to reconnect
|
||||
// and will notify any listeners through an error event on the IncomingEvents
|
||||
// channel.
|
||||
//
|
||||
// If the connection ends and the disconnect was unintentional then this will
|
||||
// attempt to reconnect.
|
||||
//
|
||||
// This should only be called once per slack API! Otherwise expect undefined
|
||||
// behavior.
|
||||
//
|
||||
// The defined error events are located in websocket_internals.go.
|
||||
func (rtm *RTM) ManageConnection() {
|
||||
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
|
||||
if info, conn, err = rtm.connect(connectionCount, rtm.useRTMStart); err != nil {
|
||||
// when the connection is unsuccessful its fatal, and we need to bail out.
|
||||
rtm.Debugf("Failed to connect with RTM on try %d: %s", connectionCount, err)
|
||||
rtm.disconnect()
|
||||
return
|
||||
}
|
||||
|
||||
// lock to prevent data races with Disconnect particularly around isConnected
|
||||
// and conn.
|
||||
rtm.mu.Lock()
|
||||
rtm.conn = conn
|
||||
rtm.info = info
|
||||
rtm.mu.Unlock()
|
||||
|
||||
rtm.IncomingEvents <- RTMEvent{"connected", &ConnectedEvent{
|
||||
ConnectionCount: connectionCount,
|
||||
Info: info,
|
||||
}}
|
||||
|
||||
rtm.Debugf("RTM connection succeeded on try %d", connectionCount)
|
||||
|
||||
rawEvents := make(chan json.RawMessage)
|
||||
// we're now connected so we can set up listeners
|
||||
go rtm.handleIncomingEvents(rawEvents)
|
||||
// this should be a blocking call until the connection has ended
|
||||
rtm.handleEvents(rawEvents)
|
||||
|
||||
select {
|
||||
case <-rtm.disconnected:
|
||||
// after handle events returns we need to check if we're disconnected
|
||||
// when this happens we need to cleanup the newly created connection.
|
||||
if err = conn.Close(); err != nil {
|
||||
rtm.Debugln("failed to close conn on disconnected RTM", err)
|
||||
}
|
||||
return
|
||||
default:
|
||||
// otherwise continue and run the loop again to reconnect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// connect attempts to connect to the slack websocket API. It handles any
|
||||
// errors that occur while connecting and will return once a connection
|
||||
// has been successfully opened.
|
||||
// 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{
|
||||
Max: 5 * time.Minute,
|
||||
}
|
||||
|
||||
for {
|
||||
var (
|
||||
backoff time.Duration
|
||||
)
|
||||
|
||||
// send connecting event
|
||||
rtm.IncomingEvents <- RTMEvent{"connecting", &ConnectingEvent{
|
||||
Attempt: boff.attempts + 1,
|
||||
ConnectionCount: connectionCount,
|
||||
}}
|
||||
|
||||
// attempt to start the connection
|
||||
info, conn, err := rtm.startRTMAndDial(useRTMStart)
|
||||
if err == nil {
|
||||
return info, conn, nil
|
||||
}
|
||||
|
||||
// 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, err
|
||||
default:
|
||||
}
|
||||
|
||||
switch actual := err.(type) {
|
||||
case statusCodeError:
|
||||
if actual.Code == http.StatusNotFound {
|
||||
rtm.Debugf("invalid auth when connecting with RTM: %s", err)
|
||||
rtm.IncomingEvents <- RTMEvent{"invalid_auth", &InvalidAuthEvent{}}
|
||||
return nil, nil, err
|
||||
}
|
||||
case *RateLimitedError:
|
||||
backoff = actual.RetryAfter
|
||||
default:
|
||||
}
|
||||
|
||||
backoff = timex.Max(backoff, boff.Duration())
|
||||
// any other errors are treated as recoverable and we try again after
|
||||
// sending the event along the IncomingEvents channel
|
||||
rtm.IncomingEvents <- RTMEvent{"connection_error", &ConnectionErrorEvent{
|
||||
Attempt: boff.attempts,
|
||||
Backoff: backoff,
|
||||
ErrorObj: err,
|
||||
}}
|
||||
|
||||
// get time we should wait before attempting to connect again
|
||||
rtm.Debugf("reconnection %d failed: %s reconnecting in %v\n", boff.attempts, err, backoff)
|
||||
|
||||
// wait for one of the following to occur,
|
||||
// backoff duration has elapsed, killChannel is signalled, or
|
||||
// the rtm finishes disconnecting.
|
||||
select {
|
||||
case <-time.After(backoff): // retry after the backoff.
|
||||
case intentional := <-rtm.killChannel:
|
||||
if intentional {
|
||||
rtm.killConnection(intentional, ErrRTMDisconnected)
|
||||
return nil, nil, ErrRTMDisconnected
|
||||
}
|
||||
case <-rtm.disconnected:
|
||||
return nil, nil, ErrRTMDisconnected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 *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
|
||||
}
|
||||
|
||||
// install connection parameters
|
||||
u, err := stdurl.Parse(url)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u.RawQuery = rtm.connParams.Encode()
|
||||
url = u.String()
|
||||
|
||||
rtm.Debugf("Dialing to websocket on url %s", url)
|
||||
// Only use HTTPS for connections to prevent MITM attacks on the connection.
|
||||
upgradeHeader := http.Header{}
|
||||
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
|
||||
}
|
||||
|
||||
// killConnection stops the websocket connection and signals to all goroutines
|
||||
// that they should cease listening to the connection for events.
|
||||
//
|
||||
// This should not be called directly! Instead a boolean value (true for
|
||||
// intentional, false otherwise) should be sent to the killChannel on the RTM.
|
||||
func (rtm *RTM) killConnection(intentional bool, cause error) (err error) {
|
||||
rtm.Debugln("killing connection", cause)
|
||||
|
||||
if rtm.conn != nil {
|
||||
err = rtm.conn.Close()
|
||||
}
|
||||
|
||||
rtm.IncomingEvents <- RTMEvent{"disconnected", &DisconnectedEvent{Intentional: intentional, Cause: cause}}
|
||||
|
||||
if intentional {
|
||||
rtm.disconnect()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// handleEvents is a blocking function that handles all events. This sends
|
||||
// pings when asked to (on rtm.forcePing) and upon every given elapsed
|
||||
// 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(events chan json.RawMessage) {
|
||||
ticker := time.NewTicker(rtm.pingInterval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
// catch "stop" signal on channel close
|
||||
case intentional := <-rtm.killChannel:
|
||||
_ = rtm.killConnection(intentional, errorsx.String("signaled"))
|
||||
return
|
||||
// detect when the connection is dead.
|
||||
case <-rtm.pingDeadman.C:
|
||||
_ = rtm.killConnection(false, ErrRTMDeadman)
|
||||
return
|
||||
// send pings on ticker interval
|
||||
case <-ticker.C:
|
||||
if err := rtm.ping(); err != nil {
|
||||
_ = rtm.killConnection(false, err)
|
||||
return
|
||||
}
|
||||
case <-rtm.forcePing:
|
||||
if err := rtm.ping(); err != nil {
|
||||
_ = rtm.killConnection(false, err)
|
||||
return
|
||||
}
|
||||
// listen for messages that need to be sent
|
||||
case msg := <-rtm.outgoingMessages:
|
||||
rtm.sendOutgoingMessage(msg)
|
||||
// listen for incoming messages that need to be parsed
|
||||
case rawEvent := <-events:
|
||||
switch rtm.handleRawEvent(rawEvent) {
|
||||
case rtmEventTypeGoodbye:
|
||||
// kill the connection, but DO NOT RETURN, a follow up kill signal will
|
||||
// be sent that still needs to be processed. this duplication is because
|
||||
// the event reader restarts once it emits the goodbye event.
|
||||
// unlike the other cases in this function a final read will be triggered
|
||||
// against the connection which will emit a kill signal. if we return early
|
||||
// this kill signal will be processed by the next connection.
|
||||
_ = rtm.killConnection(false, ErrRTMGoodbye)
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleIncomingEvents monitors the RTM's opened websocket for any incoming
|
||||
// events. It pushes the raw events into the channel.
|
||||
//
|
||||
// This will stop executing once the RTM's when a fatal error is detected, or
|
||||
// a disconnect occurs.
|
||||
func (rtm *RTM) handleIncomingEvents(events chan json.RawMessage) {
|
||||
for {
|
||||
if err := rtm.receiveIncomingEvent(events); err != nil {
|
||||
select {
|
||||
case rtm.killChannel <- false:
|
||||
case <-rtm.disconnected:
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rtm *RTM) sendWithDeadline(msg interface{}) error {
|
||||
// set a write deadline on the connection
|
||||
if err := rtm.conn.SetWriteDeadline(time.Now().Add(10 * time.Second)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := rtm.conn.WriteJSON(msg); err != nil {
|
||||
return err
|
||||
}
|
||||
// remove write deadline
|
||||
return rtm.conn.SetWriteDeadline(time.Time{})
|
||||
}
|
||||
|
||||
// sendOutgoingMessage sends the given OutgoingMessage to the slack websocket.
|
||||
//
|
||||
// It does not currently detect if a outgoing message fails due to a disconnect
|
||||
// and instead lets a future failed 'PING' detect the failed connection.
|
||||
func (rtm *RTM) sendOutgoingMessage(msg OutgoingMessage) {
|
||||
rtm.Debugln("Sending message:", msg)
|
||||
if len([]rune(msg.Text)) > MaxMessageTextLength {
|
||||
rtm.IncomingEvents <- RTMEvent{"outgoing_error", &MessageTooLongEvent{
|
||||
Message: msg,
|
||||
MaxLength: MaxMessageTextLength,
|
||||
}}
|
||||
return
|
||||
}
|
||||
|
||||
if err := rtm.sendWithDeadline(msg); err != nil {
|
||||
rtm.IncomingEvents <- RTMEvent{"outgoing_error", &OutgoingErrorEvent{
|
||||
Message: msg,
|
||||
ErrorObj: err,
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
// ping sends a 'PING' message to the RTM's websocket. If the 'PING' message
|
||||
// fails to send then this returns an error signifying that the connection
|
||||
// should be considered disconnected.
|
||||
//
|
||||
// This does not handle incoming 'PONG' responses but does store the time of
|
||||
// each successful 'PING' send so latency can be detected upon a 'PONG'
|
||||
// response.
|
||||
func (rtm *RTM) ping() error {
|
||||
id := rtm.idGen.Next()
|
||||
rtm.Debugln("Sending PING ", id)
|
||||
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())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// receiveIncomingEvent attempts to receive an event from the RTM's websocket.
|
||||
// This will block until a frame is available from the websocket.
|
||||
// If the read from the websocket results in a fatal error, this function will return non-nil.
|
||||
func (rtm *RTM) receiveIncomingEvent(events chan json.RawMessage) error {
|
||||
event := json.RawMessage{}
|
||||
err := rtm.conn.ReadJSON(&event)
|
||||
|
||||
// check if the connection was closed.
|
||||
if websocket.IsUnexpectedCloseError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
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 potential websocket disconnect
|
||||
select {
|
||||
case rtm.forcePing <- true:
|
||||
case <-rtm.disconnected:
|
||||
}
|
||||
case err != nil:
|
||||
// All other errors from ReadJSON come from NextReader, and should
|
||||
// kill the read loop and force a reconnect.
|
||||
rtm.IncomingEvents <- RTMEvent{"incoming_error", &IncomingEventError{
|
||||
ErrorObj: err,
|
||||
}}
|
||||
|
||||
return err
|
||||
case len(event) == 0:
|
||||
rtm.Debugln("Received empty event")
|
||||
default:
|
||||
rtm.Debugln("Incoming Event:", string(event))
|
||||
select {
|
||||
case events <- event:
|
||||
case <-rtm.disconnected:
|
||||
rtm.Debugln("disonnected while attempting to send raw event")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleRawEvent takes a raw JSON message received from the slack websocket
|
||||
// and handles the encoded event.
|
||||
// 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 ""
|
||||
}
|
||||
|
||||
switch event.Type {
|
||||
case rtmEventTypeAck:
|
||||
rtm.handleAck(rawEvent)
|
||||
case rtmEventTypeHello:
|
||||
rtm.IncomingEvents <- RTMEvent{"hello", &HelloEvent{}}
|
||||
case rtmEventTypePong:
|
||||
rtm.handlePong(rawEvent)
|
||||
case rtmEventTypeGoodbye:
|
||||
// just return the event type up for goodbye, will be handled by caller.
|
||||
default:
|
||||
rtm.handleEvent(event.Type, rawEvent)
|
||||
}
|
||||
|
||||
return event.Type
|
||||
}
|
||||
|
||||
// handleAck handles an incoming 'ACK' message.
|
||||
func (rtm *RTM) handleAck(event json.RawMessage) {
|
||||
ack := &AckMessage{}
|
||||
if err := json.Unmarshal(event, ack); err != nil {
|
||||
rtm.Debugln("RTM Error unmarshalling 'ack' event:", err)
|
||||
rtm.Debugln(" -> Erroneous 'ack' event:", string(event))
|
||||
return
|
||||
}
|
||||
|
||||
if ack.Ok {
|
||||
rtm.IncomingEvents <- RTMEvent{"ack", ack}
|
||||
} else if ack.RTMResponse.Error != nil {
|
||||
// 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")}}
|
||||
}
|
||||
}
|
||||
|
||||
// handlePong handles an incoming 'PONG' message which should be in response to
|
||||
// a previously sent 'PING' message. This is then used to compute the
|
||||
// connection's latency.
|
||||
func (rtm *RTM) handlePong(event json.RawMessage) {
|
||||
var (
|
||||
p Pong
|
||||
)
|
||||
|
||||
rtm.resetDeadman()
|
||||
|
||||
if err := json.Unmarshal(event, &p); err != nil {
|
||||
rtm.Client.log.Println("RTM Error unmarshalling 'pong' event:", err)
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
// special case. It matches the command's name to a mapping of defined events
|
||||
// and then sends the corresponding event struct to the IncomingEvents channel.
|
||||
// If the event type is not found or the event cannot be unmarshalled into the
|
||||
// correct struct then this sends an UnmarshallingErrorEvent to the
|
||||
// IncomingEvents channel.
|
||||
func (rtm *RTM) handleEvent(typeStr string, event json.RawMessage) {
|
||||
v, exists := EventMapping[typeStr]
|
||||
if !exists {
|
||||
rtm.Debugf("RTM Error - received unmapped event %q: %s\n", typeStr, string(event))
|
||||
err := fmt.Errorf("RTM Error: Received unmapped event %q: %s", typeStr, string(event))
|
||||
rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}}
|
||||
return
|
||||
}
|
||||
t := reflect.TypeOf(v)
|
||||
recvEvent := reflect.New(t).Interface()
|
||||
err := json.Unmarshal(event, recvEvent)
|
||||
if err != nil {
|
||||
rtm.Debugf("RTM Error, could not unmarshall event %q: %s\n", typeStr, string(event))
|
||||
err := fmt.Errorf("RTM Error: Could not unmarshall event %q: %s", typeStr, string(event))
|
||||
rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}}
|
||||
return
|
||||
}
|
||||
rtm.IncomingEvents <- RTMEvent{typeStr, recvEvent}
|
||||
}
|
||||
|
||||
// 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{}{
|
||||
"message": MessageEvent{},
|
||||
"presence_change": PresenceChangeEvent{},
|
||||
"user_typing": UserTypingEvent{},
|
||||
|
||||
"channel_marked": ChannelMarkedEvent{},
|
||||
"channel_created": ChannelCreatedEvent{},
|
||||
"channel_joined": ChannelJoinedEvent{},
|
||||
"channel_left": ChannelLeftEvent{},
|
||||
"channel_deleted": ChannelDeletedEvent{},
|
||||
"channel_rename": ChannelRenameEvent{},
|
||||
"channel_archive": ChannelArchiveEvent{},
|
||||
"channel_unarchive": ChannelUnarchiveEvent{},
|
||||
"channel_history_changed": ChannelHistoryChangedEvent{},
|
||||
|
||||
"dnd_updated": DNDUpdatedEvent{},
|
||||
"dnd_updated_user": DNDUpdatedEvent{},
|
||||
|
||||
"im_created": IMCreatedEvent{},
|
||||
"im_open": IMOpenEvent{},
|
||||
"im_close": IMCloseEvent{},
|
||||
"im_marked": IMMarkedEvent{},
|
||||
"im_history_changed": IMHistoryChangedEvent{},
|
||||
|
||||
"group_marked": GroupMarkedEvent{},
|
||||
"group_open": GroupOpenEvent{},
|
||||
"group_joined": GroupJoinedEvent{},
|
||||
"group_left": GroupLeftEvent{},
|
||||
"group_close": GroupCloseEvent{},
|
||||
"group_rename": GroupRenameEvent{},
|
||||
"group_archive": GroupArchiveEvent{},
|
||||
"group_unarchive": GroupUnarchiveEvent{},
|
||||
"group_history_changed": GroupHistoryChangedEvent{},
|
||||
|
||||
"file_created": FileCreatedEvent{},
|
||||
"file_shared": FileSharedEvent{},
|
||||
"file_unshared": FileUnsharedEvent{},
|
||||
"file_public": FilePublicEvent{},
|
||||
"file_private": FilePrivateEvent{},
|
||||
"file_change": FileChangeEvent{},
|
||||
"file_deleted": FileDeletedEvent{},
|
||||
"file_comment_added": FileCommentAddedEvent{},
|
||||
"file_comment_edited": FileCommentEditedEvent{},
|
||||
"file_comment_deleted": FileCommentDeletedEvent{},
|
||||
|
||||
"pin_added": PinAddedEvent{},
|
||||
"pin_removed": PinRemovedEvent{},
|
||||
|
||||
"star_added": StarAddedEvent{},
|
||||
"star_removed": StarRemovedEvent{},
|
||||
|
||||
"reaction_added": ReactionAddedEvent{},
|
||||
"reaction_removed": ReactionRemovedEvent{},
|
||||
|
||||
"pref_change": PrefChangeEvent{},
|
||||
|
||||
"team_join": TeamJoinEvent{},
|
||||
"team_rename": TeamRenameEvent{},
|
||||
"team_pref_change": TeamPrefChangeEvent{},
|
||||
"team_domain_change": TeamDomainChangeEvent{},
|
||||
"team_migration_started": TeamMigrationStartedEvent{},
|
||||
|
||||
"manual_presence_change": ManualPresenceChangeEvent{},
|
||||
|
||||
"user_change": UserChangeEvent{},
|
||||
|
||||
"emoji_changed": EmojiChangedEvent{},
|
||||
|
||||
"commands_changed": CommandsChangedEvent{},
|
||||
|
||||
"email_domain_changed": EmailDomainChangedEvent{},
|
||||
|
||||
"bot_added": BotAddedEvent{},
|
||||
"bot_changed": BotChangedEvent{},
|
||||
|
||||
"accounts_changed": AccountsChangedEvent{},
|
||||
|
||||
"reconnect_url": ReconnectUrlEvent{},
|
||||
|
||||
"member_joined_channel": MemberJoinedChannelEvent{},
|
||||
"member_left_channel": MemberLeftChannelEvent{},
|
||||
|
||||
"subteam_created": SubteamCreatedEvent{},
|
||||
"subteam_self_added": SubteamSelfAddedEvent{},
|
||||
"subteam_self_removed": SubteamSelfRemovedEvent{},
|
||||
"subteam_updated": SubteamUpdatedEvent{},
|
||||
|
||||
"desktop_notification": DesktopNotificationEvent{},
|
||||
}
|
141
vendor/github.com/slack-go/slack/websocket_misc.go
generated
vendored
Normal file
141
vendor/github.com/slack-go/slack/websocket_misc.go
generated
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AckMessage is used for messages received in reply to other messages
|
||||
type AckMessage struct {
|
||||
ReplyTo int `json:"reply_to"`
|
||||
Timestamp string `json:"ts"`
|
||||
Text string `json:"text"`
|
||||
RTMResponse
|
||||
}
|
||||
|
||||
// RTMResponse encapsulates response details as returned by the Slack API
|
||||
type RTMResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Error *RTMError `json:"error"`
|
||||
}
|
||||
|
||||
// RTMError encapsulates error information as returned by the Slack API
|
||||
type RTMError struct {
|
||||
Code int
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (s RTMError) Error() string {
|
||||
return fmt.Sprintf("Code %d - %s", s.Code, s.Msg)
|
||||
}
|
||||
|
||||
// MessageEvent represents a Slack Message (used as the event type for an incoming message)
|
||||
type MessageEvent Message
|
||||
|
||||
// RTMEvent is the main wrapper. You will find all the other messages attached
|
||||
type RTMEvent struct {
|
||||
Type string
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
// HelloEvent represents the hello event
|
||||
type HelloEvent struct{}
|
||||
|
||||
// PresenceChangeEvent represents the presence change event
|
||||
type PresenceChangeEvent struct {
|
||||
Type string `json:"type"`
|
||||
Presence string `json:"presence"`
|
||||
User string `json:"user"`
|
||||
Users []string `json:"users"`
|
||||
}
|
||||
|
||||
// UserTypingEvent represents the user typing event
|
||||
type UserTypingEvent struct {
|
||||
Type string `json:"type"`
|
||||
User string `json:"user"`
|
||||
Channel string `json:"channel"`
|
||||
}
|
||||
|
||||
// PrefChangeEvent represents a user preferences change event
|
||||
type PrefChangeEvent struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Value json.RawMessage `json:"value"`
|
||||
}
|
||||
|
||||
// ManualPresenceChangeEvent represents the manual presence change event
|
||||
type ManualPresenceChangeEvent struct {
|
||||
Type string `json:"type"`
|
||||
Presence string `json:"presence"`
|
||||
}
|
||||
|
||||
// UserChangeEvent represents the user change event
|
||||
type UserChangeEvent struct {
|
||||
Type string `json:"type"`
|
||||
User User `json:"user"`
|
||||
}
|
||||
|
||||
// EmojiChangedEvent represents the emoji changed event
|
||||
type EmojiChangedEvent struct {
|
||||
Type string `json:"type"`
|
||||
SubType string `json:"subtype"`
|
||||
Name string `json:"name"`
|
||||
Names []string `json:"names"`
|
||||
Value string `json:"value"`
|
||||
EventTimestamp string `json:"event_ts"`
|
||||
}
|
||||
|
||||
// CommandsChangedEvent represents the commands changed event
|
||||
type CommandsChangedEvent struct {
|
||||
Type string `json:"type"`
|
||||
EventTimestamp string `json:"event_ts"`
|
||||
}
|
||||
|
||||
// EmailDomainChangedEvent represents the email domain changed event
|
||||
type EmailDomainChangedEvent struct {
|
||||
Type string `json:"type"`
|
||||
EventTimestamp string `json:"event_ts"`
|
||||
EmailDomain string `json:"email_domain"`
|
||||
}
|
||||
|
||||
// BotAddedEvent represents the bot added event
|
||||
type BotAddedEvent struct {
|
||||
Type string `json:"type"`
|
||||
Bot Bot `json:"bot"`
|
||||
}
|
||||
|
||||
// BotChangedEvent represents the bot changed event
|
||||
type BotChangedEvent struct {
|
||||
Type string `json:"type"`
|
||||
Bot Bot `json:"bot"`
|
||||
}
|
||||
|
||||
// AccountsChangedEvent represents the accounts changed event
|
||||
type AccountsChangedEvent struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// ReconnectUrlEvent represents the receiving reconnect url event
|
||||
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"`
|
||||
}
|
||||
|
||||
// MemberLeftChannelEvent 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"`
|
||||
}
|
16
vendor/github.com/slack-go/slack/websocket_pins.go
generated
vendored
Normal file
16
vendor/github.com/slack-go/slack/websocket_pins.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
package slack
|
||||
|
||||
type pinEvent struct {
|
||||
Type string `json:"type"`
|
||||
User string `json:"user"`
|
||||
Item Item `json:"item"`
|
||||
Channel string `json:"channel_id"`
|
||||
EventTimestamp string `json:"event_ts"`
|
||||
HasPins bool `json:"has_pins,omitempty"`
|
||||
}
|
||||
|
||||
// PinAddedEvent represents the Pin added event
|
||||
type PinAddedEvent pinEvent
|
||||
|
||||
// PinRemovedEvent represents the Pin removed event
|
||||
type PinRemovedEvent pinEvent
|
25
vendor/github.com/slack-go/slack/websocket_reactions.go
generated
vendored
Normal file
25
vendor/github.com/slack-go/slack/websocket_reactions.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package slack
|
||||
|
||||
// reactionItem is a lighter-weight item than is returned by the reactions list.
|
||||
type reactionItem struct {
|
||||
Type string `json:"type"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
File string `json:"file,omitempty"`
|
||||
FileComment string `json:"file_comment,omitempty"`
|
||||
Timestamp string `json:"ts,omitempty"`
|
||||
}
|
||||
|
||||
type reactionEvent struct {
|
||||
Type string `json:"type"`
|
||||
User string `json:"user"`
|
||||
ItemUser string `json:"item_user"`
|
||||
Item reactionItem `json:"item"`
|
||||
Reaction string `json:"reaction"`
|
||||
EventTimestamp string `json:"event_ts"`
|
||||
}
|
||||
|
||||
// ReactionAddedEvent represents the Reaction added event
|
||||
type ReactionAddedEvent reactionEvent
|
||||
|
||||
// ReactionRemovedEvent represents the Reaction removed event
|
||||
type ReactionRemovedEvent reactionEvent
|
14
vendor/github.com/slack-go/slack/websocket_stars.go
generated
vendored
Normal file
14
vendor/github.com/slack-go/slack/websocket_stars.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package slack
|
||||
|
||||
type starEvent struct {
|
||||
Type string `json:"type"`
|
||||
User string `json:"user"`
|
||||
Item StarredItem `json:"item"`
|
||||
EventTimestamp string `json:"event_ts"`
|
||||
}
|
||||
|
||||
// StarAddedEvent represents the Star added event
|
||||
type StarAddedEvent starEvent
|
||||
|
||||
// StarRemovedEvent represents the Star removed event
|
||||
type StarRemovedEvent starEvent
|
35
vendor/github.com/slack-go/slack/websocket_subteam.go
generated
vendored
Normal file
35
vendor/github.com/slack-go/slack/websocket_subteam.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
package slack
|
||||
|
||||
// SubteamCreatedEvent represents the Subteam created event
|
||||
type SubteamCreatedEvent struct {
|
||||
Type string `json:"type"`
|
||||
Subteam UserGroup `json:"subteam"`
|
||||
}
|
||||
|
||||
// SubteamCreatedEvent represents the membership of an existing User Group has changed event
|
||||
type SubteamMembersChangedEvent struct {
|
||||
Type string `json:"type"`
|
||||
SubteamID string `json:"subteam_id"`
|
||||
TeamID string `json:"team_id"`
|
||||
DatePreviousUpdate JSONTime `json:"date_previous_update"`
|
||||
DateUpdate JSONTime `json:"date_update"`
|
||||
AddedUsers []string `json:"added_users"`
|
||||
AddedUsersCount string `json:"added_users_count"`
|
||||
RemovedUsers []string `json:"removed_users"`
|
||||
RemovedUsersCount string `json:"removed_users_count"`
|
||||
}
|
||||
|
||||
// SubteamSelfAddedEvent represents an event of you have been added to a User Group
|
||||
type SubteamSelfAddedEvent struct {
|
||||
Type string `json:"type"`
|
||||
SubteamID string `json:"subteam_id"`
|
||||
}
|
||||
|
||||
// SubteamSelfRemovedEvent represents an event of you have been removed from a User Group
|
||||
type SubteamSelfRemovedEvent SubteamSelfAddedEvent
|
||||
|
||||
// SubteamUpdatedEvent represents an event of an existing User Group has been updated or its members changed
|
||||
type SubteamUpdatedEvent struct {
|
||||
Type string `json:"type"`
|
||||
Subteam UserGroup `json:"subteam"`
|
||||
}
|
33
vendor/github.com/slack-go/slack/websocket_teams.go
generated
vendored
Normal file
33
vendor/github.com/slack-go/slack/websocket_teams.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package slack
|
||||
|
||||
// TeamJoinEvent represents the Team join event
|
||||
type TeamJoinEvent struct {
|
||||
Type string `json:"type"`
|
||||
User User `json:"user"`
|
||||
}
|
||||
|
||||
// TeamRenameEvent represents the Team rename event
|
||||
type TeamRenameEvent struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name,omitempty"`
|
||||
EventTimestamp string `json:"event_ts,omitempty"`
|
||||
}
|
||||
|
||||
// TeamPrefChangeEvent represents the Team preference change event
|
||||
type TeamPrefChangeEvent struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Value []string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// TeamDomainChangeEvent represents the Team domain change event
|
||||
type TeamDomainChangeEvent struct {
|
||||
Type string `json:"type"`
|
||||
URL string `json:"url"`
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
// TeamMigrationStartedEvent represents the Team migration started event
|
||||
type TeamMigrationStartedEvent struct {
|
||||
Type string `json:"type"`
|
||||
}
|
Reference in New Issue
Block a user