From a247bfb35e8b5c4d37054fbfd67e81236718ed77 Mon Sep 17 00:00:00 2001 From: Bradley Bickford Date: Thu, 26 Jun 2025 21:01:11 -0400 Subject: [PATCH] A ton of fixes and it's now possible to track servers and channels in the DB --- breadbot_test.db | Bin 20480 -> 45056 bytes src/breadbot.ts | 27 ++++++++++---- src/utilties/discord/channels.ts | 33 ++++++++++++++++++ .../discord/{command_utils.ts => commands.ts} | 0 src/utilties/discord/guilds.ts | 33 ++++++++++++++++++ src/utilties/index.ts | 10 ++++-- src/utilties/storage/enumerations.ts | 7 ++++ src/utilties/storage/interfaces.ts | 2 +- src/utilties/storage/sqlite.ts | 27 ++++++++------ src/utilties/storage/tables.ts | 17 +++++++-- 10 files changed, 132 insertions(+), 24 deletions(-) create mode 100644 src/utilties/discord/channels.ts rename src/utilties/discord/{command_utils.ts => commands.ts} (100%) create mode 100644 src/utilties/discord/guilds.ts create mode 100644 src/utilties/storage/enumerations.ts diff --git a/breadbot_test.db b/breadbot_test.db index f15a818035e62fed9676ae0d46d1242b2658c92b..c7e3fc301f164374c81911d9a90c3dd8fe06792e 100644 GIT binary patch literal 45056 zcmeI)Pi)&%90zbaX`M7_^0H1N-O89@NNu5krP*Lp*U_vh)6{O1t{}!Ad2algd2{T} zcIYM!Xk9@9AWW!6ZC^e8(z1H` zm4yX$ap~l#>e5+te(vlzW;M0`Km)+xha zTW4KldtVAm4C>WfbIms$oArFdY&LkdFJKtbet$0zxL z{;R-$VSxYyAOHafKmY;|fB*y_009Uat=?ScRVAOHafKmY;|fB*y_ z009UD0{s5}Q0_yC{7SwhpObfqK@O8l{+Il%{Pq0B{K@=iK9~DF_kHdw7K8-?5P$## zAOHafKmY;|fB*y@g229^enojaoqOl~53~(wlC9~_iE~FAlz+h9tm)SN9V}oxUHEwV z(+iu6tL*^CpswyaZppE&D|-qos3?M{2y&Yi_4X05hcEBmmX3-tHW{YoyL=DBc^0rU zoxim(JhOG+O%YIIQv-6WWIsQ+o4anGdrXw9xlY~H8x7O0tAjjZVCT$xZ*3+D84Q-H7z)G)AP-mr?Ps~Y7jb>W}y`pTD^X8OL|>|P9B&%Sn{au zId-W|Z3bvfq*&w>i=6r6!lu-2KoH5M0ho2WWSCy9>3M8Ei1(DjqMv8cFZ}Svrc@F2 zUTU)T^75c5y6vsAIV-#SS?n$zo8Px3wNKnhd)3+B$BM?eD?i!CL^ZEEW{s+|;Z%_) zk}PPH2VJQiFGwPY%_DJ|c8yKhVpA)I6D($@sHlzm;?nG+GAkJgO714s9P#WEtyc5< z|3joGk>AL7GJB^SjKmD_j2Ly<5y{X(7#0kt5S|f3C;70XHCn%mf1q{X7%CzdzO&W`ZGuWTa#|kxeQC zJS-RvVCpjYQDP4)5P$##AOHafKmY;|fB*y_009X6Zv-afKIK&VLjo(VZX7vuuv{MX z)XC=#9#$7!T3U1+Lp>9Gp(eloFOxeGdtiY81Rwwb2tWV=5P$##AOHafK%j>N#QlFh zy#(L?-$N%BO@ROeAOHafKmY;|fB*y_009WZ2;lqwV_>iu0uX=z1Rwwb2tWV=5P$## zAkaesxc}cnClgJ900bZa0SG_<0uX=z1Rwwb2*e2B{(lS%HbVdc5P$##AOHafKmY;| KfB*z~NZ>Cj3rq+A delta 125 zcmZp8z|^pSae}lU69WSSD-go~(?lI(Q6>hxqD8#?KNwi};u!c1_)qf1@%`T{DDaAx zt5J@bU0ha{v59|j4)3(dl6*$Yjbe { @@ -24,13 +28,24 @@ client.once(Events.ClientReady, () => { console.log("Breadbot is ready") client.guilds.cache.forEach(async (guild: Guild) => { - console.log(`ID: ${guild.id}, Name: ${guild.name}, Desc: ${guild.description}`) - await utilities.command.deployCommands(guild.id) + await utilities.commands.deployCommands(guild.id) + + // TODO handle failures? + await utilities.guilds.insertGuild(db, guild) + + guild.channels.cache.forEach(async (channel: GuildBasedChannel) => { + await utilities.channels.insertChannel(db, channel) + }) }) }) client.on(Events.GuildCreate, async (guild : Guild) => { - await utilities.command.deployCommands(guild.id) + await utilities.commands.deployCommands(guild.id) + await utilities.guilds.insertGuild(db, guild) + + guild.channels.cache.forEach(async (channel: GuildBasedChannel) => { + await utilities.channels.insertChannel(db, channel) + }) }) client.on(Events.InteractionCreate, async (interaction: Interaction) => { diff --git a/src/utilties/discord/channels.ts b/src/utilties/discord/channels.ts new file mode 100644 index 0000000..ebc340f --- /dev/null +++ b/src/utilties/discord/channels.ts @@ -0,0 +1,33 @@ +import { SQLCommon } from "../storage/interfaces"; +import { GuildBasedChannel } from "discord.js"; +import { SQLResult } from "../storage/enumerations"; + +export async function doesChannelExist(db: SQLCommon, channel : GuildBasedChannel) : Promise { + const queryResult : Object[] = await db.getAllParameterized( + "SELECT * FROM channels WHERE server_snowflake = ? AND channel_snowflake = ?", + [channel.guild.id, channel.id] + ) + + return queryResult.length != 0 +} + +export async function insertChannel(db: SQLCommon, channel: GuildBasedChannel) : Promise { + const alreadyExists: boolean = await doesChannelExist(db, channel) + + if(alreadyExists) { + return SQLResult.ALREADYEXISTS + } + + try { + await db.runParameterized( + "INSERT INTO channels VALUES (?, ?, ?, ?)", + [channel.id, channel.guild.id, channel.name, channel.isThread()] + ) + + return SQLResult.CREATED + } catch (err) { + //TODO Winston should handle this + console.log(err) + return SQLResult.FAILED + } +} \ No newline at end of file diff --git a/src/utilties/discord/command_utils.ts b/src/utilties/discord/commands.ts similarity index 100% rename from src/utilties/discord/command_utils.ts rename to src/utilties/discord/commands.ts diff --git a/src/utilties/discord/guilds.ts b/src/utilties/discord/guilds.ts new file mode 100644 index 0000000..4433cbf --- /dev/null +++ b/src/utilties/discord/guilds.ts @@ -0,0 +1,33 @@ +import { Guild } from "discord.js"; +import { SQLCommon } from "../storage/interfaces"; +import { SQLResult } from "../storage/enumerations"; + +export async function doesGuildExist(db: SQLCommon, guild : Guild) : Promise { + const queryResult : Object[] = await db.getAllParameterized( + "SELECT * FROM servers WHERE server_snowflake = ?", + [guild.id] + ) + + return queryResult.length != 0 +} + +export async function insertGuild(db: SQLCommon, guild: Guild) : Promise { + const alreadyExists: boolean = await doesGuildExist(db, guild) + + if (alreadyExists) { + return SQLResult.ALREADYEXISTS + } + try { + await db.runParameterized( + "INSERT INTO servers VALUES (?, ?, ?)", + [guild.id, guild.name, guild.description] + ) + + return SQLResult.CREATED + } catch (err) { + console.log("Insert Failed") + //TODO Winston should handle this + console.log(err) + return SQLResult.FAILED + } +} \ No newline at end of file diff --git a/src/utilties/index.ts b/src/utilties/index.ts index cce9d11..6f9e2ca 100644 --- a/src/utilties/index.ts +++ b/src/utilties/index.ts @@ -1,9 +1,13 @@ -import * as command from "./discord/command_utils" +import * as commands from "./discord/commands" import * as sqlite from "./storage/sqlite" import * as tables from "./storage/tables" +import * as guilds from "./discord/guilds" +import * as channels from "./discord/channels" export const utilities = { - command, + commands, sqlite, - tables + tables, + guilds, + channels } \ No newline at end of file diff --git a/src/utilties/storage/enumerations.ts b/src/utilties/storage/enumerations.ts new file mode 100644 index 0000000..ef7583c --- /dev/null +++ b/src/utilties/storage/enumerations.ts @@ -0,0 +1,7 @@ +export enum SQLResult { + CREATED, + UPDATED, + DELETED, + ALREADYEXISTS, + FAILED +} \ No newline at end of file diff --git a/src/utilties/storage/interfaces.ts b/src/utilties/storage/interfaces.ts index 5c82513..e5ee6ba 100644 --- a/src/utilties/storage/interfaces.ts +++ b/src/utilties/storage/interfaces.ts @@ -1,4 +1,4 @@ -export interface sql_common { +export interface SQLCommon { run(query: string) : Promise runParameterized(query: string, parameters: any[]): Promise getAll(query: string) : Promise diff --git a/src/utilties/storage/sqlite.ts b/src/utilties/storage/sqlite.ts index 2102604..33cc857 100644 --- a/src/utilties/storage/sqlite.ts +++ b/src/utilties/storage/sqlite.ts @@ -1,8 +1,8 @@ import * as sqlite3 from 'sqlite3' -import { sql_common } from "./interfaces" +import { SQLCommon } from "./interfaces" -export class SqliteDB implements sql_common { +export class SqliteDB implements SQLCommon { private db : sqlite3.Database; public constructor(private readonly dbName: string) { @@ -10,7 +10,7 @@ export class SqliteDB implements sql_common { } async run(query: string): Promise { - return new Promise(() => { + return new Promise((resolve, reject) => { this.db.run(query, (result : sqlite3.RunResult, err: Error) => { if (err) { // TODO Winston should handle this @@ -18,9 +18,9 @@ export class SqliteDB implements sql_common { throw err } else { if (result != null) { - return result.changes + resolve(result.changes) } else { - return 0 + resolve(0) } } }) @@ -28,42 +28,47 @@ export class SqliteDB implements sql_common { } async runParameterized(query: string, parameters: any[]): Promise { - return new Promise(() => { + return new Promise((resolve, reject) => { this.db.run(query, parameters, (result : sqlite3.RunResult, err: Error) => { if (err) { // TODO Winston should handle this console.log(err) throw err } else { - return result.changes + if (result != null) { + resolve(result.changes) + } else { + resolve(0) + } } }) }) } async getAll(query: string): Promise { - return new Promise(() => { + return new Promise((resolve, reject) => { this.db.all(query, (err: Error, rows: Object[]) => { if (err) { // TODO Winston should handle this console.log(err) throw err } else { - return rows + console.log("Got rows") + resolve(rows) } }) }) } getAllParameterized(query: string, parameters: any[]): Promise { - return new Promise(() => { + return new Promise((resolve, reject) => { this.db.all(query, parameters, (err: Error, rows: Object[]) => { if (err) { // TODO Winston should handle this console.log(err) throw err } else { - return rows + resolve(rows) } }) }) diff --git a/src/utilties/storage/tables.ts b/src/utilties/storage/tables.ts index 4304f56..ddb07fb 100644 --- a/src/utilties/storage/tables.ts +++ b/src/utilties/storage/tables.ts @@ -1,11 +1,22 @@ -import { sql_common } from "./interfaces"; +import { SQLCommon } from "./interfaces"; const tables: string[] = [ "CREATE TABLE IF NOT EXISTS servers (server_snowflake bigint NOT NULL PRIMARY KEY,server_name text NOT NULL,server_description mediumtext);", - "CREATE TABLE IF NOT EXISTS channels (channel_snowflake bigint NOT NULL PRIMARY KEY,server_snowflake bigint NOT NULL,channel_name text NOT NULL);" + "CREATE TABLE IF NOT EXISTS channels (channel_snowflake bigint NOT NULL PRIMARY KEY,server_snowflake bigint NOT NULL,channel_name text NOT NULL,is_thread bit NOT NULL);", + "CREATE TABLE IF NOT EXISTS messages (message_snowflake bigint NOT NULL PRIMARY KEY,channel_snowflake bigint NOT NULL,user_snowflake bigint NOT NULL,message_content longtext NOT NULL,message_timestamp datetime NOT NULL,message_deleted bit NOT NULL);", + "CREATE TABLE IF NOT EXISTS message_content_changes (message_change_id bigint NOT NULL PRIMARY KEY,message_snowflake bigint NOT NULL,message_change_old_timestamp datetime NOT NULL,message_change_old_content longtext NOT NULL);", + "CREATE TABLE IF NOT EXISTS message_attachments (attachment_snowflake bigint NOT NULL PRIMARY KEY,message_snowflake bigint NOT NULL,attachment_name text NOT NULL,attachment_description text,attachment_timestamp datetime NOT NULL,attachment_mime_type text,attachment_url text NOT NULL,attachment_downloaded bit NOT NULL);" ] -export async function makeTables(db: sql_common): Promise { +const constraints: string[] = [ + "ALTER TABLE channels ADD CONSTRAINT channels_server_snowflake_fk FOREIGN KEY (server_snowflake) REFERENCES servers (server_snowflake);" +] + +export async function makeTables(db: SQLCommon): Promise { return Promise.all(tables.map((statement) => db.run(statement))) } +export async function makeConstraints(db: SQLCommon): Promise { + return Promise.all(constraints.map((statement) => db.run(statement))) +} +