Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ Prometheus is a Slack bot built with `@slack/bolt` in Socket Mode. It runs via `
- `isWorkspaceAdmin` — Slack API check (`users.info`)
- `isChannelManager` — `appointed_managers` with `role = 'manager'`
- `isChannelModerator` — any role in `appointed_managers` (manager or moderator)
- `isSlackChannelManager` — Slack's native Channel Manager role (Enterprise Grid), via `getChannelManagers` in `lib/moderation.js` (undocumented `admin.roles.entity.listAssignments`). Uses the same enterprise moderation creds (`SLACK_BROWSER_TOKEN`/`SLACK_COOKIE`); no-ops (`false`) when those aren't configured
- `canManage` — globalAdmin OR channelManager (for delete/destroy/welcome)
- `canBan` — globalAdmin OR channelModerator (for ban/unban/@here)
- `canBan` — globalAdmin OR channelModerator OR isSlackChannelManager (for ban/unban/@here/@channel)
- `canAnchor` — `canManage` OR workspaceAdmin
- `SUPERADMINS` grants access to the `/pro admin` command; it does not automatically insert rows into `global_admins`

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Prometheus is a Slack bot that lets community members take responsibility for ke
- **Workspace admin**: inherited from Slack
- **Channel manager**: appointed per-channel; can delete, destroy, set welcome messages
- **Channel moderator**: appointed per-channel; can timeout, @here, @channel
- **Slack Channel Manager**: Slack's own native per-channel role (Enterprise Grid); can @here, @channel. Read via the enterprise moderation creds — inert without `SLACK_BROWSER_TOKEN`/`SLACK_COOKIE`

## Web API

Expand Down
12 changes: 12 additions & 0 deletions lib/moderation.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,15 @@ export async function deleteAttachment(channel, ts, attachment) {
attachment,
});
}

export async function getChannelManagers(channelId) {
if (!areWeEnterprise) return [];
try {
const json = await moderationAPI("admin.roles.entity.listAssignments", {
entity_id: channelId,
});
return json.role_assignments?.[0]?.users || [];
} catch {
return [];
}
}
8 changes: 7 additions & 1 deletion lib/perms.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import {
hasChannelRole as dbHasChannelRole,
isAppointedManager as dbIsAppointedManager,
} from "./db.js";
import { getChannelManagers } from "./moderation.js";

export { isGlobalAdmin };

export const isSlackChannelManager = async (userId, channelId) =>
(await getChannelManagers(channelId)).includes(userId);

export const isWorkspaceAdmin = async (client, userId) => {
try {
const r = await client.users.info({ user: userId });
Expand All @@ -25,7 +29,9 @@ export const isChannelManager = (_client, userId, channelId) =>

// timeout, untimeout, @here, @channel
export const canBan = async (client, userId, channelId) =>
(await isGlobalAdmin(userId)) || (await dbHasChannelRole(userId, channelId));
(await isGlobalAdmin(userId)) ||
(await dbHasChannelRole(userId, channelId)) ||
(await isSlackChannelManager(userId, channelId));

// delete, destroy thread, welcome
export const canManage = async (client, userId, channelId) =>
Expand Down