From 7d8e252b7969d2c61e20ff029808a627fe33549a Mon Sep 17 00:00:00 2001 From: Bradley Bickford Date: Tue, 18 Nov 2025 16:31:02 -0500 Subject: [PATCH] Finalizing the work for moving to TypeORM before testing of previously working functions begins --- src/breadbot.ts | 4 +- src/commands/breadalert.ts | 55 --------------- src/commands/index.ts | 4 +- src/utilties/breadbot/breadthread.ts | 57 ---------------- src/utilties/discord/regex_matching.ts | 28 ++++---- src/utilties/discord/voice.ts | 33 ++++----- src/utilties/events/voice.ts | 7 +- src/utilties/index.ts | 6 -- src/utilties/storage/entities/DBMessage.ts | 6 +- .../storage/entities/DBMessageRegex.ts | 18 +++++ .../storage/entities/DBMessageRegexMatches.ts | 15 ++++ src/utilties/storage/entities/DBServer.ts | 4 ++ src/utilties/storage/enumerations.ts | 7 -- src/utilties/storage/interfaces.ts | 7 -- src/utilties/storage/sqlite.ts | 68 ------------------- src/utilties/storage/tables.ts | 24 ------- 16 files changed, 82 insertions(+), 261 deletions(-) delete mode 100644 src/commands/breadalert.ts delete mode 100644 src/utilties/breadbot/breadthread.ts create mode 100644 src/utilties/storage/entities/DBMessageRegex.ts create mode 100644 src/utilties/storage/entities/DBMessageRegexMatches.ts delete mode 100644 src/utilties/storage/enumerations.ts delete mode 100644 src/utilties/storage/interfaces.ts delete mode 100644 src/utilties/storage/sqlite.ts delete mode 100644 src/utilties/storage/tables.ts diff --git a/src/breadbot.ts b/src/breadbot.ts index b2e9fc6..add93b4 100644 --- a/src/breadbot.ts +++ b/src/breadbot.ts @@ -20,6 +20,7 @@ import { commands } from "./commands" import { DBCall } from "./utilties/storage/entities/DBCall" import { DBCallTranscriptions } from "./utilties/storage/entities/DBCallTranscriptions" import { DBCallUsers } from "./utilties/storage/entities/DBCallUsers" +import { DBMessageRegex } from "./utilties/storage/entities/DBMessageRegex" console.log(__dirname + path.sep + "utilities" + path.sep + "storage" + path.sep + "entities" + path.sep + "*.ts") @@ -36,7 +37,8 @@ export const dataSource = new DataSource({ DBMessageAttachments, DBCall, DBCallTranscriptions, - DBCallUsers + DBCallUsers, + DBMessageRegex ], synchronize: true, logging: true diff --git a/src/commands/breadalert.ts b/src/commands/breadalert.ts deleted file mode 100644 index 0a19372..0000000 --- a/src/commands/breadalert.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { CommandInteraction, SlashCommandBuilder } from "discord.js"; - -export const enabled: boolean = true - -export const data = new SlashCommandBuilder() - .setName("breadalert") - .setDescription("Controls event alerting using the Bread Alert subsystem") - .addSubcommand((subcommand) => - subcommand - .setName("list") - .setDescription("List the current Bread Alert active alerts") - .addIntegerOption(option => - option - .setName("count") - .setDescription("The number of future alerts to return, default 5") - .setRequired(false) - ) - ) - .addSubcommand(subcommand => - subcommand - .setName("add") - .setDescription("Add a new Bread Alert") - .addStringOption(option => - option - .setName("name") - .setDescription("The name of the event, must be unique") - .setRequired(true) - ) - .addStringOption(option => - option - .setName("date") - .setDescription("The date and time of the event in YYYY-MM-DD HH:MM:SS format") - .setRequired(true) - ) - .addStringOption(option => - option - .setName("notifications") - .setDescription("A comma separated list of time offsets that determine when to alert prior to the event") - .setRequired(false) - ) - ) - .addSubcommand(subcommand => - subcommand - .setName("delete") - .setDescription("Delete a Bread Alert") - .addStringOption(option => - option - .setName("name") - .setDescription("The name of the event to remove") - ) - ) - -export async function execute(interaction: CommandInteraction) { - return interaction.reply("NOT IMPLEMENTED!") -} \ No newline at end of file diff --git a/src/commands/index.ts b/src/commands/index.ts index 03ea096..6333e84 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,7 +1,5 @@ import * as ping from "./ping"; -import * as breadalert from "./breadalert" export const commands = { - ping, - breadalert + ping } \ No newline at end of file diff --git a/src/utilties/breadbot/breadthread.ts b/src/utilties/breadbot/breadthread.ts deleted file mode 100644 index 77e95d3..0000000 --- a/src/utilties/breadbot/breadthread.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { timeShorthandToSeconds } from "../time/conversions"; -import { SQLCommon } from "../storage/interfaces"; -import { Client } from "discord.js"; - -export async function breadthreadLockExists(db: SQLCommon, channelId: string) : Promise { - const queryResult: Object[] = await db.getAllParameterized( - "SELECT * FROM breadthread_autolock WHERE channel_snowflake = ?", - [channelId] - ) - - return queryResult.length != 0 -} - -export async function breadthreadEnsureAutoLock(db: SQLCommon, channelId: string, inactiveTimeUntilLocked: string) { - const timeUntilLocked = timeShorthandToSeconds(inactiveTimeUntilLocked) - - if(await breadthreadLockExists(db, channelId)) { - await db.runParameterized( - "UPDATE breadthread_autolock SET inactivity_seconds = ? WHERE channel_snowflake = ?", - [timeUntilLocked, channelId] - ) - } else { - await db.runParameterized( - "INSERT INTO breadthread_autolock (channel_snowflake, inactivity_seconds, locked) VALUES (?, ?, ?)", - [channelId, timeUntilLocked, 0] - ) - } -} - -export async function breadthreadRemoveAutoLock(db: SQLCommon, channelId: string) { - await db.runParameterized( - "DELETE FROM breadthread_autolock WHERE channel_snowflake = ?", - [channelId] - ) -} - -export async function breadthreadProcessLocks(db: SQLCommon, client: Client) { - const currentTimeSeconds = Math.round((new Date()).getTime() / 1000); - (await db.getAll("SELECT * FROM breadthread_autolock WHERE locked = 0")).forEach(async (row : any) => { - const channel = client.channels.cache.find(row.channel_snowflake) - - if(channel?.isThread()) { - const lastMessageTime: number = Math.round( - channel.lastMessage?.createdAt.getTime() ?? 0 / 1000 - ) - - if(lastMessageTime != 0 && currentTimeSeconds - lastMessageTime >= row.inactivity_seconds) { - await channel.setLocked(true, "Breadbot is locking this thread because the inactivity timeout was exceeded!") - - await db.runParameterized( - "UPDATE breadthread_autolock SET locked = 1 WHERE locked = 0 AND channel_snowflake = ?", - [channel.id] - ) - } - } - }) -} \ No newline at end of file diff --git a/src/utilties/discord/regex_matching.ts b/src/utilties/discord/regex_matching.ts index 14cda5f..08094c7 100644 --- a/src/utilties/discord/regex_matching.ts +++ b/src/utilties/discord/regex_matching.ts @@ -1,16 +1,18 @@ import { Guild } from "discord.js"; -import { SQLCommon } from "../storage/interfaces"; +import { DBMessageRegex } from "../storage/entities/DBMessageRegex"; +import { Repository } from "typeorm"; +import { DBServer } from "../storage/entities/DBServer"; -export async function getRegexesForGuild(db: SQLCommon, guild: Guild): Promise { - return db.getAllParameterized( - "SELECT * FROM message_regexes WHERE server_snowflake = ?", - [guild.id] - ) -} - -export async function getRoleExclusionSnowflakesForGuild(db: SQLCommon, guild: Guild): Promise { - return (await db.getAllParameterized( - "SELECT role_snowflake FROM message_regex_no_role_check WHERE server_snowflake = ?", - [guild.id] - )).map((o) => (o as any).role_snowflake) +export async function getRegexesForGuild(db: Repository, guild: Guild): Promise { + return (await db.findOne({ + select: { + regexes: true + }, + relations: { + regexes: true + }, + where: { + server_snowflake: guild.id + } + }))?.regexes } \ No newline at end of file diff --git a/src/utilties/discord/voice.ts b/src/utilties/discord/voice.ts index 3e39757..7174b10 100644 --- a/src/utilties/discord/voice.ts +++ b/src/utilties/discord/voice.ts @@ -1,20 +1,21 @@ -import { Guild, VoiceBasedChannel } from "discord.js"; -import { SQLCommon } from "../storage/interfaces"; +import { VoiceBasedChannel } from "discord.js"; +import { IsNull, Repository } from "typeorm"; +import { DBCall } from "../storage/entities/DBCall"; -export async function breadbotInCall(db: SQLCommon, channel: VoiceBasedChannel) : Promise { - const queryResult: Object[] = await db.getAllParameterized( - "SELECT * FROM calls WHERE channel_snowflake = ? AND call_end_time IS NULL", - [channel.id] - ) - - return queryResult.length != 0 +export async function breadbotInCall(db: Repository, channel: VoiceBasedChannel) : Promise { + return (await db.findOne({ + where: { + channel: {channel_snowflake: channel.id}, + call_end_time: IsNull() + } + })) != null } -export async function getExistingCallID(db: SQLCommon, channel: VoiceBasedChannel) : Promise { - const queryResult: any[] = await db.getAllParameterized( - "SELECT * FROM calls WHERE channel_snowflake = ? AND call_end_time IS NULL", - [channel.id] - ) - - return queryResult.length != 0 ? queryResult[0].call_id : -1 +export async function getExistingCallID(db: Repository, channel: VoiceBasedChannel) : Promise { + return (await db.findOne({ + where: { + channel: {channel_snowflake: channel.id}, + call_end_time: IsNull() + } + }))?.call_id } diff --git a/src/utilties/events/voice.ts b/src/utilties/events/voice.ts index ec281c6..b16498c 100644 --- a/src/utilties/events/voice.ts +++ b/src/utilties/events/voice.ts @@ -1,7 +1,8 @@ import { Client, Events, VoiceState } from "discord.js" -import { SQLCommon } from "../storage/interfaces"; +import { Repository } from "typeorm" +import { DBCall } from "../storage/entities/DBCall" -export function setupVoice(client: Client, db: SQLCommon) { +export function setupVoice(client: Client, db: Repository) { client.on(Events.VoiceStateUpdate, (oldState: VoiceState, newState: VoiceState) => { if(oldState.channel == null && newState.channel != null) { // TODO Null Type Safety Risk? @@ -14,6 +15,6 @@ export function setupVoice(client: Client, db: SQLCommon) { }) } -async function processCallJoin(db: SQLCommon, voiceState: VoiceState) { +async function processCallJoin(db: Repository, voiceState: VoiceState) { } diff --git a/src/utilties/index.ts b/src/utilties/index.ts index 67a9f5b..30c5995 100644 --- a/src/utilties/index.ts +++ b/src/utilties/index.ts @@ -1,21 +1,15 @@ 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" import * as users from "./discord/users" -import * as breadthread from "./breadbot/breadthread" import * as roles from "./discord/roles" import { events } from "./events" export const utilities = { commands, - sqlite, - tables, guilds, channels, users, events, - breadthread, roles } \ No newline at end of file diff --git a/src/utilties/storage/entities/DBMessage.ts b/src/utilties/storage/entities/DBMessage.ts index c186e21..b35ea69 100644 --- a/src/utilties/storage/entities/DBMessage.ts +++ b/src/utilties/storage/entities/DBMessage.ts @@ -1,8 +1,9 @@ -import { Column, Entity, ManyToOne, OneToMany, PrimaryColumn } from "typeorm"; +import { Column, Entity, ManyToOne, OneToMany, OneToOne, PrimaryColumn } from "typeorm"; import { DBChannel } from "./DBChannel"; import { DBUser } from "./DBUser"; import { DBMessageContentChanges } from "./DBMessageContentChanges"; import { DBMessageAttachments } from "./DBMessageAttachment"; +import { DBMessageRegexMatches } from "./DBMessageRegexMatches"; @Entity() export class DBMessage { @@ -29,4 +30,7 @@ export class DBMessage { @OneToMany(() => DBMessageAttachments, (ma: DBMessageAttachments) => ma.attachment_snowflake, {nullable: true}) attachments: DBMessageAttachments[] | null + + @OneToOne(() => DBMessageRegexMatches, (mrm: DBMessageRegexMatches) => mrm.message, {nullable: true}) + violation_regex: DBMessageRegexMatches | null } \ No newline at end of file diff --git a/src/utilties/storage/entities/DBMessageRegex.ts b/src/utilties/storage/entities/DBMessageRegex.ts new file mode 100644 index 0000000..4971c7b --- /dev/null +++ b/src/utilties/storage/entities/DBMessageRegex.ts @@ -0,0 +1,18 @@ +import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { DBServer } from "./DBServer"; +import { DBMessageRegexMatches } from "./DBMessageRegexMatches"; + +@Entity() +export class DBMessageRegex { + @PrimaryGeneratedColumn() + message_regex_id: number + + @ManyToOne(() => DBServer, (server: DBServer) => server.regexes) + server: DBServer + + @Column() + regex: string + + @OneToMany(() => DBMessageRegexMatches, (mrm: DBMessageRegexMatches) => mrm.regex, {nullable: true}) + matches: DBMessageRegexMatches[] | null +} \ No newline at end of file diff --git a/src/utilties/storage/entities/DBMessageRegexMatches.ts b/src/utilties/storage/entities/DBMessageRegexMatches.ts new file mode 100644 index 0000000..42f6ebf --- /dev/null +++ b/src/utilties/storage/entities/DBMessageRegexMatches.ts @@ -0,0 +1,15 @@ +import { Entity, ManyToOne, OneToOne, PrimaryGeneratedColumn } from "typeorm"; +import { DBMessage } from "./DBMessage"; +import { DBMessageRegex } from "./DBMessageRegex"; + +@Entity() +export class DBMessageRegexMatches { + @PrimaryGeneratedColumn() + message_regex_match_id: number + + @OneToOne(() => DBMessage, (message: DBMessage) => message.violation_regex) + message: DBMessage + + @ManyToOne(() => DBMessageRegex, (regex: DBMessageRegex) => regex.matches) + regex: DBMessageRegex +} \ No newline at end of file diff --git a/src/utilties/storage/entities/DBServer.ts b/src/utilties/storage/entities/DBServer.ts index 2dbc475..b32c70c 100644 --- a/src/utilties/storage/entities/DBServer.ts +++ b/src/utilties/storage/entities/DBServer.ts @@ -1,6 +1,7 @@ import { Column, Entity, OneToMany, PrimaryColumn } from "typeorm"; import { DBChannel } from "./DBChannel"; import { DBRole } from "./DBRole"; +import { DBMessageRegex } from "./DBMessageRegex"; @Entity() export class DBServer { @@ -18,4 +19,7 @@ export class DBServer { @OneToMany(() => DBRole, (role: DBRole) => role.server) roles: DBRole[] + + @OneToMany(() => DBMessageRegex, (regex: DBMessageRegex) => regex.server, {nullable: true}) + regexes: DBMessageRegex[] | null } \ No newline at end of file diff --git a/src/utilties/storage/enumerations.ts b/src/utilties/storage/enumerations.ts deleted file mode 100644 index ef7583c..0000000 --- a/src/utilties/storage/enumerations.ts +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index e5ee6ba..0000000 --- a/src/utilties/storage/interfaces.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface SQLCommon { - run(query: string) : Promise - runParameterized(query: string, parameters: any[]): Promise - getAll(query: string) : Promise - getAllParameterized(query: string, parameters: any[]) : Promise -} - diff --git a/src/utilties/storage/sqlite.ts b/src/utilties/storage/sqlite.ts deleted file mode 100644 index fec7541..0000000 --- a/src/utilties/storage/sqlite.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as sqlite3 from 'sqlite3' - -import { SQLCommon } from "./interfaces" - -export class SqliteDB implements SQLCommon { - private db : sqlite3.Database; - - public constructor(private readonly dbName: string) { - this.db = new sqlite3.Database(this.dbName); - } - - async run(query: string): Promise { - return new Promise((resolve, reject) => { - this.db.run(query, function (this : sqlite3.RunResult, err: Error) { - if (err) { - // TODO Winston should handle this - console.log(err) - reject(err) - } else { - resolve(this.changes) - } - }) - }) - } - - async runParameterized(query: string, parameters: any[]): Promise { - return new Promise((resolve, reject) => { - this.db.run(query, parameters, function (this : sqlite3.RunResult, err: Error) { - if (err) { - // TODO Winston should handle this - console.log(err) - reject(err) - } else { - resolve(this.changes) - } - }) - }) - } - - async getAll(query: string): Promise { - return new Promise((resolve, reject) => { - this.db.all(query, (err: Error, rows: Object[]) => { - if (err) { - // TODO Winston should handle this - console.log(err) - reject(err) - } else { - resolve(rows) - } - }) - }) - } - - getAllParameterized(query: string, parameters: any[]): 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) - reject(err) - } else { - resolve(rows) - } - }) - }) - } - -} \ No newline at end of file diff --git a/src/utilties/storage/tables.ts b/src/utilties/storage/tables.ts deleted file mode 100644 index 97426af..0000000 --- a/src/utilties/storage/tables.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { SQLCommon } from "./interfaces"; - -const tables: string[] = [ - "CREATE TABLE IF NOT EXISTS message_scan_regex_matches (message_scan_regex_matches_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,message_snowflake bigint NOT NULL,message_regexes_id bigint NOT NULL);", - "CREATE TABLE IF NOT EXISTS message_regex_no_role_check (message_regex_no_role_check_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,server_snowflake bigint NOT NULL,role_snowflake bigint NOT NULL);", - "CREATE TABLE IF NOT EXISTS message_regexes (message_regexes_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,server_snowflake bigint NOT NULL,regex text NOT NULL,priority int NOT NULL,severity int NOT NULL);", - "CREATE TABLE IF NOT EXISTS message_regex_words (message_regex_words_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,message_regexes_id bigint,word text NOT NULL);", - "CREATE TABLE IF NOT EXISTS calls (call_id bigint NOT NULL PRIMARY KEY AUTOINCREMENT, channel_snowflake bigint NOT NULL, call_start_time datetime NOT NULL, call_end_time datetime DEFAULT NULL, call_consolidated INTEGER DEFAULT 0 CHECK(call_consolidated IN (0, 1)), call_transcribed INTEGER DEFAULT 0 CHECK(call_transcribed IN (0, 1)), call_data_cleaned_up INTEGER DEFAULT 0 CHECK(call_data_cleaned_up IN (0, 1)));", - "CREATE TABLE IF NOT EXISTS call_transcriptions (transcription_id bitint NOT NULL PRIMARY KEY AUTOINCREMENT, call_id bigint NOT NULL, user_snowflake bigint NOT NULL, speaking_start_time datetime NOT NULL, text TEXT NOT NULL);", - "CREATE TABLE IF NOT EXISTS call_users (call_users_id bigint NOT NULL PRIMARY KEY AUTOINCREMENT, call_id bigint NOT NULL, user_snowflake bigint NOT NULL, call_join_time datetime NOT NULL, call_leave_time datetime DEFAULT NULL);" -] - -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))) -} -