Compare commits
11 Commits
9d82f9dacb
...
c325bf5ab4
Author | SHA1 | Date | |
---|---|---|---|
c325bf5ab4 | |||
88fdf4e7b9 | |||
6895018d24 | |||
fd1c4c9b85 | |||
3c3ea9d90d | |||
2892fd0f04 | |||
f1bd031544 | |||
d78c4f6687 | |||
67bb6309b8 | |||
|
bd5c8bd157 | ||
|
baf48f2747 |
29
README.md
29
README.md
@ -1,18 +1,19 @@
|
||||
# WIP MODMAIL BOT
|
||||
|
||||
**Still a work in progress, has no actual integration with the main bot at this point due to the main bot still being very much a work in progress.**
|
||||
**Still a work in progress, has no actual integration with the main bot at this point due to the main bot still being very much a work in progress.**
|
||||
|
||||
Uses json for storage. If this is a problem, feel free to start a branch and write your own cache handler, alternatively use another bot.
|
||||
Shouldn't require much setup, fill in the relevant fields in config.js, should be well commented.
|
||||
Uses json for storage. If this is a problem, feel free to start a branch and write your own cache handler, alternatively use another bot.
|
||||
Shouldn't require much setup, fill in the relevant fields in config.js, should be well commented.
|
||||
|
||||
I've tried refactoring some of the original code that I wrote initially in haste, but a lot of it's still very questionable and in need of rewriting/refactoring.
|
||||
I've tried refactoring some of the original code that I wrote initially in haste, but a lot of it's still very questionable and in need of rewriting/refactoring.
|
||||
Contributions welcome.
|
||||
|
||||
## Docker image
|
||||
**Highly recommend making a named volume for the storage**
|
||||
> Create a named volume `docker volume create ModmailStorage`
|
||||
Make sure to have a config.js ready to go, see the example file for a template
|
||||
> Create a named volume `docker volume create ModmailStorage`
|
||||
Make sure to have a config.js ready to go, see the example file for a template
|
||||
> Start the container
|
||||
|
||||
```
|
||||
docker run -d --name modmail \
|
||||
-v /path/to/config.js:/modmail/config.js \
|
||||
@ -21,18 +22,18 @@ navydotgif/modmail:latest
|
||||
```
|
||||
|
||||
## How to use
|
||||
> Install [Node.js](https://nodejs.org/en/download/), at least v12, though I'd recommend at least 14 due to Discord.js requiring it in v13.
|
||||
> Install [Node.js](https://nodejs.org/en/download/), at least v12, though I'd recommend at least 14 due to Discord.js requiring it in v13.
|
||||
|
||||
> Run `git clone https://github.com/GalacticBot/modmail.git`
|
||||
> Run `git clone https://github.com/GalacticBot/modmail.git`
|
||||
|
||||
> Run `yarn install`, alternatively `npm install` if you don't have yarn for some reason.
|
||||
> Run `yarn install`, alternatively `npm install` if you don't have yarn for some reason.
|
||||
|
||||
> Rename `config.example.js` to `config.js`, open it and fill in the relevant values. The comments should explain what they are for. If something is unclear open an issue and I'll attempt to make it more clear.
|
||||
> Rename `config.example.js` to `config.js`, open it and fill in the relevant values. The comments should explain what they are for. If something is unclear open an issue and I'll attempt to make it more clear.
|
||||
|
||||
> At this point you'll probably notice you need to add 3 new categories. I'm planning on automating a part of the setup process, but for now this is how it be. Make those, add their IDs to the array.
|
||||
> At this point you'll probably notice you need to add 3 new categories. I'm planning on automating a part of the setup process, but for now this is how it be. Make those, add their IDs to the array.
|
||||
|
||||
> I'd recommend getting PM2 or whatever you prefer for process management.
|
||||
> Start up the bot and if you did everything right it should boot up and just work. If something goes wrong submit an issue, alternatively if you know how to fix it, issue a pull request.
|
||||
> I'd recommend getting PM2 or whatever you prefer for process management.
|
||||
> Start up the bot and if you did everything right it should boot up and just work. If something goes wrong submit an issue, alternatively if you know how to fix it, issue a pull request.
|
||||
|
||||
## Commands
|
||||
|
||||
@ -46,7 +47,7 @@ Both of these can be used with `!r` and `!cr` respectively and both of them supp
|
||||
`!mmqueue` - Shows users in queue.
|
||||
`!id [channel]` - Get the target user ID for the modmail thread. Can be used without the channel argument in a modmail channel, outside of a modmail channel will return the user ID for the channel's target if available.
|
||||
|
||||
The bot has an `!eval` command which you can grant access to in the config file.
|
||||
The bot has an `!eval` command which you can grant access to in the config file.
|
||||
|
||||
**Creating canned/pre-written replies**
|
||||
Simple as using `!cr create <name> <the reply content here>`. Updating an existing entry is done by overwriting it.
|
||||
|
51
package.json
51
package.json
@ -1,25 +1,26 @@
|
||||
{
|
||||
"name": "modmail",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"author": "Navy <navydotgif@gmail.com>",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"description": "Modmail bot with eventual integration with Galactic Bot's API",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"dev": "nodemon --ignore *.json index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.28.0",
|
||||
"nodemon": "^2.0.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^4.0.0",
|
||||
"diagnostics": "^2.0.2",
|
||||
"discord.js": "^12.5.3",
|
||||
"moment": "^2.29.2",
|
||||
"winston": "^3.3.3",
|
||||
"winston-transport": "^4.4.0"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "modmail",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"author": "Navy <navydotgif@gmail.com>",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"description": "Modmail bot with eventual integration with Galactic Bot's API",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"dev": "nodemon --ignore *.json index.js",
|
||||
"dockerpub": "tsc && docker build . --tag navydotgif/modmail:latest && docker push navydotgif/modmail:latest "
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.28.0",
|
||||
"nodemon": "^2.0.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^4.0.0",
|
||||
"diagnostics": "^2.0.2",
|
||||
"discord.js": "^12.5.3",
|
||||
"moment": "^2.29.4",
|
||||
"winston": "^3.3.3",
|
||||
"winston-transport": "^4.4.0"
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ class ChannelHandler {
|
||||
if (channel) await channel.edit({ parentID: state === 'read' ? this.readMail.id : this.newMail.id, lockPermissions: true });
|
||||
if (!this.cache.updatedThreads.includes(target)) this.cache.updatedThreads.push(target);
|
||||
if (this.cache.queue.includes(target) && state === 'read') this.cache.queue.splice(this.cache.queue.indexOf(target), 1);
|
||||
else if (!this.cache.queue.includes(target)) this.cache.queue.push(target);
|
||||
else if (!this.cache.queue.includes(target) && state === 'unread') this.cache.queue.push(target);
|
||||
return {};
|
||||
|
||||
}
|
||||
|
@ -102,11 +102,11 @@ class ModmailClient extends Client {
|
||||
const roles = member.roles.cache.map((r) => r.id);
|
||||
if (!roles.some((r) => this._options.staffRoles.includes(r)) && !member.hasPermission('ADMINISTRATOR')) return;
|
||||
|
||||
const [ rawCommand, ...args ] = content.split(' ');
|
||||
const [ rawCommand, ...args ] = content.split(/\s+/u);
|
||||
const commandName = rawCommand.substring(prefix.length);
|
||||
const command = this.registry.find(commandName);
|
||||
if (!command) return;
|
||||
message._caller = commandName.toLowerCase();
|
||||
message._caller = commandName;
|
||||
|
||||
if (command.showUsage && !args.length) {
|
||||
|
||||
|
@ -362,7 +362,6 @@ class Modmail {
|
||||
response = await this.channels.setReadState(userId, channel, author, state);
|
||||
}
|
||||
|
||||
|
||||
if (response.error) return response;
|
||||
this.log({ author, action: `${author.tag} marked ${user.tag}'s thread as ${state}`, target: user });
|
||||
return 'Done';
|
||||
|
@ -1,88 +1,89 @@
|
||||
const Command = require('../Command');
|
||||
|
||||
class CannedReply extends Command {
|
||||
|
||||
constructor (client) {
|
||||
super(client, {
|
||||
name: 'cannedreply',
|
||||
aliases: [ 'cr', 'canned' ],
|
||||
showUsage: true,
|
||||
usage: `<canned response name>`
|
||||
});
|
||||
}
|
||||
|
||||
async execute (message, { args }) {
|
||||
|
||||
const [ first ] = args.map((a) => a);
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { channel, content, _caller } = message,
|
||||
anon = false;
|
||||
content = content.replace(`${this.client.prefix}${_caller}`, '');
|
||||
const op = args.shift().toLowerCase();
|
||||
|
||||
if (op === 'anon') {
|
||||
anon = true;
|
||||
content = content.replace(first, '');
|
||||
} else if ([ 'create', 'delete' ].includes(op)) {
|
||||
return this.createCanned(op, args, message);
|
||||
} else if ([ 'list' ].includes(first.toLowerCase())) {
|
||||
|
||||
const list = Object.entries(this.client.modmail.replies);
|
||||
let str = '';
|
||||
// eslint-disable-next-line no-shadow
|
||||
for (const [ name, content ] of list) {
|
||||
const substr = `**${name}:** ${content}\n`;
|
||||
if (str.length + substr.length > 2000) {
|
||||
await channel.send(str);
|
||||
str = '';
|
||||
}
|
||||
str += substr;
|
||||
}
|
||||
if (str.length) return channel.send(str);
|
||||
return '**__None__**';
|
||||
|
||||
}
|
||||
|
||||
return this.client.modmail.sendCannedResponse({ message, responseName: content.trim(), anon });
|
||||
|
||||
}
|
||||
|
||||
async createCanned (op, args, { channel, author }) {
|
||||
|
||||
if (args.length < 1) return {
|
||||
error: true,
|
||||
msg: 'Missing reply name'
|
||||
};
|
||||
const [ _name, ...rest ] = args;
|
||||
|
||||
const name = _name.toLowerCase();
|
||||
const canned = this.client.modmail.replies;
|
||||
let confirmation = null;
|
||||
|
||||
if (op === 'create') {
|
||||
if (!rest.length) return {
|
||||
error: true,
|
||||
msg: 'Missing content'
|
||||
};
|
||||
|
||||
if (canned[name]) {
|
||||
confirmation = await this.client.prompt(`A canned reply by the name ${name} already exists, would you like to overwrite it?`, { channel, author });
|
||||
if (!confirmation) return 'Timed out.';
|
||||
confirmation = [ 'y', 'yes', 'ok' ].includes(confirmation.content.toLowerCase());
|
||||
if (!confirmation) return 'Cancelled';
|
||||
}
|
||||
|
||||
canned[name] = rest.join(' ');
|
||||
|
||||
} else {
|
||||
delete canned[name];
|
||||
}
|
||||
|
||||
this.client.modmail.saveReplies();
|
||||
return `Updated ${_name}`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const Command = require('../Command');
|
||||
|
||||
class CannedReply extends Command {
|
||||
|
||||
constructor (client) {
|
||||
super(client, {
|
||||
name: 'cannedreply',
|
||||
aliases: [ 'cr', 'canned' ],
|
||||
showUsage: true,
|
||||
usage: `<canned response name>`
|
||||
});
|
||||
}
|
||||
|
||||
async execute (message, { args }) {
|
||||
|
||||
const [ first, second ] = args.map((a) => a);
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { channel, content, _caller } = message,
|
||||
anon = false;
|
||||
content = content.replace(`${this.client.prefix}${_caller}`, '');
|
||||
const op = args.shift().toLowerCase();
|
||||
|
||||
if (op === 'anon') {
|
||||
anon = true;
|
||||
content = content.replace(first, '');
|
||||
} else if ([ 'create', 'delete' ].includes(op)) {
|
||||
return this.createCanned(op, args, message);
|
||||
} else if ([ 'list' ].includes(first.toLowerCase())) {
|
||||
|
||||
const list = Object.entries(this.client.modmail.replies);
|
||||
let str = '';
|
||||
if (second?.toLowerCase() !== '-here') channel = await message.author.createDM();
|
||||
// eslint-disable-next-line no-shadow
|
||||
for (const [ name, content ] of list) {
|
||||
const substr = `**${name}:** ${content}\n`;
|
||||
if (str.length + substr.length > 2000) {
|
||||
await channel.send(str);
|
||||
str = '';
|
||||
}
|
||||
str += substr;
|
||||
}
|
||||
if (str.length) return channel.send(str);
|
||||
return '**__None__**';
|
||||
|
||||
}
|
||||
|
||||
return this.client.modmail.sendCannedResponse({ message, responseName: content.trim(), anon });
|
||||
|
||||
}
|
||||
|
||||
async createCanned (op, args, { channel, author }) {
|
||||
|
||||
if (args.length < 1) return {
|
||||
error: true,
|
||||
msg: 'Missing reply name'
|
||||
};
|
||||
const [ _name, ...rest ] = args;
|
||||
|
||||
const name = _name.toLowerCase();
|
||||
const canned = this.client.modmail.replies;
|
||||
let confirmation = null;
|
||||
|
||||
if (op === 'create') {
|
||||
if (!rest.length) return {
|
||||
error: true,
|
||||
msg: 'Missing content'
|
||||
};
|
||||
|
||||
if (canned[name]) {
|
||||
confirmation = await this.client.prompt(`A canned reply by the name ${name} already exists, would you like to overwrite it?`, { channel, author });
|
||||
if (!confirmation) return 'Timed out.';
|
||||
confirmation = [ 'y', 'yes', 'ok' ].includes(confirmation.content.toLowerCase());
|
||||
if (!confirmation) return 'Cancelled';
|
||||
}
|
||||
|
||||
canned[name] = rest.join(' ');
|
||||
|
||||
} else {
|
||||
delete canned[name];
|
||||
}
|
||||
|
||||
this.client.modmail.saveReplies();
|
||||
return `Updated ${_name}`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = CannedReply;
|
@ -1,53 +1,51 @@
|
||||
const Command = require('../Command');
|
||||
|
||||
class Modmail extends Command {
|
||||
|
||||
constructor (client) {
|
||||
super(client, {
|
||||
name: 'modmail',
|
||||
aliases: [ 'mm' ],
|
||||
showUsage: true,
|
||||
usage: `<user> <content>`
|
||||
});
|
||||
}
|
||||
|
||||
async execute (message, { args }) {
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let [ first, second ] = args.map((a) => a);
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { content, _caller } = message,
|
||||
anon = false;
|
||||
content = content.replace(`${this.client.prefix}${_caller}`, '');
|
||||
if (first.toLowerCase() === 'anon') {
|
||||
anon = true;
|
||||
content = content.replace(first, '');
|
||||
first = second;
|
||||
} else if (second?.toLowerCase() === 'anon') {
|
||||
anon = true;
|
||||
content = content.replace(second, '');
|
||||
}
|
||||
|
||||
const user = await this.client.resolveUser(first, true);
|
||||
if (!user) return {
|
||||
error: true,
|
||||
msg: 'Failed to resolve user'
|
||||
};
|
||||
else if (user.bot) return {
|
||||
error: true,
|
||||
msg: 'Cannot send modmail to a bot.'
|
||||
};
|
||||
content = content.replace(first, '');
|
||||
|
||||
if (!content.length) return {
|
||||
error: true,
|
||||
msg: `Cannot send empty message`
|
||||
};
|
||||
|
||||
return this.client.modmail.sendModmail({ message, content: content.trim(), anon, target: user });
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const Command = require('../Command');
|
||||
|
||||
class Modmail extends Command {
|
||||
|
||||
constructor (client) {
|
||||
super(client, {
|
||||
name: 'modmail',
|
||||
aliases: [ 'mm' ],
|
||||
showUsage: true,
|
||||
usage: `<user> <content>`
|
||||
});
|
||||
}
|
||||
|
||||
async execute (message, { args }) {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let [ first, second ] = args.map((a) => a);
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { content, _caller } = message,
|
||||
anon = false;
|
||||
content = content.replace(`${this.client.prefix}${_caller}`, '');
|
||||
if (first.toLowerCase() === 'anon') {
|
||||
anon = true;
|
||||
content = content.replace(first, '');
|
||||
first = second;
|
||||
} else if (second?.toLowerCase() === 'anon') {
|
||||
anon = true;
|
||||
content = content.replace(second, '');
|
||||
}
|
||||
|
||||
const user = await this.client.resolveUser(first, true);
|
||||
if (!user) return {
|
||||
error: true,
|
||||
msg: 'Failed to resolve user'
|
||||
};
|
||||
else if (user.bot) return {
|
||||
error: true,
|
||||
msg: 'Cannot send modmail to a bot.'
|
||||
};
|
||||
content = content.replace(first, '').trim();
|
||||
|
||||
if (!content.length) return {
|
||||
error: true,
|
||||
msg: `Cannot send empty message`
|
||||
};
|
||||
|
||||
return this.client.modmail.sendModmail({ message, content, anon, target: user });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Modmail;
|
Loading…
Reference in New Issue
Block a user