code
This commit is contained in:
parent
e23e128850
commit
3e1da96360
10 changed files with 182 additions and 8 deletions
14
.vscode/launch.json
vendored
Normal file
14
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"command": "yarn dev",
|
||||||
|
"name": "Debug",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "node-terminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
17
src/bot/commands/debug.ts
Normal file
17
src/bot/commands/debug.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import Command from "../../struct/Command";
|
||||||
|
import { Message } from "revolt.js/dist/maps/Messages";
|
||||||
|
import { hasPerm } from "../util";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'debug',
|
||||||
|
aliases: null,
|
||||||
|
description: 'give info helpful for development and debugging',
|
||||||
|
serverOnly: false,
|
||||||
|
run: (message: Message, args: string[]) => {
|
||||||
|
message.reply(`Server ID: ${message.channel?.server_id || 'None'}\n`
|
||||||
|
+ `Channel ID: ${message.channel_id}\n`
|
||||||
|
+ `User ID: ${message.author_id}`);
|
||||||
|
|
||||||
|
console.log(hasPerm(message.member!, 'BanMembers'));
|
||||||
|
}
|
||||||
|
} as Command;
|
20
src/bot/commands/ping.ts
Normal file
20
src/bot/commands/ping.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import Command from "../../struct/Command";
|
||||||
|
import { Message } from "revolt.js/dist/maps/Messages";
|
||||||
|
import { client } from "../..";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ping',
|
||||||
|
aliases: null,
|
||||||
|
description: 'ping pong',
|
||||||
|
serverOnly: false,
|
||||||
|
run: async (message: Message, args: string[]) => {
|
||||||
|
let now = Date.now();
|
||||||
|
message.reply(`Measuring...`)
|
||||||
|
?.catch(console.error)
|
||||||
|
.then(msg => {
|
||||||
|
msg?.edit({ content: `## Ping Pong!\n`
|
||||||
|
+ `WS: \`${client.websocket.ping ?? '--'}ms\`\n`
|
||||||
|
+ `Msg: \`${Math.round(Date.now() - now) / 2}ms\`` });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} as Command;
|
74
src/bot/commands/prefix.ts
Normal file
74
src/bot/commands/prefix.ts
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import Command from "../../struct/Command";
|
||||||
|
import { Message } from "revolt.js/dist/maps/Messages";
|
||||||
|
import { client } from "../..";
|
||||||
|
import ServerConfig from "../../struct/ServerConfig";
|
||||||
|
import { DEFAULT_PREFIX } from "../modules/command_handler";
|
||||||
|
import { hasPerm } from "../util";
|
||||||
|
|
||||||
|
const SYNTAX = '/prefix set [new prefix]; /prefix get; prefix clear';
|
||||||
|
const MENTION_TEXT = 'You can also @mention me instead of using the prefix.';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'prefix',
|
||||||
|
aliases: null,
|
||||||
|
description: 'modify prefix',
|
||||||
|
syntax: SYNTAX,
|
||||||
|
serverOnly: true,
|
||||||
|
run: async (message: Message, args: string[]) => {
|
||||||
|
let config: ServerConfig = (await client.db.get('servers').findOne({ id: message.channel?.server_id })) ?? {};
|
||||||
|
switch(args[0]?.toLowerCase()) {
|
||||||
|
case 'set':
|
||||||
|
if (!hasPerm(message.member!, 'ManageServer')) return message.reply('You need ManageServer permission for this.');
|
||||||
|
|
||||||
|
args.shift();
|
||||||
|
if (args.length == 0) return message.reply('You need to specify a prefix.');
|
||||||
|
let newPrefix = args.join(' ').trim();
|
||||||
|
let oldPrefix = config.prefix ?? DEFAULT_PREFIX;
|
||||||
|
|
||||||
|
let val = validatePrefix(newPrefix);
|
||||||
|
if (typeof val != 'boolean') {
|
||||||
|
return message.reply(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.db.get('servers').update({ 'id': message.channel?.server_id }, { $set: { 'prefix': newPrefix } });
|
||||||
|
|
||||||
|
message.reply(`✅ Prefix has been changed from \`${oldPrefix}\` to \`${newPrefix}\`.\n${MENTION_TEXT}`);
|
||||||
|
break;
|
||||||
|
case 'get':
|
||||||
|
case undefined:
|
||||||
|
if (config.prefix) message.reply(`This server's prefix is \`${config.prefix}\`.\n${MENTION_TEXT}`);
|
||||||
|
else message.reply(`This server uses the default prefix \`${DEFAULT_PREFIX}\`.\n${MENTION_TEXT}`);
|
||||||
|
break;
|
||||||
|
case 'clear':
|
||||||
|
case 'reset':
|
||||||
|
if (!hasPerm(message.member!, 'ManageServer')) return message.reply('You need ManageServer permission for this.');
|
||||||
|
|
||||||
|
if (config.prefix != null) {
|
||||||
|
await client.db.get('servers').update({ 'id': message.channel?.server_id }, { $set: { 'prefix': null } });
|
||||||
|
}
|
||||||
|
|
||||||
|
message.reply(`✅ Prefix has been reset to the default: \`${DEFAULT_PREFIX}\`.`);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
message.reply(`Unknown action. Correct syntax: \`${SYNTAX}\``);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} as Command;
|
||||||
|
|
||||||
|
function validatePrefix(prefix: string): string|true {
|
||||||
|
// Check length
|
||||||
|
if (prefix.length > 32) return 'Prefix may not be longer than 32 characters';
|
||||||
|
|
||||||
|
// Check for forbidden characters
|
||||||
|
let matched = [];
|
||||||
|
for (const char of ['`', '\n', '#']) {
|
||||||
|
if (prefix.indexOf(char) > -1) matched.push(char);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matched.length > 0) return `Prefix may not contain the following characters: `
|
||||||
|
+ `${matched.map(char => char).join(', ')
|
||||||
|
.replace(new RegExp('\n', 'g'), '\\n')}`;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
import Command from "../../struct/Command";
|
import Command from "../../struct/Command";
|
||||||
import { client } from "../..";
|
|
||||||
import { Message } from "revolt.js/dist/maps/Messages";
|
import { Message } from "revolt.js/dist/maps/Messages";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -10,4 +9,4 @@ export default {
|
||||||
run: (message: Message, args: string[]) => {
|
run: (message: Message, args: string[]) => {
|
||||||
message.reply('I am here');
|
message.reply('I am here');
|
||||||
}
|
}
|
||||||
} as Command;
|
} as Command;
|
||||||
|
|
|
@ -20,9 +20,9 @@ function getDBUrl() {
|
||||||
|
|
||||||
// mongodb://username:password@hostname:port/dbname
|
// mongodb://username:password@hostname:port/dbname
|
||||||
let dburl = 'mongodb://';
|
let dburl = 'mongodb://';
|
||||||
dburl += env['DB_USERNAME'] ?? 'root';
|
if (env['DB_USERNAME']) dburl += env['DB_USERNAME'];
|
||||||
if (env['DB_PASSWORD']) dburl += `:${env['DB_PASSWORD']}`;
|
if (env['DB_PASS']) dburl += `:${env['DB_PASS']}`;
|
||||||
dburl += `@${env['DB_HOST']}`; // DB_HOST is assumed to contain the port
|
dburl += `${process.env['DB_USERNAME'] ? '@' : ''}${env['DB_HOST']}`; // DB_HOST is assumed to contain the port
|
||||||
dburl += `/${env['DB_NAME'] ?? 'automod'}`;
|
dburl += `/${env['DB_NAME'] ?? 'automod'}`;
|
||||||
|
|
||||||
return dburl;
|
return dburl;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import logger from "../logger";
|
||||||
import { client } from "../../index";
|
import { client } from "../../index";
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import ServerConfig from "../../struct/ServerConfig";
|
||||||
|
|
||||||
const DEFAULT_PREFIX = process.env['PREFIX'] ?? '/';
|
const DEFAULT_PREFIX = process.env['PREFIX'] ?? '/';
|
||||||
|
|
||||||
|
@ -12,17 +13,29 @@ let commands: Command[] = fs.readdirSync(path.join(__dirname, '..', 'commands'))
|
||||||
|
|
||||||
client.on('message', async message => {
|
client.on('message', async message => {
|
||||||
logger.debug(`Message -> ${message.content}`);
|
logger.debug(`Message -> ${message.content}`);
|
||||||
if (typeof message.content != 'string') return; // Ignore system messages
|
if (typeof message.content != 'string' || message.author_id == client.user?._id || !message.channel) return;
|
||||||
|
|
||||||
if (!message.content.startsWith(DEFAULT_PREFIX)) return;
|
let config: ServerConfig = (await client.db.get('servers').findOne({ 'id': message.channel?.server_id })) ?? {};
|
||||||
|
let guildPrefix = config.prefix ?? DEFAULT_PREFIX;
|
||||||
|
|
||||||
let args = message.content.split(' ');
|
let args = message.content.split(' ');
|
||||||
let cmdName = args.shift()?.substr(DEFAULT_PREFIX.length);
|
let cmdName = args.shift() ?? '';
|
||||||
|
|
||||||
|
if (cmdName.startsWith(`<@${client.user?._id}>`)) {
|
||||||
|
cmdName = cmdName.substr(`<@${client.user?._id}>`.length);
|
||||||
|
if (!cmdName) cmdName = args.shift() ?? ''; // Space between mention and command name
|
||||||
|
} else if (cmdName.startsWith(guildPrefix)) {
|
||||||
|
cmdName = cmdName.substr(guildPrefix.length);
|
||||||
|
if (config.spaceAfterPrefix && !cmdName) cmdName = args.shift() ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
if (!cmdName) return;
|
if (!cmdName) return;
|
||||||
|
|
||||||
let cmd = commands.find(c => c.name == cmdName || (c.aliases?.indexOf(cmdName!) ?? -1) > -1);
|
let cmd = commands.find(c => c.name == cmdName || (c.aliases?.indexOf(cmdName!) ?? -1) > -1);
|
||||||
if (!cmd) return;
|
if (!cmd) return;
|
||||||
|
|
||||||
|
logger.info(`Command: ${message.author?.username} in ${message.channel?.server?.name}: ${message.content}`);
|
||||||
|
|
||||||
if (cmd.serverOnly && !message.channel?.server) {
|
if (cmd.serverOnly && !message.channel?.server) {
|
||||||
return message.reply('This command is not available in direct messages.');
|
return message.reply('This command is not available in direct messages.');
|
||||||
}
|
}
|
||||||
|
@ -33,3 +46,5 @@ client.on('message', async message => {
|
||||||
message.reply(`### An error has occurred:\n\`\`\`js\n${e}\n\`\`\``);
|
message.reply(`### An error has occurred:\n\`\`\`js\n${e}\n\`\`\``);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export { DEFAULT_PREFIX }
|
||||||
|
|
27
src/bot/util.ts
Normal file
27
src/bot/util.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { Member } from "revolt.js/dist/maps/Members";
|
||||||
|
|
||||||
|
let ServerPermissions = {
|
||||||
|
['View' as string]: 1 << 0,
|
||||||
|
['ManageRoles' as string]: 1 << 1,
|
||||||
|
['ManageChannels' as string]: 1 << 2,
|
||||||
|
['ManageServer' as string]: 1 << 3,
|
||||||
|
['KickMembers' as string]: 1 << 4,
|
||||||
|
['BanMembers' as string]: 1 << 5,
|
||||||
|
['ChangeNickname' as string]: 1 << 12,
|
||||||
|
['ManageNicknames' as string]: 1 << 13,
|
||||||
|
['ChangeAvatar' as string]: 1 << 14,
|
||||||
|
['RemoveAvatars' as string]: 1 << 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function hasPerm(member: Member, perm: 'View'|'ManageRoles'|'ManageChannels'|'ManageServer'| // its late and im tired
|
||||||
|
'KickMembers'|'BanMembers'|'ChangeNickname'| // dont judge my code
|
||||||
|
'ManageNicknames'|'ChangeAvatar'|'RemoveAvatars') {
|
||||||
|
let p = ServerPermissions[perm];
|
||||||
|
if (member.server?.owner == member.user?._id) return true;
|
||||||
|
|
||||||
|
// TODO how the fuck do bitfields work
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { hasPerm }
|
|
@ -2,6 +2,7 @@ class Command {
|
||||||
name: string;
|
name: string;
|
||||||
aliases: string[] | null;
|
aliases: string[] | null;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
|
syntax?: string | null;
|
||||||
run: Function;
|
run: Function;
|
||||||
serverOnly: boolean;
|
serverOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
7
src/struct/ServerConfig.ts
Normal file
7
src/struct/ServerConfig.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class ServerConfig {
|
||||||
|
id: string | undefined;
|
||||||
|
prefix: string | undefined;
|
||||||
|
spaceAfterPrefix: boolean | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ServerConfig;
|
Loading…
Reference in a new issue