add flag system, new argument parser, kick blocked

This commit is contained in:
Lea 2023-03-25 20:44:51 +01:00
parent e95d452e89
commit ff4d6eda69
Signed by: Lea
GPG key ID: 1BAFFE8347019C42

View file

@ -8,18 +8,44 @@ config();
type Db = { type Db = {
probation: string[]; probation: string[];
blocked: string[]; blocked: { [key: string]: { blocked: boolean, kick: boolean } };
} }
type CommandFlag = {
name: string;
} & (
{
type: 'string';
value?: string;
} | {
type: 'boolean';
value?: boolean;
}
);
const CommandFlags: CommandFlag[] = [
{
name: 'kick',
type: 'boolean',
}
];
const PREFIX_WORD = '/kibbydev';
const RE_COMMAND_ARGS = /(?<!\\)"[^(?<!\\)"\n]*"|[\S]+/g;
const RE_COMMAND_ARG_QUOTED = /^".*(?<!\\)"$/g;
const RE_QUOTE_BACKSLASH = /\\"/g;
const RE_FLAG = /^--\S+(=.*)?$/g;
const RE_FLAG_NAME = /^--[^=\s]+(=*?|$)/g;
const DB_FILE = process.env.DB_FILE || './db.json'; const DB_FILE = process.env.DB_FILE || './db.json';
const RE_USER_MENTION = /^<@[0-9A-HJ-KM-NP-TV-Z]{26}>$/i; const RE_USER_MENTION = /^<@[0-9A-HJ-KM-NP-TV-Z]{26}>$/i;
const PUBLIC_COMMANDS = ['suicide', 'status', 'help']; const PUBLIC_COMMANDS = ['suicide', 'status', 'help'];
const COMMANDS = { const COMMANDS = {
'help': 'List available commands', 'help': 'List available commands',
'approve': 'Release users from probation',
'unapprove': 'Send users to probation',
'status': 'Edit the bot\'s status', 'status': 'Edit the bot\'s status',
'suicide': 'This will make you commit suicide', 'suicide': 'This will make you commit suicide',
'debug': 'Debug command',
'approve': 'Release users from probation',
'unapprove': 'Send users to probation',
'block': 'Troll a user', 'block': 'Troll a user',
'unblock': 'Untroll a user', 'unblock': 'Untroll a user',
} }
@ -34,9 +60,20 @@ const client = new Client({ });
client.loginBot(process.env.TOKEN); client.loginBot(process.env.TOKEN);
db.read().then(() => { db.read().then(() => {
db.data ||= { probation: [], blocked: [] }; db.data ||= { probation: [], blocked: {} };
db.data.probation ||= []; db.data.probation ||= [];
db.data.blocked ||= []; db.data.blocked ||= {};
if (db.data.blocked instanceof Array) {
console.log('Running migration: db.blocked');
const blocked = db.data.blocked as unknown as string[];
db.data.blocked = {};
for (const id of blocked) {
db.data.blocked[id] = { blocked: true, kick: false };
}
}
db.write(); db.write();
}); });
@ -210,14 +247,75 @@ const setProbation = async (member: Member, probation: boolean) => {
client.on('message', async (message) => { client.on('message', async (message) => {
try { try {
if (!message.content || typeof message.content != 'string') return; if (!message.content || typeof message.content != 'string') return;
const args = message.content.replace(/ +/g, ' ').split(' '); let args: string[] = Array.from(message.content.match(RE_COMMAND_ARGS) ?? []);
if (args.shift()?.toLowerCase() != '/kibby') return; if (args.shift()?.toLowerCase() != PREFIX_WORD) return;
// Remove quotes
args = args.map(arg => arg.match(RE_COMMAND_ARG_QUOTED) ? arg.substring(1, arg.length - 1) : arg);
args = args.map(arg => arg.replace(RE_QUOTE_BACKSLASH, '"'));
// Parse flags
let flags = [...CommandFlags];
for (let i = args.length - 1; i >= 0; i--) {
const arg = args[i];
if (arg.match(RE_FLAG)) {
const name = arg.match(RE_FLAG_NAME)?.[0].substring(2);
console.log('name', name)
if (!name) continue;
const flag = CommandFlags.find(flag => flag.name == name);
console.log('flag', flag)
if (!flag) continue;
let value: string | boolean | null = null;
const argValue = arg.includes('=')
? arg.substring(arg.indexOf('=') + 1) // Makes sure we only split at the first =
: null;
console.log(argValue);
if (flag.type == 'boolean') {
if (!argValue) value = true;
else if (['true', 't', 'yes', 'y', '1'].includes(argValue.toLowerCase())) value = true;
else if (['false', 'f', 'no', 'n', '0'].includes(argValue.toLowerCase())) value = false;
else {
await message.reply({ embeds: [
embed(
`The flag \`--${flag.name}\` is of type boolean, but no suitable value was provided.`,
'Argument error',
'ERROR',
),
] });
return;
}
}
else if (flag.type == 'string') {
if (argValue == null) {
await message.reply({ embeds: [
embed(
`The flag \`--${flag.name}\` is of type string, but no value was provided.`,
'Argument error',
'ERROR',
),
] });
return;
}
value = argValue;
}
args.splice(i, 1);
flags = flags.map(f => f.name == flag.name ? { ...f, value: value as any } : f);
}
}
const logs = client.channels.get(process.env.LOGS!); const logs = client.channels.get(process.env.LOGS!);
const privileged = message.member?.hasPermission(message.channel?.server!, 'ManageMessages'); const privileged = message.member?.hasPermission(message.channel?.server!, 'ManageMessages');
if (!privileged && db.data?.blocked.includes(message.author_id)) { if (!privileged && db.data?.blocked[message.author_id]?.blocked) {
console.log('Ignoring nerd'); console.log('Ignoring nerd');
if (db.data?.blocked[message.author_id]?.kick && message.member?.kickable) {
console.log('Also kicking the nerd');
await message.member?.kick();
}
try { try {
await message.react('01G7PX5GVMPQD35FQE15H2T08S'); await message.react('01G7PX5GVMPQD35FQE15H2T08S');
} catch(e) { console.error(e) } } catch(e) { console.error(e) }
@ -237,7 +335,27 @@ client.on('message', async (message) => {
console.log('Got command: ' + args.join(' ')); console.log('Got command: ' + args.join(' '));
if (message.channel?.server_id != process.env.SERVER) return console.log('Command received in wrong server'); if (message.channel?.server_id != process.env.SERVER) return console.log('Command received in wrong server');
const getFlag = (input: string) => {
const flag = flags.find(f => f.name == input);
if (!flag) throw 'Tried to get unknown flag';
return flag.value;
}
switch(args.shift()?.toLowerCase()) { switch(args.shift()?.toLowerCase()) {
case 'debug': {
console.log(args.map(arg => `- ${arg}`).join('\n'))
return await message.reply({ embeds: [
args.length
? embed(args.map(arg => `- ${arg}`).join('\n'), 'Command arguments', 'INFO')
: embed('No arguments', 'Command arguments', 'WARN'),
embed(
flags.map(flag => `- (${flag.type}) ${flag.name}=${flag.value ?? '*(unset)*'}`).join('\n'),
'Command flags',
'INFO'
),
] });
}
case 'approve': { case 'approve': {
const users = await extractUsers(message, args); const users = await extractUsers(message, args);
if (!users?.length) return console.log('No users received'); if (!users?.length) return console.log('No users received');
@ -330,7 +448,9 @@ client.on('message', async (message) => {
const users = await extractUsers(message, args); const users = await extractUsers(message, args);
if (!users?.length) return await message.reply('User(s) not found or no users provided'); if (!users?.length) return await message.reply('User(s) not found or no users provided');
for (const user of users) { for (const user of users) {
if (!db.data?.blocked.includes(user._id)) db.data?.blocked.push(user._id); if (!db.data?.blocked[user._id]?.blocked) {
db.data!.blocked[user._id] = { blocked: true, kick: !!getFlag('kick') }
}
} }
await db.write(); await db.write();
@ -346,7 +466,10 @@ client.on('message', async (message) => {
const users = await extractUsers(message, args); const users = await extractUsers(message, args);
if (!users?.length) return await message.reply('User(s) not found or no users provided'); if (!users?.length) return await message.reply('User(s) not found or no users provided');
for (const user of users) { for (const user of users) {
if (db.data?.blocked.includes(user._id)) db.data.blocked = db.data.blocked.filter(u => u != user._id); if (db.data?.blocked[user._id]?.blocked) {
db.data.blocked[user._id].blocked = false;
db.data.blocked[user._id].kick = false;
}
} }
await db.write(); await db.write();
@ -363,7 +486,7 @@ client.on('message', async (message) => {
return await message.reply({ embeds: [ return await message.reply({ embeds: [
embed("Kibby is this server's personal maid!"), embed("Kibby is this server's personal maid!"),
embed(`### Commands\n${commands.map(c => `- **/kibby ${c[0]}:** ${c[1]}`).join('\n')}`), embed(`### Commands\n${commands.map(c => `- **${PREFIX_WORD} ${c[0]}:** ${c[1]}`).join('\n')}`),
] }); ] });
} }