Add full Discord channel setup script (46 channels)

Task #98 implementation:
- Phase 1: Add 🎮 prefix to existing 5 server categories
- Phase 2: Add forums to existing 5 servers
- Phase 3: Create full setup for 10 new servers (category + chat + in-game + voice + forum)
- Phase 4: Create 📦 Archive category (staff-only)

Includes:
- DRY_RUN mode for safe testing
- Permission overwrites (Wanderer=view, Server Role=interact, Staff=full)
- 15 welcome posts with server-specific content
- 6 standard forum tags per forum
- Rate limiting (500ms between API calls)

Chronicler: #71
This commit is contained in:
Claude
2026-04-08 16:50:05 +00:00
parent 9752c6fd89
commit 40cb6cef31

View File

@@ -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();