made premium check faster. Added transcript endpoint, toHumanReadable function.
diff --git a/src/utils/client.ts b/src/utils/client.ts
index 00f8c05..7e84716 100644
--- a/src/utils/client.ts
+++ b/src/utils/client.ts
@@ -2,7 +2,7 @@
import { Logger } from "../utils/log.js";
import Memory from "../utils/memory.js";
import type { VerifySchema } from "../reflex/verify.js";
-import { Guilds, History, ModNotes, Premium, PerformanceTest, ScanCache, Transcript } from "../utils/database.js";
+import { Guilds, History, ModNotes, Premium, PerformanceTest, ScanCache, Transcript, } from "../utils/database.js";
import EventScheduler from "../utils/eventScheduler.js";
import type { RoleMenuSchema } from "../actions/roleMenu.js";
import config from "../config/main.js";
diff --git a/src/utils/commandRegistration/register.ts b/src/utils/commandRegistration/register.ts
index 78e3b0f..d88a13a 100644
--- a/src/utils/commandRegistration/register.ts
+++ b/src/utils/commandRegistration/register.ts
@@ -225,6 +225,6 @@
console.log(`${colors.green}Registered commands, events and context menus${colors.none}`)
console.log(
(config.enableDevelopment ? `${colors.purple}Bot started in Development mode` :
- `${colors.blue}Bot started in Production mode`) + colors.none)
- // console.log(client.commands)
+ `${colors.blue}Bot started in Production mode`) + colors.none
+ )
};
diff --git a/src/utils/database.ts b/src/utils/database.ts
index 7e80f96..b7971c3 100644
--- a/src/utils/database.ts
+++ b/src/utils/database.ts
@@ -1,4 +1,4 @@
-import type { ButtonStyle, GuildMember } from "discord.js";
+import { ButtonStyle, CommandInteraction, ComponentType, GuildMember, Message, MessageComponentInteraction } from "discord.js";
import type Discord from "discord.js";
import { Collection, MongoClient } from "mongodb";
import config from "../config/main.js";
@@ -154,7 +154,7 @@
flags?: string[];
attachments?: TranscriptAttachment[];
stickerURLs?: string[];
- referencedMessage?: string | [string, string, string];
+ referencedMessage?: string | [string, string, string]; // the message id, the channel id, the guild id
}
interface TranscriptSchema {
@@ -189,6 +189,134 @@
async read(code: string) {
return await this.transcripts.findOne({ code: code });
}
+
+ async createTranscript(messages: Message[], interaction: MessageComponentInteraction | CommandInteraction, member: GuildMember) {
+ const interactionMember = await interaction.guild?.members.fetch(interaction.user.id)
+ const newOut: Omit<TranscriptSchema, "code"> = {
+ type: "ticket",
+ for: {
+ username: member!.user.username,
+ discriminator: parseInt(member!.user.discriminator),
+ id: member!.user.id,
+ topRole: {
+ color: member!.roles.highest.color
+ }
+ },
+ guild: interaction.guild!.id,
+ channel: interaction.channel!.id,
+ messages: [],
+ createdTimestamp: Date.now(),
+ createdBy: {
+ username: interaction.user.username,
+ discriminator: parseInt(interaction.user.discriminator),
+ id: interaction.user.id,
+ topRole: {
+ color: interactionMember?.roles.highest.color ?? 0x000000
+ }
+ }
+ }
+ if(interactionMember?.roles.icon) newOut.createdBy.topRole.badgeURL = interactionMember.roles.icon.iconURL()!;
+ messages.reverse().forEach((message) => {
+ const msg: TranscriptMessage = {
+ id: message.id,
+ author: {
+ username: message.author.username,
+ discriminator: parseInt(message.author.discriminator),
+ id: message.author.id,
+ topRole: {
+ color: message.member!.roles.highest.color
+ }
+ },
+ createdTimestamp: message.createdTimestamp
+ };
+ if (message.member!.roles.icon) msg.author.topRole.badgeURL = message.member!.roles.icon.iconURL()!;
+ if (message.content) msg.content = message.content;
+ if (message.embeds.length > 0) msg.embeds = message.embeds.map(embed => {
+ const obj: TranscriptEmbed = {};
+ if (embed.title) obj.title = embed.title;
+ if (embed.description) obj.description = embed.description;
+ if (embed.fields.length > 0) obj.fields = embed.fields.map(field => {
+ return {
+ name: field.name,
+ value: field.value,
+ inline: field.inline ?? false
+ }
+ });
+ if (embed.footer) obj.footer = {
+ text: embed.footer.text,
+ };
+ if (embed.footer?.iconURL) obj.footer!.iconURL = embed.footer.iconURL;
+ return obj;
+ });
+ if (message.components.length > 0) msg.components = message.components.map(component => component.components.map(child => {
+ const obj: TranscriptComponent = {
+ type: child.type
+ }
+ if (child.type === ComponentType.Button) {
+ obj.style = child.style;
+ obj.label = child.label ?? "";
+ } else if (child.type > 2) {
+ obj.placeholder = child.placeholder ?? "";
+ }
+ return obj
+ }));
+ if (message.editedTimestamp) msg.editedTimestamp = message.editedTimestamp;
+ msg.flags = message.flags.toArray();
+
+ if (message.stickers.size > 0) msg.stickerURLs = message.stickers.map(sticker => sticker.url);
+ if (message.reference) msg.referencedMessage = [message.reference.guildId ?? "", message.reference.channelId, message.reference.messageId ?? ""];
+ newOut.messages.push(msg);
+ });
+ return newOut;
+ }
+
+ toHumanReadable(transcript: Omit<TranscriptSchema, "code">): string {
+ let out = "";
+ for (const message of transcript.messages) {
+ if (message.referencedMessage) {
+ if (Array.isArray(message.referencedMessage)) {
+ out += `> [Crosspost From] ${message.referencedMessage[0]} in ${message.referencedMessage[1]} in ${message.referencedMessage[2]}\n`;
+ }
+ else out += `> [Reply To] ${message.referencedMessage}\n`;
+ }
+ out += `${message.author.nickname ?? message.author.username}#${message.author.discriminator} (${message.author.id}) (${message.id}) `;
+ out += `[${new Date(message.createdTimestamp).toISOString()}] `;
+ if (message.editedTimestamp) out += `[Edited: ${new Date(message.editedTimestamp).toISOString()}] `;
+ out += "\n";
+ if (message.content) out += `[Content]\n${message.content}\n\n`;
+ if (message.embeds) {
+ for (const embed of message.embeds) {
+ out += `[Embed]\n`;
+ if (embed.title) out += `| Title: ${embed.title}\n`;
+ if (embed.description) out += `| Description: ${embed.description}\n`;
+ if (embed.fields) {
+ for (const field of embed.fields) {
+ out += `| Field: ${field.name} - ${field.value}\n`;
+ }
+ }
+ if (embed.footer) {
+ out += `|Footer: ${embed.footer.text}\n`;
+ }
+ out += "\n";
+ }
+ }
+ if (message.components) {
+ for (const component of message.components) {
+ out += `[Component]\n`;
+ for (const button of component) {
+ out += `| Button: ${button.label ?? button.description}\n`;
+ }
+ out += "\n";
+ }
+ }
+ if (message.attachments) {
+ for (const attachment of message.attachments) {
+ out += `[Attachment] ${attachment.filename} (${attachment.size} bytes) ${attachment.url}\n`;
+ }
+ }
+ }
+ return out
+ }
}
export class History {
@@ -317,9 +445,12 @@
export class Premium {
premium: Collection<PremiumSchema>;
+ cache: Map<string, [boolean, string, number, boolean, Date]>; // Date indicates the time one hour after it was created
+ cacheTimeout = 1000 * 60 * 60; // 1 hour
constructor() {
this.premium = database.collection<PremiumSchema>("premium");
+ this.cache = new Map<string, [boolean, string, number, boolean, Date]>();
}
async updateUser(user: string, level: number) {
@@ -337,8 +468,11 @@
}
async hasPremium(guild: string): Promise<[boolean, string, number, boolean] | null> {
+ // [Has premium, user giving premium, level, is mod: if given automatically]
+ const cached = this.cache.get(guild);
+ if (cached && cached[4].getTime() < Date.now()) return [cached[0], cached[1], cached[2], cached[3]];
const entries = await this.premium.find({}).toArray();
- const members = await (await client.guilds.fetch(guild)).members.fetch()
+ const members = (await client.guilds.fetch(guild)).members.cache
for(const {user} of entries) {
const member = members.get(user);
if(member) { //TODO: Notify user if they've given premium to a server that has since gotten premium via a mod.
@@ -355,7 +489,10 @@
member.permissions.has("ManageMessages") ||
member.permissions.has("ManageThreads")
const entry = entries.find(e => e.user === member.id);
- if(entry && (entry.level === 3) && modPerms) return [true, member.id, entry.level, true];
+ if(entry && (entry.level === 3) && modPerms) {
+ this.cache.set(guild, [true, member.id, entry.level, true, new Date(Date.now() + this.cacheTimeout)]);
+ return [true, member.id, entry.level, true];
+ }
}
}
const entry = await this.premium.findOne({
@@ -365,6 +502,7 @@
}
}
});
+ this.cache.set(guild, [entry ? true : false, entry?.user ?? "", entry?.level ?? 0, false, new Date(Date.now() + this.cacheTimeout)]);
return entry ? [true, entry.user, entry.level, false] : null;
}
@@ -427,11 +565,14 @@
}
}
- addPremium(user: string, guild: string) {
+ async addPremium(user: string, guild: string) {
+ const { level } = (await this.fetchUser(user))!;
+ this.cache.set(guild, [true, user, level, false, new Date(Date.now() + this.cacheTimeout)]);
return this.premium.updateOne({ user: user }, { $addToSet: { appliesTo: guild } }, { upsert: true });
}
removePremium(user: string, guild: string) {
+ this.cache.set(guild, [false, "", 0, false, new Date(Date.now() + this.cacheTimeout)]);
return this.premium.updateOne({ user: user }, { $pull: { appliesTo: guild } });
}
}
diff --git a/src/utils/eventScheduler.ts b/src/utils/eventScheduler.ts
index bdd0d21..5c461ad 100644
--- a/src/utils/eventScheduler.ts
+++ b/src/utils/eventScheduler.ts
@@ -14,7 +14,6 @@
collection: "eventScheduler"
}
});
-
this.agenda.define("unmuteRole", async (job) => {
const guild = await client.guilds.fetch(job.attrs.data.guild);
const user = await guild.members.fetch(job.attrs.data.user);
diff --git a/src/utils/logTranscripts.ts b/src/utils/logTranscripts.ts
deleted file mode 100644
index 0950664..0000000
--- a/src/utils/logTranscripts.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import type Discord from 'discord.js';
-
-export interface JSONTranscriptSchema {
- messages: {
- content: string | null;
- attachments: {
- url: string;
- name: string;
- size: number;
- }[];
- authorID: string;
- authorUsername: string;
- authorUsernameColor: string;
- timestamp: string;
- id: string;
- edited: boolean;
- }[];
- channel: string;
- guild: string;
- timestamp: string;
-}
-
-
-export const JSONTranscriptFromMessageArray = (messages: Discord.Message[]): JSONTranscriptSchema | null => {
- if (messages.length === 0) return null;
- return {
- guild: messages[0]!.guild!.id,
- channel: messages[0]!.channel.id,
- timestamp: Date.now().toString(),
- messages: messages.map((message: Discord.Message) => {
- return {
- content: message.content,
- attachments: message.attachments.map((attachment: Discord.Attachment) => {
- return {
- url: attachment.url,
- name: attachment.name!,
- size: attachment.size,
- };
- }),
- authorID: message.author.id,
- authorUsername: message.author.username + "#" + message.author.discriminator,
- authorUsernameColor: message.member!.displayHexColor.toString(),
- timestamp: message.createdTimestamp.toString(),
- id: message.id,
- edited: message.editedTimestamp ? true : false,
- };
- })
- };
-}
-
-export const JSONTranscriptToHumanReadable = (data: JSONTranscriptSchema): string => {
- let out = "";
-
- for (const message of data.messages) {
- const date = new Date(parseInt(message.timestamp));
- out += `${message.authorUsername} (${message.authorID}) [${date}]`;
- if (message.edited) out += " (edited)";
- if (message.content) out += "\nContent:\n" + message.content.split("\n").map((line: string) => `\n> ${line}`).join("");
- if (message.attachments.length > 0) out += "\nAttachments:\n" + message.attachments.map((attachment: { url: string; name: string; size: number; }) => `\n> [${attachment.name}](${attachment.url}) (${attachment.size} bytes)`).join("\n");
-
- out += "\n\n";
- }
- return out;
-}
\ No newline at end of file