From c570f57463342f5491f9375a502c44ed7382cd8a Mon Sep 17 00:00:00 2001 From: Bradley Bickford Date: Sun, 29 Jun 2025 09:28:03 -0400 Subject: [PATCH] Message content changes and message deletions are now tracked --- breadbot_test.db | Bin 53248 -> 53248 bytes src/breadbot.ts | 5 ++-- src/utilties/discord/messages.ts | 50 +++++++++++++++++++++++++++++-- src/utilties/events/messages.ts | 20 +++++++++++-- src/utilties/storage/sqlite.ts | 16 +++------- src/utilties/storage/tables.ts | 6 ++-- 6 files changed, 75 insertions(+), 22 deletions(-) diff --git a/breadbot_test.db b/breadbot_test.db index 5dbfce211089df3cfa41d165b72a9361d03dd293..2c712a519c3a63bb6fedf1997f17ce7dce9687c6 100644 GIT binary patch delta 1216 zcmZ8fZ)h8J82;T|?s9)}_a+TBTa&IAnvJ$;OS@@N+gzG#StzSn9hq1=Nze4x*xFv& zq+M4Pvt>~bby|MHoFJ5aun2x|4T2!*=ps6BA4HH*aKh*i8HmU}sQcnw{*2jAcMtFL zyzlcq@9*q7b9SA1&r7LmzQg-f)SJ63!{5ju3?b!f_)c~yE7D)mx_DDs6#fu5gdySD zm9;N;k`Ol4a|43i)7nZE!%O-Lb2$B?ZWwqj-}s8;p8O#tl6RF}n{@6GI{~?c;z^hM zD$8@8h?m@eJ%v2ZoyNI*s=xjtjB%VN7^KFlE%S?WGfOy?$BWC@NaJ15e)vQzl8mXz zNOUx&?kaW|`aJG7m7PT&YxfKeQs<9Xd(Pv0UeDlEI%_Opa$WLM9Jwn!ZXZb`WAWGt zvL-YuYUE_{_(;O+|5PlIB$}hZ6w?f0#DkQrJRtT{tc~-GA2K&+BX3Fm@>fcn5*f%K zxDFQ~Q+BE2Q0Qgs9LKe>g8OJpx)61ql3#h_%~TN`WS`vl_tPz8Pv%xtJ)_SU)Wt7v z-Fl*rw)pKV?>>6u_vIpL-pxrC#8qebVMT{EBGM43c*;z2 z%G7=TPE+vhEb*lYA$Gr;mj3k12z|IDo4R>1h>ns$(zvO zqgceTZH>Cse>wYLs%#FTUr;M-Jfr#PfJlDQ0;*3iiF+*%(dog8`hcjf=((wU7f&{{ zF1piBf?6l-<;ZN9XQAJ`*)hOShHdb`RnXvLxCC!Q0CHw3ca?&5yhb8TH4r_ID`O#-1>I zL_T<_f}XS2KxlooG={kv2>&=yLWfujSP@uJ*dC%yS6`!9`Q-i<8mhj=dQzdwpQ--= DALv*r delta 2514 zcmZ9OYiJx*6vt;Lo6WvvCT*?JHkwXmp z{^#61da~!}$(|RMW(Hpzy!D1btt#F$tUtAKVW(z(Vtr|RZ9Z$1jZ>vF<&TS6saDuo z_-1B7|5y2FUEzbC<8~ps!dRUtMkD3LGkc7|3?7e|W2Mq@qpxpZ?b^%}qtV}`n=*MU zSQ9;NY=}N8@5$}R=LYE7=rNN-g|emZ8;vsMfo$Q}mA%pT#U+_s^n3Bq-dt{A^U~-{ z@v7+gQh&DZ*v&oBuZ6)(HZqH^;q!E{I`gr)HIvs1Lz;E2FodWU7240P)bqJq?zMs5 z{Gu_wX!~*PF5{7>p5Av{TituhJD1-*tMzU71#1xA3}k~izUvDT@Q~033oFF-y@1&v z3#A~`;Y}Xc+^ZAj@~}$ABo7D+NkE&Nc_d_wrY|aZ$#{)<4iP+P%Ye`dX--hVLP3ek z$C>b}WV6c;O4^9`U`fMwSrBqY@L46d3W{AeVRe}5QP{Fdc1qW!gi1R^gbv4MkJ+R_ zgMbq5g-kezKZyZHNI}PWDBDQJcU^Ad+2J1Xc)$tsc%wq7^6DZ3eCHB|TpGSZTFkAI z9aJKAPFU}`Da_kk`|3wsv0$HUo;s4cBS18u|r9OHPX7asNndnDcu(L=zq&oCHI6#Q-G!_^0-UE zD@^O9uybll#aIO}7aM~%?gWy9GDVZ{A~Hl}+v5}9JdPu=%i(p@I^YOp*8~k>LWFjE zq_Qqq8t9z*;_^U^9>-Ztm)cBVPm4`(#DYrg@WnVM;}Eu|6d0>1E|JoDTll02M+^d0 z5q5zF9RH9pKV(<0_s7Vso+~Yq>O~zv$-QZfInP`)!v;!+WSd2N&)9FG&L$3 zXdqt!jmzWtUWK$>3Y9AXNKB3)?4OV-6ouBc!W<*F8okA!aEK9S3Rnm88I z9FcSZrV4}>H+Bp^-2nZmwg)z#t_jR8uBJ#D6-HRx(H-%}&5&j|FOg1YF0Z5H=y3EP zy0g=L!0LkpdNW-y0WAn7c-V&2y2`IQ#o-O80ZM78)|J$uvBL@hFw)tWRDUkOIkn9X zmULiL$?X8kI$c{yrv+w_taBQ+l}Bir2pI~Ash8*lJ>2qL88%h1gv~pgyXSo}VleqJ z^;K14>^s%4YVj5mnC8eksT2@*RHMY2%&4XmYW{Z=hv4EXBI%L&8Prw$So77)%O86G ztRBtu;pVzQU0f4Cym3^k|Nr8;&leA=+sN||ZB$#Xd#b2unr>avtl#jV{${Qm`PRCv z+>^`9-F|whCs*j4h$Rb+!fuJ zxKFnm`pSjT?&+2K;DYG0sX_gk{^;oRH9E1PFQ$opy&0{Ux?W#yM5XEF`m%C#cxstm zDMc@xsOT$-(S@lM`fwq7eQH==m5+Wpu}WXk7d?Dpi9Vc*rl*I8mg0)f4dnCv&#gas zU~(5jy6l3!f1Qxf$<^6t@6?h^PqY|k5}WY6YT9-7r2FqE-q)02BA_5NZxd9&pSHoI$mDK)ljogCR(R7R4-f&Ci_ z$*WDj9GKLK`NY_04$W${zQl-=uI0K6)~gKjqwQC1njP7bRR)fGfAQJ<*Y>1F{6i)# ZX-aGT2hA*bD?37FvLiZ~$-Q@Z=wDG*M=JmT diff --git a/src/breadbot.ts b/src/breadbot.ts index c24cba1..3d4addc 100644 --- a/src/breadbot.ts +++ b/src/breadbot.ts @@ -21,9 +21,8 @@ if (config.DB_MODE == "sqlite") { db.run("PRAGMA foreign_keys = ON") utilities.tables.makeTables(db) - utilities.tables.makeConstraints(db) - - //I really don't want this to be here. + + //TODO I really don't want this to be here. utilities.events.messages.setupMessageCapture(client, db) } diff --git a/src/utilties/discord/messages.ts b/src/utilties/discord/messages.ts index fe01049..d580845 100644 --- a/src/utilties/discord/messages.ts +++ b/src/utilties/discord/messages.ts @@ -1,8 +1,10 @@ -import { Message, OmitPartialGroupDMChannel } from "discord.js"; +import { Message, OmitPartialGroupDMChannel, PartialMessage } from "discord.js"; import { SQLCommon } from "../storage/interfaces"; import { SQLResult } from "../storage/enumerations"; -export async function doesMessageExist(db: SQLCommon, message: OmitPartialGroupDMChannel>) : Promise { +// TODO Do partial messages affect other functionality elsewhere? + +export async function doesMessageExist(db: SQLCommon, message: OmitPartialGroupDMChannel> | PartialMessage) : Promise { const queryResult: Object[] = await db.getAllParameterized( "SELECT * FROM messages WHERE message_snowflake = ?", [message.id] @@ -31,4 +33,48 @@ export async function insertMessage(db: SQLCommon, message: OmitPartialGroupDMCh console.log(err) return SQLResult.FAILED } +} + +export async function updateMessageContentHistory(db: SQLCommon, message: OmitPartialGroupDMChannel>) : Promise { + const messageIDExists: boolean = await doesMessageExist(db, message) + + if(!messageIDExists) { + return SQLResult.FAILED + } + + try { + await db.runParameterized( + "INSERT INTO message_content_changes (message_snowflake, message_change_old_timestamp, message_change_old_content) " + + "SELECT message_snowflake, message_timestamp, message_content FROM messages WHERE message_snowflake = ?;" + + "UPDATE messages SET message_timestamp = ?, message_content = ? WHERE message_snowflake = ?;", + [message.id, message.editedTimestamp, message.content, message.id] + ) + + return SQLResult.UPDATED + } catch (err) { + //TODO Winston should handle this + console.log(err) + return SQLResult.FAILED + } +} + +export async function markMessageDeleted(db: SQLCommon, message: OmitPartialGroupDMChannel> | PartialMessage) : Promise { + const messageIDExists: boolean = await doesMessageExist(db, message) + + if(!messageIDExists) { + return SQLResult.FAILED + } + + try { + await db.runParameterized( + "UPDATE messages SET message_deleted = 1 WHERE message_snowflake = ?", + [message.id] + ) + + return SQLResult.UPDATED + } catch (err) { + // TODO Winston should handle this + console.log(err) + return SQLResult.FAILED + } } \ No newline at end of file diff --git a/src/utilties/events/messages.ts b/src/utilties/events/messages.ts index d9ed299..fbe5c19 100644 --- a/src/utilties/events/messages.ts +++ b/src/utilties/events/messages.ts @@ -1,14 +1,22 @@ -import { Client, Events, Message, OmitPartialGroupDMChannel } from "discord.js"; +import { Client, Events, Message, messageLink, OmitPartialGroupDMChannel, PartialMessage } from "discord.js"; import { SQLResult } from "../storage/enumerations"; import { SQLCommon } from "../storage/interfaces"; import { insertChannel } from "../discord/channels"; import { insertUser } from "../discord/users"; -import { insertMessage } from "../discord/messages"; +import { doesMessageExist, insertMessage, markMessageDeleted, updateMessageContentHistory } from "../discord/messages"; export function setupMessageCapture(client: Client, db: SQLCommon) { client.on(Events.MessageCreate, async (message) => { processMessageCreate(db, message) }) + + client.on(Events.MessageUpdate, async (oldMessage, newMessage) => { + await processMessageModify(db, newMessage) + }) + + client.on(Events.MessageDelete, async (deletedMessage) => { + await processMessageDeleted(db, deletedMessage) + }) } async function processMessageCreate(db: SQLCommon, message: OmitPartialGroupDMChannel>) { @@ -20,4 +28,12 @@ async function processMessageCreate(db: SQLCommon, message: OmitPartialGroupDMCh await insertMessage(db, message) } +} + +async function processMessageModify(db: SQLCommon, newMessage: OmitPartialGroupDMChannel>) { + await updateMessageContentHistory(db, newMessage) +} + +async function processMessageDeleted(db: SQLCommon, deletedMessage: OmitPartialGroupDMChannel> | PartialMessage) { + await markMessageDeleted(db, deletedMessage) } \ No newline at end of file diff --git a/src/utilties/storage/sqlite.ts b/src/utilties/storage/sqlite.ts index 33cc857..3f7a3e6 100644 --- a/src/utilties/storage/sqlite.ts +++ b/src/utilties/storage/sqlite.ts @@ -11,17 +11,13 @@ export class SqliteDB implements SQLCommon { async run(query: string): Promise { return new Promise((resolve, reject) => { - this.db.run(query, (result : sqlite3.RunResult, err: Error) => { + this.db.run(query, function (this : sqlite3.RunResult, err: Error) { if (err) { // TODO Winston should handle this console.log(err) throw err } else { - if (result != null) { - resolve(result.changes) - } else { - resolve(0) - } + resolve(this.changes) } }) }) @@ -29,17 +25,13 @@ export class SqliteDB implements SQLCommon { async runParameterized(query: string, parameters: any[]): Promise { return new Promise((resolve, reject) => { - this.db.run(query, parameters, (result : sqlite3.RunResult, err: Error) => { + this.db.run(query, parameters, function (this : sqlite3.RunResult, err: Error) { if (err) { // TODO Winston should handle this console.log(err) throw err } else { - if (result != null) { - resolve(result.changes) - } else { - resolve(0) - } + resolve(this.changes) } }) }) diff --git a/src/utilties/storage/tables.ts b/src/utilties/storage/tables.ts index 1d87112..28d53eb 100644 --- a/src/utilties/storage/tables.ts +++ b/src/utilties/storage/tables.ts @@ -2,10 +2,10 @@ 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 channels (channel_snowflake bigint NOT NULL PRIMARY KEY,server_snowflake bigint,channel_name text,is_thread bit NOT NULL,is_dm bit NOT NULL);", - "CREATE TABLE users (user_snowflake bigint NOT NULL PRIMARY KEY,user_name text NOT NULL,user_displayname text);", + "CREATE TABLE IF NOT EXISTS channels (channel_snowflake bigint NOT NULL PRIMARY KEY,server_snowflake bigint,channel_name text,is_thread bit NOT NULL,is_dm bit NOT NULL);", + "CREATE TABLE IF NOT EXISTS users (user_snowflake bigint NOT NULL PRIMARY KEY,user_name text NOT NULL,user_displayname text);", "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_content_changes (message_change_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,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);" ]