Switch from TOML to INI. New documentation. Add start.bat file for Windows.

master
Dragory 2019-12-03 01:51:11 +02:00
parent 686c36dda4
commit 89e7d6373b
15 changed files with 512 additions and 192 deletions

View File

@ -8,3 +8,6 @@ end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

10
.gitignore vendored
View File

@ -1,13 +1,7 @@
/.vscode
/.idea
/node_modules
/config.json
/config.json5
/config.json.json
/config.json.txt
/config.js
/config.toml
/config.toml.toml
/config.toml.txt
/config.*
!/config.example.ini
/welcome.png
/update.sh

View File

@ -1,5 +1,12 @@
# Changelog
## v2.29.0
* **Change default configuration format to .ini**
* Existing `config.json` files will continue to work and will not be deprecated
* New, rewritten instructions for setting up and using the bot
* Updated several package dependencies
* Fixed incompatibility with certain Node.js 10 versions
## v2.28.0
* Fix error when saving attachments locally with `attachmentStorage` set to `"local"` (default) when the bot's folder is
on a different storage device than the system's temp folder

176
README.md
View File

@ -1,171 +1,19 @@
# Modmail for Discord
A bot for [Discord](https://discordapp.com/) that allows users to DM the bot to contact the server's entire mod team.
These DMs get relayed to a modmail server where each user gets their own channel, or "thread".
Moderators and admins can then reply to these threads, and these responses are relayed back to the original user as a DM.
A bot for [Discord](https://discordapp.com/) that allows users to DM the bot to contact the server's moderators/staff
without messaging them individually or pinging them publically on the server.
These DMs get relayed to modmail *threads*, channels where staff members can reply to and talk with the user.
To the user, the entire process happens in DMs with the bot.
Inspired by Reddit's modmail system.
## NOTE! If you're upgrading to v2.23.0, note that Node.js 10 is now required at minimum.
## Table of contents
- [Setup](#setup)
- [Support server](#support-server)
- [Changelog](#changelog)
- [Commands](#commands)
- [Anywhere on the inbox server](#anywhere-on-the-inbox-server)
- [Inside a modmail thread](#inside-a-modmail-thread)
- [Configuration options](#configuration-options)
- [Plugins](#plugins)
- [Specifying plugins to load](#specifying-plugins-to-load)
- [Creating a plugin](#creating-a-plugin)
## Setup
1. Install Node.js 10 (LTS) or higher
2. Download the latest release from [the releases page](https://github.com/Dragory/modmailbot/releases)
3. Create a Discord server to be used as the modmail inbox
4. Make a copy of the file `config.example.toml` in the same folder and name the copy `config.toml`. Open the file and fill in the values.
- You can also find more configurable options at the end of this page!
5. Install dependencies: `npm ci`
6. Add bot to servers, and make sure to give it proper permissions on the mail server.
7. Run the bot: `npm start`
## Getting started
* **[🛠️ Setting up the bot](docs/setup.md)**
* [📝 Configuration](docs/configuration.md)
* [🤖 Commands](docs/commands.md)
* [🧩 Plugins](docs/plugins.md)
* [Release notes](CHANGELOG.md)
## Support server
If you need help with setting up the bot or would like to discuss other things related to the bot, join the support server on Discord here:
If you need help with setting up the bot or would like to discuss other things related to it, join the support server on Discord here:
https://discord.gg/vRuhG9R
## Changelog
See [CHANGELOG.md](CHANGELOG.md)
## Commands
### Anywhere on the inbox server
`!logs <user> <page>` Lists previous modmail logs with the specified user. If there are a lot of logs, they will be paginated. In this case, you can specify the page number to view as the second argument.
`!block <user> <time>` Blocks the specified user from using modmail. If a time is specified, the block is temporary.
`!unblock <user> <time>` Unblocks the specified user from using modmail. If a time is specified, the user will be scheduled to be unblocked after that time.
`!is_blocked <user>` Checks whether the user is blocked and for how long
`!s <shortcut> <text>` Adds a snippet (a canned response). Supports {1}, {2}, etc. for arguments. See below for how to use it.
`!edit_snippet <shortcut> <text>` Edits an existing snippet (alias `!es`)
`!delete_snippet <shortcut>` Deletes the specified snippet (alias `!ds`)
`!snippets` Lists all available snippets
`!version` Print the version of the bot you're running
`!newthread <user>` Opens a new thread with the specified user
### Inside a modmail thread
`!reply <text>` Sends a reply to the user in the format "(Role) User: text" (alias `!r`)
`!anonreply <text>` Sends an anonymous reply to the user in the format "Role: text" (alias `!ar`)
`!close <time>` Closes the modmail thread. If a time is specified, the thread is scheduled to be closed later. Scheduled closing is cancelled if a message is sent to or received from the user.
`!logs <page>` Lists previous modmail logs with this user. If there are a lot of logs, they will be paginated. In this case, you can specify the page number to view as an argument.
`!block <time>` Blocks the user from using modmail. If a time is specified, the block is temporary.
`!unblock <time>` Unblocks the user from using modmail. If a time is specified, the user will be scheduled to be unblocked after that time.
`!!shortcut` Reply with a snippet. Replace `shortcut` with the snippet's actual shortcut.
`!!!shortcut` Reply with a snippet anonymously. Replace `shortcut` with the snippet's actual shortcut.
`!move <category>` If `allowMove` is enabled, moves the thread channel to the specified category
`!loglink` Shows the link to the current thread's log
`!suspend` Suspend a thread. The thread will act as closed and not receive any messages until unsuspended.
`!unsuspend` Unsuspend a thread
`!id` Prints the user's ID
`!alert` Pings you when the thread gets a new reply. Use `!alert cancel` to cancel.
To automatically reply without using !reply or !r, enable `alwaysReply` in the config. `alwaysReplyAnon` sets whether to reply anonymously. If you do not wish to reply, it will ignore any message starting in the prefix (which defaults to !), such as !note
## Configuration options
These go in `config.toml`. See also `config.example.toml`.
|Option|Default|Description|
|------|-------|-----------|
|**token**|None|**Required!** The bot user's token|
|**logChannelId**|None|**Required!** Channel where to post log links to closed threads and other alerts|
|**mailGuildId**|None|**Required!** The inbox server's ID|
|**mainGuildId**|None|**Required!** ID (or array of IDs) of the main server where people contact the bot from. Used for displaying users' nicknames and join dates, and catching bot pings.|
|accountAgeDeniedMessage|"Your Discord account is not old enough to contact modmail."|See `requiredAccountAge` below|
|allowMove|false|If enabled, allows you to move the thread to another category using `!move <category>`|
|allowUserClose|false|If set to true, users can use the close command to close threads by themselves from their DMs with the bot|
|alwaysReplyAnon|false|If `alwaysReply` is set to true, this option controls whether the auto-reply is anonymous|
|alwaysReply|false|If set to true, all messages in modmail threads will be relayed back to the user, even ones without `!r`|
|attachmentStorage|"local"|Controls where sent/received attachments are saved.<br><br>**"local"** - Files are saved locally on the machine running the bot<br>**"discord"** - Files are saved as attachments on a special channel on the inbox server. Requires `attachmentStorageChannelId` to be set.|
|attachmentStorageChannelId|null|When using "discord" attachment storage, the id of the channel on the inbox server where attachments should be saved|
|botMentionResponse|None|If set, the bot auto-responds to bot mentions with this message. Allows `{userMention}` to be added to mention the user who mentioned the bot.|
|categoryAutomation|{}|Various ways of automating thread categories on the inbox server. **Note that the options below with a dot in the name are object properties for `categoryAutomation`.**|
|categoryAutomation.newThread|None|Same as `newThreadCategoryId`. Specifies a category to open all new threads in. Also functions as a fallback for `categoryAutomation.newThreadFromGuild`.|
|categoryAutomation.newThreadFromGuild|None|Allows you to open threads in specific categories based on which guild the user is messaging the bot from. The value is an object with guild ids as the keys and category ids as the values.|
|closeMessage|None|The bot's message to the user when the thread is closed|
|commandAliases|None|Custom aliases for commands. Value is an object with the alias as the key and the real command as the value.|
|enableGreeting|false|Set to true to send a welcome message to new main guild members. Requires `mainGuildId` to be set.|
|greetingAttachment|None|Path to an image or other attachment to send along with the greeting|
|greetingMessage|None|Text content of the welcome message|
|guildGreetings|None|When using multiple mainGuildIds, this option allows you to configure greetings on a per-server basis. The syntax is an object with the guild ID as the key, and another object with `message` and `attachment` properties as the value (identical to greetingMessage and greetingAttachment)|
|ignoreAccidentalThreads|false|If set to true, the bot attempts to ignore common "accidental" messages that would start a new thread, such as "ok", "thanks", etc.|
|inboxServerPermission|None|Permission name, user id, or role id required to use bot commands on the inbox server. Also supports arrays. See ["Permissions" on this page](https://abal.moe/Eris/docs/reference) for supported permission names (e.g. `kickMembers`).|
|timeOnServerDeniedMessage|"You haven't been a member of the server for long enough to contact modmail."|See `requiredTimeOnServer` below|
|mentionRole|"here"|Role that is mentioned when new threads are created or the bot is mentioned. Accepted values are "here", "everyone", or a role id as a string. Set to null to disable these pings entirely. Multiple values in an array are supported.|
|mentionUserInThreadHeader|false|If set to true, mentions the thread's user in the thread header|
|newThreadCategoryId|None|**Deprecated** ID of the category where new modmail thread channels should be placed|
|pingOnBotMention|true|If enabled, the bot will mention staff (see mentionRole above) on the inbox server when the bot is mentioned on the main server.|
|plugins|None|Array of plugins to load on startup. See [Plugins](#plugins) section below for more information.|
|port|8890|Port from which to serve attachments and logs|
|prefix|"!"|Prefix for bot commands|
|relaySmallAttachmentsAsAttachments|false|Whether to relay small attachments from users as native attachments rather than links in modmail threads|
|requiredAccountAge|None|Required account age for contacting modmail (in hours). If the account is not old enough, a new thread will not be created and the bot will reply with `accountAgeDeniedMessage` (if set) instead.|
|requiredTimeOnServer|None|Required amount of time (in minutes) the user must be a member of the server before being able to contact modmail. If the user hasn't been a member of the server for the specified time, a new thread will not be created and the bot will reply with `timeOnServerDeniedMessage` (if set) instead.|
|responseMessage|"Thank you for your message! Our mod team will reply to you here as soon as possible."|The bot's response to DMs that start a new thread|
|rolesInThreadHeader|false|If enabled, the user's roles will be shown in the thread header|
|smallAttachmentLimit|2097152|Size limit of `relaySmallAttachmentsAsAttachments`, in bytes (default is 2MB)|
|snippetPrefix|"!!"|Prefix to use snippets|
|snippetPrefixAnon|"!!!"|Prefix to use snippets anonymously|
|status|"Message me for help"|The bot's "Playing" text|
|syncPermissionsOnMove|true|Whether to sync thread channel permissions to the category when moved with !move|
|threadTimestamps|false|Whether to show custom timestamps in threads, in addition to Discord's own timestamps. Logs always have accurate timestamps, regardless of this setting.|
|typingProxy|false|If enabled, any time a user is typing to modmail in their DMs, the modmail thread will show the bot as "typing"|
|typingProxyReverse|false|If enabled, any time a moderator is typing in a modmail thread, the user will see the bot "typing" in their DMs|
|updateNotifications|true|Whether to automatically check for bot updates and notify about them in new threads|
|url|None|URL to use for attachment and log links. Defaults to `IP:PORT`|
|useNicknames|false|If set to true, mod replies will use their nickname (on the inbox server) instead of their username|
## Plugins
The bot supports loading external plugins.
### Specifying plugins to load
Add the path to the plugin's file to the `plugins` array in the config.
The plugin will be automatically loaded on startup.
The path is relative to the bot's folder.
### Creating a plugin
Create a `.js` file that exports a function.
This function will be called when the plugin is loaded with an object that has the following properties:
* `bot` - the [Eris Client object](https://abal.moe/Eris/docs/Client)
* `knex` - the [Knex database object](https://knexjs.org/#Builder)
* `config` - the loaded config
* `commands` - an object with functions to add and manage commands
* `attachments` - an object with functions to save attachments and manage attachment storage types
See [src/plugins.js#L4](src/plugins.js#L4) for more details
#### Example plugin file
This example adds a command `!mycommand` that replies with `"Reply from my custom plugin!"` when the command is used inside a modmail inbox thread channel.
```js
module.exports = function({ bot, knex, config, commands }) {
commands.addInboxThreadCommand('mycommand', [], (msg, args, thread) => {
thread.replyToUser(msg.author, 'Reply from my custom plugin!');
});
}
```
(Note the use of [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Unpacking_fields_from_objects_passed_as_function_parameter) in the function parameters)
#### Example of a custom attachment storage type
This example adds a custom type for the `attachmentStorage` option called `"original"` that simply returns the original attachment URL without rehosting it in any way.
```js
module.exports = function({ attachments }) {
attachments.addStorageType('original', attachment => {
return { url: attachment.url };
});
};
```
To use this custom attachment storage type, you would set the `attachmentStorage` config option to `"original"`.
### Work in progress
The current plugin API is fairly rudimentary and will be expanded in the future.
The API can change in non-major releases during this early stage. Keep an eye on [CHANGELOG.md](CHANGELOG.md) for any changes.
Please send any feature suggestions to the [issue tracker](https://github.com/Dragory/modmailbot/issues)!
👉 **[Join support server](https://discord.gg/vRuhG9R)**

9
config.example.ini Normal file
View File

@ -0,0 +1,9 @@
# Required settings
# -----------------
token = REPLACE_WITH_TOKEN
mainGuildId = REPLACE_WITH_MAIN_SERVER_ID
mailGuildId = REPLACE_WITH_INBOX_SERVER_ID
logChannelId = REPLACE_WITH_LOG_CHANNEL_ID
# Add new options below this line:
# ----------------------------------

View File

@ -1,7 +0,0 @@
# Basic settings
token = "enter your token here"
mailGuildId = "enter the id of the modmail inbox server here"
mainGuildId = "enter the id of the main server here"
logChannelId = "enter the id of the channel on the inbox server where notifications of new logs etc. will be posted"
# Put other options below this line

31
docs/commands.md Normal file
View File

@ -0,0 +1,31 @@
# 🤖 Commands
## Anywhere on the inbox server
`!logs <user> <page>` Lists previous modmail logs with the specified user. If there are a lot of logs, they will be paginated. In this case, you can specify the page number to view as the second argument.
`!block <user> <time>` Blocks the specified user from using modmail. If a time is specified, the block is temporary.
`!unblock <user> <time>` Unblocks the specified user from using modmail. If a time is specified, the user will be scheduled to be unblocked after that time.
`!is_blocked <user>` Checks whether the user is blocked and for how long
`!s <shortcut> <text>` Adds a snippet (a canned response). Supports {1}, {2}, etc. for arguments. See below for how to use it.
`!edit_snippet <shortcut> <text>` Edits an existing snippet (alias `!es`)
`!delete_snippet <shortcut>` Deletes the specified snippet (alias `!ds`)
`!snippets` Lists all available snippets
`!version` Print the version of the bot you're running
`!newthread <user>` Opens a new thread with the specified user
## Inside a modmail thread
`!reply <text>` Sends a reply to the user in the format "(Role) User: text" (alias `!r`)
`!anonreply <text>` Sends an anonymous reply to the user in the format "Role: text" (alias `!ar`)
`!close <time>` Closes the modmail thread. If a time is specified, the thread is scheduled to be closed later. Scheduled closing is cancelled if a message is sent to or received from the user.
`!logs <page>` Lists previous modmail logs with this user. If there are a lot of logs, they will be paginated. In this case, you can specify the page number to view as an argument.
`!block <time>` Blocks the user from using modmail. If a time is specified, the block is temporary.
`!unblock <time>` Unblocks the user from using modmail. If a time is specified, the user will be scheduled to be unblocked after that time.
`!!shortcut` Reply with a snippet. Replace `shortcut` with the snippet's actual shortcut.
`!!!shortcut` Reply with a snippet anonymously. Replace `shortcut` with the snippet's actual shortcut.
`!move <category>` If `allowMove` is enabled, moves the thread channel to the specified category
`!loglink` Shows the link to the current thread's log
`!suspend` Suspend a thread. The thread will act as closed and not receive any messages until unsuspended.
`!unsuspend` Unsuspend a thread
`!id` Prints the user's ID
`!alert` Pings you when the thread gets a new reply. Use `!alert cancel` to cancel.
To automatically reply without using !reply or !r, [enable `alwaysReply` in the config](configuration.md).

315
docs/configuration.md Normal file
View File

@ -0,0 +1,315 @@
# 📝 Configuration
Haven't set up the bot yet? Check out [Setting up the bot](setup.md) first!
## Table of contents
- [Configuration file](#configuration-file) (start here)
- [Adding new options](#adding-new-options)
- [Required options](#required-options)
- [Other options](#other-options)
- [config.ini vs config.json](#configini-vs-configjson)
## Configuration file
All bot options are saved in a configuration file in the bot's folder.
This is created during the [setup](setup.md) and is generally either `config.ini` or, if you've been using the bot for
longer, `config.json`.
The instructions on this page are for `config.ini` but can be adapted to `config.json` as well.
See [config.ini vs config.json](#configini-vs-configjson) for more details.
Note that the format of `.ini` and `.json` are different -- you can't simply rename a `.json` to `.ini` or
vice versa.
## Adding new options
To add a new option to your `config.ini`, open the file in a text editor such as notepad.
Each option is put on a new line, and follows the format `option = value`. For example, `mainGuildId = 1234`.
**You need to restart the bot for configuration changes to take effect!**
You can add comments in the config file by prefixing the line with `#`. Example:
```ini
# This is a comment
option = value
```
### Toggle options
Some options like `allowMove` are "**Toggle options**": they control whether certain features are enabled (on) or not (off).
* To enable a toggle option, set its value to `on`, `true`, or `1`
* To disable a toggle option, set its value to `off`, `false`, or `0`
* E.g. `allowMove = on` or `allowMove = off`
### "Accepts multiple values"
Some options are marked as "**Accepts multiple values**". To give these options multiple values,
write the option as `option[] = value` and repeat for every value. For example:
```ini
inboxServerPermission[] = kickMembers
inboxServerPermission[] = manageMessages
```
You can also give these options a single value in the usual way, i.e. `inboxServerPermission = kickMembers`
### Multiple lines of text
For some options, such as `greetingMessage`, you might want to add text that spans multiple lines.
To do that, use the same format as with "Accepts multiple values" above:
```ini
greetingMessage[] = Welcome to the server!
greetingMessage[] = This is the second line of the greeting.
greetingMessage[] =
greetingMessage[] = Fourth line! With an empty line in the middle.
```
## Required options
#### token
The bot user's token from [Discord Developer Portal](https://discordapp.com/developers/).
#### mainGuildId
Your server's ID, wrapped in quotes.
#### mailGuildId
For a two-server setup, the inbox server's ID.
For a single-server setup, same as [mainGuildId](#mainguildid).
#### logChannelId
ID of a channel on the inbox server where logs are posted after a modmail thread is closed
## Other options
#### accountAgeDeniedMessage
**Default:** `Your Discord account is not old enough to contact modmail.`
See `requiredAccountAge` below
#### allowMove
**Default:** `off`
If enabled, allows you to move threads between categories using `!move <category>`
#### allowUserClose
**Default:** `off`
If enabled, users can use the close command to close threads by themselves from their DMs with the bot
#### alwaysReply
**Default:** `off`
If enabled, all messages in modmail threads will be sent to the user without having to use `!r`.
To send internal messages in the thread when this option is enabled, prefix them with `!note` (using your `prefix`),
e.g. `!note This is an internal message`.
#### alwaysReplyAnon
**Default:** `off`
If `alwaysReply` is enabled, this option controls whether the auto-reply is anonymous
#### attachmentStorage
**Default:** `local`
Controls how attachments in modmail threads are stored. Possible values:
* **local** - Files are saved locally on the machine running the bot
* **discord** - Files are saved as attachments on a special channel on the inbox server. Requires `attachmentStorageChannelId` to be set.
#### attachmentStorageChannelId
**Default:** *None*
When using attachmentStorage is set to "discord", the id of the channel on the inbox server where attachments are saved
#### botMentionResponse
**Default:** *None*
If set, the bot auto-replies to bot mentions (pings) with this message. Use `{userMention}` in the text to ping the user back.
#### categoryAutomation.newThread
**Default:** *None*
ID of the category where new threads are opened. Also functions as a fallback for `categoryAutomation.newThreadFromGuild`.
#### categoryAutomation.newThreadFromGuild.GUILDID
**Default:** *None*
When running the bot on multiple main servers, this allows you to specify new thread categories for users from each guild. Example:
```ini
# When the user is from the server ID 94882524378968064, their modmail thread will be placed in the category ID 360863035130249235
categoryAutomation.newThreadFromGuild.94882524378968064 = 360863035130249235
# When the user is from the server ID 541484311354933258, their modmail thread will be placed in the category ID 542780020972716042
categoryAutomation.newThreadFromGuild.541484311354933258 = 542780020972716042
```
#### closeMessage
**Default:** *None*
If set, the bot sends this message to the user when the modmail thread is closed.
#### commandAliases
**Default:** *None*
Custom aliases/shortcuts for commands. Example:
```ini
# !mv is an alias/shortcut for !move
commandAliases.mv = move
# !x is an alias/shortcut for !close
commandAliases.x = close
```
#### enableGreeting
**Default:** `off`
When enabled, the bot will send a greeting DM to users that join the main server.
#### greetingAttachment
**Default:** *None*
Path to an image or other attachment to send as a greeting. Requires `enableGreeting` to be enabled.
#### greetingMessage
**Default:** *None*
Message to send as a greeting. Requires `enableGreeting` to be enabled. Example:
```ini
greetingMessage[] = Welcome to the server!
greetingMessage[] = Remember to read the rules.
```
#### guildGreetings
**Default:** *None*
When running the bot on multiple main servers, this allows you to set different greetings for each server. Example:
```ini
guildGreetings.94882524378968064.message = Welcome to server ID 94882524378968064!
guildGreetings.94882524378968064.attachment = greeting.png
guildGreetings.541484311354933258.message[] = Welcome to server ID 541484311354933258!
guildGreetings.541484311354933258.message[] = Second line of the greeting.
```
#### ignoreAccidentalThreads
**Default:** `off`
If enabled, the bot attempts to ignore common "accidental" messages that would start a new thread, such as "ok", "thanks", etc.
#### inboxServerPermission
**Default:** *None*
**Accepts multiple values.** Permission name, user id, or role id required to use bot commands on the inbox server.
See ["Permissions" on this page](https://abal.moe/Eris/docs/reference) for supported permission names (e.g. `kickMembers`).
#### timeOnServerDeniedMessage
**Default:** `You haven't been a member of the server for long enough to contact modmail.`
If `requiredTimeOnServer` is set, users that are too new will be sent this message if they try to message modmail.
#### mentionRole
**Default:** `here`
**Accepts multiple values.** Role that is mentioned when new threads are created or the bot is mentioned.
Accepted values are "here", "everyone", or a role id.
Requires `pingOnBotMention` to be enabled.
Set to an empty value (`mentionRole=`) to disable these pings entirely.
#### mentionUserInThreadHeader
**Default:** `off`
If enabled, mentions the user messaging modmail in the modmail thread's header.
#### newThreadCategoryId
**Default:** *None*
**Deprecated.** Same as `categoryAutomation.newThread`.
#### pingOnBotMention
**Default:** `on`
If enabled, the bot will mention staff (see `mentionRole` option) on the inbox server when the bot is mentioned on the main server.
#### plugins
**Default:** *None*
**Accepts multiple values.** External plugins to load on startup. See [Plugins](plugins.md) for more information.
#### port
**Default:** `8890`
Port to use for attachments (when `attachmentStorage` is set to `local`) and logs.
Make sure to do the necessary [port forwarding](https://portforward.com/) and add any needed firewall exceptions so the port is accessible from the internet.
#### prefix
**Default:** `!`
Prefix for bot commands
#### relaySmallAttachmentsAsAttachments
**Default:** `off`
If enabled, small attachments from users are sent as real attachments rather than links in modmail threads.
The limit for "small" is 2MB by default; you can change this with the `smallAttachmentLimit` option.
#### requiredAccountAge
**Default:** *None*
Required account age for contacting modmail (in hours). If the account is not old enough, a new thread will not be created and the bot will reply with `accountAgeDeniedMessage` (if set) instead.
#### requiredTimeOnServer
**Default:** *None*
Required amount of time (in minutes) the user must be a member of the server before being able to contact modmail. If the user hasn't been a member of the server for the specified time, a new thread will not be created and the bot will reply with `timeOnServerDeniedMessage` (if set) instead.
#### responseMessage
**Default:** `Thank you for your message! Our mod team will reply to you here as soon as possible.`
The bot's response to the user when they message the bot and open a new modmail thread
#### rolesInThreadHeader
**Default:** `off`
If enabled, the user's roles will be shown in the modmail thread header
#### smallAttachmentLimit
**Default:** `2097152`
Size limit of `relaySmallAttachmentsAsAttachments` in bytes (default is 2MB)
#### snippetPrefix
**Default:** `!!`
Prefix for snippets
#### snippetPrefixAnon
**Default:** `"!!!"`
Prefix to use snippets anonymously
#### status
**Default:** `Message me for help`
The bot's "Playing" text
#### syncPermissionsOnMove
**Default:** `on`
If enabled, channel permissions for the thread are synchronized with the category when using `!move`. Requires `allowMove` to be enabled.
#### threadTimestamps
**Default:** `off`
If enabled, modmail threads will show accurate UTC timestamps for each message, in addition to Discord's own timestamps.
Logs show these always, regardless of this setting.
#### typingProxy
**Default:** `off`
If enabled, any time a user is typing to modmail in their DMs, the modmail thread will show the bot as "typing"
#### typingProxyReverse
**Default:** `off`
If enabled, any time a moderator is typing in a modmail thread, the user will see the bot "typing" in their DMs
#### updateNotifications
**Default:** `on`
If enabled, the bot will automatically check for new bot updates periodically and notify about them at the top of new modmail threads
#### url
**Default:** *None*
URL to use for attachment and log links. Defaults to `http://IP:PORT`.
#### useNicknames
**Default:** `off`
If enabled, mod replies will use their nicknames (on the inbox server) instead of their usernames
## config.ini vs config.json
Earlier versions of the bot instructed you to create a `config.json` instead of a `config.ini`.
**This is still fully supported, and will be in the future as well.**
However, there are some differences between `config.ini` and `config.json`.
### Formatting
*See [the example on the Wikipedia page for JSON](https://en.wikipedia.org/wiki/JSON#Example)
for a general overview of the JSON format.*
* In `config.json`, all text values and IDs need to be wrapped in quotes, e.g. `"mainGuildId": "94882524378968064"`
* In `config.json`, all numbers (other than IDs) are written without quotes, e.g. `"port": 3000`
### Toggle options
In `config.json`, valid values for toggle options are `true` and `false` (not quoted),
which correspond to `on` and `off` in `config.ini`.
### "Accepts multiple values"
Multiple values are specified in `config.json` using arrays:
```json
{
"inboxPermission": [
"kickMembers",
"manageMessages"
]
}
```
### Multiple lines of text
Since `config.json` is parsed using [JSON5](https://json5.org/), multiple lines of text are supported
by escaping the newline with a backslash (`\ `):
```json5
{
"greetingMessage": "Welcome to the server!\
This is the second line of the greeting."
}
```

47
docs/plugins.md Normal file
View File

@ -0,0 +1,47 @@
# 🧩 Plugins
The bot supports loading external plugins.
## Specifying plugins to load
For each plugin file you'd like to load, add the file path to the [`plugins` option](configuration.md#plugins).
The path is relative to the bot's folder.
Plugins are automatically loaded on bot startup.
## Creating a plugin
Create a `.js` file that exports a function.
This function will be called when the plugin is loaded, with 1 argument: an object that has the following properties:
* `bot` - the [Eris Client object](https://abal.moe/Eris/docs/Client)
* `knex` - the [Knex database object](https://knexjs.org/#Builder)
* `config` - the loaded config
* `commands` - an object with functions to add and manage commands
* `attachments` - an object with functions to save attachments and manage attachment storage types
See [src/plugins.js#L4](../src/plugins.js#L4) for more details
### Example plugin file
This example adds a command `!mycommand` that replies with `"Reply from my custom plugin!"` when the command is used inside a modmail inbox thread channel.
```js
module.exports = function({ bot, knex, config, commands }) {
commands.addInboxThreadCommand('mycommand', [], (msg, args, thread) => {
thread.replyToUser(msg.author, 'Reply from my custom plugin!');
});
}
```
(Note the use of [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Unpacking_fields_from_objects_passed_as_function_parameter) in the function parameters)
### Example of a custom attachment storage type
This example adds a custom type for the `attachmentStorage` option called `"original"` that simply returns the original attachment URL without rehosting it in any way.
```js
module.exports = function({ attachments }) {
attachments.addStorageType('original', attachment => {
return { url: attachment.url };
});
};
```
To use this custom attachment storage type, you would set the `attachmentStorage` config option to `"original"`.
## Work in progress
The current plugin API is fairly rudimentary and will be expanded on in the future.
The API can change in non-major releases during this early stage. Keep an eye on [CHANGELOG.md](../CHANGELOG.md) for any changes.
Please send any feature suggestions to the [issue tracker](https://github.com/Dragory/modmailbot/issues)!

37
docs/setup.md Normal file
View File

@ -0,0 +1,37 @@
# 🛠️ Setting up the bot
**Note:** This bot is run on your own machine or a server. To keep it online, you need to have the bot process running at all time.
## Terminology
* **Main server** (or main guild) is the server where users will be contacting modmail from
* **Inbox server** (or inbox guild, or mail guild) is the server where modmail threads will be created.
In a "single-server setup" this is the same server as the main server.
* A **modmail thread** is a channel on the **inbox server** that contains the current exchange with the **user**.
These threads can be closed to archive them. One **user** can only have 1 modmail thread open at once.
* A **moderator**, in modmail's context, is a server staff member who responds to and manages modmail threads
* A **user**, in modmail's context, is a Discord user who is contacting modmail by DMing the bot
## Prerequisites
1. Create a bot account through the [Discord Developer Portal](https://discordapp.com/developers/)
2. Invite the created bot to your server
3. Install Node.js 10, 11, or 12
- Node.js 13 is currently not supported
4. Download the latest bot version from [the releases page](https://github.com/Dragory/modmailbot/releases) and extract it to a folder
5. Make a copy of the file `config.example.ini` in the bot's folder and name the copy `config.ini`
## Single-server setup
In this setup, modmail threads are opened on the main server in a special category.
1. Open `config.ini` in a text editor and fill in the required values. `mainGuildId` and `mailGuildId` should both be set to your server's id.
2. On a new line at the end of `config.ini`, add `categoryAutomation.newThread = CATEGORY_ID_HERE`
- Replace `CATEGORY_ID_HERE` with the ID of the category where new modmail threads should go
3. Make sure the bot has `Manage Channels`, `Manage Messages`, and `Attach Files` permissions in the category
4. [🏃 Start the bot!](starting-the-bot.md)
5. Want to change other bot options? See [📝 Configuration](configuration.md).
## Two-server setup
In this setup, modmail threads are opened on a separate inbox server.
1. Create an inbox server on Discord
2. Invite the bot to the inbox server.
3. Open `config.ini` in a text editor and fill in the values
4. Make sure the bot has the `Manage Channels`, `Manage Messages`, and `Attach Files` permissions on the **inbox** server
5. [🏃 Start the bot!](starting-the-bot.md)
5. Want to change other bot options? See [📝 Configuration](configuration.md).

16
docs/starting-the-bot.md Normal file
View File

@ -0,0 +1,16 @@
# 🏃 Starting the bot
Haven't set up the bot yet? Check out [Setting up the bot](setup.md) first!
## Windows
* To start the bot, double-click on `start.bat` in the bot's folder
* To shut down the bot, close the console window
* To restart the bot, close the console window and then double-click on `start.bat` again
## Linux / macOS / Advanced on Windows
The following assumes basic knowledge about using command line tools.
1. Before first start-up and after every update, run `npm ci` in the bot's folder
2. Run `npm start` in the bot's folder to start the bot
## Process managers
If you're using a process manager like PM2, the command to run is `npm start`.
A PM2 process file, `modmailbot-pm2.json`, is included in the repository.

5
package-lock.json generated
View File

@ -24,11 +24,6 @@
"js-tokens": "^4.0.0"
}
},
"@iarna/toml": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.3.tgz",
"integrity": "sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg=="
},
"@sindresorhus/is": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",

View File

@ -15,9 +15,9 @@
"url": "https://github.com/Dragory/modmailbot"
},
"dependencies": {
"@iarna/toml": "^2.2.3",
"eris": "^0.11.1",
"humanize-duration": "^3.12.1",
"ini": "^1.3.5",
"json5": "^2.1.1",
"knex": "^0.20.3",
"knub-command-manager": "^6.1.0",

View File

@ -16,8 +16,9 @@ let userConfig;
// Config files to search for, in priority order
const configFiles = [
'config.toml',
'config.toml.toml',
'config.ini',
'config.ini.ini',
'config.ini.txt',
'config.json',
'config.json5',
'config.json.json',
@ -44,9 +45,9 @@ try {
if (foundConfigFile.endsWith('.js')) {
userConfig = require(`../${foundConfigFile}`);
} else {
const raw = fs.readFileSync(__dirname + '/../' + foundConfigFile);
if (foundConfigFile.endsWith('.toml') || foundConfigFile.endsWith('.toml.txt')) {
userConfig = require('@iarna/toml').parse(raw);
const raw = fs.readFileSync(__dirname + '/../' + foundConfigFile, { encoding: "utf8" });
if (foundConfigFile.endsWith('.ini') || foundConfigFile.endsWith('.ini.txt')) {
userConfig = require('ini').decode(raw);
} else {
userConfig = require('json5').parse(raw);
}
@ -193,17 +194,24 @@ if (finalConfig.greetingMessage || finalConfig.greetingAttachment) {
if (finalConfig.guildGreetings[guildId]) continue;
finalConfig.guildGreetings[guildId] = {
message: finalConfig.greetingMessage,
message: finalConfig.greetingMessage
attachment: finalConfig.greetingAttachment
};
}
}
// Convert arrays of lines to multiline strings in greetings
for (const obj of Object.values(finalConfig.guildGreetings)) {
if (Array.isArray(obj.message)) {
obj.message = obj.message.join('\n');
}
}
// newThreadCategoryId is syntactic sugar for categoryAutomation.newThread
if (finalConfig.newThreadCategoryId) {
finalConfig.categoryAutomation.newThread = finalConfig.newThreadCategoryId;
delete finalConfig.newThreadCategoryId;
}
console.log("Configuration ok");''
console.log("Configuration ok");
module.exports = finalConfig;

17
start.bat Normal file
View File

@ -0,0 +1,17 @@
@echo off
echo Installing/updating bot dependencies
call npm ci --only=production --loglevel=warn >NUL
if NOT ["%errorlevel%"]==["0"] (
pause
exit /b %errorlevel%
)
echo Starting the bot
call npm run start
if NOT ["%errorlevel%"]==["0"] (
pause
exit /b %errorlevel%
)