counter refactor w/ knex!!

This commit is contained in:
Jill 2023-11-11 02:01:38 +03:00
parent e57ebd47d2
commit 726586f377
Signed by: oat
GPG Key ID: 33489AA58A955108
9 changed files with 211 additions and 181 deletions

View File

@ -0,0 +1,68 @@
import { Interaction, SlashCommandBuilder } from 'discord.js';
import { Counter, db } from '../lib/db';
import { updateCounter } from '../lib/counter';
module.exports = {
data: new SlashCommandBuilder()
.setName('createcounter')
.setDescription('[ADMIN] Create a counter in this channel')
.addStringOption(option =>
option
.setName('key')
.setDescription('The codename. Best to leave descriptive for later')
.setRequired(true)
)
.addStringOption(option =>
option
.setName('name')
.setDescription('The name, anything goes')
.setRequired(true)
)
.addStringOption(option =>
option
.setName('emoji')
.setDescription('An emoji or symbol or something to represent the counter')
.setMaxLength(10)
.setRequired(true)
)
.addNumberOption(option =>
option
.setName('value')
.setDescription('Initial value to start with')
)
.setDefaultMemberPermissions('0')
.setDMPermission(false),
execute: async (interaction: Interaction) => {
if (!interaction.isChatInputCommand()) return;
await interaction.deferReply({ephemeral: true});
const key = interaction.options.getString('key')!;
const name = interaction.options.getString('name')!;
const emoji = interaction.options.getString('emoji')!;
const value = interaction.options.getNumber('value') || 0;
const channel = interaction.channelId;
const guild = interaction.guildId!;
await db<Counter>('counters')
.insert({
'key': key,
'name': name,
'emoji': emoji,
'value': value,
'channel': channel,
'guild': guild
});
const counter = await db<Counter>('counters')
.where('key', key)
.first();
await updateCounter(interaction.client, counter!);
await interaction.followUp({
content: `<#${interaction.channelId}> has been **enriched** with your new counter. Congratulations!`
});
}
};

View File

@ -1,26 +1,36 @@
import { Interaction, GuildMember, SlashCommandBuilder } from 'discord.js'; import { GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
import { changeCounterInteraction } from '../lib/counter'; import { changeCounterInteraction, counterAutocomplete } from '../lib/counter';
import { knownServers } from '../lib/knownServers';
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('decrease') .setName('decrease')
.setDescription('Decrease the counter') .setDescription('Decrease a counter')
.addStringOption(option =>
option
.setName('type')
.setAutocomplete(true)
.setDescription('The name of the counter')
.setRequired(true)
)
.addIntegerOption((option) => .addIntegerOption((option) =>
option option
.setName('amount') .setName('amount')
.setRequired(false) .setRequired(false)
.setDescription('Amount to decrease the counter by') .setDescription('Amount to decrease the counter by')
.setMinValue(1) .setMinValue(1)
), )
.setDMPermission(false),
serverWhitelist: [...knownServers.firepit],
execute: async (interaction: Interaction, member: GuildMember) => { execute: async (interaction: Interaction, member: GuildMember) => {
if (!interaction.isChatInputCommand()) return; if (!interaction.isChatInputCommand()) return;
await interaction.deferReply({ephemeral: true});
const amount = Math.trunc(interaction.options.getInteger('amount') || 1); const amount = Math.trunc(interaction.options.getInteger('amount') || 1);
changeCounterInteraction(interaction, member, -amount, false); const type = interaction.options.getString('type')!;
}
await interaction.deferReply({ephemeral: true});
changeCounterInteraction(interaction, member, -amount, type);
},
autocomplete: counterAutocomplete
}; };

View File

@ -1,26 +0,0 @@
import { Interaction, GuildMember, SlashCommandBuilder } from 'discord.js';
import { changeCounterInteraction, getCounter } from '../lib/counter';
import { knownServers } from '../lib/knownServers';
module.exports = {
data: new SlashCommandBuilder()
.setName('decrease2')
.setDescription('Decrease the cream counter')
.addIntegerOption((option) =>
option
.setName('amount')
.setRequired(false)
.setDescription('Amount to decrease the counter by')
.setMinValue(1)
),
serverWhitelist: [...knownServers.firepit],
execute: async (interaction: Interaction, member: GuildMember) => {
if (!interaction.isChatInputCommand()) return;
await interaction.deferReply({ephemeral: true});
const amount = Math.min(Math.trunc(interaction.options.getInteger('amount') || 1), getCounter(true));
changeCounterInteraction(interaction, member, -amount, true);
}
};

View File

