H
This commit is contained in:
parent
3e1da96360
commit
0d198c269b
16 changed files with 383 additions and 11 deletions
|
@ -13,10 +13,12 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/monk": "^6.0.0",
|
"@types/monk": "^6.0.0",
|
||||||
|
"axios": "^0.22.0",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"log75": "2.0.1",
|
"log75": "2.0.1",
|
||||||
"monk": "^7.3.4",
|
"monk": "^7.3.4",
|
||||||
"revolt.js": "^5.1.0-alpha.6"
|
"revolt.js": "^5.1.0-alpha.6",
|
||||||
|
"ulid": "^2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^4.4.3"
|
"typescript": "^4.4.3"
|
||||||
|
|
66
src/bot/commands/bot_managers.ts
Normal file
66
src/bot/commands/bot_managers.ts
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import Command from "../../struct/Command";
|
||||||
|
import { Message } from "revolt.js/dist/maps/Messages";
|
||||||
|
import { hasPerm, parseUser } from "../util";
|
||||||
|
import ServerConfig from "../../struct/ServerConfig";
|
||||||
|
import { client } from "../..";
|
||||||
|
import { User } from "revolt.js/dist/maps/Users";
|
||||||
|
|
||||||
|
const SYNTAX = '/admin add @user; /admin remove @user; /admin list';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'admin',
|
||||||
|
aliases: [ 'admins', 'manager', 'managers' ],
|
||||||
|
description: 'Allow users to control the bot\'s configuration',
|
||||||
|
syntax: SYNTAX,
|
||||||
|
serverOnly: true,
|
||||||
|
run: async (message: Message, args: string[]) => {
|
||||||
|
if (!hasPerm(message.member!, 'ManageServer'))
|
||||||
|
return message.reply('You need **ManageServer** permission to use this command.');
|
||||||
|
|
||||||
|
let config: ServerConfig = (await client.db.get('servers').findOne({ id: message.channel?.server_id })) ?? {};
|
||||||
|
let admins = config.botManagers ?? [];
|
||||||
|
let user: User|null;
|
||||||
|
|
||||||
|
switch(args[0]?.toLowerCase()) {
|
||||||
|
case 'add':
|
||||||
|
case 'new':
|
||||||
|
if (!args[1]) return message.reply('No user specified.');
|
||||||
|
user = await parseUser(args[1]);
|
||||||
|
if (!user) return message.reply('I can\'t find that user.');
|
||||||
|
|
||||||
|
if (admins.indexOf(user._id) > -1) return message.reply('This user is already added as bot admin.');
|
||||||
|
|
||||||
|
admins.push(user._id);
|
||||||
|
await client.db.get('servers').update({ id: message.channel?.server_id }, { $set: { botManagers: admins } });
|
||||||
|
|
||||||
|
message.reply(`✅ Added \`@${user.username}\` to bot admins.`);
|
||||||
|
break;
|
||||||
|
case 'remove':
|
||||||
|
case 'delete':
|
||||||
|
case 'rm':
|
||||||
|
case 'del':
|
||||||
|
if (!args[1]) return message.reply('No user specified.');
|
||||||
|
user = await parseUser(args[1]);
|
||||||
|
if (!user) return message.reply('I can\'t find that user.');
|
||||||
|
|
||||||
|
if (admins.indexOf(user._id) == -1) return message.reply('This user is not added as bot admin.');
|
||||||
|
|
||||||
|
admins = admins.filter(a => a != user?._id);
|
||||||
|
await client.db.get('servers').update({ id: message.channel?.server_id }, { $set: { botManagers: admins } });
|
||||||
|
|
||||||
|
message.reply(`✅ Removed \`@${user.username}\` from bot admins.`);
|
||||||
|
break;
|
||||||
|
case 'list':
|
||||||
|
case 'ls':
|
||||||
|
case 'show':
|
||||||
|
message.reply(`# Bot admins\n`
|
||||||
|
+ `Users with **ManageServer** permission can add or remove admins.\n\n`
|
||||||
|
+ `${admins.map(a => `* <@${a}>`).join('\n')}\n\n`
|
||||||
|
+ `${admins.length} user${admins.length == 1 ? '' : 's'}.`)
|
||||||
|
?.catch(e => message.reply(e));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
message.reply(`Available subcommands: ${SYNTAX}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as Command;
|
|
@ -11,7 +11,5 @@ export default {
|
||||||
message.reply(`Server ID: ${message.channel?.server_id || 'None'}\n`
|
message.reply(`Server ID: ${message.channel?.server_id || 'None'}\n`
|
||||||
+ `Channel ID: ${message.channel_id}\n`
|
+ `Channel ID: ${message.channel_id}\n`
|
||||||
+ `User ID: ${message.author_id}`);
|
+ `User ID: ${message.author_id}`);
|
||||||
|
|
||||||
console.log(hasPerm(message.member!, 'BanMembers'));
|
|
||||||
}
|
}
|
||||||
} as Command;
|
} as Command;
|
||||||
|
|
42
src/bot/commands/eval.ts
Normal file
42
src/bot/commands/eval.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import Command from "../../struct/Command";
|
||||||
|
import { Message } from "revolt.js/dist/maps/Messages";
|
||||||
|
import { inspect } from 'util';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'eval',
|
||||||
|
aliases: [ 'e' ],
|
||||||
|
description: 'Evaluate JS code',
|
||||||
|
restrict: 'BOTOWNER',
|
||||||
|
removeEmptyArgs: false,
|
||||||
|
serverOnly: false,
|
||||||
|
run: async (message: Message, args: string[]) => {
|
||||||
|
let cmd = `let { client } = require("../..");`
|
||||||
|
+ `let axios = require("axios").default;`
|
||||||
|
+ `let crypto = require("crypto");`
|
||||||
|
+ args.join(' ');
|
||||||
|
|
||||||
|
let m = await message.channel?.sendMessage(`Executing...`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
let e = eval(cmd);
|
||||||
|
|
||||||
|
if (e instanceof Promise) {
|
||||||
|
await m?.edit({ content: `## **Promise**<pending>` });
|
||||||
|
e.then((res) => {
|
||||||
|
m?.edit({
|
||||||
|
content: `## **Promise**<resolved>\n\`\`\`js\n${`${inspect(res)}`.substr(0, 1960)}\n\`\`\``
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((res) => {
|
||||||
|
m?.edit({
|
||||||
|
content: `## **Promise**<rejected>\n\`\`\`js\n${`${inspect(res)}`.substr(0, 1960)}\n\`\`\``
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.channel?.sendMessage(`\`\`\`js\n${inspect(e).substr(0, 1980)}\n\`\`\``);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
m?.edit({ content: `## Execution failed\n\`\`\`js\n${inspect(e).substr(0, 1960)}\n\`\`\`` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as Command;
|
|
@ -3,7 +3,7 @@ import { Message } from "revolt.js/dist/maps/Messages";
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
import ServerConfig from "../../struct/ServerConfig";
|
import ServerConfig from "../../struct/ServerConfig";
|
||||||
import { DEFAULT_PREFIX } from "../modules/command_handler";
|
import { DEFAULT_PREFIX } from "../modules/command_handler";
|
||||||
import { hasPerm } from "../util";
|
import { hasPerm, isBotManager, NO_MANAGER_MSG } from "../util";
|
||||||
|
|
||||||
const SYNTAX = '/prefix set [new prefix]; /prefix get; prefix clear';
|
const SYNTAX = '/prefix set [new prefix]; /prefix get; prefix clear';
|
||||||
const MENTION_TEXT = 'You can also @mention me instead of using the prefix.';
|
const MENTION_TEXT = 'You can also @mention me instead of using the prefix.';
|
||||||
|
@ -16,9 +16,10 @@ export default {
|
||||||
serverOnly: true,
|
serverOnly: true,
|
||||||
run: async (message: Message, args: string[]) => {
|
run: async (message: Message, args: string[]) => {
|
||||||
let config: ServerConfig = (await client.db.get('servers').findOne({ id: message.channel?.server_id })) ?? {};
|
let config: ServerConfig = (await client.db.get('servers').findOne({ id: message.channel?.server_id })) ?? {};
|
||||||
|
|
||||||
switch(args[0]?.toLowerCase()) {
|
switch(args[0]?.toLowerCase()) {
|
||||||
case 'set':
|
case 'set':
|
||||||
if (!hasPerm(message.member!, 'ManageServer')) return message.reply('You need ManageServer permission for this.');
|
if (!await isBotManager(message.member!)) return message.reply(NO_MANAGER_MSG);
|
||||||
|
|
||||||
args.shift();
|
args.shift();
|
||||||
if (args.length == 0) return message.reply('You need to specify a prefix.');
|
if (args.length == 0) return message.reply('You need to specify a prefix.');
|
||||||
|
@ -41,7 +42,7 @@ export default {
|
||||||
break;
|
break;
|
||||||
case 'clear':
|
case 'clear':
|
||||||
case 'reset':
|
case 'reset':
|
||||||
if (!hasPerm(message.member!, 'ManageServer')) return message.reply('You need ManageServer permission for this.');
|
if (!await isBotManager(message.member!)) return message.reply(NO_MANAGER_MSG);
|
||||||
|
|
||||||
if (config.prefix != null) {
|
if (config.prefix != null) {
|
||||||
await client.db.get('servers').update({ 'id': message.channel?.server_id }, { $set: { 'prefix': null } });
|
await client.db.get('servers').update({ 'id': message.channel?.server_id }, { $set: { 'prefix': null } });
|
||||||
|
|
33
src/bot/commands/settings.ts
Normal file
33
src/bot/commands/settings.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import Command from "../../struct/Command";
|
||||||
|
import { Message } from "revolt.js/dist/maps/Messages";
|
||||||
|
import { client } from "../..";
|
||||||
|
import AutomodSettings from "../../struct/antispam/AutomodSettings";
|
||||||
|
import AntispamRule from "../../struct/antispam/AntispamRule";
|
||||||
|
import ModerationAction from "../../struct/antispam/ModerationAction";
|
||||||
|
import { isBotManager, NO_MANAGER_MSG } from "../util";
|
||||||
|
import { ulid } from 'ulid';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'settings',
|
||||||
|
aliases: [ 'setting' ],
|
||||||
|
description: 'change antispam settings',
|
||||||
|
serverOnly: false,
|
||||||
|
run: async (message: Message, args: string[]) => {
|
||||||
|
if (!isBotManager(message.member!)) return message.reply(NO_MANAGER_MSG);
|
||||||
|
|
||||||
|
let settings = {
|
||||||
|
spam: [
|
||||||
|
{
|
||||||
|
id: ulid(),
|
||||||
|
max_msg: 5,
|
||||||
|
timeframe: 3,
|
||||||
|
action: ModerationAction.Delete,
|
||||||
|
channels: [ '01FHJD5D2PBRTEVPNFM1FRY85J' ],
|
||||||
|
} as AntispamRule
|
||||||
|
]
|
||||||
|
} as AutomodSettings;
|
||||||
|
|
||||||
|
client.db.get('servers')
|
||||||
|
.update({ id: message.channel?.server_id }, { $set: { automodSettings: settings } });
|
||||||
|
}
|
||||||
|
} as Command;
|
46
src/bot/commands/shell_eval.ts
Normal file
46
src/bot/commands/shell_eval.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import Command from "../../struct/Command";
|
||||||
|
import { Message } from "revolt.js/dist/maps/Messages";
|
||||||
|
import { exec } from 'child_process';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'shell',
|
||||||
|
aliases: [ 'exec', 'sh' ],
|
||||||
|
description: 'Run code in a shell',
|
||||||
|
restrict: 'BOTOWNER',
|
||||||
|
removeEmptyArgs: false,
|
||||||
|
serverOnly: false,
|
||||||
|
run: async (message: Message, args: string[]) => {
|
||||||
|
let cmd = args.join(' ');
|
||||||
|
|
||||||
|
let m = await message.channel?.sendMessage(`Executing...`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
let editMsg = () => {
|
||||||
|
if (str != '' && str != oldStr) {
|
||||||
|
if (str.length > 2000) {
|
||||||
|
str = str.substr(str.length - 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
m?.edit({ content: str })
|
||||||
|
.catch(e => console.warn('Failed to edit message'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let str = '', oldStr = '';
|
||||||
|
let e = exec(cmd);
|
||||||
|
let i = setInterval(editMsg, 1000);
|
||||||
|
|
||||||
|
e.stdout?.on('data', m => {
|
||||||
|
str += m;
|
||||||
|
});
|
||||||
|
|
||||||
|
e.on('exit', (code) => {
|
||||||
|
clearInterval(i);
|
||||||
|
str += `\n\n**Exit code:** ${code}`;
|
||||||
|
editMsg();
|
||||||
|
});
|
||||||
|
} catch(e) {
|
||||||
|
message.channel?.sendMessage(`${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as Command;
|
65
src/bot/modules/antispam.ts
Normal file
65
src/bot/modules/antispam.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { Message } from "revolt.js/dist/maps/Messages";
|
||||||
|
import { client } from "../..";
|
||||||
|
import ModerationAction from "../../struct/antispam/ModerationAction";
|
||||||
|
import ServerConfig from "../../struct/ServerConfig";
|
||||||
|
import logger from "../logger";
|
||||||
|
|
||||||
|
let msgCountStore: Map<string, { users: any }> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* @returns true if ok, false if spam rule triggered
|
||||||
|
*/
|
||||||
|
async function antispam(message: Message): Promise<boolean> {
|
||||||
|
let serverRules: ServerConfig = await client.db.get('servers').findOne({ id: message.channel?.server_id }) ?? {};
|
||||||
|
if (!serverRules.automodSettings) return true;
|
||||||
|
|
||||||
|
let ruleTriggered = false;
|
||||||
|
|
||||||
|
for (const rule of serverRules.automodSettings.spam) {
|
||||||
|
if (msgCountStore.get(rule.id) == null) {
|
||||||
|
msgCountStore.set(rule.id, { users: {} });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule.channels?.indexOf(message.channel_id) == -1) break;
|
||||||
|
|
||||||
|
let store = msgCountStore.get(rule.id)!;
|
||||||
|
if (!store.users[message.channel_id]) store.users[message.channel_id] = {}
|
||||||
|
let userStore = store.users[message.channel_id];
|
||||||
|
|
||||||
|
if (!userStore.count) userStore.count = 1;
|
||||||
|
else userStore.count++;
|
||||||
|
|
||||||
|
setTimeout(() => userStore.count--, rule.timeframe * 1000);
|
||||||
|
|
||||||
|
if (userStore.count > rule.max_msg) {
|
||||||
|
logger.info(`Antispam rule triggered: ${rule.max_msg}/${rule.timeframe} -> ${ModerationAction[rule.action]}`);
|
||||||
|
ruleTriggered = true;
|
||||||
|
|
||||||
|
switch(rule.action) {
|
||||||
|
case ModerationAction.Delete:
|
||||||
|
message.delete()
|
||||||
|
.catch(() => logger.warn('Antispam: Failed to delete message') );
|
||||||
|
break;
|
||||||
|
case ModerationAction.Warn:
|
||||||
|
if (!userStore.warnTriggered) {
|
||||||
|
userStore.warnTriggered = true;
|
||||||
|
setTimeout(() => userStore.warnTriggered = false, 5000);
|
||||||
|
message.channel?.sendMessage(`<@${message.author_id}>, stop spamming (placeholder warn message)`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ModerationAction.Kick:
|
||||||
|
message.reply('(Kick user)');
|
||||||
|
break;
|
||||||
|
case ModerationAction.Ban:
|
||||||
|
message.reply('(Ban user)');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !ruleTriggered;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { antispam }
|
|
@ -4,6 +4,7 @@ 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";
|
import ServerConfig from "../../struct/ServerConfig";
|
||||||
|
import { antispam } from "./antispam";
|
||||||
|
|
||||||
const DEFAULT_PREFIX = process.env['PREFIX'] ?? '/';
|
const DEFAULT_PREFIX = process.env['PREFIX'] ?? '/';
|
||||||
|
|
||||||
|
@ -15,6 +16,9 @@ client.on('message', async message => {
|
||||||
logger.debug(`Message -> ${message.content}`);
|
logger.debug(`Message -> ${message.content}`);
|
||||||
if (typeof message.content != 'string' || message.author_id == client.user?._id || !message.channel) return;
|
if (typeof message.content != 'string' || message.author_id == client.user?._id || !message.channel) return;
|
||||||
|
|
||||||
|
// Send message through anti spam check
|
||||||
|
if (!antispam(message)) return;
|
||||||
|
|
||||||
let config: ServerConfig = (await client.db.get('servers').findOne({ 'id': message.channel?.server_id })) ?? {};
|
let config: ServerConfig = (await client.db.get('servers').findOne({ 'id': message.channel?.server_id })) ?? {};
|
||||||
let guildPrefix = config.prefix ?? DEFAULT_PREFIX;
|
let guildPrefix = config.prefix ?? DEFAULT_PREFIX;
|
||||||
|
|
||||||
|
@ -34,12 +38,26 @@ client.on('message', async message => {
|
||||||
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;
|
||||||
|
|
||||||
|
let ownerIDs = process.env['BOT_OWNERS'] ? process.env['BOT_OWNERS'].split(',') : [];
|
||||||
|
if (cmd.restrict == 'BOTOWNER' && ownerIDs.indexOf(message.author_id) == -1) {
|
||||||
|
logger.warn(`User ${message.author?.username} tried to run owner-only command: ${cmdName}`);
|
||||||
|
message.reply('🔒 Access denied');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
logger.info(`Command: ${message.author?.username} in ${message.channel?.server?.name}: ${message.content}`);
|
logger.info(`Command: ${message.author?.username} in ${message.channel?.server?.name}: ${message.content}`);
|
||||||
|
|
||||||
|
// Create document for server in DB, if not already present
|
||||||
|
if (JSON.stringify(config) == '{}') await client.db.get('servers').insert({ id: message.channel?.server_id });
|
||||||
|
|
||||||
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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cmd.removeEmptyArgs !== false) {
|
||||||
|
args = args.filter(a => a.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cmd.run(message, args);
|
cmd.run(message, args);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import { Member } from "revolt.js/dist/maps/Members";
|
import { Member } from "revolt.js/dist/maps/Members";
|
||||||
|
import { User } from "revolt.js/dist/maps/Users";
|
||||||
|
import { client } from "..";
|
||||||
|
import ServerConfig from "../struct/ServerConfig";
|
||||||
|
|
||||||
let ServerPermissions = {
|
let ServerPermissions = {
|
||||||
['View' as string]: 1 << 0,
|
['View' as string]: 1 << 0,
|
||||||
|
@ -13,15 +16,66 @@ let ServerPermissions = {
|
||||||
['RemoveAvatars' as string]: 1 << 15,
|
['RemoveAvatars' as string]: 1 << 15,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NO_MANAGER_MSG = '🔒 Missing permission';
|
||||||
|
const USER_MENTION_REGEX = /^<@[0-9A-HJ-KM-NP-TV-Z]{26}>$/i;
|
||||||
|
const CHANNEL_MENTION_REGEX = /^<#[0-9A-HJ-KM-NP-TV-Z]{26}>$/i;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses user input and returns an user object.
|
||||||
|
* Supports: `userID`, `<@userID>` (mention), `username`, `@username` (if user is cached).
|
||||||
|
* @param text
|
||||||
|
* @returns null if not found, otherwise user object
|
||||||
|
*/
|
||||||
|
async function parseUser(text: string): Promise<User|null> {
|
||||||
|
if (!text) return null;
|
||||||
|
|
||||||
|
let uid: string|null = null;
|
||||||
|
if (USER_MENTION_REGEX.test(text)) {
|
||||||
|
uid = text.replace(/<@|>/g, '').toUpperCase();
|
||||||
|
} else if (/^[0-9A-HJ-KM-NP-TV-Z]{26}$/gi.test(text)) {
|
||||||
|
uid = text.toUpperCase();
|
||||||
|
} else {
|
||||||
|
if (text.startsWith('@')) text = text.substr(1);
|
||||||
|
|
||||||
|
// Why is there no .find() or .filter()
|
||||||
|
let user: User|null = null;
|
||||||
|
client.users.forEach(u => {
|
||||||
|
if (u.username?.toLowerCase() == text.toLowerCase()) {
|
||||||
|
user = u;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (user) return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uid) return await client.users.fetch(uid) || null;
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function isBotManager(member: Member) {
|
||||||
|
return hasPerm(member, 'ManageServer')
|
||||||
|
|| (((await client.db.get('servers').findOne({ id: member.server?._id }) || {}) as ServerConfig)
|
||||||
|
.botManagers?.indexOf(member.user?._id!) ?? -1) > -1;
|
||||||
|
}
|
||||||
|
|
||||||
function hasPerm(member: Member, perm: 'View'|'ManageRoles'|'ManageChannels'|'ManageServer'| // its late and im tired
|
function hasPerm(member: Member, perm: 'View'|'ManageRoles'|'ManageChannels'|'ManageServer'| // its late and im tired
|
||||||
'KickMembers'|'BanMembers'|'ChangeNickname'| // dont judge my code
|
'KickMembers'|'BanMembers'|'ChangeNickname'| // dont judge my code
|
||||||
'ManageNicknames'|'ChangeAvatar'|'RemoveAvatars') {
|
'ManageNicknames'|'ChangeAvatar'|'RemoveAvatars'): boolean {
|
||||||
let p = ServerPermissions[perm];
|
let p = ServerPermissions[perm];
|
||||||
if (member.server?.owner == member.user?._id) return true;
|
if (member.server?.owner == member.user?._id) return true;
|
||||||
|
|
||||||
// TODO how the fuck do bitfields work
|
// this should work but im not 100% certain
|
||||||
return false;
|
let userPerm = member.roles?.map(id => member.server?.roles?.[id])
|
||||||
|
.reduce((sum: number, cur: any) => sum | cur.permissions[0], member.server?.default_permissions[0]) ?? 0;
|
||||||
|
|
||||||
|
return !!(userPerm & p);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { hasPerm }
|
export {
|
||||||
|
hasPerm,
|
||||||
|
isBotManager,
|
||||||
|
parseUser,
|
||||||
|
NO_MANAGER_MSG,
|
||||||
|
USER_MENTION_REGEX,
|
||||||
|
CHANNEL_MENTION_REGEX
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ class Command {
|
||||||
aliases: string[] | null;
|
aliases: string[] | null;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
syntax?: string | null;
|
syntax?: string | null;
|
||||||
|
restrict?: 'BOTOWNER' | null;
|
||||||
|
removeEmptyArgs?: boolean | null;
|
||||||
run: Function;
|
run: Function;
|
||||||
serverOnly: boolean;
|
serverOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
|
import AutomodSettings from "./antispam/AutomodSettings";
|
||||||
|
|
||||||
class ServerConfig {
|
class ServerConfig {
|
||||||
id: string | undefined;
|
id: string | undefined;
|
||||||
prefix: string | undefined;
|
prefix: string | undefined;
|
||||||
spaceAfterPrefix: boolean | undefined;
|
spaceAfterPrefix: boolean | undefined;
|
||||||
|
automodSettings: AutomodSettings | undefined;
|
||||||
|
botManagers: string[] | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ServerConfig;
|
export default ServerConfig;
|
||||||
|
|
19
src/struct/antispam/AntispamRule.ts
Normal file
19
src/struct/antispam/AntispamRule.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import ModerationAction from "./ModerationAction";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow a maximum of X messages per X seconds.
|
||||||
|
* Example: max_msg = 5, timeframe = 3, action: Delete
|
||||||
|
* Allows a maximum of 5 messages within 3 seconds,
|
||||||
|
* and will delete any additional messages.
|
||||||
|
*
|
||||||
|
* `channels` optionally limits the rule to specific channels.
|
||||||
|
*/
|
||||||
|
class AntispamRule {
|
||||||
|
id: string;
|
||||||
|
max_msg: number;
|
||||||
|
timeframe: number;
|
||||||
|
action: ModerationAction;
|
||||||
|
channels: string[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AntispamRule;
|
7
src/struct/antispam/AutomodSettings.ts
Normal file
7
src/struct/antispam/AutomodSettings.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import AntispamRule from "./AntispamRule";
|
||||||
|
|
||||||
|
class AutomodSettings {
|
||||||
|
spam: AntispamRule[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AutomodSettings;
|
8
src/struct/antispam/ModerationAction.ts
Normal file
8
src/struct/antispam/ModerationAction.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
enum ModerationAction {
|
||||||
|
Delete = 0,
|
||||||
|
Warn = 1,
|
||||||
|
Kick = 2,
|
||||||
|
Ban = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModerationAction;
|
|
@ -41,6 +41,13 @@ axios@^0.21.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects "^1.14.0"
|
follow-redirects "^1.14.0"
|
||||||
|
|
||||||
|
axios@^0.22.0:
|
||||||
|
version "0.22.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.22.0.tgz#bf702c41fb50fbca4539589d839a077117b79b25"
|
||||||
|
integrity sha512-Z0U3uhqQeg1oNcihswf4ZD57O3NrR1+ZXhxaROaWpDmsDTx7T2HNBV2ulBtie2hwJptu8UvgnJoK+BIqdzh/1w==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.14.4"
|
||||||
|
|
||||||
base64-js@^1.3.1:
|
base64-js@^1.3.1:
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
|
@ -106,7 +113,7 @@ exponential-backoff@^3.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.0.tgz#9409c7e579131f8bd4b32d7d8094a911040f2e68"
|
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.0.tgz#9409c7e579131f8bd4b32d7d8094a911040f2e68"
|
||||||
integrity sha512-oBuz5SYz5zzyuHINoe9ooePwSu0xApKWgeNzok4hZ5YKXFh9zrQBEM15CXqoZkJJPuI2ArvqjPQd8UKJA753XA==
|
integrity sha512-oBuz5SYz5zzyuHINoe9ooePwSu0xApKWgeNzok4hZ5YKXFh9zrQBEM15CXqoZkJJPuI2ArvqjPQd8UKJA753XA==
|
||||||
|
|
||||||
follow-redirects@^1.14.0:
|
follow-redirects@^1.14.0, follow-redirects@^1.14.4:
|
||||||
version "1.14.4"
|
version "1.14.4"
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379"
|
||||||
integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==
|
integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==
|
||||||
|
|
Loading…
Reference in a new issue