diff --git a/services/arbiter-3.0/scripts/discord-channel-setup.js b/services/arbiter-3.0/scripts/discord-channel-setup.js new file mode 100644 index 0000000..2029b7b --- /dev/null +++ b/services/arbiter-3.0/scripts/discord-channel-setup.js @@ -0,0 +1,812 @@ +#!/usr/bin/env node +/** + * Discord Channel Setup โ€” Full Production Script + * Task #98: Discord Channel Automation + * + * Creates 46 channels across 15 server categories: + * - 5 existing servers: Add forum only + * - 10 new servers: Create category + chat + in-game + forum + voice + * - 1 archive category (staff only) + * + * Created: April 8, 2026 + * Chronicler: #71 + * Spec: docs/tasks/task-098-discord-channel-automation/forum-content-spec.md + */ + +require('dotenv').config({ path: '/opt/arbiter-3.0/.env' }); +const { Client, GatewayIntentBits, ChannelType, PermissionFlagsBits } = require('discord.js'); + +// ============================================================================ +// CONFIGURATION +// ============================================================================ + +const DRY_RUN = false; // Set to true to preview without creating + +// Standard forum tags for all server forums +const STANDARD_FORUM_TAGS = [ + { name: 'Builds', emoji: '๐Ÿ—๏ธ' }, + { name: 'Help', emoji: 'โ“' }, + { name: 'Suggestion', emoji: '๐Ÿ’ก' }, + { name: 'Bug Report', emoji: '๐Ÿ›' }, + { name: 'Achievement', emoji: '๐ŸŽ‰' }, + { name: 'Guide', emoji: '๐Ÿ“–' } +]; + +// ============================================================================ +// SERVER DEFINITIONS +// ============================================================================ + +// Existing 5 servers โ€” have categories, need forums only +const EXISTING_SERVERS = [ + { + name: 'Stoneblock 4', + categoryName: 'Stoneblock 4', // Current name (will add ๐ŸŽฎ prefix) + roleName: 'Stoneblock 4', + welcomeTitle: 'Welcome to Stoneblock 4!', + welcomeBody: `๐Ÿชจ **The stone remembers.** + +You've entered the void โ€” an endless expanse of stone hiding ancient Vaults, mysterious Echoes, and the legendary World Engine. Dig deep, automate everything, and never forget: in Stoneblock, even chickens are sacred. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your underground empires +- โ“ Ask for help with progression +- ๐Ÿ’ก Suggest server improvements +- ๐ŸŽ‰ Celebrate your victories + +--- + +**๐ŸŽฎ First Challenge: Show Us Your Starting Cave!** + +Post a screenshot of your first base setup. Did you pick the Lush Cave? The Darkness? Show us where your journey began! + +React with ๐Ÿ”ฅ for Fire Path or โ„๏ธ for Frost Path!` + }, + { + name: 'Society: Sunlit Valley', + categoryName: 'Society: Sunlit Valley', + roleName: 'Society: Sunlit Valley', + welcomeTitle: 'Welcome to Sunlit Valley!', + welcomeBody: `๐ŸŒป **A Stardew Valley experience in Minecraft.** + +Welcome to the valley, farmer! Grow seasonal crops, raise animals, dive into the Skull Cavern for Iridium, and build the coziest farm this side of the blocky world. Money makes the world go round โ€” so get shipping! + +**This forum is your space to:** +- ๐Ÿ—๏ธ Show off your farm layouts +- โ“ Ask about crop rotations and bundles +- ๐Ÿ’ก Suggest new farming features +- ๐ŸŽ‰ Share your biggest hauls + +--- + +**๐ŸŽฎ First Challenge: Your Dream Farm Name!** + +What did you name your farm? Post it along with a screenshot of your farmhouse! Bonus points for creative theming. + +React with ๐ŸŒพ for farming focus or โ›๏ธ for mining focus!` + }, + { + name: 'All the Mods 10: To the Sky', + categoryName: 'All the Mods 10: To the Sky', + roleName: 'All The Mods: To the Sky', + welcomeTitle: 'Welcome to ATM10: To the Sky!', + welcomeBody: `โ˜๏ธ **Start with a tree. Build an empire in the void.** + +You've got nothing but a single tree and infinite ambition. Sieve your way to resources, automate your way to power, and craft the legendary ATM Star. This is skyblock evolved โ€” 500+ mods of pure vertical progression. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your sky islands +- โ“ Ask about automation setups +- ๐Ÿ’ก Suggest efficiency improvements +- ๐ŸŽ‰ Celebrate progression milestones + +--- + +**๐ŸŽฎ First Challenge: Day One Screenshot!** + +Show us your island after your first play session. Tiny platform? Sprawling network? We want to see your start! + +React with ๐Ÿ”ฅ for Fire Path or โ„๏ธ for Frost Path!` + }, + { + name: 'All the Mons', + categoryName: 'All the Mons', + roleName: 'All The Mons', + welcomeTitle: 'Welcome to All the Mons!', + welcomeBody: `๐Ÿพ **All the Mods meets Cobblemon. Gotta catch AND automate 'em all.** + +The ultimate crossover โ€” 500+ tech and magic mods collide with Pokรฉmon. Build factories, cast spells, AND catch 'em all. Custom Pokรฉball recipes use modded materials, so your automation skills directly power your trainer journey. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Show off your bases and Pokรฉmon pastures +- โ“ Ask about spawn locations and evolution +- ๐Ÿ’ก Suggest Pokรฉmon-related improvements +- ๐ŸŽ‰ Share shiny catches and team builds + +--- + +**๐ŸŽฎ First Challenge: Your Starter Trio!** + +Post your first three Pokรฉmon! Did you go classic starters or catch wild ones? Show us your team! + +React with ๐Ÿ”ฅ for Fire types or โ„๏ธ for Ice/Water types!` + }, + { + name: 'Mythcraft 5', + categoryName: 'Mythcraft 5', + roleName: 'Mythcraft 5', + welcomeTitle: 'Welcome to Mythcraft 5!', + welcomeBody: `โš”๏ธ **Magic. Alchemy. Technology. Adventure.** + +Over 1,000 structures await exploration. A custom questline guides your progression through dungeons, fortresses, and strange dimensions. Master weapons AND spells. Unlock skills. Become legend. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your bases and discoveries +- โ“ Ask for help with progression and bosses +- ๐Ÿ’ก Suggest adventure improvements +- ๐ŸŽ‰ Celebrate boss kills and rare loot + +--- + +**๐ŸŽฎ First Challenge: Your First Boss Kill!** + +What was the first boss you took down? Share the screenshot and the story! + +React with โš”๏ธ for combat focus or ๐Ÿ”ฎ for magic focus!` + } +]; + +// New 10 servers โ€” need category + all channels +const NEW_SERVERS = [ + { + name: 'Beyond Depth', + roleName: 'Beyond Depth', + welcomeTitle: 'Welcome to Beyond!', + welcomeBody: `๐Ÿ‰ **Push the limits. Go beyond.** + +Whether you're diving into the depths or ascending to new heights, the Beyond series challenges you to master progression and conquer the unknown. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your progress +- โ“ Ask for help with challenges +- ๐Ÿ’ก Suggest improvements +- ๐ŸŽ‰ Celebrate breakthroughs + +--- + +**๐ŸŽฎ First Challenge: Your Biggest Challenge So Far!** + +What's been the hardest part? Share your struggles and triumphs! + +React with โฌ‡๏ธ for Depth or โฌ†๏ธ for Ascension!` + }, + { + name: 'Beyond Ascension', + roleName: 'Beyond Ascension', + welcomeTitle: 'Welcome to Beyond!', + welcomeBody: `๐Ÿ‰ **Push the limits. Go beyond.** + +Whether you're diving into the depths or ascending to new heights, the Beyond series challenges you to master progression and conquer the unknown. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your progress +- โ“ Ask for help with challenges +- ๐Ÿ’ก Suggest improvements +- ๐ŸŽ‰ Celebrate breakthroughs + +--- + +**๐ŸŽฎ First Challenge: Your Biggest Challenge So Far!** + +What's been the hardest part? Share your struggles and triumphs! + +React with โฌ‡๏ธ for Depth or โฌ†๏ธ for Ascension!` + }, + { + name: "Wold's Vaults", + roleName: "Wold's Vaults", + welcomeTitle: "Welcome to Wold's Vaults!", + welcomeBody: `๐Ÿ—„๏ธ **Crack the vaults. Claim the treasure.** + +A progression-focused pack centered around vault hunting. Gear up, dive in, and see what riches await those brave enough to face the challenges within. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your vault hauls +- โ“ Ask about vault strategies +- ๐Ÿ’ก Suggest improvements +- ๐ŸŽ‰ Celebrate legendary finds + +--- + +**๐ŸŽฎ First Challenge: Your Best Vault Haul!** + +What's the best thing you've pulled from a vault? Show us!` + }, + { + name: 'Otherworld [D&D]', + roleName: 'Otherworld [Dungeons & Dragons]', + welcomeTitle: 'Welcome to Otherworld!', + welcomeBody: `๐ŸŽฒ **Roll for initiative in Minecraft.** + +D&D-inspired adventures await. Character classes, dungeon crawling, and tabletop vibes brought to life in block form. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your characters and builds +- โ“ Ask about classes and progression +- ๐Ÿ’ก Suggest adventure improvements +- ๐ŸŽ‰ Share epic moments + +--- + +**๐ŸŽฎ First Challenge: Introduce Your Character!** + +Name, class, backstory. Let's hear it!` + }, + { + name: 'DeceasedCraft', + roleName: 'DeceasedCraft', + welcomeTitle: 'Welcome to DeceasedCraft!', + welcomeBody: `โ˜ ๏ธ **Survive the apocalypse.** + +The world has ended, but you haven't. Scavenge, survive, and maybe even thrive in a hostile world where death lurks around every corner. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your survival setups +- โ“ Ask about survival strategies +- ๐Ÿ’ก Suggest improvements +- ๐ŸŽ‰ Celebrate survival milestones + +--- + +**๐ŸŽฎ First Challenge: Day 7 Screenshot!** + +If you survived a week, show us your base!` + }, + { + name: 'Submerged 2', + roleName: 'Submerged 2', + welcomeTitle: 'Welcome to Submerged 2!', + welcomeBody: `๐ŸŒŠ **The depths await.** + +Dive into an underwater adventure where the ocean is your home. Build aquatic bases, explore sunken ruins, and survive the pressure of the deep. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your underwater bases +- โ“ Ask about aquatic survival +- ๐Ÿ’ก Suggest ocean improvements +- ๐ŸŽ‰ Celebrate deep sea discoveries + +--- + +**๐ŸŽฎ First Challenge: Your First Underwater Base!** + +Show us where you set up shop beneath the waves! + +React with ๐Ÿ  for ocean life or ๐Ÿ—๏ธ for engineering focus!` + }, + { + name: "Sneak's Pirate Pack", + roleName: "Sneak's Pirate Pack", + welcomeTitle: "Ahoy, Welcome to Sneak's Pirate Pack!", + welcomeBody: `๐Ÿดโ€โ˜ ๏ธ **Set sail for adventure!** + +A pirate's life for thee! Build ships, explore the seas, find treasure, and live the swashbuckling dream. Just watch out for sea monsters... + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your ships and ports +- โ“ Ask about naval adventures +- ๐Ÿ’ก Suggest pirate improvements +- ๐ŸŽ‰ Show off your treasure hoards + +--- + +**๐ŸŽฎ First Challenge: Your Ship!** + +Every pirate needs a vessel. Show us your pride and joy! + +React with โš“ for sailors or ๐Ÿ’€ for scallywags!` + }, + { + name: 'Cottage Witch', + roleName: 'Cottage Witch', + welcomeTitle: 'Welcome to Cottage Witch!', + welcomeBody: `๐Ÿง™ **Cozy vibes. Domestic magic. Witchy aesthetics.** + +Cottage Witch emphasizes the magic in everyday things โ€” cooking, crafting, decorating. Build your perfect witch's cabin, brew potions, cast spells with Ars Nouveau and Hexerei, and let Create automate your cozy life. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your witchy builds +- โ“ Ask about magic systems +- ๐Ÿ’ก Suggest cozy improvements +- ๐ŸŽ‰ Show off your collections + +--- + +**๐ŸŽฎ First Challenge: Your Witch's Corner!** + +Every witch needs a cozy corner. Show us your cauldron setup, potion station, or spell crafting area! + +React with ๐ŸŒ™ for dark witch or ๐ŸŒป for cottage witch!` + }, + { + name: 'Farm Crossing 5', + roleName: 'Farm Crossing 5', + welcomeTitle: 'Welcome to Farm Crossing 5!', + welcomeBody: `๐ŸŒพ **The coziest crossover.** + +Animal Crossing vibes meet Minecraft farming. Relax, decorate, farm, and make friends with your animal neighbors. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your island/farm layouts +- โ“ Ask about villagers and farming +- ๐Ÿ’ก Suggest cozy additions +- ๐ŸŽ‰ Show off your collections + +--- + +**๐ŸŽฎ First Challenge: Your Favorite Corner!** + +Show us your coziest spot!` + }, + { + name: 'Homestead', + roleName: 'Homestead', + welcomeTitle: 'Welcome to Homestead!', + welcomeBody: `๐Ÿ  **Build your dream. Live your peace.** + +Homestead is all about cozy survival โ€” building, farming, and creating your perfect world without the pressure. Take your time, make it beautiful, and enjoy the journey. + +**This forum is your space to:** +- ๐Ÿ—๏ธ Share your homestead builds +- โ“ Ask about building techniques +- ๐Ÿ’ก Suggest cozy additions +- ๐ŸŽ‰ Show off your finished projects + +--- + +**๐ŸŽฎ First Challenge: Your Front Door!** + +Post a screenshot standing at your front door looking out. What does home look like? + +React with ๐Ÿก for cottage vibes or ๐Ÿฐ for grand builds!` + } +]; + +// Staff/Admin roles that get full access +const ADMIN_ROLES = [ + 'Staff', + '๐Ÿ›ก๏ธ Moderator', + '๐Ÿ‘‘ The Wizard', + '๐Ÿ’Ž The Emissary', + 'โœจ The Catalyst' +]; + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +function slugify(name) { + return name + .toLowerCase() + .replace(/[^a-z0-9\s-]/g, '') + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .substring(0, 100); +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +// ============================================================================ +// MAIN SCRIPT +// ============================================================================ + +async function main() { + console.log('๐ŸŽฎ Discord Channel Setup โ€” Full Production Script'); + console.log('================================================='); + console.log(`Mode: ${DRY_RUN ? '๐Ÿ” DRY RUN (no changes)' : 'โšก LIVE (will create channels)'}`); + console.log(''); + + const client = new Client({ + intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers] + }); + + const stats = { + categoriesCreated: 0, + categoriesRenamed: 0, + forumsCreated: 0, + textChannelsCreated: 0, + voiceChannelsCreated: 0, + welcomePostsCreated: 0, + permissionsApplied: 0, + errors: [] + }; + + try { + // Login + console.log('๐Ÿ“ก Connecting to Discord...'); + await client.login(process.env.DISCORD_BOT_TOKEN); + await new Promise(resolve => { + if (client.isReady()) resolve(); + else client.once('ready', resolve); + }); + console.log(`โœ… Logged in as ${client.user.tag}`); + + // Get guild + const guild = client.guilds.cache.get(process.env.GUILD_ID); + if (!guild) throw new Error('Guild not found'); + console.log(`โœ… Found guild: ${guild.name}`); + + // Fetch all data + await guild.channels.fetch(); + await guild.roles.fetch(); + await guild.members.fetch(); + console.log(`๐Ÿ“Š Current: ${guild.channels.cache.size} channels, ${guild.roles.cache.size} roles`); + console.log(''); + + // Build role lookup + const rolesByName = new Map(); + guild.roles.cache.forEach(role => { + rolesByName.set(role.name, role); + }); + + // Get key roles + const everyoneRole = guild.roles.everyone; + const wandererRole = rolesByName.get('Wanderer'); + + if (!wandererRole) { + throw new Error('Wanderer role not found!'); + } + console.log(`โœ… Found Wanderer role: ${wandererRole.id}`); + + // Get admin roles + const adminRoleIds = []; + for (const roleName of ADMIN_ROLES) { + const role = rolesByName.get(roleName); + if (role) { + adminRoleIds.push(role.id); + console.log(`โœ… Found admin role: ${roleName} (${role.id})`); + } else { + console.log(`โš ๏ธ Admin role not found: ${roleName}`); + } + } + console.log(''); + + // ======================================================================== + // PHASE 1: Process existing 5 servers (add forum + rename category) + // ======================================================================== + + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log('PHASE 1: Existing 5 Servers โ€” Add Forums + Rename Categories'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log(''); + + for (const server of EXISTING_SERVERS) { + console.log(`๐Ÿ“ Processing: ${server.name}`); + + // Find existing category + let category = guild.channels.cache.find( + ch => ch.type === ChannelType.GuildCategory && + (ch.name === server.categoryName || ch.name === `๐ŸŽฎ ${server.categoryName}`) + ); + + if (!category) { + console.log(` โŒ Category not found: ${server.categoryName}`); + stats.errors.push(`Category not found: ${server.categoryName}`); + continue; + } + console.log(` โœ… Found category: ${category.name} (${category.id})`); + + // Find server role + const serverRole = rolesByName.get(server.roleName); + if (!serverRole) { + console.log(` โŒ Role not found: ${server.roleName}`); + stats.errors.push(`Role not found: ${server.roleName}`); + continue; + } + console.log(` โœ… Found role: ${serverRole.name} (${serverRole.id})`); + + if (DRY_RUN) { + console.log(` [DRY RUN] Would rename category to: ๐ŸŽฎ ${server.categoryName}`); + console.log(` [DRY RUN] Would create forum: ${slugify(server.name)}-forum`); + console.log(` [DRY RUN] Would post welcome message`); + console.log(''); + continue; + } + + // Rename category if needed + if (!category.name.startsWith('๐ŸŽฎ')) { + await category.setName(`๐ŸŽฎ ${server.categoryName}`); + console.log(` โœ… Renamed category to: ๐ŸŽฎ ${server.categoryName}`); + stats.categoriesRenamed++; + await sleep(500); + } + + // Check if forum already exists + const existingForum = guild.channels.cache.find( + ch => ch.type === ChannelType.GuildForum && ch.parentId === category.id + ); + + if (existingForum) { + console.log(` โš ๏ธ Forum already exists: ${existingForum.name}`); + } else { + // Create forum + const forum = await guild.channels.create({ + name: `${slugify(server.name)}-forum`, + type: ChannelType.GuildForum, + parent: category.id, + topic: `Discussion forum for ${server.name}`, + availableTags: STANDARD_FORUM_TAGS.map(tag => ({ + name: tag.name, + emoji: { name: tag.emoji } + })), + reason: 'Task #98 Discord Channel Automation - Chronicler #71' + }); + console.log(` โœ… Created forum: ${forum.name} (${forum.id})`); + stats.forumsCreated++; + await sleep(500); + + // Post welcome message + const welcomeThread = await forum.threads.create({ + name: server.welcomeTitle, + message: { content: server.welcomeBody }, + reason: 'Task #98 Welcome Post - Chronicler #71' + }); + console.log(` โœ… Posted welcome: ${welcomeThread.name}`); + stats.welcomePostsCreated++; + await sleep(500); + } + + // Apply permissions to category + const permissionOverwrites = [ + // @everyone: deny all + { id: everyoneRole.id, deny: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.Connect] }, + // Wanderer: view only + { id: wandererRole.id, allow: [PermissionFlagsBits.ViewChannel], deny: [PermissionFlagsBits.SendMessages, PermissionFlagsBits.Connect] }, + // Server role: full access + { id: serverRole.id, allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.Connect, PermissionFlagsBits.ReadMessageHistory] }, + // Admin roles: full access + ...adminRoleIds.map(roleId => ({ + id: roleId, + allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.Connect, PermissionFlagsBits.ReadMessageHistory] + })) + ]; + + await category.permissionOverwrites.set(permissionOverwrites); + console.log(` โœ… Applied permissions to category`); + stats.permissionsApplied++; + await sleep(500); + + console.log(''); + } + + // ======================================================================== + // PHASE 2: Create 10 new servers (category + all channels) + // ======================================================================== + + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log('PHASE 2: New 10 Servers โ€” Create Categories + All Channels'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log(''); + + for (const server of NEW_SERVERS) { + console.log(`๐Ÿ“ Creating: ${server.name}`); + + // Find server role + const serverRole = rolesByName.get(server.roleName); + if (!serverRole) { + console.log(` โŒ Role not found: ${server.roleName}`); + stats.errors.push(`Role not found: ${server.roleName}`); + continue; + } + console.log(` โœ… Found role: ${serverRole.name} (${serverRole.id})`); + + // Check if category already exists + let category = guild.channels.cache.find( + ch => ch.type === ChannelType.GuildCategory && + (ch.name === server.name || ch.name === `๐ŸŽฎ ${server.name}`) + ); + + if (DRY_RUN) { + console.log(` [DRY RUN] Would create category: ๐ŸŽฎ ${server.name}`); + console.log(` [DRY RUN] Would create: ${slugify(server.name)}-chat`); + console.log(` [DRY RUN] Would create: ${slugify(server.name)}-in-game`); + console.log(` [DRY RUN] Would create: ${slugify(server.name)}-forum`); + console.log(` [DRY RUN] Would create voice: ${server.name}`); + console.log(` [DRY RUN] Would post welcome message`); + console.log(''); + continue; + } + + // Build permission overwrites + const permissionOverwrites = [ + { id: everyoneRole.id, deny: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.Connect] }, + { id: wandererRole.id, allow: [PermissionFlagsBits.ViewChannel], deny: [PermissionFlagsBits.SendMessages, PermissionFlagsBits.Connect] }, + { id: serverRole.id, allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.Connect, PermissionFlagsBits.ReadMessageHistory] }, + ...adminRoleIds.map(roleId => ({ + id: roleId, + allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.Connect, PermissionFlagsBits.ReadMessageHistory] + })) + ]; + + if (category) { + console.log(` โš ๏ธ Category already exists: ${category.name}`); + } else { + // Create category + category = await guild.channels.create({ + name: `๐ŸŽฎ ${server.name}`, + type: ChannelType.GuildCategory, + permissionOverwrites, + reason: 'Task #98 Discord Channel Automation - Chronicler #71' + }); + console.log(` โœ… Created category: ${category.name} (${category.id})`); + stats.categoriesCreated++; + stats.permissionsApplied++; + await sleep(500); + } + + // Create chat channel + const chatExists = guild.channels.cache.find( + ch => ch.parentId === category.id && ch.name.includes('chat') + ); + if (!chatExists) { + const chat = await guild.channels.create({ + name: `${slugify(server.name)}-chat`, + type: ChannelType.GuildText, + parent: category.id, + topic: `General chat for ${server.name}`, + reason: 'Task #98 Discord Channel Automation - Chronicler #71' + }); + console.log(` โœ… Created: ${chat.name}`); + stats.textChannelsCreated++; + await sleep(500); + } + + // Create in-game channel + const ingameExists = guild.channels.cache.find( + ch => ch.parentId === category.id && ch.name.includes('in-game') + ); + if (!ingameExists) { + const ingame = await guild.channels.create({ + name: `${slugify(server.name)}-in-game`, + type: ChannelType.GuildText, + parent: category.id, + topic: `In-game chat bridge for ${server.name}`, + reason: 'Task #98 Discord Channel Automation - Chronicler #71' + }); + console.log(` โœ… Created: ${ingame.name}`); + stats.textChannelsCreated++; + await sleep(500); + } + + // Create forum + const forumExists = guild.channels.cache.find( + ch => ch.type === ChannelType.GuildForum && ch.parentId === category.id + ); + if (!forumExists) { + const forum = await guild.channels.create({ + name: `${slugify(server.name)}-forum`, + type: ChannelType.GuildForum, + parent: category.id, + topic: `Discussion forum for ${server.name}`, + availableTags: STANDARD_FORUM_TAGS.map(tag => ({ + name: tag.name, + emoji: { name: tag.emoji } + })), + reason: 'Task #98 Discord Channel Automation - Chronicler #71' + }); + console.log(` โœ… Created forum: ${forum.name} (${forum.id})`); + stats.forumsCreated++; + await sleep(500); + + // Post welcome message + const welcomeThread = await forum.threads.create({ + name: server.welcomeTitle, + message: { content: server.welcomeBody }, + reason: 'Task #98 Welcome Post - Chronicler #71' + }); + console.log(` โœ… Posted welcome: ${welcomeThread.name}`); + stats.welcomePostsCreated++; + await sleep(500); + } + + // Create voice channel + const voiceExists = guild.channels.cache.find( + ch => ch.type === ChannelType.GuildVoice && ch.parentId === category.id + ); + if (!voiceExists) { + const voice = await guild.channels.create({ + name: server.name, + type: ChannelType.GuildVoice, + parent: category.id, + reason: 'Task #98 Discord Channel Automation - Chronicler #71' + }); + console.log(` โœ… Created voice: ${voice.name}`); + stats.voiceChannelsCreated++; + await sleep(500); + } + + console.log(''); + } + + // ======================================================================== + // PHASE 3: Create Archive category + // ======================================================================== + + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log('PHASE 3: Create Archive Category'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log(''); + + const archiveExists = guild.channels.cache.find( + ch => ch.type === ChannelType.GuildCategory && ch.name.includes('Archive') + ); + + if (DRY_RUN) { + console.log('[DRY RUN] Would create: ๐Ÿ“ฆ Archive (staff only)'); + } else if (archiveExists) { + console.log(`โš ๏ธ Archive category already exists: ${archiveExists.name}`); + } else { + const archivePerms = [ + { id: everyoneRole.id, deny: [PermissionFlagsBits.ViewChannel] }, + ...adminRoleIds.map(roleId => ({ + id: roleId, + allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.ReadMessageHistory] + })) + ]; + + const archive = await guild.channels.create({ + name: '๐Ÿ“ฆ Archive', + type: ChannelType.GuildCategory, + permissionOverwrites: archivePerms, + reason: 'Task #98 Discord Channel Automation - Chronicler #71' + }); + console.log(`โœ… Created: ${archive.name} (${archive.id})`); + stats.categoriesCreated++; + } + + // ======================================================================== + // SUMMARY + // ======================================================================== + + console.log(''); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log('๐Ÿ“Š SUMMARY'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log(`Categories created: ${stats.categoriesCreated}`); + console.log(`Categories renamed: ${stats.categoriesRenamed}`); + console.log(`Forums created: ${stats.forumsCreated}`); + console.log(`Text channels created: ${stats.textChannelsCreated}`); + console.log(`Voice channels created: ${stats.voiceChannelsCreated}`); + console.log(`Welcome posts created: ${stats.welcomePostsCreated}`); + console.log(`Permissions applied: ${stats.permissionsApplied}`); + console.log(''); + + if (stats.errors.length > 0) { + console.log('โš ๏ธ ERRORS:'); + stats.errors.forEach(e => console.log(` - ${e}`)); + } else { + console.log('โœ… No errors!'); + } + + console.log(''); + console.log('๐ŸŽ‰ Task #98 Discord Channel Automation โ€” COMPLETE'); + console.log(''); + console.log('๐Ÿ‘€ Next steps:'); + console.log(' 1. Check Discord to verify all channels'); + console.log(' 2. Test permissions with a Wanderer account'); + console.log(' 3. Test permissions with a subscriber account'); + console.log(' 4. Revoke Arbiter admin permissions'); + + } catch (error) { + console.error(''); + console.error('โŒ FATAL ERROR:', error.message); + console.error(error.stack); + } finally { + client.destroy(); + console.log(''); + console.log('๐Ÿ‘‹ Disconnected from Discord.'); + } +} + +main();