Spaces:
Sleeping
Sleeping
| require("dotenv").config(); | |
| const fs = require("fs"); | |
| const path = require("path"); | |
| const { MongoClient } = require("mongodb"); | |
| const DB_FILE = path.join(__dirname, "db.json"); | |
| const CSV_FILE = path.join(__dirname, "characters.csv"); | |
| // Helper to parse a CSV line, respecting double quotes | |
| function parseCSVLine(line) { | |
| const result = []; | |
| let current = ""; | |
| let inQuotes = false; | |
| for (let i = 0; i < line.length; i++) { | |
| const char = line[i]; | |
| if (char === '"') { | |
| inQuotes = !inQuotes; | |
| } else if (char === "," && !inQuotes) { | |
| result.push(current.trim()); | |
| current = ""; | |
| } else { | |
| current += char; | |
| } | |
| } | |
| result.push(current.trim()); | |
| return result; | |
| } | |
| // Generate template if CSV does not exist | |
| if (!fs.existsSync(CSV_FILE)) { | |
| const templateContent = `Name,Base price,features,Image URL 1,Caption 1,Image URL 2,Caption 2,Image URL 3,CAption 3,Category | |
| Goku,100,"Series: Dragon Ball; Rarity: SSR",AgACAg...,Goku Super Saiyan,AgACAg...,Goku Base Form,,,Anime | |
| Naruto,80,"Series: Naruto; Rarity: SR",AgACAg...,Naruto Sage Mode,,,,,,Anime | |
| Luffy,90,"Series: One Piece; Rarity: UR",AgACAg...,Luffy Gear 5,,,,,,Anime | |
| `; | |
| fs.writeFileSync(CSV_FILE, templateContent, "utf8"); | |
| console.log("๐ No 'characters.csv' file was found."); | |
| console.log("โ Created a sample 'characters.csv' with the requested columns!"); | |
| console.log("โน๏ธ Open 'characters.csv' in Excel/Google Sheets, add your characters, and run this script again."); | |
| process.exit(0); | |
| } | |
| const fileContent = fs.readFileSync(CSV_FILE, "utf8"); | |
| const lines = fileContent.split(/\r?\n/).filter((line) => line.trim() !== ""); | |
| if (lines.length <= 1) { | |
| console.log("โ ๏ธ The 'characters.csv' file is empty or only contains the header row."); | |
| process.exit(0); | |
| } | |
| // Skip header | |
| const header = lines[0]; | |
| const dataLines = lines.slice(1); | |
| let successCount = 0; | |
| let failCount = 0; | |
| const errors = []; | |
| const importedChars = []; | |
| dataLines.forEach((line, index) => { | |
| const lineNum = index + 2; // 1-indexed plus header line offset | |
| const parts = parseCSVLine(line); | |
| // We expect at least the first 3 columns | |
| if (parts.length < 3) { | |
| failCount++; | |
| errors.push(`Line ${lineNum}: Missing core columns (Expected Name, Base price, features)`); | |
| return; | |
| } | |
| const name = parts[0]; | |
| const basePriceVal = parts[1]; | |
| const featuresRaw = parts[2]; | |
| const img1 = parts[3]; | |
| const cap1 = parts[4]; | |
| const img2 = parts[5]; | |
| const cap2 = parts[6]; | |
| const img3 = parts[7]; | |
| const cap3 = parts[8]; | |
| const category = parts[9]; | |
| if (!name) { | |
| failCount++; | |
| errors.push(`Line ${lineNum}: Character Name is required.`); | |
| return; | |
| } | |
| const base_price = parseInt(basePriceVal, 10); | |
| if (isNaN(base_price) || base_price < 0) { | |
| failCount++; | |
| errors.push(`Line ${lineNum}: Invalid Base Price '${basePriceVal}'. Must be a non-negative number.`); | |
| return; | |
| } | |
| // Parse features (separated by ;) | |
| const features = featuresRaw | |
| ? featuresRaw | |
| .split(";") | |
| .map((f) => f.trim()) | |
| .filter((f) => f.length > 0) | |
| : []; | |
| // Build image_url list | |
| const image_url = []; | |
| if (img1) { | |
| if (cap1) { | |
| image_url.push({ file_id: img1, caption: cap1 }); | |
| } else { | |
| image_url.push(img1); | |
| } | |
| } | |
| if (img2) { | |
| if (cap2) { | |
| image_url.push({ file_id: img2, caption: cap2 }); | |
| } else { | |
| image_url.push(img2); | |
| } | |
| } | |
| if (img3) { | |
| if (cap3) { | |
| image_url.push({ file_id: img3, caption: cap3 }); | |
| } else { | |
| image_url.push(img3); | |
| } | |
| } | |
| // Generate unique character ID | |
| const id = `custom_${Date.now()}_${Math.floor(1000 + Math.random() * 9000)}`; | |
| importedChars.push({ | |
| id, | |
| name, | |
| image_url, | |
| base_price, | |
| features, | |
| category: category || "", | |
| addedBy: "bulk_import" | |
| }); | |
| successCount++; | |
| }); | |
| async function saveImportedData() { | |
| if (successCount === 0) { | |
| console.log(`\nโ Bulk Import Failed.`); | |
| return; | |
| } | |
| const mongoUri = process.env.MONGODB_URI; | |
| if (mongoUri) { | |
| console.log("\n๐ Connecting to MongoDB for import..."); | |
| let client; | |
| try { | |
| client = new MongoClient(mongoUri); | |
| await client.connect(); | |
| const db = client.db(); | |
| const collection = db.collection("bot_state"); | |
| const record = await collection.findOne({ _id: "state" }); | |
| let currentDB = record ? record.data : { customCharacters: [] }; | |
| currentDB.customCharacters = currentDB.customCharacters || []; | |
| currentDB.customCharacters.push(...importedChars); | |
| await collection.updateOne( | |
| { _id: "state" }, | |
| { $set: { data: currentDB } }, | |
| { upsert: true } | |
| ); | |
| console.log(`\n๐ Bulk Import Complete (MongoDB Atlas)!`); | |
| console.log(`โ Successfully imported *${successCount}* characters.`); | |
| } catch (err) { | |
| console.error("โ Failed to save changes to MongoDB:", err.message || err); | |
| process.exit(1); | |
| } finally { | |
| if (client) await client.close(); | |
| } | |
| } else { | |
| // Fallback to local db.json | |
| console.log("\n๐พ Saving to local db.json..."); | |
| let localDB = { customCharacters: [] }; | |
| if (fs.existsSync(DB_FILE)) { | |
| try { | |
| localDB = JSON.parse(fs.readFileSync(DB_FILE, "utf8")); | |
| } catch (err) { | |
| console.error("โ Failed to parse local db.json:", err); | |
| process.exit(1); | |
| } | |
| } | |
| localDB.customCharacters = localDB.customCharacters || []; | |
| localDB.customCharacters.push(...importedChars); | |
| try { | |
| fs.writeFileSync(DB_FILE, JSON.stringify(localDB, null, 2), "utf8"); | |
| console.log(`\n๐ Bulk Import Complete (local file)!`); | |
| console.log(`โ Successfully imported *${successCount}* characters.`); | |
| } catch (err) { | |
| console.error("โ Failed to save changes to db.json:", err); | |
| process.exit(1); | |
| } | |
| } | |
| if (failCount > 0) { | |
| console.log(`โ ๏ธ Failed to import *${failCount}* rows due to formatting/validation errors:`); | |
| errors.forEach((err) => console.log(` - ${err}`)); | |
| } | |
| } | |
| saveImportedData(); | |