Skip to content

Instantly share code, notes, and snippets.

@aripalo
Last active August 23, 2024 08:03
Show Gist options
  • Save aripalo/2e64623a65cc89b60dd45de5cdf50ebb to your computer and use it in GitHub Desktop.
Save aripalo/2e64623a65cc89b60dd45de5cdf50ebb to your computer and use it in GitHub Desktop.
Get users (and their email, first name and last name) from Slack channel

Usage

  1. You need to create a new Slack app with following scopes:

    • channels:read
    • groups:read
    • users:read
    • users:read.email
  2. Install it into a workspace

  3. Invite it into a channel: /invite @<app-name>

  4. Install npm i @slack/web-api

  5. Run the script (NodeJS v20+ required):

    export SLACK_TOKEN=<apps-bot-token> ./slack-channel-users.mjs --channel="<channel-id>"
#!/usr/bin/env node
import { parseArgs } from "node:util";
import assert from "node:assert/strict";
import { WebClient } from "@slack/web-api";
const token = process.env.SLACK_TOKEN;
assert(typeof token === "string", "SLACK_TOKEN missing");
const web = new WebClient(token);
/**
* Reads channel ID from `--channel` CLI option
* @returns {string}
*/
function getChannelFromArgs() {
const args = parseArgs({ options: { channel: { type: "string" } } });
assert(
typeof args.values.channel === "string",
"Channel CLI option is required"
);
return args.values.channel;
}
/**
* Asserts that the response is a valid ConversationsMembers
* @param {unknown} value
* @returns {asserts value is import("@slack/web-api").ConversationsMembersResponse}
*/
function assertMembersResponse(value) {
assert(value.ok, "API request failed");
assert(Array.isArray(value.members), "Members not found");
}
/**
* Get members of a channel
* @param {string} channel
* @returns {Promise<string[]>} list of member IDs
*/
async function getMembers(channel) {
/** @type {string[]} */
const members = [];
for await (const page of web.paginate("conversations.members", { channel })) {
assert(page.ok, "API request failed");
assert(Array.isArray(page.members), "Members not found");
members.push(...page.members);
}
return members;
}
/**
* Get single user
* @param {string} user
* @returns {Promise<Exclude<import("@slack/web-api").UsersInfoResponse["user"], undefined>>}
*/
async function getUser(user) {
const response = await web.users.info({ user });
assert(response.ok, "API request failed");
assert(typeof response.user !== "undefined", "User not found");
return response.user;
}
/**
* Describe user basic data
* @typedef {Object} UserBasicData
* @prop {string} email
* @prop {string} firstName
* @prop {string} lastName
*/
/**
* Parse User object from UsersInfoResponse
* @param {Exclude<import("@slack/web-api").UsersInfoResponse["user"], undefined>} user
* @returns {UserBasicData}
*/
function parseUserBasicData(user) {
assert(typeof user.profile !== "undefined", "Profile not found");
const { profile } = user;
return {
email: profile.email,
firstName: profile.first_name,
lastName: profile.last_name,
};
}
/**
* Main workflow
* @param {string} channelId
*/
async function main(channelId) {
const members = await getMembers(channelId);
const users = await Promise.all(members.map((member) => getUser(member)));
const usersBasicData = users.map(parseUserBasicData);
console.log(usersBasicData);
}
main(getChannelFromArgs()).catch(console.error);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment