global blacklist

This commit is contained in:
janderedev 2022-04-09 22:43:36 +02:00
parent 88214057e5
commit db3d9391fd
Signed by: Lea
GPG key ID: 5D5E18ACB990F57A
8 changed files with 224 additions and 15 deletions

View file

@ -1,4 +1,4 @@
import { app, logger } from '..'; import { app, db, logger } from '..';
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { botReq } from './internal/ws'; import { botReq } from './internal/ws';
@ -22,3 +22,16 @@ app.get('/stats', async (req: Request, res: Response) => {
servers: SERVER_COUNT, servers: SERVER_COUNT,
}); });
}); });
app.get('/stats/global_blacklist', async (req: Request, res: Response) => {
try {
const users = await db.get('users').find({ globalBlacklist: true });
res.send({
total: users.length,
blacklist: users.map(u => ({ id: u.id?.toUpperCase() })),
});
} catch(e) {
console.error(''+e);
}
});

View file

@ -1,6 +1,6 @@
import SimpleCommand from "../../struct/commands/SimpleCommand"; import SimpleCommand from "../../struct/commands/SimpleCommand";
import MessageCommandContext from "../../struct/MessageCommandContext"; import MessageCommandContext from "../../struct/MessageCommandContext";
import { client } from "../.."; import { client, dbs } from "../..";
import { commands, DEFAULT_PREFIX, ownerIDs } from "../modules/command_handler"; import { commands, DEFAULT_PREFIX, ownerIDs } from "../modules/command_handler";
import child_process from 'child_process'; import child_process from 'child_process';
import fs from 'fs'; import fs from 'fs';
@ -9,6 +9,11 @@ import { wordlist } from "../modules/user_scan";
import { User } from "@janderedev/revolt.js/dist/maps/Users"; import { User } from "@janderedev/revolt.js/dist/maps/Users";
import { adminBotLog } from "../logging"; import { adminBotLog } from "../logging";
import CommandCategory from "../../struct/commands/CommandCategory"; import CommandCategory from "../../struct/commands/CommandCategory";
import { parseUserOrId } from "../util";
import { ChannelPermission, ServerPermission } from "@janderedev/revolt.js";
const BLACKLIST_BAN_REASON = `This user is globally blacklisted and has been banned automatically. If you wish to opt out of the global blacklist, run '/botctl ignore_blacklist yes'.`;
const BLACKLIST_MESSAGE = (username: string) => `\`@${username}\` has been banned automatically. Check the ban reason for more info.`;
// id: expireDate // id: expireDate
const sudoOverrides: { [key: string]: number|null } = {} const sudoOverrides: { [key: string]: number|null } = {}
@ -30,6 +35,11 @@ const getCommitHash = (): Promise<string|null> => new Promise((resolve) => {
const SUBCOMMANDS: string[] = [ const SUBCOMMANDS: string[] = [
'stats', 'stats',
'sudo', 'sudo',
'userinfo',
'blacklist',
'unblacklist',
'ignore',
'unignore',
]; ];
export default { export default {
@ -126,6 +136,129 @@ export default {
break; break;
} }
case 'userinfo': {
const target = await parseUserOrId(args.shift() || '');
if (!target) return message.reply('Specified user could not be found.');
const res = await dbs.USERS.findOne({ id: target._id });
if (!res) await message.reply(`Nothing stored about this user.`);
else await message.reply(`\`\`\`json\n${JSON.stringify(res, null, 4)}\n\`\`\``);
break;
}
case 'blacklist': {
const target = await parseUserOrId(args.shift() || '');
if (!target) return message.reply('Specified user could not be found.');
if (target._id == message.author_id) return message.reply(`no`);
await dbs.USERS.update({
id: target._id,
}, {
$setOnInsert: { id: target._id },
$set: { globalBlacklist: true }
}, { upsert: true });
try {
// Ban the user from all shared servers (unless those who opted out)
if (target instanceof User) {
const msg = await message.reply(`User update stored.`);
let bannedServers = 0;
const mutuals = await target.fetchMutual();
for (const serverid of mutuals.servers) {
const server = client.servers.get(serverid);
if (!server) continue;
if (server.permission & ServerPermission.BanMembers) {
const config = await dbs.SERVERS.findOne({ id: server._id });
if (config?.allowBlacklistedUsers) continue;
try {
await server.banUser(target._id, {
reason: BLACKLIST_BAN_REASON,
});
bannedServers++;
if (server.system_messages?.user_banned) {
const channel = server.channels.find(c => c!._id == server.system_messages!.user_banned);
if (channel && channel.permission & ChannelPermission.SendMessage) {
await channel.sendMessage(BLACKLIST_MESSAGE(target.username));
}
}
} catch(e) {
console.error(`Failed to ban in ${serverid}: ${e}`);
}
}
}
if (bannedServers) {
msg?.edit({ content: `User update stored. User has been banned from ${bannedServers} servers.` });
}
} else await message.reply(`User update stored. No servers are currently shared with this user.`);
} catch(e) {
console.error(''+e);
await message.reply(`Failed to ban target from mutual servers: ${e}\n`);
}
break;
}
case 'unblacklist': {
const target = await parseUserOrId(args.shift() || '');
if (!target) return message.reply('Specified user could not be found.');
await dbs.USERS.update({
id: target._id,
}, {
$setOnInsert: { id: target._id },
$set: { globalBlacklist: false }
}, { upsert: true });
await message.reply(`User update stored. Existing bans will not be lifted automatically.`);
break;
}
case 'ignore': {
const target = await parseUserOrId(args.shift() || '');
if (!target) return message.reply('Specified user could not be found.');
if (target._id == message.author_id) return message.reply(`no`);
await dbs.USERS.update(
{ id: target._id },
{
$setOnInsert: { id: target._id },
$set: { ignore: true },
},
{ upsert: true }
);
await message.reply(`User update stored.`);
break;
}
case 'unignore': {
const target = await parseUserOrId(args.shift() || '');
if (!target) return message.reply('Specified user could not be found.');
if (target._id == message.author_id) return message.reply(`no`);
await dbs.USERS.update(
{ id: target._id },
{
$setOnInsert: { id: target._id },
$set: { ignore: false },
},
{ upsert: true }
);
await message.reply(`User update stored.`);
break;
}
default: default:
message.reply('Unknown subcommand. Available subcommands: ' + SUBCOMMANDS.join(', ')); message.reply('Unknown subcommand. Available subcommands: ' + SUBCOMMANDS.join(', '));
} }
@ -133,4 +266,4 @@ export default {
} }
} as SimpleCommand; } as SimpleCommand;
export { isSudo, updateSudoTimeout } export { isSudo, updateSudoTimeout, BLACKLIST_BAN_REASON, BLACKLIST_MESSAGE }

