110 lines
No EOL
3.7 KiB
TypeScript
110 lines
No EOL
3.7 KiB
TypeScript
/**
|
|
* 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<void> {
|
|
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<string, string> = {};
|
|
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); |