From 003160ffab46546810d8fa958c999db9e4818866 Mon Sep 17 00:00:00 2001 From: TheCodedProf Date: Sat, 4 Mar 2023 17:09:40 -0500 Subject: [PATCH] nice commit --- src/commands/settings/autopublish.ts | 20 ++++-- src/config/emojis.json | 1 + src/premium/createTranscript.ts | 2 +- src/utils/database.ts | 92 +++++++++++++++++++++++++--- src/utils/eventScheduler.ts | 14 +++++ 5 files changed, 112 insertions(+), 17 deletions(-) diff --git a/src/commands/settings/autopublish.ts b/src/commands/settings/autopublish.ts index 1dc97e0..a21657f 100644 --- a/src/commands/settings/autopublish.ts +++ b/src/commands/settings/autopublish.ts @@ -1,9 +1,10 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelSelectMenuBuilder, CommandInteraction, SlashCommandSubcommandBuilder } from "discord.js"; +import { ActionRowBuilder, APIMessageComponentEmoji, ButtonBuilder, ButtonStyle, ChannelSelectMenuBuilder, ChannelType, CommandInteraction, SlashCommandSubcommandBuilder } from "discord.js"; import type Discord from "discord.js"; import client from "../../utils/client.js"; import { LoadingEmbed } from "../../utils/defaults.js"; import compare from "lodash" import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; +import getEmojiByName from "../../utils/getEmojiByName.js"; export const command = new SlashCommandSubcommandBuilder() .setName("autopublish") @@ -24,14 +25,14 @@ export const callback = async (interaction: CommandInteraction): Promise = .addComponents( new ButtonBuilder() .setCustomId("switch") - .setLabel(data.enabled ? "Disabled" : "Enabled") - .setStyle(data.enabled ? ButtonStyle.Danger : ButtonStyle.Success) - .setEmoji(data.enabled ? "✅" : "❌"), + .setLabel(data.enabled ? "Enabled" : "Disabled") + .setStyle(data.enabled ? ButtonStyle.Success : ButtonStyle.Danger) + .setEmoji(getEmojiByName("CONTROL." + (data.enabled ? "TICK" : "CROSS"), "id") as APIMessageComponentEmoji), new ButtonBuilder() .setCustomId("save") .setLabel("Save") .setStyle(ButtonStyle.Success) - .setEmoji("💾") + .setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji) .setDisabled(compare.isEqual(data, config.autoPublish)) ); @@ -41,11 +42,18 @@ export const callback = async (interaction: CommandInteraction): Promise = .setCustomId("channel") .setPlaceholder("Select a channel") .setMinValues(1) + .setChannelTypes(ChannelType.GuildAnnouncement, ChannelType.AnnouncementThread) ); + const current = data.channels.map((c) => `> <#${c}>`).join("\n") || "*None set*"; + const embed = new EmojiEmbed() + .setTitle("Auto Publish") + .setDescription("Currently enabled in:\n" + current) + .setStatus('Success') + .setEmoji("ICONS.PUBLISH") - await interaction.editReply({ + await interaction.editReply({ embeds: [embed], components: [channelSelect, buttons] }); diff --git a/src/config/emojis.json b/src/config/emojis.json index ecf1858..d1398cb 100644 --- a/src/config/emojis.json +++ b/src/config/emojis.json @@ -26,6 +26,7 @@ "LOGGING": "999613304446144562", "SAVE": "1065722246322200586", "REORDER": "1069323453909454890", + "PUBLISH": "1081691380004421743", "NOTIFY": { "ON": "1000726394579464232", "OFF": "1078058136092541008" diff --git a/src/premium/createTranscript.ts b/src/premium/createTranscript.ts index fa5ec84..cdbc7b9 100644 --- a/src/premium/createTranscript.ts +++ b/src/premium/createTranscript.ts @@ -79,7 +79,7 @@ export default async function (interaction: CommandInteraction | MessageComponen "You can view the transcript using the link below. You can save the link for later" + (guildConfig.logging.logs.channel ? ` or find it in <#${guildConfig.logging.logs.channel}> once you press delete below. After this the channel will be deleted.` - : ".") + : ". It will be automatically deleted after 30 days if a logging channel is not set.") ) .setStatus("Success") .setEmoji("CONTROL.DOWNLOAD") diff --git a/src/utils/database.ts b/src/utils/database.ts index d000340..c1728c3 100644 --- a/src/utils/database.ts +++ b/src/utils/database.ts @@ -186,11 +186,20 @@ interface TranscriptSchema { createdBy: TranscriptAuthor; } +interface findDocSchema { channelID:string, messageID: string; transcript: string } + export class Transcript { transcripts: Collection; + messageToTranscript: Collection; constructor() { this.transcripts = database.collection("transcripts"); + this.messageToTranscript = database.collection("messageToTranscript"); + } + + async upload(data: findDocSchema) { + // console.log("Transcript upload") + await this.messageToTranscript.insertOne(data); } async create(transcript: Omit) { @@ -209,13 +218,84 @@ export class Transcript { } const doc = await this.transcripts.insertOne(Object.assign(transcript, { code: code }), collectionOptions); - if(doc.acknowledged) return [code, key, iv]; + if(doc.acknowledged) { + client.database.eventScheduler.schedule("deleteTranscript", (Date.now() + 1000 * 60 * 60 * 24 * 7).toString(), { guild: transcript.guild, code: code, iv: iv, key: key }); + return [code, key, iv]; + } else return [null, null, null]; } + async delete(code: string) { + // console.log("Transcript delete") + await this.transcripts.deleteOne({ code: code }); + } + + 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 readEncrypted(code: string) { + // console.log("Transcript read") + let doc: TranscriptSchema | null = await this.transcripts.findOne({ code: code }); + let findDoc: findDocSchema | null = null; + if(!doc) findDoc = (await this.messageToTranscript.findOne({ transcript: code })); + if(findDoc) { + const message = await ((client.channels.cache.get(findDoc.channelID)) as Discord.TextBasedChannel | null)?.messages.fetch(findDoc.messageID); + if(!message) return null; + const attachment = message.attachments.first(); + if(!attachment) return null; + const transcript = (await fetch(attachment.url)).body; + if(!transcript) return null; + const reader = transcript.getReader(); + let data: Uint8Array | null = null; + let allPacketsReceived = false; + while (!allPacketsReceived) { + const { value, done } = await reader.read(); + if (done) {allPacketsReceived = true; continue;} + if(!data) { + data = value; + } else { + data = new Uint8Array(Buffer.concat([data, value])); + } + } + if(!data) return null; + doc = JSON.parse(Buffer.from(data).toString()); + } + if(!doc) return null; + return doc; + } + async read(code: string, key: string, iv: string) { // console.log("Transcript read") - const doc = await this.transcripts.findOne({ code: code }); + let doc: TranscriptSchema | null = await this.transcripts.findOne({ code: code }); + let findDoc: findDocSchema | null = null; + if(!doc) findDoc = (await this.messageToTranscript.findOne({ transcript: code })); + if(findDoc) { + const message = await ((client.channels.cache.get(findDoc.channelID)) as Discord.TextBasedChannel | null)?.messages.fetch(findDoc.messageID); + if(!message) return null; + const attachment = message.attachments.first(); + if(!attachment) return null; + const transcript = (await fetch(attachment.url)).body; + if(!transcript) return null; + const reader = transcript.getReader(); + let data: Uint8Array | null = null; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, no-constant-condition + while(true) { + const { value, done } = await reader.read(); + if (done) break; + if(!data) { + data = value; + } else { + data = new Uint8Array(Buffer.concat([data, value])); + } + } + if(!data) return null; + doc = JSON.parse(Buffer.from(data).toString()); + } if(!doc) return null; for(const message of doc.messages) { if(message.content) { @@ -226,14 +306,6 @@ export class Transcript { 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) { const interactionMember = await interaction.guild?.members.fetch(interaction.user.id) const newOut: Omit = { diff --git a/src/utils/eventScheduler.ts b/src/utils/eventScheduler.ts index a79a260..f3b7a00 100644 --- a/src/utils/eventScheduler.ts +++ b/src/utils/eventScheduler.ts @@ -3,6 +3,7 @@ import client from "./client.js"; import * as fs from "fs"; import * as path from "path"; import config from "../config/main.js"; +import { TextChannel } from "discord.js"; class EventScheduler { private agenda: Agenda; @@ -21,6 +22,19 @@ class EventScheduler { if (role) await user.roles.remove(role); await job.remove(); }); + this.agenda.define("uploadTranscript", async (job) => { + const channelID: string | null = (await client.database.guilds.read(job.attrs.data.guild)).logging.logs.channel; + if (!channelID) return; + const channel = await client.channels.fetch(channelID); + if(!channel || !(channel instanceof TextChannel)) return; + const transcript = await client.database.transcripts.read(job.attrs.data.transcript, job.attrs.data.key, job.attrs.data.iv); + await client.database.transcripts.delete(job.attrs.data.transcript); + const file = Buffer.from(JSON.stringify(transcript), "base64"); + const fileName = `${job.attrs.data.transcript}.json`; + const m = await channel.send({ files: [{ attachment: file, name: fileName }] }); + await client.database.transcripts.upload({ channelID: channel.id, messageID: m.id, transcript: job.attrs.data.transcript}) + await job.remove(); + }); this.agenda.define("deleteFile", async (job) => { fs.rm(path.resolve("dist/utils/temp", job.attrs.data.fileName), (e) => { client.emit("error", e as Error); }); await job.remove();