A ton of fixes and it's now possible to track servers and channels in the DB

This commit is contained in:
Bradley Bickford 2025-06-26 21:01:11 -04:00
parent d9bcdb766c
commit a247bfb35e
10 changed files with 132 additions and 24 deletions

Binary file not shown.

View File

@ -1,9 +1,10 @@
import { Client, Events, GatewayIntentBits, Guild, Interaction } from "discord.js"
import { Client, Events, GatewayIntentBits, Guild, GuildBasedChannel, Interaction } from "discord.js"
import { utilities } from "./utilties"
import { commands } from "./commands"
import { config } from "./config"
import { SQLCommon } from "./utilties/storage/interfaces"
const client : Client = new Client({
export const client : Client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
@ -11,12 +12,15 @@ const client : Client = new Client({
]
})
let db: SQLCommon
if (config.DB_MODE == "sqlite") {
const db = new utilities.sqlite.SqliteDB("breadbot_test.db")
db = new utilities.sqlite.SqliteDB("breadbot_test.db")
db.run("PRAGMA foreign_keys = ON")
utilities.tables.makeTables(db)
utilities.tables.makeConstraints(db)
}
client.once(Events.ClientReady, () => {
@ -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) => {

View File

@ -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<boolean> {
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<SQLResult> {
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
}
}

View File

@ -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<boolean> {
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<SQLResult> {
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
}
}

View File

@ -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
}

View File

@ -0,0 +1,7 @@
export enum SQLResult {
CREATED,
UPDATED,
DELETED,
ALREADYEXISTS,
FAILED
}

View File

@ -1,4 +1,4 @@
export interface sql_common {
export interface SQLCommon {
run(query: string) : Promise<number>
runParameterized(query: string, parameters: any[]): Promise<number>
getAll(query: string) : Promise<Object[]>

View File

@ -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<number> {
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<number> {
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<Object[]> {
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<Object[]> {
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)
}
})
})

View File

@ -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<number[]> {
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<number[]> {
return Promise.all(tables.map((statement) => db.run(statement)))
}
export async function makeConstraints(db: SQLCommon): Promise<number[]> {
return Promise.all(constraints.map((statement) => db.run(statement)))
}