Mein Code
// HoH Helper -> 5by Wiki Import (CLEAN)
const usernameKeychainKey = "hoh_helper_mobile_username";
const passwordKeychainKey = "hoh_helper_mobile_password";
const PROTOBUF_CONTENT_TYPE = "application/x-protobuf";
const JSON_CONTENT_TYPE = "application/json";
// Login läuft über die Browser-Seite
const loginUrl = "https://www.heroesgame.com/api/login";
const accountPlayUrl = "https://un0.heroesofhistorygame.com/core/api/account/play";
const startupApiUrl = "https://un1.heroesofhistorygame.com/game/startup";
// ✅ DEINE Wiki API
const wikiInGameDataUrl = "https://wiki.5by.de/wp-json/hoh/v1/ingame";
// Profil öffnen nach Export
const WIKI_PROFILE_KEYCHAIN = "hoh_helper_wiki_profile_login";
// Optional Secret
const WIKI_SECRET_KEYCHAIN = "hoh_helper_wiki_secret";
function addDefaultHeaders() {
return { "Content-Type": JSON_CONTENT_TYPE };
}
function addStartupHeaders(sessionData) {
return {
"X-AUTH-TOKEN": sessionData.sessionId,
"X-Request-Id": UUID.string(),
"X-Platform": "browser",
"X-ClientVersion": sessionData.clientVersion,
"Accept-Encoding": "gzip",
"Accept": PROTOBUF_CONTENT_TYPE,
"Content-Type": PROTOBUF_CONTENT_TYPE
};
}
function checkCredentials() {
return Keychain.contains(usernameKeychainKey) && Keychain.contains(passwordKeychainKey);
}
async function setupCredentials() {
const prompt = new Alert();
prompt.message = "Enter your in-game credentials.";
prompt.addTextField("Username");
prompt.addSecureTextField("Password");
prompt.addAction("Save");
prompt.addCancelAction("Cancel");
const action = await prompt.present();
if (action === 0) {
Keychain.set(usernameKeychainKey, prompt.textFieldValue(0));
Keychain.set(passwordKeychainKey, prompt.textFieldValue(1));
}
return action;
}
async function pickAction() {
const prompt = new Alert();
prompt.message = "Run script, delete credentials, or set wiki secret?";
prompt.addAction("Run");
prompt.addDestructiveAction("Delete credentials");
prompt.addAction("Set wiki secret");
prompt.addCancelAction("Cancel");
return await prompt.present();
}
async function deleteCredentials() {
Keychain.remove(usernameKeychainKey);
Keychain.remove(passwordKeychainKey);
}
async function setupWikiSecret() {
const prompt = new Alert();
prompt.title = "Wiki Secret (optional)";
prompt.message = "If your wiki endpoint requires a secret, paste it here.";
prompt.addSecureTextField("Secret");
prompt.addAction("Save");
prompt.addCancelAction("Cancel");
const action = await prompt.present();
if (action === 0) {
Keychain.set(WIKI_SECRET_KEYCHAIN, prompt.textFieldValue(0));
}
}
async function getWikiProfileLogin() {
if (Keychain.contains(WIKI_PROFILE_KEYCHAIN)) {
return Keychain.get(WIKI_PROFILE_KEYCHAIN);
}
const prompt = new Alert();
prompt.title = "Wiki Profil-Login";
prompt.message = "Gib deinen WordPress Login-Namen ein (Teil der URL /player/DEINLOGIN/).";
prompt.addTextField("Login (z.B. Noah");
prompt.addAction("Speichern");
prompt.addCancelAction("Cancel");
const res = await prompt.present();
if (res !== 0) return null;
const v = (prompt.textFieldValue(0) || "").trim();
if (!v) return null;
Keychain.set(WIKI_PROFILE_KEYCHAIN, v);
return v;
}
async function login() {
const username = Keychain.get(usernameKeychainKey);
const password = Keychain.get(passwordKeychainKey);
const loginReq = new Request(loginUrl);
loginReq.method = "POST";
loginReq.headers = addDefaultHeaders();
loginReq.body = JSON.stringify({ username, password, useRememberMe: false });
const loginData = await loginReq.loadJSON();
if (!loginData || !loginData.redirectUrl) {
const err = new Alert();
err.title = "Login fehlgeschlagen";
err.message =
"Die Login-API hat keine redirectUrl zurückgegeben.\n\nAntwort:\n" +
JSON.stringify(loginData, null, 2).slice(0, 1500);
err.addAction("OK");
await err.present();
return null;
}
const redirectReq = new Request(loginData.redirectUrl);
redirectReq.method = "GET";
const redirectHtml = await redirectReq.loadString();
const sessionCookie = redirectReq.response.cookies.find(c => c.name === "SESSION");
const cookieHeader = sessionCookie ? "SESSION=" + sessionCookie.value : "";
if (!cookieHeader) throw new Error("SESSION cookie not found.");
const clientVersionMatch = redirectHtml.match(/const\s+clientVersion\s*=\s*"([^"]+)"/);
if (!clientVersionMatch) throw new Error("Client version not found.");
const clientVersion = clientVersionMatch[1];
const playPayload = {
createDeviceToken: false,
meta: {
clientVersion,
device: "browser",
deviceHardware: "browser",
deviceManufacturer: "none",
deviceName: "browser",
locale: "en_DK",
networkType: "wlan",
operatingSystemName: "browser",
operatingSystemVersion: "1",
userAgent: "hoh-helper-mobile"
},
network: "BROWSER_SESSION",
token: "",
worldId: null
};
const playReq = new Request(accountPlayUrl);
playReq.method = "POST";
playReq.headers = {
"Content-Type": JSON_CONTENT_TYPE,
"Cookie": cookieHeader
};
playReq.body = JSON.stringify(playPayload);
const sessionData = await playReq.loadJSON();
sessionData.clientVersion = clientVersion;
return sessionData;
}
async function getStartupAsync(sessionData) {
const req = new Request(startupApiUrl);
req.method = "POST";
req.headers = addStartupHeaders(sessionData);
const response = await req.load();
return response.toBase64String();
}
async function sendStartupToWikiAsync(startupBase64) {
const req = new Request(wikiInGameDataUrl);
req.method = "POST";
req.headers = addDefaultHeaders();
const secret = Keychain.contains(WIKI_SECRET_KEYCHAIN) ? Keychain.get(WIKI_SECRET_KEYCHAIN) : "";
if (secret) req.headers["X-HOH-SECRET"] = secret;
req.body = JSON.stringify({ inGameStartupData: startupBase64 });
return await req.loadJSON();
}
async function main() {
if (!checkCredentials()) {
const res = await setupCredentials();
if (res !== 0) return;
if (!checkCredentials()) return;
} else {
const action = await pickAction();
if (action === 1) { await deleteCredentials(); return; }
if (action === 2) { await setupWikiSecret(); return; }
if (action === 3) return;
}
const sessionData = await login();
if (!sessionData) return;
const startupDataBase64 = await getStartupAsync(sessionData);
const wikiResponse = await sendStartupToWikiAsync(startupDataBase64);
// Debug anzeigen (kurz)
const debug = new Alert();
debug.title = "Wiki Response";
debug.message = JSON.stringify(wikiResponse, null, 2).slice(0, 1800);
debug.addAction("OK");
await debug.present();
if (!wikiResponse?.heroesJson) {
const fail = new Alert();
fail.title = "Import fehlgeschlagen";
fail.message = "Die Wiki hat kein heroesJson zurückgegeben.";
fail.addAction("OK");
await fail.present();
return;
}
Pasteboard.copyString(wikiResponse.heroesJson);
const done = new Alert();
done.title = "Export fertig";
done.message = "Helden-JSON wurde in die Zwischenablage kopiert.";
done.addAction("OK");
await done.present();
const profileLogin = await getWikiProfileLogin();
if (profileLogin) {
Safari.open(`https://wiki.5by.de/player/${encodeURIComponent(profileLogin)}/#import`);
}
}
await main();