gimmickCellar

Hotswap

Feb 22nd, 2026 (edited)
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.99 KB | None | 0 0
  1. // ==UserScript==
  2. // @name hotswap
  3. // @namespace gimmickCellar
  4. // @version 1.2
  5. // @description Isolate and switch OWOT/Uvias accounts.
  6. // @author gimmickCellar
  7. // @match *://*.ourworldoftext.com/*
  8. // @match *://ourworldoftext.com/*
  9. // @match http://localhost:8080/*
  10. // @match http://127.0.0.1:8080/*
  11. // @match *://uvias.com/*
  12. // @match *://*.uvias.com/*
  13. // @grant GM_cookie
  14. // @run-at document-idle
  15. // ==/UserScript==
  16.  
  17. (async function() {
  18. 'use strict';
  19.  
  20. const host = location.hostname;
  21. if (!host.endsWith('ourworldoftext.com') && host !== 'localhost' && host !== '127.0.0.1') return;
  22. if (typeof GM_cookie === 'undefined') return;
  23.  
  24. const KEY = 'owot_ultimate_accounts';
  25. const base = host.includes('ourworldoftext.com') ? '.ourworldoftext.com' : host;
  26. const isTest = host.includes('test.') || host === 'localhost' || host === '127.0.0.1';
  27. const loginUrl = `https://uvias.com/api/loginto/${isTest ? 'owottest' : 'owot'}`;
  28.  
  29. let accounts = JSON.parse(localStorage.getItem(KEY) || '{}');
  30.  
  31. const getC = (d) => new Promise(res => GM_cookie.list(d, (c, e) => res(e ? [] : (c || []))));
  32. const setC = (d) => new Promise(res => GM_cookie.set(d, res));
  33. const delC = (d) => new Promise(res => GM_cookie.delete(d, res));
  34.  
  35. const getStorage = () => {
  36. let s = {};
  37. for (let i = 0; i < localStorage.length; i++) {
  38. let k = localStorage.key(i);
  39. if (k !== KEY) s[k] = localStorage.getItem(k);
  40. }
  41. return s;
  42. };
  43.  
  44. const setStorage = (s) => {
  45. let ks = [];
  46. for (let i = 0; i < localStorage.length; i++) {
  47. let k = localStorage.key(i);
  48. if (k !== KEY) ks.push(k);
  49. }
  50. ks.forEach(k => localStorage.removeItem(k));
  51. if (s) Object.keys(s).forEach(k => localStorage.setItem(k, s[k]));
  52. };
  53.  
  54. const getMe = () => {
  55. if (window.state?.userModel?.authenticated) return window.state.userModel.username;
  56. let t = document.getElementById('topbar');
  57. return t ? t.innerText.match(/Logged in as\s+([^\s:()]+)/)?.[1].trim() : null;
  58. };
  59.  
  60. const sync = async () => {
  61. const me = getMe();
  62. if (!me) return;
  63. let owotC = await getC({ url: location.href });
  64. let auth = owotC.find(c => c.name === 'token' || c.name === 'sessionid');
  65. let uviasC = await getC({ url: 'https://uvias.com/' });
  66.  
  67. if (auth) {
  68. auth.domain = base;
  69. accounts[me] = { auth, uviasC, storage: getStorage() };
  70. localStorage.setItem(KEY, JSON.stringify(accounts));
  71. }
  72. };
  73.  
  74. await sync();
  75. setInterval(sync, 20000);
  76.  
  77. const swap = async (u) => {
  78. let acc = accounts[u];
  79. if (!acc) return;
  80.  
  81. let o = await getC({ url: location.href });
  82. for (let c of o) if (c.name === 'token' || c.name === 'sessionid') await delC({ url: location.href, name: c.name });
  83. let uv = await getC({ url: 'https://uvias.com/' });
  84. for (let c of uv) await delC({ url: 'https://uvias.com/', name: c.name });
  85.  
  86. if (acc.auth) {
  87. let d = acc.auth;
  88. await setC({ url: location.href, name: d.name, value: d.value, domain: d.domain, path: '/', secure: d.secure, httpOnly: true });
  89. }
  90. if (acc.uviasC) {
  91. for (let c of acc.uviasC) await setC({ url: 'https://uvias.com/', name: c.name, value: c.value, path: '/', secure: c.secure, httpOnly: true });
  92. }
  93.  
  94. setStorage(acc.storage);
  95. location.reload();
  96. };
  97.  
  98. const draw = () => {
  99. if (document.getElementById('ac-mgr-ui')) return;
  100. const me = getMe();
  101. const ui = document.createElement('div');
  102. ui.id = 'ac-mgr-ui';
  103. ui.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#E5E5FF;border:2px solid #000;padding:15px;z-index:1000001;font-family:Verdana;font-size:12px;min-width:280px;box-shadow:5px 5px 0px rgba(0,0,0,0.2);';
  104. ui.innerHTML = '<strong>Account Manager</strong><hr style="border:0;border-top:1px solid #000;margin:10px 0;"><div id="ac-list" style="max-height:250px;overflow-y:auto;"></div>';
  105. const list = ui.querySelector('#ac-list');
  106.  
  107. Object.keys(accounts).forEach(u => {
  108. const row = document.createElement('div');
  109. row.style.cssText = 'display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid #bbb;align-items:center;';
  110. const btn = document.createElement('a');
  111. btn.href = '#';
  112. btn.innerText = u + (u === me ? " (Active)" : "");
  113. btn.style.cssText = `color:${u === me ? 'green' : 'blue'}; font-weight:${u === me ? 'bold' : 'normal'}; text-decoration:underline;`;
  114. if (u !== me) btn.onclick = (e) => { e.preventDefault(); swap(u); };
  115. const del = document.createElement('span');
  116. del.innerHTML = '&#10006;';
  117. del.style.cssText = 'color:red;cursor:pointer;font-weight:bold;padding:0 5px;';
  118. del.onclick = () => { if (confirm(`Forget ${u}?`)) { delete accounts[u]; localStorage.setItem(KEY, JSON.stringify(accounts)); ui.remove(); ovl.remove(); draw(); } };
  119. row.append(btn, del);
  120. list.append(row);
  121. });
  122.  
  123. const bts = document.createElement('div');
  124. bts.style.marginTop = '15px';
  125. const add = document.createElement('button');
  126. add.innerText = '+ Add Account';
  127. add.onclick = async () => {
  128. let o = await getC({ url: location.href });
  129. for (let c of o) if (c.name === 'token' || c.name === 'sessionid') await delC({ url: location.href, name: c.name });
  130. let uv = await getC({ url: 'https://uvias.com/' });
  131. for (let c of uv) await delC({ url: 'https://uvias.com/', name: c.name });
  132. setStorage({});
  133. location.href = loginUrl;
  134. };
  135. const cls = document.createElement('button');
  136. cls.innerText = 'Close';
  137. cls.style.marginLeft = '8px';
  138. cls.onclick = () => { ui.remove(); ovl.remove(); };
  139. bts.append(add, cls);
  140. ui.append(bts);
  141. const ovl = document.createElement('div');
  142. ovl.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.4);z-index:1000000;';
  143. ovl.onclick = () => { ui.remove(); ovl.remove(); };
  144. document.body.append(ovl, ui);
  145. };
  146.  
  147. const check = setInterval(() => {
  148. if (window.menu?.addOption) {
  149. clearInterval(check);
  150. window.menu.addOption("Account Manager", draw);
  151. }
  152. }, 400);
  153.  
  154. const t = document.getElementById('topbar');
  155. if (t) {
  156. const a = document.createElement('a');
  157. a.href = "#"; a.innerText = "Account Manager"; a.style.fontWeight = "bold";
  158. a.onclick = (e) => { e.preventDefault(); draw(); };
  159. t.append(document.createTextNode(' | '), a);
  160. }
  161. })();
Advertisement
Add Comment
Please, Sign In to add comment