typecheck commands

This commit is contained in:
Jill 2023-11-16 13:33:11 +03:00
parent 20b914417f
commit f807baf3f2
21 changed files with 135 additions and 81 deletions

View File

@ -1,5 +1,6 @@
import { CategoryChannel, GuildMember, EmbedBuilder, TextChannel, Interaction, ChannelType, SlashCommandBuilder } from 'discord.js';
import { CategoryChannel, GuildMember, EmbedBuilder, TextChannel, CommandInteraction, ChannelType, SlashCommandBuilder } from 'discord.js';
import { knownServers } from '../lib/knownServers';
import { Command } from '../types/index';
const rand = [
'This change has no significance.',
@ -38,7 +39,7 @@ const nicknames = [
'goobert'
];
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('change')
.setDescription('Change')
@ -51,9 +52,11 @@ module.exports = {
serverWhitelist: [...knownServers.firepit, ...knownServers.fbi],
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
const what = interaction.options.getString('what', true);
let title = `**${member.displayName}** changed the **${what}**`;
let response;
@ -108,4 +111,4 @@ module.exports = {
ephemeral: false,
});
}
};
} satisfies Command;

View File

@ -1,8 +1,9 @@
import { RoleCreateOptions, GuildMember, Interaction, EmbedBuilder, TextChannel, ActionRowBuilder, ButtonBuilder, ButtonStyle, SlashCommandBuilder } from 'discord.js';
import { RoleCreateOptions, GuildMember, CommandInteraction, EmbedBuilder, TextChannel, ActionRowBuilder, ButtonBuilder, ButtonStyle, SlashCommandBuilder } from 'discord.js';
import { default as parseColor, Color } from 'parse-color';
import { isColorRole, COLOR_ROLE_SEPERATOR } from '../lib/assignableRoles';
import { knownServers } from '../lib/knownServers';
import * as log from '../lib/log';
import { Command } from '../types/index';
const PREVIEW_DURATION = 1000 * 60;
@ -35,7 +36,7 @@ async function applyColor(member: GuildMember, color: Color) {
await member.roles.add(role);
}
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('color')
.setDescription('Change your role color.')
@ -49,9 +50,11 @@ module.exports = {
serverWhitelist: [...knownServers.firepit],
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
const color = interaction.options.getString('color');
const preview = interaction.options.getBoolean('preview');
@ -129,4 +132,4 @@ module.exports = {
});
}
}
};
} satisfies Command;

View File

