No description
Find a file
2026-01-18 12:18:21 +08:00
.gitignore Add caching 2026-01-15 15:05:14 +08:00
app.py Formatting 2026-01-18 12:18:21 +08:00
config.json.example Initial version 2026-01-15 14:30:40 +08:00
moderator.py Initial version 2026-01-15 14:30:40 +08:00
portal_client.py Add caching 2026-01-15 15:05:14 +08:00
pyproject.toml Initial version 2026-01-15 14:30:40 +08:00
README.md Initial version 2026-01-15 14:30:40 +08:00
requirements.txt Initial version 2026-01-15 14:30:40 +08:00
slack_manifest.json Initial version 2026-01-15 14:30:40 +08:00
uv.lock Initial version 2026-01-15 14:30:40 +08:00

Slack Moderator Bot

A Slack bot that monitors configured channels and enforces posting permissions based on Portal user groups.

Requirements

  • Python 3.8+
  • Slack App with Socket Mode enabled
  • Portal API key with access to user mapping and group membership endpoints

Setup

  1. Install dependencies:

    Using uv (recommended):

    uv sync
    

    Or using pip:

    pip install -r requirements.txt
    
  2. Configure Slack App:

    • Create a Slack App at https://api.slack.com/apps using slack_manifest.json
    • Enable Socket Mode
    • Generate an App-Level Token with connections:write scope (this is your app_token)
    • Install the app to the workspace
      • Copy the Bot User OAuth Token (this is your bot_token, starts with xoxb-)
      • Copy the User OAuth Token (this is your user_token, starts with xoxp-)
  3. Get Portal API Key:

    • Log into the Portal as an admin
    • Navigate to /admin/api-keys
    • Generate a new API key
  4. Configure the bot:

    Edit config.json:

    {
      "slack": {
        "app_token": "xapp-...",
        "log_channel": "C1234567890"
      },
      "portal": {
        "base_url": "https://members.artifactory.org.au",
        "api_key": "your-api-key-here"
      },
      "channels": {
        "C1234567890": {
          "allowed_groups": ["portal.management_committee", "portal.moderators"],
          "message": "You don't have permission to post directly in this channel"
        },
        "C0987654321": {
          "allowed_groups": ["portal.members"],
          "message": "Custom message for this channel",
          "log_channel": "C123123"
        },
        "C123464354": {
          "allowed_groups": ["portal.members"]
        }
      }
    }
    
    • slack.app_token: Your Slack App-Level Token (starts with xapp-, used for Socket Mode connection)
    • slack.bot_token: Your Slack Bot User OAuth Token (starts with xoxb-, used for most API calls)
    • slack.user_token: Your Slack User OAuth Token (starts with xoxp-, required for deleting messages)
    • slack.log_channel: Channel ID where removal logs will be posted
    • portal.base_url: Base URL of the Portal API
    • portal.api_key: Your Portal API key
    • channels: Map of channel IDs to their configuration
      • allowed_groups: List of Portal groups allowed to post (user must be in at least one)
      • message: Optional custom message to send when message is removed (defaults to "You don't have permission to post directly in this channel. Use 'reply in thread' instead.")
      • log_channel: Optional channel ID to override the global log channel for this specific channel's removal logs
  5. Get Channel IDs:

    • In Slack, right-click on a channel → "View channel details"
    • Scroll to the bottom to find the Channel ID (starts with C)

Running

Start the bot:

Using uv:

uv run app.py

Or using python directly:

python app.py

The bot will connect to Slack via Socket Mode and begin monitoring configured channels.

How It Works

  1. Bot receives a message event from Slack
  2. Checks if the channel is configured for moderation
  3. Allows the message if:
    • It's from a bot
    • It's a threaded reply
    • The user is a member of at least one allowed group
  4. If the message is forbidden:
    • Deletes the message
    • Sends an ephemeral message to the user
    • DMs the removed message content to the user
    • Logs the removal to the configured log channel

Error Handling

  • If the Portal API is unavailable, errors are logged and messages are allowed (fail open)
  • If a user is not found in the Portal, the message is deleted (assume no permissions)
  • If logging to the log channel fails, the error is logged but the operation continues