diff --git a/services/arbiter-3.0/scripts/discord-channel-full-setup.js b/services/arbiter-3.0/scripts/discord-channel-full-setup.js new file mode 100644 index 0000000..b454595 --- /dev/null +++ b/services/arbiter-3.0/scripts/discord-channel-full-setup.js @@ -0,0 +1,851 @@ +#!/usr/bin/env node +/** + * Discord Channel Full Setup Script + * Task #98: Create 46 channels for 15 Minecraft servers + * + * Creates: + * - 10 new categories (for servers without channels) + * - 15 forum channels (all servers) + * - 10 text channels (chat) + * - 10 text channels (in-game-chat) + * - 10 voice channels + * - 1 Archive category + * - 15 welcome posts + * - Permission overwrites on all categories + * + * 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 = true; // SET TO false TO EXECUTE + +// 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 +// ============================================================================ + +// Servers that ALREADY have categories (just need forum added) +const EXISTING_SERVERS = [ + { + name: 'Stoneblock 4', + categoryName: 'Stoneblock 4', // Will add ๐ŸŽฎ prefix + roleName: 'Stoneblock 4', + forumName: 'stoneblock-4-forum', + 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', + forumName: 'sunlit-valley-forum', + 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', + forumName: 'atm10-sky-forum', + 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', + forumName: 'all-the-mons-forum', + 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', + forumName: 'mythcraft-5-forum', + 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!` + } +]; + +// Servers that need FULL setup (category + all channels) +const NEW_SERVERS = [ + { + name: 'Beyond Depth', + categoryName: '๐ŸŽฎ Beyond Depth', + roleName: 'Beyond Depth', + chatName: 'beyond-depth-chat', + inGameName: 'beyond-depth-in-game', + voiceName: 'Beyond Depth', + forumName: 'beyond-depth-forum', + 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', + categoryName: '๐ŸŽฎ Beyond Ascension', + roleName: 'Beyond Ascension', + chatName: 'beyond-ascension-chat', + inGameName: 'beyond-ascension-in-game', + voiceName: 'Beyond Ascension', + forumName: 'beyond-ascension-forum', + welcomeTitle: 'Welcome to Beyond Ascension!', + 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", + categoryName: "๐ŸŽฎ Wold's Vaults", + roleName: "Wold's Vaults", + chatName: 'wolds-vaults-chat', + inGameName: 'wolds-vaults-in-game', + voiceName: "Wold's Vaults", + forumName: 'wolds-vaults-forum', + 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]', + categoryName: '๐ŸŽฎ Otherworld [D&D]', + roleName: 'Otherworld [Dungeons & Dragons]', + chatName: 'otherworld-chat', + inGameName: 'otherworld-in-game', + voiceName: 'Otherworld', + forumName: 'otherworld-forum', + 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', + categoryName: '๐ŸŽฎ DeceasedCraft', + roleName: 'DeceasedCraft', + chatName: 'deceasedcraft-chat', + inGameName: 'deceasedcraft-in-game', + voiceName: 'DeceasedCraft', + forumName: 'deceasedcraft-forum', + 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', + categoryName: '๐ŸŽฎ Submerged 2', + roleName: 'Submerged 2', + chatName: 'submerged-2-chat', + inGameName: 'submerged-2-in-game', + voiceName: 'Submerged 2', + forumName: 'submerged-2-forum', + 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", + categoryName: "๐ŸŽฎ Sneak's Pirate Pack", + roleName: "Sneak's Pirate Pack", + chatName: 'sneaks-pirate-pack-chat', + inGameName: 'sneaks-pirate-pack-in-game', + voiceName: "Sneak's Pirate Pack", + forumName: 'sneaks-pirate-pack-forum', + 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', + categoryName: '๐ŸŽฎ Cottage Witch', + roleName: 'Cottage Witch', + chatName: 'cottage-witch-chat', + inGameName: 'cottage-witch-in-game', + voiceName: 'Cottage Witch', + forumName: 'cottage-witch-forum', + 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', + categoryName: '๐ŸŽฎ Farm Crossing 5', + roleName: 'Farm Crossing 5', + chatName: 'farm-crossing-5-chat', + inGameName: 'farm-crossing-5-in-game', + voiceName: 'Farm Crossing 5', + forumName: 'farm-crossing-5-forum', + 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', + categoryName: '๐ŸŽฎ Homestead', + roleName: 'Homestead', + chatName: 'homestead-chat', + inGameName: 'homestead-in-game', + voiceName: 'Homestead', + forumName: 'homestead-forum', + 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!` + } +]; + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +/** + * Find a role by name (case-insensitive) + */ +function findRole(guild, roleName) { + return guild.roles.cache.find(r => r.name.toLowerCase() === roleName.toLowerCase()); +} + +/** + * Find a category by name (case-insensitive, ignores emoji prefix) + */ +function findCategory(guild, categoryName) { + const searchName = categoryName.replace(/^๐ŸŽฎ\s*/, '').toLowerCase(); + return guild.channels.cache.find(ch => + ch.type === ChannelType.GuildCategory && + ch.name.replace(/^๐ŸŽฎ\s*/, '').toLowerCase() === searchName + ); +} + +/** + * Build permission overwrites for a server category + */ +function buildPermissionOverwrites(guild, serverRole) { + const everyone = guild.roles.everyone; + const wanderer = findRole(guild, 'Wanderer'); + const staff = findRole(guild, 'Staff'); + const moderator = findRole(guild, '๐Ÿ›ก๏ธ Moderator'); + const wizard = findRole(guild, '๐Ÿ‘‘ The Wizard'); + const emissary = findRole(guild, '๐Ÿ’Ž The Emissary'); + const catalyst = findRole(guild, 'โœจ The Catalyst'); + + const overwrites = [ + // @everyone - deny all + { + id: everyone.id, + deny: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.Connect] + }, + // Server role - allow all + { + id: serverRole.id, + allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.Connect, PermissionFlagsBits.ReadMessageHistory] + } + ]; + + // Wanderer - view only (window shopping) + if (wanderer) { + overwrites.push({ + id: wanderer.id, + allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.ReadMessageHistory], + deny: [PermissionFlagsBits.SendMessages, PermissionFlagsBits.Connect] + }); + } + + // Staff roles - allow all + const staffRoles = [staff, moderator, wizard, emissary, catalyst].filter(Boolean); + for (const role of staffRoles) { + overwrites.push({ + id: role.id, + allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.Connect, PermissionFlagsBits.ReadMessageHistory] + }); + } + + return overwrites; +} + +/** + * Sleep helper for rate limiting + */ +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +// ============================================================================ +// MAIN SCRIPT +// ============================================================================ + +async function main() { + console.log('๐Ÿ”ง Discord Channel Full Setup'); + 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}`); + + 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(); + console.log(`๐Ÿ“Š Current: ${guild.channels.cache.size} channels, ${guild.roles.cache.size} roles`); + console.log(''); + + // ======================================================================== + // PHASE 1: Add ๐ŸŽฎ prefix to existing 5 server categories + // ======================================================================== + console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); + console.log('PHASE 1: Update existing server categories (add ๐ŸŽฎ prefix)'); + console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); + + for (const server of EXISTING_SERVERS) { + const category = findCategory(guild, server.categoryName); + if (!category) { + console.log(` โš ๏ธ Category not found: ${server.categoryName}`); + stats.errors.push(`Category not found: ${server.categoryName}`); + continue; + } + + // Add ๐ŸŽฎ prefix if not present + if (!category.name.startsWith('๐ŸŽฎ')) { + const newName = `๐ŸŽฎ ${category.name}`; + console.log(` ๐Ÿ“ Renaming: "${category.name}" โ†’ "${newName}"`); + if (!DRY_RUN) { + await category.setName(newName, 'Task #98 - Add emoji prefix'); + await sleep(500); + } + stats.categoriesRenamed++; + } else { + console.log(` โœ“ Already has prefix: ${category.name}`); + } + } + + // ======================================================================== + // PHASE 2: Add forums to existing 5 servers + // ======================================================================== + console.log(''); + console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); + console.log('PHASE 2: Add forum channels to existing 5 servers'); + console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); + + for (const server of EXISTING_SERVERS) { + console.log(`\n ๐Ÿ“ฆ ${server.name}`); + + const category = findCategory(guild, server.categoryName); + if (!category) { + console.log(` โš ๏ธ Category not found, skipping`); + continue; + } + + const serverRole = findRole(guild, server.roleName); + if (!serverRole) { + console.log(` โš ๏ธ Role not found: ${server.roleName}`); + stats.errors.push(`Role not found: ${server.roleName}`); + continue; + } + + // 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}`); + continue; + } + + // Create forum + console.log(` Creating forum: ${server.forumName}`); + if (!DRY_RUN) { + const forum = await guild.channels.create({ + name: server.forumName, + type: ChannelType.GuildForum, + parent: category.id, + topic: `Discussion forum for ${server.name}`, + availableTags: STANDARD_FORUM_TAGS.map(tag => ({ + name: tag.name, + emoji: tag.emoji ? { name: tag.emoji } : null + })), + permissionOverwrites: buildPermissionOverwrites(guild, serverRole), + reason: 'Task #98 - Discord channel automation' + }); + stats.forumsCreated++; + await sleep(500); + + // Create welcome post + console.log(` Creating welcome post...`); + await forum.threads.create({ + name: server.welcomeTitle, + message: { content: server.welcomeBody }, + reason: 'Task #98 - Server welcome post' + }); + stats.welcomePostsCreated++; + await sleep(500); + } else { + stats.forumsCreated++; + stats.welcomePostsCreated++; + } + console.log(` โœ… Done`); + } + + // ======================================================================== + // PHASE 3: Create full setup for 10 new servers + // ======================================================================== + console.log(''); + console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); + console.log('PHASE 3: Create categories + channels for 10 new servers'); + console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); + + for (const server of NEW_SERVERS) { + console.log(`\n ๐Ÿ“ฆ ${server.name}`); + + const serverRole = findRole(guild, server.roleName); + if (!serverRole) { + console.log(` โš ๏ธ Role not found: ${server.roleName}, skipping`); + stats.errors.push(`Role not found: ${server.roleName}`); + continue; + } + + // Check if category already exists + let category = findCategory(guild, server.categoryName); + if (category) { + console.log(` โœ“ Category already exists: ${category.name}`); + } else { + console.log(` Creating category: ${server.categoryName}`); + if (!DRY_RUN) { + category = await guild.channels.create({ + name: server.categoryName, + type: ChannelType.GuildCategory, + permissionOverwrites: buildPermissionOverwrites(guild, serverRole), + reason: 'Task #98 - Discord channel automation' + }); + await sleep(500); + } + stats.categoriesCreated++; + } + + if (!DRY_RUN && category) { + // Create chat channel + console.log(` Creating: ${server.chatName}`); + await guild.channels.create({ + name: server.chatName, + type: ChannelType.GuildText, + parent: category.id, + topic: `General chat for ${server.name}`, + reason: 'Task #98 - Discord channel automation' + }); + stats.textChannelsCreated++; + await sleep(500); + + // Create in-game channel + console.log(` Creating: ${server.inGameName}`); + await guild.channels.create({ + name: server.inGameName, + type: ChannelType.GuildText, + parent: category.id, + topic: `In-game chat bridge for ${server.name}`, + reason: 'Task #98 - Discord channel automation' + }); + stats.textChannelsCreated++; + await sleep(500); + + // Create voice channel + console.log(` Creating: ${server.voiceName}`); + await guild.channels.create({ + name: server.voiceName, + type: ChannelType.GuildVoice, + parent: category.id, + reason: 'Task #98 - Discord channel automation' + }); + stats.voiceChannelsCreated++; + await sleep(500); + + // Create forum + console.log(` Creating: ${server.forumName}`); + const forum = await guild.channels.create({ + name: server.forumName, + type: ChannelType.GuildForum, + parent: category.id, + topic: `Discussion forum for ${server.name}`, + availableTags: STANDARD_FORUM_TAGS.map(tag => ({ + name: tag.name, + emoji: tag.emoji ? { name: tag.emoji } : null + })), + reason: 'Task #98 - Discord channel automation' + }); + stats.forumsCreated++; + await sleep(500); + + // Create welcome post + console.log(` Creating welcome post...`); + await forum.threads.create({ + name: server.welcomeTitle, + message: { content: server.welcomeBody }, + reason: 'Task #98 - Server welcome post' + }); + stats.welcomePostsCreated++; + await sleep(500); + + stats.permissionsApplied++; + } else if (DRY_RUN) { + stats.textChannelsCreated += 2; + stats.voiceChannelsCreated++; + stats.forumsCreated++; + stats.welcomePostsCreated++; + stats.permissionsApplied++; + } + + console.log(` โœ… Done`); + } + + // ======================================================================== + // PHASE 4: Create Archive category + // ======================================================================== + console.log(''); + console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); + console.log('PHASE 4: Create Archive category'); + console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); + + const existingArchive = guild.channels.cache.find( + ch => ch.type === ChannelType.GuildCategory && ch.name.includes('Archive') + ); + if (existingArchive) { + console.log(` โœ“ Archive category already exists: ${existingArchive.name}`); + } else { + console.log(' Creating: ๐Ÿ“ฆ Archive'); + if (!DRY_RUN) { + const staff = findRole(guild, 'Staff'); + const moderator = findRole(guild, '๐Ÿ›ก๏ธ Moderator'); + const wizard = findRole(guild, '๐Ÿ‘‘ The Wizard'); + const emissary = findRole(guild, '๐Ÿ’Ž The Emissary'); + const catalyst = findRole(guild, 'โœจ The Catalyst'); + + const archiveOverwrites = [ + { id: guild.roles.everyone.id, deny: [PermissionFlagsBits.ViewChannel] } + ]; + + [staff, moderator, wizard, emissary, catalyst].filter(Boolean).forEach(role => { + archiveOverwrites.push({ + id: role.id, + allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages] + }); + }); + + await guild.channels.create({ + name: '๐Ÿ“ฆ Archive', + type: ChannelType.GuildCategory, + permissionOverwrites: archiveOverwrites, + position: 999, // Put at bottom + reason: 'Task #98 - Archive category for retired servers' + }); + stats.categoriesCreated++; + } else { + stats.categoriesCreated++; + } + console.log(' โœ… Done'); + } + + // ======================================================================== + // SUMMARY + // ======================================================================== + console.log(''); + console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); + console.log('SUMMARY'); + console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); + console.log(` Mode: ${DRY_RUN ? 'DRY RUN' : 'LIVE'}`); + 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(` Permission sets applied: ${stats.permissionsApplied}`); + + if (stats.errors.length > 0) { + console.log(''); + console.log(' โš ๏ธ Errors:'); + stats.errors.forEach(e => console.log(` - ${e}`)); + } + + console.log(''); + if (DRY_RUN) { + console.log('๐Ÿ” This was a DRY RUN. No changes were made.'); + console.log(' Set DRY_RUN = false to execute for real.'); + } else { + console.log('โœ… All channels created successfully!'); + } + + } catch (error) { + console.error(''); + console.error('โŒ FATAL ERROR:', error.message); + if (error.rawError) { + console.error(' Raw:', JSON.stringify(error.rawError, null, 2)); + } + } finally { + client.destroy(); + console.log(''); + console.log('๐Ÿ‘‹ Disconnected from Discord.'); + } +} + +main();