Compare commits
3 Commits
255de61a2e
...
726586f377
Author | SHA1 | Date |
---|---|---|
Jill | 726586f377 | |
Jill | e57ebd47d2 | |
Jill | 57b96ce082 |
|
@ -8,4 +8,5 @@ dist/
|
|||
/counter.json
|
||||
/counterMessageID.txt
|
||||
/counterCream.json
|
||||
/counterCreamMessageID.txt
|
||||
/counterCreamMessageID.txt
|
||||
/*.sqlite
|
|
@ -1,4 +1,3 @@
|
|||
{
|
||||
"token": "token",
|
||||
"disableDaytimeAnnouncements": false
|
||||
"token": "token"
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
const fs = require("node:fs");
|
||||
const { REST, Routes } = require("discord.js");
|
||||
const {token} = require('./config.json');
|
||||
|
||||
const rest = new REST({ version: "9" }).setToken(token);
|
||||
|
||||
rest.get(Routes.applicationGuildCommands('898850107892596776', '587108210121506816')).then((data) => {
|
||||
const promises = [];
|
||||
for (const command of data) {
|
||||
const deleteUrl = `${Routes.applicationGuildCommands('898850107892596776', '587108210121506816')}/${command.id}`;
|
||||
promises.push(rest.delete(deleteUrl));
|
||||
}
|
||||
console.log(`Removing ${promises.length} commands...`);
|
||||
Promise.all(promises).then(() => console.log('Done!'));
|
||||
});
|
|
@ -1,41 +1,51 @@
|
|||
const fs = require("node:fs");
|
||||
const { REST, Routes } = require("discord.js");
|
||||
const { token } = require('./config.json');
|
||||
const { exec } = require('child_process');
|
||||
|
||||
const rest = new REST({ version: "9" }).setToken(token);
|
||||
const appID = '898850107892596776';
|
||||
|
||||
console.log('building...');
|
||||
exec('pnpm tsc', (err, stdout, stderr) => {
|
||||
if (err) throw err;
|
||||
const isDev = process.argv.includes('--dev');
|
||||
|
||||
console.log(stdout);
|
||||
rest
|
||||
.get(Routes.currentApplication())
|
||||
.then(application => {
|
||||
console.log(`Operating on application ${application.name}`);
|
||||
if (isDev) console.log('Dev mode on - this may produce unwanted results in prod');
|
||||
const appID = application.id;
|
||||
|
||||
const commands = [];
|
||||
const commandFiles = fs.readdirSync("./dist/commands").filter((file) => file.endsWith(".js") && !file.startsWith('.'));
|
||||
const commands = [];
|
||||
const commandFiles = fs.readdirSync("./dist/commands").filter((file) => file.endsWith(".js") && !file.startsWith('.'));
|
||||
|
||||
for (const file of commandFiles) {
|
||||
const command = require(`./dist/commands/${file}`);
|
||||
commands.push(command);
|
||||
}
|
||||
|
||||
for (const file of commandFiles) {
|
||||
const command = require(`./dist/commands/${file}`);
|
||||
commands.push(command);
|
||||
}
|
||||
if (!isDev) {
|
||||
const knownServers = require('./dist/lib/knownServers').knownServers;
|
||||
const servers = [...new Set(Object.values(knownServers).reduce((a, b) => a.concat(b), []))];
|
||||
|
||||
for (const id of servers) {
|
||||
const serverCommands = commands.filter(command => command.serverWhitelist && command.serverWhitelist.includes(id));
|
||||
|
||||
rest
|
||||
.put(Routes.applicationGuildCommands(appID, id), { body: serverCommands.map(cmd => cmd.data.toJSON()) })
|
||||
.then(() => console.log(`${serverCommands.length} commands added to ${id}`))
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
const globalCommands = commands.filter(command => !command.serverWhitelist);
|
||||
|
||||
rest
|
||||
.put(Routes.applicationCommands(appID), { body: globalCommands.map(cmd => cmd.data.toJSON()) })
|
||||
.then(() => console.log(`${globalCommands.length} commands added globally, might take a bit to refresh`))
|
||||
.catch(console.error);
|
||||
} else {
|
||||
const guildID = '741963936689160282'; // TODO
|
||||
|
||||
const knownServers = require('./dist/lib/knownServers').knownServers;
|
||||
const servers = [...new Set(Object.values(knownServers).reduce((a, b) => a.concat(b), []))];
|
||||
|
||||
for (const id of servers) {
|
||||
const serverCommands = commands.filter(command => command.serverWhitelist && command.serverWhitelist.includes(id));
|
||||
|
||||
rest
|
||||
.put(Routes.applicationGuildCommands(appID, id), { body: serverCommands.map(cmd => cmd.data.toJSON()) })
|
||||
.then(() => console.log(`${serverCommands.length} commands added to ${id}`))
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
const globalCommands = commands.filter(command => !command.serverWhitelist);
|
||||
|
||||
rest
|
||||
.put(Routes.applicationCommands(appID), { body: globalCommands.map(cmd => cmd.data.toJSON()) })
|
||||
.then(() => console.log(`${globalCommands.length} commands added globally, might take a bit to refresh`))
|
||||
.catch(console.error);
|
||||
});
|
||||
rest
|
||||
.put(Routes.applicationGuildCommands(appID, guildID), { body: commands.map(cmd => cmd.data.toJSON()) })
|
||||
.then(() => console.log(`${commands.length} commands added to ${guildID}`))
|
||||
.catch(console.error);
|
||||
}
|
||||
});
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
pnpm run build && pm2 restart foggy-v2
|
|
@ -7,8 +7,7 @@
|
|||
"start": "tsc && node deploy-commands.cjs && node dist/index.js",
|
||||
"dev": "tsc && node dist/index.js",
|
||||
"build": "tsc",
|
||||
"deploy-commands": "tsc && node deploy-commands.cjs",
|
||||
"delete-commands": "tsc && node delete-commands.cjs"
|
||||
"deploy-commands": "tsc && node deploy-commands.cjs"
|
||||
},
|
||||
"author": "oatmealine",
|
||||
"license": "AGPL-3.0",
|
||||
|
@ -16,8 +15,10 @@
|
|||
"d3-array": "^2.12.1",
|
||||
"discord.js": "^14.13.0",
|
||||
"got": "^11.8.6",
|
||||
"knex": "^3.0.1",
|
||||
"parse-color": "^1.0.0",
|
||||
"random-seed": "^0.3.0"
|
||||
"random-seed": "^0.3.0",
|
||||
"sqlite3": "^5.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/d3-array": "^3.0.9",
|
||||
|
|
863
pnpm-lock.yaml
863
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -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!`
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,26 +1,36 @@
|
|||
import { Interaction, GuildMember, SlashCommandBuilder } from 'discord.js';
|
||||
import { changeCounterInteraction } from '../lib/counter';
|
||||
import { knownServers } from '../lib/knownServers';
|
||||
import { GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
|
||||
import { changeCounterInteraction, counterAutocomplete } from '../lib/counter';
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.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) =>
|
||||
option
|
||||
.setName('amount')
|
||||
.setRequired(false)
|
||||
.setDescription('Amount to decrease the counter by')
|
||||
.setMinValue(1)
|
||||
),
|
||||
|
||||
serverWhitelist: [...knownServers.firepit],
|
||||
)
|
||||
.setDMPermission(false),
|
||||
|
||||
execute: async (interaction: Interaction, member: GuildMember) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
await interaction.deferReply({ephemeral: true});
|
||||
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
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
};
|
|
@ -1,26 +1,36 @@
|
|||
import { GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
|
||||
import { changeCounterInteraction } from '../lib/counter';
|
||||
import { knownServers } from '../lib/knownServers';
|
||||
import { changeCounterInteraction, counterAutocomplete } from '../lib/counter';
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.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) =>
|
||||
option
|
||||
.setName('amount')
|
||||
.setRequired(false)
|
||||
.setDescription('Amount to increase the counter by')
|
||||
.setMinValue(1)
|
||||
),
|
||||
|
||||
serverWhitelist: [...knownServers.firepit],
|
||||
)
|
||||
.setDMPermission(false),
|
||||
|
||||
execute: async (interaction: Interaction, member: GuildMember) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
const amount = Math.trunc(interaction.options.getInteger('amount') || 1);
|
||||
const type = interaction.options.getString('type')!;
|
||||
|
||||
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
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
};
|
|
@ -1,15 +1,16 @@
|
|||
import { Interaction, SlashCommandBuilder } from 'discord.js';
|
||||
import { saveSubscriptions, subscriptions, timeAnnouncements } from '../lib/subscriptions';
|
||||
import { isSubscribed, subscribe, timeAnnouncements, unsubscribe } from '../lib/subscriptions';
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('subscribe')
|
||||
.setDescription('Subscribe/unsubscribe to a time announcement')
|
||||
.setDescription('[ADMIN] Subscribe/unsubscribe to a time announcement')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('type')
|
||||
.setChoices(...Object.keys(timeAnnouncements).map(l => ({name: l, value: l})))
|
||||
.setDescription('The name of the time announcement')
|
||||
.setRequired(true)
|
||||
)
|
||||
.setDefaultMemberPermissions('0'),
|
||||
|
||||
|
@ -19,19 +20,18 @@ module.exports = {
|
|||
await interaction.deferReply({ephemeral: true});
|
||||
|
||||
const announcementType = interaction.options.getString('type', true);
|
||||
const channel = interaction.channelId;
|
||||
|
||||
if (subscriptions[announcementType] && subscriptions[announcementType].includes(interaction.channelId)) {
|
||||
subscriptions[announcementType] = subscriptions[announcementType].filter(id => id !== interaction.channelId);
|
||||
if (await isSubscribed(announcementType, channel)) {
|
||||
await unsubscribe(announcementType, channel);
|
||||
await interaction.followUp({
|
||||
content: `<#${interaction.channelId}> has been unsubscribed from \`${announcementType}\``
|
||||
});
|
||||
} else {
|
||||
subscriptions[announcementType] = subscriptions[announcementType] || [];
|
||||
subscriptions[announcementType].push(interaction.channelId);
|
||||
await subscribe(announcementType, interaction.guildId || undefined, channel);
|
||||
await interaction.followUp({
|
||||
content: `<#${interaction.channelId}> has been subscribed to \`${announcementType}\``
|
||||
});
|
||||
}
|
||||
saveSubscriptions();
|
||||
}
|
||||
};
|
47
src/index.ts
47
src/index.ts
|
@ -1,12 +1,9 @@
|
|||
import { Client, GatewayIntentBits, Events, Collection, TextChannel } from 'discord.js';
|
||||
import * as fs from 'fs';
|
||||
const { token, disableDaytimeAnnouncements } = JSON.parse(fs.readFileSync('./config.json', 'utf8'));
|
||||
const { token } = JSON.parse(fs.readFileSync('./config.json', 'utf8'));
|
||||
import * as path from 'path';
|
||||
import { initializeCounter } from './lib/counter';
|
||||
import { initializeAnnouncements, loadNext, loadSubscriptions } from './lib/subscriptions';
|
||||
|
||||
loadNext();
|
||||
loadSubscriptions();
|
||||
import { initializeAnnouncements } from './lib/subscriptions';
|
||||
import { initTables } from './lib/db';
|
||||
|
||||
const bot = new Client({
|
||||
intents: [
|
||||
|
@ -21,12 +18,7 @@ const bot = new Client({
|
|||
});
|
||||
|
||||
bot.on(Events.ClientReady, async () => {
|
||||
initializeCounter(false);
|
||||
initializeCounter(true);
|
||||
|
||||
if (!disableDaytimeAnnouncements) {
|
||||
initializeAnnouncements(bot);
|
||||
}
|
||||
initializeAnnouncements(bot);
|
||||
|
||||
bot.commands = new Collection();
|
||||
const cmdFiles = fs.readdirSync(path.join(__dirname, './commands')).filter((file) => file.endsWith('.js'));
|
||||
|
@ -40,17 +32,26 @@ bot.on(Events.ClientReady, async () => {
|
|||
});
|
||||
|
||||
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);
|
||||
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);
|
||||
try {
|
||||
await command.autocomplete(interaction);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -63,4 +64,4 @@ process.on('uncaughtException', err => {
|
|||
console.error(err);
|
||||
});
|
||||
|
||||
bot.login(token);
|
||||
initTables().then(() => bot.login(token));
|
||||
|
|
|
@ -1,94 +1,49 @@
|
|||
import { Client, CommandInteraction, GuildMember, EmbedBuilder, TextChannel } from 'discord.js';
|
||||
import * as fsp from 'fs/promises';
|
||||
import { exists, getSign } from './util';
|
||||
import { Client, CommandInteraction, GuildMember, EmbedBuilder, TextChannel, AutocompleteInteraction } from 'discord.js';
|
||||
import { getSign } from './util';
|
||||
import { Counter, db } from './db';
|
||||
|
||||
const counterFile = './counter.json';
|
||||
const counterFileCream = './counterCream.json';
|
||||
const counterMessageFile = './counterMessageID.txt';
|
||||
const counterCreamMessageFile = './counterCreamMessageID.txt';
|
||||
export async function getCounter(type: string) {
|
||||
const counter = await db<Counter>('counters')
|
||||
.where('key', type)
|
||||
.first();
|
||||
|
||||
const PISS_CHANNEL = '975802147126018150';
|
||||
const CREAM_CHANNEL = '1098319707741900910';
|
||||
export const PISS_EMOJI = '🪣';
|
||||
export const CREAM_EMOJI = '🥛';
|
||||
if (!counter) throw 'No such counter';
|
||||
|
||||
function getCounterFile(cream: boolean) {
|
||||
return cream ? counterFileCream : counterFile;
|
||||
return counter.value;
|
||||
}
|
||||
|
||||
let pissCounter = 0;
|
||||
let creamCounter = 0;
|
||||
export async function changeCounter(delta: number, type: string) {
|
||||
const value = await getCounter(type);
|
||||
const newValue = value + delta;
|
||||
|
||||
export async function initializeCounter(cream: boolean) {
|
||||
const filename = getCounterFile(cream);
|
||||
if (await exists(filename)) {
|
||||
const count = parseInt(await fsp.readFile(filename, 'utf8'));
|
||||
if (cream) {
|
||||
creamCounter = count;
|
||||
} else {
|
||||
pissCounter = count;
|
||||
}
|
||||
} else {
|
||||
if (cream) {
|
||||
creamCounter = 0;
|
||||
} else {
|
||||
pissCounter = 0;
|
||||
}
|
||||
await saveCounter(cream);
|
||||
}
|
||||
await db<Counter>('counters')
|
||||
.where('key', type)
|
||||
.update({
|
||||
'value': newValue
|
||||
});
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
export function getCounter(cream: boolean) {
|
||||
return cream ? creamCounter : pissCounter;
|
||||
export async function getCounterData(type: string) {
|
||||
const counter = await db<Counter>('counters')
|
||||
.select('*')
|
||||
.where('key', type)
|
||||
.first();
|
||||
|
||||
if (!counter) throw 'No such counter';
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
async function saveCounter(cream: boolean) {
|
||||
fsp.writeFile(getCounterFile(cream), Math.trunc(getCounter(cream)).toString());
|
||||
}
|
||||
export async function updateCounter(bot: Client, counter: Counter) {
|
||||
const channel = await bot.channels.fetch(counter.channel) as TextChannel;
|
||||
const messageID = counter.message;
|
||||
|
||||
export async function changeCounter(delta: number, cream: boolean) {
|
||||
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)}`;
|
||||
const content = `[${counter.emoji}] x${counter.value}`;
|
||||
|
||||
// bit janky
|
||||
// yeah you don't say
|
||||
try {
|
||||
if (messageID) {
|
||||
const message = await channel.messages.fetch(messageID);
|
||||
|
@ -100,12 +55,17 @@ export async function updateCounter(bot: Client, cream: boolean) {
|
|||
} catch(err) {
|
||||
const message = await channel.send(content);
|
||||
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) {
|
||||
const channel = await bot.channels.fetch(getChannel(cream)) as TextChannel;
|
||||
export async function announceCounterUpdate(bot: Client, member: GuildMember, delta: number, counter: Counter) {
|
||||
const channel = await bot.channels.fetch(counter.channel) as TextChannel;
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
|
@ -116,7 +76,7 @@ export async function announceCounterUpdate(bot: Client, member: GuildMember, de
|
|||
.setColor(member.displayColor)
|
||||
.setTimestamp()
|
||||
.setFooter({
|
||||
text: `[${getEmoji(cream)}] x${getCounter(cream)}`
|
||||
text: `[${counter.emoji}] x${counter.value}`
|
||||
});
|
||||
|
||||
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) {
|
||||
const newCount = await changeCounter(amount, cream);
|
||||
await updateCounter(interaction.client, cream);
|
||||
await announceCounterUpdate(interaction.client, member, amount, cream);
|
||||
export async function changeCounterInteraction(interaction: CommandInteraction, member: GuildMember, amount: number, type: string) {
|
||||
const counter = await getCounterData(type);
|
||||
|
||||
const newCount = await changeCounter(amount, type);
|
||||
await updateCounter(interaction.client, counter);
|
||||
await announceCounterUpdate(interaction.client, member, amount, counter);
|
||||
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 }))
|
||||
);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
import knex from 'knex';
|
||||
|
||||
export const db = knex({
|
||||
client: 'sqlite3',
|
||||
connection: {
|
||||
filename: './jillo.sqlite'
|
||||
},
|
||||
useNullAsDefault: true
|
||||
});
|
||||
|
||||
export interface ScheduledSubscription {
|
||||
name: string;
|
||||
next: number;
|
||||
}
|
||||
export interface Subscription {
|
||||
key: string,
|
||||
channel: 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() {
|
||||
await db.schema.createTableIfNotExists('scheduledSubscriptions', table => {
|
||||
table.string('name').primary();
|
||||
table.timestamp('next').defaultTo(db.fn.now());
|
||||
});
|
||||
await db.schema.createTableIfNotExists('subscriptions', table => {
|
||||
table.string('key')
|
||||
.references('name').inTable('scheduledSubscriptions');
|
||||
table.string('channel');
|
||||
table.string('guild').nullable();
|
||||
});
|
||||
await db.schema.createTableIfNotExists('counters', table => {
|
||||
table.string('key').notNullable();
|
||||
table.string('name').notNullable();
|
||||
table.string('emoji').notNullable();
|
||||
table.integer('value').defaultTo(0);
|
||||
table.string('channel').notNullable();
|
||||
table.string('guild').notNullable();
|
||||
table.string('message');
|
||||
table.boolean('allowlist');
|
||||
});
|
||||
await db.schema.createTableIfNotExists('counterUserLink', table => {
|
||||
table.string('key').references('key').inTable('counters');
|
||||
table.string('user').notNullable();
|
||||
});
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { Client, TextChannel } from 'discord.js';
|
||||
import * as fs from 'fs';
|
||||
import { ScheduledSubscription, Subscription, db } from './db';
|
||||
|
||||
interface AnnouncementType {
|
||||
hour: number;
|
||||
|
@ -159,55 +159,65 @@ function getNextTime(hour: number, randomMinute = true) {
|
|||
return next.getTime();
|
||||
}
|
||||
|
||||
export let next: Record<string, number> = {};
|
||||
export let subscriptions: Record<string, string[]> = {};
|
||||
|
||||
export function saveNext() {
|
||||
fs.writeFileSync('./next.json', JSON.stringify(next));
|
||||
export async function subscribe(type: string, guild: string | undefined, channel: string) {
|
||||
await db<Subscription>('subscriptions')
|
||||
.insert({
|
||||
key: type,
|
||||
guild: guild,
|
||||
channel: channel
|
||||
});
|
||||
}
|
||||
export function saveSubscriptions() {
|
||||
fs.writeFileSync('./subscriptions.json', JSON.stringify(subscriptions));
|
||||
export async function unsubscribe(type: string, channel: string) {
|
||||
await db<Subscription>('subscriptions')
|
||||
.delete()
|
||||
.where('type', type).where('channel', channel);
|
||||
}
|
||||
|
||||
export function loadNext() {
|
||||
if (fs.existsSync('./next.json')) {
|
||||
next = JSON.parse(fs.readFileSync('./next.json', 'utf8'));
|
||||
}
|
||||
for (const k of Object.keys(timeAnnouncements)) {
|
||||
if (!next[k]) next[k] = getNextAnnouncementTime(timeAnnouncements[k]);
|
||||
}
|
||||
saveNext();
|
||||
}
|
||||
export function loadSubscriptions() {
|
||||
if (fs.existsSync('./subscriptions.json')) {
|
||||
subscriptions = JSON.parse(fs.readFileSync('./subscriptions.json', 'utf8'));
|
||||
}
|
||||
saveSubscriptions();
|
||||
export async function isSubscribed(type: string, channel: string): Promise<boolean> {
|
||||
return await db<Subscription>('subscriptions')
|
||||
.where('key', type).where('channel', channel)
|
||||
.first() !== undefined;
|
||||
}
|
||||
|
||||
export function initializeAnnouncements(bot: Client) {
|
||||
setInterval(() => {
|
||||
setInterval(async () => {
|
||||
const current = new Date().getTime();
|
||||
|
||||
// console.log(current, next.morning, next.night);
|
||||
|
||||
for (const k of Object.keys(timeAnnouncements)) {
|
||||
if (next[k] && current > next[k]) {
|
||||
const announcement = timeAnnouncements[k];
|
||||
next[k] = getNextAnnouncementTime(announcement);
|
||||
saveNext();
|
||||
const announcement = timeAnnouncements[k];
|
||||
|
||||
if (subscriptions[k]) {
|
||||
for (const channelID of subscriptions[k]) {
|
||||
bot.channels.fetch(channelID, {allowUnknownGuild: true})
|
||||
.then(c =>
|
||||
(c as TextChannel).send(
|
||||
`${announcement.messagesPrefix ? announcement.messagesPrefix : ''} ${announcement.messages[Math.floor(Math.random() * announcement.messages.length)]}`
|
||||
)
|
||||
)
|
||||
.catch(err => `failed to send ${k} announcement to ${channelID}: ${err}`);
|
||||
}
|
||||
}
|
||||
const announcementObj = await db<ScheduledSubscription>('scheduledSubscriptions')
|
||||
.select('*')
|
||||
.where('name', k)
|
||||
.first();
|
||||
|
||||
if (!announcementObj) {
|
||||
await db<ScheduledSubscription>('scheduledSubscriptions')
|
||||
.insert({
|
||||
name: k,
|
||||
next: getNextAnnouncementTime(announcement)
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current < announcementObj.next) continue;
|
||||
|
||||
await db<ScheduledSubscription>('scheduledSubscriptions')
|
||||
.where('name', k)
|
||||
.update('next', getNextAnnouncementTime(announcement));
|
||||
|
||||
const subscriptions = await db<Subscription>('subscriptions')
|
||||
.select('*')
|
||||
.where('key', k);
|
||||
|
||||
for (const { channel } of subscriptions) {
|
||||
bot.channels.fetch(channel, {allowUnknownGuild: true})
|
||||
.then(c =>
|
||||
(c as TextChannel).send(
|
||||
`${announcement.messagesPrefix ? announcement.messagesPrefix : ''} ${announcement.messages[Math.floor(Math.random() * announcement.messages.length)]}`
|
||||
)
|
||||
)
|
||||
.catch(err => console.error(`failed to send ${k} announcement to ${channel}: ${err}`));
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
|
|
Loading…
Reference in New Issue