Development (#12)
Co-authored-by: PineaFan <ash@pinea.dev>
Co-authored-by: pineafan <pineapplefanyt@gmail.com>
Co-authored-by: PineappleFan <PineaFan@users.noreply.github.com>
Co-authored-by: Skyler <skyler3665@gmail.com>
diff --git a/src/api/index.ts b/src/api/index.ts
index 9676194..c8b7b14 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -152,7 +152,7 @@
app.get("/transcript/:code/human", jsonParser, async function (req: express.Request, res: express.Response) {
const code = req.params.code;
if (code === undefined) return res.status(400).send("No code provided");
- const entry = await client.database.transcripts.read(code);
+ const entry = await client.database.transcripts.read(code, req.query.key as string, req.query.iv as string);
if (entry === null) return res.status(404).send("Could not find a transcript by that code");
// Convert to a human readable format
const data = client.database.transcripts.toHumanReadable(entry);
@@ -164,7 +164,7 @@
app.get("/transcript/:code", jsonParser, async function (req: express.Request, res: express.Response) {
const code = req.params.code;
if (code === undefined) return res.status(400).send("No code provided");
- const entry = await client.database.transcripts.read(code);
+ const entry = await client.database.transcripts.read(code, req.query.key as string, req.query.iv as string);
if (entry === null) return res.status(404).send("Could not find a transcript by that code");
// Convert to a human readable format
return res.status(200).send(entry);
diff --git a/src/commands/mod/purge.ts b/src/commands/mod/purge.ts
index 8644e26..004f5ff 100644
--- a/src/commands/mod/purge.ts
+++ b/src/commands/mod/purge.ts
@@ -318,7 +318,8 @@
)).map(message => message as Message);
const newOut = await client.database.transcripts.createTranscript(messageArray, interaction, interaction.member as GuildMember);
- const code = await client.database.transcripts.create(newOut);
+ const [code, key, iv] = await client.database.transcripts.create(newOut);
+
await interaction.editReply({
embeds: [
new EmojiEmbed()
@@ -329,7 +330,9 @@
],
components: [
new Discord.ActionRowBuilder<ButtonBuilder>().addComponents([
- new ButtonBuilder().setLabel("View").setStyle(ButtonStyle.Link).setURL(`https://clicks.codes/nucleus/transcript?code=${code}`),
+
+ new ButtonBuilder().setLabel("View").setStyle(ButtonStyle.Link).setURL(`https://clicks.codes/nucleus/transcript/${code}?key=${key}&iv=${iv}`).setDisabled(!code),
+
])
]
});
diff --git a/src/commands/privacy.ts b/src/commands/privacy.ts
index 46784f5..dcdebb1 100644
--- a/src/commands/privacy.ts
+++ b/src/commands/privacy.ts
@@ -179,9 +179,11 @@
continue;
}
if (confirmation.success) {
- client.database.guilds.delete(interaction.guild!.id);
- client.database.history.delete(interaction.guild!.id);
- client.database.notes.delete(interaction.guild!.id);
+ await client.database.guilds.delete(interaction.guild!.id);
+ await client.database.history.delete(interaction.guild!.id);
+ await client.database.notes.delete(interaction.guild!.id);
+ await client.database.transcripts.deleteAll(interaction.guild!.id);
+
nextFooter = "All data cleared";
continue;
} else {
diff --git a/src/context/messages/purgeto.ts b/src/context/messages/purgeto.ts
index aef159b..616b085 100644
--- a/src/context/messages/purgeto.ts
+++ b/src/context/messages/purgeto.ts
@@ -193,7 +193,9 @@
)
)).map(message => message as Message);
const transcript = await client.database.transcripts.createTranscript(messageArray, interaction, interaction.member as GuildMember);
- const code = await client.database.transcripts.create(transcript);
+
+ const [code, key, iv] = await client.database.transcripts.create(transcript);
+
await interaction.editReply({
embeds: [
new EmojiEmbed()
@@ -204,7 +206,9 @@
],
components: [
new Discord.ActionRowBuilder<ButtonBuilder>().addComponents([
- new ButtonBuilder().setLabel("View").setStyle(ButtonStyle.Link).setURL(`https://clicks.codes/nucleus/transcript?code=${code}`),
+
+ new ButtonBuilder().setLabel("View").setStyle(ButtonStyle.Link).setURL(`https://clicks.codes/nucleus/transcript/${code}?key=${key}&iv=${iv}`).setDisabled(!code),
+
])
]
});
diff --git a/src/events/guildDelete.ts b/src/events/guildDelete.ts
new file mode 100644
index 0000000..92af401
--- /dev/null
+++ b/src/events/guildDelete.ts
@@ -0,0 +1,10 @@
+import client, { NucleusClient } from '../utils/client.js'
+import type { Guild } from 'discord.js'
+
+export const event = 'guildDelete'
+export const callback = async (_client: NucleusClient, guild: Guild) => {
+ await client.database.guilds.delete(guild.id);
+ await client.database.history.delete(guild.id);
+ await client.database.notes.delete(guild.id);
+ await client.database.transcripts.deleteAll(guild.id);
+}
\ No newline at end of file
diff --git a/src/premium/createTranscript.ts b/src/premium/createTranscript.ts
index 67aed04..dd01a98 100644
--- a/src/premium/createTranscript.ts
+++ b/src/premium/createTranscript.ts
@@ -60,7 +60,9 @@
const newOut = await client.database.transcripts.createTranscript(messages, interaction, member);
- const code = await client.database.transcripts.create(newOut);
+
+ const [code, key, iv] = await client.database.transcripts.create(newOut);
+
if(!code) return await interaction.reply({
embeds: [
new EmojiEmbed()
@@ -86,7 +88,9 @@
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
- new ButtonBuilder().setLabel("View").setStyle(ButtonStyle.Link).setURL(`https://clicks.codes/nucleus/transcript/${code}`),
+
+ new ButtonBuilder().setLabel("View").setStyle(ButtonStyle.Link).setURL(`https://testing.coded.codes/nucleus/transcript/${code}?key=${key}&iv=${iv}`),
+
new ButtonBuilder()
.setLabel("Delete")
.setStyle(ButtonStyle.Danger)
diff --git a/src/utils/database.ts b/src/utils/database.ts
index 2e64320..06c41f0 100644
--- a/src/utils/database.ts
+++ b/src/utils/database.ts
@@ -22,6 +22,9 @@
const collectionOptions = { authdb: "admin" };
+const getIV = () => crypto.randomBytes(16);
+
+
export class Guilds {
guilds: Collection<GuildConfig>;
defaultData: GuildConfig;
@@ -203,15 +206,39 @@
do {
code = crypto.randomBytes(64).toString("base64").replace(/=/g, "").replace(/\//g, "_").replace(/\+/g, "-");
} while (await this.transcripts.findOne({ code: code }));
+ const key = crypto.randomBytes(32**2).toString("base64").replace(/=/g, "").replace(/\//g, "_").replace(/\+/g, "-").substring(0, 32);
+ const iv = getIV().toString("base64").replace(/=/g, "").replace(/\//g, "_").replace(/\+/g, "-");
+ for(const message of transcript.messages) {
+ if(message.content) {
+ const encCipher = crypto.createCipheriv("AES-256-CBC", key, iv);
+ message.content = encCipher.update(message.content, "utf8", "base64") + encCipher.final("base64");
+ }
+ }
const doc = await this.transcripts.insertOne(Object.assign(transcript, { code: code }), collectionOptions);
- if(doc.acknowledged) return code;
- else return null;
+ if(doc.acknowledged) return [code, key, iv];
+ else return [null, null, null];
}
- async read(code: string) {
+ async read(code: string, key: string, iv: string) {
// console.log("Transcript read")
- return await this.transcripts.findOne({ code: code });
+ const doc = await this.transcripts.findOne({ code: code });
+ if(!doc) return null;
+ for(const message of doc.messages) {
+ if(message.content) {
+ const decCipher = crypto.createDecipheriv("AES-256-CBC", key, iv);
+ message.content = decCipher.update(message.content, "base64", "utf8") + decCipher.final("utf8");
+ }
+ }
+ return doc;
+ }
+
+ async deleteAll(guild: string) {
+ // console.log("Transcript delete")
+ const filteredDocs = await this.transcripts.find({ guild: guild }).toArray();
+ for (const doc of filteredDocs) {
+ await this.transcripts.deleteOne({ code: doc.code });
+ }
}
async createTranscript(messages: Message[], interaction: MessageComponentInteraction | CommandInteraction, member: GuildMember) {