Repository: slack-go/slack
Pull request #1283 (2024-04-26 11:52:54)

fix!: Expose `team_id` parameter for use with org-wide app
Fixes https://github.com/slack-go/slack/issues/1282


Patch:
diff --git a/.gitignore b/.gitignore
index ac6f3ee..027f4de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 *.test
 *~
 .idea/
+/vendor/
\ No newline at end of file
diff --git a/bots.go b/bots.go
index da21ba0..fbe5d25 100644
--- a/bots.go
+++ b/bots.go
@@ -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)
diff --git a/chat.go b/chat.go
index 2668b38..d043ef0 100644
--- a/chat.go
+++ b/chat.go
@@ -807,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
@@ -826,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)
 	}
diff --git a/examples/team/team.go b/examples/team/team.go
index 8d2fcdb..cba71aa 100644
--- a/examples/team/team.go
+++ b/examples/team/team.go
@@ -9,17 +9,16 @@ import (
 func main() {
 	api := slack.New("YOUR_TOKEN_HERE")
 	//Example for single user
-	billingActive, err := api.GetBillableInfo("U023BECGF")
+	billingActive, err := api.GetBillableInfo(slack.GetBillableInfoParams{User: "U023BECGF"})
 	if err != nil {
 		fmt.Printf("%s\n", err)
 		return
 	}
 	fmt.Printf("ID: U023BECGF, BillingActive: %v\n\n\n", billingActive["U023BECGF"])
 
-	//Example for team
-	billingActiveForTeam, _ := api.GetBillableInfoForTeam()
+	//Example for team. Note: passing empty TeamID just uses the current user team.
+	billingActiveForTeam, _ := api.GetBillableInfo(slack.GetBillableInfoParams{})
 	for id, value := range billingActiveForTeam {
 		fmt.Printf("ID: %v, BillingActive: %v\n", id, value)
 	}
-
 }
diff --git a/files.go b/files.go
index f724f54..b2b40d8 100644
--- a/files.go
+++ b/files.go
@@ -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
 }
@@ -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))
 	}
diff --git a/reactions.go b/reactions.go
index 2a9bd42..39aa5ba 100644
--- a/reactions.go
+++ b/reactions.go
@@ -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))
 	}
diff --git a/search.go b/search.go
index de6b40a..d27497a 100644
--- a/search.go
+++ b/search.go
@@ -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)
 	}
diff --git a/team.go b/team.go
index d21a1b6..40a0f00 100644
--- a/team.go
+++ b/team.go
@@ -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,30 +212,28 @@ 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) {
+func (api *Client) GetBillableInfoContext(ctx context.Context, params GetBillableInfoParams) (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())
-}
+	if params.TeamID != "" {
+		values.Add("team_id", params.TeamID)
+	}
 
-// GetBillableInfoForTeamContext returns the billing_active status of all users on the team with a custom context
-func (api *Client) GetBillableInfoForTeamContext(ctx context.Context) (map[string]BillingActive, error) {
-	values := url.Values{
-		"token": {api.token},
+	if params.User != "" {
+		values.Add("user", params.User)
 	}
 
 	return api.billableInfoRequest(ctx, "team.billableInfo", values)
diff --git a/usergroups.go b/usergroups.go
index c5d7a17..7244aba 100644
--- a/usergroups.go
+++ b/usergroups.go
@@ -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")
 	}


Test patch:
diff --git a/bots_test.go b/bots_test.go
index ce7f668..14a509e 100644
--- a/bots_test.go
+++ b/bots_test.go
@@ -29,7 +29,7 @@ func TestGetBotInfo(t *testing.T) {
 	once.Do(startServer)
 	api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/"))
 
-	bot, err := api.GetBotInfo("B02875YLA")
+	bot, err := api.GetBotInfo(GetBotInfoParameters{Bot: "B02875YLA"})
 	if err != nil {
 		t.Errorf("Unexpected error: %s", err)
 		return
diff --git a/slacktest/handlers_test.go b/slacktest/handlers_test.go
index dec4c99..8ccb704 100644
--- a/slacktest/handlers_test.go
+++ b/slacktest/handlers_test.go
@@ -117,7 +117,7 @@ func TestBotInfoHandler(t *testing.T) {
 	go s.Start()
 
 	client := slack.New("ABCDEFG", slack.OptionAPIURL(s.GetAPIURL()))
-	bot, err := client.GetBotInfo(s.BotID)
+	bot, err := client.GetBotInfo(slack.GetBotInfoParameters{Bot: s.BotID})
 	assert.NoError(t, err)
 	assert.Equal(t, s.BotID, bot.ID)
 	assert.Equal(t, s.BotName, bot.Name)