From 76cad00dd917c86c45e6c1881f36ea018df6bd1b Mon Sep 17 00:00:00 2001 From: sirpiglr <49359077-sirpiglr@users.noreply.replit.com> Date: Wed, 10 Dec 2025 01:41:25 +0000 Subject: [PATCH] Enhance bot with federation protection and new command features Introduces federation protection listener, expands federation commands, adds federation API endpoints to web server, and updates bot status to 'Protecting the Federation'. Replit-Commit-Author: Agent Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: d254ee4b-e69e-44a5-b1be-b89c088a485a Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/FUs0R2K Replit-Helium-Checkpoint-Created: true --- .replit | 4 + aethex-bot/bot.js | 45 +- aethex-bot/commands/federation.js | 788 +++++++++++++++--- aethex-bot/listeners/federationProtection.js | 118 +++ aethex-bot/public/federation.html | 625 ++++++++++++++ aethex-bot/server/webServer.js | 190 +++++ ...gy-You-aren-t-selling-th_1765329237794.txt | 46 + 7 files changed, 1676 insertions(+), 140 deletions(-) create mode 100644 aethex-bot/listeners/federationProtection.js create mode 100644 aethex-bot/public/federation.html create mode 100644 attached_assets/Pasted-This-is-a-Network-Effect-strategy-You-aren-t-selling-th_1765329237794.txt diff --git a/.replit b/.replit index 9585c0f..696ae75 100644 --- a/.replit +++ b/.replit @@ -21,6 +21,10 @@ externalPort = 80 localPort = 8080 externalPort = 8080 +[[ports]] +localPort = 45655 +externalPort = 3000 + [workflows] runButton = "Project" diff --git a/aethex-bot/bot.js b/aethex-bot/bot.js index 7ecb390..5f226de 100644 --- a/aethex-bot/bot.js +++ b/aethex-bot/bot.js @@ -718,7 +718,7 @@ if (fs.existsSync(sentinelPath)) { // ============================================================================= const listenersPath = path.join(__dirname, "listeners"); -const generalListenerFiles = ['welcome.js', 'goodbye.js', 'xpTracker.js', 'reactionXp.js', 'voiceXp.js', 'starboard.js']; +const generalListenerFiles = ['welcome.js', 'goodbye.js', 'xpTracker.js', 'reactionXp.js', 'voiceXp.js', 'starboard.js', 'federationProtection.js']; for (const file of generalListenerFiles) { const filePath = path.join(listenersPath, file); if (fs.existsSync(filePath)) { @@ -2500,33 +2500,22 @@ client.login(token).catch((error) => { }); // ============================================================================= -// DYNAMIC STATUS ROTATION +// BOT STATUS - WATCHING PROTECTING THE FEDERATION // ============================================================================= -function startDynamicStatus(client) { - const statuses = [ - () => ({ name: `${client.guilds.cache.size} servers`, type: 3 }), // Watching - () => ({ name: `${client.guilds.cache.reduce((sum, g) => sum + g.memberCount, 0).toLocaleString()} members`, type: 3 }), // Watching - () => ({ name: '/help | aethex.studio', type: 0 }), // Playing - () => ({ name: '🛡️ Guarding the Federation', type: 4 }), // Custom - () => ({ name: 'for threats', type: 3 }), // Watching - () => ({ name: '⚔️ Warden • Free Forever', type: 4 }), // Custom - ]; - - let currentIndex = 0; - - const updateStatus = () => { - try { - const status = statuses[currentIndex](); - client.user.setActivity(status.name, { type: status.type }); - currentIndex = (currentIndex + 1) % statuses.length; - } catch (e) { - console.error('[Status] Error updating status:', e.message); - } - }; - - updateStatus(); - setInterval(updateStatus, 30000); // Rotate every 30 seconds +function setWardenStatus(client) { + try { + client.user.setPresence({ + activities: [{ + name: 'Protecting the Federation', + type: 3 // ActivityType.Watching + }], + status: 'online' + }); + console.log('[Status] Set to: WATCHING Protecting the Federation'); + } catch (e) { + console.error('[Status] Error setting status:', e.message); + } } client.once("clientReady", async () => { @@ -2550,8 +2539,8 @@ client.once("clientReady", async () => { console.error("Failed to register commands:", regResult.error); } - // Dynamic rotating status - startDynamicStatus(client); + // Static status: WATCHING Protecting the Federation + setWardenStatus(client); if (setupFeedListener && supabase) { setupFeedListener(client); diff --git a/aethex-bot/commands/federation.js b/aethex-bot/commands/federation.js index 678aa32..504c077 100644 --- a/aethex-bot/commands/federation.js +++ b/aethex-bot/commands/federation.js @@ -1,132 +1,696 @@ -const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js'); +const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js'); const { getServerMode, EMBED_COLORS } = require('../utils/modeHelper'); module.exports = { data: new SlashCommandBuilder() .setName('federation') - .setDescription('Manage cross-server role synchronization') - .setDefaultMemberPermissions(PermissionFlagsBits.ManageRoles) - .addSubcommand(subcommand => - subcommand - .setName('link') - .setDescription('Link a role for cross-server sync') - .addRoleOption(option => - option.setName('role') - .setDescription('Role to sync across servers') - .setRequired(true) - ) + .setDescription('AeThex Federation - Global protection & cross-server network') + .addSubcommandGroup(group => + group + .setName('roles') + .setDescription('Manage cross-server role synchronization') + .addSubcommand(sub => sub.setName('link').setDescription('Link a role for cross-server sync') + .addRoleOption(opt => opt.setName('role').setDescription('Role to sync').setRequired(true))) + .addSubcommand(sub => sub.setName('unlink').setDescription('Remove a role from sync') + .addRoleOption(opt => opt.setName('role').setDescription('Role to remove').setRequired(true))) + .addSubcommand(sub => sub.setName('list').setDescription('List all linked roles')) ) - .addSubcommand(subcommand => - subcommand - .setName('unlink') - .setDescription('Remove a role from sync') - .addRoleOption(option => - option.setName('role') - .setDescription('Role to remove from sync') - .setRequired(true) - ) + .addSubcommandGroup(group => + group + .setName('bans') + .setDescription('Global Ban List - Protect the network') + .addSubcommand(sub => sub.setName('add').setDescription('Add user to global ban list') + .addUserOption(opt => opt.setName('user').setDescription('User to ban').setRequired(true)) + .addStringOption(opt => opt.setName('reason').setDescription('Reason for ban').setRequired(true)) + .addStringOption(opt => opt.setName('severity').setDescription('Severity level') + .addChoices( + { name: 'Low - Minor offense', value: 'low' }, + { name: 'Medium - Moderate offense', value: 'medium' }, + { name: 'High - Serious offense', value: 'high' }, + { name: 'Critical - Nuker/Scammer', value: 'critical' } + ))) + .addSubcommand(sub => sub.setName('lookup').setDescription('Check if user is on global ban list') + .addUserOption(opt => opt.setName('user').setDescription('User to lookup').setRequired(true))) + .addSubcommand(sub => sub.setName('list').setDescription('View recent global bans')) + .addSubcommand(sub => sub.setName('remove').setDescription('Remove user from global ban list') + .addUserOption(opt => opt.setName('user').setDescription('User to remove').setRequired(true))) ) - .addSubcommand(subcommand => - subcommand - .setName('list') - .setDescription('List all linked roles') + .addSubcommandGroup(group => + group + .setName('servers') + .setDescription('Federation Server Directory') + .addSubcommand(sub => sub.setName('directory').setDescription('Browse all federation servers')) + .addSubcommand(sub => sub.setName('info').setDescription('View a specific server') + .addStringOption(opt => opt.setName('server').setDescription('Server name or ID').setRequired(true))) + .addSubcommand(sub => sub.setName('featured').setDescription('View featured servers')) + ) + .addSubcommandGroup(group => + group + .setName('membership') + .setDescription('Join or manage federation membership') + .addSubcommand(sub => sub.setName('apply').setDescription('Apply to join the federation') + .addStringOption(opt => opt.setName('category').setDescription('Server category') + .addChoices( + { name: 'Gaming', value: 'gaming' }, + { name: 'Creative', value: 'creative' }, + { name: 'Development', value: 'development' }, + { name: 'Education', value: 'education' }, + { name: 'Community', value: 'community' }, + { name: 'Business', value: 'business' } + ).setRequired(true)) + .addStringOption(opt => opt.setName('description').setDescription('Brief description of your server').setRequired(true))) + .addSubcommand(sub => sub.setName('status').setDescription('Check your server\'s federation status')) + .addSubcommand(sub => sub.setName('treaty').setDescription('View the Federation Treaty')) + ) + .addSubcommandGroup(group => + group + .setName('scouts') + .setDescription('Talent Scout - Cross-server reputation') + .addSubcommand(sub => sub.setName('leaderboard').setDescription('View global reputation leaderboard')) + .addSubcommand(sub => sub.setName('profile').setDescription('View cross-server profile') + .addUserOption(opt => opt.setName('user').setDescription('User to lookup'))) ), async execute(interaction, supabase, client) { const mode = await getServerMode(supabase, interaction.guildId); + const group = interaction.options.getSubcommandGroup(); + const subcommand = interaction.options.getSubcommand(); - if (mode === 'standalone') { + if (mode === 'standalone' && group !== 'membership') { const embed = new EmbedBuilder() .setColor(EMBED_COLORS.standalone) - .setTitle('🏠 Standalone Mode') - .setDescription('Federation features are disabled in standalone mode.\n\nThis server operates independently and does not sync roles across the AeThex network.\n\nUse `/config mode` to switch to federated mode.'); + .setTitle('Standalone Mode') + .setDescription('Federation features are disabled in standalone mode.\n\nUse `/federation membership apply` to join the network, or `/config mode` to switch to federated mode.'); return interaction.reply({ embeds: [embed], ephemeral: true }); } - const subcommand = interaction.options.getSubcommand(); - - if (subcommand === 'link') { - const role = interaction.options.getRole('role'); - - const mappingData = { - name: role.name, - guildId: interaction.guildId, - guildName: interaction.guild.name, - linkedAt: Date.now(), - }; - client.federationMappings.set(role.id, mappingData); - - if (client.saveFederationMapping) { - await client.saveFederationMapping(role.id, mappingData); - } - - const embed = new EmbedBuilder() - .setColor(0x00ff00) - .setTitle('Role Linked') - .setDescription(`${role} is now linked for federation sync.`) - .addFields( - { name: 'Role ID', value: role.id, inline: true }, - { name: 'Server', value: interaction.guild.name, inline: true } - ) - .setTimestamp(); - - await interaction.reply({ embeds: [embed] }); - } - - if (subcommand === 'unlink') { - const role = interaction.options.getRole('role'); - - if (client.federationMappings.has(role.id)) { - client.federationMappings.delete(role.id); - - if (client.deleteFederationMapping) { - await client.deleteFederationMapping(role.id); - } - - const embed = new EmbedBuilder() - .setColor(0xff6600) - .setTitle('Role Unlinked') - .setDescription(`${role} has been removed from federation sync.`) - .setTimestamp(); - - await interaction.reply({ embeds: [embed] }); - } else { - const embed = new EmbedBuilder() - .setColor(0xff0000) - .setTitle('Not Found') - .setDescription(`${role} is not currently linked.`) - .setTimestamp(); - - await interaction.reply({ embeds: [embed], ephemeral: true }); - } - } - - if (subcommand === 'list') { - const mappings = [...client.federationMappings.entries()]; - - if (mappings.length === 0) { - const embed = new EmbedBuilder() - .setColor(0x7c3aed) - .setTitle('Federation Roles') - .setDescription('No roles are currently linked for federation sync.\nUse `/federation link` to add roles.') - .setTimestamp(); - - await interaction.reply({ embeds: [embed] }); - return; - } - - const roleList = mappings.map(([roleId, data]) => - `<@&${roleId}> - ${data.guildName}` - ).join('\n'); - - const embed = new EmbedBuilder() - .setColor(0x7c3aed) - .setTitle('Federation Roles') - .setDescription(roleList) - .setFooter({ text: `${mappings.length} role(s) linked` }) - .setTimestamp(); - - await interaction.reply({ embeds: [embed] }); + if (group === 'roles') { + await handleRoles(interaction, supabase, client, subcommand); + } else if (group === 'bans') { + await handleBans(interaction, supabase, client, subcommand); + } else if (group === 'servers') { + await handleServers(interaction, supabase, client, subcommand); + } else if (group === 'membership') { + await handleMembership(interaction, supabase, client, subcommand); + } else if (group === 'scouts') { + await handleScouts(interaction, supabase, client, subcommand); } }, }; + +async function handleRoles(interaction, supabase, client, subcommand) { + if (subcommand === 'link') { + if (!interaction.member.permissions.has(PermissionFlagsBits.ManageRoles)) { + return interaction.reply({ content: 'You need Manage Roles permission.', ephemeral: true }); + } + + const role = interaction.options.getRole('role'); + const mappingData = { + name: role.name, + guildId: interaction.guildId, + guildName: interaction.guild.name, + linkedAt: Date.now(), + }; + client.federationMappings.set(role.id, mappingData); + + if (client.saveFederationMapping) { + await client.saveFederationMapping(role.id, mappingData); + } + + const embed = new EmbedBuilder() + .setColor(0x00ff00) + .setTitle('Role Linked') + .setDescription(`${role} is now linked for federation sync.`) + .addFields( + { name: 'Role ID', value: role.id, inline: true }, + { name: 'Server', value: interaction.guild.name, inline: true } + ) + .setTimestamp(); + + await interaction.reply({ embeds: [embed] }); + } + + if (subcommand === 'unlink') { + if (!interaction.member.permissions.has(PermissionFlagsBits.ManageRoles)) { + return interaction.reply({ content: 'You need Manage Roles permission.', ephemeral: true }); + } + + const role = interaction.options.getRole('role'); + + if (client.federationMappings.has(role.id)) { + client.federationMappings.delete(role.id); + if (client.deleteFederationMapping) { + await client.deleteFederationMapping(role.id); + } + + const embed = new EmbedBuilder() + .setColor(0xff6600) + .setTitle('Role Unlinked') + .setDescription(`${role} has been removed from federation sync.`) + .setTimestamp(); + + await interaction.reply({ embeds: [embed] }); + } else { + await interaction.reply({ content: `${role} is not currently linked.`, ephemeral: true }); + } + } + + if (subcommand === 'list') { + const mappings = [...client.federationMappings.entries()]; + + if (mappings.length === 0) { + const embed = new EmbedBuilder() + .setColor(0x7c3aed) + .setTitle('Federation Roles') + .setDescription('No roles are currently linked.\nUse `/federation roles link` to add roles.') + .setTimestamp(); + return interaction.reply({ embeds: [embed] }); + } + + const roleList = mappings.map(([roleId, data]) => + `<@&${roleId}> - ${data.guildName}` + ).join('\n'); + + const embed = new EmbedBuilder() + .setColor(0x7c3aed) + .setTitle('Federation Roles') + .setDescription(roleList) + .setFooter({ text: `${mappings.length} role(s) linked` }) + .setTimestamp(); + + await interaction.reply({ embeds: [embed] }); + } +} + +async function handleBans(interaction, supabase, client, subcommand) { + if (!supabase) { + return interaction.reply({ content: 'Database not available.', ephemeral: true }); + } + + if (subcommand === 'add') { + if (!interaction.member.permissions.has(PermissionFlagsBits.BanMembers)) { + return interaction.reply({ content: 'You need Ban Members permission.', ephemeral: true }); + } + + const user = interaction.options.getUser('user'); + const reason = interaction.options.getString('reason'); + const severity = interaction.options.getString('severity') || 'medium'; + + const { data: existing } = await supabase + .from('federation_bans') + .select('id') + .eq('user_id', user.id) + .eq('active', true) + .maybeSingle(); + + if (existing) { + return interaction.reply({ content: `${user.tag} is already on the global ban list.`, ephemeral: true }); + } + + const { error } = await supabase.from('federation_bans').insert({ + user_id: user.id, + username: user.tag, + reason, + severity, + banned_by_guild_id: interaction.guildId, + banned_by_user_id: interaction.user.id, + }); + + if (error) { + console.error('Federation ban error:', error); + return interaction.reply({ content: 'Failed to add ban.', ephemeral: true }); + } + + const severityColors = { low: 0xffff00, medium: 0xff9900, high: 0xff3300, critical: 0xff0000 }; + const severityEmojis = { low: '⚠️', medium: '🔶', high: '🔴', critical: '☠️' }; + + const embed = new EmbedBuilder() + .setColor(severityColors[severity]) + .setTitle(`${severityEmojis[severity]} Global Ban Added`) + .setThumbnail(user.displayAvatarURL()) + .addFields( + { name: 'User', value: `${user.tag}\n\`${user.id}\``, inline: true }, + { name: 'Severity', value: severity.toUpperCase(), inline: true }, + { name: 'Reason', value: reason } + ) + .setFooter({ text: `Added by ${interaction.user.tag}` }) + .setTimestamp(); + + await interaction.reply({ embeds: [embed] }); + + await createBanAlerts(supabase, client, user.id, severity); + } + + if (subcommand === 'lookup') { + const user = interaction.options.getUser('user'); + + const { data: ban } = await supabase + .from('federation_bans') + .select('*') + .eq('user_id', user.id) + .eq('active', true) + .maybeSingle(); + + if (!ban) { + const embed = new EmbedBuilder() + .setColor(0x00ff00) + .setTitle('User Clear') + .setThumbnail(user.displayAvatarURL()) + .setDescription(`${user.tag} is **not** on the global ban list.`) + .setTimestamp(); + return interaction.reply({ embeds: [embed] }); + } + + const severityColors = { low: 0xffff00, medium: 0xff9900, high: 0xff3300, critical: 0xff0000 }; + + const embed = new EmbedBuilder() + .setColor(severityColors[ban.severity]) + .setTitle('User Flagged') + .setThumbnail(user.displayAvatarURL()) + .addFields( + { name: 'User', value: `${user.tag}\n\`${user.id}\``, inline: true }, + { name: 'Severity', value: ban.severity.toUpperCase(), inline: true }, + { name: 'Reason', value: ban.reason }, + { name: 'Banned On', value: new Date(ban.created_at).toLocaleDateString(), inline: true } + ) + .setTimestamp(); + + await interaction.reply({ embeds: [embed] }); + } + + if (subcommand === 'list') { + const { data: bans } = await supabase + .from('federation_bans') + .select('*') + .eq('active', true) + .order('created_at', { ascending: false }) + .limit(10); + + if (!bans || bans.length === 0) { + return interaction.reply({ content: 'No active global bans.', ephemeral: true }); + } + + const severityEmojis = { low: '⚠️', medium: '🔶', high: '🔴', critical: '☠️' }; + + const banList = bans.map(b => + `${severityEmojis[b.severity]} **${b.username || b.user_id}** - ${b.reason.substring(0, 50)}${b.reason.length > 50 ? '...' : ''}` + ).join('\n'); + + const embed = new EmbedBuilder() + .setColor(0xff0000) + .setTitle('Global Ban List') + .setDescription(banList) + .setFooter({ text: `Showing ${bans.length} most recent bans` }) + .setTimestamp(); + + await interaction.reply({ embeds: [embed] }); + } + + if (subcommand === 'remove') { + if (!interaction.member.permissions.has(PermissionFlagsBits.Administrator)) { + return interaction.reply({ content: 'You need Administrator permission.', ephemeral: true }); + } + + const user = interaction.options.getUser('user'); + + const { error } = await supabase + .from('federation_bans') + .update({ active: false, updated_at: new Date().toISOString() }) + .eq('user_id', user.id) + .eq('active', true); + + if (error) { + return interaction.reply({ content: 'Failed to remove ban.', ephemeral: true }); + } + + const embed = new EmbedBuilder() + .setColor(0x00ff00) + .setTitle('Ban Removed') + .setDescription(`${user.tag} has been removed from the global ban list.`) + .setTimestamp(); + + await interaction.reply({ embeds: [embed] }); + } +} + +async function handleServers(interaction, supabase, client, subcommand) { + if (!supabase) { + return interaction.reply({ content: 'Database not available.', ephemeral: true }); + } + + if (subcommand === 'directory') { + const { data: servers } = await supabase + .from('federation_servers') + .select('*') + .eq('status', 'approved') + .order('member_count', { ascending: false }) + .limit(15); + + if (!servers || servers.length === 0) { + const embed = new EmbedBuilder() + .setColor(0x7c3aed) + .setTitle('Federation Directory') + .setDescription('No servers in the federation yet.\nUse `/federation membership apply` to be the first!') + .setTimestamp(); + return interaction.reply({ embeds: [embed] }); + } + + const categoryEmojis = { gaming: '🎮', creative: '🎨', development: '💻', education: '📚', community: '👥', business: '🏢' }; + + const serverList = servers.map((s, i) => { + const emoji = categoryEmojis[s.category] || '🌐'; + const featured = s.featured ? '⭐' : ''; + return `${i + 1}. ${emoji} **${s.guild_name}** ${featured}\n ${s.member_count?.toLocaleString() || '?'} members • ${s.category || 'general'}`; + }).join('\n\n'); + + const embed = new EmbedBuilder() + .setColor(0x7c3aed) + .setTitle('Federation Directory') + .setDescription(serverList) + .setFooter({ text: `${servers.length} servers in the federation` }) + .setTimestamp(); + + await interaction.reply({ embeds: [embed] }); + } + + if (subcommand === 'info') { + const serverQuery = interaction.options.getString('server'); + + const { data: server } = await supabase + .from('federation_servers') + .select('*') + .or(`guild_id.eq.${serverQuery},guild_name.ilike.%${serverQuery}%`) + .eq('status', 'approved') + .maybeSingle(); + + if (!server) { + return interaction.reply({ content: 'Server not found in the federation.', ephemeral: true }); + } + + const embed = new EmbedBuilder() + .setColor(0x7c3aed) + .setTitle(server.guild_name) + .setDescription(server.description || 'No description provided.') + .addFields( + { name: 'Members', value: server.member_count?.toLocaleString() || 'Unknown', inline: true }, + { name: 'Category', value: server.category || 'General', inline: true }, + { name: 'Tier', value: server.tier?.toUpperCase() || 'FREE', inline: true }, + { name: 'Joined Federation', value: new Date(server.joined_at).toLocaleDateString(), inline: true } + ) + .setTimestamp(); + + if (server.guild_icon) { + embed.setThumbnail(`https://cdn.discordapp.com/icons/${server.guild_id}/${server.guild_icon}.png`); + } + + await interaction.reply({ embeds: [embed] }); + } + + if (subcommand === 'featured') { + const { data: featured } = await supabase + .from('federation_servers') + .select('*') + .eq('featured', true) + .eq('status', 'approved'); + + if (!featured || featured.length === 0) { + return interaction.reply({ content: 'No featured servers at this time.', ephemeral: true }); + } + + const serverList = featured.map(s => + `⭐ **${s.guild_name}**\n${s.description?.substring(0, 100) || 'No description'}${s.description?.length > 100 ? '...' : ''}` + ).join('\n\n'); + + const embed = new EmbedBuilder() + .setColor(0xffd700) + .setTitle('Featured Servers') + .setDescription(serverList) + .setTimestamp(); + + await interaction.reply({ embeds: [embed] }); + } +} + +async function handleMembership(interaction, supabase, client, subcommand) { + if (!supabase) { + return interaction.reply({ content: 'Database not available.', ephemeral: true }); + } + + if (subcommand === 'apply') { + if (!interaction.member.permissions.has(PermissionFlagsBits.Administrator)) { + return interaction.reply({ content: 'Only administrators can apply to the federation.', ephemeral: true }); + } + + const { data: existing } = await supabase + .from('federation_servers') + .select('status') + .eq('guild_id', interaction.guildId) + .maybeSingle(); + + if (existing) { + return interaction.reply({ content: `Your server is already ${existing.status} in the federation.`, ephemeral: true }); + } + + const { data: pendingApp } = await supabase + .from('federation_applications') + .select('status') + .eq('guild_id', interaction.guildId) + .maybeSingle(); + + if (pendingApp) { + return interaction.reply({ content: `Application already ${pendingApp.status}. Use /federation membership status to check.`, ephemeral: true }); + } + + const category = interaction.options.getString('category'); + const description = interaction.options.getString('description'); + + const { error } = await supabase.from('federation_applications').insert({ + guild_id: interaction.guildId, + guild_name: interaction.guild.name, + guild_icon: interaction.guild.icon, + member_count: interaction.guild.memberCount, + category, + description, + admin_id: interaction.user.id, + admin_username: interaction.user.tag, + treaty_agreed: true, + }); + + if (error) { + console.error('Federation application error:', error); + return interaction.reply({ content: 'Failed to submit application.', ephemeral: true }); + } + + const embed = new EmbedBuilder() + .setColor(0x00ff00) + .setTitle('Application Submitted') + .setDescription('Your server has been submitted for federation membership!') + .addFields( + { name: 'Server', value: interaction.guild.name, inline: true }, + { name: 'Category', value: category, inline: true }, + { name: 'Status', value: 'Pending Review', inline: true } + ) + .setFooter({ text: 'You will be notified when your application is reviewed.' }) + .setTimestamp(); + + await interaction.reply({ embeds: [embed] }); + } + + if (subcommand === 'status') { + const { data: server } = await supabase + .from('federation_servers') + .select('*') + .eq('guild_id', interaction.guildId) + .maybeSingle(); + + if (server) { + const embed = new EmbedBuilder() + .setColor(0x00ff00) + .setTitle('Federation Member') + .addFields( + { name: 'Status', value: server.status.toUpperCase(), inline: true }, + { name: 'Tier', value: server.tier?.toUpperCase() || 'FREE', inline: true }, + { name: 'Joined', value: new Date(server.joined_at).toLocaleDateString(), inline: true } + ) + .setTimestamp(); + return interaction.reply({ embeds: [embed] }); + } + + const { data: application } = await supabase + .from('federation_applications') + .select('*') + .eq('guild_id', interaction.guildId) + .maybeSingle(); + + if (application) { + const statusColors = { pending: 0xffff00, approved: 0x00ff00, rejected: 0xff0000 }; + const embed = new EmbedBuilder() + .setColor(statusColors[application.status] || 0x7c3aed) + .setTitle('Application Status') + .addFields( + { name: 'Status', value: application.status.toUpperCase(), inline: true }, + { name: 'Submitted', value: new Date(application.created_at).toLocaleDateString(), inline: true } + ) + .setTimestamp(); + + if (application.rejection_reason) { + embed.addFields({ name: 'Reason', value: application.rejection_reason }); + } + + return interaction.reply({ embeds: [embed] }); + } + + const embed = new EmbedBuilder() + .setColor(0x7c3aed) + .setTitle('Not a Member') + .setDescription('This server is not in the federation.\nUse `/federation membership apply` to join!') + .setTimestamp(); + + await interaction.reply({ embeds: [embed] }); + } + + if (subcommand === 'treaty') { + const embed = new EmbedBuilder() + .setColor(0x7c3aed) + .setTitle('The AeThex Federation Treaty') + .setDescription('By joining the Federation, your server agrees to:') + .addFields( + { name: '1. Contribute to Global Safety', value: 'Report nukers, scammers, and bad actors to the Global Ban List.' }, + { name: '2. Maintain Standards', value: 'Uphold basic moderation and community guidelines.' }, + { name: '3. Respect the Network', value: 'Do not exploit federation features or share protected data.' }, + { name: '4. Display Membership', value: 'Optionally display Federation badge to verify authenticity.' } + ) + .setFooter({ text: 'Together, we are untouchable.' }) + .setTimestamp(); + + const row = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId('federation_agree_treaty') + .setLabel('I Agree') + .setStyle(ButtonStyle.Success), + new ButtonBuilder() + .setLabel('Learn More') + .setStyle(ButtonStyle.Link) + .setURL('https://aethex.dev/federation') + ); + + await interaction.reply({ embeds: [embed], components: [row] }); + } +} + +async function handleScouts(interaction, supabase, client, subcommand) { + if (!supabase) { + return interaction.reply({ content: 'Database not available.', ephemeral: true }); + } + + if (subcommand === 'leaderboard') { + const { data: leaders } = await supabase + .from('federation_reputation') + .select('*') + .order('reputation_score', { ascending: false }) + .limit(15); + + if (!leaders || leaders.length === 0) { + const embed = new EmbedBuilder() + .setColor(0x7c3aed) + .setTitle('Federation Leaderboard') + .setDescription('No reputation data yet. Be active across federation servers to appear here!') + .setTimestamp(); + return interaction.reply({ embeds: [embed] }); + } + + const tierEmojis = { newcomer: '🌱', member: '⭐', veteran: '🏆', elite: '💎', legend: '👑' }; + + const leaderList = leaders.map((l, i) => { + const emoji = tierEmojis[l.rank_tier] || '🌱'; + const medal = i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : `${i + 1}.`; + return `${medal} ${emoji} <@${l.discord_id}> - **${l.reputation_score?.toLocaleString() || 0}** rep`; + }).join('\n'); + + const embed = new EmbedBuilder() + .setColor(0xffd700) + .setTitle('Federation Leaderboard') + .setDescription(leaderList) + .setFooter({ text: 'Reputation earned across all federation servers' }) + .setTimestamp(); + + await interaction.reply({ embeds: [embed] }); + } + + if (subcommand === 'profile') { + const user = interaction.options.getUser('user') || interaction.user; + + const { data: rep } = await supabase + .from('federation_reputation') + .select('*') + .eq('discord_id', user.id) + .maybeSingle(); + + if (!rep) { + const embed = new EmbedBuilder() + .setColor(0x7c3aed) + .setTitle('No Federation Profile') + .setDescription(`${user.tag} doesn't have a federation profile yet.\nBe active across federation servers to build reputation!`) + .setTimestamp(); + return interaction.reply({ embeds: [embed] }); + } + + const tierEmojis = { newcomer: '🌱', member: '⭐', veteran: '🏆', elite: '💎', legend: '👑' }; + + const embed = new EmbedBuilder() + .setColor(0x7c3aed) + .setTitle(`${user.tag}'s Federation Profile`) + .setThumbnail(user.displayAvatarURL()) + .addFields( + { name: 'Reputation', value: rep.reputation_score?.toLocaleString() || '0', inline: true }, + { name: 'Rank', value: `${tierEmojis[rep.rank_tier] || '🌱'} ${rep.rank_tier?.toUpperCase() || 'NEWCOMER'}`, inline: true }, + { name: 'Active In', value: `${rep.servers_active_in || 0} servers`, inline: true }, + { name: 'Total XP', value: rep.total_xp?.toLocaleString() || '0', inline: true }, + { name: 'Highest Level', value: `${rep.highest_level || 0}`, inline: true }, + { name: 'Prestige', value: `${rep.prestige_total || 0}`, inline: true } + ) + .setFooter({ text: `Last active: ${rep.last_active ? new Date(rep.last_active).toLocaleDateString() : 'Unknown'}` }) + .setTimestamp(); + + if (rep.badges && rep.badges.length > 0) { + embed.addFields({ name: 'Badges', value: rep.badges.join(' ') }); + } + + await interaction.reply({ embeds: [embed] }); + } +} + +async function createBanAlerts(supabase, client, userId, severity) { + try { + const { data: servers } = await supabase + .from('federation_servers') + .select('guild_id, tier') + .eq('status', 'approved'); + + if (!servers) return; + + const alerts = servers + .filter(s => severity === 'critical' || s.tier === 'premium') + .map(s => ({ + guild_id: s.guild_id, + alert_type: 'new_ban', + })); + + if (alerts.length > 0) { + const { data: ban } = await supabase + .from('federation_bans') + .select('id') + .eq('user_id', userId) + .eq('active', true) + .maybeSingle(); + + if (ban) { + const alertsWithBanId = alerts.map(a => ({ ...a, ban_id: ban.id })); + await supabase.from('federation_alerts').insert(alertsWithBanId); + } + } + } catch (err) { + console.error('Error creating ban alerts:', err); + } +} diff --git a/aethex-bot/listeners/federationProtection.js b/aethex-bot/listeners/federationProtection.js new file mode 100644 index 0000000..04413e4 --- /dev/null +++ b/aethex-bot/listeners/federationProtection.js @@ -0,0 +1,118 @@ +const { EmbedBuilder } = require('discord.js'); + +module.exports = { + name: 'guildMemberAdd', + + async execute(member, client, supabase) { + if (!supabase) return; + + try { + const { data: serverConfig } = await supabase + .from('federation_servers') + .select('tier, status') + .eq('guild_id', member.guild.id) + .eq('status', 'approved') + .maybeSingle(); + + if (!serverConfig) return; + + const { data: ban } = await supabase + .from('federation_bans') + .select('*') + .eq('user_id', member.id) + .eq('active', true) + .maybeSingle(); + + if (!ban) return; + + const isPremium = serverConfig.tier === 'premium'; + const isCritical = ban.severity === 'critical'; + + if (!isPremium && !isCritical) { + return; + } + + const severityColors = { low: 0xffff00, medium: 0xff9900, high: 0xff3300, critical: 0xff0000 }; + const severityEmojis = { low: '⚠️', medium: '🔶', high: '🔴', critical: '☠️' }; + + const alertEmbed = new EmbedBuilder() + .setColor(severityColors[ban.severity]) + .setTitle(`${severityEmojis[ban.severity]} Federation Alert: Flagged User Joined`) + .setThumbnail(member.displayAvatarURL()) + .addFields( + { name: 'User', value: `${member.user.tag}\n\`${member.id}\``, inline: true }, + { name: 'Severity', value: ban.severity.toUpperCase(), inline: true }, + { name: 'Reason', value: ban.reason || 'No reason provided' } + ) + .setFooter({ text: 'AeThex Federation Protection' }) + .setTimestamp(); + + const { data: config } = await supabase + .from('server_config') + .select('modlog_channel') + .eq('guild_id', member.guild.id) + .maybeSingle(); + + if (config?.modlog_channel) { + const logChannel = await client.channels.fetch(config.modlog_channel).catch(() => null); + if (logChannel) { + await logChannel.send({ embeds: [alertEmbed] }); + } + } + + if (isCritical) { + try { + await member.ban({ reason: `[Federation] Global ban: ${ban.reason}` }); + + alertEmbed.setTitle(`${severityEmojis[ban.severity]} Federation Auto-Ban: Critical Threat Removed`); + alertEmbed.addFields({ name: 'Action Taken', value: 'User was automatically banned' }); + + await supabase.from('federation_alerts').update({ + delivered: true, + delivered_at: new Date().toISOString(), + action_taken: 'auto_ban' + }).eq('guild_id', member.guild.id).eq('ban_id', ban.id); + + } catch (banError) { + console.error('[Federation] Failed to auto-ban:', banError.message); + alertEmbed.addFields({ name: 'Action Required', value: 'Auto-ban failed. Please ban manually.' }); + } + } else if (isPremium) { + try { + await member.kick(`[Federation] Global ban (${ban.severity} severity): ${ban.reason}`); + alertEmbed.addFields({ name: 'Action Taken', value: 'User was automatically kicked (Premium Protection)' }); + + await supabase.from('federation_alerts').update({ + delivered: true, + delivered_at: new Date().toISOString(), + action_taken: 'auto_kick' + }).eq('guild_id', member.guild.id).eq('ban_id', ban.id); + } catch (kickError) { + console.error('[Federation] Failed to auto-kick:', kickError.message); + alertEmbed.addFields({ name: 'Action Required', value: 'Auto-kick failed. Please remove user manually.' }); + } + } + + const owner = await member.guild.fetchOwner().catch(() => null); + if (owner && ban.severity === 'critical') { + try { + const dmEmbed = new EmbedBuilder() + .setColor(0xff0000) + .setTitle('Federation Alert: Critical Threat') + .setDescription(`A user on the global ban list (Critical severity) joined **${member.guild.name}** and was automatically banned.`) + .addFields( + { name: 'User', value: `${member.user.tag} (\`${member.id}\`)` }, + { name: 'Reason', value: ban.reason } + ) + .setTimestamp(); + + await owner.send({ embeds: [dmEmbed] }); + } catch (dmError) { + } + } + + } catch (error) { + console.error('[Federation Protection] Error:', error.message); + } + }, +}; diff --git a/aethex-bot/public/federation.html b/aethex-bot/public/federation.html new file mode 100644 index 0000000..14d3c1a --- /dev/null +++ b/aethex-bot/public/federation.html @@ -0,0 +1,625 @@ + + + + + + Federation - AeThex | Warden + + + +
+ +
+
+ + + + + Add to Server +
+
+ +
+
+
+

The Federation

+

A network of protected servers. Ban one, ban all.

+
+
+ +
+
+
+
-
+
Member Servers
+
+
+
-
+
Active Bans
+
+
+
-
+
Pending Applications
+
+
+ +
+ + + + +
+ +
+
+
Loading servers...
+
+
+ +
+
+
+ Global Ban List +
+ + + + + + + + + + + + +
UserSeverityReasonDate
Loading bans...
+
+
+ +
+
+
+ Pending Applications +
+ + + + + + + + + + + + + +
ServerCategoryMembersStatusActions
Loading applications...
+
+
+ +
+
+
+ Federation Reputation Leaders +
+
+
Loading leaderboard...
+
+
+
+
+
+ + + + diff --git a/aethex-bot/server/webServer.js b/aethex-bot/server/webServer.js index 65be149..f799337 100644 --- a/aethex-bot/server/webServer.js +++ b/aethex-bot/server/webServer.js @@ -1038,6 +1038,192 @@ function createWebServer(discordClient, supabase, options = {}) { } }); + // ============ FEDERATION API ============ + + app.get('/api/federation/stats', async (req, res) => { + if (!supabase) { + return res.status(503).json({ error: 'Database not available' }); + } + + try { + const [servers, bans, applications] = await Promise.all([ + supabase.from('federation_servers').select('id', { count: 'exact' }).eq('status', 'approved'), + supabase.from('federation_bans').select('id', { count: 'exact' }).eq('active', true), + supabase.from('federation_applications').select('id', { count: 'exact' }).eq('status', 'pending') + ]); + + res.json({ + totalServers: servers.count || 0, + activeBans: bans.count || 0, + pendingApplications: applications.count || 0 + }); + } catch (error) { + res.status(500).json({ error: 'Failed to fetch stats' }); + } + }); + + app.get('/api/federation/bans', async (req, res) => { + if (!supabase) { + return res.status(503).json({ error: 'Database not available' }); + } + + const { limit = 50, severity } = req.query; + + try { + let query = supabase + .from('federation_bans') + .select('*') + .eq('active', true) + .order('created_at', { ascending: false }) + .limit(parseInt(limit)); + + if (severity) { + query = query.eq('severity', severity); + } + + const { data: bans } = await query; + res.json({ bans: bans || [] }); + } catch (error) { + res.status(500).json({ error: 'Failed to fetch bans' }); + } + }); + + app.get('/api/federation/servers', async (req, res) => { + if (!supabase) { + return res.status(503).json({ error: 'Database not available' }); + } + + try { + const { data: servers } = await supabase + .from('federation_servers') + .select('*') + .eq('status', 'approved') + .order('member_count', { ascending: false }); + + res.json({ servers: servers || [] }); + } catch (error) { + res.status(500).json({ error: 'Failed to fetch servers' }); + } + }); + + app.get('/api/federation/applications', async (req, res) => { + if (!supabase) { + return res.status(503).json({ error: 'Database not available' }); + } + + const userId = req.session.user?.id; + if (!userId) { + return res.status(401).json({ error: 'Not authenticated' }); + } + + try { + const { data: applications } = await supabase + .from('federation_applications') + .select('*') + .order('created_at', { ascending: false }); + + res.json({ applications: applications || [] }); + } catch (error) { + res.status(500).json({ error: 'Failed to fetch applications' }); + } + }); + + app.post('/api/federation/applications/:appId/approve', async (req, res) => { + if (!supabase) { + return res.status(503).json({ error: 'Database not available' }); + } + + const userId = req.session.user?.id; + if (!userId) { + return res.status(401).json({ error: 'Not authenticated' }); + } + + const { appId } = req.params; + + try { + const { data: app } = await supabase + .from('federation_applications') + .select('*') + .eq('id', appId) + .maybeSingle(); + + if (!app) { + return res.status(404).json({ error: 'Application not found' }); + } + + await supabase.from('federation_applications').update({ + status: 'approved', + reviewed_by: userId, + reviewed_at: new Date().toISOString() + }).eq('id', appId); + + await supabase.from('federation_servers').insert({ + guild_id: app.guild_id, + guild_name: app.guild_name, + guild_icon: app.guild_icon, + description: app.description, + category: app.category, + member_count: app.member_count, + owner_id: app.admin_id, + status: 'approved', + treaty_accepted: true, + treaty_accepted_at: new Date().toISOString() + }); + + res.json({ success: true }); + } catch (error) { + console.error('Failed to approve application:', error); + res.status(500).json({ error: 'Failed to approve application' }); + } + }); + + app.post('/api/federation/applications/:appId/reject', async (req, res) => { + if (!supabase) { + return res.status(503).json({ error: 'Database not available' }); + } + + const userId = req.session.user?.id; + if (!userId) { + return res.status(401).json({ error: 'Not authenticated' }); + } + + const { appId } = req.params; + const { reason } = req.body; + + try { + await supabase.from('federation_applications').update({ + status: 'rejected', + reviewed_by: userId, + reviewed_at: new Date().toISOString(), + rejection_reason: reason || 'No reason provided' + }).eq('id', appId); + + res.json({ success: true }); + } catch (error) { + res.status(500).json({ error: 'Failed to reject application' }); + } + }); + + app.get('/api/federation/leaderboard', async (req, res) => { + if (!supabase) { + return res.status(503).json({ error: 'Database not available' }); + } + + const { limit = 50 } = req.query; + + try { + const { data: leaders } = await supabase + .from('federation_reputation') + .select('*') + .order('reputation_score', { ascending: false }) + .limit(parseInt(limit)); + + res.json({ leaderboard: leaders || [] }); + } catch (error) { + res.status(500).json({ error: 'Failed to fetch leaderboard' }); + } + }); + app.get('/health', (req, res) => { res.json({ status: 'ok', @@ -1046,6 +1232,10 @@ function createWebServer(discordClient, supabase, options = {}) { }); }); + app.get('/federation', (req, res) => { + res.sendFile(path.join(__dirname, '../public/federation.html')); + }); + app.get('/dashboard', (req, res) => { res.sendFile(path.join(__dirname, '../public/dashboard.html')); }); diff --git a/attached_assets/Pasted-This-is-a-Network-Effect-strategy-You-aren-t-selling-th_1765329237794.txt b/attached_assets/Pasted-This-is-a-Network-Effect-strategy-You-aren-t-selling-th_1765329237794.txt new file mode 100644 index 0000000..687e6d1 --- /dev/null +++ b/attached_assets/Pasted-This-is-a-Network-Effect-strategy-You-aren-t-selling-th_1765329237794.txt @@ -0,0 +1,46 @@ +This is a Network Effect strategy. You aren't selling the code; you are selling Membership. + +If the AeThex | Warden bot allows external servers to "Join the Federation," you are essentially building a Digital Alliance (like NATO or the United Nations of Roblox). + +Here is how you extract massive value (money, power, and data) from letting other servers join your Federation. + +1. The "Global Ban List" (Data Supremacy) +The Mechanism: When a Federation server bans a user for being a "Nuker" or "Scammer," that ID is pushed to your central Supabase database. + +The Benefit: You build the ultimate Global Blacklist. + +The Monetization: + +Free Tier: Servers contribute bans but only get protection from "High Risk" nukers. + +Paid Tier ($50/mo): Servers get real-time protection from all Federation bans. "Protect your server from scammers before they even join." + +2. The "Cross-Pollination" (Traffic) +The Mechanism: The bot includes a /federation directory or a "Featured Server" channel that is synced across all Federation members. + +The Benefit: Free advertising for your projects (Lone Star). + +The Monetization: You can charge member servers for "Featured Spots." + +Example: "Want your game promoted to the 50,000 users across the entire Federation network? That's $200/week." + +3. The "Diplomatic" Leverage (Power) +The Mechanism: To join the Federation, a server admin must agree to your "Treaty" (Terms of Service). + +The Benefit: You become the Standard Setter. + +The Strategy: If a studio wants to be in the Federation (to look safe/official), they have to use your standards. This positions AeThex as the "Governing Body" of the alliance. It elevates your brand from "Studio" to "Authority." + +4. The "Talent Scout" (Recruitment) +The Mechanism: With Federation Sync, you can see high-level users across all connected servers. + +The Benefit: You can identify top developers or active users in other people's servers. + +The Strategy: If you see a user who is Level 50 in three different Federation coding servers, you know they are legit. You can invite them to StarFoundry directly. + +Summary: How to Pitch It +You don't sell the bot. You sell Safety in Numbers. + +"Join the AeThex Federation. When a nuker attacks one of us, they are banned from all of us instantly. Together, we are untouchable." + +Verdict: This is the smartest "Long Game" move. It costs you nothing but server fees, but it builds a massive defensive network that you control. \ No newline at end of file