/** * Bulk emoji upload script * Usage: npx ts-node scripts/upload-emojis.ts [emoji_dir] * * Place emoji PNG files in a directory named after the emoji key. * Example: fb.png, wi.png, capella.png, wrank_1.png, wrank_1_gold.png * * Automatically updates messages/emojis.json with the uploaded emoji IDs. */ import { REST, Routes } from "discord.js"; import fs from "fs"; import path from "path"; // Load .env manually since we're outside the bot const envPath = path.join(__dirname, "../.env"); if (fs.existsSync(envPath)) { for (const line of fs.readFileSync(envPath, "utf8").split("\n")) { const [key, ...rest] = line.split("="); if (key && rest.length) process.env[key.trim()] = rest.join("=").trim(); } } const TOKEN = process.env.DISCORD_TOKEN!; const GUILD_ID = process.env.GUILD_ID!; if (!TOKEN || !GUILD_ID) { console.error("❌ DISCORD_TOKEN and GUILD_ID must be set in .env"); process.exit(1); } const emojiDir = process.argv[2] ?? path.join(__dirname, "../emoji-uploads"); const emojisPath = path.join(__dirname, "../messages/emojis.json"); if (!fs.existsSync(emojiDir)) { console.error(`❌ Emoji directory not found: ${emojiDir}`); console.error(` Create it and place your emoji PNG files inside.`); process.exit(1); } const rest = new REST({ version: "10" }).setToken(TOKEN); async function uploadEmojis(): Promise { const files = fs.readdirSync(emojiDir).filter((f) => [".png", ".jpg", ".gif", ".webp"].includes(path.extname(f).toLowerCase()) ); if (files.length === 0) { console.error("❌ No image files found in the emoji directory."); process.exit(1); } // Load existing emojis.json let emojiMap: Record = {}; try { emojiMap = JSON.parse(fs.readFileSync(emojisPath, "utf8")); } catch { console.warn("⚠️ Could not load emojis.json — will create fresh mapping."); } console.log(`📁 Found ${files.length} file(s) in ${emojiDir}\n`); // Fetch existing guild emojis to skip duplicates const existing = await rest.get(Routes.guildEmojis(GUILD_ID)) as any[]; const existingMap = new Map(existing.map((e: any) => [e.name, e.id])); let uploaded = 0; let skipped = 0; let failed = 0; for (const file of files) { const emojiName = path.basename(file, path.extname(file)); const filePath = path.join(emojiDir, file); const ext = path.extname(file).toLowerCase(); const mimeType = ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/png"; if (existingMap.has(emojiName)) { const formatted = `<:${emojiName}:${existingMap.get(emojiName)}>`; emojiMap[emojiName] = formatted; console.log(`⏭️ Already exists: ${emojiName} → ${formatted}`); skipped++; continue; } try { const base64 = `data:${mimeType};base64,${fs.readFileSync(filePath).toString("base64")}`; const result = await rest.post(Routes.guildEmojis(GUILD_ID), { body: { name: emojiName, image: base64 }, }) as any; const formatted = `<:${emojiName}:${result.id}>`; emojiMap[emojiName] = formatted; console.log(`✅ Uploaded: ${emojiName} → ${formatted}`); uploaded++; // Avoid rate limiting await new Promise((r) => setTimeout(r, 600)); } catch (err: any) { console.error(`❌ Failed: ${emojiName} — ${err.message}`); failed++; } } fs.writeFileSync(emojisPath, JSON.stringify(emojiMap, null, 2)); console.log(`\n📊 ${uploaded} uploaded · ${skipped} skipped · ${failed} failed`); console.log(`💾 messages/emojis.json updated`); } uploadEmojis().catch(console.error);