diff --git a/src/plugins/Utility.ts b/src/plugins/Utility.ts index 3f1a98d7..408276a3 100644 --- a/src/plugins/Utility.ts +++ b/src/plugins/Utility.ts @@ -1,5 +1,5 @@ import { decorators as d } from "knub"; -import { Channel, EmbedOptions, Member, Message, TextChannel, User, VoiceChannel } from "eris"; +import { Channel, EmbedOptions, Member, Message, Role, TextChannel, User, VoiceChannel } from "eris"; import { chunkArray, embedPadding, @@ -87,32 +87,73 @@ export class UtilityPlugin extends ZeppelinPlugin { } } - @d.command("roles", "[search:string$]") + @d.command("roles", "[search:string$]", { + options: [ + { + name: "counts", + type: "bool", + }, + ], + }) @d.permission("roles") - async rolesCmd(msg: Message, args: { search?: string }) { - let roles = (msg.channel as TextChannel).guild.roles.map(role => `${role.name} ${role.id}`); + async rolesCmd(msg: Message, args: { search?: string; counts?: boolean }) { + let roles: Array<{ _memberCount?: number } & Role> = Array.from((msg.channel as TextChannel).guild.roles.values()); if (args.search) { const searchStr = args.search.toLowerCase(); - roles = roles.filter(r => r.toLowerCase().includes(searchStr)); + roles = roles.filter(r => r.name.toLowerCase().includes(searchStr) || r.id === searchStr); } - roles.sort((a, b) => { - if (a.toLowerCase() > b.toLowerCase()) return 1; - if (a.toLowerCase() < b.toLowerCase()) return -1; - return 0; - }); + if (args.counts) { + // If the user requested role member counts as well, calculate them and sort the roles by their member count + const roleCounts: Map = Array.from(this.guild.members.values()).reduce((map, member) => { + for (const roleId of member.roles) { + if (!map.has(roleId)) map.set(roleId, 0); + map.set(roleId, map.get(roleId) + 1); + } + + return map; + }, new Map()); + + // The "everyone" role always has all members in it + roleCounts.set(this.guildId, this.guild.memberCount); + + for (const role of roles) { + role._memberCount = roleCounts.has(role.id) ? roleCounts.get(role.id) : 0; + } + + roles.sort((a, b) => { + if (a._memberCount > b._memberCount) return -1; + if (a._memberCount < b._memberCount) return 1; + return 0; + }); + } else { + // Otherwise sort by name + roles.sort((a, b) => { + if (a.name.toLowerCase() > b.name.toLowerCase()) return 1; + if (a.name.toLowerCase() < b.name.toLowerCase()) return -1; + return 0; + }); + } const chunks = chunkArray(roles, 20); for (const [i, chunk] of chunks.entries()) { + const roleLines = chunk.map(role => { + let line = `${role.id} ${role.name}`; + if (role._memberCount != null) { + line += role._memberCount === 1 ? ` (${role._memberCount} member)` : ` (${role._memberCount} members)`; + } + return line; + }); + if (i === 0) { msg.channel.createMessage( trimLines(` ${args.search ? "Total roles found" : "Total roles"}: ${roles.length} - \`\`\`py\n${chunk.join("\n")}\`\`\` + \`\`\`py\n${roleLines.join("\n")}\`\`\` `), ); } else { - msg.channel.createMessage("```py\n" + chunk.join("\n") + "```"); + msg.channel.createMessage("```py\n" + roleLines.join("\n") + "```"); } } }