Update of nlopes/slack dependency (#511)
This commit is contained in:
parent
69646a160d
commit
917040b044
4
go.mod
4
go.mod
|
@ -27,6 +27,8 @@ require (
|
|||
github.com/labstack/echo v0.0.0-20180219162101-7eec915044a1
|
||||
github.com/labstack/gommon v0.2.1 // indirect
|
||||
github.com/lrstanley/girc v0.0.0-20180913221000-0fb5b684054e
|
||||
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // indirect
|
||||
github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 // indirect
|
||||
github.com/magiconair/properties v0.0.0-20180217134545-2c9e95027885 // indirect
|
||||
github.com/matterbridge/discordgo v0.0.0-20180806170629-ef40ff5ba64f
|
||||
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91
|
||||
|
@ -41,7 +43,7 @@ require (
|
|||
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect
|
||||
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect
|
||||
github.com/nicksnyder/go-i18n v1.4.0 // indirect
|
||||
github.com/nlopes/slack v0.3.1-0.20180805133408-21749ab136a8
|
||||
github.com/nlopes/slack v0.4.0
|
||||
github.com/onsi/ginkgo v1.6.0 // indirect
|
||||
github.com/onsi/gomega v1.4.1 // indirect
|
||||
github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83
|
||||
|
|
12
go.sum
12
go.sum
|
@ -51,10 +51,12 @@ github.com/labstack/echo v0.0.0-20180219162101-7eec915044a1 h1:cOIt0LZKdfeirAfTP
|
|||
github.com/labstack/echo v0.0.0-20180219162101-7eec915044a1/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
|
||||
github.com/labstack/gommon v0.2.1 h1:C+I4NYknueQncqKYZQ34kHsLZJVeB5KwPUhnO0nmbpU=
|
||||
github.com/labstack/gommon v0.2.1/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
|
||||
github.com/lrstanley/girc v0.0.0-20180427160007-102f17f86306 h1:IqN61cmi7LM/IaYaP9a/KXFtHRS2a3+WHu8GhAXJT7c=
|
||||
github.com/lrstanley/girc v0.0.0-20180427160007-102f17f86306/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk=
|
||||
github.com/lrstanley/girc v0.0.0-20180913221000-0fb5b684054e h1:RpktB2igr6nS1EN7bCvjldAEfngrM5GyAbmOa4/cafU=
|
||||
github.com/lrstanley/girc v0.0.0-20180913221000-0fb5b684054e/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk=
|
||||
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 h1:AsEBgzv3DhuYHI/GiQh2HxvTP71HCCE9E/tzGUzGdtU=
|
||||
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5/go.mod h1:c2mYKRyMb1BPkO5St0c/ps62L4S0W2NAkaTXj9qEI+0=
|
||||
github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 h1:iOAVXzZyXtW408TMYejlUPo6BIn92HmOacWtIfNyYns=
|
||||
github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6/go.mod h1:sFlOUpQL1YcjhFVXhg1CG8ZASEs/Mf1oVb6H75JL/zg=
|
||||
github.com/magiconair/properties v0.0.0-20180217134545-2c9e95027885 h1:HWxJJvF+QceKcql4r9PC93NtMEgEBfBxlQrZPvbcQvs=
|
||||
github.com/magiconair/properties v0.0.0-20180217134545-2c9e95027885/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/matterbridge/discordgo v0.0.0-20180806170629-ef40ff5ba64f h1:9IIOO9Aznn8zJx3nokZ4U6nfuzWw5xAlygPvuRZMisQ=
|
||||
|
@ -83,10 +85,8 @@ github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff h1:HLGD5/9UxxfEuO9Dt
|
|||
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff/go.mod h1:B8jLfIIPn2sKyWr0D7cL2v7tnrDD5z291s2Zypdu89E=
|
||||
github.com/nicksnyder/go-i18n v1.4.0 h1:AgLl+Yq7kg5OYlzCgu9cKTZOyI4tD/NgukKqLqC8E+I=
|
||||
github.com/nicksnyder/go-i18n v1.4.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q=
|
||||
github.com/nlopes/slack v0.0.0-20180101221843-107290b5bbaf h1:M+xGhDxie/MqC+tzs+3ZHBSY4Wsv+fEkrpIMCKy8PTg=
|
||||
github.com/nlopes/slack v0.0.0-20180101221843-107290b5bbaf/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
|
||||
github.com/nlopes/slack v0.3.1-0.20180805133408-21749ab136a8 h1:PSy8NkmkyldLmPPnNNw7mwfQFOHDqOI6bINpJ+/KV7Y=
|
||||
github.com/nlopes/slack v0.3.1-0.20180805133408-21749ab136a8/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
|
||||
github.com/nlopes/slack v0.4.0 h1:OVnHm7lv5gGT5gkcHsZAyw++oHVFihbjWbL3UceUpiA=
|
||||
github.com/nlopes/slack v0.4.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
|
||||
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U=
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
|
||||
## Gopkg.toml example (these lines may be deleted)
|
||||
|
||||
## "metadata" defines metadata about the project that could be used by other independent
|
||||
## systems. The metadata defined here will be ignored by dep.
|
||||
# [metadata]
|
||||
# key1 = "value that convey data to other systems"
|
||||
# system1-data = "value that is used by a system"
|
||||
# system2-data = "value that is used by another system"
|
||||
|
||||
## "required" lists a set of packages (not projects) that must be included in
|
||||
## Gopkg.lock. This list is merged with the set of packages imported by the current
|
||||
## project. Use it when your project needs a package it doesn't explicitly import -
|
||||
## including "main" packages.
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
|
||||
## "ignored" lists a set of packages (not projects) that are ignored when
|
||||
## dep statically analyzes source code. Ignored packages can be in this project,
|
||||
## or in a dependency.
|
||||
# ignored = ["github.com/user/project/badpkg"]
|
||||
|
||||
## Constraints are rules for how directly imported projects
|
||||
## may be incorporated into the depgraph. They are respected by
|
||||
## dep whether coming from the Gopkg.toml of the current project or a dependency.
|
||||
# [[constraint]]
|
||||
## Required: the root import path of the project being constrained.
|
||||
# name = "github.com/user/project"
|
||||
#
|
||||
## Recommended: the version constraint to enforce for the project.
|
||||
## Only one of "branch", "version" or "revision" can be specified.
|
||||
# version = "1.0.0"
|
||||
# branch = "master"
|
||||
# revision = "abc123"
|
||||
#
|
||||
## Optional: an alternate location (URL or import path) for the project's source.
|
||||
# source = "https://github.com/myfork/package.git"
|
||||
#
|
||||
## "metadata" defines metadata about the dependency or override that could be used
|
||||
## by other independent systems. The metadata defined here will be ignored by dep.
|
||||
# [metadata]
|
||||
# key1 = "value that convey data to other systems"
|
||||
# system1-data = "value that is used by a system"
|
||||
# system2-data = "value that is used by another system"
|
||||
|
||||
## Overrides have the same structure as [[constraint]], but supersede all
|
||||
## [[constraint]] declarations from all projects. Only [[override]] from
|
||||
## the current project's are applied.
|
||||
##
|
||||
## Overrides are a sledgehammer. Use them only as a last resort.
|
||||
# [[override]]
|
||||
## Required: the root import path of the project being constrained.
|
||||
# name = "github.com/user/project"
|
||||
#
|
||||
## Optional: specifying a version constraint override will cause all other
|
||||
## constraints on this project to be ignored; only the overridden constraint
|
||||
## need be satisfied.
|
||||
## Again, only one of "branch", "version" or "revision" can be specified.
|
||||
# version = "1.0.0"
|
||||
# branch = "master"
|
||||
# revision = "abc123"
|
||||
#
|
||||
## Optional: specifying an alternate source location as an override will
|
||||
## enforce that the alternate location is used for that project, regardless of
|
||||
## what source location any dependent projects specify.
|
||||
# source = "https://github.com/myfork/package.git"
|
||||
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
version = "3.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/labstack/gommon"
|
||||
version = "0.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.1.4"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/valyala/fasttemplate"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
|
@ -1,3 +1,15 @@
|
|||
### 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)
|
||||
|
|
|
@ -13,6 +13,12 @@
|
|||
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
|
@ -28,6 +34,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "888307bf47ee004aaaa4c45e6139929b4984f2253e48e382246bfb8c66f3cd65"
|
||||
inputs-digest = "596fa546322c2a1e9708a10c9f39aca2e04792b477fab86fb2899fbaab776070"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
ignored = ["github.com/lusis/slack-test"]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
version = "1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/errors"
|
||||
version = "0.8.0"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
|
@ -4,7 +4,8 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/nlopes/slack/slackutilsx"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -164,22 +165,24 @@ func (api *Client) SendMessageContext(ctx context.Context, channelID string, opt
|
|||
return "", "", "", err
|
||||
}
|
||||
|
||||
if err = postSlackMethod(ctx, api.httpclient, string(config.mode), config.values, &response, api.debug); err != nil {
|
||||
if err = postForm(ctx, api.httpclient, config.endpoint, config.values, &response, api.debug); err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
return response.Channel, response.getMessageTimestamp(), response.Text, response.Err()
|
||||
}
|
||||
|
||||
// ApplyMsgOptions utility function for debugging/testing chat requests.
|
||||
func ApplyMsgOptions(token, channel string, options ...MsgOption) (string, url.Values, error) {
|
||||
// UnsafeApplyMsgOptions utility function for debugging/testing chat requests.
|
||||
// NOTE: USE AT YOUR OWN RISK: No issues relating to the use of this function
|
||||
// will be supported by the library.
|
||||
func UnsafeApplyMsgOptions(token, channel string, options ...MsgOption) (string, url.Values, error) {
|
||||
config, err := applyMsgOptions(token, channel, options...)
|
||||
return string(config.mode), config.values, err
|
||||
return config.endpoint, config.values, err
|
||||
}
|
||||
|
||||
func applyMsgOptions(token, channel string, options ...MsgOption) (sendConfig, error) {
|
||||
config := sendConfig{
|
||||
mode: chatPostMessage,
|
||||
endpoint: SLACK_API + string(chatPostMessage),
|
||||
values: url.Values{
|
||||
"token": {token},
|
||||
"channel": {channel},
|
||||
|
@ -195,11 +198,6 @@ func applyMsgOptions(token, channel string, options ...MsgOption) (sendConfig, e
|
|||
return config, nil
|
||||
}
|
||||
|
||||
func escapeMessage(message string) string {
|
||||
replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
|
||||
return replacer.Replace(message)
|
||||
}
|
||||
|
||||
type sendMode string
|
||||
|
||||
const (
|
||||
|
@ -211,8 +209,8 @@ const (
|
|||
)
|
||||
|
||||
type sendConfig struct {
|
||||
mode sendMode
|
||||
values url.Values
|
||||
endpoint string
|
||||
values url.Values
|
||||
}
|
||||
|
||||
// MsgOption option provided when sending a message.
|
||||
|
@ -221,7 +219,7 @@ type MsgOption func(*sendConfig) error
|
|||
// MsgOptionPost posts a messages, this is the default.
|
||||
func MsgOptionPost() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.mode = chatPostMessage
|
||||
config.endpoint = SLACK_API + string(chatPostMessage)
|
||||
config.values.Del("ts")
|
||||
return nil
|
||||
}
|
||||
|
@ -231,7 +229,7 @@ func MsgOptionPost() MsgOption {
|
|||
// posts an ephemeral message.
|
||||
func MsgOptionPostEphemeral() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.mode = chatPostEphemeral
|
||||
config.endpoint = SLACK_API + string(chatPostEphemeral)
|
||||
config.values.Del("ts")
|
||||
return nil
|
||||
}
|
||||
|
@ -240,7 +238,7 @@ func MsgOptionPostEphemeral() MsgOption {
|
|||
// MsgOptionPostEphemeral2 - posts an ephemeral message to the provided user.
|
||||
func MsgOptionPostEphemeral2(userID string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.mode = chatPostEphemeral
|
||||
config.endpoint = SLACK_API + string(chatPostEphemeral)
|
||||
MsgOptionUser(userID)(config)
|
||||
config.values.Del("ts")
|
||||
|
||||
|
@ -251,7 +249,7 @@ func MsgOptionPostEphemeral2(userID string) MsgOption {
|
|||
// MsgOptionMeMessage posts a "me message" type from the calling user
|
||||
func MsgOptionMeMessage() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.mode = chatMeMessage
|
||||
config.endpoint = SLACK_API + string(chatMeMessage)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -259,7 +257,7 @@ func MsgOptionMeMessage() MsgOption {
|
|||
// MsgOptionUpdate updates a message based on the timestamp.
|
||||
func MsgOptionUpdate(timestamp string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.mode = chatUpdate
|
||||
config.endpoint = SLACK_API + string(chatUpdate)
|
||||
config.values.Add("ts", timestamp)
|
||||
return nil
|
||||
}
|
||||
|
@ -268,7 +266,7 @@ func MsgOptionUpdate(timestamp string) MsgOption {
|
|||
// MsgOptionDelete deletes a message based on the timestamp.
|
||||
func MsgOptionDelete(timestamp string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.mode = chatDelete
|
||||
config.endpoint = SLACK_API + string(chatDelete)
|
||||
config.values.Add("ts", timestamp)
|
||||
return nil
|
||||
}
|
||||
|
@ -297,7 +295,7 @@ func MsgOptionUser(userID string) MsgOption {
|
|||
func MsgOptionText(text string, escape bool) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
if escape {
|
||||
text = escapeMessage(text)
|
||||
text = slackutilsx.EscapeMessage(text)
|
||||
}
|
||||
config.values.Add("text", text)
|
||||
return nil
|
||||
|
@ -392,6 +390,18 @@ func MsgOptionParse(b bool) MsgOption {
|
|||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
|
@ -29,6 +29,8 @@ type conversation struct {
|
|||
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
|
||||
}
|
||||
|
@ -64,6 +66,13 @@ type GetUsersInConversationParameters struct {
|
|||
Limit int
|
||||
}
|
||||
|
||||
type GetConversationsForUserParameters struct {
|
||||
UserID string
|
||||
Cursor string
|
||||
Types []string
|
||||
Limit int
|
||||
}
|
||||
|
||||
type responseMetaData struct {
|
||||
NextCursor string `json:"next_cursor"`
|
||||
}
|
||||
|
@ -100,6 +109,41 @@ func (api *Client) GetUsersInConversationContext(ctx context.Context, params *Ge
|
|||
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},
|
||||
"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, ","))
|
||||
}
|
||||
response := struct {
|
||||
Channels []Channel `json:"channels"`
|
||||
ResponseMetaData responseMetaData `json:"response_metadata"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err = postSlackMethod(ctx, api.httpclient, "users.conversations", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, "", errors.New(response.Error)
|
||||
}
|
||||
return response.Channels, response.ResponseMetaData.NextCursor, nil
|
||||
}
|
||||
|
||||
// ArchiveConversation archives a conversation
|
||||
func (api *Client) ArchiveConversation(channelID string) error {
|
||||
return api.ArchiveConversationContext(context.Background(), channelID)
|
||||
|
|
|
@ -6,52 +6,47 @@ import (
|
|||
"errors"
|
||||
)
|
||||
|
||||
// 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 textfield 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"`
|
||||
}
|
||||
|
||||
// DialogTrigger ...
|
||||
type DialogTrigger struct {
|
||||
TriggerId string `json:"trigger_id"` //Required. Must respond within 3 seconds.
|
||||
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 {
|
||||
CallbackId string `json:"callback_id"` //Required.
|
||||
Title string `json:"title"` //Required.
|
||||
SubmitLabel string `json:"submit_label,omitempty"` //Optional. Default value is 'Submit'
|
||||
NotifyOnCancel bool `json:"notify_on_cancel,omitempty"` //Optional. Default value is false
|
||||
Elements []DialogElement `json:"elements"` //Required.
|
||||
TriggerID string `json:"trigger_id"` //Required
|
||||
CallbackID string `json:"callback_id"` //Required
|
||||
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{}
|
||||
|
||||
type DialogTextElement struct {
|
||||
Label string `json:"label"` //Required.
|
||||
Name string `json:"name"` //Required.
|
||||
Type string `json:"type"` //Required. Allowed values: "text", "textarea", "select".
|
||||
Placeholder string `json:"placeholder,omitempty"` //Optional.
|
||||
Optional bool `json:"optional,omitempty"` //Optional. Default value is false
|
||||
Value string `json:"value,omitempty"` //Optional.
|
||||
MaxLength int `json:"max_length,omitempty"` //Optional.
|
||||
MinLength int `json:"min_length,omitempty"` //Optional,. Default value is 0
|
||||
Hint string `json:"hint,omitempty"` //Optional.
|
||||
Subtype string `json:"subtype,omitempty"` //Optional. Allowed values: "email", "number", "tel", "url".
|
||||
}
|
||||
|
||||
type DialogSelectElement struct {
|
||||
Label string `json:"label"` //Required.
|
||||
Name string `json:"name"` //Required.
|
||||
Type string `json:"type"` //Required. Allowed values: "text", "textarea", "select".
|
||||
Placeholder string `json:"placeholder,omitempty"` //Optional.
|
||||
Optional bool `json:"optional,omitempty"` //Optional. Default value is false
|
||||
Value string `json:"value,omitempty"` //Optional.
|
||||
DataSource string `json:"data_source,omitempty"` //Optional. Allowed values: "users", "channels", "conversations", "external".
|
||||
SelectedOptions string `json:"selected_options,omitempty"` //Optional. Default value for "external" only
|
||||
Options []DialogElementOption `json:"options,omitempty"` //One of options or option_groups is required.
|
||||
OptionGroups []DialogElementOption `json:"option_groups,omitempty"` //Provide up to 100 options.
|
||||
}
|
||||
|
||||
type DialogElementOption struct {
|
||||
Label string `json:"label"` //Required.
|
||||
Value string `json:"value"` //Required.
|
||||
}
|
||||
|
||||
// DialogCallback is sent from Slack when a user submits a form from within a dialog
|
||||
type DialogCallback struct {
|
||||
Type string `json:"type"`
|
||||
|
@ -78,28 +73,43 @@ type DialogSuggestionCallback struct {
|
|||
CallbackID string `json:"callback_id"`
|
||||
}
|
||||
|
||||
// OpenDialog opens a dialog window where the triggerId originated from
|
||||
func (api *Client) OpenDialog(triggerId string, dialog Dialog) (err error) {
|
||||
return api.OpenDialogContext(context.Background(), triggerId, dialog)
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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
|
||||
func (api *Client) OpenDialogContext(ctx context.Context, triggerId string, dialog Dialog) (err error) {
|
||||
if triggerId == "" {
|
||||
// EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable.
|
||||
func (api *Client) OpenDialogContext(ctx context.Context, triggerID string, dialog Dialog) (err error) {
|
||||
if triggerID == "" {
|
||||
return errors.New("received empty parameters")
|
||||
}
|
||||
|
||||
resp := DialogTrigger{
|
||||
TriggerId: triggerId,
|
||||
req := DialogTrigger{
|
||||
TriggerID: triggerID,
|
||||
Dialog: dialog,
|
||||
}
|
||||
jsonResp, err := json.Marshal(resp)
|
||||
|
||||
encoded, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response := &SlackResponse{}
|
||||
|
||||
response := &DialogOpenResponse{}
|
||||
endpoint := SLACK_API + "dialog.open"
|
||||
if err := postJSON(ctx, api.httpclient, endpoint, api.token, jsonResp, response, api.debug); err != nil {
|
||||
if err := postJSON(ctx, api.httpclient, endpoint, api.token, encoded, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
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 string `json:"selected_options,omitempty"` //Optional. Default value for "external" only
|
||||
Options []DialogSelectOption `json:"options,omitempty"` //One of options or option_groups is required.
|
||||
OptionGroups []DialogOptionGroup `json:"option_groups,omitempty"` //Provide up to 100 options.
|
||||
}
|
||||
|
||||
// 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, groups map[string]map[string]string) *DialogInputSelect {
|
||||
optionGroups := []DialogOptionGroup{}
|
||||
for groupName, options := range groups {
|
||||
optionGroups = append(optionGroups, DialogOptionGroup{
|
||||
Label: groupName,
|
||||
Options: optionsFromMap(options),
|
||||
})
|
||||
}
|
||||
return &DialogInputSelect{
|
||||
DialogInput: DialogInput{
|
||||
Type: InputTypeSelect,
|
||||
Name: name,
|
||||
Label: label,
|
||||
},
|
||||
DataSource: DialogDataSourceStatic,
|
||||
OptionGroups: optionGroups,
|
||||
}
|
||||
}
|
||||
|
||||
func optionsFromArray(options []string) []DialogSelectOption {
|
||||
selectOptions := make([]DialogSelectOption, len(options))
|
||||
for idx, value := range options {
|
||||
selectOptions[idx] = DialogSelectOption{
|
||||
Label: value,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
return selectOptions
|
||||
}
|
||||
|
||||
func optionsFromMap(options map[string]string) []DialogSelectOption {
|
||||
selectOptions := make([]DialogSelectOption, len(options))
|
||||
idx := 0
|
||||
var option DialogSelectOption
|
||||
for key, value := range options {
|
||||
option = DialogSelectOption{
|
||||
Label: key,
|
||||
Value: value,
|
||||
}
|
||||
selectOptions[idx] = option
|
||||
idx++
|
||||
}
|
||||
return selectOptions
|
||||
}
|
||||
|
||||
// NewConversationsSelect returns a `Conversations` select
|
||||
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,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package slack
|
||||
|
||||
// TextInputSubtype Accepts email, number, tel, or url. In some form factors, optimized input is provided for this subtype.
|
||||
type TextInputSubtype string
|
||||
|
||||
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) *TextInputElement {
|
||||
return &TextInputElement{
|
||||
DialogInput: DialogInput{
|
||||
Type: InputTypeText,
|
||||
Name: name,
|
||||
Label: label,
|
||||
},
|
||||
Value: text,
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
|
@ -93,14 +93,15 @@ type File struct {
|
|||
// 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.
|
||||
type FileUploadParameters struct {
|
||||
File string
|
||||
Content string
|
||||
Reader io.Reader
|
||||
Filetype string
|
||||
Filename string
|
||||
Title string
|
||||
InitialComment string
|
||||
Channels []string
|
||||
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
|
||||
|
@ -237,6 +238,9 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam
|
|||
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, ","))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package slack
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// SecretsVerifier contains the information needed to verify that the request comes from Slack
|
||||
type SecretsVerifier struct {
|
||||
slackSig string
|
||||
timeStamp string
|
||||
hmac hash.Hash
|
||||
}
|
||||
|
||||
// NewSecretsVerifier returns a SecretsVerifier object in exchange for an http.Header object and signing secret
|
||||
func NewSecretsVerifier(header http.Header, signingSecret string) (SecretsVerifier, error) {
|
||||
if header["X-Slack-Signature"][0] == "" || header["X-Slack-Request-Timestamp"][0] == "" {
|
||||
return SecretsVerifier{}, errors.New("Headers are empty, cannot create SecretsVerifier")
|
||||
}
|
||||
|
||||
hash := hmac.New(sha256.New, []byte(signingSecret))
|
||||
hash.Write([]byte(fmt.Sprintf("v0:%s:", header["X-Slack-Request-Timestamp"][0])))
|
||||
return SecretsVerifier{
|
||||
slackSig: header["X-Slack-Signature"][0],
|
||||
timeStamp: header["X-Slack-Request-Timestamp"][0],
|
||||
hmac: hash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *SecretsVerifier) Write(body []byte) (n int, err error) {
|
||||
return v.hmac.Write(body)
|
||||
}
|
||||
|
||||
// Ensure compares the signature sent from Slack with the actual computed hash to judge validity
|
||||
func (v SecretsVerifier) Ensure() error {
|
||||
computed := "v0=" + string(hex.EncodeToString(v.hmac.Sum(nil)))
|
||||
if computed == v.slackSig {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Expected signing signature: %s, but computed: %s", v.slackSig, computed)
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// 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)
|
||||
}
|
|
@ -25,6 +25,7 @@ type UserGroup struct {
|
|||
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)
|
||||
|
@ -121,16 +122,62 @@ func (api *Client) EnableUserGroupContext(ctx context.Context, userGroup string)
|
|||
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() ([]UserGroup, error) {
|
||||
return api.GetUserGroupsContext(context.Background())
|
||||
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) ([]UserGroup, error) {
|
||||
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 := userGroupRequest(ctx, api.httpclient, "usergroups.list", values, api.debug)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
package slack
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"net/http"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type WebhookMessage struct {
|
||||
Text string `json:"text,omitempty"`
|
||||
Attachments []Attachment `json:"attachments,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Attachments []Attachment `json:"attachments,omitempty"`
|
||||
}
|
||||
|
||||
func PostWebhook(url string, msg *WebhookMessage) error {
|
||||
|
@ -19,7 +20,7 @@ func PostWebhook(url string, msg *WebhookMessage) error {
|
|||
return errors.Wrap(err, "marshal failed")
|
||||
}
|
||||
|
||||
response, err := http.Post(url, "application/json", bytes.NewReader(raw));
|
||||
response, err := http.Post(url, "application/json", bytes.NewReader(raw))
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to post webhook")
|
||||
|
|
|
@ -524,4 +524,9 @@ var EventMapping = map[string]interface{}{
|
|||
|
||||
"member_joined_channel": MemberJoinedChannelEvent{},
|
||||
"member_left_channel": MemberLeftChannelEvent{},
|
||||
|
||||
"subteam_created": SubteamCreatedEvent{},
|
||||
"subteam_self_added": SubteamSelfAddedEvent{},
|
||||
"subteam_self_removed": SubteamSelfRemovedEvent{},
|
||||
"subteam_updated": SubteamUpdatedEvent{},
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
################################################################################
|
||||
## Comment
|
||||
|
||||
# Speak your mind with the hash symbol. They go from the symbol to the end of
|
||||
# the line.
|
||||
|
||||
|
||||
################################################################################
|
||||
## Table
|
||||
|
||||
# Tables (also known as hash tables or dictionaries) are collections of
|
||||
# key/value pairs. They appear in square brackets on a line by themselves.
|
||||
|
||||
[table]
|
||||
|
||||
key = "value" # Yeah, you can do this.
|
||||
|
||||
# Nested tables are denoted by table names with dots in them. Name your tables
|
||||
# whatever crap you please, just don't use #, ., [ or ].
|
||||
|
||||
[table.subtable]
|
||||
|
||||
key = "another value"
|
||||
|
||||
# You don't need to specify all the super-tables if you don't want to. TOML
|
||||
# knows how to do it for you.
|
||||
|
||||
# [x] you
|
||||
# [x.y] don't
|
||||
# [x.y.z] need these
|
||||
[x.y.z.w] # for this to work
|
||||
|
||||
|
||||
################################################################################
|
||||
## Inline Table
|
||||
|
||||
# Inline tables provide a more compact syntax for expressing tables. They are
|
||||
# especially useful for grouped data that can otherwise quickly become verbose.
|
||||
# Inline tables are enclosed in curly braces `{` and `}`. No newlines are
|
||||
# allowed between the curly braces unless they are valid within a value.
|
||||
|
||||
[table.inline]
|
||||
|
||||
name = { first = "Tom", last = "Preston-Werner" }
|
||||
point = { x = 1, y = 2 }
|
||||
|
||||
|
||||
################################################################################
|
||||
## String
|
||||
|
||||
# There are four ways to express strings: basic, multi-line basic, literal, and
|
||||
# multi-line literal. All strings must contain only valid UTF-8 characters.
|
||||
|
||||
[string.basic]
|
||||
|
||||
basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
|
||||
|
||||
[string.multiline]
|
||||
|
||||
# The following strings are byte-for-byte equivalent:
|
||||
key1 = "One\nTwo"
|
||||
key2 = """One\nTwo"""
|
||||
key3 = """
|
||||
One
|
||||
Two"""
|
||||
|
||||
[string.multiline.continued]
|
||||
|
||||
# The following strings are byte-for-byte equivalent:
|
||||
key1 = "The quick brown fox jumps over the lazy dog."
|
||||
|
||||
key2 = """
|
||||
The quick brown \
|
||||
|
||||
|
||||
fox jumps over \
|
||||
the lazy dog."""
|
||||
|
||||
key3 = """\
|
||||
The quick brown \
|
||||
fox jumps over \
|
||||
the lazy dog.\
|
||||
"""
|
||||
|
||||
[string.literal]
|
||||
|
||||
# What you see is what you get.
|
||||
winpath = 'C:\Users\nodejs\templates'
|
||||
winpath2 = '\\ServerX\admin$\system32\'
|
||||
quoted = 'Tom "Dubs" Preston-Werner'
|
||||
regex = '<\i\c*\s*>'
|
||||
|
||||
|
||||
[string.literal.multiline]
|
||||
|
||||
regex2 = '''I [dw]on't need \d{2} apples'''
|
||||
lines = '''
|
||||
The first newline is
|
||||
trimmed in raw strings.
|
||||
All other whitespace
|
||||
is preserved.
|
||||
'''
|
||||
|
||||
|
||||
################################################################################
|
||||
## Integer
|
||||
|
||||
# Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
|
||||
# Negative numbers are prefixed with a minus sign.
|
||||
|
||||
[integer]
|
||||
|
||||
key1 = +99
|
||||
key2 = 42
|
||||
key3 = 0
|
||||
key4 = -17
|
||||
|
||||
[integer.underscores]
|
||||
|
||||
# For large numbers, you may use underscores to enhance readability. Each
|
||||
# underscore must be surrounded by at least one digit.
|
||||
key1 = 1_000
|
||||
key2 = 5_349_221
|
||||
key3 = 1_2_3_4_5 # valid but inadvisable
|
||||
|
||||
|
||||
################################################################################
|
||||
## Float
|
||||
|
||||
# A float consists of an integer part (which may be prefixed with a plus or
|
||||
# minus sign) followed by a fractional part and/or an exponent part.
|
||||
|
||||
[float.fractional]
|
||||
|
||||
key1 = +1.0
|
||||
key2 = 3.1415
|
||||
key3 = -0.01
|
||||
|
||||
[float.exponent]
|
||||
|
||||
key1 = 5e+22
|
||||
key2 = 1e6
|
||||
key3 = -2E-2
|
||||
|
||||
[float.both]
|
||||
|
||||
key = 6.626e-34
|
||||
|
||||
[float.underscores]
|
||||
|
||||
key1 = 9_224_617.445_991_228_313
|
||||
key2 = 1e1_00
|
||||
|
||||
|
||||
################################################################################
|
||||
## Boolean
|
||||
|
||||
# Booleans are just the tokens you're used to. Always lowercase.
|
||||
|
||||
[boolean]
|
||||
|
||||
True = true
|
||||
False = false
|
||||
|
||||
|
||||
################################################################################
|
||||
## Datetime
|
||||
|
||||
# Datetimes are RFC 3339 dates.
|
||||
|
||||
[datetime]
|
||||
|
||||
key1 = 1979-05-27T07:32:00Z
|
||||
key2 = 1979-05-27T00:32:00-07:00
|
||||
key3 = 1979-05-27T00:32:00.999999-07:00
|
||||
|
||||
|
||||
################################################################################
|
||||
## Array
|
||||
|
||||
# Arrays are square brackets with other primitives inside. Whitespace is
|
||||
# ignored. Elements are separated by commas. Data types may not be mixed.
|
||||
|
||||
[array]
|
||||
|
||||
key1 = [ 1, 2, 3 ]
|
||||
key2 = [ "red", "yellow", "green" ]
|
||||
key3 = [ [ 1, 2 ], [3, 4, 5] ]
|
||||
#key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
|
||||
|
||||
# Arrays can also be multiline. So in addition to ignoring whitespace, arrays
|
||||
# also ignore newlines between the brackets. Terminating commas are ok before
|
||||
# the closing bracket.
|
||||
|
||||
key5 = [
|
||||
1, 2, 3
|
||||
]
|
||||
key6 = [
|
||||
1,
|
||||
2, # this is ok
|
||||
]
|
||||
|
||||
|
||||
################################################################################
|
||||
## Array of Tables
|
||||
|
||||
# These can be expressed by using a table name in double brackets. Each table
|
||||
# with the same double bracketed name will be an element in the array. The
|
||||
# tables are inserted in the order encountered.
|
||||
|
||||
[[products]]
|
||||
|
||||
name = "Hammer"
|
||||
sku = 738594937
|
||||
|
||||
[[products]]
|
||||
|
||||
[[products]]
|
||||
|
||||
name = "Nail"
|
||||
sku = 284758393
|
||||
color = "gray"
|
||||
|
||||
|
||||
# You can create nested arrays of tables as well.
|
||||
|
||||
[[fruit]]
|
||||
name = "apple"
|
||||
|
||||
[fruit.physical]
|
||||
color = "red"
|
||||
shape = "round"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "red delicious"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "granny smith"
|
||||
|
||||
[[fruit]]
|
||||
name = "banana"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "plantain"
|
|
@ -0,0 +1,29 @@
|
|||
# This is a TOML document. Boom.
|
||||
|
||||
title = "TOML Example"
|
||||
|
||||
[owner]
|
||||
name = "Tom Preston-Werner"
|
||||
organization = "GitHub"
|
||||
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
|
||||
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
|
||||
|
||||
[database]
|
||||
server = "192.168.1.1"
|
||||
ports = [ 8001, 8001, 8002 ]
|
||||
connection_max = 5000
|
||||
enabled = true
|
||||
|
||||
[servers]
|
||||
|
||||
# You can indent as you please. Tabs or spaces. TOML don't care.
|
||||
[servers.alpha]
|
||||
ip = "10.0.0.1"
|
||||
dc = "eqdc10"
|
||||
|
||||
[servers.beta]
|
||||
ip = "10.0.0.2"
|
||||
dc = "eqdc10"
|
||||
|
||||
[clients]
|
||||
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
|
|
@ -0,0 +1,29 @@
|
|||
# This is a TOML document. Boom.
|
||||
|
||||
title = "TOML Example"
|
||||
|
||||
[owner]
|
||||
name = "Tom Preston-Werner"
|
||||
organization = "GitHub"
|
||||
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
|
||||
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
|
||||
|
||||
[database]
|
||||
server = "192.168.1.1"
|
||||
ports = [ 8001, 8001, 8002 ]
|
||||
connection_max = 5000
|
||||
enabled = true
|
||||
|
||||
[servers]
|
||||
|
||||
# You can indent as you please. Tabs or spaces. TOML don't care.
|
||||
[servers.alpha]
|
||||
ip = "10.0.0.1"
|
||||
dc = "eqdc10"
|
||||
|
||||
[servers.beta]
|
||||
ip = "10.0.0.2"
|
||||
dc = "eqdc10"
|
||||
|
||||
[clients]
|
||||
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
|
|
@ -0,0 +1,38 @@
|
|||
title = "TOML Marshal Testing"
|
||||
|
||||
[basic]
|
||||
bool = true
|
||||
date = 1979-05-27T07:32:00Z
|
||||
float = 123.4
|
||||
int = 5000
|
||||
string = "Bite me"
|
||||
uint = 5001
|
||||
|
||||
[basic_lists]
|
||||
bools = [true,false,true]
|
||||
dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z]
|
||||
floats = [12.3,45.6,78.9]
|
||||
ints = [8001,8001,8002]
|
||||
strings = ["One","Two","Three"]
|
||||
uints = [5002,5003]
|
||||
|
||||
[basic_map]
|
||||
one = "one"
|
||||
two = "two"
|
||||
|
||||
[subdoc]
|
||||
|
||||
[subdoc.first]
|
||||
name = "First"
|
||||
|
||||
[subdoc.second]
|
||||
name = "Second"
|
||||
|
||||
[[subdoclist]]
|
||||
name = "List.First"
|
||||
|
||||
[[subdoclist]]
|
||||
name = "List.Second"
|
||||
|
||||
[[subdocptrs]]
|
||||
name = "Second"
|
|
@ -95,8 +95,9 @@ github.com/nicksnyder/go-i18n/i18n
|
|||
github.com/nicksnyder/go-i18n/i18n/bundle
|
||||
github.com/nicksnyder/go-i18n/i18n/language
|
||||
github.com/nicksnyder/go-i18n/i18n/translation
|
||||
# github.com/nlopes/slack v0.3.1-0.20180805133408-21749ab136a8
|
||||
# github.com/nlopes/slack v0.4.0
|
||||
github.com/nlopes/slack
|
||||
github.com/nlopes/slack/slackutilsx
|
||||
# github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83
|
||||
github.com/paulrosania/go-charset/charset
|
||||
github.com/paulrosania/go-charset/data
|
||||
|
|
Loading…
Reference in New Issue