Skip to content

Commit c85cb79

Browse files
authored
Merge pull request #5 from jamietsao/multi-user-support
Add support for inviting multiple users at once
2 parents 5a19c3b + 4b230eb commit c85cb79

2 files changed

Lines changed: 34 additions & 33 deletions

File tree

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# slack-multi-channel-invite
2-
Have you ever googled `"slack invite user to multiple channels"`? Yeah, me too. I do this every time a new engineer joins my team, and inevitably end up inviting said engineer to each Slack channel manually. I got tired of this, so I rolled up my sleeves and whipped up this script in a couple of hours.
2+
Have you ever googled `"slack invite user to multiple channels"`? Yeah, me too. I do this every time a new engineer joins my team, and I inevitably end up inviting said engineer to each Slack channel manually. I got tired of this, so I rolled up my sleeves and whipped up this script.
33

4-
I assume Slack will eventually add this ability. Until then, hopefully you can save some time by using this script.
4+
I assume Slack will eventually add this ability. Until then, hopefully you can save some time by using this.
55

66
Enjoy!
77

@@ -20,16 +20,16 @@ Enjoy!
2020
- Else download the binary directly: https://github.com/jamietsao/slack-multi-channel-invite/releases
2121
5. Run script:
2222

23-
`slack-multi-channel-invite -api_token=<oauth-access-token> -channels=foo,bar,baz -user_email=steph@curry.com -private=<true|false>`
23+
`slack-multi-channel-invite -api_token=<oauth-access-token> -emails=steph@warriors.com,klay@warriors.com -channels=dubnation,splashbrothers,thetown -private=<true|false>`
2424

25-
The user with email `steph@curry.com` should be invited to channels `foo`, `bar`, and `baz`!
25+
The users with emails `steph@warriors.com` and `klay@warriors.com` should be invited to channels `dubnation`, `splashbrothers`, and `thetown`!
2626

2727
_* Set `private` flag to `true` if you want to invite users to private channels. As noted above, this will require the additional permission scopes of `groups:read` and `groups:write`_
2828

