const { Role, MessageAttachment } = require('discord.js'); const { Command } = require('../../../../interfaces/'); const { Util, Emojis } = require('../../../../../util/'); const { stripIndents } = require('common-tags'); const Constants = { pageSize: 9 }; class PermissionsCommand extends Command { constructor(client) { super(client, { name: 'permissions', module: 'administration', usage: "['list'|role|user]", aliases: [ 'perms', 'permission', 'perm' ], examples: [ "list", "Server Moderators", "@nolan#2887" ], arguments: [ { name: 'export', type: 'BOOLEAN', types: ['VERBAL', 'FLAG'], default: true } ], // memberPermissions: ['ADMINISTRATOR', 'MANAGE_GUILD'], guildOnly: true }); } async execute(message, { params, args }) { const permissions = await message.guild.permissions(); if(args.export) { await this._displayRaw(message, permissions); return undefined; } if(params[0] === 'list') { await this._listAvailablePermissions(message); return undefined; } const parameters = params.join(' '); let resolveable = await this._parseResolveable(message, parameters); if(resolveable) { if(resolveable.user) resolveable = resolveable.user; const type = resolveable.tag ? 'user' : 'role'; const permission = permissions[resolveable?.id || parameters]; if(resolveable && !permission) { await message.respond(message.format('C_PERMISSIONS_PERMISSIONSNOTFOUND', { resolveable: resolveable.tag || resolveable.name, type, they: type === 'user' ? 'they ' : '' }), { emoji: 'failure' }); return undefined; } if(!permission) { await message.respond(message.format('C_PERMISSIONS_NOTFOUND'), { emoji: 'failure' }); return undefined; } const embed = { author: { name: `${resolveable?.user?.tag || resolveable?.tag || resolveable?.name || parameters}'s Permissions`, icon_url: resolveable.displayAvatarURL ? resolveable.displayAvatarURL() : message.guild.iconURL() //eslint-disable-line camelcase }, description: `${message.format('C_PERMISSIONS_GLOBAL', { permissions: permission.global.length > 0 ? this._displayNames(permission.global).map((p) => `\`${p}\``).join(', ') : "`N/A`" })} ${Object.values(permission.channels).length > 0 ? message.format('C_PERMISSIONS_GLOBALALT') : ''}`, fields: [] }; let update = false; for(const [channelId, perms] of Object.entries(permission.channels)) { const channel = await this.client.resolver.resolveChannel(channelId, true, message.guild); if(!channel) { delete permission.channels[channelId]; update = true; continue; } else { if(embed.fields.length === 25) { embed.description += `\n${message.format('C_PERMISSIONS_MAXFIELDS')}`; break; } embed.fields.push({ name: `#${channel.name}`, value: this._displayNames(perms).map((p) => `\`${p}\``).join(', ') }); } } if(update) { delete permissions._id; try { await this.client.storageManager.mongodb.permissions.updateOne( { guildId: message.guild.id }, permissions ); } catch(error) { this.client.logger.error(`Error removing channel permissions to ${message.guild.id}:\n${error.stack || error}`); } } return message.embed(embed); } //End of displaying user/role permissions. const _permissions = []; for(const [key, value] of Object.entries(permissions)) { if(value?.global?.length === 0 && Object.keys(value?.channels).length === 0) { await this._deletePermission(message.guild, key); continue; } if(!Number.isNaN(parseInt(key))) _permissions.push({ ...value, id: key }); } let currentPage = 1; if(parameters.length > 0) { const number = parseInt(parameters[0]); if(!Number.isNaN(number) && number > 1) { currentPage = number; } } const size = _permissions.length; if(size === 0) { return message.respond(message.format('C_PERMISSIONS_NOPERMISSIONS'), { emoji: 'failure' }); } let { items, page, maxPage } = Util.paginate(_permissions, currentPage, Constants.pageSize); //eslint-disable-line prefer-const const embed = { author: { name: `Guild Permissions`, icon_url: message.guild.iconURL() //eslint-disable-line camelcase }, fields: [], footer: { text: `• Page ${page}/${maxPage} | ${size} Results` } }; for(const item of items) { item.resolveable = await this._parseResolveable(message, item.id); item.permissions = this._displayNames(item.global); } const display = (items) => { items = items.sort((a, b) => b.permissions.length - a.permissions.length); for(const item of items) { const field = { name: `${item.resolveable instanceof Role ? Emojis.role : Emojis.member} ${Util.escapeMarkdown(item.resolveable.display)}`, value: item.permissions.map((n) => `\`${n}\``).join('\n'), inline: true }; const channels = Object.keys(item.channels).length; if(channels > 0) { field.value += `\n\`..${channels} channel${channels === 1 ? '' : 's'}\``; } embed.fields.push(field); } }; display(items.filter((i) => i.resolveable instanceof Role)); display(items.filter((i) => !(i.resolveable instanceof Role))); const empty = items.length % 3; for(let i = 0; i c.type === 'command' || c.type === 'module' && c.components.some((c) => c.type === 'command')) .sort((a, b) => a - b); return message.respond(message.format('C_PERMISSIONS_LIST', { permissions: components.map((c) => `\`${c.resolveable}\``).join(', ') }), { emoji: 'success' }); } async _parseResolveable(message, resolveable) { let parsed = await this.client.resolver.resolveRole(resolveable, true, message.guild); if(!parsed) { parsed = await this.client.resolver.resolveMember(resolveable, true, message.guild); if(!parsed) { parsed = await this.client.resolver.resolveUser(resolveable, true); if(!parsed) return null; } } return parsed; } async _displayRaw(message, permissions) { const string = JSON.stringify(permissions); const attachment = new MessageAttachment(Buffer.from(string), "permissions.json"); return message.respond(message.format('C_PERMISSIONS_JSON'), { emoji: 'success', files: [ attachment ] }); } _displayNames(permissions) { const modules = this.client.registry.components.filter((c) => c.type === 'module'); let names = []; let temp = []; for(const module of modules.values()) { for(const component of module.components.filter((c) => c.type === 'command').values()) { if(permissions.includes(component.resolveable)) { temp.push(component.resolveable); } } temp.length === module.components.filter((c) => c.type === 'command').size //eslint-disable-line no-unused-expressions ? names.push(module.resolveable) : names = names.concat(temp); temp = []; } return names; } async _deletePermission(guild, key) { const permissions = guild._permissions; delete permissions[key]; delete permissions._id; try { await this.client.storageManager.mongodb.permissions.updateOne( { guildId: guild.id }, permissions ); } catch(error) { return false; } return true; } } module.exports = PermissionsCommand;