Skip to content
This repository has been archived by the owner on Jul 11, 2019. It is now read-only.
/ slack-moderator Public archive
forked from foqal/slack-moderator

Moderation for Slack communities is hard. Here is a bot to help

License

Notifications You must be signed in to change notification settings

hangops/slack-moderator

 
 

Repository files navigation

Slack Moderator

As Slack is taking over for IRC as the community default platform, and because Slack isn't really designed as a community app, we need some basic moderation features. This project is to help create basic moderation rules to help control bad actors, and stop spam.

Since we dont want the bad actors to have the signals that we look for, we create a configurable interface which allows you to create rules and checks that apply to each message or event from Slack. The rules themselves are hidden away but accessible if you contact us. Ultimately this is just a processor that runs configs and speaks Slack.

Installing + Running

Bot

To install simply run:

$ npm i -g slack-moderator

Once its installed you can run to see the basic options.

$ slack-moderator --help

You will need to create 2 config files for this to run - (Rules Config and General Config once that is done, you can simply run the bot like:

$ slack-moderator --config [PATH_TO_CONFIG] --rules-config [PATH_TO_RULES_CONFIG]

Slack

To have the Slack moderator be able to work, you will need to create a Slack Bot and point it at the Slack Moderator bot. I will not cover this, but this will require hosting the Slack Moderator app somewhere and creating a public url to it.

  1. Go to the Apps dashboard in Slack - https://api.slack.com/apps and create a new App. Giving it the name you want to use.
  2. Under the Basic Information tab, you will want to store the verification token. This will be used in General Config.
  3. Click on Event Subscriptions and and click "On" to enable event subscriptions. 3.1 In the Request URL, enter whatever url you are exposing the bot as. The path should be /slack/hook. For example, if the external URL of your bot is "https://my-host.aws.com" the request URL should be "https://my-host.aws.com/slack/hook". 3.2 Once you change, it Slack will automatically test that endpoint. Make sure your bot is running and this will work. 3.3 Subscribe to events that you want to monitor. This will depend on the rules you want to run. For example, if you want to simply see all public channel messages, subscribe to "message.channels". If you want to disable file sharing, add "file_shared". 3.4 Save Changes at the bottom.
  4. Under Bot Users - click "Add a Bot User". Here you will need to give it a display name and username then click Save Changes
  5. Install the app to your workspace. You will then get a "But User OAuth Access Token" which you will use in Rules Config.
  6. Apply the stored tokens to your Rules Config and General Config and restart the Slack Moderator Bot.

Config

Most of the config is pretty simple however the rules also contain the info for the attack vectors. For some rules contact us and we will give you a base to work with.

General Config

This is the default config. You can configure it however you want but you will need to add the verification token you got from step 2 in Installing Slack

exports.default = {
    rules: "rules",
    server: {
        host: "0.0.0.0",
        port: 3000
    },
    slack: {
        verificationToken: "[VERIFICATION_TOKEN]",
    },
    logging: {
        appName: "moderator",
        stdoutLevel: "info",
        errorPath: "/var/tmp/moderator-error.log",
        requestLevels: {
            default: "info",
            500: "error",
            404: "warn"
        },
        color: true
    },
};

Rules Config

The rules file consists of 3 sections and like the general config is a javascript file.

So lets say you wanted to create a rule that deletes a message from any user that writes potato. It would look something like:

exports.default = {
    users: {
        "admin-user": {
            "token": "[USER_TOKEN_WITH_ADMIN_PERMISSIONS]"
        }
    },
    actions: {
        "delete-current-message": {
            type: "delete-message",
            user: "admin-user"
        }
    },
    rules: {
        "remove-text-potato": {
            "description": "Removes the message if a user writes potato",
            if: {
                type: "and",
                rules: [
                    {
                        field: "type",
                        type: "equal",
                        value: "message"
                    },
                    {
                        field: "text",
                        type: "token-equals",
                        value: "potato"
                    },
                ]
            },
            actions: [
                "delete-current-message"
            ]
        }
    }
}

The general pattern is:

exports.default = {
    users: {
        "[user-name]": {
            "token": "[xoxb-slack-user-or-bot-token]"
        }
    },
    actions: {
        "[action-name]": {
            type: "[action-type]",
            ... other options here
        }
    },
    rules: {
        "[rule-name]": {
            "description": "Friendly description to describe what the rule does",
            if: {
                ...conditions
            },
            actions: [
                "[action-name]"
            ]
        }
    }
}

Users

These are the users you want to perform certain actions on behalf of. The only thing we need here is the token used to act on behalf of this user.

...
users: {
    "my-user": {
        "token": "[xoxb-slack-user-or-bot-token]"
    }
},
...

Actions

This is a block where you can configure certain actions to run and use them in the rules as triggers. For example,

...
actions: {
    "my-action": {
        type: "notify",
        channel: "[my-admin-channel]",
        message: "User <@${user}> wrote 'potato' messages on channel <#${channel}>.",
        user: "my-user"
    }
},
...

There are currently a few actions we support:

Notify

This action will send a message to the specified channel with the specified message. There are a few strings that are able to be replaced into the message to let the channel know what happened.

Fields:

field description
channel The channel ID to use. The easiest way to find this is looking at the URL when in a slack channel. For example in https://my-slack.slack.com/messages/C1111111 C1111111 is the channel ID.
message The message to write to the channel. Currently you can use ${user} and ${channel} to replace the raw channel and user id.
user This is the user to run the command as. This should match to a user registered in the Users Section

Example:

...
"my-action": {
    type: "notify",
    channel: "[my-admin-channel]", 
    message: "User <@${user}> wrote 'potato' messages on channel <#${channel}>.",
    user: "my-user"
}
...

delete-message

This action will delete the message send by the user.

Fields:

field description
user This is the user to run the command as. This should match to a user registered in the Users Section. This user has to have admin permissions.

Example:

...
"my-action": {
    type: "delete-message",
    user: "my-user"
}
...

block-user

This action will block the current user.

Fields:

field description
user This is the user to run the command as. This should match to a user registered in the Users Section. This user has to have admin permissions.

Example:

...
"my-action": {
    type: "delete-message",
    user: "my-user"
}
...

Rules

The rules are the actual checks which you want to run against the messages that users write and actions that happen. The general pattern is:

...
rules: {
    "[rule-name]": {
        "description": "Friendly description to describe what the rule does",
        if: {
            ...conditions
        },
        actions: [
            "[action-name]"
        ]
    }
}
...

Looking at this example, you will see that a rule has a name and 3 sections. The name will be used only in logs but has to be unique.

field description
description This is just a description used to define what this rule does.
if This is the object containing the condition to match for this rule to trigger. There one root condition but can be replaced with a "and" or "or" condtion to specifiy multiple rules. (Conditions)
actions This a list of string action names referring to actions registered in the Actions Block

Conditions

There are obviously a few conditions to help us create a rule to validate an action starting with very simple ones to very complex conditions.

and

This is a meta condition and will resolve to true if all the child rules also resolve to true.

Fields:

field description
rules This is a list of child rules all of which to resolve.

Example:

...
if: {
    type: "and",
    rules: [
        ...child rules
    ]
}
...

or

This is a meta condition and will resolve to true if any of the child rules resolve to true.

Fields:

field description
rules This is a list of child rules one of which to resolve.

Example:

...
if: {
    type: "or",
    rules: [
        ...child rules
    ]
}
...

equal

This will compare a field in the incoming message with a static value defined in config. If these are equal this will resolve to true.

Fields:

field description
field The field name to extract from the incoming Slack message - "text" is most common
value The value to compare to the field

Example:

...
if: {
    type: "equal",
    field: "type",
    value: "file_share"
}
...

includes

This will check if a string defined in config is a substring of a field in the incoming message. If the field in the incoming message has a given string, this will resolve to true.

Fields:

field description
field The field name to extract from the incoming Slack message - "text" is most common
value The value to look for as a substring of the incoming message object.

Example: Will resolve to true if someone sends something like i like potatos.

...
if: {
    type: "includes",
    field: "text",
    value: "potato"
}
...

token-equals

This is a slightly more complex version of includes. This will first try to tokenize and normalize the tokens before doing a comparison. For example if you want to block anyone writing "hell", using an includes rule, we would also trigger on "hello world". What this condition does is it first breaks on "hello" and "world" and checks if one of those tokens is exactly equal to "hell".

Fields:

field description
field The field name to extract from the incoming Slack message - "text" is most common
value The value to look for as token in the given message

Example: Will resolve to true if someone sends something like hell with this but not hello world.

...
if: {
    type: "token-equals",
    field: "text",
    value: "hell"
}
...

About

Moderation for Slack communities is hard. Here is a bot to help

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 99.5%
  • Shell 0.5%