mirror of
https://github.com/janderedev/automod.git
synced 2024-12-22 18:55:28 +00:00
added more owner-only commands
This commit is contained in:
parent
c2f5d889f5
commit
c1b837b38c
|
@ -21,7 +21,7 @@ export default {
|
||||||
removeEmptyArgs: true,
|
removeEmptyArgs: true,
|
||||||
category: 'moderation',
|
category: 'moderation',
|
||||||
run: async (message: MessageCommandContext, args: string[]) => {
|
run: async (message: MessageCommandContext, args: string[]) => {
|
||||||
if (!await isModerator(message.member!, message.serverContext))
|
if (!await isModerator(message))
|
||||||
return message.reply(NO_MANAGER_MSG);
|
return message.reply(NO_MANAGER_MSG);
|
||||||
|
|
||||||
if (args.length == 0)
|
if (args.length == 0)
|
||||||
|
|
136
src/bot/commands/botadm.ts
Normal file
136
src/bot/commands/botadm.ts
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
import Command from "../../struct/Command";
|
||||||
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
|
import { client } from "../..";
|
||||||
|
import { commands, DEFAULT_PREFIX, ownerIDs } from "../modules/command_handler";
|
||||||
|
import child_process from 'child_process';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { wordlist } from "../modules/user_scan";
|
||||||
|
import { User } from "revolt.js/dist/maps/Users";
|
||||||
|
import { adminBotLog } from "../logging";
|
||||||
|
|
||||||
|
// id: expireDate
|
||||||
|
const sudoOverrides: { [key: string]: number|null } = {}
|
||||||
|
|
||||||
|
const isSudo = (user: User): boolean => {
|
||||||
|
console.log(sudoOverrides[user._id])
|
||||||
|
return !!(sudoOverrides[user._id] && sudoOverrides[user._id]! > Date.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateSudoTimeout = (user: User) => {
|
||||||
|
sudoOverrides[user._id] = Date.now() + (1000 * 60 * 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCommitHash = (): Promise<string|null> => new Promise((resolve) => {
|
||||||
|
child_process.exec('git rev-parse HEAD', (err, stdout) => {
|
||||||
|
if (err?.code) resolve(null); else resolve(stdout);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const SUBCOMMANDS: string[] = [
|
||||||
|
'stats',
|
||||||
|
'sudo',
|
||||||
|
];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'botadm',
|
||||||
|
aliases: [ 'botadmin' ],
|
||||||
|
description: 'Bot administration',
|
||||||
|
removeEmptyArgs: true,
|
||||||
|
restrict: 'BOTOWNER',
|
||||||
|
category: 'moderation',
|
||||||
|
run: async (message: MessageCommandContext, args: string[]) => {
|
||||||
|
if (!args.length) return message.reply('No subcommand specified. Available subcommands: ' + SUBCOMMANDS.join(', '));
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch(args.shift()?.toLowerCase()) {
|
||||||
|
case 'stats': {
|
||||||
|
const pjson = JSON.parse((await fs.promises.readFile(path.join(process.cwd(), 'package.json'))).toString());
|
||||||
|
let msg = `# AutoMod stats\n`
|
||||||
|
+ `### Cache\n`
|
||||||
|
+ `Servers: \`${client.servers.size}\`\n`
|
||||||
|
+ `Channels: \`${client.channels.size}\`\n`
|
||||||
|
+ `Users: \`${client.users.size}\`\n`
|
||||||
|
+ `### Misc\n`
|
||||||
|
+ `Command count: \`${commands.length}\`\n`
|
||||||
|
+ `Environment: \`${process.env.NODE_ENV || 'testing'}\`\n`
|
||||||
|
+ `Commit hash: \`${await getCommitHash() || 'Unknown'}\`\n`
|
||||||
|
+ `### Packages\n`
|
||||||
|
+ `revolt.js: \`${pjson.dependencies['revolt.js']}\`\n`
|
||||||
|
+ `discord.js: \`${pjson.dependencies['discord.js']}\`\n`
|
||||||
|
+ `axios: \`${pjson.dependencies['axios']}\`\n`
|
||||||
|
+ `log75: \`${pjson.dependencies['log75']}\`\n`
|
||||||
|
+ `typescript: \`${pjson.devDependencies['typescript']}\`\n`
|
||||||
|
+ `### Connection\n`
|
||||||
|
+ `API Endpoint: \`${client.apiURL}\`\n`
|
||||||
|
+ `Heartbeat: \`${client.heartbeat}\`\n`
|
||||||
|
+ `Ping: \`${client.websocket.ping ?? 'Unknown'}\`\n`
|
||||||
|
+ `### Bot configuration\n`
|
||||||
|
+ `Owners: \`${ownerIDs.length}\` (${ownerIDs.join(', ')})\n`
|
||||||
|
+ `Wordlist loaded: \`${wordlist ? `Yes (${wordlist.length} line${wordlist.length == 1 ? '' : 's'})` : 'No'}\`\n`;
|
||||||
|
|
||||||
|
await message.reply(msg, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'sudo': {
|
||||||
|
switch(args[0]?.toLowerCase()) {
|
||||||
|
case 'enable':
|
||||||
|
case 'on': {
|
||||||
|
if (isSudo(message.author!)) return message.reply('You are already in sudo mode!');
|
||||||
|
|
||||||
|
sudoOverrides[message.author_id] = Date.now() + (1000 * 60 * 5);
|
||||||
|
|
||||||
|
let msg = `# %emoji% Sudo mode enabled\n`
|
||||||
|
+ `In sudo mode, you will be able to run any command regardless of your server permissions.\n`
|
||||||
|
+ `Sudo mode will automatically expire **5 minutes** after your last bot interaction. `
|
||||||
|
+ `To disable now, run \`${DEFAULT_PREFIX}botadm sudo disable\`.`;
|
||||||
|
|
||||||
|
const sentMsg = await message.reply(msg.replace('%emoji%', ':lock:'), false);
|
||||||
|
setTimeout(() => sentMsg?.edit({ content: msg.replace('%emoji%', ':unlock:') }).catch(()=>{}), 200);
|
||||||
|
|
||||||
|
await adminBotLog({ type: 'WARN', message: `@${message.author!.username} has enabled sudo mode.` });
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'disable':
|
||||||
|
case 'off': {
|
||||||
|
if (!isSudo(message.author!)) return message.reply('You currently not in sudo mode.');
|
||||||
|
|
||||||
|
sudoOverrides[message.author_id] = null;
|
||||||
|
|
||||||
|
let msg = `# %emoji% Sudo mode disabled.`;
|
||||||
|
const sentMsg = await message.reply(msg.replace('%emoji%', ':unlock:'), false);
|
||||||
|
setTimeout(() => sentMsg?.edit({ content: msg.replace('%emoji%', ':lock:') }).catch(()=>{}), 200);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case null:
|
||||||
|
case undefined:
|
||||||
|
case '': {
|
||||||
|
let msg = `# :unlock: Sudo mode\n`
|
||||||
|
+ `Sudo mode allows bot owners to bypass all permission checks for a limited time. `
|
||||||
|
+ `After activating, you will be able to run any command regardless of your server permissions.\n\n`
|
||||||
|
+ `To enable, run \`${DEFAULT_PREFIX}botadm sudo enable\`.\n`
|
||||||
|
+ `It will automatically be deactivated **5 minutes** after your last bot interaction.`;
|
||||||
|
|
||||||
|
await message.reply(msg, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
await message.reply('sudo: Unknown subcommand');
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
message.reply('Unknown subcommand. Available subcommands: ' + SUBCOMMANDS.join(', '));
|
||||||
|
}
|
||||||
|
} catch(e) { console.error(e) }
|
||||||
|
}
|
||||||
|
} as Command;
|
||||||
|
|
||||||
|
export { isSudo, updateSudoTimeout }
|
|
@ -14,7 +14,7 @@ export default {
|
||||||
description: 'Perform administrative actions',
|
description: 'Perform administrative actions',
|
||||||
category: 'configuration',
|
category: 'configuration',
|
||||||
run: async (message: MessageCommandContext, args: string[]) => {
|
run: async (message: MessageCommandContext, args: string[]) => {
|
||||||
if (!isBotManager(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG);
|
if (!isBotManager(message)) return message.reply(NO_MANAGER_MSG);
|
||||||
|
|
||||||
let action = args.shift();
|
let action = args.shift();
|
||||||
switch(action) {
|
switch(action) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default {
|
||||||
removeEmptyArgs: true,
|
removeEmptyArgs: true,
|
||||||
category: 'moderation',
|
category: 'moderation',
|
||||||
run: async (message: MessageCommandContext, args: string[]) => {
|
run: async (message: MessageCommandContext, args: string[]) => {
|
||||||
if (!await isModerator(message.member!, message.serverContext))
|
if (!await isModerator(message))
|
||||||
return message.reply(NO_MANAGER_MSG);
|
return message.reply(NO_MANAGER_MSG);
|
||||||
|
|
||||||
if (args.length == 0)
|
if (args.length == 0)
|
||||||
|
|
|
@ -17,7 +17,7 @@ export default {
|
||||||
syntax: SYNTAX,
|
syntax: SYNTAX,
|
||||||
category: 'configuration',
|
category: 'configuration',
|
||||||
run: async (message: MessageCommandContext, args: string[]) => {
|
run: async (message: MessageCommandContext, args: string[]) => {
|
||||||
if (!await isBotManager(message.member!, message.channel?.server!)) return message.reply(NO_MANAGER_MSG);
|
if (!await isBotManager(message)) return message.reply(NO_MANAGER_MSG);
|
||||||
|
|
||||||
let config: ServerConfig = (await client.db.get('servers').findOne({ id: message.serverContext._id })) ?? {};
|
let config: ServerConfig = (await client.db.get('servers').findOne({ id: message.serverContext._id })) ?? {};
|
||||||
let mods = config.moderators ?? [];
|
let mods = config.moderators ?? [];
|
||||||
|
|
|
@ -20,7 +20,7 @@ export default {
|
||||||
|
|
||||||
switch(args[0]?.toLowerCase()) {
|
switch(args[0]?.toLowerCase()) {
|
||||||
case 'set':
|
case 'set':
|
||||||
if (!await isBotManager(message.member!, message.channel?.server!)) return message.reply(NO_MANAGER_MSG);
|
if (!await isBotManager(message)) 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.');
|
||||||
|
@ -43,7 +43,7 @@ export default {
|
||||||
break;
|
break;
|
||||||
case 'clear':
|
case 'clear':
|
||||||
case 'reset':
|
case 'reset':
|
||||||
if (!await isBotManager(message.member!, message.channel?.server!)) return message.reply(NO_MANAGER_MSG);
|
if (!await isBotManager(message)) 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 } });
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default {
|
||||||
category: 'moderation',
|
category: 'moderation',
|
||||||
run: async (message: MessageCommandContext, args: string[]) => {
|
run: async (message: MessageCommandContext, args: string[]) => {
|
||||||
try {
|
try {
|
||||||
if (!message.member || !await isModerator(message.member!, message.channel?.server!)) return message.reply('🔒 Access denied');
|
if (!message.member || !await isModerator(message)) return message.reply('🔒 Access denied');
|
||||||
|
|
||||||
let messages: Array<Message> = [];
|
let messages: Array<Message> = [];
|
||||||
// X amount of messages from bottom
|
// X amount of messages from bottom
|
||||||
|
|
|
@ -14,7 +14,7 @@ export default {
|
||||||
description: 'Manage AutoMod\'s configuration',
|
description: 'Manage AutoMod\'s configuration',
|
||||||
category: 'configuration',
|
category: 'configuration',
|
||||||
run: async (message: MessageCommandContext, args: string[]) => {
|
run: async (message: MessageCommandContext, args: string[]) => {
|
||||||
if (!isBotManager(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG);
|
if (!isBotManager(message)) return message.reply(NO_MANAGER_MSG);
|
||||||
|
|
||||||
return 'This feature is currently disabled';
|
return 'This feature is currently disabled';
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ export default {
|
||||||
syntax: '/unban [@user or ID]',
|
syntax: '/unban [@user or ID]',
|
||||||
category: 'moderation',
|
category: 'moderation',
|
||||||
run: async (message: MessageCommandContext, args: string[]) => {
|
run: async (message: MessageCommandContext, args: string[]) => {
|
||||||
if (!isModerator(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG);
|
if (!isModerator(message)) return message.reply(NO_MANAGER_MSG);
|
||||||
|
|
||||||
let checkTempBans = async (id: string): Promise<number> => {
|
let checkTempBans = async (id: string): Promise<number> => {
|
||||||
let tempbans: FindResult<TempBan> = await client.db.get('tempbans').find({ bannedUser: id, server: message.serverContext._id });
|
let tempbans: FindResult<TempBan> = await client.db.get('tempbans').find({ bannedUser: id, server: message.serverContext._id });
|
||||||
|
|
|
@ -13,7 +13,7 @@ export default {
|
||||||
description: 'add an infraction to an user\'s record',
|
description: 'add an infraction to an user\'s record',
|
||||||
category: 'moderation',
|
category: 'moderation',
|
||||||
run: async (message: MessageCommandContext, args: string[]) => {
|
run: async (message: MessageCommandContext, args: string[]) => {
|
||||||
if (!await isModerator(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG);
|
if (!await isModerator(message)) return message.reply(NO_MANAGER_MSG);
|
||||||
let user = await parseUserOrId(args.shift() ?? '');
|
let user = await parseUserOrId(args.shift() ?? '');
|
||||||
if (!user) return message.reply('I can\'t find that user.');
|
if (!user) return message.reply('I can\'t find that user.');
|
||||||
if ((user as any)?.bot != null) return message.reply('You cannot warn bots.');
|
if ((user as any)?.bot != null) return message.reply('You cannot warn bots.');
|
||||||
|
|
|
@ -18,7 +18,7 @@ export default {
|
||||||
syntax: '/warns; /warns @username ["export-csv"]; /warns rm [ID]',
|
syntax: '/warns; /warns @username ["export-csv"]; /warns rm [ID]',
|
||||||
category: 'moderation',
|
category: 'moderation',
|
||||||
run: async (message: MessageCommandContext, args: string[]) => {
|
run: async (message: MessageCommandContext, args: string[]) => {
|
||||||
if (!await isModerator(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG);
|
if (!await isModerator(message)) return message.reply(NO_MANAGER_MSG);
|
||||||
|
|
||||||
let collection = client.db.get('infractions');
|
let collection = client.db.get('infractions');
|
||||||
let infractions: Array<Infraction> = await collection.find({
|
let infractions: Array<Infraction> = await collection.find({
|
||||||
|
|
|
@ -18,7 +18,7 @@ export default {
|
||||||
let config: ServerConfig = await client.db.get('servers').findOne({ id: message.serverContext._id }) || {}
|
let config: ServerConfig = await client.db.get('servers').findOne({ id: message.serverContext._id }) || {}
|
||||||
if (!config.whitelist) config.whitelist = { users: [], roles: [], managers: true }
|
if (!config.whitelist) config.whitelist = { users: [], roles: [], managers: true }
|
||||||
|
|
||||||
if (!isBotManager(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG);
|
if (!isBotManager(message)) return message.reply(NO_MANAGER_MSG);
|
||||||
|
|
||||||
let user: User|null, role: string|undefined;
|
let user: User|null, role: string|undefined;
|
||||||
switch(args[0]?.toLowerCase()) {
|
switch(args[0]?.toLowerCase()) {
|
||||||
|
|
|
@ -30,7 +30,7 @@ async function antispam(message: Message): Promise<boolean> {
|
||||||
if (message.author?.bot != null) break;
|
if (message.author?.bot != null) break;
|
||||||
if (serverRules.whitelist?.users?.includes(message.author_id)) break;
|
if (serverRules.whitelist?.users?.includes(message.author_id)) break;
|
||||||
if (message.member?.roles?.filter(r => serverRules.whitelist?.roles?.includes(r)).length) break;
|
if (message.member?.roles?.filter(r => serverRules.whitelist?.roles?.includes(r)).length) break;
|
||||||
if (serverRules.whitelist?.managers !== false && await isModerator(message.member!, message.channel?.server!)) break;
|
if (serverRules.whitelist?.managers !== false && await isModerator(message)) break;
|
||||||
if (rule.channels?.indexOf(message.channel_id) == -1) break;
|
if (rule.channels?.indexOf(message.channel_id) == -1) break;
|
||||||
|
|
||||||
let store = msgCountStore.get(rule.id)!;
|
let store = msgCountStore.get(rule.id)!;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { getOwnMemberInServer, hasPermForChannel } from "../util";
|
import { getOwnMemberInServer, hasPermForChannel } from "../util";
|
||||||
import { prepareMessage } from "./prepare_message";
|
import { prepareMessage } from "./prepare_message";
|
||||||
|
import { isSudo, updateSudoTimeout } from "../commands/botadm";
|
||||||
|
|
||||||
// thanks a lot esm
|
// thanks a lot esm
|
||||||
const filename = fileURLToPath(import.meta.url);
|
const filename = fileURLToPath(import.meta.url);
|
||||||
|
@ -68,6 +69,8 @@ let commands: Command[];
|
||||||
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;
|
||||||
|
|
||||||
|
if (isSudo(msg.author!)) updateSudoTimeout(msg.author!);
|
||||||
|
|
||||||
if (cmd.restrict == 'BOTOWNER' && ownerIDs.indexOf(msg.author_id) == -1) {
|
if (cmd.restrict == 'BOTOWNER' && ownerIDs.indexOf(msg.author_id) == -1) {
|
||||||
logger.warn(`User ${msg.author?.username} tried to run owner-only command: ${cmdName}`);
|
logger.warn(`User ${msg.author?.username} tried to run owner-only command: ${cmdName}`);
|
||||||
msg.reply('🔒 Access denied');
|
msg.reply('🔒 Access denied');
|
||||||
|
|
|
@ -191,4 +191,4 @@ new Promise((res: (value: void) => void) => client.user ? res() : client.once('r
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
export { scanServer };
|
export { scanServer, USERSCAN_WORDLIST_PATH, wordlist };
|
||||||
|
|
|
@ -13,6 +13,8 @@ import logger from "./logger";
|
||||||
import { ulid } from "ulid";
|
import { ulid } from "ulid";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||||
import { ChannelPermission, ServerPermission } from "revolt.js";
|
import { ChannelPermission, ServerPermission } from "revolt.js";
|
||||||
|
import { Message } from "revolt.js/dist/maps/Messages";
|
||||||
|
import { isSudo } from "./commands/botadm";
|
||||||
|
|
||||||
|
|
||||||
const NO_MANAGER_MSG = '🔒 Missing permission';
|
const NO_MANAGER_MSG = '🔒 Missing permission';
|
||||||
|
@ -73,16 +75,30 @@ async function parseUserOrId(text: string): Promise<User|{_id: string}|null> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isModerator(member: Member, server: Server) {
|
async function isModerator(message: Message) {
|
||||||
|
let member = message.member!, server = message.channel!.server!;
|
||||||
return hasPerm(member, 'KickMembers')
|
return hasPerm(member, 'KickMembers')
|
||||||
|| await isBotManager(member, server)
|
|| await isBotManager(message)
|
||||||
|| (((await client.db.get('servers').findOne({ id: server._id }) || {}) as ServerConfig)
|
|| (((await client.db.get('servers').findOne({ id: server._id }) || {}) as ServerConfig)
|
||||||
.moderators?.indexOf(member.user?._id!) ?? -1) > -1;
|
.moderators?.indexOf(member.user?._id!) ?? -1) > -1
|
||||||
|
|| await checkSudoPermission(message);
|
||||||
}
|
}
|
||||||
async function isBotManager(member: Member, server: Server) {
|
async function isBotManager(message: Message) {
|
||||||
|
let member = message.member!, server = message.channel!.server!;
|
||||||
return hasPerm(member, 'ManageServer')
|
return hasPerm(member, 'ManageServer')
|
||||||
|| (((await client.db.get('servers').findOne({ id: server._id }) || {}) as ServerConfig)
|
|| (((await client.db.get('servers').findOne({ id: server._id }) || {}) as ServerConfig)
|
||||||
.botManagers?.indexOf(member.user?._id!) ?? -1) > -1;
|
.botManagers?.indexOf(member.user?._id!) ?? -1) > -1
|
||||||
|
|| await checkSudoPermission(message);
|
||||||
|
}
|
||||||
|
async function checkSudoPermission(message: Message): Promise<boolean> {
|
||||||
|
const hasPerm = isSudo(message.author!);
|
||||||
|
console.log(hasPerm)
|
||||||
|
if (!hasPerm) return false;
|
||||||
|
else {
|
||||||
|
await message.reply(`# :unlock: Bypassed permission check\n`
|
||||||
|
+ `Sudo mode is enabled for @${message.author!.username}.\n`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasPerm(member: Member, perm: keyof typeof ServerPermission): boolean {
|
function hasPerm(member: Member, perm: keyof typeof ServerPermission): boolean {
|
||||||
|
|
Loading…
Reference in a new issue