Furaffinity-Request-Helper

Helper Library to simplyfy requests to Furaffinity

Tätä skriptiä ei tulisi asentaa suoraan. Se on kirjasto muita skriptejä varten sisällytettäväksi metadirektiivillä // @require https://update.greasyfork.org/scripts/483952/1478384/Furaffinity-Request-Helper.js.

// ==UserScript==
// @name        Furaffinity-Request-Helper
// @namespace   Violentmonkey Scripts
// @grant       none
// @version     1.0.4
// @author      Midori Dragon
// @description Helper Library to simplyfy requests to Furaffinity
// @icon        https://www.furaffinity.net/themes/beta/img/banners/fa_logo.png?v2
// @homepageURL https://greasyfork.org/de/scripts/483952-furaffinity-request-helper
// @supportURL  https://greasyfork.org/de/scripts/483952-furaffinity-request-helper/feedback
// @license     MIT
// ==/UserScript==

// jshint esversion: 8

(() => {
  //#region Helper Classes
  class Semaphore {
    constructor(maxConcurrency) {
      this.maxConcurrency = maxConcurrency;
      this.currentConcurrency = 0;
      this.waitingQueue = [];
    }

    acquire() {
      return new Promise((resolve, reject) => {
        if (this.currentConcurrency < this.maxConcurrency) {
          this.currentConcurrency++;
          resolve();
        } else this.waitingQueue.push(resolve);
      });
    }

    release() {
      if (this.waitingQueue.length > 0) {
        let nextResolve = this.waitingQueue.shift();
        nextResolve();
      } else this.currentConcurrency--;
    }
  }

  class PercentHelper {
    constructor() {
      throw new Error("The PercentHelper class cannot be instantiated.");
    }

    static percentAll = [];

    static setPercentValue(id, value) {
      if (id && value && PercentHelper.percentAll.hasOwnProperty(id)) {
        PercentHelper.percentAll[id] = value;
        return true;
      } else return false;
    }

    static getPercentValue(id, decimalPlaces = 2) {
      if (!id) return -1;
      const percent = PercentHelper.percentAll[id];
      if (!percent) return -1;
      return percent.toFixed(decimalPlaces);
    }

    static createPercentValue(uniqueId) {
      if (!uniqueId) uniqueId = Date.now() + Math.random();
      PercentHelper.percentAll[uniqueId] = 0;
      return uniqueId;
    }

    static deletePercentValue(id) {
      if (PercentHelper.percentAll.hasOwnProperty(id)) delete PercentHelper.percentAll[id];
    }
  }

  class WaitAndCallAction {
    constructor(action, usePercent, delay = 100) {
      this.action = action;
      this.delay = delay;
      this.intervalId;
      this.usePercent = usePercent;
      this._running = false;
    }

    start() {
      if (this.action && !this._running) {
        this._running = true;
        this.intervalId = setInterval(() => {
          if (this.usePercent) this.action(PercentHelper.getPercentValue(this.intervalId));
          else this.action();
        }, this.delay);
        if (this.usePercent) PercentHelper.createPercentValue(this.intervalId);
        return this.intervalId;
      }
    }
    stop() {
      if (this._running) {
        this._running = false;
        clearInterval(this.intervalId);
        if (this.usePercent) PercentHelper.deletePercentValue(this.intervalId);
      }
    }
  }

  class Parameters {
    constructor() {
      this.allParameters = [];
    }

    get length() {
      return this.allParameters.length;
    }

    setParameter(name, value) {
      this.allParameters[name] = value;
    }

    getParameter(name) {
      if (this.allParameters.hasOwnProperty(name)) return this.allParameters[name];
    }

    removeParameter(name) {
      if (this.allParameters.hasOwnProperty(name)) delete this.allParameters[name];
    }
  }
  //#endregion

  //#region Request Helper
  class FuraffinityRequestHelper {
    constructor(maxAmountRequests = 2) {
      this._semaphore = new Semaphore(maxAmountRequests);
      this.UserRequests = new UserRequests(this._semaphore);
      this.PersonalUserRequests = new PersonalUserRequests(this._semaphore);
      this.SubmissionRequests = new SubmissionRequests(this._semaphore);
    }

    set maxAmountRequests(value) {
      if (this._semaphore.maxConcurrency == value) return;
      this._semaphore.maxConcurrency = value;
    }
    get maxAmountRequests() {
      return this._semaphore.maxConcurrency;
    }

    set useHttps(value) {
      if (this._useHttps == value) return;
      this._useHttps = value;
      if (value) _httpsString = "https://";
      else _httpsString = "http://";
    }
    get useHttps() {
      return this._useHttps;
    }

    static logLevel = 1;
    static domain = "www.furaffinity.net";
    static _useHttps = true;
    static _httpsString = "https://";

    static getUrl() {
      return FuraffinityRequestHelper._httpsString + FuraffinityRequestHelper.domain;
    }

    async getHTML(url, action, delay = 100) {
      const waitAndCallAction = new WaitAndCallAction(action, delay);
      waitAndCallAction.start();
      const html = await getHTMLLocal(url, this._semaphore);
      waitAndCallAction.stop();
      return html;
    }
  }
  window.FARequestHelper = FuraffinityRequestHelper;

  async function getHTMLLocal(url, semaphore) {
    const semaphoreActive = semaphore && semaphore.maxConcurrency > 0;
    if (semaphoreActive) await semaphore.acquire();
    try {
      const response = await fetch(url);
      const html = await response.text();
      const parser = new DOMParser();
      const doc = parser.parseFromString(html, "text/html");
      return doc;
    } catch (error) {
      logError(error);
    } finally {
      if (semaphoreActive) semaphore.release();
    }
  }

  async function sendHttpPostLocal(url, payload, semaphore) {
    const semaphoreActive = semaphore && semaphore.maxConcurrency > 0;
    if (semaphoreActive) await semaphore.acquire();
    try {
      const response = await fetch(url, {
        method: "POST",
        body: new URLSearchParams(
          payload.reduce((acc, { key, value }) => {
            acc.append(key, value);
            return acc;
          }, new URLSearchParams())
        ).toString(),
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        },
      });

      if (!response.ok) {
        logError(`HTTP error! Status: ${response.status}`);
        return;
      }

      const responseData = await response.text();
      return responseData;
    } catch (error) {
      logError(error);
    } finally {
      if (semaphoreActive) semaphore.release();
    }
  }

  //#endregion

  //#region User Requests
  class UserRequests {
    constructor(semaphore) {
      this.semaphore = semaphore;
      this.GalleryRequests = new GalleryRequests(semaphore);
    }

    static hardLinks = {
      user: FuraffinityRequestHelper.getUrl() + "/user/",
      watch: FuraffinityRequestHelper.getUrl() + "/watch/",
      unwatch: FuraffinityRequestHelper.getUrl() + "/unwatch/",
      block: FuraffinityRequestHelper.getUrl() + "/block/",
      unblock: FuraffinityRequestHelper.getUrl() + "/unblock/",
    };

    async getUserPage(username, action, delay = 100) {
      return await getUserPageHandleLocal(username, action, delay, this.semaphore);
    }

    async watchUser(username, watchKey, action, delay = 100) {
      return await watchUserHandleLocal(username, watchKey, action, delay, this.semaphore);
    }

    async unwatchUser(username, unwatchKey, action, delay = 100) {
      return await unwatchUserHandleLocal(username, unwatchKey, action, delay, this.semaphore);
    }

    async blockUser(username, blockKey, action, delay = 100) {
      return await blockUserHandleLocal(username, blockKey, action, delay, this.semaphore);
    }

    async unblockUser(username, unblockKey, action, delay = 100) {
      return await unblockUserHandleLocal(username, unblockKey, action, delay, this.semaphore);
    }
  }

  async function getUserPageHandleLocal(username, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const getUserPage = await getUserPageLocal(username, semaphore);
    waitAndCallAction.stop();
    return getUserPage;
  }

  async function getUserPageLocal(username, semaphore) {
    if (!username) {
      logWarning("No username given");
      return null;
    }

    if (!UserRequests.hardLinks["user"].endsWith("/")) UserRequests.hardLinks["user"] += "/";

    const url = UserRequests.hardLinks["user"] + username;
    if (url) return await getHTMLLocal(url, semaphore);
    else return null;
  }

  async function watchUserHandleLocal(username, watchKey, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const success = await watchUserLocal(username, watchKey, semaphore);
    waitAndCallAction.stop();
    return success;
  }

  async function watchUserLocal(username, watchKey, semaphore) {
    if (!username) {
      logWarning("No username given");
      return null;
    }
    if (!watchKey) {
      logWarning("No watch key given");
      return null;
    }

    if (!UserRequests.hardLinks["watch"].endsWith("/")) UserRequests.hardLinks["watch"] += "/";
    const url = UserRequests.hardLinks["watch"] + username + "?key=" + watchKey;

    if (url) {
      if (await getHTMLLocal(url, semaphore)) return true;
      else return false;
    } else return false;
  }

  async function unwatchUserHandleLocal(username, unwatchKey, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const success = await unwatchUserLocal(username, unwatchKey, semaphore);
    waitAndCallAction.stop();
    return success;
  }

  async function unwatchUserLocal(username, unwatchKey, semaphore) {
    if (!username) {
      logWarning("No username given");
      return null;
    }
    if (!unwatchKey) {
      logWarning("No unwatch key given");
      return null;
    }

    if (!UserRequests.hardLinks["unwatch"].endsWith("/")) UserRequests.hardLinks["unwatch"] += "/";
    const url = UserRequests.hardLinks["unwatch"] + username + "?key=" + unwatchKey;

    if (url) {
      if (await getHTMLLocal(url, semaphore)) return true;
      else return false;
    } else return false;
  }

  async function blockUserHandleLocal(username, blockKey, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const success = await blockUserLocal(username, blockKey, semaphore);
    waitAndCallAction.stop();
    return success;
  }

  async function blockUserLocal(username, blockKey, semaphore) {
    if (!username) {
      logWarning("No username given");
      return null;
    }
    if (!blockKey) {
      logWarning("No block key given");
      return null;
    }

    if (!UserRequests.hardLinks["block"].endsWith("/")) UserRequests.hardLinks["block"] += "/";
    const url = UserRequests.hardLinks["block"] + username + "?key=" + blockKey;

    if (url) {
      if (await getHTMLLocal(url, semaphore)) return true;
      else return false;
    } else return false;
  }

  async function unblockUserHandleLocal(username, unblockKey, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const success = await unblockUserLocal(username, unblockKey, semaphore);
    waitAndCallAction.stop();
    return success;
  }

  async function unblockUserLocal(username, unblockKey, semaphore) {
    if (!username) {
      logWarning("No username given");
      return null;
    }
    if (!unblockKey) {
      logWarning("No unblock key given");
      return null;
    }

    if (!UserRequests.hardLinks["unblock"].endsWith("/")) UserRequests.hardLinks["unblock"] += "/";
    const url = UserRequests.hardLinks["unblock"] + username + "?key=" + unblockKey;

    if (url) {
      if (await getHTMLLocal(url, semaphore)) return true;
      else return false;
    } else return false;
  }
  //#endregion

  //#region Gallery Requests
  class GalleryRequests {
    constructor(semaphore) {
      this.semaphore = semaphore;
      this.Gallery = new Gallery(semaphore);
      this.Scraps = new Scraps(semaphore);
      this.Favorites = new Favorites(semaphore);
      this.Journals = new Journals(semaphore);
    }
  }

  class Gallery {
    constructor(semaphore) {
      this.semaphore = semaphore;
    }

    static hardLink = FuraffinityRequestHelper.getUrl() + "/gallery/";

    async getFiguresTillId(username, toId, action, delay = 100) {
      return await getGalleryFiguresTillIdHandleLocal(username, toId, null, action, delay, "gallery", this.semaphore);
    }

    async getFiguresSinceId(username, fromId, action, delay = 100) {
      return await getGalleryFiguresSinceIdHandleLocal(username, fromId, null, action, delay, "gallery", this.semaphore);
    }

    async getFiguresBetweenIds(username, fromId, toId, action, delay = 100) {
      return await getGalleryFiguresBetweenIdsHandleLocal(username, fromId, toId, null, null, action, delay, "gallery", this.semaphore);
    }

    async getFiguresTillIdSincePage(username, toId, fromPageNumber, action, delay = 100) {
      return await getGalleryFiguresTillIdHandleLocal(username, toId, fromPageNumber, action, delay, "gallery", this.semaphore);
    }

    async getFiguresSinceIdTillPage(username, fromId, toPageNumber, action, delay = 100) {
      return await getGalleryFiguresSinceIdHandleLocal(username, fromId, toPageNumber, action, delay, "gallery", this.semaphore);
    }

    async getFiguresBetweenIdsBetweenPages(username, fromId, toId, fromPageNumber, toPageNumber, action, delay = 100) {
      return await getGalleryFiguresBetweenIdsHandleLocal(username, fromId, toId, fromPageNumber, toPageNumber, action, delay, "gallery", this.semaphore);
    }

    async getFiguresTillPage(username, toPageNumber, action, delay = 100) {
      return await getGalleryFiguresTillPageHandleLocal(username, toPageNumber, action, delay, "gallery", this.semaphore);
    }

    async getFiguresSincePage(username, fromPageNumber, action, delay = 100) {
      return await getGalleryFiguresSincePageHandleLocal(username, fromPageNumber, action, delay, "gallery", this.semaphore);
    }

    async getFiguresBetweenPages(username, fromPageNumber, toPageNumber, action, delay = 100) {
      return await getGalleryFiguresHandleBetweenPagesLocal(username, fromPageNumber, toPageNumber, action, delay, "gallery", this.semaphore);
    }

    async getFigures(username, pageNumber, action, delay = 100) {
      return await getGalleryFiguresHandleLocal(username, pageNumber, action, delay, "gallery", this.semaphore);
    }

    async getPage(username, pageNumber, action, delay = 100) {
      return await getGalleryPageHandleLocal(username, pageNumber, action, delay, "gallery", this.semaphore);
    }
  }

  class Scraps {
    constructor(semaphore) {
      this.semaphore = semaphore;
    }

    static hardLink = FuraffinityRequestHelper.getUrl() + "/scraps/";

    async getFiguresTillId(username, toId, action, delay = 100) {
      return await getGalleryFiguresTillIdHandleLocal(username, toId, null, action, delay, "scraps", this.semaphore);
    }

    async getFiguresSinceId(username, fromId, action, delay = 100) {
      return await getGalleryFiguresSinceIdHandleLocal(username, fromId, null, action, delay, "scraps", this.semaphore);
    }

    async getFiguresBetweenIds(username, fromId, toId, action, delay = 100) {
      return await getGalleryFiguresBetweenIdsHandleLocal(username, fromId, toId, null, null, action, delay, "scraps", this.semaphore);
    }

    async getFiguresTillIdSincePage(username, toId, fromPageNumber, action, delay = 100) {
      return await getGalleryFiguresTillIdHandleLocal(username, toId, fromPageNumber, action, delay, "scraps", this.semaphore);
    }

    async getFiguresSinceIdTillPage(username, fromId, toPageNumber, action, delay = 100) {
      return await getGalleryFiguresSinceIdHandleLocal(username, fromId, toPageNumber, action, delay, "scraps", this.semaphore);
    }

    async getFiguresBetweenIdsBetweenPages(username, fromId, toId, fromPageNumber, toPageNumber, action, delay = 100) {
      return await getGalleryFiguresBetweenIdsHandleLocal(username, fromId, toId, fromPageNumber, toPageNumber, action, delay, "scraps", this.semaphore);
    }

    async getFiguresTillPage(username, toPageNumber, action, delay = 100) {
      return await getGalleryFiguresTillPageHandleLocal(username, toPageNumber, action, delay, "scraps", this.semaphore);
    }

    async getFiguresSincePage(username, fromPageNumber, action, delay = 100) {
      return await getGalleryFiguresSincePageHandleLocal(username, fromPageNumber, action, delay, "scraps", this.semaphore);
    }

    async getFiguresBetween(username, fromPageNumber, toPageNumber, action, delay = 100) {
      return await getGalleryFiguresHandleBetweenPagesLocal(username, fromPageNumber, toPageNumber, action, delay, "scraps", this.semaphore);
    }

    async getFigures(username, pageNumber, action, delay = 100) {
      return await getGalleryFiguresHandleLocal(username, pageNumber, action, delay, "scraps", this.semaphore);
    }

    async getPage(username, pageNumber, action, delay = 100) {
      return await getGalleryPageHandleLocal(username, pageNumber, action, delay, "scraps", this.semaphore);
    }
  }

  class Favorites {
    constructor(semaphore) {
      this.semaphore = semaphore;
    }

    static hardLink = FuraffinityRequestHelper.getUrl() + "/favorites/";

    async getFiguresTillId(username, toId, action, delay = 100) {
      return await getGalleryFiguresTillIdHandleLocal(username, toId, null, action, delay, "favorites", this.semaphore);
    }

    async getFiguresSinceId(username, fromId, action, delay = 100) {
      return await getGalleryFiguresSinceIdHandleLocal(username, fromId, null, action, delay, "favorites", this.semaphore);
    }

    async getFiguresBetweenIds(username, fromId, toId, action, delay = 100) {
      return await getGalleryFiguresBetweenIdsHandleLocal(username, fromId, toId, null, null, action, delay, "favorites", this.semaphore);
    }

    async getFiguresTillIdSincePage(username, toId, fromPageNumber, action, delay = 100) {
      return await getGalleryFiguresTillIdHandleLocal(username, toId, fromPageNumber, action, delay, "favorites", this.semaphore);
    }

    async getFiguresSinceIdTillPage(username, fromId, toPageNumber, action, delay = 100) {
      return await getGalleryFiguresSinceIdHandleLocal(username, fromId, toPageNumber, action, delay, "favorites", this.semaphore);
    }

    async getFiguresBetweenIdsBetweenPages(username, fromId, toId, fromPageNumber, toPageNumber, action, delay = 100) {
      return await getGalleryFiguresBetweenIdsHandleLocal(username, fromId, toId, fromPageNumber, toPageNumber, action, delay, "favorites", this.semaphore);
    }

    async getFiguresTillPage(username, toPageNumber, action, delay = 100) {
      return await getGalleryFiguresTillPageHandleLocal(username, toPageNumber, action, delay, "favorites", this.semaphore);
    }

    async getFiguresSincePage(username, fromPageNumber, action, delay = 100) {
      return await getGalleryFiguresSincePageHandleLocal(username, fromPageNumber, action, delay, "favorites", this.semaphore);
    }

    async getFiguresBetween(username, fromPageNumber, toPageNumber, action, delay = 100) {
      return await getGalleryFiguresHandleBetweenPagesLocal(username, fromPageNumber, toPageNumber, action, delay, "favorites", this.semaphore);
    }

    async getFigures(username, pageNumber, action, delay = 100) {
      return await getGalleryFiguresHandleLocal(username, pageNumber, action, delay, "favorites", this.semaphore);
    }

    async getPage(username, pageNumber, action, delay = 100) {
      return await getGalleryPageHandleLocal(username, pageNumber, action, delay, "favorites", this.semaphore);
    }
  }

  class Journals {
    constructor(semaphore) {
      this.semaphore = semaphore;
    }

    static hardLink = FuraffinityRequestHelper.getUrl() + "/journals/";

    async getFiguresTillId(username, toId, action, delay = 100) {
      return await getJournalsSectionsTillIdHandleLocal(username, toId, null, action, delay, this.semaphore);
    }

    async getFiguresSinceId(username, fromId, action, delay = 100) {
      return await getJournalsSectionsSinceIdHandleLocal(username, fromId, null, action, delay, this.semaphore);
    }

    async getFiguresBetweenIds(username, fromId, toId, action, delay = 100) {
      return await getJournalsSectionsBetweenIdsHandleLocal(username, fromId, toId, null, null, action, delay, this.semaphore);
    }

    async getFiguresTillIdSincePage(username, toId, fromPageNumber, action, delay = 100) {
      return await getJournalsSectionsTillIdHandleLocal(username, toId, fromPageNumber, action, delay, this.semaphore);
    }

    async getFiguresSinceIdTillPage(username, fromId, toPageNumber, action, delay = 100) {
      return await getJournalsSectionsSinceIdHandleLocal(username, fromId, toPageNumber, action, delay, this.semaphore);
    }

    async getFiguresBetweenIdsBetweenPages(username, fromId, toId, fromPageNumber, toPageNumber, action, delay = 100) {
      return await getJournalsSectionsBetweenIdsHandleLocal(username, fromId, toId, fromPageNumber, toPageNumber, action, delay, this.semaphore);
    }

    async getSectionsTillPage(username, toPageNumber, action, delay = 100) {
      return await getJournalsSectionsTillPageHandleLocal(username, toPageNumber, action, delay, this.semaphore);
    }

    async getSectionsSincePage(username, fromPageNumber, action, delay = 100) {
      return await getJournalsSectionsSincePageHandleLocal(username, fromPageNumber, action, delay, this.semaphore);
    }

    async getSectionsBetweenPages(username, fromPageNumber, toPageNumber, action, delay = 100) {
      return await getJournalsSectionsHandleBetweenPagesLocal(username, fromPageNumber, toPageNumber, action, delay, this.semaphore);
    }

    async getSections(username, pageNumber, action, delay = 100) {
      return await getJournalsSectionsHandleLocal(username, pageNumber, action, delay, this.semaphore);
    }

    async getPage(username, pageNumber, action, delay = 100) {
      return await getJournalsPageHandleLocal(username, pageNumber, action, delay, this.semaphore);
    }
  }

  async function getGalleryFiguresTillPageHandleLocal(username, toPageNumber, action, delay, galleryType, semaphore) {
    if (!toPageNumber || toPageNumber == 0) toPageNumber = 1;
    const waitAndCallAction = new WaitAndCallAction(action, true, delay);
    const percentId = waitAndCallAction.start();
    const getUserGalleryFiguresTillPage = await getGalleryFiguresTillPageLocal(username, toPageNumber, galleryType, percentId, semaphore);
    waitAndCallAction.stop();
    return getUserGalleryFiguresTillPage;
  }

  async function getGalleryFiguresSincePageHandleLocal(username, fromPageNumber, action, delay, galleryType, semaphore) {
    if (!fromPageNumber || fromPageNumber == 0) fromPageNumber = 1;
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const getUserGalleryFiguresSincePage = await getGalleryFiguresSincePageLocal(username, fromPageNumber, galleryType, semaphore);
    waitAndCallAction.stop();
    return getUserGalleryFiguresSincePage;
  }

  async function getGalleryFiguresHandleBetweenPagesLocal(username, fromPageNumber, toPageNumber, action, delay, galleryType, semaphore) {
    if (!fromPageNumber || fromPageNumber == 0) fromPageNumber = 1;
    if (!toPageNumber || toPageNumber == 0) toPageNumber = 1;
    const waitAndCallAction = new WaitAndCallAction(action, true, delay);
    const percentId = waitAndCallAction.start();
    const getUserGalleryFigureBetween = await getGalleryFiguresBetweenPagesLocal(username, fromPageNumber, toPageNumber, galleryType, percentId, semaphore);
    waitAndCallAction.stop();
    return getUserGalleryFigureBetween;
  }

  async function getGalleryFiguresHandleLocal(username, pageNumber, action, delay, galleryType, semaphore) {
    if (!pageNumber || pageNumber == 0) pageNumber = 1;
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const getUserGalleryFigures = await getGalleryFiguresLocal(username, pageNumber, galleryType, semaphore);
    waitAndCallAction.stop();
    return getUserGalleryFigures;
  }

  async function getGalleryPageHandleLocal(username, pageNumber, action, delay, galleryType, semaphore) {
    if (!pageNumber || pageNumber == 0) pageNumber = 1;
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const getUserGalleryPage = await getGalleryPageLocal(username, pageNumber, galleryType, semaphore);
    waitAndCallAction.stop();
    return getUserGalleryPage;
  }

  async function getGalleryFiguresTillIdHandleLocal(username, toId, fromPage, action, delay, galleryType, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const getUserGalleryFiguresTillId = await getGalleryFiguresTillIdLocal(username, toId, fromPage, galleryType, semaphore);
    waitAndCallAction.stop();
    return getUserGalleryFiguresTillId;
  }

  async function getGalleryFiguresSinceIdHandleLocal(username, fromId, toPage, action, delay, galleryType, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const getUserGalleryFiguresSinceId = await getGalleryFiguresSinceIdLocal(username, fromId, toPage, galleryType, semaphore);
    waitAndCallAction.stop();
    return getUserGalleryFiguresSinceId;
  }

  async function getGalleryFiguresBetweenIdsHandleLocal(username, fromId, toId, fromPage, toPage, action, delay, galleryType, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, true, delay);
    const percentId = waitAndCallAction.start();
    const getUserGalleryFiguresBetweenIds = await getGalleryFiguresBetweenIdsLocal(username, fromId, toId, fromPage, toPage, galleryType, percentId, semaphore);
    waitAndCallAction.stop();
    return getUserGalleryFiguresBetweenIds;
  }

  async function getGalleryFiguresTillIdLocal(username, toId, fromPage, galleryType, semaphore) {
    if (!toId) {
      logError("No toId given");
      return null;
    }

    let allFigures = [];
    let lastFigureId;
    let running = true;
    let i = 1;
    if (fromPage && fromPage >= 1) i = fromPage;
    while (running) {
      const figures = await getGalleryFiguresLocal(username, i, galleryType, semaphore);
      let currFigureId = lastFigureId;
      if (figures && figures.length !== 0) currFigureId = figures[0].id;
      if (currFigureId == lastFigureId) running = false;
      else {
        if (idArrayContainsId(figures, toId)) {
          allFigures.push(getIdArrayTillId(figures, toId));
          running = false;
        } else {
          allFigures.push(figures);
          i++;
        }
      }
    }

    if (allFigures.length === 0) return null;
    else return allFigures;
  }

  async function getGalleryFiguresSinceIdLocal(username, fromId, toPage, galleryType, semaphore) {
    if (!fromId) {
      logError("No fromId given");
      return null;
    }

    let lastFigureId;
    let running = true;
    let i = 1;
    while (running) {
      if (toPage && toPage >= 1 && i == toPage) {
        running = false;
        break;
      }
      const figures = await getGalleryFiguresLocal(username, i, galleryType, semaphore);
      let currFigureId = lastFigureId;
      if (figures && figures.length !== 0) currFigureId = figures[0].id;
      if (currFigureId == lastFigureId) running = false;
      else {
        if (idArrayContainsId(figures, fromId)) running = false;
        else i++;
      }
    }

    let allFigures = [];
    lastFigureId = null;
    running = true;
    while (running) {
      const figures = await getGalleryFiguresLocal(username, i, galleryType, semaphore);
      let currFigureId = lastFigureId;
      if (figures && figures.length !== 0) currFigureId = figures[0].id;
      if (currFigureId == lastFigureId) running = false;
      else {
        allFigures.push(getIdArraySinceId(figures, fromId));
        i++;
      }
    }

    if (allFigures.length === 0) return null;
    else return allFigures;
  }

  async function getGalleryFiguresBetweenIdsLocal(username, fromId, toId, fromPage, toPage, galleryType, percentId, semaphore) {
    if (!fromId) {
      logError("No fromId given");
      return null;
    }
    if (!toId) {
      logError("No toId given");
      return null;
    }
    if (!fromPage || fromPage <= 0 || !toPage || toPage <= 1) {
      logWarning("No fromPage or toPage given. Percentages can not be calculated.");
      percentId = null;
    }
    let lastFigureId;
    let running = true;
    let i = 1;
    if (fromPage && fromPage >= 1) i = fromPage;
    while (running) {
      const figures = await getGalleryFiguresLocal(username, i, galleryType, semaphore);
      let currFigureId = lastFigureId;
      if (figures && figures.length !== 0) currFigureId = figures[0].id;
      if (currFigureId == lastFigureId) running = false;
      else {
        if (idArrayContainsId(figures, fromId)) running = false;
        else i++;
      }
    }

    let allFigures = [];
    let completedPages = 0;
    lastFigureId = null;
    running = true;
    while (running) {
      if (toPage && toPage >= 1 && i == toPage) {
        running = false;
        break;
      }
      const figures = await getGalleryFiguresLocal(username, i, galleryType, semaphore);
      let currFigureId = lastFigureId;
      if (figures && figures.length !== 0) currFigureId = figures[0].id;
      if (currFigureId == lastFigureId) running = false;
      else {
        if (idArrayContainsId(figures, toId)) {
          allFigures.push(getIdArrayBetweenIds(figures, fromId, toId));
          running = false;
        } else {
          allFigures.push(figures);
          i++;
        }
      }
    }

    completedPages++;
    if (percentId) {
      const progress = (completedPages / toPage) * 100;
      PercentHelper.setPercentValue(percentId, progress);
    }
  }

  async function getGalleryFiguresTillPageLocal(username, toPageNumber, galleryType, percentId, semaphore) {
    if (!toPageNumber) {
      logError("No toPageNumber given");
      return null;
    } else if (toPageNumber <= 0) {
      logError("toPageNumber must be greater than 0");
      return null;
    }

    let allFigures = [];
    let completedPages = 0;
    for (let i = 1; i <= toPageNumber; i++) {
      const figures = await getGalleryFiguresLocal(username, i, galleryType, semaphore);
      if (figures && figures.length !== 0) allFigures.push(figures);

      completedPages++;
      if (percentId) {
        const progress = (completedPages / toPageNumber) * 100;
        PercentHelper.setPercentValue(percentId, progress);
      }
    }

    if (allFigures.length === 0) return null;
    else return allFigures;
  }

  async function getGalleryFiguresSincePageLocal(username, fromPageNumber, galleryType, semaphore) {
    if (!fromPageNumber) {
      logError("No fromPageNumber given");
      return null;
    } else if (fromPageNumber <= 0) {
      logError("fromPageNumber must be greater than 0");
      return null;
    }

    let allFigures = [];
    let lastFigureId;
    let running = true;
    let i = fromPageNumber;
    while (running) {
      const figures = await getGalleryFiguresLocal(username, i, galleryType, semaphore);
      let currFigureId = lastFigureId;
      if (figures && figures.length !== 0) currFigureId = figures[0].id;
      if (currFigureId == lastFigureId) running = false;
      else {
        allFigures.push(figures);
        i++;
      }
    }

    if (allFigures.length === 0) return null;
    else return allFigures;
  }

  async function getGalleryFiguresBetweenPagesLocal(username, fromPageNumber, toPageNumber, galleryType, percentId, semaphore) {
    if (!fromPageNumber) {
      logError("No fromPageNumber given");
      return null;
    } else if (fromPageNumber <= 0) {
      logError("fromPageNumber must be greater than 0");
      return null;
    }
    if (!toPageNumber) {
      logError("No toPageNumber given");
      return null;
    } else if (toPageNumber <= 0) {
      logError("toPageNumber must be greater than 0");
      return null;
    }

    let allFigures = [];
    const direction = fromPageNumber < toPageNumber ? 1 : -1;
    const totalPages = Math.abs(toPageNumber - fromPageNumber) + 1;
    let completedPages = 0;
    for (let i = fromPageNumber; i <= toPageNumber; i += direction) {
      const figures = await getGalleryFiguresLocal(username, i, galleryType, semaphore);
      if (figures && figures.length !== 0) allFigures.push(figures);

      completedPages++;
      if (percentId) {
        const progress = (completedPages / totalPages) * 100;
        PercentHelper.setPercentValue(percentId, progress);
      }
    }

    if (allFigures.length === 0) return null;
    else return allFigures;
  }

  async function getGalleryFiguresLocal(username, pageNumber, galleryType, semaphore) {
    const galleryDoc = await getGalleryPageLocal(username, pageNumber, galleryType, semaphore);
    if (!galleryDoc || galleryDoc.getElementById("no-images")) {
      logMessage(`No images found at ${galleryType} of "${username}" on page "${pageNumber}".`);
      return null;
    }

    const figures = galleryDoc.getElementsByTagName("figure");
    if (!figures || figures.length === 0) {
      logMessage(`No figures found at ${galleryType} of "${username}" on page "${pageNumber}".`);
      return null;
    }

    return figures;
  }

  async function getGalleryPageLocal(username, pageNumber, galleryType, semaphore) {
    if (!username) {
      logError("No username given");
      return null;
    }
    if (!pageNumber) {
      logError("No page number given");
      return null;
    } else if (pageNumber <= 0) {
      logError("Page number must be greater than 0");
      return null;
    }
    if (!galleryType) {
      logWarning("No gallery type given. Using default 'Gallery' instead.");
      galleryType = "gallery";
    }

    let url;
    if (!username.endsWith("/")) username += "/";
    switch (galleryType) {
      case "gallery":
        if (!Gallery.hardLink.endsWith("/")) Gallery.hardLink += "/";
        url = Gallery.hardLink + username;
        break;
      case "scraps":
        if (!Scraps.hardLink.endsWith("/")) Scraps.hardLink += "/";
        url = Scraps.hardLink + username;
        break;
      case "favorites":
        if (!Favorites.hardLink.endsWith("/")) Favorites.hardLink += "/";
        url = Favorites.hardLink + username;
        break;
    }
    if (url) return await getHTMLLocal(url + pageNumber, semaphore);
    else return null;
  }

  async function getJournalsSectionsTillPageHandleLocal(username, toPageNumber, action, delay, galleryType, semaphore) {
    if (!toPageNumber || toPageNumber == 0) toPageNumber = 1;
    const waitAndCallAction = new WaitAndCallAction(action, true, delay);
    const percentId = waitAndCallAction.start();
    const getUserJournalsSectionsTillPage = await getJournalsSectionsTillPageLocal(username, toPageNumber, galleryType, percentId, semaphore);
    waitAndCallAction.stop();
    return getUserJournalsSectionsTillPage;
  }

  async function getJournalsSectionsSincePageHandleLocal(username, fromPageNumber, action, delay, galleryType, semaphore) {
    if (!fromPageNumber || fromPageNumber == 0) fromPageNumber = 1;
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const getUserJournalsSectionsSincePage = await getJournalsSectionsSincePageLocal(username, fromPageNumber, galleryType, semaphore);
    waitAndCallAction.stop();
    return getUserJournalsSectionsSincePage;
  }

  async function getJournalsSectionsHandleBetweenPagesLocal(username, fromPageNumber, toPageNumber, action, delay, semaphore) {
    if (!fromPageNumber || fromPageNumber == 0) fromPageNumber = 1;
    if (!toPageNumber || toPageNumber == 0) toPageNumber = 1;
    const waitAndCallAction = new WaitAndCallAction(action, true, delay);
    const percentId = waitAndCallAction.start();
    const getUserJournalsSectionsBetween = await getJournalsSectionsBetweenPagesLocal(username, fromPageNumber, toPageNumber, percentId, semaphore);
    waitAndCallAction.stop();
    return getUserJournalsSectionsBetween;
  }

  async function getJournalsSectionsHandleLocal(username, pageNumber, action, delay, semaphore) {
    if (!pageNumber || pageNumber == 0) pageNumber = 1;
    const waitAndCallAction = new WaitAndCallAction(action, true, delay);
    waitAndCallAction.start();
    const getUserJournalsSections = await getJournalsSectionsLocal(username, pageNumber, semaphore);
    waitAndCallAction.stop();
    return getUserJournalsSections;
  }

  async function getJournalsPageHandleLocal(username, pageNumber, action, delay, semaphore) {
    if (!pageNumber || pageNumber == 0) pageNumber = 1;
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const getUserJournalsPage = await getJournalsPageLocal(username, pageNumber, semaphore);
    waitAndCallAction.stop();
    return getUserJournalsPage;
  }

  async function getJournalsSectionsTillIdHandleLocal(username, toId, fromPage, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const getUserJournalsSectionsTillId = await getJournalsSectionsTillIdLocal(username, toId, fromPage, semaphore);
    waitAndCallAction.stop();
    return getUserJournalsSectionsTillId;
  }

  async function getJournalsSectionsSinceIdHandleLocal(username, fromId, toPage, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const getUserJournalsSectionsSinceId = await getJournalsSectionsSinceIdLocal(username, fromId, toPage, semaphore);
    waitAndCallAction.stop();
    return getUserJournalsSectionsSinceId;
  }

  async function getJournalsSectionsBetweenIdsHandleLocal(username, fromId, toId, fromPage, toPage, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const getUserJournalsSectionsBetweenIds = await getJournalsSectionsBetweenIdsLocal(username, fromId, toId, fromPage, toPage, semaphore);
    waitAndCallAction.stop();
    return getUserJournalsSectionsBetweenIds;
  }

  async function getJournalsSectionsTillIdLocal(username, toId, fromPage, semaphore) {
    if (toId) {
      logError("No toId given");
      return null;
    }

    let allSections = [];
    let lastSectionId;
    let running = true;
    let i = 1;
    if (fromPage && fromPage >= 1) i = fromPage;
    while (running) {
      const sections = await getJournalsSectionsLocal(username, i, semaphore);
      let currSectionId = lastSectionId;
      if (sections && sections.length !== 0) currSectionId = sections[0].id;
      if (currSectionId == lastSectionId) running = false;
      else {
        if (idArrayContainsId(sections, toId)) {
          allSections.push(getIdArrayTillId(sections, toId));
          running = false;
        } else {
          allSections.push(sections);
          i++;
        }
      }
    }

    if (allSections.length === 0) return null;
    else return allSections;
  }

  async function getJournalsSectionsSinceIdLocal(username, fromId, toPage, semaphore) {
    if (!fromId) {
      logError("No fromId given");
      return null;
    }

    let lastSectionId;
    let running = true;
    let i = 1;
    while (running) {
      if (toPage && toPage >= 1 && i == toPage) {
        running = false;
        break;
      }
      const sections = await getJournalsSectionsLocal(username, i, semaphore);
      let currSectionId = lastSectionId;
      if (sections && sections.length !== 0) currSectionId = sections[0].id;
      if (currSectionId == lastSectionId) running = false;
      else {
        if (idArrayContainsId(sections, fromId)) running = false;
        else i++;
      }
    }

    let allSections = [];
    lastSectionId = null;
    running = true;
    while (running) {
      const sections = await getJournalsSectionsLocal(username, i, semaphore);
      let currSectionId = lastSectionId;
      if (sections && sections.length !== 0) currSectionId = sections[0].id;
      if (currSectionId == lastSectionId) running = false;
      else {
        allSections.push(getIdArraySinceId(sections, fromId));
        i++;
      }
    }

    if (allSections.length === 0) return null;
    else return allSections;
  }

  async function getJournalsSectionsBetweenIdsLocal(username, fromId, toId, fromPage, toPage, percentId, semaphore) {
    if (!fromId) {
      logError("No fromId given");
      return null;
    }
    if (!toId) {
      logError("No toId given");
      return null;
    }
    if (!fromPage || fromPage <= 0 || !toPage || toPage <= 1) {
      logWarning("No fromPage or toPage given. Percentages can not be calculated.");
      percentId = null;
    }

    let lastSectionId;
    let running = true;
    let i = 1;
    if (fromPage && fromPage >= 1) i = fromPage;
    while (running) {
      const sections = await getJournalsSectionsLocal(username, i, semaphore);
      let currSectionId = lastSectionId;
      if (sections && sections.length !== 0) currSectionId = sections[0].id;
      if (currSectionId == lastSectionId) running = false;
      else {
        if (idArrayContainsId(sections, fromId)) running = false;
        else i++;
      }
    }

    let allSections = [];
    lastSectionId = null;
    running = true;
    let completedPages = 0;
    while (running) {
      if (toPage && toPage >= 1 && i == toPage) {
        running = false;
        break;
      }
      const sections = await getJournalsSectionsLocal(username, i, semaphore);
      let currFigureId = lastSectionId;
      if (sections && sections.length !== 0) currFigureId = sections[0].id;
      if (currFigureId == lastSectionId) running = false;
      else {
        if (idArrayContainsId(sections, toId)) {
          allSections.push(getIdArrayBetweenIds(sections, fromId, toId));
          running = false;
        } else {
          allSections.push(sections);
          i++;
        }
      }
    }

    completedPages++;
    if (percentId) {
      const progress = (completedPages / toPage) * 100;
      PercentHelper.setPercentValue(percentId, progress);
    }
  }

  async function getJournalsSectionsTillPageLocal(username, toPageNumber, percentId, semaphore) {
    if (!toPageNumber) {
      logError("No toPageNumber given");
      return null;
    } else if (toPageNumber <= 0) {
      logError("toPageNumber must be greater than 0");
      return null;
    }

    let allSections = [];
    let completedPages = 0;
    for (let i = 1; i <= toPageNumber; i++) {
      const sections = await getJournalsSectionsLocal(username, i, semaphore);
      if (sections && sections.length !== 0) allSections.push(sections);

      completedPages++;
      if (percentId) {
        const progress = (completedPages / toPageNumber) * 100;
        PercentHelper.setPercentValue(percentId, progress);
      }
    }

    if (allSections.length === 0) return null;
    else return allSections;
  }

  async function getJournalsSectionsSincePageLocal(username, fromPageNumber, semaphore) {
    if (!fromPageNumber) {
      logError("No fromPageNumber given");
      return null;
    } else if (fromPageNumber <= 0) {
      logError("fromPageNumber must be greater than 0");
      return null;
    }

    let allSections = [];
    let lastSectionId;
    let running = true;
    let i = fromPageNumber;
    while (running) {
      const sections = await getJournalsSectionsLocal(username, i, semaphore);
      if (sections && sections.length !== 0) {
        const currSectionId = sections[0].id;
        if (currSectionId == lastSectionId) running = false;
        else {
          allSections.push(sections);
          i++;
        }
      }
    }

    if (allSections.length === 0) return null;
    else return allSections;
  }

  async function getJournalsSectionsBetweenPagesLocal(username, fromPageNumber, toPageNumber, percentId, semaphore) {
    if (!fromPageNumber) {
      logError("No fromPageNumber given");
      return null;
    } else if (fromPageNumber <= 0) {
      logError("fromPageNumber must be greater than 0");
      return null;
    }
    if (!toPageNumber) {
      logError("No toPageNumber given");
      return null;
    } else if (toPageNumber <= 0) {
      logError("toPageNumber must be greater than 0");
      return null;
    }

    let allSections = [];
    let direction = fromPageNumber < toPageNumber ? 1 : -1;
    const totalPages = Math.abs(toPageNumber - fromPageNumber) + 1;
    let completedPages = 0;
    for (let i = fromPageNumber; i <= toPageNumber; i += direction) {
      const sections = await getJournalsSectionsLocal(username, i, semaphore);
      if (sections) allSections.push(sections);

      completedPages++;
      const progress = (completedPages / totalPages) * 100;
      PercentHelper.setPercentValue(percentId, progress);
    }

    if (allSections.length === 0) return null;
    else return allSections;
  }

  async function getJournalsSectionsLocal(username, pageNumber, semaphore) {
    const galleryDoc = await getJournalsPageLocal(username, pageNumber, semaphore);
    if (!galleryDoc) {
      logMessage(`No journals found at "${username}" on page "${pageNumber}".`);
      return null;
    }

    const columnPage = galleryDoc.getElementById("columnpage");
    if (!columnPage) {
      logMessage(`No column page found at "${username}" on page "${pageNumber}".`);
      return null;
    }
    const sections = columnPage.getElementsByTagName("section");
    if (!sections || sections.length === 0) {
      logMessage(`No journals found at "${username}" on page "${pageNumber}".`);
      return null;
    }

    return sections;
  }

  async function getJournalsPageLocal(username, pageNumber, semaphore) {
    if (!username) {
      logError("No username given");
      return null;
    }
    if (!pageNumber) {
      logError("No page number given");
      return null;
    } else if (pageNumber <= 0) {
      logError("Page number must be greater than 0");
      return null;
    }

    if (!username.endsWith("/")) username += "/";
    if (!Journals.hardLink.endsWith("/")) Journals.hardLink += "/";
    const url = Journals.hardLink + username;

    if (url) return await getHTMLLocal(url + pageNumber, semaphore);
    else return null;
  }
  //#endregion

  //#region Personal User Requests
  class PersonalUserRequests {
    constructor(semaphore) {
      this.semaphore = semaphore;
      this.MessageRequests = new MessageRequests(semaphore);
      this.AccountInformation = new AccountInformation(semaphore);
      this.UserProfile = new UserProfile(semaphore);
      this.ManageContent = new ManageContent(semaphore);
      this.Security = new Security(semaphore);
    }
  }
  //#endregion

  //#region Message Requests
  class MessageRequests {
    constructor(semaphore) {
      this.semaphore = semaphore;
      this.NewSubmissions = new NewSubmissions(semaphore);
    }

    static hardActions = {
      remove: "remove_checked",
      nuke: "nuke_notifications",
    };
  }

  class NewSubmissions {
    constructor(semaphore) {
      this.semaphore = semaphore;
    }

    static hardLink = FuraffinityRequestHelper.getUrl() + "/msg/submissions/";

    async getSubmissionsPage(firstSubmissionId, action, delay = 100) {
      return await getNewSubmissionsSubmissionsPageHandleLocal(firstSubmissionId, action, delay, this.semaphore);
    }

    async removeSubmissions(submissionIds, action, delay = 100) {
      return await removeNewSubmissionsSubmissionsHandleLocal(submissionIds, action, delay, this.semaphore);
    }

    async nukeSubmissions(action, delay = 100) {
      return await nukeNewSubmissionsSubmissionsHandleLocal(action, delay, this.semaphore);
    }
  }

  async function getNewSubmissionsSubmissionsPageHandleLocal(firstSubmissionId, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await getNewSubmissionsSubmissionsPageLocal(firstSubmissionId, semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function getNewSubmissionsSubmissionsPageLocal(firstSubmissionId, semaphore) {
    if (!NewSubmissions.hardLink.endsWith("/")) NewSubmissions.hardLink += "/";

    if (firstSubmissionId) return await getHTMLLocal(`${NewSubmissions.hardLink}new~${firstSubmissionId}@72/`, semaphore);
    else return await getHTMLLocal(`${NewSubmissions.hardLink}new@72/`, semaphore);
  }

  async function removeNewSubmissionsSubmissionsHandleLocal(submissionIds, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await removeNewSubmissionsSubmissionsLocal(submissionIds, semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function removeNewSubmissionsSubmissionsLocal(submissionIds, semaphore) {
    if (!NewSubmissions.hardLink.endsWith("/")) NewSubmissions.hardLink += "/";

    if (!submissionIds || submissionIds.length == 0) {
      logWarning("No submission ids to remove");
      return;
    }

    const payload = [];
    for (const submissionId of submissionIds) {
      payload.push({ key: "submissions[]", value: submissionId });
    }
    payload.push({ key: "messagecenter-action", value: MessageRequests.hardActions["remove"] });

    return await sendHttpPostLocal(`${NewSubmissions.hardLink}new@72/`, payload, semaphore);
  }

  async function nukeNewSubmissionsSubmissionsHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await nukeNewSubmissionsSubmissionsLocal(semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function nukeNewSubmissionsSubmissionsLocal(semaphore) {
    if (!NewSubmissions.hardLink.endsWith("/")) NewSubmissions.hardLink += "/";

    const payload = [];
    payload.push({ key: "messagecenter-action", value: MessageRequests.hardActions["nuke"] });

    return await sendHttpPostLocal(`${NewSubmissions.hardLink}new@72/`, payload, semaphore);
  }
  //#endregion

  //#region Account Information Requests
  class AccountInformation {
    constructor(semaphore) {
      this.semaphore = semaphore;
    }

    static hardLinks = {
      settings: FuraffinityRequestHelper.getUrl() + "/controls/settings/",
      siteSettings: FuraffinityRequestHelper.getUrl() + "/controls/site-settings/",
      userSettings: FuraffinityRequestHelper.getUrl() + "/controls/user-settings/",
    };

    async getSettingsPage(action, delay = 100) {
      return await getAccountInformationSettingsPageHandleLocal(action, delay, this.semaphore);
    }

    async getSiteSettingsPage(action, delay = 100) {
      return await getAccountInformationSiteSettingsPageHandleLocal(action, delay, this.semaphore);
    }

    async getUserSettingsPage(action, delay = 100) {
      return await getAccountInformationUserSettingsPageHandleLocal(action, delay, this.semaphore);
    }
  }

  async function getAccountInformationSettingsPageHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await getAccountInformationSettingsPageLocal(semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function getAccountInformationSiteSettingsPageHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await getAccountInformationSiteSettingsPageLocal(semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function getAccountInformationUserSettingsPageHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await getAccountInformationUserSettingsPageLocal(semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function getAccountInformationSettingsPageLocal(semaphore) {
    if (!AccountInformation.hardLinks["settings"].endsWith("/")) AccountInformation.hardLinks["settings"] += "/";
    return await getHTMLLocal(AccountInformation.hardLinks["settings"], semaphore);
  }

  async function getAccountInformationSiteSettingsPageLocal(semaphore) {
    if (!AccountInformation.hardLinks["siteSettings"].endsWith("/")) AccountInformation.hardLinks["siteSettings"] += "/";
    return await getHTMLLocal(AccountInformation.hardLinks["siteSettings"], semaphore);
  }

  async function getAccountInformationUserSettingsPageLocal(semaphore) {
    if (!AccountInformation.hardLinks["userSettings"].endsWith("/")) AccountInformation.hardLinks["userSettings"] += "/";
    return await getHTMLLocal(AccountInformation.hardLinks["userSettings"], semaphore);
  }
  //#endregion

  //#region User Profile Requests
  class UserProfile {
    constructor(semaphore) {
      this.semaphore = semaphore;
    }

    static hardLinks = {
      profile: FuraffinityRequestHelper.getUrl() + "/controls/profile/",
      profilebanner: FuraffinityRequestHelper.getUrl() + "/controls/profilebanner/",
      contacts: FuraffinityRequestHelper.getUrl() + "/controls/contacts/",
      avatar: FuraffinityRequestHelper.getUrl() + "/controls/avatar/",
    };

    async getProfilePage(action, delay = 100) {
      return await getUserProfileProfilePageHandleLocal(action, delay, this.semaphore);
    }

    async getProfilebannerPage(action, delay = 100) {
      return await getUserProfileProfilebannerPageHandleLocal(action, delay, this.semaphore);
    }

    async getContactsPage(action, delay = 100) {
      return await getUserProfileContactsPageHandleLocal(action, delay, this.semaphore);
    }

    async getAvatarPage(action, delay = 100) {
      return await getUserProfileAvatarPageHandleLocal(action, delay, this.semaphore);
    }
  }

  async function getUserProfileProfilePageHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await getUserProfileProfilePageLocal(semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function getUserProfileProfilebannerPageHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await getUserProfileProfilebannerPageLocal(semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function getUserProfileContactsPageHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await getUserProfileContactsPageLocal(semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function getUserProfileAvatarPageHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await getUserProfileAvatarPageLocal(semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function getUserProfileProfilePageLocal(semaphore) {
    if (!UserProfile.hardLinks["profile"].endsWith("/")) UserProfile.hardLinks["profile"] += "/";
    return await getHTMLLocal(UserProfile.hardLinks["profile"], semaphore);
  }

  async function getUserProfileProfilebannerPageLocal(semaphore) {
    if (!UserProfile.hardLinks["profilebanner"].endsWith("/")) UserProfile.hardLinks["profilebanner"] += "/";
    return await getHTMLLocal(UserProfile.hardLinks["profilebanner"], semaphore);
  }

  async function getUserProfileContactsPageLocal(semaphore) {
    if (!UserProfile.hardLinks["contacts"].endsWith("/")) UserProfile.hardLinks["contacts"] += "/";
    return await getHTMLLocal(UserProfile.hardLinks["contacts"], semaphore);
  }

  async function getUserProfileAvatarPageLocal(semaphore) {
    if (!UserProfile.hardLinks["avatar"].endsWith("/")) UserProfile.hardLinks["avatar"] += "/";
    return await getHTMLLocal(UserProfile.hardLinks["avatar"], semaphore);
  }

  //#endregion

  //#region Manage Content Requests
  class ManageContent {
    constructor(semaphore) {
      this.semaphore = semaphore;
    }

    static hardLinks = {
      submissions: FuraffinityRequestHelper.getUrl() + "/controls/submissions/",
      folders: FuraffinityRequestHelper.getUrl() + "/controls/folders/submissions/",
      journals: FuraffinityRequestHelper.getUrl() + "/controls/journal/",
      favorites: FuraffinityRequestHelper.getUrl() + "/controls/favorites/",
      buddylist: FuraffinityRequestHelper.getUrl() + "/controls/buddylist/",
      shouts: FuraffinityRequestHelper.getUrl() + "/controls/shouts/",
      badges: FuraffinityRequestHelper.getUrl() + "/controls/badges/",
    };

    async getFoldersPages(action, delay = 100) {
      return await getContentFoldersHandleLocal(action, delay, this.semaphore);
    }

    async getAllWatchesPages(action, delay = 100) {
      return await getContentAllWatchesPagesHandleLocal(action, delay, this.semaphore);
    }

    async getWatchesPage(pageNumber, action, delay = 100) {
      return await getWatchesPageHandleLocal(pageNumber, action, delay, this.semaphore);
    }
  }

  async function getContentFoldersHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await getContentFoldersLocal(semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function getContentFoldersLocal(semaphore) {
    if (!ManageContent.hardLinks["folders"].endsWith("/")) ManageContent.hardLinks["folders"] += "/";
    return await getHTMLLocal(ManageContent.hardLinks["folders"], semaphore);
  }

  async function getContentAllWatchesPagesHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const getAllWatches = await getContentAllWatchesPagesLocal(semaphore);
    waitAndCallAction.stop();
    return getAllWatches;
  }

  async function getContentAllWatchesPagesLocal(semaphore) {
    if (!ManageContent.hardLinks["buddylist"].endsWith("/")) ManageContent.hardLinks["buddylist"] += "/";
    let usersDoc = await getHTMLLocal(ManageContent.hardLinks["buddylist"] + "x", semaphore);
    const columnPage = usersDoc.getElementById("columnpage");
    const sectionBody = columnPage.querySelector('div[class="section-body"');
    const pages = sectionBody.querySelectorAll(":scope > a");
    let userPageDocs = [];
    for (let i = 1; i <= pages.length; i++) {
      usersDoc = await getWatchesPageLocal(i, semaphore);
      if (usersDoc) userPageDocs.push(usersDoc);
    }

    if (userPageDocs.length == 0) return null;
    else return userPageDocs;
  }

  async function getWatchesPageHandleLocal(pageNumber, action, delay, semaphore) {
    if (!pageNumber || pageNumber == 0) pageNumber = 1;
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const getWatchesPage = await getWatchesPageLocal(pageNumber, semaphore);
    waitAndCallAction.stop();
    return getWatchesPage;
  }

  async function getWatchesPageLocal(pageNumber, semaphore) {
    if (!pageNumber) {
      logError("No page number given");
      return null;
    } else if (pageNumber <= 0) {
      logError("pageNumber must be greater than 0");
      return null;
    }

    if (!ManageContent.hardLinks["buddylist"].endsWith("/")) ManageContent.hardLinks["buddylist"] += "/";
    const usersDoc = await getHTMLLocal(ManageContent.hardLinks["buddylist"] + pageNumber, semaphore);
    return usersDoc;
  }
  //#endregion

  //#region Security Requests
  class Security {
    constructor(semaphore) {
      this.semaphore = semaphore;
    }

    static hardLinks = {
      sessions: FuraffinityRequestHelper.getUrl() + "/controls/sessions/logins/",
      logs: FuraffinityRequestHelper.getUrl() + "/controls/logs/",
      labels: FuraffinityRequestHelper.getUrl() + "/controls/labels/",
    };

    async getSessionsPage(action, delay = 100) {
      return await getSecuritySessionsPageHandleLocal(action, delay, this.semaphore);
    }

    async getLogsPage(action, delay = 100) {
      return await getSecurityLogsPageHandleLocal(action, delay, this.semaphore);
    }

    async getLabelsPage(action, delay = 100) {
      return await getSecurityLabelsPageHandleLocal(action, delay, this.semaphore);
    }
  }

  async function getSecuritySessionsPageHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await getSecuritySessionsPageLocal(semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function getSecurityLogsPageHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await getSecurityLogsPageLocal(semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function getSecurityLabelsPageHandleLocal(action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, delay);
    waitAndCallAction.start();
    const result = await getSecurityLabelsPageLocal(semaphore);
    waitAndCallAction.stop();
    return result;
  }

  async function getSecuritySessionsPageLocal(semaphore) {
    if (!Security.hardLinks["sessions"].endsWith("/")) Security.hardLinks["sessions"] += "/";
    return await getHTMLLocal(Security.hardLinks["sessions"], semaphore);
  }

  async function getSecurityLogsPageLocal(semaphore) {
    if (!Security.hardLinks["logs"].endsWith("/")) Security.hardLinks["logs"] += "/";
    return await getHTMLLocal(Security.hardLinks["logs"], semaphore);
  }

  async function getSecurityLabelsPageLocal(semaphore) {
    if (!Security.hardLinks["labels"].endsWith("/")) Security.hardLinks["labels"] += "/";
    return await getHTMLLocal(Security.hardLinks["labels"], semaphore);
  }

  //#endregion

  //#region Submission Requests
  class SubmissionRequests {
    constructor(semaphore) {
      this.semaphore = semaphore;
    }

    static hardLinks = {
      view: FuraffinityRequestHelper.getUrl() + "/view/",
      fav: FuraffinityRequestHelper.getUrl() + "/fav/",
      unfav: FuraffinityRequestHelper.getUrl() + "/unfav/",
      journal: FuraffinityRequestHelper.getUrl() + "/journal/",
    };

    async getSubmissionPage(submissionId, action, delay = 100) {
      return await getSubmissionPageHandleLocal(submissionId, action, delay, this.semaphore);
    }

    async favSubmission(submissionId, favKey, action, delay = 100) {
      return await favSubmissionHandleLocal(submissionId, favKey, action, delay, this.semaphore);
    }

    async unfavSubmission(submissionId, unfavKey, action, delay = 100) {
      return await unfavSubmissionHandleLocal(submissionId, unfavKey, action, delay, this.semaphore);
    }

    async getJournalPage(journalId, action, delay = 100) {
      return await getJournalPageHandleLocal(journalId, action, delay, this.semaphore);
    }
  }

  async function getSubmissionPageHandleLocal(submissionId, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const submissionPage = await getSubmissionPageLocal(submissionId, semaphore);
    waitAndCallAction.stop();
    return submissionPage;
  }

  async function getSubmissionPageLocal(submissionId, semaphore) {
    if (!submissionId) {
      logError("No submissionId given");
      return null;
    }

    if (!SubmissionRequests.hardLinks["view"].endsWith("/")) SubmissionRequests.hardLinks["view"] += "/";
    const url = SubmissionRequests.hardLinks["view"] + submissionId;

    if (url) return await getHTMLLocal(url, semaphore);
    else return null;
  }

  async function favSubmissionHandleLocal(submissionId, favKey, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const unfavKey = await favSubmissionLocal(submissionId, favKey, semaphore);
    waitAndCallAction.stop();
    return unfavKey;
  }

  async function favSubmissionLocal(submissionId, favKey, semaphore) {
    if (!submissionId) {
      logError("No submissionId given");
      return null;
    }
    if (!favKey) {
      logError("No favKey given");
      return null;
    }

    if (!SubmissionRequests.hardLinks["fav"].endsWith("/")) SubmissionRequests.hardLinks["fav"] += "/";
    const url = SubmissionRequests.hardLinks["fav"] + submissionId + "?key=" + favKey;

    if (url) {
      const resultDoc = await getHTMLLocal(url, semaphore);
      try {
        const standardpage = resultDoc.getElementById("standardpage");
        if (standardpage) {
          const blocked = standardpage.querySelector('div[class="redirect-message"]');
          if (blocked && blocked.textContent.includes("blocked"))
            return;
        }
        const unfavKey = getFavKey(resultDoc);
        return unfavKey;
      } catch { }
    }
  }

  async function unfavSubmissionHandleLocal(submissionId, unfavKey, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const success = await unfavSubmissionLocal(submissionId, unfavKey, semaphore);
    waitAndCallAction.stop();
    return success;
  }

  async function unfavSubmissionLocal(submissionId, unfavKey, semaphore) {
    if (!submissionId) {
      logError("No submissionId given");
      return null;
    }
    if (!unfavKey) {
      logError("No unfavKey given");
      return null;
    }

    if (!SubmissionRequests.hardLinks["unfav"].endsWith("/")) SubmissionRequests.hardLinks["unfav"] += "/";
    const url = SubmissionRequests.hardLinks["unfav"] + submissionId + "?key=" + unfavKey;

    if (url) {
      const resultDoc = await getHTMLLocal(url, semaphore);
      if (resultDoc) {
        const favKey = getFavKey(resultDoc);
        return favKey;
      }
    }
  }

  async function getJournalPageHandleLocal(journalId, action, delay, semaphore) {
    const waitAndCallAction = new WaitAndCallAction(action, false, delay);
    waitAndCallAction.start();
    const journalPage = await getJournalPageLocal(journalId, semaphore);
    waitAndCallAction.stop();
    return journalPage;
  }

  async function getJournalPageLocal(journalId, semaphore) {
    if (!journalId) {
      logError("No journalId given");
      return null;
    }

    if (!JournalRequests.hardLinks["journal"].endsWith("/")) JournalRequests.hardLinks["journal"] += "/";
    const url = JournalRequests.hardLinks["journal"] + journalId;

    if (url) return await getHTMLLocal(url, semaphore);
    else return null;
  }
  //#endregion

  //#region Utility Functions
  function logMessage(message) {
    if (FuraffinityRequestHelper.logLevel >= 3) console.log(message);
  }
  function logWarning(message) {
    if (FuraffinityRequestHelper.logLevel >= 2) console.warn(message);
  }
  function logError(message) {
    if (FuraffinityRequestHelper.logLevel >= 1) console.error(message);
  }

  function getIdArrayTillId(array, toId) {
    const result = [];
    for (const elem of array) {
      result.push(elem);
      if (elem.id.toString().replace('sid-', '') == toId) break;
    }
    return result;
  }
  function getIdArraySinceId(array, fromId) {
    array = Array.from(array);
    array.reverse();
    const result = [];
    for (const elem of array) {
      result.push(elem);
      if (elem.id.toString().replace('sid-', '') == fromId) break;
    }
    result.reverse();
    return result;
  }
  function getIdArrayBetweenIds(array, fromId, toId) {
    let startIndex = -1;
    let endIndex = -1;
    for (let i = 0; i < array.length; i++) {
      if (array[i].id.toString().replace('sid-', '') == fromId) startIndex = i;
      if (array[i].id.toString().replace('sid-', '') == toId) endIndex = i;
      if (startIndex != -1 && endIndex != -1) break;
    }

    if (startIndex == -1 && endIndex == -1) return array;

    if (startIndex == -1) startIndex = 0;
    if (endIndex == -1) endIndex = array.length - 1;

    const result = [];
    for (let i = startIndex; i <= endIndex; i++) {
      result.push(array[i]);
    }

    return result;
  }
  function idArrayContainsId(array, id) {
    for (const elem of array) {
      if (elem.id.toString().replace('sid-', '') == id) return true;
    }
    return false;
  }
  function getFavKey(doc) {
    const columnPage = doc.getElementById("columnpage");
    const navbar = columnPage.querySelector('div[class*="favorite-nav"');
    const buttons = navbar.querySelectorAll('a[class*="button"][href]');
    let favButton;
    for (const button of buttons) {
      if (button.textContent.toLowerCase().includes("fav"))
        favButton = button;
    }

    if (favButton) {
      const favKey = favButton.href.split("?key=")[1];
      return favKey;
    }
  }
  //#endregion
})();