blob: 2f07717992c83fde5e26c055ee46cc12dfad37d0 [file] [log] [blame]
Skyler Greyda16adf2023-03-05 10:22:12 +00001import { GuildChannel, AuditLogEvent, ChannelType, TextChannel, VoiceChannel, StageChannel } from "discord.js";
2import type { GuildAuditLogsEntry } from "discord.js";
TheCodedProf309d6182023-01-18 18:10:29 -05003//@ts-expect-error
pineafan63fc5e22022-08-04 22:04:10 +01004import humanizeDuration from "humanize-duration";
PineaFane6ba7882023-01-18 20:41:16 +00005import type { NucleusClient } from "../utils/client.js";
pineafan63fc5e22022-08-04 22:04:10 +01006import getEmojiByName from "../utils/getEmojiByName.js";
TheCodedProf7b985d82023-06-08 16:40:41 -04007import client from "../utils/client.js";
PineaFan19dc9b82023-01-19 12:25:54 +00008import { capitalize } from "../utils/generateKeyValueList.js";
9
TheCodedProf7b985d82023-06-08 16:40:41 -040010let entry = client.logger.entry;
pineafan32767212022-03-14 21:27:39 +000011
TheCodedProf309d6182023-01-18 18:10:29 -050012const channelTypeEmoji: Record<number, string> = {
Skyler Greyda16adf2023-03-05 10:22:12 +000013 0: "Text", // Text channel
14 2: "Voice", // Voice channel
15 5: "Announcement", // Announcement channel
16 13: "Stage", // Stage channel
17 15: "Forum", // Forum channel
18 99: "Rules" // Rules channel
TheCodedProf309d6182023-01-18 18:10:29 -050019};
20
Skyler Greye6416232023-06-14 14:04:01 +020021// this eslint rule is invalid here, as the type definition is actually incorrect
22// if you make it an interface due to the [key: string]: unknown line. Try it if you like :)
23// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
TheCodedProf7b985d82023-06-08 16:40:41 -040024type channelChanges = {
TheCodedProfb493b8a2023-01-18 21:11:00 -050025 channelId: ReturnType<typeof entry>;
26 channel: ReturnType<typeof entry>;
27 edited: ReturnType<typeof entry>;
28 editedBy: ReturnType<typeof entry>;
29 type?: ReturnType<typeof entry>;
30 name?: ReturnType<typeof entry>;
31 position?: ReturnType<typeof entry>;
32 description?: ReturnType<typeof entry>;
33 nsfw?: ReturnType<typeof entry>;
34 slowmode?: ReturnType<typeof entry>;
35 topic?: ReturnType<typeof entry>;
36 bitrate?: ReturnType<typeof entry>;
37 userLimit?: ReturnType<typeof entry>;
TheCodedProfb493b8a2023-01-18 21:11:00 -050038 parent?: ReturnType<typeof entry>;
39 permissionOverwrites?: ReturnType<typeof entry>;
40 region?: ReturnType<typeof entry>;
41 maxUsers?: ReturnType<typeof entry>;
TheCodedProf6ec331b2023-02-20 12:13:06 -050042 autoArchiveDuration?: ReturnType<typeof entry>;
TheCodedProf7b985d82023-06-08 16:40:41 -040043 [key: string]: unknown;
TheCodedProf309d6182023-01-18 18:10:29 -050044}
45
pineafan63fc5e22022-08-04 22:04:10 +010046export const event = "channelUpdate";
pineafan32767212022-03-14 21:27:39 +000047
TheCodedProf7b985d82023-06-08 16:40:41 -040048export async function callback(_client: NucleusClient, oldChannel: GuildChannel, newChannel: GuildChannel) {
TheCodedProf6ec331b2023-02-20 12:13:06 -050049 const { getAuditLog, log, isLogging, NucleusColors, renderDelta, renderUser, renderChannel } = client.logger;
Skyler Greyda16adf2023-03-05 10:22:12 +000050 if (!(await isLogging(newChannel.guild.id, "channelUpdate"))) return;
PineaFane6ba7882023-01-18 20:41:16 +000051 const config = await client.memory.readGuildInfo(newChannel.guild.id);
TheCodedProfb493b8a2023-01-18 21:11:00 -050052 entry = client.logger.entry;
PineaFane6ba7882023-01-18 20:41:16 +000053 if (newChannel.parent && newChannel.parent.id === config.tickets.category) return;
pineafan32767212022-03-14 21:27:39 +000054
Skyler Greyda16adf2023-03-05 10:22:12 +000055 const auditLog: null | GuildAuditLogsEntry<AuditLogEvent.ChannelUpdate> = (
56 await getAuditLog(newChannel.guild, AuditLogEvent.ChannelUpdate)
57 ).filter(
58 (entry: GuildAuditLogsEntry) => (entry.target as GuildChannel)!.id === newChannel.id
59 )[0] as GuildAuditLogsEntry<AuditLogEvent.ChannelUpdate> | null;
PineaFanc4d6c3f2023-01-19 12:17:25 +000060 if (!auditLog) return;
TheCodedProf309d6182023-01-18 18:10:29 -050061 if (auditLog.executor!.id === client.user!.id) return;
pineafan32767212022-03-14 21:27:39 +000062
Skyler Grey75ea9172022-08-06 10:22:23 +010063 let emoji: string;
64 let readableType: string;
65 let displayName: string;
TheCodedProf309d6182023-01-18 18:10:29 -050066 const changes: channelChanges = {
PineaFane6ba7882023-01-18 20:41:16 +000067 channelId: entry(newChannel.id, `\`${newChannel.id}\``),
68 channel: entry(newChannel.id, renderChannel(newChannel)),
TheCodedProf6ec331b2023-02-20 12:13:06 -050069 edited: entry(Date.now(), renderDelta(Date.now())),
Skyler Greyda16adf2023-03-05 10:22:12 +000070 editedBy: entry(
71 auditLog.executor!.id,
72 renderUser((await newChannel.guild.members.fetch(auditLog.executor!.id)).user)
73 )
pineafan63fc5e22022-08-04 22:04:10 +010074 };
Skyler Greyda16adf2023-03-05 10:22:12 +000075 if (oldChannel.name !== newChannel.name)
76 changes.name = entry([oldChannel.name, newChannel.name], `${oldChannel.name} -> ${newChannel.name}`);
PineaFane6ba7882023-01-18 20:41:16 +000077 if (oldChannel.position !== newChannel.position)
Skyler Greyda16adf2023-03-05 10:22:12 +000078 changes.position = entry(
79 [oldChannel.position.toString(), newChannel.position.toString()],
80 `${oldChannel.position} -> ${newChannel.position}`
81 );
pineafan63fc5e22022-08-04 22:04:10 +010082
PineaFane6ba7882023-01-18 20:41:16 +000083 switch (newChannel.type) {
TheCodedProf6ec331b2023-02-20 12:13:06 -050084 case ChannelType.PrivateThread:
85 case ChannelType.PublicThread: {
86 return;
87 }
TheCodedProf309d6182023-01-18 18:10:29 -050088 case ChannelType.GuildText: {
Skyler Grey75ea9172022-08-06 10:22:23 +010089 emoji = "CHANNEL.TEXT.EDIT";
90 readableType = "Text";
91 displayName = "Text Channel";
TheCodedProf6ec331b2023-02-20 12:13:06 -050092 let oldTopic = (oldChannel as TextChannel).topic ?? "*None*",
93 newTopic = (oldChannel as TextChannel).topic ?? "*None*";
Skyler Grey75ea9172022-08-06 10:22:23 +010094 if (oldTopic) {
95 if (oldTopic.length > 256)
Skyler Grey11236ba2022-08-08 21:13:33 +010096 oldTopic = `\`\`\`\n${oldTopic.replace("`", "'").substring(0, 253) + "..."}\n\`\`\``;
Skyler Grey75ea9172022-08-06 10:22:23 +010097 else oldTopic = `\`\`\`\n${oldTopic.replace("`", "'")}\n\`\`\``;
98 } else {
99 oldTopic = "None";
100 }
101 if (newTopic) {
102 if (newTopic.length > 256)
Skyler Grey11236ba2022-08-08 21:13:33 +0100103 newTopic = `\`\`\`\n${newTopic.replace("`", "'").substring(0, 253) + "..."}\n\`\`\``;
Skyler Grey75ea9172022-08-06 10:22:23 +0100104 else newTopic = `\`\`\`\n${newTopic.replace("`", "'")}\n\`\`\``;
105 } else {
106 newTopic = "None";
107 }
108 const nsfw = ["", ""];
Skyler Greyda16adf2023-03-05 10:22:12 +0000109 nsfw[0] = (oldChannel as TextChannel).nsfw
110 ? `${getEmojiByName("CONTROL.TICK")} Yes`
111 : `${getEmojiByName("CONTROL.CROSS")} No`;
112 nsfw[1] = (newChannel as TextChannel).nsfw
113 ? `${getEmojiByName("CONTROL.TICK")} Yes`
114 : `${getEmojiByName("CONTROL.CROSS")} No`;
TheCodedProf6ec331b2023-02-20 12:13:06 -0500115 if (oldTopic !== newTopic)
Skyler Greyda16adf2023-03-05 10:22:12 +0000116 changes.description = entry(
117 [(oldChannel as TextChannel).topic ?? "", (newChannel as TextChannel).topic ?? ""],
118 `\nBefore: ${oldTopic}\nAfter: ${newTopic}`
119 );
120 if ((oldChannel as TextChannel).nsfw !== (newChannel as TextChannel).nsfw)
121 changes.nsfw = entry(
122 [(oldChannel as TextChannel).nsfw ? "On" : "Off", (newChannel as TextChannel).nsfw ? "On" : "Off"],
123 `${nsfw[0]} -> ${nsfw[1]}`
124 );
TheCodedProf6ec331b2023-02-20 12:13:06 -0500125 if ((oldChannel as TextChannel).rateLimitPerUser !== (newChannel as TextChannel).rateLimitPerUser)
126 changes.slowmode = entry(
Skyler Greyda16adf2023-03-05 10:22:12 +0000127 [
128 (oldChannel as TextChannel).rateLimitPerUser.toString(),
129 (newChannel as TextChannel).rateLimitPerUser.toString()
130 ],
131 `${humanizeDuration((oldChannel as TextChannel).rateLimitPerUser * 1000)} -> ${humanizeDuration(
132 (newChannel as TextChannel).rateLimitPerUser * 1000
133 )}`
Skyler Grey75ea9172022-08-06 10:22:23 +0100134 );
Skyler Greyda16adf2023-03-05 10:22:12 +0000135 if (
136 (oldChannel as TextChannel).defaultAutoArchiveDuration !==
137 (newChannel as TextChannel).defaultAutoArchiveDuration
138 ) {
TheCodedProf6ec331b2023-02-20 12:13:06 -0500139 changes.autoArchiveDuration = entry(
Skyler Greyda16adf2023-03-05 10:22:12 +0000140 [
141 ((oldChannel as TextChannel).defaultAutoArchiveDuration ?? 4320).toString(),
142 ((newChannel as TextChannel).defaultAutoArchiveDuration ?? 4320).toString()
143 ],
144 `${humanizeDuration(
145 ((oldChannel as TextChannel).defaultAutoArchiveDuration ?? 4320) * 60 * 1000
146 )} -> ${humanizeDuration(
147 ((newChannel as TextChannel).defaultAutoArchiveDuration ?? 4320) * 60 * 1000
148 )}`
TheCodedProf6ec331b2023-02-20 12:13:06 -0500149 );
150 }
pineafan63fc5e22022-08-04 22:04:10 +0100151
Skyler Grey75ea9172022-08-06 10:22:23 +0100152 break;
153 }
TheCodedProf309d6182023-01-18 18:10:29 -0500154 case ChannelType.GuildAnnouncement: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100155 emoji = "CHANNEL.TEXT.EDIT";
TheCodedProf309d6182023-01-18 18:10:29 -0500156 readableType = "Announcement";
PineaFan638eb132023-01-19 10:41:22 +0000157 displayName = "Announcement Channel";
TheCodedProf309d6182023-01-18 18:10:29 -0500158 let oldTopic = (oldChannel as TextChannel).topic,
159 newTopic = (newChannel as TextChannel).topic;
Skyler Grey75ea9172022-08-06 10:22:23 +0100160 if (oldTopic) {
161 if (oldTopic.length > 256)
Skyler Grey11236ba2022-08-08 21:13:33 +0100162 oldTopic = `\`\`\`\n${oldTopic.replace("`", "'").substring(0, 253) + "..."}\n\`\`\``;
Skyler Grey75ea9172022-08-06 10:22:23 +0100163 else oldTopic = `\`\`\`\n${oldTopic.replace("`", "'")}\n\`\`\``;
164 } else {
165 oldTopic = "None";
166 }
167 if (newTopic) {
168 if (newTopic.length > 256)
Skyler Grey11236ba2022-08-08 21:13:33 +0100169 newTopic = `\`\`\`\n${newTopic.replace("`", "'").substring(0, 253) + "..."}\n\`\`\``;
Skyler Grey75ea9172022-08-06 10:22:23 +0100170 else newTopic = `\`\`\`\n${newTopic.replace("`", "'")}\n\`\`\``;
171 } else {
172 newTopic = "None";
173 }
TheCodedProf6ec331b2023-02-20 12:13:06 -0500174 if ((oldChannel as TextChannel).nsfw !== (newChannel as TextChannel).nsfw) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000175 changes.nsfw = entry(
176 [(oldChannel as TextChannel).nsfw ? "On" : "Off", (newChannel as TextChannel).nsfw ? "On" : "Off"],
177 `${(oldChannel as TextChannel).nsfw ? "On" : "Off"} -> ${
178 (newChannel as TextChannel).nsfw ? "On" : "Off"
179 }`
180 );
TheCodedProf6ec331b2023-02-20 12:13:06 -0500181 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000182 if (
183 (oldChannel as TextChannel).defaultAutoArchiveDuration !==
184 (newChannel as TextChannel).defaultAutoArchiveDuration
185 ) {
TheCodedProf6ec331b2023-02-20 12:13:06 -0500186 changes.autoArchiveDuration = entry(
Skyler Greyda16adf2023-03-05 10:22:12 +0000187 [
188 ((oldChannel as TextChannel).defaultAutoArchiveDuration ?? 4320).toString(),
189 ((newChannel as TextChannel).defaultAutoArchiveDuration ?? 4320).toString()
190 ],
191 `${humanizeDuration(
192 ((oldChannel as TextChannel).defaultAutoArchiveDuration ?? 4320) * 60 * 1000
193 )} -> ${humanizeDuration(
194 ((newChannel as TextChannel).defaultAutoArchiveDuration ?? 4320) * 60 * 1000
195 )}`
TheCodedProf6ec331b2023-02-20 12:13:06 -0500196 );
197 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100198 break;
199 }
TheCodedProf309d6182023-01-18 18:10:29 -0500200 case ChannelType.GuildVoice: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100201 emoji = "CHANNEL.VOICE.EDIT";
202 readableType = "Voice";
203 displayName = "Voice Channel";
TheCodedProf309d6182023-01-18 18:10:29 -0500204 if ((oldChannel as VoiceChannel).bitrate !== (newChannel as VoiceChannel).bitrate)
Skyler Greyda16adf2023-03-05 10:22:12 +0000205 changes.bitrate = entry(
206 [(oldChannel as VoiceChannel).bitrate.toString(), (newChannel as VoiceChannel).bitrate.toString()],
207 `${(oldChannel as VoiceChannel).bitrate} -> ${(newChannel as VoiceChannel).bitrate}`
208 );
TheCodedProf309d6182023-01-18 18:10:29 -0500209 if ((oldChannel as VoiceChannel).userLimit !== (newChannel as VoiceChannel).userLimit)
Skyler Grey75ea9172022-08-06 10:22:23 +0100210 changes.maxUsers = entry(
Skyler Greyda16adf2023-03-05 10:22:12 +0000211 [
212 (oldChannel as VoiceChannel).userLimit.toString(),
213 (newChannel as VoiceChannel).userLimit.toString()
214 ],
215 `${
216 (oldChannel as VoiceChannel).userLimit ? (oldChannel as VoiceChannel).userLimit : "Unlimited"
217 } -> ${(newChannel as VoiceChannel).userLimit}`
Skyler Grey75ea9172022-08-06 10:22:23 +0100218 );
TheCodedProf309d6182023-01-18 18:10:29 -0500219 if ((oldChannel as VoiceChannel).rtcRegion !== (newChannel as VoiceChannel).rtcRegion)
Skyler Grey75ea9172022-08-06 10:22:23 +0100220 changes.region = entry(
Skyler Greyda16adf2023-03-05 10:22:12 +0000221 [
222 (oldChannel as VoiceChannel).rtcRegion ?? "automatic",
223 (newChannel as VoiceChannel).rtcRegion ?? "automatic"
224 ],
225 `${capitalize(
226 (oldChannel as VoiceChannel).rtcRegion?.toUpperCase() ?? "automatic"
227 )} -> ${capitalize((newChannel as VoiceChannel).rtcRegion?.toUpperCase() ?? "automatic")}`
Skyler Grey75ea9172022-08-06 10:22:23 +0100228 );
229 break;
230 }
TheCodedProf309d6182023-01-18 18:10:29 -0500231 case ChannelType.GuildStageVoice: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100232 emoji = "CHANNEL.VOICE.EDIT";
233 readableType = "Stage";
234 displayName = "Stage Channel";
TheCodedProf309d6182023-01-18 18:10:29 -0500235 let oldTopic = (oldChannel as StageChannel).topic,
236 newTopic = (newChannel as StageChannel).topic;
Skyler Grey75ea9172022-08-06 10:22:23 +0100237 if (oldTopic) {
238 if (oldTopic.length > 256)
Skyler Grey11236ba2022-08-08 21:13:33 +0100239 oldTopic = `\`\`\`\n${oldTopic.replace("`", "'").substring(0, 253) + "..."}\n\`\`\``;
Skyler Grey75ea9172022-08-06 10:22:23 +0100240 else oldTopic = `\`\`\`\n${oldTopic.replace("`", "'")}\n\`\`\``;
241 } else {
242 oldTopic = "None";
243 }
244 if (newTopic) {
245 if (newTopic.length > 256)
Skyler Grey11236ba2022-08-08 21:13:33 +0100246 newTopic = `\`\`\`\n${newTopic.replace("`", "'").substring(0, 253) + "..."}\n\`\`\``;
Skyler Grey75ea9172022-08-06 10:22:23 +0100247 else newTopic = `\`\`\`\n${newTopic.replace("`", "'")}\n\`\`\``;
248 } else {
249 newTopic = "None";
250 }
TheCodedProf309d6182023-01-18 18:10:29 -0500251 if ((oldChannel as StageChannel).bitrate !== (newChannel as StageChannel).bitrate)
Skyler Greyda16adf2023-03-05 10:22:12 +0000252 changes.bitrate = entry(
253 [(oldChannel as StageChannel).bitrate.toString(), (newChannel as StageChannel).bitrate.toString()],
254 `${(oldChannel as StageChannel).bitrate} -> ${(newChannel as StageChannel).bitrate}`
255 );
TheCodedProf309d6182023-01-18 18:10:29 -0500256 if ((oldChannel as StageChannel).userLimit !== (newChannel as StageChannel).userLimit)
Skyler Grey75ea9172022-08-06 10:22:23 +0100257 changes.maxUsers = entry(
Skyler Greyda16adf2023-03-05 10:22:12 +0000258 [
259 (oldChannel as StageChannel).userLimit.toString(),
260 (newChannel as StageChannel).userLimit.toString()
261 ],
262 `${
263 (oldChannel as StageChannel).userLimit ? (oldChannel as StageChannel).userLimit : "Unlimited"
264 } -> ${(newChannel as StageChannel).userLimit}`
Skyler Grey75ea9172022-08-06 10:22:23 +0100265 );
TheCodedProf309d6182023-01-18 18:10:29 -0500266 if ((oldChannel as StageChannel).rtcRegion !== (newChannel as StageChannel).rtcRegion)
Skyler Grey75ea9172022-08-06 10:22:23 +0100267 changes.region = entry(
Skyler Greyda16adf2023-03-05 10:22:12 +0000268 [
269 (oldChannel as StageChannel).rtcRegion ?? "Automatic",
270 (newChannel as StageChannel).rtcRegion ?? "Automatic"
271 ],
272 `${capitalize(
273 (oldChannel as StageChannel).rtcRegion?.toLowerCase() ?? "automatic"
274 )} -> ${capitalize((newChannel as StageChannel).rtcRegion?.toLowerCase() ?? "automatic")}`
Skyler Grey75ea9172022-08-06 10:22:23 +0100275 );
276 break;
277 }
TheCodedProf309d6182023-01-18 18:10:29 -0500278 case ChannelType.GuildCategory: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100279 emoji = "CHANNEL.CATEGORY.EDIT";
280 readableType = "Category";
281 displayName = "Category";
282 break;
283 }
284 default: {
285 emoji = "CHANNEL.TEXT.EDIT";
286 readableType = "Channel";
287 displayName = "Channel";
288 }
pineafan63fc5e22022-08-04 22:04:10 +0100289 }
PineaFan638eb132023-01-19 10:41:22 +0000290 const ocType = channelTypeEmoji[oldChannel.type],
TheCodedProf309d6182023-01-18 18:10:29 -0500291 ncType = channelTypeEmoji[newChannel.type];
Skyler Greyda16adf2023-03-05 10:22:12 +0000292 if (oldChannel.type !== newChannel.type) changes.type = entry([ocType!, ncType!], `${ocType!} -> ${readableType}`);
pineafan63fc5e22022-08-04 22:04:10 +0100293 if (!(Object.values(changes).length - 4)) return;
294 const data = {
Skyler Grey75ea9172022-08-06 10:22:23 +0100295 meta: {
pineafan63fc5e22022-08-04 22:04:10 +0100296 type: "channelUpdate",
297 displayName: displayName + " Edited",
298 calculateType: "channelUpdate",
299 color: NucleusColors.yellow,
300 emoji: emoji,
TheCodedProf309d6182023-01-18 18:10:29 -0500301 timestamp: auditLog.createdTimestamp
pineafan63fc5e22022-08-04 22:04:10 +0100302 },
303 list: changes,
304 hidden: {
PineaFane6ba7882023-01-18 20:41:16 +0000305 guild: newChannel.guild.id
pineafane625d782022-05-09 18:04:32 +0100306 }
pineafan63fc5e22022-08-04 22:04:10 +0100307 };
Skyler Greyf4f21c42023-03-08 14:36:29 +0000308 await log(data);
Skyler Grey75ea9172022-08-06 10:22:23 +0100309}