Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | 6x 6x 6x 6x 39x 39x 39x 39x 39x 4x 4x 4x 4x 3x 1x 1x 1x 2x 2x 2x 2x 2x 1x 1x 1x 1x 10x 1x 1x 9x 3x 6x 3x 3x 1x 2x 1x 1x 14x 1x 1x 13x 6x 7x 3x 4x 1x 3x 2x 1x 4x 4x 4x 4x 2x 3x 3x 4x 4x 4x 2x 2x 2x 1x 1x 6x | const ProposalCommandHandler = require('./ProposalCommandHandler'); const EventCommandHandler = require('./EventCommandHandler'); const BotControlHandler = require('./BotControlHandler'); const AdminCommandHandler = require('./AdminCommandHandler'); /** * CommandRouter - Routes commands to appropriate domain-specific handlers * * Acts as the central dispatch system for all bot commands, replacing a monolithic approach * with cleaner separation of concerns. Each domain (proposals, events, admin) has its own * specialized handler, making the codebase more maintainable and testable. * * Design rationale: * - Channel-based permissions: Commands are allowed/restricted based on the channel they're sent in * - Role-based access control: Different commands require different permission levels * - Domain separation: Each functional area (governance, events, admin) has its own handler * - Consistent error handling: All command routing failures are handled uniformly */ class CommandRouter { constructor(bot) { this.bot = bot; // Initialize domain-specific handlers for clean separation of concerns // Each handler specializes in one functional area to maintain code clarity this.proposalHandler = new ProposalCommandHandler(bot); // Democratic governance commands this.eventHandler = new EventCommandHandler(bot); // Community event management this.botControlHandler = new BotControlHandler(bot); // Bot enable/disable controls this.adminHandler = new AdminCommandHandler(bot); // System administration commands } async handleCommand(message, isModeratorChannel = false) { try { console.log(`Handling command from ${message.author.tag} in ${isModeratorChannel ? 'moderator' : 'member'} channel`); const guild = message.guild; const member = guild.members.cache.get(message.author.id); if (!member) { console.log('Member not found in guild cache'); await message.reply('Error: Could not find your membership in this server.'); return; } const content = message.content.trim(); console.log(`Processing command: "${content}"`); // Determine user permissions for access control // Permission checks are done upfront to fail fast and provide clear feedback const isModerator = this.bot.getUserValidator().canUseModerator(member, this.bot.getModeratorRoleId()); const isMember = this.bot.getUserValidator().hasRole(member, this.bot.getMemberRoleId()); // Route to appropriate permission level handler // Channel context determines which commands are available, providing logical separation if (isModeratorChannel) { await this.handleModeratorCommand(message, member, content, isModerator); } else { await this.handleMemberCommand(message, member, content, isMember); } } catch (error) { console.error('Error handling command:', error); await message.reply('❌ An error occurred while processing your command.'); } } async handleModeratorCommand(message, member, content, isModerator) { // Check if user can use moderator commands if (!isModerator) { await message.reply('❌ You need the moderator role or "Manage Roles" permission to use commands in this channel.'); return; } // Route to appropriate handler based on command if (content.startsWith('!forcevote ') || content.startsWith('!voteinfo ') || content === '!proposals' || content === '!activevotes' || content === '!moderators') { await this.proposalHandler.handleModeratorCommand(message, member, content); } else if (content.startsWith('!addevent ') || content.startsWith('!quietaddevent ') || content.startsWith('!removeevent ') || content === '!events' || content.startsWith('!events ') || content === '!clearevents') { await this.eventHandler.handleModeratorCommand(message, member, content); } else if (content.startsWith('!boton ') || content.startsWith('!botoff ')) { await this.botControlHandler.handleModeratorCommand(message, member, content); } else if (content === '!ping' || content === '!help') { await this.adminHandler.handleModeratorCommand(message, member, content); } else { await message.reply('❓ Unknown moderator command. Type `!help` for available commands.'); } } async handleMemberCommand(message, member, content, isMember) { // Check if user is a member if (!isMember) { await message.reply('❌ You need the member role to use bot commands.'); return; } // Route to appropriate handler based on command if (content.startsWith('!propose ') || content === '!proposals' || content === '!activevotes' || content === '!moderators' || content.startsWith('!voteinfo ')) { await this.proposalHandler.handleMemberCommand(message, member, content); } else if (content.startsWith('!addevent ') || content.startsWith('!removeevent ') || content === '!events' || content.startsWith('!events ') || content === '!clearevents') { await this.eventHandler.handleMemberCommand(message, member, content); } else if (content.startsWith('!boton ') || content.startsWith('!botoff ')) { await this.botControlHandler.handleMemberCommand(message, member, content); } else if (content === '!ping' || content === '!help') { await this.adminHandler.handleMemberCommand(message, member, content); } else { await message.reply('❓ Unknown command. Type `!help` for available commands.'); } } // Utility methods used by handlers (moved from original CommandHandler) createProgressBar(current, required, length = 8) { const filled = Math.min(Math.floor((current / required) * length), length); const empty = length - filled; return '█'.repeat(filled) + '░'.repeat(empty); } formatUserMentions(text, guild) { if (!text || !guild) return text; // Replace user mentions <@123456> with @username return text.replace(/<@!?(\d+)>/g, (match, userId) => { const member = guild.members.cache.get(userId); return member ? `@${member.displayName}` : match; }); } calculateTimeRemaining(endTime) { const now = Date.now(); const remaining = endTime - now; if (remaining <= 0) return 'Voting ended'; const hours = Math.floor(remaining / (1000 * 60 * 60)); const minutes = Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) { return `${hours}h ${minutes}m`; } else { return `${minutes}m`; } } } module.exports = CommandRouter; |