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 | 3x 3x 3x 3x 3x 35x 35x 35x 35x 35x 35x 35x 35x 35x 35x 35x 35x 35x 35x 35x 35x 35x 11x 11x 10x 10x 9x 9x 8x 3x 3x 10x 10x 10x 10x 10x 10x 10x 10x 10x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 5x 4x 6x 5x 3x 2x 2x 2x 3x | const { Client, GatewayIntentBits } = require('discord.js');
const ConfigurationResolver = require('./core/ConfigurationResolver');
const ComponentOrchestrator = require('./core/ComponentOrchestrator');
const BotLifecycleManager = require('./core/BotLifecycleManager');
const BotStateController = require('./core/BotStateController');
/**
* DiscordReactionBot - Main bot coordinator class
* Orchestrates specialized components for a clean, maintainable architecture
* Now focused purely on coordination and high-level bot lifecycle management
*/
class DiscordReactionBot {
constructor() {
// Discord client with necessary intents for reaction roles and proposal system
// These specific intents allow reading reactions, messages, and managing member roles
this.client = new Client({
intents: [
GatewayIntentBits.Guilds, // Access to guild info
GatewayIntentBits.GuildMessages, // Read messages for commands
GatewayIntentBits.GuildMessageReactions, // Monitor reaction events
GatewayIntentBits.MessageContent, // Access message text for proposal parsing
GatewayIntentBits.GuildMembers // Role management capabilities
]
});
// Runtime configuration loaded from deployment
// These values come from terraform and vary per deployment environment
this.config = null;
this.guildId = null;
this.botToken = null;
this.runId = null;
this.moderatorRoleId = null;
this.memberRoleId = null;
this.commandChannelId = null;
this.memberCommandChannelId = null;
this.eventsTable = null;
this.reminderIntervals = null;
// Initialize specialized component managers
this.configResolver = new ConfigurationResolver();
this.componentOrchestrator = new ComponentOrchestrator(this);
this.lifecycleManager = new BotLifecycleManager(this);
this.stateController = new BotStateController(this.client);
// Initialize components and setup event handlers
this.componentOrchestrator.initializeComponents();
this.lifecycleManager.setupEventHandlers();
}
/**
* Initialize the bot with runtime configuration and start Discord connection
*/
async initialize() {
try {
// Load and validate runtime configuration
const runtimeConfig = await this.configResolver.loadConfiguration();
// Extract configuration values
this.extractConfigurationValues(runtimeConfig);
// Initialize components that require configuration
await this.componentOrchestrator.initializeConfigurableComponents(runtimeConfig);
// Connect to Discord and start processing events
// Bot becomes active and responsive after this point
console.log('Logging into Discord...');
await this.client.login(this.botToken);
console.log('Bot initialized successfully');
} catch (error) {
console.error('Failed to initialize bot:', error);
process.exit(1);
}
}
/**
* Extract configuration values from runtime config
*/
extractConfigurationValues(runtimeConfig) {
// Extract Discord-specific configuration values
// These IDs are unique to each Discord server and deployment
this.guildId = runtimeConfig.guildId;
this.botToken = runtimeConfig.botToken;
this.runId = runtimeConfig.runId || 'unknown';
this.moderatorRoleId = runtimeConfig.moderatorRoleId;
this.memberRoleId = runtimeConfig.memberRoleId;
this.commandChannelId = runtimeConfig.commandChannelId;
this.memberCommandChannelId = runtimeConfig.memberCommandChannelId;
this.eventsTable = runtimeConfig.eventsTable;
this.reminderIntervals = runtimeConfig.reminderIntervals;
}
// Getter methods for controlled access to bot configuration and components
// These provide a clean interface for other modules to access bot state
// without exposing the internal structure or allowing direct modification
getGuildId() { return this.guildId; }
getRunId() { return this.runId; }
getModeratorRoleId() { return this.moderatorRoleId; }
getMemberRoleId() { return this.memberRoleId; }
getCommandChannelId() { return this.commandChannelId; }
getMemberCommandChannelId() { return this.memberCommandChannelId; }
getEventsTable() { return this.eventsTable; }
getReminderIntervals() { return this.reminderIntervals; }
// Component access methods
getConfig() { return this.configManager.getConfig(); }
getConfigManager() { return this.configManager; }
getProposalManager() { return this.proposalManager; }
getUserValidator() { return this.userValidator; }
getEventManager() { return this.eventManager; }
// Bot state management delegation
enableBot(botId) {
return this.stateController.enableBot(botId);
}
disableBot(botId) {
return this.stateController.disableBot(botId);
}
isBotEnabled(botId) {
return this.stateController.isBotEnabled(botId);
}
isThisBotEnabled() {
return this.stateController.isThisBotEnabled();
}
getBotId() {
return this.stateController.getBotId();
}
/**
* Cleanup method for graceful shutdown
*/
async cleanup() {
await this.componentOrchestrator.cleanup();
this.client.destroy();
console.log('✅ DiscordReactionBot cleaned up');
}
}
module.exports = DiscordReactionBot; |