const { Role, MessageAttachment } = require('discord.js'); const { Command } = require('../../../../interfaces/'); const { stripIndents } = require('common-tags'); class PermissionsCommand extends Command { constructor(client) { super(client, { name: 'permissions', module: 'administration', usage: "", aliases: [ 'perms', 'permission', 'perm' ], examples: [ "list", "Server Moderators", "@nolan#2887" ], arguments: [ { name: 'user', aliases: ['users'], type: 'BOOLEAN', types: ['VERBAL', 'FLAG'], default: true }, { name: 'raw', aliases: ['json'], 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.raw) { await this._displayRaw(message, permissions); return undefined; } if(params.length === 0) { await this._showPermissions(message, Boolean(args.user)); 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.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() }, 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.transactionHandler.send({ provider: 'mongodb', request: { type: 'updateOne', collection: 'permissions', query: { guildId: message.guild.id }, data: permissions } }); } catch(error) { this.client.logger.error(`Error removing channel permissions to ${message.guild.id}:\n${error.stack || error}`); } } return message.embed(embed); } async _showPermissions(message, user = false) { const embed = { author: { name: message.format('C_PERMISSIONS_SHOWTITLE', { user }, true), icon_url: message.guild.iconURL() //eslint-disable-line camelcase }, description: message.format('C_PERMISSIONS_SHOWDESCRIPTION', { resolve: user ? 'user' : 'role' }), fields: [] }; const permissions = message.guild._permissions; for(const [id, value] of Object.entries(permissions)) { if(id === '_id' || id === 'guildId') continue; const item = await this.client.resolver[user ? 'resolveUser' : 'resolveRole'](id, true, message.guild); //dont kill me if(item instanceof Role && user || !user && !(item instanceof Role) || !item) continue; if(embed.fields.length === 25) { embed.description += `\n${message.format('C_PERMISSIONS_MAXFIELDS')}`; break; } const name = item?.user?.tag || item?.tag || item?.name || id; //please dont kill me again const channels = Object.values(value.channels).length; if(channels === 0 && value.global.length === 0) { embed.description += `\n\n${message.format('C_PERMISSIONS_NOPERMISSIONS')}`; break; } embed.fields.push({ name, value: stripIndents`${this._displayNames(value.global).map((n) => `\`${n}\``).join('\n')} ${channels > 0 ? `\`..${channels} channel${channels === 1 ? '' : 's'}\`` : ''}` }); } return message.embed(embed); } async _listAvailablePermissions(message) { const components = this.client.registry.components.filter((c) => c.type === 'command' || c.type === 'module' && c.components.some((c) => c.type === 'command')) .sort((a, b) => a - b); return await 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, false, message.guild); if(!parsed) { parsed = await this.client.resolver.resolveMember(resolveable, false, message.guild); if(!parsed) { parsed = await this.client.resolver.resolveUser(resolveable, false); 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 await message.respond(message.format('C_PERMISSIONS_JSON'), { emoji: 'success', attachments: [ 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 ? names.push(module.resolveable) : names = names.concat(temp); temp = []; } return names; } } module.exports = PermissionsCommand;