View file

@ -47,10 +47,29 @@ export default {
userscans = userscans.filter(s => s != message.serverContext._id); userscans = userscans.filter(s => s != message.serverContext._id);
} }
break; break;
case 'ignore_blacklist':
try {
if (args[0] == 'yes') {
await dbs.SERVERS.update({ id: message.serverContext._id }, { $set: { allowBlacklistedUsers: true } });
await message.reply('Globally blacklisted users will no longer get banned in this server. Previously banned users will need to be unbanned manually.');
} else if (args[0] == 'no') {
await dbs.SERVERS.update({ id: message.serverContext._id }, { $set: { allowBlacklistedUsers: false } });
await message.reply('Globally blacklisted users will now get banned in this server.');
} else {
await message.reply(`Please specify either 'yes' or 'no' to toggle this setting.`);
}
} catch(e) {
console.error(''+e);
message.reply('Something went wrong: ' + e);
}
break;
case undefined: case undefined:
case '': case '':
message.reply(`### Available subcommands\n` message.reply(`### Available subcommands\n`
+ `- \`scan_userlist\` - If user scanning is enabled, this will scan the entire user list.`); + `- \`scan_userlist\` - If user scanning is enabled, this will scan the entire user list.\n`
+ `- \`ignore_blacklist\` - Ignore the bot's global blacklist.`);
break break
default: default:
message.reply(`Unknown option`); message.reply(`Unknown option`);

View file

@ -12,6 +12,8 @@ import CommandCategory from "../../struct/commands/CommandCategory";
Day.extend(RelativeTime); Day.extend(RelativeTime);
const GLOBAL_BLACKLIST_TEXT = `> :warning: This user has been flagged and is globally blacklisted. [Learn more.](https://github.com/janderedev/automod/wiki/Global-Blacklist)\n\n`;
export default { export default {
name: 'warns', name: 'warns',
aliases: [ 'warnings', 'infractions', 'infraction' ], aliases: [ 'warnings', 'infractions', 'infraction' ],
@ -66,10 +68,18 @@ export default {
let user = await parseUserOrId(args[0]); let user = await parseUserOrId(args[0]);
if (!user?._id) return message.reply('I can\'t find this user.'); if (!user?._id) return message.reply('I can\'t find this user.');
let infs = userInfractions.get(user._id); const infs = userInfractions.get(user._id);
if (!infs) return message.reply(`There are no infractions stored for \`${await fetchUsername(user._id)}\`.`); const userConfig = await dbs.USERS.findOne({ id: user._id });
if (!infs) return message.reply(`There are no infractions stored for \`${await fetchUsername(user._id)}\`.`
+ (userConfig?.globalBlacklist ? '\n' + GLOBAL_BLACKLIST_TEXT : ''), false);
else { else {
let msg = `## ${infs.length} infractions stored for ${await fetchUsername(user._id)}\n\u200b\n`; let msg = `## ${infs.length} infractions stored for ${await fetchUsername(user._id)}\n`;
if (userConfig?.globalBlacklist) {
msg += GLOBAL_BLACKLIST_TEXT;
} else msg += '\u200b\n';
let attachSpreadsheet = false; let attachSpreadsheet = false;
for (const i in infs) { for (const i in infs) {
let inf = infs[i]; let inf = infs[i];
@ -111,12 +121,12 @@ export default {
let sheet = Xlsx.utils.aoa_to_sheet(csv_data); let sheet = Xlsx.utils.aoa_to_sheet(csv_data);
let csv = Xlsx.utils.sheet_to_csv(sheet); let csv = Xlsx.utils.sheet_to_csv(sheet);
message.reply({ content: msg, attachments: [ await uploadFile(csv, `${user._id}.csv`) ] }); message.reply({ content: msg, attachments: [ await uploadFile(csv, `${user._id}.csv`) ] }, false);
} catch(e) { } catch(e) {
console.error(e); console.error(e);
message.reply(msg); message.reply(msg, false);
} }
} else message.reply(msg); } else message.reply(msg, false);
} }
break; break;
} }

