[DB] Merge getGuildSettings & putGuild into GetOrCreateSettings (#9)

This commit is contained in:
2026-08-19 15:44:26 +00:00
committed by GitHub
parent 9c91e18962
commit cb2348f89c
23 changed files with 74 additions and 130 deletions
+2
View File
@@ -1,4 +1,6 @@
{
"name": "discordbot-ng",
"devDependencies": {
"@eslint/js": "9.39.5",
"@stylistic/eslint-plugin": "5.2.1",
+2 -4
View File
@@ -15,10 +15,8 @@ export default {
const guild = await client.guilds.fetch(interaction.guildId!);
const guildSettings = await Database.getGuildSettings(guild.id);
if (!guildSettings || !guildSettings.private_help_role_id)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to claim ticket.' });
const settings = await Database.getOrCreateSettings(guild.id);
if (!settings.private_help_role_id) return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to claim ticket.' });
const closeButton = new ButtonBuilder()
.setCustomId('close-ticket')
+3 -2
View File
@@ -7,8 +7,9 @@ export default {
try {
if (!interaction.guild) throw new Error('This action must be run in a guild.');
const settings = await Database.getGuildSettings(interaction.guild.id);
if (!settings || !settings.devwatch_role_id) throw new Error('`devwatch_role_id` was not configured. Please configure a role then try again.');
const settings = await Database.getOrCreateSettings(interaction.guild.id);
if (!settings.devwatch_role_id) throw new Error('`devwatch_role_id` was not configured. Please configure a role then try again.');
const member = await interaction.guild.members.fetch(interaction.user.id);
if (!member) throw new Error('Unable to retreive the member executing this command.');
+2 -4
View File
@@ -17,10 +17,8 @@ export default {
const guild = await client.guilds.fetch(interaction.guildId!);
const guildSettings = await Database.getGuildSettings(guild.id);
if (!guildSettings || !guildSettings.private_help_role_id)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to unclaim ticket.' });
const settings = await Database.getOrCreateSettings(guild.id);
if (!settings.private_help_role_id) return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to unclaim ticket.' });
const closeButton = new ButtonBuilder()
.setCustomId('close-ticket')
+3 -3
View File
@@ -174,12 +174,12 @@ async function purgePhrases(interaction: ChatInputCommandInteraction, user: User
async function dumpPhrases(interaction: ChatInputCommandInteraction) {
let content = '';
const guildSettings = await Database.getGuildSettings(interaction.guildId!);
const settings = await Database.getOrCreateSettings(interaction.guildId!);
await Database.getAllTicketPhrases((phrase: TicketPhrase) => {
if (phrase.user_id == 'admin' && (!guildSettings || !guildSettings.admin_role_id)) return;
if (phrase.user_id == 'admin' && (!settings.admin_role_id)) return;
const mention = phrase.user_id == 'admin' ? `<@&${guildSettings?.admin_role_id}>` : `<@${phrase.user_id}>`;
const mention = phrase.user_id == 'admin' ? `<@&${settings.admin_role_id}>` : `<@${phrase.user_id}>`;
content += `${mention}: \`${phrase.phrase}\`\n`;
});
+1 -1
View File
@@ -39,7 +39,7 @@ export default {
.addComponents(button);
await interaction.channel.send({ components: [row], content });
await Database.updateGuildSettings(interaction.guildId, 'private_help_channel_id', interaction.channelId);
await Database.updateSettings(interaction.guildId, 'private_help_channel_id', interaction.channelId);
interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Sent.' });
}
};
+5 -14
View File
@@ -17,23 +17,14 @@ export default {
.setRequired(true)
),
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
const guildSettings = await Database.getGuildSettings(interaction.guildId!);
if (!guildSettings || !guildSettings.general_chat_id) {
return interaction.reply('No general chat id found.');
}
const settings = await Database.getOrCreateSettings(interaction.guildId!);
if (!settings.general_chat_id) return interaction.reply('No general chat id found.');
const name = interaction.options.getString('new-name', true);
if (name.length > 100) return interaction.reply('Name must be less than 100 characters in length.');
if (name.length > 100) {
return interaction.reply('Name must be less than 100 characters in length.');
}
const channel = await interaction.guild!.channels.fetch(guildSettings.general_chat_id)!;
if (!channel) {
return interaction.reply('No general chat id found.');
}
const channel = await interaction.guild!.channels.fetch(settings.general_chat_id)!;
if (!channel) return interaction.reply('No general chat id found.');
try {
await channel.setName(name);
+15 -16
View File
@@ -134,80 +134,79 @@ export default {
await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
const response: string[] = [];
const settings = await Database.getGuildSettings(interaction.guildId!);
if (!settings) await Database.putGuild(interaction.guildId);
await Database.getOrCreateSettings(interaction.guildId);
const generalChannel = interaction.options.getChannel('general-channel');
if (generalChannel) {
await Database.updateGuildSettings(interaction.guildId, 'general_chat_id', generalChannel.id);
await Database.updateSettings(interaction.guildId, 'general_chat_id', generalChannel.id);
response.push(`**general_chat_id** has been set to: ${generalChannel}.`);
}
const ticketsChannel = interaction.options.getChannel('tickets-channel');
if (ticketsChannel) {
await Database.updateGuildSettings(interaction.guildId, 'tickets_channel_id', ticketsChannel.id);
await Database.updateSettings(interaction.guildId, 'tickets_channel_id', ticketsChannel.id);
response.push(`**tickets_channel_id** has been set to: ${ticketsChannel}.`);
}
const appealsChannel = interaction.options.getChannel('appeals-channel');
if (appealsChannel) {
await Database.updateGuildSettings(interaction.guildId, 'appeals_channel_id', appealsChannel.id);
await Database.updateSettings(interaction.guildId, 'appeals_channel_id', appealsChannel.id);
response.push(`**appeals_channel_id** has been set to ${appealsChannel}.`);
}
const eventLogsChannel = interaction.options.getChannel('event-logs-channel');
if (eventLogsChannel) {
await Database.updateGuildSettings(interaction.guildId, 'event_logs_channel_id', eventLogsChannel.id);
await Database.updateSettings(interaction.guildId, 'event_logs_channel_id', eventLogsChannel.id);
response.push(`**event_logs_channel_id** has been set to: ${eventLogsChannel}.`);
}
const discordLogsChannel = interaction.options.getChannel('discord-logs-channel');
if (discordLogsChannel) {
await Database.updateGuildSettings(interaction.guildId, 'discord_logs_channel_id', discordLogsChannel.id);
await Database.updateSettings(interaction.guildId, 'discord_logs_channel_id', discordLogsChannel.id);
response.push(`**discord_logs_channel_id** has been set to: ${discordLogsChannel}.`);
}
const auditLogsChannel = interaction.options.getChannel('audit-logs-channel');
if (auditLogsChannel) {
await Database.updateGuildSettings(interaction.guildId, 'audit_logs_channel_id', auditLogsChannel.id);
await Database.updateSettings(interaction.guildId, 'audit_logs_channel_id', auditLogsChannel.id);
response.push(`**audit_logs_channel_id** has been set to: ${auditLogsChannel}.`);
}
const voiceLogsChannel = interaction.options.getChannel('voice-logs-channel');
if (voiceLogsChannel) {
await Database.updateGuildSettings(interaction.guildId, 'voice_logs_channel_id', voiceLogsChannel.id);
await Database.updateSettings(interaction.guildId, 'voice_logs_channel_id', voiceLogsChannel.id);
response.push(`**voice_logs_channel_id** has been set to: ${voiceLogsChannel}.`);
}
const newMemberLogsChannel = interaction.options.getChannel('new-member-channel');
if (newMemberLogsChannel) {
await Database.updateGuildSettings(interaction.guildId, 'new_member_channel_id', newMemberLogsChannel.id);
await Database.updateSettings(interaction.guildId, 'new_member_channel_id', newMemberLogsChannel.id);
response.push(`**new_member_channel_id** has been set to: ${newMemberLogsChannel}.`);
}
const moderatorChannel = interaction.options.getChannel('moderator-channel');
if (moderatorChannel) {
await Database.updateGuildSettings(interaction.guildId, 'moderator_channel_id', moderatorChannel.id);
await Database.updateSettings(interaction.guildId, 'moderator_channel_id', moderatorChannel.id);
response.push(`**moderator_channel_id** has been set to": ${moderatorChannel}.`);
}
const adminRole = interaction.options.getRole('admin-role');
if (adminRole) {
await Database.updateGuildSettings(interaction.guildId, 'admin_role_id', adminRole.id);
await Database.updateSettings(interaction.guildId, 'admin_role_id', adminRole.id);
response.push(`**admin_role_id** has been set to: ${adminRole}.`);
}
const privateHelperRole = interaction.options.getRole('private-helper-role');
if (privateHelperRole) {
await Database.updateGuildSettings(interaction.guildId, 'private_help_role_id', privateHelperRole.id);
await Database.updateSettings(interaction.guildId, 'private_help_role_id', privateHelperRole.id);
response.push(`**private_help_role_id** has been set to: ${privateHelperRole}.`);
}
const devWatchRole = interaction.options.getRole('devwatch-role');
if (devWatchRole) {
await Database.updateGuildSettings(interaction.guildId, 'devwatch_role_id', devWatchRole.id);
await Database.updateSettings(interaction.guildId, 'devwatch_role_id', devWatchRole.id);
response.push(`**devwatch_role_id** has been set to ${devWatchRole}.`);
}
@@ -264,13 +263,13 @@ export default {
const githubReleaseChannel = interaction.options.getChannel('github-release-channel');
if (githubReleaseChannel) {
await Database.updateGuildSettings(interaction.guildId, 'github_release_channel', githubReleaseChannel.id);
await Database.updateSettings(interaction.guildId, 'github_release_channel', githubReleaseChannel.id);
response.push(`**github_release_channel** has been set to ${githubReleaseChannel}.`);
}
const siteBreakerRole = interaction.options.getRole('site-breaker-role');
if (siteBreakerRole) {
await Database.updateGuildSettings(interaction.guildId, 'site_breaker_role_id', siteBreakerRole.id);
await Database.updateSettings(interaction.guildId, 'site_breaker_role_id', siteBreakerRole.id);
response.push(`**site_breaker_role_id** has been set to ${siteBreakerRole}.`);
}
+4 -38
View File
@@ -49,49 +49,15 @@ export default {
modal.addLabelComponents(additionalInfoLabel, createPrivateTicket);
const guildSettings = await Database.getGuildSettings(interaction.guildId!);
if (!guildSettings || !guildSettings.moderator_channel_id)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Report channel missing. Unable to submit report' });
const reportsChannel = await interaction.guild!.channels.fetch(guildSettings.moderator_channel_id);
const settings = await Database.getOrCreateSettings(interaction.guildId!);
if (!settings.moderator_channel_id)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Moderator channel missing. Unable to submit report' });
const reportsChannel = await interaction.guild!.channels.fetch(settings.moderator_channel_id);
if (!reportsChannel || !reportsChannel.isSendable())
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Report channel missing. Unable to submit report' });
await interaction.showModal(modal);
// await reportsChannel.send({
// embeds: [{
// title: 'New Message Report!',
// fields: [
// {
// name: 'Message',
// value: interaction.targetMessage.url,
// inline: false
// },
// {
// name: 'Author',
// value: interaction.targetMessage.author.toString(),
// inline: false
// },
// {
// name: 'Reporter',
// value: interaction.user.toString(),
// inline: false
// }
// ]
// }]
// });
// await interaction.reply({
// embeds: [{
// color: 0x014995,
// description:
// "Thanks for making a report! I've notified the moderators who can take further action."
// }],
// flags: [MessageFlags.Ephemeral]
// });
} catch (error) {
console.error(error);
if (!interaction.replied) {
+2 -3
View File
@@ -10,12 +10,11 @@ const IGNORED_ACTIONS = [
export async function handleAuditLogCreate(entry: GuildAuditLogsEntry, guild: Guild) {
if (!await shouldLog(entry, guild)) return;
const settings = await Database.getGuildSettings(guild.id);
if (!settings || !settings.audit_logs_channel_id) return;
const settings = await Database.getOrCreateSettings(guild.id);
if (!settings.audit_logs_channel_id) return;
const channel = await guild.channels.fetch(settings.audit_logs_channel_id);
if (!channel || !channel.isSendable()) return;
const fields: APIEmbedField[] = [
+2 -6
View File
@@ -2,9 +2,5 @@ import { Guild } from 'discord.js';
import { Database } from '../shared/Database';
export async function handleGuildCreate(guild: Guild) {
try {
if (!await Database.getGuildSettings(guild.id)) await Database.putGuild(guild.id);
} catch (e) {
console.error(e);
}
}
await Database.getOrCreateSettings(guild.id);
}
+2 -2
View File
@@ -3,9 +3,9 @@ import { Database } from '../shared/Database';
import { getE621Alts } from '../utils';
export async function handleMemberJoin(member: GuildMember) {
const guildSettings = await Database.getGuildSettings(member.guild.id);
const guildSettings = await Database.getOrCreateSettings(member.guild.id);
if (guildSettings?.new_member_channel_id) {
if (guildSettings.new_member_channel_id) {
const channel = await member.guild.channels.fetch(guildSettings.new_member_channel_id) as GuildTextBasedChannel;
if (channel) {
+1 -1
View File
@@ -34,7 +34,7 @@ async function sendMovedMessage(channel: GuildTextBasedChannel, member: GuildMem
}
async function getVoiceLogsChannel(guild: Guild): Promise<GuildTextBasedChannel | undefined> {
const settings = await Database.getGuildSettings(guild.id);
const settings = await Database.getOrCreateSettings(guild.id);
if (!settings || !settings.voice_logs_channel_id) return;
+1 -1
View File
@@ -10,7 +10,7 @@ export default {
const guild = await client.guilds.fetch(interaction.guildId!);
const member = await guild.members.fetch(userId);
const guildSettings = await Database.getGuildSettings(guild.id);
const guildSettings = await Database.getOrCreateSettings(guild.id);
if (!guildSettings || !guildSettings.private_help_channel_id)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to create ticket. Please report this to a developer.' });
+1 -1
View File
@@ -8,7 +8,7 @@ export default {
const guild = await client.guilds.fetch(interaction.guildId!);
const member = await guild.members.fetch(interaction.user.id);
const guildSettings = await Database.getGuildSettings(guild.id);
const guildSettings = await Database.getOrCreateSettings(guild.id);
if (!guildSettings || !guildSettings.private_help_role_id)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to create ticket. Please report this to a staff member.' });
+1 -1
View File
@@ -18,7 +18,7 @@ export default {
if (!reportedMessageChannel || !reportedMessage)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to submit report. Please report this to a staff member.' });
const guildSettings = await Database.getGuildSettings(interaction.guildId!);
const guildSettings = await Database.getOrCreateSettings(interaction.guildId!);
if (!guildSettings || !guildSettings.moderator_channel_id)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Report channel missing. Unable to submit report.' });
+19 -24
View File
@@ -2,7 +2,7 @@ import path from 'path';
import { open, Database as SqliteDatabase } from 'sqlite';
import sqlite3 from 'sqlite3';
import { Message } from '../events';
import { AppealMessage, Ban, GithubUserMapping, GuildArraySetting, GuildSetting, GuildSettings, KnowledgebaseItem, LoggedMessage, Note, PrivateHelpTicket, RoleButton, TicketMessage, TicketPhrase } from '../types';
import { AppealMessage, Ban, GithubUserMapping, GuildArraySetting, GuildSettings, KnowledgebaseItem, LoggedMessage, Note, PrivateHelpTicket, RoleButton, TicketMessage, TicketPhrase } from '../types';
import { deserializeMessage, serializeMessage, wait } from '../utils';
import { readFileSync } from 'fs';
import { Encrypter } from './Encrypter';
@@ -14,6 +14,7 @@ export const enum PrivateHelpTicketStatus {
export class Database {
private static db: SqliteDatabase;
private static settings: Map<string, GuildSettings> = new Map();
static async open(file: string): Promise<void> {
if (Database.db) return;
@@ -28,7 +29,6 @@ export class Database {
console.log('SQLite database opened');
await Database.ensure();
await Database.migrate();
}
@@ -75,50 +75,45 @@ export class Database {
//#region Guild Settings
static async getGuildSettings(guildId: string): Promise<GuildSettings> {
return await Database.db.get<GuildSettings>('SELECT * FROM settings WHERE guild_id = ?', guildId) as GuildSettings;
static async getOrCreateSettings(guildId: string): Promise<GuildSettings> {
const localStore = this.settings.get(guildId);
if (localStore) return localStore;
await Database.db.run('INSERT OR IGNORE INTO settings (guild_id) VALUES (?)', guildId);
const store = await Database.db.get<GuildSettings>('SELECT * FROM settings WHERE guild_id = ?', guildId) as GuildSettings;
this.settings.set(guildId, store);
return store;
}
static async putGuild(guildId: string) {
await Database.db.run('INSERT INTO settings(guild_id) VALUES (?)', guildId);
}
static async updateGuildSettings(guildId: string, key: GuildSetting, value: string) {
static async updateSettings(guildId: string, key: Exclude<keyof GuildSettings, 'guild_id'>, value: string) {
await Database.db.run(`UPDATE settings SET ${key} = ? WHERE guild_id = ?`, value, guildId);
this.settings.delete(guildId);
}
// Since "setting" has guaranteed values and is never set by the user, this shouldn't cause any security issues.
// But it does allow me to skip rewriting this a bunch.
static async getGuildArraySetting(setting: GuildArraySetting, guildId: string): Promise<string[]> {
const settings = await Database.db.get<{ [setting]: string }>(`SELECT ${setting} FROM settings WHERE guild_id = ?`, guildId);
if (!settings || !settings[setting]) return [];
const settings = await Database.getOrCreateSettings(guildId);
if (!settings[setting]) return [];
return settings[setting].split(',');
}
static async putGuildArraySetting(setting: GuildArraySetting, guildId: string, value: string) {
const values = await Database.getGuildArraySetting(setting, guildId);
if (!values.includes(value)) values.push(value);
if (values.indexOf(value) == -1) values.push(value);
const newString = values.join(',');
await Database.db.run(`UPDATE settings SET ${setting} = ? WHERE guild_id = ?`, newString, guildId);
await Database.updateSettings(guildId, setting, values.join(','));
}
static async removeGuildArraySetting(setting: GuildArraySetting, guildId: string, value: string): Promise<boolean> {
const values = await Database.getGuildArraySetting(setting, guildId);
const index = values.indexOf(value);
if (index == -1) return false;
if (index === -1) return false;
values.splice(index, 1);
const newString = values.join(',');
await Database.db.run(`UPDATE settings SET ${setting} = ? WHERE guild_id = ?`, newString, guildId);
await Database.updateSettings(guildId, setting, values.join(','));
return true;
}
-1
View File
@@ -33,7 +33,6 @@ export type GuildSettings = {
site_breaker_role_id?: string
}
type GuildSetting = Exclude<keyof GuildSettings, 'guild_id'>
export type GuildArraySetting = 'staff_categories' | 'safe_channels' | 'link_skip_channels';
export type TicketMessage = {
+2 -2
View File
@@ -17,7 +17,7 @@ export async function appealUpdateHandler(client: Client, update: string) {
}
async function postAppeal(client: Client, data: AppealUpdate) {
const guildSettings = await Database.getGuildSettings(config.DISCORD_GUILD_ID!);
const guildSettings = await Database.getOrCreateSettings(config.DISCORD_GUILD_ID!);
if (!guildSettings || !guildSettings.appeals_channel_id) return;
@@ -40,7 +40,7 @@ async function postAppeal(client: Client, data: AppealUpdate) {
}
async function updateAppeal(client: Client, data: AppealUpdate) {
const guildSettings = await Database.getGuildSettings(config.DISCORD_GUILD_ID!);
const guildSettings = await Database.getOrCreateSettings(config.DISCORD_GUILD_ID!);
if (!guildSettings || !guildSettings.appeals_channel_id) return;
+1 -1
View File
@@ -86,7 +86,7 @@ export async function logCustomEvent(guild: Guild, data: CustomEventLogData) {
}
async function getEventLogChannel(guild: Guild, channel: GuildBasedChannel | null = null): Promise<GuildTextBasedChannel | null> {
const settings = await Database.getGuildSettings(guild.id);
const settings = await Database.getOrCreateSettings(guild.id);
if (!settings) return null;
+1 -1
View File
@@ -25,7 +25,7 @@ export async function closeOldTickets(client: Client) {
}
export async function createPrivateHelpTicketThread(client: Client, guild: Guild, creator: GuildMember | null, reason: string, customTitle: string = '', additionalMembersToAdd: string[] = []): Promise<PrivateThreadChannel | null> {
const guildSettings = await Database.getGuildSettings(guild.id);
const guildSettings = await Database.getOrCreateSettings(guild.id);
if (!guildSettings || !guildSettings.private_help_channel_id || !guildSettings.private_help_role_id) return null;
+3 -3
View File
@@ -18,7 +18,7 @@ export async function ticketUpdateHandler(client: Client, update: string) {
}
async function postTicket(client: Client, data: TicketUpdate) {
const guildSettings = await Database.getGuildSettings(config.DISCORD_GUILD_ID!);
const guildSettings = await Database.getOrCreateSettings(config.DISCORD_GUILD_ID!);
if (!guildSettings || !guildSettings.tickets_channel_id) return;
@@ -40,7 +40,7 @@ async function postTicket(client: Client, data: TicketUpdate) {
}
async function updateTicket(client: Client, data: TicketUpdate) {
const guildSettings = await Database.getGuildSettings(config.DISCORD_GUILD_ID!);
const guildSettings = await Database.getOrCreateSettings(config.DISCORD_GUILD_ID!);
if (!guildSettings || !guildSettings.tickets_channel_id) return;
@@ -205,7 +205,7 @@ async function getButtons(ticket: Ticket): Promise<ActionRowBuilder<ButtonBuilde
}
async function sendTicketAlerts(ticket: Ticket, channel: SendableChannels) {
const guildSettings = await Database.getGuildSettings(config.DISCORD_GUILD_ID!);
const guildSettings = await Database.getOrCreateSettings(config.DISCORD_GUILD_ID!);
if (!guildSettings || !guildSettings.admin_role_id) return;
+1 -1
View File
@@ -205,7 +205,7 @@ async function handleGithubRelease(client: Client, req: Request, res: Response):
if (data.action != 'published' || data.repository.id != GITHUB_REPO_ID) return;
const settings = await Database.getGuildSettings(config.DISCORD_GUILD_ID!);
const settings = await Database.getOrCreateSettings(config.DISCORD_GUILD_ID!);
if (!settings || !settings.github_release_channel) return;