UNPKG

@reaxion/utils

Version:

Core utilities for Reaxion: channels, threads, roles, categories

387 lines (325 loc) 13.8 kB
import { getClient } from './registry.js'; import { PermissionsBitField, Colors } from 'discord.js'; /** * Gets a role by ID * @param {String} guildId The ID of the guild * @param {String} roleId The ID of the role to get * @param {Object} options Additional options * @param {Boolean} [options.ignoreNotFound=false] Whether to return null instead of throwing when role isn't found * @returns {Promise<Role|null>} The found role or null */ export async function getRole(guildId, roleId, options = {}) { if (!guildId) throw new Error('Guild ID must be provided'); if (!roleId) throw new Error('Role ID must be provided'); // Get the guild const guild = await getClient().guilds.fetch(guildId).catch(error => { if (error.code === 10004) { // Unknown Guild if (options.ignoreNotFound) return null; throw new Error(`Guild with ID ${guildId} not found`); } throw error; }); if (!guild) return null; // Get the role const role = await guild.roles.fetch(roleId).catch(error => { if (error.code === 10011) { // Unknown Role if (options.ignoreNotFound) return null; throw new Error(`Role with ID ${roleId} not found in guild ${guild.name}`); } throw error; }); if (!role) { if (options.ignoreNotFound) return null; throw new Error(`Role with ID ${roleId} not found in guild ${guild.name}`); } return role; } /** * Find a role by name * @param {String} guildId The ID of the guild * @param {String} roleName The name or partial name of the role * @param {Object} options Additional options * @param {Boolean} [options.exactMatch=false] Whether to require exact name match * @param {Boolean} [options.caseSensitive=false] Whether the match should be case sensitive * @returns {Promise<Role|null>} The found role or null */ export async function findRoleByName(guildId, roleName, options = {}) { if (!guildId) throw new Error('Guild ID must be provided'); if (!roleName) throw new Error('Role name must be provided'); // Get the guild const guild = await getClient().guilds.fetch(guildId); // Fetch all roles await guild.roles.fetch(); // Format search terms based on case sensitivity const searchName = options.caseSensitive ? roleName : roleName.toLowerCase(); // Find role by name const foundRole = guild.roles.cache.find(role => { const roleCmpName = options.caseSensitive ? role.name : role.name.toLowerCase(); if (options.exactMatch) { return roleCmpName === searchName; } else { return roleCmpName.includes(searchName); } }); return foundRole || null; } /** * Create a new role in a guild * @param {String} guildId The ID of the guild * @param {Object} roleData The role data * @param {String} roleData.name The name of the role * @param {ColorResolvable} [roleData.color] The color of the role * @param {Boolean} [roleData.hoist=false] Whether the role should be displayed separately * @param {Boolean} [roleData.mentionable=false] Whether the role should be mentionable * @param {PermissionResolvable} [roleData.permissions] The permissions for the role * @param {String} [roleData.reason] Reason for creating the role (audit log) * @returns {Promise<Role>} The created role */ export async function createRole(guildId, roleData) { if (!guildId) throw new Error('Guild ID must be provided'); if (!roleData.name) throw new Error('Role name must be provided'); // Get the guild const guild = await getClient().guilds.fetch(guildId); // Create the role return guild.roles.create({ name: roleData.name, color: roleData.color || null, hoist: roleData.hoist || false, mentionable: roleData.mentionable || false, permissions: roleData.permissions || [], reason: roleData.reason || 'Role created via roleUtils' }); } /** * Delete a role from a guild * @param {String} guildId The ID of the guild * @param {String} roleId The ID of the role to delete * @param {String} [reason] Reason for deleting the role (audit log) * @returns {Promise<Role>} The deleted role */ export async function deleteRole(guildId, roleId, reason) { const role = await getRole(guildId, roleId); return role.delete(reason || 'Role deleted via roleUtils'); } /** * Modify an existing role * @param {String} guildId The ID of the guild * @param {String} roleId The ID of the role to modify * @param {Object} roleData The role data to update * @param {String} [roleData.name] New name for the role * @param {ColorResolvable} [roleData.color] New color for the role * @param {Boolean} [roleData.hoist] Whether the role should be displayed separately * @param {Boolean} [roleData.mentionable] Whether the role should be mentionable * @param {PermissionResolvable} [roleData.permissions] New permissions for the role * @param {String} [roleData.reason] Reason for modifying the role (audit log) * @returns {Promise<Role>} The modified role */ export async function modifyRole(guildId, roleId, roleData) { const role = await getRole(guildId, roleId); // Prepare edit data const editData = {}; if (roleData.name !== undefined) editData.name = roleData.name; if (roleData.color !== undefined) editData.color = roleData.color; if (roleData.hoist !== undefined) editData.hoist = roleData.hoist; if (roleData.mentionable !== undefined) editData.mentionable = roleData.mentionable; if (roleData.permissions !== undefined) editData.permissions = roleData.permissions; return role.edit(editData, roleData.reason || 'Role modified via roleUtils'); } /** * Check if a role exists * @param {String} guildId The ID of the guild * @param {String} roleId The ID of the role to check * @returns {Promise<Boolean>} Whether the role exists */ export async function roleExists(guildId, roleId) { try { const role = await getRole(guildId, roleId, { ignoreNotFound: true }); return !!role; } catch (error) { return false; } } /** * Get all roles in a guild * @param {String} guildId The ID of the guild * @param {Object} options Additional options * @param {Boolean} [options.excludeEveryone=true] Whether to exclude the @everyone role * @param {Boolean} [options.excludeManaged=false] Whether to exclude managed roles (bots, integrations) * @param {Function} [options.filter] Custom filter function (role) => boolean * @returns {Promise<Array<Role>>} Array of roles */ export async function getAllRoles(guildId, options = {}) { const guild = await getClient().guilds.fetch(guildId); // Fetch all roles await guild.roles.fetch(); // Get roles from cache let roles = [...guild.roles.cache.values()]; // Apply filters if (options.excludeEveryone !== false) { roles = roles.filter(role => role.id !== guild.id); } if (options.excludeManaged) { roles = roles.filter(role => !role.managed); } if (typeof options.filter === 'function') { roles = roles.filter(options.filter); } return roles; } /** * Add a role to a guild member * @param {String} guildId The ID of the guild * @param {String} userId The ID of the user * @param {String} roleId The ID of the role to add * @param {String} [reason] Reason for adding the role (audit log) * @returns {Promise<GuildMember>} The updated guild member */ export async function addRoleToMember(guildId, userId, roleId, reason) { // Get the guild const guild = await getClient().guilds.fetch(guildId); // Get the member const member = await guild.members.fetch(userId); if (!member) throw new Error(`Member with ID ${userId} not found in guild ${guild.name}`); // Get the role const role = await getRole(guildId, roleId); // Add the role return member.roles.add(role, reason || 'Role added via roleUtils'); } /** * Remove a role from a guild member * @param {String} guildId The ID of the guild * @param {String} userId The ID of the user * @param {String} roleId The ID of the role to remove * @param {String} [reason] Reason for removing the role (audit log) * @returns {Promise<GuildMember>} The updated guild member */ export async function removeRoleFromMember(guildId, userId, roleId, reason) { // Get the guild const guild = await getClient().guilds.fetch(guildId); // Get the member const member = await guild.members.fetch(userId); if (!member) throw new Error(`Member with ID ${userId} not found in guild ${guild.name}`); // Get the role const role = await getRole(guildId, roleId); // Remove the role return member.roles.remove(role, reason || 'Role removed via roleUtils'); } /** * Check if a member has a role * @param {String} guildId The ID of the guild * @param {String} userId The ID of the user * @param {String} roleId The ID of the role * @returns {Promise<Boolean>} Whether the member has the role */ export async function memberHasRole(guildId, userId, roleId) { // Get the guild const guild = await getClient().guilds.fetch(guildId); // Get the member const member = await guild.members.fetch(userId).catch(() => null); if (!member) return false; return member.roles.cache.has(roleId); } /** * Get all members who have a specific role * @param {String} guildId The ID of the guild * @param {String} roleId The ID of the role * @returns {Promise<Array<GuildMember>>} Array of guild members with the role */ export async function getMembersWithRole(guildId, roleId) { const role = await getRole(guildId, roleId); // Fetch all guild members (warning: can be slow for large guilds) const guild = await getClient().guilds.fetch(guildId); await guild.members.fetch(); // Filter members who have the role return guild.members.cache .filter(member => member.roles.cache.has(roleId)) .toJSON(); } /** * Create a color role for a member (e.g. for name color) * @param {String} guildId The ID of the guild * @param {String} userId The ID of the user * @param {ColorResolvable} color The color to set * @param {Object} options Additional options * @param {Boolean} [options.removeOtherColorRoles=true] Whether to remove other color roles * @param {String} [options.rolePrefix='color-'] Prefix for color role names * @returns {Promise<Role>} The created/updated color role */ export async function setMemberColorRole(guildId, userId, color, options = {}) { // Get the guild const guild = await getClient().guilds.fetch(guildId); // Get the member const member = await guild.members.fetch(userId); if (!member) throw new Error(`Member with ID ${userId} not found in guild ${guild.name}`); // Default options const rolePrefix = options.rolePrefix || 'color-'; const removeOtherColorRoles = options.removeOtherColorRoles !== false; // Check for existing color role for this user const colorRoleName = `${rolePrefix}${userId}`; const existingRole = await findRoleByName(guildId, colorRoleName, { exactMatch: true }); // If we should remove other color roles if (removeOtherColorRoles) { const colorRoles = member.roles.cache.filter(role => role.name.startsWith(rolePrefix) && (!existingRole || role.id !== existingRole.id) ); if (colorRoles.size > 0) { await member.roles.remove(colorRoles, 'Removing old color roles'); } } // Update or create role if (existingRole) { await existingRole.edit({ color: color }, 'Updating color role'); // Make sure member has this role if (!member.roles.cache.has(existingRole.id)) { await member.roles.add(existingRole, 'Adding color role'); } return existingRole; } else { // Create new role and position it above the highest non-admin role const newRole = await createRole(guildId, { name: colorRoleName, color: color, reason: 'Creating color role for member' }); // Add role to member await member.roles.add(newRole, 'Adding new color role'); return newRole; } } /** * Get highest role position for a member * @param {String} guildId The ID of the guild * @param {String} userId The ID of the user * @returns {Promise<Number>} The position of the highest role the member has */ export async function getHighestRolePosition(guildId, userId) { // Get the guild const guild = await getClient().guilds.fetch(guildId); // Get the member const member = await guild.members.fetch(userId); return member.roles.highest.position; } /** * Check if one member can moderate another based on role hierarchy * @param {String} guildId The ID of the guild * @param {String} moderatorId The ID of the potential moderator * @param {String} targetId The ID of the potential target * @returns {Promise<Boolean>} Whether the moderator can moderate the target */ export async function canModerate(guildId, moderatorId, targetId) { // Get the guild const guild = await getClient().guilds.fetch(guildId); // Get the members const moderator = await guild.members.fetch(moderatorId); const target = await guild.members.fetch(targetId); // Can't moderate yourself or the server owner if (moderatorId === targetId || targetId === guild.ownerId) { return false; } // Server owner can moderate anyone if (moderatorId === guild.ownerId) { return true; } // Check role hierarchy (higher position can moderate lower) return moderator.roles.highest.position > target.roles.highest.position; }