View file

@ -61,10 +61,15 @@ let commands: SimpleCommand[];
if (!await antispam(msg)) return; if (!await antispam(msg)) return;
checkCustomRules(msg); checkCustomRules(msg);
let [ config, userConfig ] = await Promise.all([
dbs.SERVERS.findOne({ id: msg.channel!.server_id! }),
dbs.USERS.findOne({ id: msg.author_id }),
]);
if (userConfig?.ignore) return;
let args = msg.content.split(' '); let args = msg.content.split(' ');
let cmdName = args.shift() ?? ''; let cmdName = args.shift() ?? '';
let config = await dbs.SERVERS.findOne({ id: msg.channel!.server_id! });
let guildPrefix = config?.prefix ?? DEFAULT_PREFIX; let guildPrefix = config?.prefix ?? DEFAULT_PREFIX;
if (cmdName.startsWith(`<@${client.user?._id}>`)) { if (cmdName.startsWith(`<@${client.user?._id}>`)) {

View file

@ -1,7 +1,9 @@
import { ChannelPermission, ServerPermission } from "@janderedev/revolt.js";
import { ulid } from "ulid"; import { ulid } from "ulid";
import { client, dbs } from "../.."; import { client, dbs } from "../..";
import Infraction from "../../struct/antispam/Infraction"; import Infraction from "../../struct/antispam/Infraction";
import InfractionType from "../../struct/antispam/InfractionType"; import InfractionType from "../../struct/antispam/InfractionType";
import { BLACKLIST_BAN_REASON, BLACKLIST_MESSAGE } from "../commands/botadm";
import logger from "../logger"; import logger from "../logger";
import { hasPermForChannel, storeInfraction } from "../util"; import { hasPermForChannel, storeInfraction } from "../util";
import { DEFAULT_PREFIX } from "./command_handler"; import { DEFAULT_PREFIX } from "./command_handler";
@ -48,7 +50,34 @@ client.on('message', async message => {
} as Infraction).catch(console.warn); } as Infraction).catch(console.warn);
} catch(e) { console.error(e) } } catch(e) { console.error(e) }
break; break;
case 'user_joined': break; case 'user_joined': {
if (!sysMsg.user) break;
try {
const [ serverConfig, userConfig ] = await Promise.all([
dbs.SERVERS.findOne({ id: message.channel!.server_id! }),
dbs.USERS.findOne({ id: sysMsg.user._id }),
]);
if (userConfig?.globalBlacklist && !serverConfig?.allowBlacklistedUsers) {
const server = message.channel?.server;
if (server && (server?.permission ?? 0) & ServerPermission.BanMembers) {
await server.banUser(sysMsg.user._id, { reason: BLACKLIST_BAN_REASON });
if (server.system_messages?.user_banned) {
const channel = server.channels.find(c => c?._id == server.system_messages?.user_banned);
if (channel && channel.permission & ChannelPermission.SendMessage) {
await channel.sendMessage(BLACKLIST_MESSAGE(sysMsg.user.username));
}
}
}
}
} catch(e) {
console.error(''+e);
}
break;
};
case 'user_left' : break; case 'user_left' : break;
} }
}); });

View file

@ -2,7 +2,6 @@ import { Member } from "@janderedev/revolt.js/dist/maps/Members";
import { User } from "@janderedev/revolt.js/dist/maps/Users"; import { User } from "@janderedev/revolt.js/dist/maps/Users";
import { client, dbs } from ".."; import { client, dbs } from "..";
import Infraction from "../struct/antispam/Infraction"; import Infraction from "../struct/antispam/Infraction";
import ServerConfig from "../struct/ServerConfig";
import FormData from 'form-data'; import FormData from 'form-data';
import axios from 'axios'; import axios from 'axios';
import { Server } from "@janderedev/revolt.js/dist/maps/Servers"; import { Server } from "@janderedev/revolt.js/dist/maps/Servers";

View file

@ -26,6 +26,7 @@ class ServerConfig {
userScan?: LogConfig // User profile matched word list userScan?: LogConfig // User profile matched word list
}; };
enableUserScan?: boolean; enableUserScan?: boolean;
allowBlacklistedUsers?: boolean; // Whether the server explicitly allows users that are globally blacklisted
} }
export default ServerConfig; export default ServerConfig;