From e91a009760b59cbfe03456cfee577eb3473ba9cb Mon Sep 17 00:00:00 2001 From: szialajoscosplay <70654182+k3rielit@users.noreply.github.com> Date: Tue, 29 Aug 2023 14:43:08 +0200 Subject: [PATCH] improved rdmt.user.js reliability --- redmenta/README.md | 15 ++++++++++ redmenta/rdmt.user.js | 68 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/redmenta/README.md b/redmenta/README.md index 807d624..f25aa1c 100644 --- a/redmenta/README.md +++ b/redmenta/README.md @@ -8,3 +8,18 @@ Creates an overlay with the BearerToken, UserId, and UserType values that ship w 1. Install the Tampermonkey extension. ([Link](https://www.tampermonkey.net)) 2. [Install](https://github.com/k3rielit/scripts/raw/main/redmenta/rdmt.user.js) + +## Todo + +- Clear the stored token once the user logged out, or changed accounts +- Define API routes +- Intercept API calls ([XHR](https://stackoverflow.com/questions/629671/how-can-i-intercept-xmlhttprequests-from-a-greasemonkey-script), [fetch](https://stackoverflow.com/questions/45425169/intercept-fetch-api-requests-and-responses-in-javascript)) + +## Changelog + +### Version 1.1 `2023.08.29.` + +- Waits for the body to be in the DOM (for too early injections) +- Stores the last known token in the userscript storage (for too late injections) +- Added a `sleep` and a `waitForElement` function to mitigate too early injections +- Added a `stringToElement` function that uses templates to turn a string into a Node diff --git a/redmenta/rdmt.user.js b/redmenta/rdmt.user.js index a89ea2f..2aa07e5 100644 --- a/redmenta/rdmt.user.js +++ b/redmenta/rdmt.user.js @@ -1,12 +1,13 @@ // ==UserScript== // @name Redmenta BearerToken // @namespace com.k3rielit.redmenta -// @version 1.0 +// @version 1.1 // @description Tries to load the bearer token, then displays as an overlay. // @author k3rielit // @match *://redmenta.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=redmenta.com -// @grant none +// @grant GM_setValue +// @grant GM_getValue // @run-at document-start // @updateURL https://github.com/k3rielit/scripts/raw/main/redmenta/rdmt.user.js // @downloadURL https://github.com/k3rielit/scripts/raw/main/redmenta/rdmt.user.js @@ -14,16 +15,55 @@ (function() { 'use strict'; - // Load data before it gets removed - const auth = { - token: document.body.getAttribute('data-rdmt'), - uid: document.body.getAttribute('data-rdmt-id'), - utype: document.body.getAttribute('data-rdmt-type') - }; - console.log(auth); - // Display it as an overlay - const overlay = document.createElement('div'); - overlay.innerText = JSON.stringify(auth); - overlay.setAttribute('style','position: fixed; z-index: 999; left: 10px; bottom: 10px; font-weight: 700; color: white; background-color: rgb(255 83 22); border-radius: 20px; padding: 4px; border: 2px solid rgb(245 206 187); box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);'); - document.body.appendChild(overlay); + // Functions + function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + function waitForElement(selector) { + // Promise constructor anti-pattern, but necessary for too early injections [tested:works] + // Replacing it with chained promises is better: https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it + return new Promise(async (resolve, reject) => { + if (document.querySelector(selector)) { + return resolve(document.querySelector(selector)); + } + console.log('[waitForElement] Waiting for: '+selector); + while(!document.body) await sleep(1); + // Assuming the body exists, wait for the element to appear + const observer = new MutationObserver((mutationList, observer) => { + if (document.querySelector(selector)) { + console.log(document.querySelector(selector)); + resolve(document.querySelector(selector)); + observer.disconnect(); + } + }); + observer.observe(document.body, { + attributes: true, childList: true, subtree: true + }); + }); + } + + function stringToElement(str) { + let template = document.createElement('template'); + template.innerHTML = str.trim(); + return template.content.firstChild; + } + + // Main + waitForElement('body').then(() => { + const localStorageKey = 'rdmt-auth'; + const overlayStyle = 'position: fixed; z-index: 999; left: 10px; bottom: 10px; font-weight: 700; color: white; background-color: rgb(255 83 22); border-radius: 20px; padding: 4px; border: 2px solid rgb(245 206 187); box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);'; + // Load the data before it gets removed + let auth = { + token: document.body.getAttribute('data-rdmt'), + uid: document.body.getAttribute('data-rdmt-id'), + utype: document.body.getAttribute('data-rdmt-type') + }; + console.log(auth); + // Try to mitigate injection timing inconsistency with GM storage (unsafe) + if(!auth.token && GM_getValue(localStorageKey,null)) auth = JSON.parse(GM_getValue(localStorageKey,null)); + else if(auth.token) GM_setValue(localStorageKey, JSON.stringify(auth)); + // Display it as an overlay + document.body.appendChild(stringToElement(`