blob: 325aaa8376ee5e32381e10506611d6a0db3b96f5 [file] [log] [blame]
pineafan63fc5e22022-08-04 22:04:10 +01001import * as Discord from "discord.js";
pineafan63fc5e22022-08-04 22:04:10 +01002import { toHexArray } from "./calculate.js";
3import { promisify } from "util";
4import generateKeyValueList from "./generateKeyValueList.js";
5import client from "./client.js";
Skyler Grey67691762023-03-06 09:58:19 +00006import { DiscordAPIError } from "discord.js";
TheCodedProf4a7c25d2023-06-07 17:09:45 -04007import { Stream } from "node:stream";
8import EmojiEmbed from "./generateEmojiEmbed.js";
pineafan32767212022-03-14 21:27:39 +00009
10const wait = promisify(setTimeout);
11
TheCodedProf6ec331b2023-02-20 12:13:06 -050012export interface LoggerOptions {
13 meta: {
14 type: string;
15 displayName: string;
16 calculateType: string;
17 color: number;
18 emoji: string;
19 timestamp: number;
TheCodedProf4a7c25d2023-06-07 17:09:45 -040020 files?: (
21 | Discord.BufferResolvable
22 | Stream
23 | Discord.JSONEncodable<Discord.APIAttachment>
24 | Discord.Attachment
25 | Discord.AttachmentBuilder
26 | Discord.AttachmentPayload
27 )[];
28 showDetails?: boolean;
TheCodedProf6ec331b2023-02-20 12:13:06 -050029 };
TheCodedProf7b985d82023-06-08 16:40:41 -040030 list: Record<string | symbol | number, unknown>;
TheCodedProf6ec331b2023-02-20 12:13:06 -050031 hidden: {
32 guild: string;
Skyler Greyda16adf2023-03-05 10:22:12 +000033 };
TheCodedProf6ec331b2023-02-20 12:13:06 -050034 separate?: {
35 start?: string;
36 end?: string;
Skyler Greyda16adf2023-03-05 10:22:12 +000037 };
TheCodedProf6ec331b2023-02-20 12:13:06 -050038}
39
40async function isLogging(guild: string, type: string): Promise<boolean> {
41 const config = await client.database.guilds.read(guild);
42 if (!config.logging.logs.enabled) return false;
43 if (!config.logging.logs.channel) return false;
Skyler Greyda16adf2023-03-05 10:22:12 +000044 if (!toHexArray(config.logging.logs.toLog).includes(type)) {
45 return false;
46 }
TheCodedProf6ec331b2023-02-20 12:13:06 -050047 return true;
48}
PineaFan64486c42022-12-28 09:21:04 +000049
50export const Logger = {
pineafane625d782022-05-09 18:04:32 +010051 renderUser(user: Discord.User | string) {
PineaFanb0d0c242023-02-05 10:59:45 +000052 if (typeof user === "string") user = client.users.cache.get(user)!;
pineafane625d782022-05-09 18:04:32 +010053 return `${user.username} [<@${user.id}>]`;
PineaFan64486c42022-12-28 09:21:04 +000054 },
pineafane625d782022-05-09 18:04:32 +010055 renderTime(t: number) {
Skyler Grey67691762023-03-06 09:58:19 +000056 if (isNaN(t)) return "Unknown";
Skyler Grey75ea9172022-08-06 10:22:23 +010057 t = Math.floor((t /= 1000));
pineafane625d782022-05-09 18:04:32 +010058 return `<t:${t}:D> at <t:${t}:T>`;
PineaFan64486c42022-12-28 09:21:04 +000059 },
pineafane625d782022-05-09 18:04:32 +010060 renderDelta(t: number) {
Skyler Grey67691762023-03-06 09:58:19 +000061 if (isNaN(t)) return "Unknown";
Skyler Grey75ea9172022-08-06 10:22:23 +010062 t = Math.floor((t /= 1000));
pineafane625d782022-05-09 18:04:32 +010063 return `<t:${t}:R> (<t:${t}:D> at <t:${t}:T>)`;
PineaFan64486c42022-12-28 09:21:04 +000064 },
pineafanbd02b4a2022-08-05 22:01:38 +010065 renderNumberDelta(num1: number, num2: number) {
pineafan63fc5e22022-08-04 22:04:10 +010066 const delta = num2 - num1;
67 return `${num1} -> ${num2} (${delta > 0 ? "+" : ""}${delta})`;
PineaFan64486c42022-12-28 09:21:04 +000068 },
Skyler Greyda16adf2023-03-05 10:22:12 +000069 entry(
70 value: string | number | boolean | null | (string | boolean)[],
71 displayValue: string
72 ): { value: string | boolean | null | (string | boolean | number)[]; displayValue: string } {
PineaFan0d06edc2023-01-17 22:10:31 +000073 if (typeof value === "number") value = value.toString();
pineafan63fc5e22022-08-04 22:04:10 +010074 return { value: value, displayValue: displayValue };
PineaFan64486c42022-12-28 09:21:04 +000075 },
TheCodedProf4a6d5712023-01-19 15:54:40 -050076 renderChannel(channel: Discord.GuildChannel | Discord.ThreadChannel | string) {
Skyler Greyda16adf2023-03-05 10:22:12 +000077 if (typeof channel === "string")
78 channel = client.channels.cache.get(channel) as Discord.GuildChannel | Discord.ThreadChannel;
pineafane625d782022-05-09 18:04:32 +010079 return `${channel.name} [<#${channel.id}>]`;
PineaFan64486c42022-12-28 09:21:04 +000080 },
TheCodedProf486bca32023-02-02 16:49:44 -050081 renderRole(role: Discord.Role | string, guild?: Discord.Guild | string) {
Skyler Greyda16adf2023-03-05 10:22:12 +000082 if (typeof role === "string")
83 role = (typeof guild === "string" ? client.guilds.cache.get(guild) : guild)!.roles.cache.get(role)!;
pineafane625d782022-05-09 18:04:32 +010084 return `${role.name} [<@&${role.id}>]`;
PineaFan64486c42022-12-28 09:21:04 +000085 },
pineafane625d782022-05-09 18:04:32 +010086 renderEmoji(emoji: Discord.GuildEmoji) {
Skyler Grey11236ba2022-08-08 21:13:33 +010087 return `<${emoji.animated ? "a" : ""}:${emoji.name}:${emoji.id}> [\`:${emoji.name}:\`]`;
PineaFan64486c42022-12-28 09:21:04 +000088 },
89 NucleusColors: {
Skyler Grey75ea9172022-08-06 10:22:23 +010090 red: 0xf27878,
91 yellow: 0xf2d478,
92 green: 0x68d49e
PineaFan64486c42022-12-28 09:21:04 +000093 },
Skyler Greyda16adf2023-03-05 10:22:12 +000094 async getAuditLog(
95 guild: Discord.Guild,
96 event: Discord.GuildAuditLogsResolvable,
97 delay?: number
98 ): Promise<Discord.GuildAuditLogsEntry[]> {
Skyler Grey1b669632023-03-05 10:58:11 +000099 if (!guild.members.me?.permissions.has("ViewAuditLog")) return [];
TheCodedProf686829f2023-02-22 15:08:01 -0500100 await wait(delay ?? 250);
Skyler Grey1b669632023-03-05 10:58:11 +0000101 try {
102 const auditLog = (await guild.fetchAuditLogs({ type: event })).entries.map((m) => m);
103 return auditLog as Discord.GuildAuditLogsEntry[];
104 } catch (e) {
105 if (e instanceof DiscordAPIError) return [];
106 throw e;
107 }
PineaFan64486c42022-12-28 09:21:04 +0000108 },
TheCodedProfc3195b52023-06-23 15:53:00 -0400109 async preLog(guild: string, file: string): Promise<Discord.Message | void> {
110 const config = await client.database.guilds.read(guild);
111 if (!config.logging.logs.channel) return;
112 const channel = (await client.channels.fetch(config.logging.logs.channel)) as Discord.TextChannel | null;
113 if (!channel) return;
114 const message = await channel.send({
115 files: [
116 {
117 attachment: Buffer.from(file, "base64"),
118 name: "log.json"
119 }
120 ],
121 flags: ["SuppressEmbeds"]
122 });
123 return message;
124 },
TheCodedProf6ec331b2023-02-20 12:13:06 -0500125 async log(log: LoggerOptions): Promise<void> {
Skyler Greyda16adf2023-03-05 10:22:12 +0000126 if (!(await isLogging(log.hidden.guild, log.meta.calculateType))) return;
pineafan63fc5e22022-08-04 22:04:10 +0100127 const config = await client.database.guilds.read(log.hidden.guild);
Samuel Shuerta1511f92023-03-04 13:55:33 -0500128
pineafane625d782022-05-09 18:04:32 +0100129 if (config.logging.logs.channel) {
Skyler Grey11236ba2022-08-08 21:13:33 +0100130 const channel = (await client.channels.fetch(config.logging.logs.channel)) as Discord.TextChannel | null;
pineafanbd02b4a2022-08-05 22:01:38 +0100131 const description: Record<string, string> = {};
Skyler Grey75ea9172022-08-06 10:22:23 +0100132 Object.entries(log.list).map((entry) => {
pineafanbd02b4a2022-08-05 22:01:38 +0100133 const key: string = entry[0];
pineafan63fc5e22022-08-04 22:04:10 +0100134 // eslint-disable-next-line @typescript-eslint/no-explicit-any
135 const value: any = entry[1];
Skyler Grey75ea9172022-08-06 10:22:23 +0100136 if (value.displayValue) {
pineafane625d782022-05-09 18:04:32 +0100137 description[key] = value.displayValue;
138 } else {
139 description[key] = value;
140 }
pineafan63fc5e22022-08-04 22:04:10 +0100141 });
pineafane625d782022-05-09 18:04:32 +0100142 if (channel) {
TheCodedProf1807fb32023-02-20 14:33:48 -0500143 log.separate = log.separate ?? {};
TheCodedProf4a7c25d2023-06-07 17:09:45 -0400144 const messageOptions: Parameters<Discord.TextChannel["send"]>[0] = {};
145 const components: Discord.ActionRowBuilder<Discord.ButtonBuilder> = new Discord.ActionRowBuilder();
146 messageOptions.embeds = [
147 new EmojiEmbed()
148 .setEmoji(log.meta.emoji)
149 .setTitle(log.meta.displayName)
150 .setDescription(
151 (log.separate.start ? log.separate.start + "\n" : "") +
152 generateKeyValueList(description) +
153 (log.separate.end ? "\n" + log.separate.end : "")
154 )
155 .setTimestamp(log.meta.timestamp)
156 .setColor(log.meta.color)
157 ];
158 if (log.meta.files) messageOptions.files = log.meta.files;
159 if (log.meta.showDetails) {
160 components.addComponents(
161 new Discord.ButtonBuilder()
162 .setCustomId("log:showDetails")
163 .setLabel("Show Details")
164 .setStyle(Discord.ButtonStyle.Primary)
165 );
166 messageOptions.components = [components];
167 }
168 await channel.send(messageOptions);
pineafane625d782022-05-09 18:04:32 +0100169 }
170 }
TheCodedProf6ec331b2023-02-20 12:13:06 -0500171 },
172 isLogging
PineaFan64486c42022-12-28 09:21:04 +0000173};
174
pineafan63fc5e22022-08-04 22:04:10 +0100175export default {};