(Feat): Added a minimal pikanetwork client
This commit is contained in:
268
src/index.ts
Normal file
268
src/index.ts
Normal file
@@ -0,0 +1,268 @@
|
||||
/**
|
||||
* Elly Discord Bot
|
||||
* Main entry point
|
||||
*/
|
||||
|
||||
import { loadConfig, validateConfig, type ConfigValidationResult } from './config/config.ts';
|
||||
import { REST, Routes } from 'discord.js';
|
||||
import { EllyClient } from './client/EllyClient.ts';
|
||||
import { createLogger } from './utils/logger.ts';
|
||||
|
||||
// Import commands - Statistics
|
||||
import { bedwarsCommand } from './commands/statistics/bedwars.ts';
|
||||
import { skywarsCommand } from './commands/statistics/skywars.ts';
|
||||
import { guildCommand } from './commands/statistics/guild.ts';
|
||||
import { serverCommand } from './commands/statistics/server.ts';
|
||||
|
||||
// Import commands - Utility
|
||||
import { remindCommand } from './commands/utility/remind.ts';
|
||||
import { awayCommand } from './commands/utility/away.ts';
|
||||
import { suggestionsCommand } from './commands/suggestions/index.ts';
|
||||
import { championCommand } from './commands/utility/champion.ts';
|
||||
import { roleCommand } from './commands/utility/role.ts';
|
||||
import { qotdCommand } from './commands/qotd/index.ts';
|
||||
import { applicationsCommand } from './commands/applications/index.ts';
|
||||
import { staffCommand } from './commands/utility/staff.ts';
|
||||
|
||||
// Import commands - Family
|
||||
import { marryCommand } from './commands/family/marry.ts';
|
||||
import { divorceCommand } from './commands/family/divorce.ts';
|
||||
import { relationshipCommand } from './commands/family/relationship.ts';
|
||||
import { adoptCommand } from './commands/family/adopt.ts';
|
||||
|
||||
// Import commands - Moderation
|
||||
import { purgeCommand } from './commands/moderation/purge.ts';
|
||||
import { filterCommand } from './commands/moderation/filter.ts';
|
||||
|
||||
// Import commands - Developer
|
||||
import { reloadCommand } from './commands/developer/reload.ts';
|
||||
import { syncCommand } from './commands/developer/sync.ts';
|
||||
import { evalCommand } from './commands/developer/eval.ts';
|
||||
import { debugCommand } from './commands/developer/debug.ts';
|
||||
import { databaseCommand } from './commands/developer/database.ts';
|
||||
import { emitCommand } from './commands/developer/emit.ts';
|
||||
import { shellCommand } from './commands/developer/shell.ts';
|
||||
import { blacklistCommand } from './commands/developer/blacklist.ts';
|
||||
|
||||
// Import events
|
||||
import { messageCreateEvent } from './events/messageCreate.ts';
|
||||
|
||||
const logger = createLogger('Main');
|
||||
|
||||
async function main(): Promise<void> {
|
||||
console.log('');
|
||||
console.log('╔═══════════════════════════════════════════════════════════╗');
|
||||
console.log('║ 🌸 ELLY DISCORD BOT 🌸 ║');
|
||||
console.log('║ v1.0.0 - TypeScript ║');
|
||||
console.log('╚═══════════════════════════════════════════════════════════╝');
|
||||
console.log('');
|
||||
|
||||
logger.info('Starting Elly Discord Bot...');
|
||||
|
||||
// Load configuration
|
||||
logger.info('Loading configuration from config.toml...');
|
||||
let config;
|
||||
try {
|
||||
config = await loadConfig('./config.toml');
|
||||
|
||||
// Validate configuration
|
||||
const validation = validateConfig(config);
|
||||
|
||||
if (!validation.valid) {
|
||||
logger.error('✗ Configuration validation failed:');
|
||||
for (const error of validation.errors) {
|
||||
logger.error(` ✗ [${error.field}] ${error.message}`);
|
||||
}
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
// Log warnings
|
||||
if (validation.warnings.length > 0) {
|
||||
logger.warn(`⚠ Configuration has ${validation.warnings.length} warning(s):`);
|
||||
for (const warning of validation.warnings) {
|
||||
logger.warn(` ⚠ [${warning.field}] ${warning.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('✓ Configuration loaded and validated');
|
||||
logger.info(` ├─ Bot Name: ${config.bot.name}`);
|
||||
logger.info(` ├─ Guild: ${config.guild.name} (${config.guild.id})`);
|
||||
logger.info(` ├─ Database: ${config.database.path}`);
|
||||
logger.info(` └─ Owners: ${config.bot.owners?.ids?.length ?? 0} configured`);
|
||||
} catch (error) {
|
||||
logger.error('✗ Failed to load configuration', error);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
// Create client
|
||||
logger.info('Initializing Discord client...');
|
||||
const client = new EllyClient(config);
|
||||
logger.info('✓ Discord client created');
|
||||
|
||||
// Register commands
|
||||
logger.info('Registering commands...');
|
||||
const commands = [
|
||||
// Statistics
|
||||
{ cmd: bedwarsCommand, category: 'Statistics' },
|
||||
{ cmd: skywarsCommand, category: 'Statistics' },
|
||||
{ cmd: guildCommand, category: 'Statistics' },
|
||||
{ cmd: serverCommand, category: 'Statistics' },
|
||||
// Utility
|
||||
{ cmd: remindCommand, category: 'Utility' },
|
||||
{ cmd: awayCommand, category: 'Utility' },
|
||||
{ cmd: suggestionsCommand, category: 'Suggestions' },
|
||||
{ cmd: championCommand, category: 'Utility' },
|
||||
{ cmd: roleCommand, category: 'Utility' },
|
||||
{ cmd: qotdCommand, category: 'QOTD' },
|
||||
{ cmd: applicationsCommand, category: 'Applications' },
|
||||
{ cmd: staffCommand, category: 'Utility' },
|
||||
// Family
|
||||
{ cmd: marryCommand, category: 'Family' },
|
||||
{ cmd: divorceCommand, category: 'Family' },
|
||||
{ cmd: relationshipCommand, category: 'Family' },
|
||||
{ cmd: adoptCommand, category: 'Family' },
|
||||
// Moderation
|
||||
{ cmd: purgeCommand, category: 'Moderation' },
|
||||
{ cmd: filterCommand, category: 'Moderation' },
|
||||
// Developer
|
||||
{ cmd: reloadCommand, category: 'Developer' },
|
||||
{ cmd: syncCommand, category: 'Developer' },
|
||||
{ cmd: evalCommand, category: 'Developer' },
|
||||
{ cmd: debugCommand, category: 'Developer' },
|
||||
{ cmd: databaseCommand, category: 'Developer' },
|
||||
{ cmd: emitCommand, category: 'Developer' },
|
||||
{ cmd: shellCommand, category: 'Developer' },
|
||||
{ cmd: blacklistCommand, category: 'Developer' },
|
||||
];
|
||||
|
||||
for (const { cmd, category } of commands) {
|
||||
client.registerCommand(cmd);
|
||||
logger.debug(` ├─ /${cmd.data.name} [${category}]`);
|
||||
}
|
||||
logger.info(`✓ Registered ${client.commands.size} commands`);
|
||||
|
||||
// Register message event for filtering
|
||||
client.on(messageCreateEvent.name, (...args) => messageCreateEvent.execute(...args));
|
||||
logger.info('✓ Registered message filtering event');
|
||||
|
||||
// Initialize client
|
||||
try {
|
||||
await client.initialize();
|
||||
} catch (error) {
|
||||
logger.error('Failed to initialize client', error);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
// Set up interaction handler
|
||||
client.on('interactionCreate', async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
const command = client.commands.get(interaction.commandName);
|
||||
if (!command) return;
|
||||
|
||||
// Check cooldown
|
||||
const cooldownRemaining = client.isOnCooldown(interaction.user.id, interaction.commandName);
|
||||
if (cooldownRemaining > 0) {
|
||||
await interaction.reply({
|
||||
content: `Please wait ${cooldownRemaining} seconds before using this command again.`,
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Check permissions
|
||||
if (interaction.guild && interaction.member) {
|
||||
const member = interaction.guild.members.cache.get(interaction.user.id);
|
||||
if (member && !client.permissions.hasPermission(member, command.permission)) {
|
||||
await interaction.reply({
|
||||
content: client.permissions.formatDeniedMessage(command.permission),
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute command
|
||||
try {
|
||||
await command.execute(interaction);
|
||||
|
||||
// Set cooldown
|
||||
if (command.cooldown) {
|
||||
client.setCooldown(interaction.user.id, interaction.commandName, command.cooldown);
|
||||
}
|
||||
} catch (error) {
|
||||
// Use the centralized error handler
|
||||
await client.errorHandler.handleCommandError(error, interaction);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle shutdown
|
||||
const shutdown = async () => {
|
||||
logger.info('Received shutdown signal');
|
||||
await client.shutdown();
|
||||
Deno.exit(0);
|
||||
};
|
||||
|
||||
Deno.addSignalListener('SIGINT', shutdown);
|
||||
Deno.addSignalListener('SIGTERM', shutdown);
|
||||
|
||||
// Login
|
||||
const token = Deno.env.get('DISCORD_TOKEN');
|
||||
if (!token) {
|
||||
logger.error('DISCORD_TOKEN environment variable is not set');
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
await client.login(token);
|
||||
} catch (error) {
|
||||
logger.error('Failed to login', error);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
// Sync commands after login
|
||||
logger.info('Syncing slash commands...');
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
const rest = new REST({ version: '10' }).setToken(token);
|
||||
|
||||
// Build command data array
|
||||
const commandData = Array.from(client.commands.values()).map((cmd) => cmd.data.toJSON());
|
||||
|
||||
// Sync to guild (faster for development)
|
||||
const result = await rest.put(
|
||||
Routes.applicationGuildCommands(client.user!.id, config.guild.id),
|
||||
{ body: commandData }
|
||||
) as unknown[];
|
||||
|
||||
const syncTime = Date.now() - startTime;
|
||||
|
||||
logger.info('✓ Commands synced successfully');
|
||||
logger.info(` ├─ Commands: ${result.length} synced`);
|
||||
logger.info(` ├─ Guild: ${config.guild.name} (${config.guild.id})`);
|
||||
logger.info(` └─ Time: ${syncTime}ms`);
|
||||
|
||||
// Log command details
|
||||
const categories = new Map<string, string[]>();
|
||||
for (const { cmd, category } of commands) {
|
||||
if (!categories.has(category)) {
|
||||
categories.set(category, []);
|
||||
}
|
||||
categories.get(category)!.push(cmd.data.name);
|
||||
}
|
||||
|
||||
logger.debug('Command breakdown by category:');
|
||||
for (const [category, cmds] of categories) {
|
||||
logger.debug(` ├─ ${category}: ${cmds.join(', ')}`);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('✗ Failed to sync commands', error);
|
||||
// Don't exit - bot can still work with existing commands
|
||||
}
|
||||
}
|
||||
|
||||
// Run
|
||||
main().catch((error) => {
|
||||
logger.error('Unhandled error', error);
|
||||
Deno.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user