added more owner-only commands
This commit is contained in:
parent
c2f5d889f5
commit
c1b837b38c
16 changed files with 174 additions and 19 deletions
|
@ -21,7 +21,7 @@ export default {
|
|||
removeEmptyArgs: true,
|
||||
category: 'moderation',
|
||||
run: async (message: MessageCommandContext, args: string[]) => {
|
||||
if (!await isModerator(message.member!, message.serverContext))
|
||||
if (!await isModerator(message))
|
||||
return message.reply(NO_MANAGER_MSG);
|
||||
|
||||
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',
|
||||
category: 'configuration',
|
||||
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();
|
||||
switch(action) {
|
||||
|
|
|
@ -16,7 +16,7 @@ export default {
|
|||
removeEmptyArgs: true,
|
||||
category: 'moderation',
|
||||
run: async (message: MessageCommandContext, args: string[]) => {
|
||||
if (!await isModerator(message.member!, message.serverContext))
|
||||
if (!await isModerator(message))
|
||||
return message.reply(NO_MANAGER_MSG);
|
||||
|
||||
if (args.length == 0)
|
||||
|
|
|
@ -17,7 +17,7 @@ export default {
|
|||
syntax: SYNTAX,
|
||||
category: 'configuration',
|
||||
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 mods = config.moderators ?? [];
|
||||
|
|
|
@ -20,7 +20,7 @@ export default {
|
|||
|
||||
switch(args[0]?.toLowerCase()) {
|
||||
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();
|
||||
if (args.length == 0) return message.reply('You need to specify a prefix.');
|
||||
|
@ -43,7 +43,7 @@ export default {
|
|||
break;
|
||||
case 'clear':
|
||||
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) {
|
||||
await client.db.get('servers').update({ 'id': message.channel?.server_id }, { $set: { 'prefix': null } });
|
||||
|
|
|
@ -15,7 +15,7 @@ export default {
|
|||
category: 'moderation',
|
||||
run: async (message: MessageCommandContext, args: string[]) => {
|
||||
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> = [];
|
||||
// X amount of messages from bottom
|
||||
|
|
|
@ -14,7 +14,7 @@ export default {
|
|||
description: 'Manage AutoMod\'s configuration',
|
||||
category: 'configuration',
|
||||
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';
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ export default {
|
|||
syntax: '/unban [@user or ID]',
|
||||
category: 'moderation',
|
||||
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 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',
|
||||
category: 'moderation',
|
||||
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() ?? '');
|
||||
if (!user) return message.reply('I can\'t find that user.');
|
||||
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]',
|
||||
category: 'moderation',
|
||||
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 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 }) || {}
|
||||
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;
|
||||
switch(args[0]?.toLowerCase()) {
|
||||
|
|
|
@ -30,7 +30,7 @@ async function antispam(message: Message): Promise<boolean> {
|
|||
if (message.author?.bot != null) break;
|
||||
if (serverRules.whitelist?.users?.includes(message.author_id)) 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;
|
||||
|
||||
let store = msgCountStore.get(rule.id)!;
|
||||
|
|
|
@ -10,6 +10,7 @@ import MessageCommandContext from "../../struct/MessageCommandContext";
|
|||
import { fileURLToPath } from 'url';
|
||||
import { getOwnMemberInServer, hasPermForChannel } from "../util";
|
||||
import { prepareMessage } from "./prepare_message";
|
||||
import { isSudo, updateSudoTimeout } from "../commands/botadm";
|
||||
|
||||
// thanks a lot esm
|
||||
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);
|
||||
if (!cmd) return;
|
||||
|
||||
if (isSudo(msg.author!)) updateSudoTimeout(msg.author!);
|
||||
|
||||
if (cmd.restrict == 'BOTOWNER' && ownerIDs.indexOf(msg.author_id) == -1) {
|
||||
logger.warn(`User ${msg.author?.username} tried to run owner-only command: ${cmdName}`);
|
||||
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 { Channel } from "revolt.js/dist/maps/Channels";
|
||||
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';
|
||||
|
@ -73,16 +75,30 @@ async function parseUserOrId(text: string): Promise<User|{_id: string}|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')
|
||||
|| await isBotManager(member, server)
|
||||
|| await isBotManager(message)
|
||||
|| (((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')
|
||||
|| (((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 {
|
||||
|
|
Loading…
Reference in a new issue