Add full Discord channel setup script (46 channels)

Task #98: Discord Channel Automation
- Phase 1: Add forums to existing 5 servers + rename categories
- Phase 2: Create 10 new server categories with all channels
- Phase 3: Create Archive category (staff only)

Includes:
- 15 server-specific welcome posts
- Standard forum tags (6 tags)
- Permission template (Wanderer view-only, Server Role full access)
- Rate limiting (500ms delays)
- Idempotent (skips existing channels)

Chronicler: #71
This commit is contained in:
Claude
2026-04-08 16:48:48 +00:00
parent 911f5801fc
commit 9752c6fd89

View File

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