mirror of
https://github.com/ahmadk953/poixpixel-discord-bot.git
synced 2025-05-10 02:33:06 +00:00
249 lines
6.1 KiB
TypeScript
249 lines
6.1 KiB
TypeScript
import {
|
|
User,
|
|
GuildMember,
|
|
GuildChannel,
|
|
EmbedField,
|
|
PermissionsBitField,
|
|
} from 'discord.js';
|
|
|
|
import { LogActionPayload, LogActionType, RoleProperties } from './types.js';
|
|
import { ACTION_EMOJIS } from './constants.js';
|
|
|
|
/**
|
|
* Formats a permission name to be more readable
|
|
* @param perm - The permission to format
|
|
* @returns - The formatted permission name
|
|
*/
|
|
export const formatPermissionName = (perm: string): string => {
|
|
return perm
|
|
.split('_')
|
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
.join(' ');
|
|
};
|
|
|
|
/**
|
|
* Creates a field for a user
|
|
* @param user - The user to create a field for
|
|
* @param label - The label for the field
|
|
* @returns - The created field
|
|
*/
|
|
export const createUserField = (
|
|
user: User | GuildMember,
|
|
label = 'User',
|
|
): EmbedField => ({
|
|
name: label,
|
|
value: `<@${user.id}>`,
|
|
inline: true,
|
|
});
|
|
|
|
/**
|
|
* Creates a field for a moderator
|
|
* @param moderator - The moderator to create a field for
|
|
* @param label - The label for the field
|
|
* @returns - The created field
|
|
*/
|
|
export const createModeratorField = (
|
|
moderator?: GuildMember,
|
|
label = 'Moderator',
|
|
): EmbedField | null =>
|
|
moderator
|
|
? {
|
|
name: label,
|
|
value: `<@${moderator.id}>`,
|
|
inline: true,
|
|
}
|
|
: null;
|
|
|
|
/**
|
|
* Creates a field for a channel
|
|
* @param channel - The channel to create a field for
|
|
* @returns - The created field
|
|
*/
|
|
export const createChannelField = (channel: GuildChannel): EmbedField => ({
|
|
name: 'Channel',
|
|
value: `<#${channel.id}>`,
|
|
inline: true,
|
|
});
|
|
|
|
/**
|
|
* Creates a field for changed permissions
|
|
* @param oldPerms - The old permissions
|
|
* @param newPerms - The new permissions
|
|
* @returns - The created fields
|
|
*/
|
|
export const createPermissionChangeFields = (
|
|
oldPerms: Readonly<PermissionsBitField>,
|
|
newPerms: Readonly<PermissionsBitField>,
|
|
): EmbedField[] => {
|
|
const fields: EmbedField[] = [];
|
|
const changes: { added: string[]; removed: string[] } = {
|
|
added: [],
|
|
removed: [],
|
|
};
|
|
|
|
Object.keys(PermissionsBitField.Flags).forEach((perm) => {
|
|
const hasOld = oldPerms.has(perm as keyof typeof PermissionsBitField.Flags);
|
|
const hasNew = newPerms.has(perm as keyof typeof PermissionsBitField.Flags);
|
|
|
|
if (hasOld !== hasNew) {
|
|
if (hasNew) {
|
|
changes.added.push(formatPermissionName(perm));
|
|
} else {
|
|
changes.removed.push(formatPermissionName(perm));
|
|
}
|
|
}
|
|
});
|
|
|
|
if (changes.added.length) {
|
|
fields.push({
|
|
name: '✅ Added Permissions',
|
|
value: changes.added.join('\n'),
|
|
inline: true,
|
|
});
|
|
}
|
|
|
|
if (changes.removed.length) {
|
|
fields.push({
|
|
name: '❌ Removed Permissions',
|
|
value: changes.removed.join('\n'),
|
|
inline: true,
|
|
});
|
|
}
|
|
|
|
return fields;
|
|
};
|
|
|
|
/**
|
|
* Gets the names of the permissions in a bitfield
|
|
* @param permissions - The permissions to get the names of
|
|
* @returns - The names of the permissions
|
|
*/
|
|
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;
|
|
};
|
|
|
|
/**
|
|
* Compares two bitfields and returns the names of the permissions that are in the first bitfield but not the second
|
|
* @param a - The first bitfield
|
|
* @param b - The second bitfield
|
|
* @returns - The names of the permissions that are in the first bitfield but not the second
|
|
*/
|
|
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;
|
|
};
|
|
|
|
/**
|
|
* Creates a field for a role
|
|
* @param oldRole - The old role
|
|
* @param newRole - The new role
|
|
* @returns - The fields for the role changes
|
|
*/
|
|
export const createRoleChangeFields = (
|
|
oldRole: Partial<RoleProperties>,
|
|
newRole: Partial<RoleProperties>,
|
|
): EmbedField[] => {
|
|
const fields: EmbedField[] = [];
|
|
|
|
if (oldRole.name !== newRole.name) {
|
|
fields.push({
|
|
name: 'Name Changed',
|
|
value: `${oldRole.name} → ${newRole.name}`,
|
|
inline: true,
|
|
});
|
|
}
|
|
|
|
if (oldRole.color !== newRole.color) {
|
|
fields.push({
|
|
name: 'Color Changed',
|
|
value: `${oldRole.color || 'None'} → ${newRole.color || 'None'}`,
|
|
inline: true,
|
|
});
|
|
}
|
|
|
|
const booleanProps: Array<
|
|
keyof Pick<RoleProperties, 'hoist' | 'mentionable'>
|
|
> = ['hoist', 'mentionable'];
|
|
|
|
for (const prop of booleanProps) {
|
|
if (oldRole[prop] !== newRole[prop]) {
|
|
fields.push({
|
|
name: `${prop.charAt(0).toUpperCase() + prop.slice(1)} Changed`,
|
|
value: `${oldRole[prop] ? 'Yes' : 'No'} → ${newRole[prop] ? 'Yes' : 'No'}`,
|
|
inline: true,
|
|
});
|
|
}
|
|
}
|
|
|
|
return fields;
|
|
};
|
|
|
|
/**
|
|
* Gets the ID of the item that was logged
|
|
* @param payload - The payload to get the log item ID from
|
|
* @returns - The ID of the log item
|
|
*/
|
|
export const getLogItemId = (payload: LogActionPayload): string => {
|
|
switch (payload.action) {
|
|
case 'roleCreate':
|
|
case 'roleDelete':
|
|
case 'roleUpdate':
|
|
case 'roleAdd':
|
|
case 'roleRemove':
|
|
return payload.role.id;
|
|
|
|
case 'channelCreate':
|
|
case 'channelDelete':
|
|
case 'channelUpdate':
|
|
return payload.channel.id;
|
|
|
|
case 'messageDelete':
|
|
case 'messageEdit':
|
|
return payload.message.id;
|
|
|
|
case 'memberJoin':
|
|
case 'memberLeave':
|
|
return payload.member.id;
|
|
|
|
case 'ban':
|
|
case 'kick':
|
|
case 'mute':
|
|
case 'unban':
|
|
case 'unmute':
|
|
case 'warn':
|
|
return payload.target.id;
|
|
|
|
default:
|
|
return 'N/A';
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Gets the emoji for an action
|
|
* @param action - The action to get an emoji for
|
|
* @returns - The emoji for the action
|
|
*/
|
|
export const getEmojiForAction = (action: LogActionType): string => {
|
|
return ACTION_EMOJIS[action] || '📝';
|
|
};
|