Heroes of History Wiki

Code anzeigen

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();