@ -1,8 +1,9 @@
import { AutocompleteInteraction, Interaction, SlashCommandBuilder } from 'discord.js';
import { AutocompleteInteraction, CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { Counter, CounterUserLink, db } from '../lib/db';
import { counterAutocomplete, counterConfigs, findCounter, getCounterConfigRaw, getOptions, parseConfig, setCounterConfig, toStringConfig, updateCounter } from '../lib/rpg/counter';
import { outdent } from 'outdent';
import { formatItem, formatItems, getItem, itemAutocomplete } from '../lib/rpg/items';
import { Command } from '../types/index';
function extendOption(t: string) {
return {name: t, value: t};
@ -28,7 +29,7 @@ const help = new Map([
`]
]);
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('counter')
.setDescription('[ADMIN] Counter management')
@ -238,7 +239,7 @@ module.exports = {
.setDefaultMemberPermissions('0')
.setDMPermission(false),
execute: async (interaction: Interaction) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
await interaction.deferReply({ephemeral: true});
@ -487,4 +488,4 @@ module.exports = {
await interaction.respond(options);
}
}}
};
} satisfies Command;

View File

@ -1,10 +1,11 @@
import { AutocompleteInteraction, GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
import { AutocompleteInteraction, GuildMember, CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { CraftingStationCooldown, db } from '../lib/db';
import { getStation, canUseStation, craftingStations, verb } from '../lib/rpg/craftingStations';
import { formatItem, getItemQuantity, formatItems, getMaxStack, giveItem, formatItemsArray } from '../lib/rpg/items';
import { getRecipe, defaultRecipes, formatRecipe } from '../lib/rpg/recipes';
import { Command } from '../types/index';
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('craft')
.setDescription('Craft an item with items you have')
@ -24,9 +25,11 @@ module.exports = {
)
.setDMPermission(false),
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
const recipeID = parseInt(interaction.options.getString('recipe', true));
await interaction.deferReply({ephemeral: true});
@ -120,4 +123,4 @@ module.exports = {
return interaction.respond(found);
}
}
};
} satisfies Command;

View File

@ -1,7 +1,8 @@
import { GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
import { GuildMember, CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { changeCounterInteraction, counterAutocomplete } from '../lib/rpg/counter';
import { Command } from '../types/index';
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('decrease')
.setDescription('Decrease a counter')
@ -21,9 +22,11 @@ module.exports = {
)
.setDMPermission(false),
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
const amount = Math.trunc(interaction.options.getInteger('amount') || 1);
const type = interaction.options.getString('type')!;
@ -33,4 +36,4 @@ module.exports = {
},
autocomplete: counterAutocomplete
};
} satisfies Command;

View File

@ -1,7 +1,8 @@
import { GuildMember, EmbedBuilder, SlashCommandBuilder, Interaction } from 'discord.js';
import { GuildMember, EmbedBuilder, SlashCommandBuilder, CommandInteraction } from 'discord.js';
import { writeTmpFile } from '../lib/util';
import { Command } from '../types/index';
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('emotedump')
.setDescription('Dump every emote in the server for Gitea')
@ -14,11 +15,11 @@ module.exports = {
.setMaxValue(512)
),
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const size = interaction.options.getInteger('size') || 64;
const emojis = member.guild.emojis;
const emojis = interaction.guild!.emojis;
const embed = new EmbedBuilder()
.setDescription(`names: \`${emojis.cache.map(emote => emote.name).join(',')}\``);
@ -38,4 +39,4 @@ module.exports = {
ephemeral: true
});
}
};
} satisfies Command;

View File

@ -1,6 +1,7 @@
import { Guild, GuildMember, Interaction, Role, SlashCommandBuilder } from 'discord.js';
import { Guild, GuildMember, CommandInteraction, Role, SlashCommandBuilder } from 'discord.js';
import { isColorRole, isPronounRole } from '../lib/assignableRoles';
import { knownServers } from '../lib/knownServers';
import { Command } from '../types/index';
async function fetchRoleMembers(role: Role) {
const members = await role.guild.members.fetch();
@ -21,7 +22,7 @@ async function garbageCollectRoles(guild: Guild, dryRun: boolean): Promise<Role[
return (await Promise.all(deletedRoles)).filter(r => r) as Role[];
}
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('garbage-collect-roles')
.setDescription('Garbage collect unused roles for colors and pronouns.')
@ -30,7 +31,7 @@ module.exports = {
serverWhitelist: [...knownServers.firepit],
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
await interaction.deferReply({
@ -38,7 +39,7 @@ module.exports = {
});
const dryrun = interaction.options.getBoolean('dry-run');
const colorRoles = await garbageCollectRoles(member.guild, dryrun || false);
const colorRoles = await garbageCollectRoles(interaction.guild!, dryrun || false);
if (dryrun) {
interaction.followUp({
@ -52,4 +53,4 @@ module.exports = {
});
}
}
};
} satisfies Command;

View File

@ -1,7 +1,8 @@
import { GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
import { GuildMember, CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { changeCounterInteraction, counterAutocomplete } from '../lib/rpg/counter';
import { Command } from '../types/index';
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('increase')
.setDescription('Increase a counter')
@ -21,9 +22,11 @@ module.exports = {
)
.setDMPermission(false),
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
const amount = Math.trunc(interaction.options.getInteger('amount') || 1);
const type = interaction.options.getString('type')!;
@ -33,4 +36,4 @@ module.exports = {
},
autocomplete: counterAutocomplete
};
} satisfies Command;

View File

@ -1,16 +1,19 @@
import { GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
import { GuildMember, CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { ItemInventory, db } from '../lib/db';
import { formatItems, getItem } from '../lib/rpg/items';
import { Command } from '../types/index';
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('inventory')
.setDescription('Check your inventory')
.setDMPermission(false),
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
await interaction.deferReply({ephemeral: true});
const itemsList = await db<ItemInventory>('itemInventories')
@ -24,4 +27,4 @@ module.exports = {
`Your inventory:\n${items.length === 0 ? '_Your inventory is empty!_' : items.map(i => `- ${formatItems(i.item!, i.quantity)}`).join('\n')}`
);
}
};
} satisfies Command;

