184 lines
4.8 KiB
JavaScript
184 lines
4.8 KiB
JavaScript
import fs from "fs";
|
|
import path from "path";
|
|
|
|
/**
|
|
* Blizzard Account Library Importer
|
|
* Nutzt OAuth 2.0 für Authentifizierung
|
|
*
|
|
* Unterstützt:
|
|
* - World of Warcraft
|
|
* - Diablo
|
|
* - Overwatch
|
|
* - StarCraft
|
|
* - Warcraft III
|
|
* - Heroes of the Storm
|
|
* - Hearthstone
|
|
*/
|
|
|
|
const loadConfig = () => {
|
|
const configPath = path.join(process.cwd(), "config.local.json");
|
|
try {
|
|
if (fs.existsSync(configPath)) {
|
|
return JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
}
|
|
} catch (error) {
|
|
console.log("⚠️ Config nicht lesbar, nutze Defaults");
|
|
}
|
|
return {
|
|
blizzard: {
|
|
clientId: "",
|
|
clientSecret: "",
|
|
accountName: "",
|
|
region: "eu",
|
|
},
|
|
};
|
|
};
|
|
|
|
const fetchBlizzardGames = async ({ clientId, clientSecret, region }) => {
|
|
// OAuth 2.0 Token Endpoint
|
|
const tokenUrl = `https://${region}.battle.net/oauth/token`;
|
|
const libraryUrl = `https://${region}.api.blizzard.com/d3/profile/${clientId}/hero`;
|
|
|
|
try {
|
|
// Schritt 1: Bearer Token holen (Client Credentials Flow)
|
|
const tokenResponse = await fetch(tokenUrl, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
Authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString("base64")}`,
|
|
},
|
|
body: new URLSearchParams({
|
|
grant_type: "client_credentials",
|
|
scope: "d3.profile.us",
|
|
}),
|
|
});
|
|
|
|
if (!tokenResponse.ok) {
|
|
throw new Error(
|
|
`Token-Fehler: ${tokenResponse.status} - ${await tokenResponse.text()}`,
|
|
);
|
|
}
|
|
|
|
const { access_token } = await tokenResponse.json();
|
|
|
|
// Schritt 2: Games/Accountinfo laden
|
|
const gamesResponse = await fetch(libraryUrl, {
|
|
headers: {
|
|
Authorization: `Bearer ${access_token}`,
|
|
"User-Agent": "WhatToPlay/1.0",
|
|
},
|
|
});
|
|
|
|
if (!gamesResponse.ok) {
|
|
console.warn(
|
|
`⚠️ Blizzard API: ${gamesResponse.status} - Möglicherweise falscher Region oder Credentials`,
|
|
);
|
|
return [];
|
|
}
|
|
|
|
const data = await gamesResponse.json();
|
|
|
|
// Blizzard gibt Heros statt Games zurück
|
|
// Wir extrahieren Informationen über verfügbare Spiele
|
|
return data.heroes || [];
|
|
} catch (error) {
|
|
console.error(`❌ Blizzard Fehler: ${error.message}`);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
const buildBlizzardEntry = (hero, gameType = "Diablo III") => ({
|
|
id: `blizzard-${hero.id}`,
|
|
title: `${gameType} - ${hero.name}`,
|
|
platform: "Blizzard",
|
|
class: hero.class,
|
|
level: hero.level,
|
|
experience: hero.experience,
|
|
killed: hero.kills?.elites || 0,
|
|
hardcore: hero.hardcore || false,
|
|
lastPlayed: hero.lastUpdated
|
|
? new Date(hero.lastUpdated).toISOString()
|
|
: null,
|
|
url: `https://www.diablo3.com/en/profile/${hero.id}/`,
|
|
});
|
|
|
|
const buildTextFile = (game) => {
|
|
const lines = [
|
|
`# ${game.title}`,
|
|
"",
|
|
`**Plattform**: ${game.platform}`,
|
|
`**Charaktertyp**: ${game.class || "Unbekannt"}`,
|
|
`**Level**: ${game.level || "N/A"}`,
|
|
game.hardcore ? `**Hardcore**: Ja ⚔️` : "",
|
|
`**Elite-Kills**: ${game.killed || 0}`,
|
|
`**Erfahrung**: ${game.experience || 0}`,
|
|
game.lastPlayed
|
|
? `**Zuletzt gespielt**: ${new Date(game.lastPlayed).toLocaleDateString("de-DE")}`
|
|
: "",
|
|
"",
|
|
`[Im Profil anschauen](${game.url})`,
|
|
];
|
|
|
|
return lines.filter(Boolean).join("\n");
|
|
};
|
|
|
|
const writeBlizzardData = async (games) => {
|
|
const dataDir = path.join(process.cwd(), "public/data");
|
|
const textDir = path.join(dataDir, "blizzard-text");
|
|
|
|
// Stelle sicher dass Verzeichnisse existieren
|
|
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
|
|
if (!fs.existsSync(textDir)) fs.mkdirSync(textDir, { recursive: true });
|
|
|
|
// Schreibe JSON-Datei
|
|
fs.writeFileSync(
|
|
path.join(dataDir, "blizzard.json"),
|
|
JSON.stringify(games, null, 2),
|
|
"utf-8",
|
|
);
|
|
|
|
// Schreibe Text-Dateien für jeden Hero
|
|
games.forEach((game) => {
|
|
const textFile = `${game.id}.txt`;
|
|
const filePath = path.join(textDir, textFile);
|
|
const content = buildTextFile(game);
|
|
fs.writeFileSync(filePath, content, "utf-8");
|
|
});
|
|
|
|
return games.length;
|
|
};
|
|
|
|
const main = async () => {
|
|
const config = loadConfig();
|
|
const { clientId, clientSecret, region } = config.blizzard || {};
|
|
|
|
if (!clientId || !clientSecret) {
|
|
console.log(
|
|
"⚠️ Blizzard: Keine Credentials - Überspringe\n → Für iOS/Web: Backend mit OAuth benötigt\n → Siehe docs/BLIZZARD-SETUP.md für Development-Setup",
|
|
);
|
|
return;
|
|
}
|
|
|
|
console.log("⏳ Blizzard-Games laden...");
|
|
const games = await fetchBlizzardGames({
|
|
clientId,
|
|
clientSecret,
|
|
region: region || "eu",
|
|
});
|
|
|
|
if (games.length === 0) {
|
|
console.log(
|
|
"⚠️ Keine Blizzard-Games gefunden\n → Stelle sicher dass der Account mit Heros in Diablo III hat",
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Verarbeite jeden Hero
|
|
const processedGames = games.map((hero) => buildBlizzardEntry(hero));
|
|
|
|
const count = await writeBlizzardData(processedGames);
|
|
console.log(`✓ Blizzard-Export fertig: ${count} Charaktere`);
|
|
};
|
|
|
|
main().catch(console.error);
|