gazmull / Kamihime Project NTK - Bypass New DL

/* eslint-disable max-len */

// ==UserScript==
// @name        Kamihime Project NTK - Bypass New DL
// @version     3.0.0
// @icon        https://g.pikan.party/wiki/portrait/(Dream%20Cotton%20Spiral)%20Uriel%20Portrait.png
// @description Have you ever felt that as soon as you read this, you already wasted 4 hours of reading? FOUR. HOURS!one1!!1!one1
// @author      Eunicorn
// @license     MIT
// @match       https://pf.nutaku.com/*
// @match       https://osapi.nutaku.com/*
// @match       https://gnkh-api-r.prod.nkh.dmmgames.com/front/cocos2d-proj/components-pc/game/app.html
// @match       https://gnkh-api-g.prod.nkh.dmmgames.com/front/cocos2d-proj/components-pc/game/app.html
// @match       https://gnkh-api-r.prod.nkh.dmmgames.com/front/cocos2d-proj/components-sp/game/app.html
// @match       https://gnkh-api-g.prod.nkh.dmmgames.com/front/cocos2d-proj/components-sp/game/app.html
// @run-at      document-start
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_registerMenuCommand
// @grant       GM_setClipboard
// @require     https://openuserjs.org/src/libs/sizzle/GM_config.js
// ==/UserScript==

/* eslint-enable max-len */

/* globals GM_config, kh, Q */

const gm = {
  key: 'Ramiel_Sucks_Her_Own_Dick_Yes_Stop_Reading_Me_GTFO',
  async getCredentials () {
    const settings = await GM_getValue(this.key, undefined);

    if (!settings) return undefined;

    return JSON.parse(settings);
  },
  async setCredentials (key, val) {
    const creds = (await this.getCredentials()) || { };

    if (!key) return GM_setValue(this.key, '');

    Object.assign(creds, {
      [key]: val,
      updated: new Date().valueOf()
    });

    return GM_setValue(this.key, JSON.stringify(creds));
  }
};

// GM_Config
/* eslint-disable camelcase */

GM_config.init({
  id: 'ramielcfg',
  title: 'Bypass DL - Settings',
  fields: {
    popup: {
      label: 'Popup Window',
      type: 'checkbox',
      default: true
    },
    size: {
      label: 'Popup Window iFrame Size',
      type: 'text',
      default: '960x640'
    },
    colour: {
      label: 'Popup Window Background Colour',
      type: 'text',
      default: '#000'
    }
  }
});

GM_registerMenuCommand('Bypass DL Settings', () => GM_config.open());

/* eslint-enable camelcase */

const SESSION_ID = 'sessionId';
const CSRF_TOKEN = 'csrfToken';
const RAMIEL_CFG_DEFAULT = '{ "popup": true, "size": "960x640" }';

function getHTML (width, height, colour, versionType, deviceType) {
  /* eslint-disable indent */

  return [
    '<!DOCTYPE html>',
    '<html>',
      '<head>',
        '<title>Uriel is Probably Gay</title>',
      '</head>',
      `<body style="background-color:${colour};">`,
        '<p align="center">',
          // eslint-disable-next-line max-len
          `<iframe title="Ditto" src="https://gnkh-api-${versionType}.prod.nkh.dmmgames.com/front/cocos2d-proj/components-${deviceType}/game/app.html" width="${width}" height="${height}" />`,
        '</p>',
      '</body>',
    '</html>',
  ].join('\n');

  /* eslint-enable indent */
}

// function sleep (ms = 2e3) {
//   return new Promise(res => setTimeout(res, ms));
// }

(async () => {
  const ntkApiRegex = /^https:\/\/(?:pf|osapi)\.nutaku\.com\/gadgets\/ifr.*kamihime/i;

  if (ntkApiRegex.test(window.location.href)) {
    let lastUpdated = new Date().valueOf();
    const interval = setInterval(async () => {
      if (!kh || (kh && !kh.Session)) return;
      if (!kh.Session.getCsrfToken() || !kh.Session.getSessionId()) return;
      if ((new Date().valueOf() - lastUpdated) < 5e3) return;

      await gm.setCredentials(CSRF_TOKEN, kh.Session.getCsrfToken());
      await gm.setCredentials(SESSION_ID, kh.Session.getSessionId());

      clearInterval(interval);

      const deviceType = kh.env.isSp ? 'sp' : 'pc';
      const versionType = kh.env.isR18 ? 'r' : 'g';
      const url = [
        `https://gnkh-api-${versionType}`,
        `prod.nkh.dmmgames.com/.dmmgames.com/front/cocos2d-proj/components-${deviceType}/game/app.html`,
      ].join('.');

      try {
        const gmc = JSON.parse(await GM_getValue('ramielcfg', RAMIEL_CFG_DEFAULT));
        const sized = gmc.size.trim().split('x').filter(s => s).map(s => Number(s));
        const [ width = 960, height = 640 ] = sized;

        if (gmc.popup) {
          // Keeps the window size intact, instead of going to DL right away
          const newWindow = window.open('', '_blank', `width=${width + 25}, height=${height + 25}`);

          newWindow.document.open();
          newWindow.document.write(getHTML(width, height, gmc.colour, versionType, deviceType));
          newWindow.document.close();
          newWindow.focus();

          window.alert('You can close this window now.');
        } else top.window.location.href = url;
      } catch (_) {
        console.error(_);
        await GM_setClipboard(url, 'text');

        alert([
          'Redirect/popup is disabled.',
          'Please allow this site to redirect/popup or change script settings.',
          '\nAnyhow: Game URL has been copied to your clipboard. You can access it now.',
        ].join('\n'));
      }
    }, 30);

    return;
  }

  const creds = await gm.getCredentials();
  const fail = async () => {
    const isAdultVersion = /cf\.r/i.test(window.location.href);
    const url = `https://nutaku.${isAdultVersion ? 'net' : 'com'}/games/kamihime${isAdultVersion ? '-r' : ''}/play`;

    await GM_setClipboard(url, 'text');

    return alert([
      'You must go to normal game window first.',
      '\nURL has been copied to your clipboard if you don\'t know it.',
    ].join('\n'));
  };

  if (!creds) return fail();
  // If token already 1 hour last updated
  if ((new Date().valueOf() - creds.updated) > 36e5) {
    await gm.setCredentials(undefined);

    return fail();
  }

  const interval = setInterval(() => {
    if (!kh || (kh && !kh.GameSession)) return;

    clearInterval(interval);

    Object.defineProperties(kh.GameSession.prototype, {
      awaitSetFirstSession: {
        value () {
          this._sendParentFrameFlag = false;

          this.setSessionId(creds.sessionId, false, true);
          this.setCsrfToken(creds.csrfToken, false, true);

          return Q.resolve();
        }
      },
      setSessionId: {
        value (val, toParent = true, initial) {
          this.set(SESSION_ID, val, toParent);

          if (!initial) gm.setCredentials(SESSION_ID, val);
        }
      },
      setCsrfToken: {
        value (val, toParent = true, initial) {
          this.set(CSRF_TOKEN, val, toParent);

          if (!initial) gm.setCredentials(CSRF_TOKEN, val);
        }
      }
    });
  }, 30);
})();