View File

@ -1,4 +1,5 @@
import { GuildMember, EmbedBuilder, SlashCommandBuilder, Interaction } from 'discord.js';
import { GuildMember, EmbedBuilder, SlashCommandBuilder, CommandInteraction } from 'discord.js';
import { Command } from '../types/index';
const rand = require('random-seed').create();
const results = [
@ -19,16 +20,18 @@ function seperate(l: string[]): string {
return l.slice(0, -1).join(', ') + ' or ' + l.slice(-1);
}
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('investigate')
.setDescription('Investigate someone.')
.addUserOption((option) => option.setName('who').setDescription('Investigate who?').setRequired(true))
.addBooleanOption((option) => option.setName('sheriff').setDescription('Switch to Sheriff-style investigation').setRequired(false)),
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
const who = interaction.options.getUser('who', true);
const sheriff = interaction.options.getBoolean('sheriff');
let response;
@ -69,4 +72,4 @@ module.exports = {
ephemeral: true,
});
}
};
} satisfies Command;

View File

@ -1,13 +1,14 @@
import { AutocompleteInteraction, Interaction, SlashCommandBuilder } from 'discord.js';
import { AutocompleteInteraction, CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { CustomItem, db } from '../lib/db';
import { formatItems, getItem, giveItem, itemAutocomplete } from '../lib/rpg/items';
import { behaviors } from '../lib/rpg/behaviors';
import { Command } from '../types/index';
//function extendOption(t: string) {
// return {name: t, value: t};
//}
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('item')
.setDescription('[ADMIN] Create, edit and otherwise deal with custom items')
@ -172,7 +173,7 @@ module.exports = {
.setDefaultMemberPermissions('0')
.setDMPermission(false),
execute: async (interaction: Interaction) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
await interaction.deferReply({ephemeral: true});
@ -219,4 +220,4 @@ module.exports = {
return itemAutocomplete(interaction);
}
}
};
} satisfies Command;

View File

@ -1,5 +1,6 @@
import { GuildMember, SlashCommandBuilder, Interaction, messageLink } from 'discord.js';
import { GuildMember, SlashCommandBuilder, CommandInteraction, messageLink } from 'discord.js';
import { getTextResponsePrettyPlease, randomWord, sendSegments, startGame } from '../lib/game';
import { Command } from '../types/index';
const END_TEMPLATES = [
'Alright! Here\'s the messages you all conjured:',
@ -8,7 +9,7 @@ const END_TEMPLATES = [
'That does it! Here\'s what you\'ve all cooked up together:'
];
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('markov')
.setDescription('Play a Markov chain game')
@ -28,9 +29,11 @@ module.exports = {
.setMinValue(1)
.setMaxValue(100)),
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
const context = interaction.options.getInteger('context') || 3;
const maxIterations = interaction.options.getInteger('iterations') || 10;
@ -72,4 +75,4 @@ module.exports = {
});
});
}
};
} satisfies Command;

View File