@ -1,26 +1,36 @@
import { GuildMember, Interaction, SlashCommandBuilder } from 'discord.js'; import { GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
import { changeCounterInteraction } from '../lib/counter'; import { changeCounterInteraction, counterAutocomplete } from '../lib/counter';
import { knownServers } from '../lib/knownServers';
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('increase') .setName('increase')
.setDescription('Increase the counter') .setDescription('Increase a counter')
.addStringOption(option =>
option
.setName('type')
.setAutocomplete(true)
.setDescription('The name of the counter')
.setRequired(true)
)
.addIntegerOption((option) => .addIntegerOption((option) =>
option option
.setName('amount') .setName('amount')
.setRequired(false) .setRequired(false)
.setDescription('Amount to increase the counter by') .setDescription('Amount to increase the counter by')
.setMinValue(1) .setMinValue(1)
), )
.setDMPermission(false),
serverWhitelist: [...knownServers.firepit],
execute: async (interaction: Interaction, member: GuildMember) => { execute: async (interaction: Interaction, member: GuildMember) => {
if (!interaction.isChatInputCommand()) return; if (!interaction.isChatInputCommand()) return;
const amount = Math.trunc(interaction.options.getInteger('amount') || 1);
const type = interaction.options.getString('type')!;
await interaction.deferReply({ephemeral: true}); await interaction.deferReply({ephemeral: true});
const amount = Math.trunc(interaction.options.getInteger('amount') || 1);
changeCounterInteraction(interaction, member, amount, false); changeCounterInteraction(interaction, member, amount, type);
} },
autocomplete: counterAutocomplete
}; };

View File

@ -1,31 +0,0 @@
import { GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
import { changeCounterInteraction } from '../lib/counter';
import { knownServers } from '../lib/knownServers';
module.exports = {
data: new SlashCommandBuilder()
.setName('increase2')
.setDescription('Increase the cream counter')
.addIntegerOption((option) =>
option
.setName('amount')
.setRequired(false)
.setDescription('Amount to increase the counter by')
.setMinValue(1)
),
serverWhitelist: [...knownServers.firepit],
execute: async (interaction: Interaction, member: GuildMember) => {
if (!interaction.isChatInputCommand()) return;
if (member.id !== '212481359589933056' && member.id !== '321126371189587968')
return await interaction.reply({
ephemeral: true,
content: 'you are not Dragon.'
});
await interaction.deferReply({ephemeral: true});
const amount = Math.trunc(interaction.options.getInteger('amount') || 1);
changeCounterInteraction(interaction, member, amount, true);
}
};

View File

@ -4,12 +4,13 @@ import { isSubscribed, subscribe, timeAnnouncements, unsubscribe } from '../lib/
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('subscribe') .setName('subscribe')
.setDescription('Subscribe/unsubscribe to a time announcement') .setDescription('[ADMIN] Subscribe/unsubscribe to a time announcement')
.addStringOption(option => .addStringOption(option =>
option option
.setName('type') .setName('type')
.setChoices(...Object.keys(timeAnnouncements).map(l => ({name: l, value: l}))) .setChoices(...Object.keys(timeAnnouncements).map(l => ({name: l, value: l})))
.setDescription('The name of the time announcement') .setDescription('The name of the time announcement')
.setRequired(true)
) )
.setDefaultMemberPermissions('0'), .setDefaultMemberPermissions('0'),

View File

@ -2,7 +2,6 @@ import { Client, GatewayIntentBits, Events, Collection, TextChannel } from 'disc
import * as fs from 'fs'; import * as fs from 'fs';
const { token } = JSON.parse(fs.readFileSync('./config.json', 'utf8')); const { token } = JSON.parse(fs.readFileSync('./config.json', 'utf8'));
import * as path from 'path'; import * as path from 'path';
import { initializeCounter } from './lib/counter';
import { initializeAnnouncements } from './lib/subscriptions'; import { initializeAnnouncements } from './lib/subscriptions';
import { initTables } from './lib/db'; import { initTables } from './lib/db';
@ -19,9 +18,6 @@ const bot = new Client({
}); });
bot.on(Events.ClientReady, async () => { bot.on(Events.ClientReady, async () => {
initializeCounter(false);
initializeCounter(true);
initializeAnnouncements(bot); initializeAnnouncements(bot);
bot.commands = new Collection(); bot.commands = new Collection();
@ -36,17 +32,26 @@ bot.on(Events.ClientReady, async () => {
}); });
bot.on(Events.InteractionCreate, async (interaction) => { bot.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isCommand()) return; if (interaction.isCommand()) {
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;
try {
await command.execute(interaction, interaction.member);
} catch (error) {
if (interaction.isRepliable() && !interaction.replied && !interaction.deferred) interaction.reply({ content: '`ERROR`', ephemeral: true });
if (interaction.deferred) interaction.followUp('`ERROR`');
console.error(error);
}
} else if (interaction.isAutocomplete()) {
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;
const command = interaction.client.commands.get(interaction.commandName); try {
if (!command) return; await command.autocomplete(interaction);
} catch (error) {
try { console.error(error);
await command.execute(interaction, interaction.member); }
} catch (error) {
if (interaction.isRepliable() && !interaction.replied && !interaction.deferred) interaction.reply({ content: '`ERROR`', ephemeral: true });
if (interaction.deferred) interaction.followUp('`ERROR`');
console.error(error);
} }
}); });