2929
## Implementation
30-
Initially, I figured this script would be a simple loop that invoked some API to invite a user to a channel. It turns out this API endpoint ([`conversations.invite`](https://api.slack.com/methods/conversations.invite)) expects the user ID (instead of username) and channel ID (instead of channel name). Problem is, it's not very straightforward to get user and channel IDs. There isn't a way to lookup a user by username (only by email). And there's no way to look up a single channel, unless you have the channel ID already (chicken and egg).
30+
Initially, I figured this script would be a simple loop that invoked some API to invite users to a channel. It turns out this API endpoint ([`conversations.invite`](https://api.slack.com/methods/conversations.invite)) expects the user ID (instead of username) and channel ID (instead of channel name). Problem is, it's not very straightforward to get user and channel IDs. There isn't a way to lookup a user by username (only by email). And there's no way to look up a single channel, unless you have the channel ID already (chicken and egg).
3131

3232
For these reasons, I wrote the script like so:
33-
1. [Look up](https://api.slack.com/methods/users.lookupByEmail) the Slack user ID by email.
33+
1. [Look up](https://api.slack.com/methods/users.lookupByEmail) Slack user IDs for all given emails.
3434
2. [Query](https://api.slack.com/methods/conversations.list) all public (or private) channels in the workspace and create a name -> ID mapping.
35-
3. For each of the given channels, [invite](https://api.slack.com/methods/conversations.invite) the user to the channel using the user ID and channel ID from steps 1 & 2.
35+
3. For each of the given channels, [invite](https://api.slack.com/methods/conversations.invite) the users to the channel using the user IDs and channel ID from steps 1 & 2.

main.go

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -58,39 +58,43 @@ type (
5858
}
5959
)
6060

61-
// This script invites the given user to the given list of channels on Slack.
61+
// This script invites the given users to the given channels on Slack.
6262
// Due to the oddness of the Slack API, this is accomplished via these steps:
63-
// 1) Look up the Slack user ID by email
63+
// 1) Look up Slack user IDs by email
6464
// 2) Query all public (private if 'private' flag is set to true) channels in the workspace and create a name -> ID mapping
65-
// 3) For each of the given channels, invite the user (user ID) to the channel (channel ID)
65+
// 3) For each of the given channels, invite the users (user IDs) to the channel (channel ID)
6666
func main() {
6767
var apiToken string
68-
var userEmail string
68+
var emails string
6969
var channelsArg string
7070
var private bool
7171
var debug bool
7272

7373
// parse flags
7474
flag.StringVar(&apiToken, "api_token", "", "Slack OAuth Access Token")
75-
flag.StringVar(&userEmail, "user_email", "", "Email of Slack user to invite")
76-
flag.StringVar(&channelsArg, "channels", "", "Comma separated list of channels to invite user to")
75+
flag.StringVar(&emails, "emails", "", "Comma separated list of Slack user emails to invite")
76+
flag.StringVar(&channelsArg, "channels", "", "Comma separated list of channels to invite users to")
7777
flag.BoolVar(&private, "private", false, "Boolean flag to enable private channel invitations (requires OAuth scopes 'groups:read' and 'groups:write')")
7878
flag.BoolVar(&debug, "debug", false, "Enables debug logging when set to true")
7979
flag.Parse()
8080

81-
if apiToken == "" || userEmail == "" || channelsArg == "" {
81+
if apiToken == "" || emails == "" || channelsArg == "" {
8282
flag.Usage()
8383
os.Exit(1)
8484
}
8585

86-
// lookup user by email
87-
userID, err := getUserID(apiToken, userEmail)
88-
if err != nil {
89-
panic(err)
90-
}
86+
// lookup users by email
87+
fmt.Printf("\nLooking up users ...\n")
88+
var userIDs []string
89+
for _, email := range strings.Split(emails, ",") {
90+
userID, err := getUserID(apiToken, email)
91+
if err != nil {
92+
fmt.Printf("Error while looking up user with email %s: %s\n", email, err)
93+
continue
94+
}
9195

92-
if debug {
93-
fmt.Printf("User ID for '%s': %s\n", userEmail, userID)
96+
fmt.Printf("Valid user (ID: %s) found for '%s'\n", userID, email)
97+
userIDs = append(userIDs, userID)
9498
}
9599

96100
// get all channels
@@ -100,10 +104,11 @@ func main() {
100104
}
101105

102106
if debug {
103-
fmt.Printf("Total # of channels retrieved: %d\n", len(channelNameToIDMap))
107+
fmt.Printf("DEBUG: Total # of channels retrieved: %d\n", len(channelNameToIDMap))
104108
}
105109

106-
// invite user to each channel
110+
// invite users to each channel
111+
fmt.Printf("\nInviting users to channels ...\n")
107112
channels := strings.Split(channelsArg, ",")
108113
for _, channel := range channels {
109114
channelID := channelNameToIDMap[channel]
@@ -112,17 +117,13 @@ func main() {
112117
continue
113118
}
114119

115-
if debug {
116-
fmt.Printf("Inviting user %s (%s) to channel %s (%s)\n", userEmail, userID, channel, channelID)
117-
}
118-
119-
err := inviteUserToChannel(apiToken, userID, channelID)
120+
err := inviteUsersToChannel(apiToken, userIDs, channelID)
120121
if err != nil {
121-
fmt.Printf("Error while inviting %s (%s) to %s (%s): %s\n", userEmail, userID, channel, channelID, err)
122+
fmt.Printf("Error while inviting users to %s (%s): %s\n", channel, channelID, err)
122123
continue
123124
}
124125

125-
fmt.Printf("User %s invited to '%s'\n", userEmail, channel)
126+
fmt.Printf("Users invited to '%s'\n", channel)
126127
}
127128

128129
fmt.Println("\nAll done! You're welcome =)")
@@ -198,7 +199,7 @@ func getChannels(apiToken string, private bool, debug bool) (map[string]string,
198199
}
199200

200201
if debug {
201-
fmt.Printf("# of channels returned in page: %d\n", len(data.Channels))
202+
fmt.Printf("DEBUG: # of channels returned in page: %d\n", len(data.Channels))
202203
}
203204

204205
// map of channel names to IDs
@@ -216,12 +217,12 @@ func getChannels(apiToken string, private bool, debug bool) (map[string]string,
216217
return nameToID, nil
217218
}
218219

219-
func inviteUserToChannel(apiToken, userID, channelID string) error {
220+
func inviteUsersToChannel(apiToken string, userIDs []string, channelID string) error {
220221
httpClient := &http.Client{}
221222

222223
reqBody, err := json.Marshal(conversationsInviteRequest{
223224
ChannelID: channelID,
224-
UserIDs: userID,
225+
UserIDs: strings.Join(userIDs, ","),
225226
})
226227
if err != nil {
227228
return err

0 commit comments

Comments
 (0)