@ -1,12 +1,14 @@
import { EmbedBuilder, Interaction, SlashCommandBuilder } from 'discord.js';
import { EmbedBuilder, CommandInteraction, SlashCommandBuilder } from 'discord.js';
import got from 'got';
import { knownServers } from '../lib/knownServers';
import { Command } from '../types/index';
const rand = require('random-seed').create();
const imagesEndpoint = 'https://commons.wikimedia.org/w/api.php?action=query&cmlimit=500&cmtitle=Category%3ALiminal_spaces&cmtype=file&list=categorymembers&format=json';
const imageEndpoint = 'https://commons.wikimedia.org/w/api.php?action=query&piprop=thumbnail&pithumbsize=200&prop=pageimages&titles={}&format=json';
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('monitor')
.setDescription('Monitor')
@ -19,7 +21,7 @@ module.exports = {
serverWhitelist: [...knownServers.firepit_extended, ...knownServers.fbi],
execute: async (interaction: Interaction) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
await interaction.deferReply({ephemeral: false});
@ -49,4 +51,4 @@ module.exports = {
embeds: [embed]
});
}
};
} satisfies Command;

View File

@ -1,13 +1,14 @@
import { RoleCreateOptions, GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
import { RoleCreateOptions, GuildMember, CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { pronouns, PRONOUN_ROLE_SEPERATOR } from '../lib/assignableRoles';
import { knownServers } from '../lib/knownServers';
import * as log from '../lib/log';
import { Command } from '../types/index';
function extendOption(t: string) {
return {name: t, value: t};
}
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('pronoun')
.setDescription('Give yourself a pronoun or two.')
@ -21,9 +22,11 @@ module.exports = {
serverWhitelist: [...knownServers.firepit],
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
await interaction.deferReply({
ephemeral: true
});
@ -57,4 +60,4 @@ module.exports = {
});
}
}
};
} satisfies Command;

View File

@ -1,7 +1,8 @@
import { GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
import { GuildMember, CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { changeLinkedCounterInteraction, linkedCounterAutocomplete } from '../lib/rpg/counter';
import { Command } from '../types/index';
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('put')
.setDescription('Put an item from your inventory into the counter')
@ -21,8 +22,10 @@ module.exports = {
)
.setDMPermission(false),
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
const amount = Math.trunc(interaction.options.getInteger('amount') || 1);
const type = interaction.options.getString('type')!;
@ -33,4 +36,4 @@ module.exports = {
},
autocomplete: linkedCounterAutocomplete
};
} satisfies Command;

View File

@ -1,7 +1,8 @@
import { Interaction, SlashCommandBuilder } from 'discord.js';
import { CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { isSubscribed, subscribe, timeAnnouncements, unsubscribe } from '../lib/subscriptions';
import { Command } from '../types/index';
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('subscribe')
.setDescription('[ADMIN] Subscribe/unsubscribe to a time announcement')
@ -14,7 +15,7 @@ module.exports = {
)
.setDefaultMemberPermissions('0'),
execute: async (interaction: Interaction) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
await interaction.deferReply({ephemeral: true});
@ -34,4 +35,4 @@ module.exports = {
});
}
}
};
} satisfies Command;

View File

@ -1,6 +1,7 @@
import { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, Client, Collection, MessageComponentInteraction, StringSelectMenuBuilder, ModalBuilder, TextChannel, TextInputStyle, Message, ButtonStyle, ComponentType, APIButtonComponentWithCustomId, Events, TextInputBuilder, SlashCommandBuilder } from 'discord.js';
import * as fs from 'fs/promises';
import { knownServers } from '../lib/knownServers';
import { Command } from '../types/index';
const RESPONSES_CHANNEL = '983762973858361364';
const GENERAL_CHANNEL = '587108210683412493';
@ -599,7 +600,7 @@ async function advanceSurvey(userId: string, dontAdvanceProgress = false) {
}
}
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('createsurvey')
.setDescription('Re-create the survey button'),
@ -621,7 +622,7 @@ module.exports = {
});
},
onClientReady: (bot: Client) => {
onClientReady: async (bot: Client) => {
bot.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isMessageComponent()) return;
if (interaction.isModalSubmit()) return;
@ -758,4 +759,4 @@ module.exports = {
advanceSurvey(member.id);
});
}
};
} satisfies Command;

View File

@ -1,7 +1,8 @@
import { GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
import { GuildMember, CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { changeLinkedCounterInteraction, linkedCounterAutocomplete } from '../lib/rpg/counter';
import { Command } from '../types/index';
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('take')
.setDescription('Take an item from a counter')
@ -21,8 +22,10 @@ module.exports = {
)
.setDMPermission(false),
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
const amount = Math.trunc(interaction.options.getInteger('amount') || 1);
const type = interaction.options.getString('type')!;
@ -33,4 +36,4 @@ module.exports = {
},
autocomplete: linkedCounterAutocomplete
};
} satisfies Command;

View File

@ -1,6 +1,7 @@
import { GuildMember, SlashCommandBuilder, Interaction, messageLink, User } from 'discord.js';
import { GuildMember, SlashCommandBuilder, CommandInteraction, messageLink, User } from 'discord.js';
import { getTextResponsePrettyPlease, sendSegments, startGame } from '../lib/game';
import { shuffle } from 'd3-array';
import { Command } from '../types/index';
const horrorStarters = [
'I was playing with my boobs.',
@ -59,13 +60,15 @@ function shift<T>(arr: T[]): T[] {
return [...arr.slice(1), arr[0]];
}
module.exports = {
export default {
data: new SlashCommandBuilder()
.setName('twosentencehorror')
.setDescription('Communally create the worst horror stories known to man'),
execute: async (interaction: Interaction, member: GuildMember) => {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
startGame(interaction, member.user, 'Two Sentence Horror', async (players, channel) => {
players = shuffle(players);
@ -106,4 +109,4 @@ module.exports = {
});
});
}
};
} satisfies Command;

View File

@ -6,6 +6,7 @@ import { initializeAnnouncements } from './lib/subscriptions';
import * as log from './lib/log';
import chalk from 'chalk';
import prettyBytes from 'pretty-bytes';
import { Command } from './types/index';
const bot = new Client({
intents: [
@ -43,7 +44,7 @@ bot.on(Events.ClientReady, async () => {
bot.commands = new Collection();
const cmdFiles = fs.readdirSync(path.join(__dirname, './commands')).filter((file) => file.endsWith('.js'));
for (const file of cmdFiles) {
const cmd = (await import(`./commands/${file}`));
const cmd = (await import(`./commands/${file}`)) as Command;
bot.commands.set(cmd.data.name, cmd);
if (cmd.onClientReady) cmd.onClientReady(bot);
}
@ -90,7 +91,7 @@ bot.on(Events.InteractionCreate, async (interaction) => {
log.nonsense(stringifyCommand(interaction));
try {
await command.execute(interaction, interaction.member);
await command.execute(interaction);
} catch (error) {
if (interaction.isRepliable() && !interaction.replied && !interaction.deferred) interaction.reply({ content: `\`ERROR\`\n\`\`\`\n${error}\n\`\`\``, ephemeral: true });
if (interaction.deferred) interaction.followUp(`\`ERROR\`\n\`\`\`\n${error}\n\`\`\``);
@ -101,6 +102,7 @@ bot.on(Events.InteractionCreate, async (interaction) => {
if (!command) return;
try {
if (!command.autocomplete) throw `Trying to invoke autocomplete for command ${interaction.commandName} which does not have it defined`;
await command.autocomplete(interaction);
} catch (error) {
log.error(error);

12
src/types/index.d.ts vendored
View File

@ -1,7 +1,15 @@
import { Collection } from 'discord.js';
import { Collection, SlashCommandBuilder, CommandInteraction, Client } from 'discord.js';
export interface Command {
data: Pick<SlashCommandBuilder, "toJSON" | "name">,
execute: (interaction: CommandInteraction) => Promise<any>,
autocomplete?: (interaction: AutocompleteInteraction) => Promise<any>,
onClientReady?: (client: Client) => Promise<any>,
serverWhitelist?: string[],
}
declare module 'discord.js' {
export interface Client {
commands: Collection<unknown, any>;
commands: Collection<string, Command>;
}
}