From e44e2fe070484e06d384a31ef2699c3a2d5d474e Mon Sep 17 00:00:00 2001 From: RaindropsSys Date: Thu, 13 Jun 2024 15:46:03 +0200 Subject: GitHub migration --- src/PrisbeamDerpibooru.ts | 293 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100755 src/PrisbeamDerpibooru.ts (limited to 'src/PrisbeamDerpibooru.ts') diff --git a/src/PrisbeamDerpibooru.ts b/src/PrisbeamDerpibooru.ts new file mode 100755 index 0000000..0680f13 --- /dev/null +++ b/src/PrisbeamDerpibooru.ts @@ -0,0 +1,293 @@ +import {PrisbeamApp} from "./PrisbeamApp"; +import {BrowserWindow, shell} from "@electron/remote"; +import {BrowserWindow as TBrowserWindow, SafeStorage} from "electron"; + +interface DerpibooruJSDataStore { + fancyTagEdit: boolean; + fancyTagUpload: boolean; + filterId: number; + hiddenFilter: string; + hiddenTagList: number[]; + ignoredTagList: number[]; + interactions: any[]; + spoilerType: string; + spoileredFilter: string; + spoileredTagList: number[]; + userCanEditFilter: boolean; + userIsSignedIn: boolean; + watchedTagList: number[]; +} + +export class PrisbeamDerpibooru { + instance: PrisbeamApp; + enabled: boolean; + window: TBrowserWindow; + dataStore: DerpibooruJSDataStore; + + constructor(instance: PrisbeamApp) { + this.instance = instance; + this.enabled = null; + } + + async getDataStore() { + if (await this.window.webContents.executeJavaScript("!!document.getElementsByClassName(\"js-datastore\")[0]")) { + let attributes = await this.window.webContents.executeJavaScript("document.getElementsByClassName(\"js-datastore\")[0].getAttributeNames().filter(i => i.startsWith(\"data\"))"); + let obj = {}; + + for (let name of attributes) { + let data = await this.window.webContents.executeJavaScript("document.getElementsByClassName(\"js-datastore\")[0].getAttribute(\"" + name + "\")"); + + name = name.substring(5).toLowerCase().replace(/-(.)/g, function(_: string, g: string) { + return g.toUpperCase(); + }); + + try { + obj[name] = JSON.parse(data); + } catch (e) { + obj[name] = data; + } + } + + return obj; + } else { + return {}; + } + } + + startLoginFlow() { + (document.getElementById("derpibooru-email") as HTMLInputElement).value = ""; + (document.getElementById("derpibooru-password") as HTMLInputElement).value = ""; + (document.getElementById("derpibooru-2fa") as HTMLInputElement).value = ""; + document.getElementById("derpibooru-login-2fa").style.display = "none"; + document.getElementById("derpibooru-login-initial").style.display = ""; + + this.setFormLock(false); + this.instance.dataStore.login.show(); + } + + validateEmail(email: string) { + return /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + .test(email.toLowerCase()); + } + + validateCredentials(email: string, password: string) { + if (email.trim() === "") { + alert("Please enter an email address."); + document.getElementById("derpibooru-email").focus(); + return false; + } + + if (password.trim() === "") { + alert("Please enter a password."); + document.getElementById("derpibooru-password").focus(); + return false; + } + + if (!this.validateEmail(email)) { + alert("Please enter a valid email address."); + document.getElementById("derpibooru-email").focus(); + return false; + } + + if (password.length < 12) { + alert("Your password must be at least 12 characters."); + document.getElementById("derpibooru-password").focus(); + return false; + } + + return true; + } + + setFormLock(lock: boolean) { + document.getElementById("login").style['pointerEvents'] = lock ? "none" : ""; + (document.getElementById("derpibooru-email") as HTMLInputElement).disabled = lock; + (document.getElementById("derpibooru-password") as HTMLInputElement).disabled = lock; + (document.getElementById("derpibooru-2fa") as HTMLInputElement).disabled = lock; + document.getElementById("derpibooru-confirm-btn").classList[lock ? "add" : "remove"]("disabled"); + document.getElementById("derpibooru-confirm-btn2").classList[lock ? "add" : "remove"]("disabled"); + } + + async handleLoginPage(email?: string, password?: string) { + if (this.window.webContents.getURL().endsWith("/sessions/new") || + this.window.webContents.getURL().endsWith("/sessions/new/") || + this.window.webContents.getURL().endsWith("/sessions") || + this.window.webContents.getURL().endsWith("/sessions/")) { + await this.requestCredentials(email, password); + } else if (this.window.webContents.getURL().endsWith("/sessions/totp/new") || this.window.webContents.getURL().endsWith("/sessions/totp/new/")) { + await this.request2fa(); + } else { + await this.regularLoginItem(); + } + } + + async requestCredentials(email?: string, password?: string) { + let msg = await this.window.webContents.executeJavaScript('document.querySelector(".alert.alert-danger")?.innerText'); + + if (msg) { + alert(msg); + this.setFormLock(false); + document.getElementById("derpibooru-email").focus(); + } else { + this.window.webContents.once("did-stop-loading", () => this.handleLoginPage(email, password)); + + await this.window.webContents.executeJavaScript("document.getElementById('user_email').value = \"" + email.trim().replaceAll('\\', '\\\\').replaceAll('"', '\\"') + "\";"); + await this.window.webContents.executeJavaScript("document.getElementById('user_password').value = \"" + password.trim().replaceAll('\\', '\\\\').replaceAll('"', '\\"') + "\";"); + await this.window.webContents.executeJavaScript("document.getElementById('user_remember_me').checked = true;"); + await this.window.webContents.executeJavaScript('document.querySelector("[action=\\"/sessions\\"] [type=\\"submit\\"]").click();'); + } + } + + async request2fa() { + document.getElementById("derpibooru-login-2fa").style.display = ""; + document.getElementById("derpibooru-login-initial").style.display = "none"; + document.getElementById("derpibooru-2fa").focus(); + this.setFormLock(false); + } + + async loadUserData() { + if (!this.dataStore.userIsSignedIn) { + document.getElementById("derpibooru-login-btn").classList.remove("disabled"); + document.getElementById("derpibooru-login").style.display = ""; + Array.from(document.getElementsByClassName("derpibooru-when-logged-in")) + .map((i: HTMLElement) => i.style.display = "none"); + } else { + document.getElementById("derpibooru-login-btn").classList.add("disabled"); + document.getElementById("derpibooru-login").style.display = "none"; + Array.from(document.getElementsByClassName("derpibooru-when-logged-in")) + .map((i: HTMLElement) => i.style.display = ""); + + let avatar = await this.window.webContents + .executeJavaScript('document.querySelector("[src^=\\"https://derpicdn.net/avatars/\\"]").src'); + await this.instance.propertyStore.setItem("pba_derpibooru_avatar", avatar); + (document.getElementById("avatar") as HTMLImageElement).src = avatar; + + this.window.webContents.on('did-stop-loading', async () => { + let apiKey = await this.window.webContents.executeJavaScript('document.getElementById("api-key").innerText.trim();'); + let apiKeyToStore = apiKey; + let safeStorage: SafeStorage = require('@electron/remote').safeStorage; + + if (safeStorage.isEncryptionAvailable()) { + apiKeyToStore = safeStorage.encryptString(apiKey).toString("base64"); + } + + await this.instance.propertyStore.setItem("pba_derpibooru_key_encrypted", apiKeyToStore); + + let userName = await this.window.webContents.executeJavaScript('document.querySelector("[class=\\"header__link\\"][href^=\\"/profiles/\\"]").innerText'); + await this.instance.propertyStore.setItem("pba_derpibooru_user_name", userName); + document.getElementById("user-name").innerText = "Logged in as: " + userName; + }); + + await this.window.loadURL("https://derpibooru.org/registrations/edit"); + } + } + + openManager() { + shell.openExternal("https://derpibooru.org/registrations/edit"); + } + + async logOut() { + if (confirm("Are you sure you want to log out from Derpibooru? Any features using Derpibooru will stop working and your Derpibooru user data will be removed from Prisbeam.")) { + await this.instance.propertyStore.removeItem("pba_derpibooru_user_name"); + await this.instance.propertyStore.removeItem("pba_derpibooru_key_encrypted"); + await this.instance.propertyStore.removeItem("pba_derpibooru_avatar"); + await this.window.webContents.executeJavaScript('document.querySelector("[data-method=\\"delete\\"][href=\\"/sessions\\"]").click();'); + location.reload(); + } + } + + async regularLoginItem() { + if ((await this.getDataStore() as DerpibooruJSDataStore).userIsSignedIn) { + this.setFormLock(false); + this.instance.dataStore.login.hide(); + location.reload(); + } else { + if (document.getElementById("derpibooru-login-2fa").style.display !== "none") { + alert("An invalid two-factor authentication code was entered. Please try again."); + (document.getElementById("derpibooru-2fa") as HTMLInputElement).value = ""; + (document.getElementById("derpibooru-email") as HTMLInputElement).value = ""; + (document.getElementById("derpibooru-password") as HTMLInputElement).value = ""; + this.setFormLock(false); + } + + document.getElementById("derpibooru-login-2fa").style.display = "none"; + document.getElementById("derpibooru-login-initial").style.display = ""; + } + } + + submitLogin() { + let email = (document.getElementById("derpibooru-email") as HTMLInputElement).value; + let password = (document.getElementById("derpibooru-password") as HTMLInputElement).value; + if (!this.validateCredentials(email, password)) return; + + this.setFormLock(true); + this.window.webContents.once("did-stop-loading", () => this.handleLoginPage(email, password)); + + this.window.loadURL("https://derpibooru.org/sessions/new"); + } + + async submit2fa() { + let mfa = parseInt((document.getElementById("derpibooru-2fa") as HTMLInputElement).value).toString(); + if (mfa.length !== 6) { + alert("A two-factor authentication code contains 6 digits, but you entered " + mfa.length + "."); + document.getElementById("derpibooru-2fa").focus(); + return; + } + + this.setFormLock(true); + this.window.webContents.once("did-stop-loading", () => this.handleLoginPage(null, null)); + await this.window.webContents.executeJavaScript("document.getElementById('user_twofactor_token').value = \"" + mfa.trim().replaceAll('\\', '\\\\').replaceAll('"', '\\"') + "\";"); + await this.window.webContents.executeJavaScript("document.getElementById('user_remember_me').checked = true;"); + await this.window.webContents.executeJavaScript('document.querySelector("[action=\\"/sessions/totp\\"] [type=\\"submit\\"]").click();'); + } + + initialize() { + document.getElementById("login").addEventListener("shown.bs.modal", () => { + if (this.window.webContents.getURL().endsWith("/sessions/totp/new") || this.window.webContents.getURL().endsWith("/sessions/totp/new/")) { + document.getElementById("derpibooru-login-2fa").style.display = ""; + document.getElementById("derpibooru-login-initial").style.display = "none"; + document.getElementById("derpibooru-2fa").focus(); + } else { + document.getElementById("derpibooru-login-2fa").style.display = "none"; + document.getElementById("derpibooru-login-initial").style.display = ""; + document.getElementById("derpibooru-email").focus(); + } + }); + + document.getElementById("derpibooru-email").onkeydown = document.getElementById("derpibooru-password").onkeydown = (event) => { + if (event.key === "Enter") { + this.submitLogin(); + } + } + + document.getElementById("derpibooru-2fa").onkeydown = (event) => { + if (event.key === "Enter") { + this.submit2fa(); + } + } + + document.getElementById("avatar").onerror = () => { + (document.getElementById("avatar") as HTMLImageElement).src = "../logo/placeholder.jpg"; + } + + return new Promise(async (res) => { + this.enabled = false; + + this.window = new BrowserWindow({ + show: false + }); + + await this.window.loadURL("https://derpibooru.org"); + + this.dataStore = await this.getDataStore() as DerpibooruJSDataStore; + await this.loadUserData(); + this.enabled = true; + + this.window.webContents.on('did-stop-loading', async () => { + this.dataStore = await this.getDataStore() as DerpibooruJSDataStore; + if (!this.dataStore.userIsSignedIn) await this.loadUserData(); + }); + + res(); + }) + } +} -- cgit