| 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"; |
| |
| 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 |
| }; |
| |
| interface channelChanges { |
| channelId: { value: string | boolean | string[] | null; displayValue: string; }; |
| channel: { value: string | boolean | string[] | null; displayValue: string; }; |
| edited: { value: string | boolean | string[] | null; displayValue: string; }; |
| editedBy: { value: string | boolean | string[] | null; displayValue: string; }; |
| type?: { value: string | boolean | string[] | null; displayValue: string; }; |
| name?: { value: string | boolean | string[] | null; displayValue: string; }; |
| position?: { value: string | boolean | string[] | null; displayValue: string; }; |
| description?: { value: string | boolean | string[] | null; displayValue: string; }; |
| nsfw?: { value: string | boolean | string[] | null; displayValue: string; }; |
| slowmode?: { value: string | boolean | string[] | null; displayValue: string; }; |
| topic?: { value: string | boolean | string[] | null; displayValue: string; }; |
| bitrate?: { value: string | boolean | string[] | null; displayValue: string; }; |
| userLimit?: { value: string | boolean | string[] | null; displayValue: string; }; |
| rateLimitPerUser?: { value: string | boolean | string[] | null; displayValue: string; }; |
| parent?: { value: string | boolean | string[] | null; displayValue: string; }; |
| permissionOverwrites?: { value: string | boolean | string[] | null; displayValue: string; }; |
| region?: { value: string | boolean | string[] | null; displayValue: string; }; |
| maxUsers?: { value: string | boolean | string[] | null; displayValue: string; }; |
| } |
| |
| |
| |
| export const event = "channelUpdate"; |
| |
| export async function callback(client: NucleusClient, oldChannel: GuildChannel, newChannel: GuildChannel) { |
| const config = await client.memory.readGuildInfo(newChannel.guild.id); |
| const { getAuditLog, log, NucleusColors, entry, renderDelta, renderUser, renderChannel } = client.logger; |
| |
| if (newChannel.parent && newChannel.parent.id === config.tickets.category) return; |
| |
| const auditLog: GuildAuditLogsEntry<AuditLogEvent.ChannelUpdate> = (await getAuditLog(newChannel.guild, AuditLogEvent.ChannelUpdate)) |
| .filter((entry: GuildAuditLogsEntry) => (entry.target as GuildChannel)!.id === newChannel.id)[0] as GuildAuditLogsEntry<AuditLogEvent.ChannelUpdate>; |
| 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(new Date().getTime(), renderDelta(new Date().getTime())), |
| 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.GuildText: { |
| emoji = "CHANNEL.TEXT.EDIT"; |
| readableType = "Text"; |
| displayName = "Text 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"; |
| } |
| const nsfw = ["", ""]; |
| console.log((oldChannel as TextChannel).rateLimitPerUser, (newChannel as TextChannel).rateLimitPerUser); |
| 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 ((oldChannel as TextChannel).topic !== (newChannel as TextChannel).topic) |
| 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 && (oldChannel as TextChannel).rateLimitPerUser !== undefined) |
| changes.rateLimitPerUser = entry( |
| [((oldChannel as TextChannel).rateLimitPerUser ?? 0).toString(), ((newChannel as TextChannel).rateLimitPerUser ?? 0).toString()], |
| `${humanizeDuration((oldChannel as TextChannel).rateLimitPerUser * 1000)} -> ${humanizeDuration((newChannel as TextChannel).rateLimitPerUser * 1000)}` |
| ); |
| |
| break; |
| } |
| case ChannelType.GuildAnnouncement: { |
| emoji = "CHANNEL.TEXT.EDIT"; |
| readableType = "Announcement"; |
| displayName = "Announcment 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"}`); |
| 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"], |
| `${(oldChannel as VoiceChannel).rtcRegion?.toUpperCase() || "Automatic"} -> ${(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"], |
| `${(oldChannel as StageChannel).rtcRegion?.toUpperCase() || "Automatic"} -> ${(newChannel as StageChannel).rtcRegion?.toUpperCase() || "Automatic"}` |
| ); |
| break; |
| } |
| case ChannelType.GuildCategory: { |
| emoji = "CHANNEL.CATEGORY.EDIT"; |
| readableType = "Category"; |
| displayName = "Category"; |
| break; |
| } |
| default: { |
| emoji = "CHANNEL.TEXT.EDIT"; |
| readableType = "Channel"; |
| displayName = "Channel"; |
| } |
| } |
| let 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 |
| } |
| }; |
| log(data); |
| } |
| //TODO: Capitialize RTC Regions |