add flag system, new argument parser, kick blocked
This commit is contained in:
parent
e95d452e89
commit
ff4d6eda69
145
src/index.ts
145
src/index.ts
|
@ -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')}`),
|
||||||
] });
|
] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue