Update dependencies and build to go1.22 (#2113)

* Update dependencies and build to go1.22

* Fix api changes wrt to dependencies

* Update golangci config
This commit is contained in:
Wim
2024-05-23 23:44:31 +02:00
committed by GitHub
parent 56e7bd01ca
commit 2f33fe86f5
1556 changed files with 3279522 additions and 1924375 deletions

View File

@@ -1,3 +1,4 @@
*.test
*~
.idea/
/vendor/

View File

@@ -18,6 +18,7 @@ const (
MBTInput MessageBlockType = "input"
MBTHeader MessageBlockType = "header"
MBTRichText MessageBlockType = "rich_text"
MBTVideo MessageBlockType = "video"
)
// Block defines an interface all block types should implement
@@ -39,6 +40,7 @@ type BlockAction struct {
Type ActionType `json:"type"`
Text TextBlockObject `json:"text"`
Value string `json:"value"`
Files []File `json:"files"`
ActionTs string `json:"action_ts"`
SelectedOption OptionBlockObject `json:"selected_option"`
SelectedOptions []OptionBlockObject `json:"selected_options"`

View File

@@ -2,7 +2,6 @@ package slack
import (
"encoding/json"
"errors"
"fmt"
)
@@ -66,8 +65,12 @@ func (b *Blocks) UnmarshalJSON(data []byte) error {
block = &InputBlock{}
case "rich_text":
block = &RichTextBlock{}
case "rich_text_input":
block = &RichTextBlock{}
case "section":
block = &SectionBlock{}
case "video":
block = &VideoBlock{}
default:
block = &UnknownBlock{}
}
@@ -114,6 +117,8 @@ func (b *InputBlock) UnmarshalJSON(data []byte) error {
e = &DateTimePickerBlockElement{}
case "plain_text_input":
e = &PlainTextInputBlockElement{}
case "rich_text_input":
e = &RichTextInputBlockElement{}
case "email_text_input":
e = &EmailTextInputBlockElement{}
case "url_text_input":
@@ -130,8 +135,10 @@ func (b *InputBlock) UnmarshalJSON(data []byte) error {
e = &RadioButtonsBlockElement{}
case "number_input":
e = &NumberInputBlockElement{}
case "file_input":
e = &FileInputBlockElement{}
default:
return errors.New("unsupported block element type")
return fmt.Errorf("unsupported block element type %v", s.TypeVal)
}
if err := json.Unmarshal(a.Element, e); err != nil {
@@ -196,6 +203,8 @@ func (b *BlockElements) UnmarshalJSON(data []byte) error {
blockElement = &DateTimePickerBlockElement{}
case "plain_text_input":
blockElement = &PlainTextInputBlockElement{}
case "rich_text_input":
blockElement = &RichTextInputBlockElement{}
case "email_text_input":
blockElement = &EmailTextInputBlockElement{}
case "url_text_input":
@@ -298,6 +307,12 @@ func (a *Accessory) UnmarshalJSON(data []byte) error {
return err
}
a.PlainTextInputElement = element.(*PlainTextInputBlockElement)
case "rich_text_input":
element, err := unmarshalBlockElement(r, &RichTextInputBlockElement{})
if err != nil {
return err
}
a.RichTextInputElement = element.(*RichTextInputBlockElement)
case "radio_buttons":
element, err := unmarshalBlockElement(r, &RadioButtonsBlockElement{})
if err != nil {
@@ -429,7 +444,7 @@ func (e *ContextElements) UnmarshalJSON(data []byte) error {
e.Elements = append(e.Elements, elem.(*ImageBlockElement))
default:
return errors.New("unsupported context element type")
return fmt.Errorf("unsupported context element type %v", contextElementType)
}
}

View File

@@ -12,9 +12,11 @@ const (
METDatetimepicker MessageElementType = "datetimepicker"
METPlainTextInput MessageElementType = "plain_text_input"
METRadioButtons MessageElementType = "radio_buttons"
METRichTextInput MessageElementType = "rich_text_input"
METEmailTextInput MessageElementType = "email_text_input"
METURLTextInput MessageElementType = "url_text_input"
METNumber MessageElementType = "number_input"
METFileInput MessageElementType = "file_input"
MixedElementImage MixedElementType = "mixed_image"
MixedElementText MixedElementType = "mixed_text"
@@ -51,6 +53,7 @@ type Accessory struct {
DatePickerElement *DatePickerBlockElement
TimePickerElement *TimePickerBlockElement
PlainTextInputElement *PlainTextInputBlockElement
RichTextInputElement *RichTextInputBlockElement
RadioButtonsElement *RadioButtonsBlockElement
SelectElement *SelectBlockElement
MultiSelectElement *MultiSelectBlockElement
@@ -73,6 +76,8 @@ func NewAccessory(element BlockElement) *Accessory {
return &Accessory{TimePickerElement: element.(*TimePickerBlockElement)}
case *PlainTextInputBlockElement:
return &Accessory{PlainTextInputElement: element.(*PlainTextInputBlockElement)}
case *RichTextInputBlockElement:
return &Accessory{RichTextInputElement: element.(*RichTextInputBlockElement)}
case *RadioButtonsBlockElement:
return &Accessory{RadioButtonsElement: element.(*RadioButtonsBlockElement)}
case *SelectBlockElement:
@@ -177,6 +182,12 @@ func (s *ButtonBlockElement) WithConfirm(confirm *ConfirmationBlockObject) *Butt
return s
}
// WithURL adds a URL for the button to link to and returns the modified ButtonBlockElement
func (s *ButtonBlockElement) WithURL(url string) *ButtonBlockElement {
s.URL = url
return s
}
// 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{
@@ -509,6 +520,32 @@ func NewPlainTextInputBlockElement(placeholder *TextBlockObject, actionID string
}
}
// RichTextInputBlockElement creates a field where allows users to enter formatted text
// in a WYSIWYG composer, offering the same messaging writing experience as in Slack
// More Information: https://api.slack.com/reference/block-kit/block-elements#rich_text_input
type RichTextInputBlockElement struct {
Type MessageElementType `json:"type"`
ActionID string `json:"action_id,omitempty"`
Placeholder *TextBlockObject `json:"placeholder,omitempty"`
InitialValue string `json:"initial_value,omitempty"`
DispatchActionConfig *DispatchActionConfig `json:"dispatch_action_config,omitempty"`
FocusOnLoad bool `json:"focus_on_load,omitempty"`
}
// ElementType returns the type of the Element
func (s RichTextInputBlockElement) ElementType() MessageElementType {
return s.Type
}
// NewRichTextInputBlockElement returns an instance of a rich-text input element
func NewRichTextInputBlockElement(placeholder *TextBlockObject, actionID string) *RichTextInputBlockElement {
return &RichTextInputBlockElement{
Type: METRichTextInput,
ActionID: actionID,
Placeholder: placeholder,
}
}
// CheckboxGroupsBlockElement defines an element which allows users to choose
// one or more items from a list of possible options.
//
@@ -591,3 +628,40 @@ func NewNumberInputBlockElement(placeholder *TextBlockObject, actionID string, i
IsDecimalAllowed: isDecimalAllowed,
}
}
// FileInputBlockElement creates a field where a user can upload a file.
//
// File input elements are currently only available in modals.
//
// More Information: https://api.slack.com/reference/block-kit/block-elements#file_input
type FileInputBlockElement struct {
Type MessageElementType `json:"type"`
ActionID string `json:"action_id,omitempty"`
FileTypes []string `json:"filetypes,omitempty"`
MaxFiles int `json:"max_files,omitempty"`
}
// ElementType returns the type of the Element
func (s FileInputBlockElement) ElementType() MessageElementType {
return s.Type
}
// NewFileInputBlockElement returns an instance of a file input element
func NewFileInputBlockElement(actionID string) *FileInputBlockElement {
return &FileInputBlockElement{
Type: METFileInput,
ActionID: actionID,
}
}
// WithFileTypes sets the file types that can be uploaded
func (s *FileInputBlockElement) WithFileTypes(fileTypes ...string) *FileInputBlockElement {
s.FileTypes = fileTypes
return s
}
// WithMaxFiles sets the maximum number of files that can be uploaded
func (s *FileInputBlockElement) WithMaxFiles(maxFiles int) *FileInputBlockElement {
s.MaxFiles = maxFiles
return s
}

View File

@@ -147,6 +147,16 @@ func (s TextBlockObject) Validate() error {
return errors.New("emoji cannot be true in mrkdown")
}
// https://api.slack.com/reference/block-kit/composition-objects#text__fields
if len(s.Text) == 0 {
return errors.New("text must have a minimum length of 1")
}
// https://api.slack.com/reference/block-kit/composition-objects#text__fields
if len(s.Text) > 3000 {
return errors.New("text cannot be longer than 3000 characters")
}
return nil
}
@@ -177,7 +187,7 @@ type ConfirmationBlockObject struct {
Title *TextBlockObject `json:"title"`
Text *TextBlockObject `json:"text"`
Confirm *TextBlockObject `json:"confirm"`
Deny *TextBlockObject `json:"deny"`
Deny *TextBlockObject `json:"deny,omitempty"`
Style Style `json:"style,omitempty"`
}

View File

@@ -40,6 +40,12 @@ func (e *RichTextBlock) UnmarshalJSON(b []byte) error {
switch s.Type {
case RTESection:
elem = &RichTextSection{}
case RTEList:
elem = &RichTextList{}
case RTEQuote:
elem = &RichTextQuote{}
case RTEPreformatted:
elem = &RichTextPreformatted{}
default:
elems = append(elems, &RichTextUnknown{
Type: s.Type,
@@ -92,12 +98,92 @@ func (u RichTextUnknown) RichTextElementType() RichTextElementType {
return u.Type
}
type RichTextListElementType string
const (
RTEListOrdered RichTextListElementType = "ordered"
RTEListBullet RichTextListElementType = "bullet"
)
type RichTextList struct {
Type RichTextElementType `json:"type"`
Elements []RichTextElement `json:"elements"`
Style RichTextListElementType `json:"style"`
Indent int `json:"indent"`
}
// NewRichTextList returns a new rich text list element.
func NewRichTextList(style RichTextListElementType, indent int, elements ...RichTextElement) *RichTextList {
return &RichTextList{
Type: RTEList,
Elements: elements,
Style: style,
Indent: indent,
}
}
// ElementType returns the type of the Element
func (s RichTextList) RichTextElementType() RichTextElementType {
return s.Type
}
func (e *RichTextList) UnmarshalJSON(b []byte) error {
var raw struct {
RawElements []json.RawMessage `json:"elements"`
Style RichTextListElementType `json:"style"`
Indent int `json:"indent"`
}
if string(b) == "{}" {
return nil
}
if err := json.Unmarshal(b, &raw); err != nil {
return err
}
elems := make([]RichTextElement, 0, len(raw.RawElements))
for _, r := range raw.RawElements {
var s struct {
Type RichTextElementType `json:"type"`
}
if err := json.Unmarshal(r, &s); err != nil {
return err
}
var elem RichTextElement
switch s.Type {
case RTESection:
elem = &RichTextSection{}
case RTEList:
elem = &RichTextList{}
case RTEQuote:
elem = &RichTextQuote{}
case RTEPreformatted:
elem = &RichTextPreformatted{}
default:
elems = append(elems, &RichTextUnknown{
Type: s.Type,
Raw: string(r),
})
continue
}
if err := json.Unmarshal(r, elem); err != nil {
return err
}
elems = append(elems, elem)
}
*e = RichTextList{
Type: RTEList,
Elements: elems,
Style: raw.Style,
Indent: raw.Indent,
}
return nil
}
type RichTextSection struct {
Type RichTextElementType `json:"type"`
Elements []RichTextSectionElement `json:"elements"`
}
// ElementType returns the type of the Element
// RichTextElementType returns the type of the Element
func (s RichTextSection) RichTextElementType() RichTextElementType {
return s.Type
}
@@ -381,3 +467,62 @@ type RichTextSectionUnknownElement struct {
func (r RichTextSectionUnknownElement) RichTextSectionElementType() RichTextSectionElementType {
return r.Type
}
// RichTextQuote represents rich_text_quote element type.
type RichTextQuote RichTextSection
// RichTextElementType returns the type of the Element
func (s *RichTextQuote) RichTextElementType() RichTextElementType {
return s.Type
}
func (s *RichTextQuote) UnmarshalJSON(b []byte) error {
// reusing the RichTextSection struct, as it's the same as RichTextQuote.
var rts RichTextSection
if err := json.Unmarshal(b, &rts); err != nil {
return err
}
*s = RichTextQuote(rts)
s.Type = RTEQuote
return nil
}
// RichTextPreformatted represents rich_text_quote element type.
type RichTextPreformatted struct {
RichTextSection
Border int `json:"border"`
}
// RichTextElementType returns the type of the Element
func (s *RichTextPreformatted) RichTextElementType() RichTextElementType {
return s.Type
}
func (s *RichTextPreformatted) UnmarshalJSON(b []byte) error {
var rts RichTextSection
if err := json.Unmarshal(b, &rts); err != nil {
return err
}
// we define standalone fields because we need to unmarshal the border
// field. We can not directly unmarshal the data into
// RichTextPreformatted because it will cause an infinite loop. We also
// can not define a struct with embedded RichTextSection and Border fields
// because the json package will not unmarshal the data into the
// standalone fields, once it sees UnmarshalJSON method on the embedded
// struct. The drawback is that we have to process the data twice, and
// have to define a standalone struct with the same set of fields as the
// original struct, which may become a maintenance burden (i.e. update the
// fields in two places, should it ever change).
var standalone struct {
Border int `json:"border"`
}
if err := json.Unmarshal(b, &standalone); err != nil {
return err
}
*s = RichTextPreformatted{
RichTextSection: rts,
Border: standalone.Border,
}
s.Type = RTEPreformatted
return nil
}

65
vendor/github.com/slack-go/slack/block_video.go generated vendored Normal file
View File

@@ -0,0 +1,65 @@
package slack
// VideoBlock defines data required to display a video as a block element
//
// More Information: https://api.slack.com/reference/block-kit/blocks#video
type VideoBlock struct {
Type MessageBlockType `json:"type"`
VideoURL string `json:"video_url"`
ThumbnailURL string `json:"thumbnail_url"`
AltText string `json:"alt_text"`
Title *TextBlockObject `json:"title"`
BlockID string `json:"block_id,omitempty"`
TitleURL string `json:"title_url,omitempty"`
AuthorName string `json:"author_name,omitempty"`
ProviderName string `json:"provider_name,omitempty"`
ProviderIconURL string `json:"provider_icon_url,omitempty"`
Description *TextBlockObject `json:"description,omitempty"`
}
// BlockType returns the type of the block
func (s VideoBlock) BlockType() MessageBlockType {
return s.Type
}
// NewVideoBlock returns an instance of a new Video Block type
func NewVideoBlock(videoURL, thumbnailURL, altText, blockID string, title *TextBlockObject) *VideoBlock {
return &VideoBlock{
Type: MBTVideo,
VideoURL: videoURL,
ThumbnailURL: thumbnailURL,
AltText: altText,
BlockID: blockID,
Title: title,
}
}
// WithAuthorName sets the author name for the VideoBlock
func (s *VideoBlock) WithAuthorName(authorName string) *VideoBlock {
s.AuthorName = authorName
return s
}
// WithTitleURL sets the title URL for the VideoBlock
func (s *VideoBlock) WithTitleURL(titleURL string) *VideoBlock {
s.TitleURL = titleURL
return s
}
// WithDescription sets the description for the VideoBlock
func (s *VideoBlock) WithDescription(description *TextBlockObject) *VideoBlock {
s.Description = description
return s
}
// WithProviderIconURL sets the provider icon URL for the VideoBlock
func (s *VideoBlock) WithProviderIconURL(providerIconURL string) *VideoBlock {
s.ProviderIconURL = providerIconURL
return s
}
// WithProviderName sets the provider name for the VideoBlock
func (s *VideoBlock) WithProviderName(providerName string) *VideoBlock {
s.ProviderName = providerName
return s
}

View File

@@ -35,19 +35,28 @@ func (api *Client) botRequest(ctx context.Context, path string, values url.Value
return response, nil
}
type GetBotInfoParameters struct {
Bot string
TeamID string
}
// GetBotInfo will retrieve the complete bot information
func (api *Client) GetBotInfo(bot string) (*Bot, error) {
return api.GetBotInfoContext(context.Background(), bot)
func (api *Client) GetBotInfo(parameters GetBotInfoParameters) (*Bot, error) {
return api.GetBotInfoContext(context.Background(), parameters)
}
// GetBotInfoContext will retrieve the complete bot information using a custom context
func (api *Client) GetBotInfoContext(ctx context.Context, bot string) (*Bot, error) {
func (api *Client) GetBotInfoContext(ctx context.Context, parameters GetBotInfoParameters) (*Bot, error) {
values := url.Values{
"token": {api.token},
}
if bot != "" {
values.Add("bot", bot)
if parameters.Bot != "" {
values.Add("bot", parameters.Bot)
}
if parameters.TeamID != "" {
values.Add("team_id", parameters.TeamID)
}
response, err := api.botRequest(ctx, "bots.info", values)

View File

@@ -7,6 +7,7 @@ import (
"io/ioutil"
"net/http"
"net/url"
"regexp"
"strconv"
"github.com/slack-go/slack/slackutilsx"
@@ -29,9 +30,9 @@ const (
type chatResponseFull struct {
Channel string `json:"channel"`
Timestamp string `json:"ts"` //Regular message timestamp
MessageTimeStamp string `json:"message_ts"` //Ephemeral message timestamp
ScheduledMessageID string `json:"scheduled_message_id,omitempty"` //Scheduled message id
Timestamp string `json:"ts"` // Regular message timestamp
MessageTimeStamp string `json:"message_ts"` // Ephemeral message timestamp
ScheduledMessageID string `json:"scheduled_message_id,omitempty"` // Scheduled message id
Text string `json:"text"`
SlackResponse
}
@@ -224,7 +225,7 @@ func (api *Client) SendMessageContext(ctx context.Context, channelID string, opt
return "", "", "", err
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody))
api.Debugf("Sending request: %s", string(reqBody))
api.Debugf("Sending request: %s", redactToken(reqBody))
}
if err = doPost(ctx, api.httpclient, req, parser(&response), api); err != nil {
@@ -234,6 +235,20 @@ func (api *Client) SendMessageContext(ctx context.Context, channelID string, opt
return response.Channel, response.getMessageTimestamp(), response.Text, response.Err()
}
func redactToken(b []byte) []byte {
// See https://api.slack.com/authentication/token-types
// and https://api.slack.com/authentication/rotation
re, err := regexp.Compile(`(token=x[a-z.]+)-[0-9A-Za-z-]+`)
if err != nil {
// The regular expression above should never result in errors,
// but just in case, do no harm.
return b
}
// Keep "token=" and the first element of the token, which identifies its type
// (this could be useful for debugging, e.g. when using a wrong token).
return re.ReplaceAll(b, []byte("$1-REDACTED"))
}
// 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.
@@ -681,6 +696,14 @@ func MsgOptionMetadata(metadata SlackMetadata) MsgOption {
}
}
// MsgOptionLinkNames finds and links user groups. Does not support linking individual users
func MsgOptionLinkNames(linkName bool) MsgOption {
return func(config *sendConfig) error {
config.values.Set("link_names", strconv.FormatBool(linkName))
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
@@ -784,6 +807,7 @@ func (api *Client) GetPermalinkContext(ctx context.Context, params *PermalinkPar
type GetScheduledMessagesParameters struct {
Channel string
TeamID string
Cursor string
Latest string
Limit int
@@ -803,6 +827,9 @@ func (api *Client) GetScheduledMessagesContext(ctx context.Context, params *GetS
if params.Channel != "" {
values.Add("channel", params.Channel)
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.Cursor != "" {
values.Add("cursor", params.Cursor)
}

View File

@@ -22,6 +22,7 @@ type Conversation struct {
IsIM bool `json:"is_im"`
IsExtShared bool `json:"is_ext_shared"`
IsOrgShared bool `json:"is_org_shared"`
IsGlobalShared bool `json:"is_global_shared"`
IsPendingExtShared bool `json:"is_pending_ext_shared"`
IsPrivate bool `json:"is_private"`
IsMpIM bool `json:"is_mpim"`
@@ -30,6 +31,9 @@ type Conversation struct {
NumMembers int `json:"num_members"`
Priority float64 `json:"priority"`
User string `json:"user"`
ConnectedTeamIDs []string `json:"connected_team_ids,omitempty"`
SharedTeamIDs []string `json:"shared_team_ids,omitempty"`
InternalTeamIDs []string `json:"internal_team_ids,omitempty"`
// TODO support pending_shared
// TODO support previous_names
@@ -296,6 +300,53 @@ func (api *Client) InviteUsersToConversationContext(ctx context.Context, channel
return response.Channel, response.Err()
}
// InviteSharedEmailsToConversation invites users to a shared channels by email
func (api *Client) InviteSharedEmailsToConversation(channelID string, emails ...string) (string, bool, error) {
return api.inviteSharedToConversationHelper(context.Background(), channelID, emails, nil)
}
// InviteSharedEmailsToConversationContext invites users to a shared channels by email using context
func (api *Client) InviteSharedEmailsToConversationContext(ctx context.Context, channelID string, emails ...string) (string, bool, error) {
return api.inviteSharedToConversationHelper(ctx, channelID, emails, nil)
}
// InviteSharedUserIDsToConversation invites users to a shared channels by user id
func (api *Client) InviteSharedUserIDsToConversation(channelID string, userIDs ...string) (string, bool, error) {
return api.inviteSharedToConversationHelper(context.Background(), channelID, nil, userIDs)
}
// InviteSharedUserIDsToConversationContext invites users to a shared channels by user id with context
func (api *Client) InviteSharedUserIDsToConversationContext(ctx context.Context, channelID string, userIDs ...string) (string, bool, error) {
return api.inviteSharedToConversationHelper(ctx, channelID, nil, userIDs)
}
// inviteSharedToConversationHelper invites emails or userIDs to a channel with a custom context.
// This is a helper function for InviteSharedEmailsToConversation and InviteSharedUserIDsToConversation.
// It accepts either emails or userIDs, but not both.
func (api *Client) inviteSharedToConversationHelper(ctx context.Context, channelID string, emails []string, userIDs []string) (string, bool, error) {
values := url.Values{
"token": {api.token},
"channel": {channelID},
}
if len(emails) > 0 {
values.Add("emails", strings.Join(emails, ","))
} else if len(userIDs) > 0 {
values.Add("user_ids", strings.Join(userIDs, ","))
}
response := struct {
SlackResponse
InviteID string `json:"invite_id"`
IsLegacySharedChannel bool `json:"is_legacy_shared_channel"`
}{}
err := api.postMethod(ctx, "conversations.inviteShared", values, &response)
if err != nil {
return "", false, err
}
return response.InviteID, response.IsLegacySharedChannel, 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)

View File

@@ -129,6 +129,7 @@ type FileUploadParameters struct {
type GetFilesParameters struct {
User string
Channel string
TeamID string
TimestampFrom JSONTime
TimestampTo JSONTime
Types string
@@ -142,6 +143,7 @@ type ListFilesParameters struct {
Limit int
User string
Channel string
TeamID string
Types string
Cursor string
}
@@ -253,12 +255,12 @@ func (api *Client) GetFileInfoContext(ctx context.Context, fileID string, count,
return &response.File, response.Comments, &response.Paging, nil
}
// GetFile retreives a given file from its private download URL
// GetFile retrieves a given file from its private download URL
func (api *Client) GetFile(downloadURL string, writer io.Writer) error {
return api.GetFileContext(context.Background(), downloadURL, writer)
}
// GetFileContext retreives a given file from its private download URL with a custom context
// GetFileContext retrieves a given file from its private download URL with a custom context
//
// For more details, see GetFile documentation.
func (api *Client) GetFileContext(ctx context.Context, downloadURL string, writer io.Writer) error {
@@ -281,6 +283,9 @@ func (api *Client) GetFilesContext(ctx context.Context, params GetFilesParameter
if params.Channel != DEFAULT_FILES_CHANNEL {
values.Add("channel", params.Channel)
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.TimestampFrom != DEFAULT_FILES_TS_FROM {
values.Add("ts_from", strconv.FormatInt(int64(params.TimestampFrom), 10))
}
@@ -326,6 +331,9 @@ func (api *Client) ListFilesContext(ctx context.Context, params ListFilesParamet
if params.Channel != DEFAULT_FILES_CHANNEL {
values.Add("channel", params.Channel)
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.Limit != DEFAULT_FILES_COUNT {
values.Add("limit", strconv.Itoa(params.Limit))
}

View File

@@ -33,29 +33,30 @@ const (
// 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"`
View View `json:"view"`
ActionID string `json:"action_id"`
APIAppID string `json:"api_app_id"`
BlockID string `json:"block_id"`
Container Container `json:"container"`
Enterprise Enterprise `json:"enterprise"`
WorkflowStep InteractionWorkflowStep `json:"workflow_step"`
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"`
View View `json:"view"`
ActionID string `json:"action_id"`
APIAppID string `json:"api_app_id"`
BlockID string `json:"block_id"`
Container Container `json:"container"`
Enterprise Enterprise `json:"enterprise"`
IsEnterpriseInstall bool `json:"is_enterprise_install"`
WorkflowStep InteractionWorkflowStep `json:"workflow_step"`
DialogSubmissionCallback
ViewSubmissionCallback
ViewClosedCallback

287
vendor/github.com/slack-go/slack/manifests.go generated vendored Normal file
View File

@@ -0,0 +1,287 @@
package slack
import (
"context"
"encoding/json"
"net/url"
)
// Manifest is an application manifest schema
type Manifest struct {
Metadata ManifestMetadata `json:"_metadata,omitempty" yaml:"_metadata,omitempty"`
Display Display `json:"display_information" yaml:"display_information"`
Settings Settings `json:"settings,omitempty" yaml:"settings,omitempty"`
Features Features `json:"features,omitempty" yaml:"features,omitempty"`
OAuthConfig OAuthConfig `json:"oauth_config,omitempty" yaml:"oauth_config,omitempty"`
}
// CreateManifest creates an app from an app manifest
func (api *Client) CreateManifest(manifest *Manifest, token string) (*ManifestResponse, error) {
return api.CreateManifestContext(context.Background(), manifest, token)
}
// CreateManifestContext creates an app from an app manifest with a custom context
func (api *Client) CreateManifestContext(ctx context.Context, manifest *Manifest, token string) (*ManifestResponse, error) {
if token == "" {
token = api.configToken
}
jsonBytes, err := json.Marshal(manifest)
if err != nil {
return nil, err
}
values := url.Values{
"token": {token},
"manifest": {string(jsonBytes)},
}
response := &ManifestResponse{}
err = api.postMethod(ctx, "apps.manifest.create", values, response)
if err != nil {
return nil, err
}
return response, response.Err()
}
// DeleteManifest permanently deletes an app created through app manifests
func (api *Client) DeleteManifest(token string, appId string) (*SlackResponse, error) {
return api.DeleteManifestContext(context.Background(), token, appId)
}
// DeleteManifestContext permanently deletes an app created through app manifests with a custom context
func (api *Client) DeleteManifestContext(ctx context.Context, token string, appId string) (*SlackResponse, error) {
if token == "" {
token = api.configToken
}
values := url.Values{
"token": {token},
"app_id": {appId},
}
response := &SlackResponse{}
err := api.postMethod(ctx, "apps.manifest.delete", values, response)
if err != nil {
return nil, err
}
return response, response.Err()
}
// ExportManifest exports an app manifest from an existing app
func (api *Client) ExportManifest(token string, appId string) (*Manifest, error) {
return api.ExportManifestContext(context.Background(), token, appId)
}
// ExportManifestContext exports an app manifest from an existing app with a custom context
func (api *Client) ExportManifestContext(ctx context.Context, token string, appId string) (*Manifest, error) {
if token == "" {
token = api.configToken
}
values := url.Values{
"token": {token},
"app_id": {appId},
}
response := &ExportManifestResponse{}
err := api.postMethod(ctx, "apps.manifest.export", values, response)
if err != nil {
return nil, err
}
return &response.Manifest, response.Err()
}
// UpdateManifest updates an app from an app manifest
func (api *Client) UpdateManifest(manifest *Manifest, token string, appId string) (*UpdateManifestResponse, error) {
return api.UpdateManifestContext(context.Background(), manifest, token, appId)
}
// UpdateManifestContext updates an app from an app manifest with a custom context
func (api *Client) UpdateManifestContext(ctx context.Context, manifest *Manifest, token string, appId string) (*UpdateManifestResponse, error) {
if token == "" {
token = api.configToken
}
jsonBytes, err := json.Marshal(manifest)
if err != nil {
return nil, err
}
values := url.Values{
"token": {token},
"app_id": {appId},
"manifest": {string(jsonBytes)},
}
response := &UpdateManifestResponse{}
err = api.postMethod(ctx, "apps.manifest.update", values, response)
if err != nil {
return nil, err
}
return response, response.Err()
}
// ValidateManifest sends a request to apps.manifest.validate to validate your app manifest
func (api *Client) ValidateManifest(manifest *Manifest, token string, appId string) (*ManifestResponse, error) {
return api.ValidateManifestContext(context.Background(), manifest, token, appId)
}
// ValidateManifestContext sends a request to apps.manifest.validate to validate your app manifest with a custom context
func (api *Client) ValidateManifestContext(ctx context.Context, manifest *Manifest, token string, appId string) (*ManifestResponse, error) {
if token == "" {
token = api.configToken
}
// Marshal manifest into string
jsonBytes, err := json.Marshal(manifest)
if err != nil {
return nil, err
}
values := url.Values{
"token": {token},
"manifest": {string(jsonBytes)},
}
if appId != "" {
values.Add("app_id", appId)
}
response := &ManifestResponse{}
err = api.postMethod(ctx, "apps.manifest.validate", values, response)
if err != nil {
return nil, err
}
return response, response.Err()
}
// ManifestMetadata is a group of settings that describe the manifest
type ManifestMetadata struct {
MajorVersion int `json:"major_version,omitempty" yaml:"major_version,omitempty"`
MinorVersion int `json:"minor_version,omitempty" yaml:"minor_version,omitempty"`
}
// Display is a group of settings that describe parts of an app's appearance within Slack
type Display struct {
Name string `json:"name" yaml:"name"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
LongDescription string `json:"long_description,omitempty" yaml:"long_description,omitempty"`
BackgroundColor string `json:"background_color,omitempty" yaml:"background_color,omitempty"`
}
// Settings is a group of settings corresponding to the Settings section of the app config pages.
type Settings struct {
AllowedIPAddressRanges []string `json:"allowed_ip_address_ranges,omitempty" yaml:"allowed_ip_address_ranges,omitempty"`
EventSubscriptions EventSubscriptions `json:"event_subscriptions,omitempty" yaml:"event_subscriptions,omitempty"`
Interactivity Interactivity `json:"interactivity,omitempty" yaml:"interactivity,omitempty"`
OrgDeployEnabled bool `json:"org_deploy_enabled,omitempty" yaml:"org_deploy_enabled,omitempty"`
SocketModeEnabled bool `json:"socket_mode_enabled,omitempty" yaml:"socket_mode_enabled,omitempty"`
}
// EventSubscriptions is a group of settings that describe the Events API configuration
type EventSubscriptions struct {
RequestUrl string `json:"request_url,omitempty" yaml:"request_url,omitempty"`
BotEvents []string `json:"bot_events,omitempty" yaml:"bot_events,omitempty"`
UserEvents []string `json:"user_events,omitempty" yaml:"user_events,omitempty"`
}
// Interactivity is a group of settings that describe the interactivity configuration
type Interactivity struct {
IsEnabled bool `json:"is_enabled" yaml:"is_enabled"`
RequestUrl string `json:"request_url,omitempty" yaml:"request_url,omitempty"`
MessageMenuOptionsUrl string `json:"message_menu_options_url,omitempty" yaml:"message_menu_options_url,omitempty"`
}
// Features is a group of settings corresponding to the Features section of the app config pages
type Features struct {
AppHome AppHome `json:"app_home,omitempty" yaml:"app_home,omitempty"`
BotUser BotUser `json:"bot_user,omitempty" yaml:"bot_user,omitempty"`
Shortcuts []Shortcut `json:"shortcuts,omitempty" yaml:"shortcuts,omitempty"`
SlashCommands []ManifestSlashCommand `json:"slash_commands,omitempty" yaml:"slash_commands,omitempty"`
WorkflowSteps []WorkflowStep `json:"workflow_steps,omitempty" yaml:"workflow_steps,omitempty"`
}
// AppHome is a group of settings that describe the App Home configuration
type AppHome struct {
HomeTabEnabled bool `json:"home_tab_enabled,omitempty" yaml:"home_tab_enabled,omitempty"`
MessagesTabEnabled bool `json:"messages_tab_enabled,omitempty" yaml:"messages_tab_enabled,omitempty"`
MessagesTabReadOnlyEnabled bool `json:"messages_tab_read_only_enabled,omitempty" yaml:"messages_tab_read_only_enabled,omitempty"`
}
// BotUser is a group of settings that describe bot user configuration
type BotUser struct {
DisplayName string `json:"display_name" yaml:"display_name"`
AlwaysOnline bool `json:"always_online,omitempty" yaml:"always_online,omitempty"`
}
// Shortcut is a group of settings that describes shortcut configuration
type Shortcut struct {
Name string `json:"name" yaml:"name"`
CallbackID string `json:"callback_id" yaml:"callback_id"`
Description string `json:"description" yaml:"description"`
Type ShortcutType `json:"type" yaml:"type"`
}
// ShortcutType is a new string type for the available types of shortcuts
type ShortcutType string
const (
MessageShortcut ShortcutType = "message"
GlobalShortcut ShortcutType = "global"
)
// ManifestSlashCommand is a group of settings that describes slash command configuration
type ManifestSlashCommand struct {
Command string `json:"command" yaml:"command"`
Description string `json:"description" yaml:"description"`
ShouldEscape bool `json:"should_escape,omitempty" yaml:"should_escape,omitempty"`
Url string `json:"url,omitempty" yaml:"url,omitempty"`
UsageHint string `json:"usage_hint,omitempty" yaml:"usage_hint,omitempty"`
}
// WorkflowStep is a group of settings that describes workflow steps configuration
type WorkflowStep struct {
Name string `json:"name" yaml:"name"`
CallbackID string `json:"callback_id" yaml:"callback_id"`
}
// OAuthConfig is a group of settings that describe OAuth configuration for the app
type OAuthConfig struct {
RedirectUrls []string `json:"redirect_urls,omitempty" yaml:"redirect_urls,omitempty"`
Scopes OAuthScopes `json:"scopes,omitempty" yaml:"scopes,omitempty"`
}
// OAuthScopes is a group of settings that describe permission scopes configuration
type OAuthScopes struct {
Bot []string `json:"bot,omitempty" yaml:"bot,omitempty"`
User []string `json:"user,omitempty" yaml:"user,omitempty"`
}
// ManifestResponse is the response returned by the API for apps.manifest.x endpoints
type ManifestResponse struct {
Errors []ManifestValidationError `json:"errors,omitempty"`
SlackResponse
}
// ManifestValidationError is an error message returned for invalid manifests
type ManifestValidationError struct {
Message string `json:"message"`
Pointer string `json:"pointer"`
}
type ExportManifestResponse struct {
Manifest Manifest `json:"manifest,omitempty"`
SlackResponse
}
type UpdateManifestResponse struct {
AppId string `json:"app_id,omitempty"`
PermissionsUpdated bool `json:"permissions_updated,omitempty"`
ManifestResponse
}

View File

@@ -33,17 +33,18 @@ type OAuthResponse struct {
// OAuthV2Response ...
type OAuthV2Response struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
Scope string `json:"scope"`
BotUserID string `json:"bot_user_id"`
AppID string `json:"app_id"`
Team OAuthV2ResponseTeam `json:"team"`
IncomingWebhook OAuthResponseIncomingWebhook `json:"incoming_webhook"`
Enterprise OAuthV2ResponseEnterprise `json:"enterprise"`
AuthedUser OAuthV2ResponseAuthedUser `json:"authed_user"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int `json:"expires_in"`
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
Scope string `json:"scope"`
BotUserID string `json:"bot_user_id"`
AppID string `json:"app_id"`
Team OAuthV2ResponseTeam `json:"team"`
IncomingWebhook OAuthResponseIncomingWebhook `json:"incoming_webhook"`
Enterprise OAuthV2ResponseEnterprise `json:"enterprise"`
IsEnterpriseInstall bool `json:"is_enterprise_install"`
AuthedUser OAuthV2ResponseAuthedUser `json:"authed_user"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int `json:"expires_in"`
SlackResponse
}
@@ -69,6 +70,15 @@ type OAuthV2ResponseAuthedUser struct {
TokenType string `json:"token_type"`
}
// OpenIDConnectResponse ...
type OpenIDConnectResponse struct {
Ok bool `json:"ok"`
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
IdToken string `json:"id_token"`
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)
@@ -137,12 +147,12 @@ func GetOAuthV2ResponseContext(ctx context.Context, client httpClient, clientID,
return response, response.Err()
}
// RefreshOAuthV2AccessContext with a context, gets a V2 OAuth access token response
// RefreshOAuthV2Token with a context, gets a V2 OAuth access token response
func RefreshOAuthV2Token(client httpClient, clientID, clientSecret, refreshToken string) (resp *OAuthV2Response, err error) {
return RefreshOAuthV2TokenContext(context.Background(), client, clientID, clientSecret, refreshToken)
}
// RefreshOAuthV2AccessContext with a context, gets a V2 OAuth access token response
// RefreshOAuthV2TokenContext with a context, gets a V2 OAuth access token response
func RefreshOAuthV2TokenContext(ctx context.Context, client httpClient, clientID, clientSecret, refreshToken string) (resp *OAuthV2Response, err error) {
values := url.Values{
"client_id": {clientID},
@@ -156,3 +166,24 @@ func RefreshOAuthV2TokenContext(ctx context.Context, client httpClient, clientID
}
return response, response.Err()
}
// GetOpenIDConnectToken exchanges a temporary OAuth verifier code for an access token for Sign in with Slack.
// see: https://api.slack.com/methods/openid.connect.token
func GetOpenIDConnectToken(client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OpenIDConnectResponse, err error) {
return GetOpenIDConnectTokenContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
}
// GetOpenIDConnectTokenContext with a context, gets an access token for Sign in with Slack.
func GetOpenIDConnectTokenContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OpenIDConnectResponse, err error) {
values := url.Values{
"client_id": {clientID},
"client_secret": {clientSecret},
"code": {code},
"redirect_uri": {redirectURI},
}
response := &OpenIDConnectResponse{}
if err = postForm(ctx, client, APIURL+"openid.connect.token", values, response, discard{}); err != nil {
return nil, err
}
return response, response.Err()
}

View File

@@ -67,10 +67,11 @@ const (
// ListReactionsParameters is the inputs to find all reactions by a user.
type ListReactionsParameters struct {
User string
Count int
Page int
Full bool
User string
TeamID string
Count int
Page int
Full bool
}
// NewListReactionsParameters initializes the inputs to find all reactions
@@ -246,6 +247,9 @@ func (api *Client) ListReactionsContext(ctx context.Context, params ListReaction
if params.User != DEFAULT_REACTIONS_USER {
values.Add("user", params.User)
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.Count != DEFAULT_REACTIONS_COUNT {
values.Add("count", strconv.Itoa(params.Count))
}

View File

@@ -15,6 +15,7 @@ const (
)
type SearchParameters struct {
TeamID string
Sort string
SortDirection string
Highlight bool
@@ -93,6 +94,9 @@ func (api *Client) _search(ctx context.Context, path, query string, params Searc
"token": {api.token},
"query": {query},
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.Sort != DEFAULT_SEARCH_SORT {
values.Add("sort", params.Sort)
}

View File

@@ -57,12 +57,14 @@ type authTestResponseFull struct {
type ParamOption func(*url.Values)
type Client struct {
token string
appLevelToken string
endpoint string
debug bool
log ilogger
httpclient httpClient
token string
appLevelToken string
configToken string
configRefreshToken string
endpoint string
debug bool
log ilogger
httpclient httpClient
}
// Option defines an option for a Client
@@ -99,6 +101,16 @@ func OptionAppLevelToken(token string) func(*Client) {
return func(c *Client) { c.appLevelToken = token }
}
// OptionConfigToken sets a configuration token for the client.
func OptionConfigToken(token string) func(*Client) {
return func(c *Client) { c.configToken = token }
}
// OptionConfigRefreshToken sets a configuration refresh token for the client.
func OptionConfigRefreshToken(token string) func(*Client) {
return func(c *Client) { c.configRefreshToken = token }
}
// New builds a slack client from the provided token and options.
func New(token string, options ...Option) *Client {
s := &Client{

View File

@@ -1,25 +1,29 @@
package slack
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
)
// 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"`
APIAppID string `json:"api_app_id"`
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"`
IsEnterpriseInstall bool `json:"is_enterprise_install"`
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"`
APIAppID string `json:"api_app_id"`
}
// SlashCommandParse will parse the request of the slash command
@@ -32,6 +36,7 @@ func SlashCommandParse(r *http.Request) (s SlashCommand, err error) {
s.TeamDomain = r.PostForm.Get("team_domain")
s.EnterpriseID = r.PostForm.Get("enterprise_id")
s.EnterpriseName = r.PostForm.Get("enterprise_name")
s.IsEnterpriseInstall = r.PostForm.Get("is_enterprise_install") == "true"
s.ChannelID = r.PostForm.Get("channel_id")
s.ChannelName = r.PostForm.Get("channel_name")
s.UserID = r.PostForm.Get("user_id")
@@ -53,3 +58,34 @@ func (s SlashCommand) ValidateToken(verificationTokens ...string) bool {
}
return false
}
// UnmarshalJSON handles is_enterprise_install being either a boolean or a
// string when parsing JSON from various payloads
func (s *SlashCommand) UnmarshalJSON(data []byte) error {
type SlashCommandCopy SlashCommand
scopy := &struct {
*SlashCommandCopy
IsEnterpriseInstall interface{} `json:"is_enterprise_install"`
}{
SlashCommandCopy: (*SlashCommandCopy)(s),
}
if err := json.Unmarshal(data, scopy); err != nil {
return err
}
switch rawValue := scopy.IsEnterpriseInstall.(type) {
case string:
b, err := strconv.ParseBool(rawValue)
if err != nil {
return fmt.Errorf("parsing boolean for is_enterprise_install: %w", err)
}
s.IsEnterpriseInstall = b
case bool:
s.IsEnterpriseInstall = rawValue
default:
return fmt.Errorf("wrong data type for is_enterprise_install: %T", scopy.IsEnterpriseInstall)
}
return nil
}

View File

@@ -74,8 +74,9 @@ type BillingActive struct {
// AccessLogParameters contains all the parameters necessary (including the optional ones) for a GetAccessLogs() request
type AccessLogParameters struct {
Count int
Page int
TeamID string
Count int
Page int
}
// NewAccessLogParameters provides an instance of AccessLogParameters with all the sane default values set
@@ -164,22 +165,24 @@ func (api *Client) GetTeamInfoContext(ctx context.Context) (*TeamInfo, error) {
}
// GetTeamProfile gets the Team Profile settings of the user
func (api *Client) GetTeamProfile() (*TeamProfile, error) {
return api.GetTeamProfileContext(context.Background())
func (api *Client) GetTeamProfile(teamID ...string) (*TeamProfile, error) {
return api.GetTeamProfileContext(context.Background(), teamID...)
}
// GetTeamProfileContext gets the Team Profile settings of the user with a custom context
func (api *Client) GetTeamProfileContext(ctx context.Context) (*TeamProfile, error) {
func (api *Client) GetTeamProfileContext(ctx context.Context, teamID ...string) (*TeamProfile, error) {
values := url.Values{
"token": {api.token},
}
if len(teamID) > 0 {
values["team_id"] = teamID
}
response, err := api.teamProfileRequest(ctx, api.httpclient, "team.profile.get", values)
if err != nil {
return nil, err
}
return &response.Profile, nil
}
// GetAccessLogs retrieves a page of logins according to the parameters given
@@ -192,6 +195,9 @@ func (api *Client) GetAccessLogsContext(ctx context.Context, params AccessLogPar
values := url.Values{
"token": {api.token},
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.Count != DEFAULT_LOGINS_COUNT {
values.Add("count", strconv.Itoa(params.Count))
}
@@ -206,31 +212,29 @@ func (api *Client) GetAccessLogsContext(ctx context.Context, params AccessLogPar
return response.Logins, &response.Paging, nil
}
type GetBillableInfoParams struct {
User string
TeamID string
}
// GetBillableInfo ...
func (api *Client) GetBillableInfo(user string) (map[string]BillingActive, error) {
return api.GetBillableInfoContext(context.Background(), user)
func (api *Client) GetBillableInfo(params GetBillableInfoParams) (map[string]BillingActive, error) {
return api.GetBillableInfoContext(context.Background(), params)
}
// 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) {
func (api *Client) GetBillableInfoContext(ctx context.Context, params GetBillableInfoParams) (map[string]BillingActive, error) {
values := url.Values{
"token": {api.token},
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.User != "" {
values.Add("user", params.User)
}
return api.billableInfoRequest(ctx, "team.billableInfo", values)
}

50
vendor/github.com/slack-go/slack/tokens.go generated vendored Normal file
View File

@@ -0,0 +1,50 @@
package slack
import (
"context"
"net/url"
)
// RotateTokens exchanges a refresh token for a new app configuration token
func (api *Client) RotateTokens(configToken string, refreshToken string) (*TokenResponse, error) {
return api.RotateTokensContext(context.Background(), configToken, refreshToken)
}
// RotateTokensContext exchanges a refresh token for a new app configuration token with a custom context
func (api *Client) RotateTokensContext(ctx context.Context, configToken string, refreshToken string) (*TokenResponse, error) {
if configToken == "" {
configToken = api.configToken
}
if refreshToken == "" {
refreshToken = api.configRefreshToken
}
values := url.Values{
"refresh_token": {refreshToken},
}
response := &TokenResponse{}
err := api.getMethod(ctx, "tooling.tokens.rotate", configToken, values, response)
if err != nil {
return nil, err
}
return response, response.Err()
}
// UpdateConfigTokens replaces the configuration tokens in the client with those returned by the API
func (api *Client) UpdateConfigTokens(response *TokenResponse) {
api.configToken = response.Token
api.configRefreshToken = response.RefreshToken
}
type TokenResponse struct {
Token string `json:"token,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
TeamId string `json:"team_id,omitempty"`
UserId string `json:"user_id,omitempty"`
IssuedAt uint64 `json:"iat,omitempty"`
ExpiresAt uint64 `json:"exp,omitempty"`
SlackResponse
}

View File

@@ -62,6 +62,10 @@ func (api *Client) CreateUserGroupContext(ctx context.Context, userGroup UserGro
"name": {userGroup.Name},
}
if userGroup.TeamID != "" {
values["team_id"] = []string{userGroup.TeamID}
}
if userGroup.Handle != "" {
values["handle"] = []string{userGroup.Handle}
}
@@ -122,6 +126,12 @@ func (api *Client) EnableUserGroupContext(ctx context.Context, userGroup string)
// GetUserGroupsOption options for the GetUserGroups method call.
type GetUserGroupsOption func(*GetUserGroupsParams)
func GetUserGroupsOptionWithTeamID(teamID string) GetUserGroupsOption {
return func(params *GetUserGroupsParams) {
params.TeamID = teamID
}
}
// GetUserGroupsOptionIncludeCount include the number of users in each User Group (default: false)
func GetUserGroupsOptionIncludeCount(b bool) GetUserGroupsOption {
return func(params *GetUserGroupsParams) {
@@ -145,6 +155,7 @@ func GetUserGroupsOptionIncludeUsers(b bool) GetUserGroupsOption {
// GetUserGroupsParams contains arguments for GetUserGroups method call
type GetUserGroupsParams struct {
TeamID string
IncludeCount bool
IncludeDisabled bool
IncludeUsers bool
@@ -166,6 +177,9 @@ func (api *Client) GetUserGroupsContext(ctx context.Context, options ...GetUserG
values := url.Values{
"token": {api.token},
}
if params.TeamID != "" {
values.Add("team_id", params.TeamID)
}
if params.IncludeCount {
values.Add("include_count", "true")
}

View File

@@ -17,31 +17,32 @@ const (
// UserProfile contains all the information details of a given user
type UserProfile struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
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"`
AvatarHash string `json:"avatar_hash"`
Email string `json:"email,omitempty"`
Skype string `json:"skyp,omitempty"`
Phone string `json:"phone,omitempty"`
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"`
ImageOriginal string `json:"image_original"`
Title string `json:"title"`
ImageOriginal string `json:"image_original,omitempty"`
Title string `json:"title,omitempty"`
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"`
StatusEmojiDisplayInfo []UserProfileStatusEmojiDisplayInfo `json:"status_emoji_display_info,omitempty"`
StatusExpiration int `json:"status_expiration"`
StatusExpiration int `json:"status_expiration,omitempty"`
Team string `json:"team"`
Fields UserProfileCustomFields `json:"fields"`
Fields UserProfileCustomFields `json:"fields,omitempty"`
}
type UserProfileStatusEmojiDisplayInfo struct {