View File

@ -1,94 +1,49 @@
import { Client, CommandInteraction, GuildMember, EmbedBuilder, TextChannel } from 'discord.js'; import { Client, CommandInteraction, GuildMember, EmbedBuilder, TextChannel, AutocompleteInteraction } from 'discord.js';
import * as fsp from 'fs/promises'; import { getSign } from './util';
import { exists, getSign } from './util'; import { Counter, db } from './db';
const counterFile = './counter.json'; export async function getCounter(type: string) {
const counterFileCream = './counterCream.json'; const counter = await db<Counter>('counters')
const counterMessageFile = './counterMessageID.txt'; .where('key', type)
const counterCreamMessageFile = './counterCreamMessageID.txt'; .first();
const PISS_CHANNEL = '975802147126018150'; if (!counter) throw 'No such counter';
const CREAM_CHANNEL = '1098319707741900910';
export const PISS_EMOJI = '🪣';
export const CREAM_EMOJI = '🥛';
function getCounterFile(cream: boolean) { return counter.value;
return cream ? counterFileCream : counterFile;
} }
let pissCounter = 0; export async function changeCounter(delta: number, type: string) {
let creamCounter = 0; const value = await getCounter(type);
const newValue = value + delta;
export async function initializeCounter(cream: boolean) { await db<Counter>('counters')
const filename = getCounterFile(cream); .where('key', type)
if (await exists(filename)) { .update({
const count = parseInt(await fsp.readFile(filename, 'utf8')); 'value': newValue
if (cream) { });
creamCounter = count;
} else { return newValue;
pissCounter = count;
}
} else {
if (cream) {
creamCounter = 0;
} else {
pissCounter = 0;
}
await saveCounter(cream);
}
} }
export function getCounter(cream: boolean) { export async function getCounterData(type: string) {
return cream ? creamCounter : pissCounter; const counter = await db<Counter>('counters')
.select('*')
.where('key', type)
.first();
if (!counter) throw 'No such counter';
return counter;
} }
async function saveCounter(cream: boolean) { export async function updateCounter(bot: Client, counter: Counter) {
fsp.writeFile(getCounterFile(cream), Math.trunc(getCounter(cream)).toString()); const channel = await bot.channels.fetch(counter.channel) as TextChannel;
} const messageID = counter.message;
export async function changeCounter(delta: number, cream: boolean) { const content = `[${counter.emoji}] x${counter.value}`;
if (cream) {
creamCounter += delta;
} else {
pissCounter += delta;
}
await saveCounter(cream);
return getCounter(cream);
}
function getCounterMessageFile(cream: boolean) {
return cream ? counterMessageFile : counterCreamMessageFile;
}
async function getCounterMessageID(cream: boolean) {
const filename = getCounterMessageFile(cream);
if (await exists(filename)) {
const str = await fsp.readFile(filename, 'utf8');
return str;
} else {
return null;
}
}
function saveCounterMessageID(id: string, cream: boolean) {
return fsp.writeFile(getCounterMessageFile(cream), id);
}
function getEmoji(cream: boolean) {
return cream ? CREAM_EMOJI : PISS_EMOJI;
}
function getChannel(cream: boolean) {
return cream ? CREAM_CHANNEL : PISS_CHANNEL;
}
export async function updateCounter(bot: Client, cream: boolean) {
const channel = await bot.channels.fetch(getChannel(cream)) as TextChannel;
const messageID = await getCounterMessageID(cream);
const content = `[${getEmoji(cream)}] x${getCounter(cream)}`;
// bit janky // bit janky
// yeah you don't say
try { try {
if (messageID) { if (messageID) {
const message = await channel.messages.fetch(messageID); const message = await channel.messages.fetch(messageID);
@ -100,12 +55,17 @@ export async function updateCounter(bot: Client, cream: boolean) {
} catch(err) { } catch(err) {
const message = await channel.send(content); const message = await channel.send(content);
message.pin(); message.pin();
await saveCounterMessageID(message.id, cream);
await db<Counter>('counters')
.where('key', counter.key)
.update({
'message': message.id
});
} }
} }
export async function announceCounterUpdate(bot: Client, member: GuildMember, delta: number, cream: boolean) { export async function announceCounterUpdate(bot: Client, member: GuildMember, delta: number, counter: Counter) {
const channel = await bot.channels.fetch(getChannel(cream)) as TextChannel; const channel = await bot.channels.fetch(counter.channel) as TextChannel;
const embed = new EmbedBuilder() const embed = new EmbedBuilder()
.setAuthor({ .setAuthor({
@ -116,7 +76,7 @@ export async function announceCounterUpdate(bot: Client, member: GuildMember, de
.setColor(member.displayColor) .setColor(member.displayColor)
.setTimestamp() .setTimestamp()
.setFooter({ .setFooter({
text: `[${getEmoji(cream)}] x${getCounter(cream)}` text: `[${counter.emoji}] x${counter.value}`
}); });
await channel.send({ await channel.send({
@ -124,11 +84,33 @@ export async function announceCounterUpdate(bot: Client, member: GuildMember, de
}); });
} }
export async function changeCounterInteraction(interaction: CommandInteraction, member: GuildMember, amount: number, cream: boolean) { export async function changeCounterInteraction(interaction: CommandInteraction, member: GuildMember, amount: number, type: string) {
const newCount = await changeCounter(amount, cream); const counter = await getCounterData(type);
await updateCounter(interaction.client, cream);
await announceCounterUpdate(interaction.client, member, amount, cream); const newCount = await changeCounter(amount, type);
await updateCounter(interaction.client, counter);
await announceCounterUpdate(interaction.client, member, amount, counter);
interaction.followUp({ interaction.followUp({
content: `${getEmoji(cream)} **You have ${amount > 0 ? 'increased' : 'decreased'} the counter.**\n\`\`\`diff\n ${newCount - amount}\n${getSign(amount)}${Math.abs(amount)}\n ${newCount}\`\`\`` content: `${counter.emoji} **You have ${amount > 0 ? 'increased' : 'decreased'} the counter.**\n\`\`\`diff\n ${newCount - amount}\n${getSign(amount)}${Math.abs(amount)}\n ${newCount}\`\`\``
}); });
}
export async function counterAutocomplete(interaction: AutocompleteInteraction) {
const focusedValue = interaction.options.getFocused();
const guild = interaction.guildId;
const query = db<Counter>('counters')
.select('name', 'key')
.whereLike('name', `%${focusedValue.toLowerCase()}%`)
.limit(25);
if (guild) {
query.where('guild', guild);
}
const foundCounters = await query;
await interaction.respond(
foundCounters.map(choice => ({ name: choice.name, value: choice.key }))
);
} }

View File

@ -17,6 +17,16 @@ export interface Subscription {
channel: string, channel: string,
guild?: string guild?: string
} }
export interface Counter {
key: string,
name: string,
emoji: string,
value: number,
channel: string,
guild: string,
message?: string,
allowlist: boolean
}
export async function initTables() { export async function initTables() {
await db.schema.createTableIfNotExists('scheduledSubscriptions', table => { await db.schema.createTableIfNotExists('scheduledSubscriptions', table => {
@ -29,17 +39,18 @@ export async function initTables() {
table.string('channel'); table.string('channel');
table.string('guild').nullable(); table.string('guild').nullable();
}); });
/*await db.schema.createTableIfNotExists('counters', table => { await db.schema.createTableIfNotExists('counters', table => {
table.string('key').primary(); table.string('key').notNullable();
table.string('name').notNullable(); table.string('name').notNullable();
table.string('emoji').notNullable(); table.string('emoji').notNullable();
table.integer('value').defaultTo(0); table.integer('value').defaultTo(0);
table.string('channel').notNullable(); table.string('channel').notNullable();
table.string('guild').notNullable();
table.string('message'); table.string('message');
table.boolean('allowlist'); table.boolean('allowlist');
}); });
await db.schema.createTableIfNotExists('counterUserLink', table => { await db.schema.createTableIfNotExists('counterUserLink', table => {
table.string('key').references('key').inTable('counters'); table.string('key').references('key').inTable('counters');
table.string('user').notNullable(); table.string('user').notNullable();
});*/ });
} }