blob: b90d7145cb2089eeca2b15307fdcafaea9bcc1d2 [file] [log] [blame]
import { GuildChannel, AuditLogEvent, ChannelType, TextChannel, VoiceChannel, StageChannel } from "discord.js";
import type { GuildAuditLogsEntry } from "discord.js";
//@ts-expect-error
import humanizeDuration from "humanize-duration";
import type { NucleusClient } from "../utils/client.js";
import getEmojiByName from "../utils/getEmojiByName.js";
import client from "../utils/client.js";
import { capitalize } from "../utils/generateKeyValueList.js";
let entry = client.logger.entry;
const channelTypeEmoji: Record<number, string> = {
0: "Text", // Text channel
2: "Voice", // Voice channel
5: "Announcement", // Announcement channel
13: "Stage", // Stage channel
15: "Forum", // Forum channel
99: "Rules" // Rules channel
};
// this eslint rule is invalid here, as the type definition is actually incorrect
// if you make it an interface due to the [key: string]: unknown line. Try it if you like :)
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
type channelChanges = {
channelId: ReturnType<typeof entry>;
channel: ReturnType<typeof entry>;
edited: ReturnType<typeof entry>;
editedBy: ReturnType<typeof entry>;
type?: ReturnType<typeof entry>;
name?: ReturnType<typeof entry>;
position?: ReturnType<typeof entry>;
description?: ReturnType<typeof entry>;
nsfw?: ReturnType<typeof entry>;
slowmode?: ReturnType<typeof entry>;
topic?: ReturnType<typeof entry>;
bitrate?: ReturnType<typeof entry>;
userLimit?: ReturnType<typeof entry>;
parent?: ReturnType<typeof entry>;
permissionOverwrites?: ReturnType<typeof entry>;
region?: ReturnType<typeof entry>;
maxUsers?: ReturnType<typeof entry>;
autoArchiveDuration?: ReturnType<typeof entry>;
[key: string]: unknown;
};
export const event = "channelUpdate";
export async function callback(_client: NucleusClient, oldChannel: GuildChannel, newChannel: GuildChannel) {
const { getAuditLog, log, isLogging, NucleusColors, renderDelta, renderUser, renderChannel } = client.logger;
if (!(await isLogging(newChannel.guild.id, "channelUpdate"))) return;
const config = await client.memory.readGuildInfo(newChannel.guild.id);
entry = client.logger.entry;
if (newChannel.parent && newChannel.parent.id === config.tickets.category) return;
const auditLog: null | GuildAuditLogsEntry<AuditLogEvent.ChannelUpdate> = (
await getAuditLog(newChannel.guild, AuditLogEvent.ChannelUpdate)
).filter(
(entry: GuildAuditLogsEntry) => (entry.target as GuildChannel)!.id === newChannel.id
)[0] as GuildAuditLogsEntry<AuditLogEvent.ChannelUpdate> | null;
if (!auditLog) return;
if (auditLog.executor!.id === client.user!.id) return;
let emoji: string;
let readableType: string;
let displayName: string;
const changes: channelChanges = {
channelId: entry(newChannel.id, `\`${newChannel.id}\``),
channel: entry(newChannel.id, renderChannel(newChannel)),
edited: entry(Date.now(), renderDelta(Date.now())),
editedBy: entry(
auditLog.executor!.id,
renderUser((await newChannel.guild.members.fetch(auditLog.executor!.id)).user)
)
};
if (oldChannel.name !== newChannel.name)
changes.name = entry([oldChannel.name, newChannel.name], `${oldChannel.name} -> ${newChannel.name}`);
if (oldChannel.position !== newChannel.position)
changes.position = entry(
[oldChannel.position.toString(), newChannel.position.toString()],
`${oldChannel.position} -> ${newChannel.position}`
);
switch (newChannel.type) {
case ChannelType.PrivateThread:
case ChannelType.PublicThread: {
return;
}
case ChannelType.GuildText: {
emoji = "CHANNEL.TEXT.EDIT";
readableType = "Text";
displayName = "Text Channel";
let oldTopic = (oldChannel as TextChannel).topic ?? "*None*",
newTopic = (oldChannel as TextChannel).topic ?? "*None*";
if (oldTopic) {
if (oldTopic.length > 256)
oldTopic = `\`\`\`\n${oldTopic.replace("`", "'").substring(0, 253) + "..."}\n\`\`\``;
else oldTopic = `\`\`\`\n${oldTopic.replace("`", "'")}\n\`\`\``;
} else {
oldTopic = "None";
}
if (newTopic) {
if (newTopic.length > 256)
newTopic = `\`\`\`\n${newTopic.replace("`", "'").substring(0, 253) + "..."}\n\`\`\``;
else newTopic = `\`\`\`\n${newTopic.replace("`", "'")}\n\`\`\``;
} else {
newTopic = "None";
}
const nsfw = ["", ""];
nsfw[0] = (oldChannel as TextChannel).nsfw
? `${getEmojiByName("CONTROL.TICK")} Yes`
: `${getEmojiByName("CONTROL.CROSS")} No`;
nsfw[1] = (newChannel as TextChannel).nsfw
? `${getEmojiByName("CONTROL.TICK")} Yes`
: `${getEmojiByName("CONTROL.CROSS")} No`;
if (oldTopic !== newTopic)
changes.description = entry(
[(oldChannel as TextChannel).topic ?? "", (newChannel as TextChannel).topic ?? ""],
`\nBefore: ${oldTopic}\nAfter: ${newTopic}`
);
if ((oldChannel as TextChannel).nsfw !== (newChannel as TextChannel).nsfw)
changes.nsfw = entry(
[(oldChannel as TextChannel).nsfw ? "On" : "Off", (newChannel as TextChannel).nsfw ? "On" : "Off"],
`${nsfw[0]} -> ${nsfw[1]}`
);
if ((oldChannel as TextChannel).rateLimitPerUser !== (newChannel as TextChannel).rateLimitPerUser)
changes.slowmode = entry(
[
(oldChannel as TextChannel).rateLimitPerUser.toString(),
(newChannel as TextChannel).rateLimitPerUser.toString()
],
`${humanizeDuration((oldChannel as TextChannel).rateLimitPerUser * 1000)} -> ${humanizeDuration(
(newChannel as TextChannel).rateLimitPerUser * 1000
)}`
);
if (
(oldChannel as TextChannel).defaultAutoArchiveDuration !==
(newChannel as TextChannel).defaultAutoArchiveDuration
) {
changes.autoArchiveDuration = entry(
[
((oldChannel as TextChannel).defaultAutoArchiveDuration ?? 4320).toString(),
((newChannel as TextChannel).defaultAutoArchiveDuration ?? 4320).toString()
],
`${humanizeDuration(
((oldChannel as TextChannel).defaultAutoArchiveDuration ?? 4320) * 60 * 1000
)} -> ${humanizeDuration(
((newChannel as TextChannel).defaultAutoArchiveDuration ?? 4320) * 60 * 1000
)}`
);
}
break;
}
case ChannelType.GuildAnnouncement: {
emoji = "CHANNEL.TEXT.EDIT";
readableType = "Announcement";
displayName = "Announcement Channel";
let oldTopic = (oldChannel as TextChannel).topic,
newTopic = (newChannel as TextChannel).topic;
if (oldTopic) {
if (oldTopic.length > 256)
oldTopic = `\`\`\`\n${oldTopic.replace("`", "'").substring(0, 253) + "..."}\n\`\`\``;
else oldTopic = `\`\`\`\n${oldTopic.replace("`", "'")}\n\`\`\``;
} else {
oldTopic = "None";
}
if (newTopic) {
if (newTopic.length > 256)
newTopic = `\`\`\`\n${newTopic.replace("`", "'").substring(0, 253) + "..."}\n\`\`\``;
else newTopic = `\`\`\`\n${newTopic.replace("`", "'")}\n\`\`\``;
} else {
newTopic = "None";
}
if ((oldChannel as TextChannel).nsfw !== (newChannel as TextChannel).nsfw) {
changes.nsfw = entry(
[(oldChannel as TextChannel).nsfw ? "On" : "Off", (newChannel as TextChannel).nsfw ? "On" : "Off"],
`${(oldChannel as TextChannel).nsfw ? "On" : "Off"} -> ${
(newChannel as TextChannel).nsfw ? "On" : "Off"
}`
);
}
if (
(oldChannel as TextChannel).defaultAutoArchiveDuration !==
(newChannel as TextChannel).defaultAutoArchiveDuration
) {
changes.autoArchiveDuration = entry(
[
((oldChannel as TextChannel).defaultAutoArchiveDuration ?? 4320).toString(),
((newChannel as TextChannel).defaultAutoArchiveDuration ?? 4320).toString()
],
`${humanizeDuration(
((oldChannel as TextChannel).defaultAutoArchiveDuration ?? 4320) * 60 * 1000
)} -> ${humanizeDuration(
((newChannel as TextChannel).defaultAutoArchiveDuration ?? 4320) * 60 * 1000
)}`
);
}
break;
}
case ChannelType.GuildVoice: {
emoji = "CHANNEL.VOICE.EDIT";
readableType = "Voice";
displayName = "Voice Channel";
if ((oldChannel as VoiceChannel).bitrate !== (newChannel as VoiceChannel).bitrate)
changes.bitrate = entry(
[(oldChannel as VoiceChannel).bitrate.toString(), (newChannel as VoiceChannel).bitrate.toString()],
`${(oldChannel as VoiceChannel).bitrate} -> ${(newChannel as VoiceChannel).bitrate}`
);
if ((oldChannel as VoiceChannel).userLimit !== (newChannel as VoiceChannel).userLimit)
changes.maxUsers = entry(
[
(oldChannel as VoiceChannel).userLimit.toString(),
(newChannel as VoiceChannel).userLimit.toString()
],
`${
(oldChannel as VoiceChannel).userLimit ? (oldChannel as VoiceChannel).userLimit : "Unlimited"
} -> ${(newChannel as VoiceChannel).userLimit}`
);
if ((oldChannel as VoiceChannel).rtcRegion !== (newChannel as VoiceChannel).rtcRegion)
changes.region = entry(
[
(oldChannel as VoiceChannel).rtcRegion ?? "automatic",
(newChannel as VoiceChannel).rtcRegion ?? "automatic"
],
`${capitalize(
(oldChannel as VoiceChannel).rtcRegion?.toUpperCase() ?? "automatic"
)} -> ${capitalize((newChannel as VoiceChannel).rtcRegion?.toUpperCase() ?? "automatic")}`
);
break;
}
case ChannelType.GuildStageVoice: {
emoji = "CHANNEL.VOICE.EDIT";
readableType = "Stage";
displayName = "Stage Channel";
let oldTopic = (oldChannel as StageChannel).topic,
newTopic = (newChannel as StageChannel).topic;
if (oldTopic) {
if (oldTopic.length > 256)
oldTopic = `\`\`\`\n${oldTopic.replace("`", "'").substring(0, 253) + "..."}\n\`\`\``;
else oldTopic = `\`\`\`\n${oldTopic.replace("`", "'")}\n\`\`\``;
} else {
oldTopic = "None";
}
if (newTopic) {
if (newTopic.length > 256)
newTopic = `\`\`\`\n${newTopic.replace("`", "'").substring(0, 253) + "..."}\n\`\`\``;
else newTopic = `\`\`\`\n${newTopic.replace("`", "'")}\n\`\`\``;
} else {
newTopic = "None";
}
if ((oldChannel as StageChannel).bitrate !== (newChannel as StageChannel).bitrate)
changes.bitrate = entry(
[(oldChannel as StageChannel).bitrate.toString(), (newChannel as StageChannel).bitrate.toString()],
`${(oldChannel as StageChannel).bitrate} -> ${(newChannel as StageChannel).bitrate}`
);
if ((oldChannel as StageChannel).userLimit !== (newChannel as StageChannel).userLimit)
changes.maxUsers = entry(
[
(oldChannel as StageChannel).userLimit.toString(),
(newChannel as StageChannel).userLimit.toString()
],
`${
(oldChannel as StageChannel).userLimit ? (oldChannel as StageChannel).userLimit : "Unlimited"
} -> ${(newChannel as StageChannel).userLimit}`
);
if ((oldChannel as StageChannel).rtcRegion !== (newChannel as StageChannel).rtcRegion)
changes.region = entry(
[
(oldChannel as StageChannel).rtcRegion ?? "Automatic",
(newChannel as StageChannel).rtcRegion ?? "Automatic"
],
`${capitalize(
(oldChannel as StageChannel).rtcRegion?.toLowerCase() ?? "automatic"
)} -> ${capitalize((newChannel as StageChannel).rtcRegion?.toLowerCase() ?? "automatic")}`
);
break;
}
case ChannelType.GuildCategory: {
emoji = "CHANNEL.CATEGORY.EDIT";
readableType = "Category";
displayName = "Category";
break;
}
default: {
emoji = "CHANNEL.TEXT.EDIT";
readableType = "Channel";
displayName = "Channel";
}
}
const ocType = channelTypeEmoji[oldChannel.type],
ncType = channelTypeEmoji[newChannel.type];
if (oldChannel.type !== newChannel.type) changes.type = entry([ocType!, ncType!], `${ocType!} -> ${readableType}`);
if (!(Object.values(changes).length - 4)) return;
const data = {
meta: {
type: "channelUpdate",
displayName: displayName + " Edited",
calculateType: "channelUpdate",
color: NucleusColors.yellow,
emoji: emoji,
timestamp: auditLog.createdTimestamp
},
list: changes,
hidden: {
guild: newChannel.guild.id
}
};
await log(data);
}