mirror of
https://github.com/ahmadk953/poixpixel-discord-bot.git
synced 2025-04-02 09:44:14 +00:00
Improved channelUpdate event logging
This commit is contained in:
parent
6c523bbeba
commit
3762e554b4
5 changed files with 295 additions and 16 deletions
|
@ -1,7 +1,4 @@
|
||||||
import {
|
import { PermissionsBitField, SlashCommandBuilder } from 'discord.js';
|
||||||
PermissionsBitField,
|
|
||||||
SlashCommandBuilder,
|
|
||||||
} from 'discord.js';
|
|
||||||
|
|
||||||
import { updateMember, updateMemberModerationHistory } from '../../db/db.js';
|
import { updateMember, updateMemberModerationHistory } from '../../db/db.js';
|
||||||
import { parseDuration, scheduleUnban } from '../../util/helpers.js';
|
import { parseDuration, scheduleUnban } from '../../util/helpers.js';
|
||||||
|
|
|
@ -1,7 +1,98 @@
|
||||||
import { AuditLogEvent, Events, GuildChannel } from 'discord.js';
|
import {
|
||||||
|
AuditLogEvent,
|
||||||
|
Events,
|
||||||
|
GuildChannel,
|
||||||
|
PermissionOverwrites,
|
||||||
|
} from 'discord.js';
|
||||||
|
|
||||||
import logAction from '../util/logging/logAction.js';
|
import logAction from '../util/logging/logAction.js';
|
||||||
|
import { ChannelLogAction } from '../util/logging/types.js';
|
||||||
import { Event } from '../types/EventTypes.js';
|
import { Event } from '../types/EventTypes.js';
|
||||||
|
|
||||||
|
function arePermissionsEqual(
|
||||||
|
oldPerms: Map<string, PermissionOverwrites>,
|
||||||
|
newPerms: Map<string, PermissionOverwrites>,
|
||||||
|
): boolean {
|
||||||
|
if (oldPerms.size !== newPerms.size) return false;
|
||||||
|
|
||||||
|
for (const [id, oldPerm] of oldPerms.entries()) {
|
||||||
|
const newPerm = newPerms.get(id);
|
||||||
|
if (!newPerm) return false;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!oldPerm.allow.equals(newPerm.allow) ||
|
||||||
|
!oldPerm.deny.equals(newPerm.deny)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function getPermissionChanges(
|
||||||
|
oldChannel: GuildChannel,
|
||||||
|
newChannel: GuildChannel,
|
||||||
|
): ChannelLogAction['permissionChanges'] {
|
||||||
|
const changes: ChannelLogAction['permissionChanges'] = [];
|
||||||
|
const newPerms = newChannel.permissionOverwrites.cache;
|
||||||
|
const oldPerms = oldChannel.permissionOverwrites.cache;
|
||||||
|
|
||||||
|
for (const [id, newPerm] of newPerms.entries()) {
|
||||||
|
const oldPerm = oldPerms.get(id);
|
||||||
|
const targetType = newPerm.type === 0 ? 'role' : 'member';
|
||||||
|
const targetName =
|
||||||
|
newPerm.type === 0
|
||||||
|
? newChannel.guild.roles.cache.get(id)?.name || id
|
||||||
|
: newChannel.guild.members.cache.get(id)?.user.username || id;
|
||||||
|
|
||||||
|
if (!oldPerm) {
|
||||||
|
changes.push({
|
||||||
|
action: 'added',
|
||||||
|
targetId: id,
|
||||||
|
targetType,
|
||||||
|
targetName,
|
||||||
|
allow: newPerm.allow,
|
||||||
|
deny: newPerm.deny,
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
!oldPerm.allow.equals(newPerm.allow) ||
|
||||||
|
!oldPerm.deny.equals(newPerm.deny)
|
||||||
|
) {
|
||||||
|
changes.push({
|
||||||
|
action: 'modified',
|
||||||
|
targetId: id,
|
||||||
|
targetType,
|
||||||
|
targetName,
|
||||||
|
oldAllow: oldPerm.allow,
|
||||||
|
oldDeny: oldPerm.deny,
|
||||||
|
newAllow: newPerm.allow,
|
||||||
|
newDeny: newPerm.deny,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [id, oldPerm] of oldPerms.entries()) {
|
||||||
|
if (!newPerms.has(id)) {
|
||||||
|
const targetType = oldPerm.type === 0 ? 'role' : 'member';
|
||||||
|
const targetName =
|
||||||
|
oldPerm.type === 0
|
||||||
|
? oldChannel.guild.roles.cache.get(id)?.name || id
|
||||||
|
: oldChannel.guild.members.cache.get(id)?.user.username || id;
|
||||||
|
|
||||||
|
changes.push({
|
||||||
|
action: 'removed',
|
||||||
|
targetId: id,
|
||||||
|
targetType,
|
||||||
|
targetName,
|
||||||
|
allow: oldPerm.allow,
|
||||||
|
deny: oldPerm.deny,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
export const channelCreate = {
|
export const channelCreate = {
|
||||||
name: Events.ChannelCreate,
|
name: Events.ChannelCreate,
|
||||||
execute: async (channel: GuildChannel) => {
|
execute: async (channel: GuildChannel) => {
|
||||||
|
@ -58,16 +149,33 @@ export const channelUpdate = {
|
||||||
name: Events.ChannelUpdate,
|
name: Events.ChannelUpdate,
|
||||||
execute: async (oldChannel: GuildChannel, newChannel: GuildChannel) => {
|
execute: async (oldChannel: GuildChannel, newChannel: GuildChannel) => {
|
||||||
try {
|
try {
|
||||||
|
if (
|
||||||
|
oldChannel.name === newChannel.name &&
|
||||||
|
oldChannel.type === newChannel.type &&
|
||||||
|
oldChannel.permissionOverwrites.cache.size ===
|
||||||
|
newChannel.permissionOverwrites.cache.size &&
|
||||||
|
arePermissionsEqual(
|
||||||
|
oldChannel.permissionOverwrites.cache,
|
||||||
|
newChannel.permissionOverwrites.cache,
|
||||||
|
) &&
|
||||||
|
oldChannel.position !== newChannel.position
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { guild } = newChannel;
|
const { guild } = newChannel;
|
||||||
const auditLogs = await guild.fetchAuditLogs({
|
const auditLogs = await guild.fetchAuditLogs({
|
||||||
type: AuditLogEvent.ChannelUpdate,
|
type: AuditLogEvent.ChannelUpdate,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
});
|
});
|
||||||
const executor = auditLogs.entries.first()?.executor;
|
const log = auditLogs.entries.first();
|
||||||
|
const executor = log?.executor;
|
||||||
const moderator = executor
|
const moderator = executor
|
||||||
? await guild.members.fetch(executor.id)
|
? await guild.members.fetch(executor.id)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
const permissionChanges = getPermissionChanges(oldChannel, newChannel);
|
||||||
|
|
||||||
await logAction({
|
await logAction({
|
||||||
guild,
|
guild,
|
||||||
action: 'channelUpdate',
|
action: 'channelUpdate',
|
||||||
|
@ -75,8 +183,8 @@ export const channelUpdate = {
|
||||||
moderator,
|
moderator,
|
||||||
oldName: oldChannel.name,
|
oldName: oldChannel.name,
|
||||||
newName: newChannel.name,
|
newName: newChannel.name,
|
||||||
oldPermissions: oldChannel.permissionOverwrites.cache.first()?.allow,
|
permissionChanges:
|
||||||
newPermissions: newChannel.permissionOverwrites.cache.first()?.allow,
|
(permissionChanges ?? []).length > 0 ? permissionChanges : undefined,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error handling channel update:', error);
|
console.error('Error handling channel update:', error);
|
||||||
|
|
|
@ -19,6 +19,8 @@ import {
|
||||||
createRoleChangeFields,
|
createRoleChangeFields,
|
||||||
getLogItemId,
|
getLogItemId,
|
||||||
getEmojiForAction,
|
getEmojiForAction,
|
||||||
|
getPermissionDifference,
|
||||||
|
getPermissionNames,
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
|
|
||||||
export default async function logAction(payload: LogActionPayload) {
|
export default async function logAction(payload: LogActionPayload) {
|
||||||
|
@ -209,6 +211,12 @@ export default async function logAction(payload: LogActionPayload) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'channelUpdate': {
|
case 'channelUpdate': {
|
||||||
|
const changesExist =
|
||||||
|
payload.oldName !== payload.newName ||
|
||||||
|
(payload.permissionChanges && payload.permissionChanges.length > 0);
|
||||||
|
|
||||||
|
if (!changesExist) return;
|
||||||
|
|
||||||
fields.push({
|
fields.push({
|
||||||
name: '📝 Channel Information',
|
name: '📝 Channel Information',
|
||||||
value: [
|
value: [
|
||||||
|
@ -223,12 +231,138 @@ export default async function logAction(payload: LogActionPayload) {
|
||||||
inline: false,
|
inline: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (payload.oldPermissions && payload.newPermissions) {
|
if (payload.permissionChanges && payload.permissionChanges.length > 0) {
|
||||||
const permissionChanges = createPermissionChangeFields(
|
const changes = {
|
||||||
payload.oldPermissions,
|
added: payload.permissionChanges.filter((c) => c.action === 'added'),
|
||||||
payload.newPermissions,
|
modified: payload.permissionChanges.filter(
|
||||||
);
|
(c) => c.action === 'modified',
|
||||||
fields.push(...permissionChanges);
|
),
|
||||||
|
removed: payload.permissionChanges.filter(
|
||||||
|
(c) => c.action === 'removed',
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (changes.added.length > 0) {
|
||||||
|
fields.push({
|
||||||
|
name: '➕ Added Permissions',
|
||||||
|
value: changes.added
|
||||||
|
.map((c) => {
|
||||||
|
const targetMention =
|
||||||
|
c.targetType === 'role'
|
||||||
|
? `<@&${c.targetId}>`
|
||||||
|
: `<@${c.targetId}>`;
|
||||||
|
return `For ${c.targetType} ${targetMention} (${c.targetName})`;
|
||||||
|
})
|
||||||
|
.join('\n'),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
changes.added.forEach((c) => {
|
||||||
|
if (c.allow?.bitfield || c.deny?.bitfield) {
|
||||||
|
const permList = [];
|
||||||
|
if (c.allow?.bitfield) {
|
||||||
|
const allowedPerms = getPermissionNames(c.allow);
|
||||||
|
if (allowedPerms.length) {
|
||||||
|
permList.push(`✅ **Allowed:** ${allowedPerms.join(', ')}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c.deny?.bitfield) {
|
||||||
|
const deniedPerms = getPermissionNames(c.deny);
|
||||||
|
if (deniedPerms.length) {
|
||||||
|
permList.push(`❌ **Denied:** ${deniedPerms.join(', ')}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permList.length > 0) {
|
||||||
|
fields.push({
|
||||||
|
name: `Permissions for ${c.targetType} ${c.targetName}`,
|
||||||
|
value: permList.join('\n'),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes.modified.length > 0) {
|
||||||
|
fields.push({
|
||||||
|
name: '🔄 Modified Permissions',
|
||||||
|
value: changes.modified
|
||||||
|
.map((c) => {
|
||||||
|
const targetMention =
|
||||||
|
c.targetType === 'role'
|
||||||
|
? `<@&${c.targetId}>`
|
||||||
|
: `<@${c.targetId}>`;
|
||||||
|
return `For ${c.targetType} ${targetMention} (${c.targetName})`;
|
||||||
|
})
|
||||||
|
.join('\n'),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
changes.modified.forEach((c) => {
|
||||||
|
if (c.oldAllow && c.newAllow && c.oldDeny && c.newDeny) {
|
||||||
|
const addedPerms = getPermissionDifference(
|
||||||
|
c.newAllow,
|
||||||
|
c.oldAllow,
|
||||||
|
);
|
||||||
|
const removedPerms = getPermissionDifference(
|
||||||
|
c.oldAllow,
|
||||||
|
c.newAllow,
|
||||||
|
);
|
||||||
|
const addedDenies = getPermissionDifference(c.newDeny, c.oldDeny);
|
||||||
|
const removedDenies = getPermissionDifference(
|
||||||
|
c.oldDeny,
|
||||||
|
c.newDeny,
|
||||||
|
);
|
||||||
|
|
||||||
|
const permissionChanges = [];
|
||||||
|
if (addedPerms.length) {
|
||||||
|
permissionChanges.push(
|
||||||
|
`✅ **Newly Allowed:** ${addedPerms.join(', ')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (removedPerms.length) {
|
||||||
|
permissionChanges.push(
|
||||||
|
`⬇️ **No Longer Allowed:** ${removedPerms.join(', ')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (addedDenies.length) {
|
||||||
|
permissionChanges.push(
|
||||||
|
`❌ **Newly Denied:** ${addedDenies.join(', ')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (removedDenies.length) {
|
||||||
|
permissionChanges.push(
|
||||||
|
`⬆️ **No Longer Denied:** ${removedDenies.join(', ')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissionChanges.length > 0) {
|
||||||
|
fields.push({
|
||||||
|
name: `Changes for ${c.targetType} ${c.targetName}`,
|
||||||
|
value: permissionChanges.join('\n'),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes.removed.length > 0) {
|
||||||
|
fields.push({
|
||||||
|
name: '➖ Removed Permissions',
|
||||||
|
value: changes.removed
|
||||||
|
.map((c) => {
|
||||||
|
const targetMention =
|
||||||
|
c.targetType === 'role'
|
||||||
|
? `<@&${c.targetId}>`
|
||||||
|
: `<@${c.targetId}>`;
|
||||||
|
return `For ${c.targetType} ${targetMention} (${c.targetName})`;
|
||||||
|
})
|
||||||
|
.join('\n'),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const moderatorField = createModeratorField(
|
const moderatorField = createModeratorField(
|
||||||
|
|
|
@ -108,8 +108,18 @@ export interface ChannelLogAction extends BaseLogAction {
|
||||||
channel: GuildChannel;
|
channel: GuildChannel;
|
||||||
oldName?: string;
|
oldName?: string;
|
||||||
newName?: string;
|
newName?: string;
|
||||||
oldPermissions?: Readonly<PermissionsBitField>;
|
permissionChanges?: Array<{
|
||||||
newPermissions?: Readonly<PermissionsBitField>;
|
action: 'added' | 'modified' | 'removed';
|
||||||
|
targetId: string;
|
||||||
|
targetType: 'role' | 'member';
|
||||||
|
targetName: string;
|
||||||
|
allow?: Readonly<PermissionsBitField>;
|
||||||
|
deny?: Readonly<PermissionsBitField>;
|
||||||
|
oldAllow?: Readonly<PermissionsBitField>;
|
||||||
|
oldDeny?: Readonly<PermissionsBitField>;
|
||||||
|
newAllow?: Readonly<PermissionsBitField>;
|
||||||
|
newDeny?: Readonly<PermissionsBitField>;
|
||||||
|
}>;
|
||||||
moderator?: GuildMember;
|
moderator?: GuildMember;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,36 @@ export const createPermissionChangeFields = (
|
||||||
return fields;
|
return fields;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getPermissionNames = (
|
||||||
|
permissions: Readonly<PermissionsBitField>,
|
||||||
|
): string[] => {
|
||||||
|
const names: string[] = [];
|
||||||
|
|
||||||
|
Object.keys(PermissionsBitField.Flags).forEach((perm) => {
|
||||||
|
if (permissions.has(perm as keyof typeof PermissionsBitField.Flags)) {
|
||||||
|
names.push(formatPermissionName(perm));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return names;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPermissionDifference = (
|
||||||
|
a: Readonly<PermissionsBitField>,
|
||||||
|
b: Readonly<PermissionsBitField>,
|
||||||
|
): string[] => {
|
||||||
|
const names: string[] = [];
|
||||||
|
|
||||||
|
Object.keys(PermissionsBitField.Flags).forEach((perm) => {
|
||||||
|
const permKey = perm as keyof typeof PermissionsBitField.Flags;
|
||||||
|
if (a.has(permKey) && !b.has(permKey)) {
|
||||||
|
names.push(formatPermissionName(perm));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return names;
|
||||||
|
};
|
||||||
|
|
||||||
export const createRoleChangeFields = (
|
export const createRoleChangeFields = (
|
||||||
oldRole: Partial<RoleProperties>,
|
oldRole: Partial<RoleProperties>,
|
||||||
newRole: Partial<RoleProperties>,
|
newRole: Partial<RoleProperties>,
|
||||||
|
|
Loading…
Add table
Reference in a new issue