Definable ModMenu

Definable is a modular ModMenu for Drawaria.Online

当前为 2024-12-22 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Definable ModMenu
  3. // @namespace Definable
  4. // @version 0.1.2
  5. // @description Definable is a modular ModMenu for Drawaria.Online
  6. // @homepage https://drawaria.online/profile/?uid=63196790-c7da-11ec-8266-c399f90709b7
  7. // @author ≺ᴄᴜʙᴇ³≻
  8. // @match https://drawaria.online/
  9. // @match https://drawaria.online/test
  10. // @match https://drawaria.online/room/*
  11. // @icon https://i.ibb.co/8Xb1gXg/Andrew-T-Austin-brightly-lit-tesseract-hypercube.png
  12. // @grant none
  13. // @license GNU GPLv3
  14. // @run-at document-end
  15. // ==/UserScript==
  16.  
  17. (() => {
  18. String.prototype.toFunction = function () {
  19. return new Function("(" + this + ")()");
  20. };
  21. String.prototype.__invokeAsFunction = function (...args) {
  22. new Function("(" + this + ")()")();
  23. };
  24.  
  25. Function.prototype.callAsWorker = function (...args) {
  26. return new Promise((resolve, reject) => {
  27. const code = `self.onmessage = e => self.postMessage((${this.toString()}).call(null, ...e.data));`,
  28. blob = new Blob([code], { type: "text/javascript" }),
  29. worker = new Worker((window.URL ?? window.webkitURL).createObjectURL(blob));
  30. worker.onmessage = (e) => (resolve(e.data), worker.terminate());
  31. worker.onerror = (e) => (reject(e.message), worker.terminate());
  32. worker.postMessage(args);
  33. });
  34. };
  35.  
  36. const originalElementPrototypeAppend = Element.prototype.append;
  37. Element.prototype.append = function (...elements) {
  38. elements = elements.filter((appendingElement) => {
  39. if (appendingElement instanceof HTMLElement) return true;
  40. return false;
  41. });
  42. originalElementPrototypeAppend.apply(this, elements);
  43. return this;
  44. };
  45. // Function.toString().replace(/^\s*\/\/.+$/gm, "").split("\n").filter((s) => s).map((s) => s.trim()).join("");
  46. })();
  47.  
  48. (async function ($name) {
  49. "use strict";
  50. const cache = {
  51. storage: await caches.open($name),
  52. /**
  53. * @param {string} url
  54. * @param {string} scriptContent
  55. */
  56. saveScript: function (url, scriptContent) {
  57. this.storage.put(url, new Response(scriptContent, { headers: { "Content-Type": "application/javascript" } }));
  58. },
  59. /**
  60. * @param {string} url
  61. * @param {object} json
  62. */
  63. saveObject: function (url, json) {
  64. this.storage.put(url, new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json" } }));
  65. },
  66. /**
  67. * @param {string} url
  68. * @returns {Promise<string>}
  69. */
  70. loadScript: async function (url) {
  71. const response = (await this.storage.match(url)) || (await fetch(url));
  72. return await response.text();
  73. },
  74. /**
  75. * @param {string} url
  76. * @returns {Promise<object>}
  77. */
  78. loadObject: async function (url) {
  79. const response = (await this.storage.match(url)) || (await fetch(url));
  80. return await response.json();
  81. },
  82. };
  83. const UI = {
  84. /**
  85. * Returns the first element that is a descendant of node that matches selectors.
  86. * @param {string} selectors
  87. * @param {ParentNode} parentElement
  88. * @returns {Element|null}
  89. */
  90. querySelect: (selectors, parentElement = document) => parentElement.querySelector(selectors),
  91. /**
  92. * Returns all element descendants of node that match selectors.
  93. * @param {string} selectors
  94. * @param {ParentNode} parentElement
  95. * @returns {NodeListOf<Element>}
  96. */
  97. querySelectAll: (selectors, parentElement = document) => parentElement.querySelectorAll(selectors),
  98. /**
  99. * Create Element and assign properties to it.
  100. * @param {string} tagName
  101. * @param {object} properties
  102. * @returns {Element}
  103. */
  104. createElement: (tagName, properties = {}) => {
  105. /** @type {Element} */
  106. const element = Object.assign(document.createElement(tagName), properties);
  107. if (properties.title) {
  108. try {
  109. element.setAttribute("data-toggle", "tooltip");
  110. element.setAttribute("data-html", "true");
  111. jQuery(element).tooltip({ boundary: "window" });
  112. } catch (error) {}
  113. }
  114. return element;
  115. },
  116. /**
  117. * Assign attributes to element.
  118. * @param {Element} element
  119. * @param {object} attributes
  120. */
  121. setAttributes: (element, attributes) => Object.entries(attributes).forEach(([k, v]) => element.setAttribute(k, v)),
  122. /**
  123. * Assign styles to element.
  124. * @param {Element} element
  125. * @param {object} styles
  126. */
  127. setStyles: (element, styles) => Object.assign(element.style, styles),
  128. /**
  129. * @param {string} name
  130. * @returns {HTMLDivElement}
  131. */
  132. createContainer: function (name = "div") {
  133. return this.createElement(name, { className: "container" });
  134. },
  135. /**
  136. * @returns {HTMLDivElement}
  137. */
  138. createRow: function () {
  139. return this.createElement("div", { className: "row" });
  140. },
  141. /**
  142. * @param {string} name
  143. * @returns {HTMLElement}
  144. */
  145. createIcon: function (name) {
  146. return this.createElement("i", { className: `fas fa-${name}` });
  147. },
  148. /**
  149. * @param {string} type
  150. * @param {object} properties
  151. * @returns {HTMLInputElement}
  152. */
  153. createInput: function (type, properties = {}) {
  154. const input = this.createElement("input", { ...{ className: "form-control" }, ...properties, ...{ type: type } });
  155. if (!input.id) input.id = uid(8);
  156. return input;
  157. },
  158. /**
  159. * @param {HTMLInputElement} input
  160. * @param {object} properties
  161. * @returns {HTMLLabelElement}
  162. */
  163. createLabelFor: function (input, properties = {}) {
  164. const label = this.createElement("label", { ...{ className: "btn btn-sm btn-outline-secondary" }, ...properties, ...{ htmlFor: input.id } });
  165. label.appendChild(input);
  166. input.hidden = true;
  167. if (input.type === "checkbox" || input.type === "radio") {
  168. input.addEventListener("input", function () {
  169. label.classList[input.checked ? "add" : "remove"]("active");
  170. });
  171. }
  172. return label;
  173. },
  174. /**
  175. * @param {string} className
  176. * @returns {HTMLElement & { show: Function, hide: Function }}
  177. */
  178. createSpinner: function (className = "text-secondary") {
  179. const spinner = this.createElement("div", { className: [className, "spinner-border spinner-border-sm"].join(" ") });
  180. spinner.show = function () {
  181. spinner.classList.remove("d-none");
  182. };
  183. spinner.hide = function () {
  184. spinner.classList.add("d-none");
  185. };
  186. spinner.hide();
  187. return spinner;
  188. },
  189. /**
  190. * @param {HTMLInputElement} input
  191. * @param {HTMLLabelElement|HTMLInputElement|HTMLButtonElement|HTMLElement} addon
  192. * @returns {HTMLDivElement}
  193. */
  194. createGroup: function (input, addon) {
  195. const group = this.createElement("div", { className: "input-group input-group-sm" });
  196. const groupAppend = this.createElement("div", { className: "input-group-append" });
  197.  
  198. if (!(addon instanceof HTMLInputElement || addon instanceof HTMLButtonElement)) {
  199. addon.classList.add("input-group-text");
  200. }
  201. groupAppend.appendChild(addon);
  202.  
  203. group.appendChild(input);
  204. group.appendChild(groupAppend);
  205.  
  206. return group;
  207. },
  208. /**
  209. * @param {Array<HTMLLabelElement|HTMLInputElement|HTMLButtonElement|HTMLElement>} inputs
  210. * @returns {HTMLDivElement}
  211. */
  212. createInputGroup: function (...inputs) {
  213. const group = this.createElement("div", { className: "input-group input-group-sm" });
  214.  
  215. let previousElementSibling = inputs[0];
  216. let appendGroup = this.createElement("div", { className: "input-group-append" });
  217. for (let index = 0; index < inputs.length; index++) {
  218. const input = inputs[index];
  219.  
  220. if (input instanceof HTMLInputElement) {
  221. if (!(previousElementSibling instanceof HTMLInputElement)) {
  222. group.appendChild(appendGroup);
  223. appendGroup = this.createElement("div", { className: "input-group-append" });
  224. }
  225. input.hidden = false;
  226. group.appendChild(input);
  227. } else {
  228. input.classList.add("input-group-text");
  229. appendGroup.appendChild(input);
  230. if (index + 1 === inputs.length) {
  231. group.appendChild(appendGroup);
  232. }
  233. }
  234. previousElementSibling = input;
  235. }
  236.  
  237. return group;
  238. },
  239. };
  240. const DrawariaOnlineMessageTypes = {
  241. /**
  242. * @param {string} message
  243. * @returns {string}
  244. */
  245. chatmsg: function (message) {
  246. // 42["chatmsg","a"]
  247. const data = ["chatmsg", message];
  248. return `${42}${JSON.stringify(data)}`;
  249. },
  250. /**
  251. * @returns {string}
  252. */
  253. passturn: function () {
  254. // 42["passturn"]
  255. const data = ["passturn"];
  256. return `${42}${JSON.stringify(data)}`;
  257. },
  258. /**
  259. * @param {number|string} playerid
  260. * @returns {string}
  261. */
  262. pgdrawvote: function (playerid) {
  263. // 42["pgdrawvote",2,0]
  264. const data = ["pgdrawvote", playerid, 0];
  265. return `${42}${JSON.stringify(data)}`;
  266. },
  267. /**
  268. * @returns {string}
  269. */
  270. pgswtichroom: function () {
  271. // 42["pgswtichroom"]
  272. const data = ["pgswtichroom"];
  273. return `${42}${JSON.stringify(data)}`;
  274. },
  275. /**
  276. * @returns {string}
  277. */
  278. playerafk: function () {
  279. // 42["playerafk"]
  280. const data = ["playerafk"];
  281. return `${42}${JSON.stringify(data)}`;
  282. },
  283. /**
  284. * @returns {string}
  285. */
  286. playerrated: function () {
  287. // 42["playerrated"]
  288. const data = ["playerrated"];
  289. return `${42}${JSON.stringify(data)}`;
  290. },
  291. /**
  292. * @param {number|string} gestureid
  293. * @returns {string}
  294. */
  295. sendgesture: function (gestureid) {
  296. // 42["sendgesture",16]
  297. const data = ["sendgesture", gestureid];
  298. return `${42}${JSON.stringify(data)}`;
  299. },
  300. /**
  301. * @returns {string}
  302. */
  303. sendvote: function () {
  304. // 42["sendvote"]
  305. const data = ["sendvote"];
  306. return `${42}${JSON.stringify(data)}`;
  307. },
  308. /**
  309. * @param {number|string} playerid
  310. * @returns {string}
  311. */
  312. sendvotekick: function (playerid) {
  313. // 42["sendvotekick",93]
  314. const data = ["sendvotekick", playerid];
  315. return `${42}${JSON.stringify(data)}`;
  316. },
  317. /**
  318. * @param {number|string} wordid
  319. * @returns {string}
  320. */
  321. wordselected: function (wordid) {
  322. // 42["wordselected",0]
  323. const data = ["sendvotekick", wordid];
  324. return `${42}${JSON.stringify(data)}`;
  325. },
  326. clientcmd: {
  327. /**
  328. * @param {number|string} itemid
  329. * @param {boolean} isactive
  330. * @returns {string}
  331. */
  332. activateitem: function (itemid, isactive) {
  333. const data = ["clientcmd", 12, [itemid, isactive]];
  334. return `${42}${JSON.stringify(data)}`;
  335. },
  336. /**
  337. * @param {number|string} itemid
  338. * @returns {string}
  339. */
  340. buyitem: function (itemid) {
  341. const data = ["clientcmd", 11, [itemid]];
  342. return `${42}${JSON.stringify(data)}`;
  343. },
  344. /**
  345. * @param {number|string} itemid
  346. * @param {"zindex"|"shared"} target
  347. * @param {any} value
  348. * @returns {string}
  349. */
  350. canvasobj_changeattr: function (itemid, target, value) {
  351. const data = ["clientcmd", 234, [itemid, target, value]];
  352. return `${42}${JSON.stringify(data)}`;
  353. },
  354. /**
  355. * @returns {string}
  356. */
  357. canvasobj_getobjects: function () {
  358. const data = ["clientcmd", 233];
  359. return `${42}${JSON.stringify(data)}`;
  360. },
  361. /**
  362. * @param {number|string} itemid
  363. * @returns {string}
  364. */
  365. canvasobj_remove: function (itemid) {
  366. let data = ["clientcmd", 232, [itemid]];
  367. return `${42}${JSON.stringify(data)}`;
  368. },
  369. /**
  370. * @param {number|string} itemid
  371. * @param {number|string} positionX
  372. * @param {number|string} positionY
  373. * @param {number|string} speed
  374. * @returns {string}
  375. */
  376. canvasobj_setposition: function (itemid, positionX, positionY, speed) {
  377. const data = ["clientcmd", 230, [itemid, 100 / positionX, 100 / positionY, { movespeed: speed }]];
  378. return `${42}${JSON.stringify(data)}`;
  379. },
  380. /**
  381. * @param {number|string} itemid
  382. * @param {number|string} rotation
  383. * @returns {string}
  384. */
  385. canvasobj_setrotation: function (itemid, rotation) {
  386. const data = ["clientcmd", 231, [itemid, rotation]];
  387. return `${42}${JSON.stringify(data)}`;
  388. },
  389. /**
  390. * @param {any} value
  391. * @returns {string}
  392. */
  393. customvoting_setvote: function (value) {
  394. const data = ["clientcmd", 301, [value]];
  395. return `${42}${JSON.stringify(data)}`;
  396. },
  397. /**
  398. * @param {any} value
  399. * @returns {string}
  400. */
  401. getfpid: function (value) {
  402. const data = ["clientcmd", 901, [value]];
  403. return `${42}${JSON.stringify(data)}`;
  404. },
  405. /**
  406. * @returns {string}
  407. */
  408. getinventory: function () {
  409. const data = ["clientcmd", 10, [true]];
  410. return `${42}${JSON.stringify(data)}`;
  411. },
  412. /**
  413. * @returns {string}
  414. */
  415. getspawnsstate: function () {
  416. const data = ["clientcmd", 102];
  417. return `${42}${JSON.stringify(data)}`;
  418. },
  419. /**
  420. * @param {number|string} positionX
  421. * @param {number|string} positionY
  422. * @returns {string}
  423. */
  424. moveavatar: function (positionX, positionY) {
  425. const data = ["clientcmd", 103, [1e4 * Math.floor(positionX * 0.01 * 1e4) + Math.floor(positionY * 0.01 * 1e4), false]];
  426. return `${42}${JSON.stringify(data)}`;
  427. },
  428. /**
  429. * @returns {string}
  430. */
  431. setavatarprop: function () {
  432. const data = ["clientcmd", 115];
  433. return `${42}${JSON.stringify(data)}`;
  434. },
  435. /**
  436. * @param {number|string} flagid
  437. * @param {boolean} isactive
  438. * @returns {string}
  439. */
  440. setstatusflag: function (flagid, isactive) {
  441. const data = ["clientcmd", 3, [flagid, isactive]];
  442. return `${42}${JSON.stringify(data)}`;
  443. },
  444. /**
  445. * @param {number|string} playerid
  446. * @param {number|string} tokenid
  447. * @returns {string}
  448. */
  449. settoken: function (playerid, tokenid) {
  450. const data = ["clientcmd", 2, [playerid, tokenid]];
  451. return `${42}${JSON.stringify(data)}`;
  452. },
  453. /**
  454. * @param {number|string} playerid
  455. * @param {any} value
  456. * @returns {string}
  457. */
  458. snapchatmessage: function (playerid, value) {
  459. const data = ["clientcmd", 330, [playerid, value]];
  460. return `${42}${JSON.stringify(data)}`;
  461. },
  462. /**
  463. * @returns {string}
  464. */
  465. spawnavatar: function () {
  466. const data = ["clientcmd", 101];
  467. return `${42}${JSON.stringify(data)}`;
  468. },
  469. /**
  470. * @returns {string}
  471. */
  472. startrollbackvoting: function () {
  473. const data = ["clientcmd", 320];
  474. return `${42}${JSON.stringify(data)}`;
  475. },
  476. /**
  477. * @returns {string}
  478. */
  479. trackforwardvoting: function () {
  480. const data = ["clientcmd", 321];
  481. return `${42}${JSON.stringify(data)}`;
  482. },
  483. /**
  484. * @param {number|string} trackid
  485. * @returns {string}
  486. */
  487. votetrack: function (trackid) {
  488. const data = ["clientcmd", 1, [trackid]];
  489. return `${42}${JSON.stringify(data)}`;
  490. },
  491. },
  492. /**
  493. * @param {string} roomID
  494. * @param {string} name
  495. * @param {string} uid
  496. * @param {string} wt
  497. * @returns {string}
  498. */
  499. startplay: function (roomID, name = undefined, uid = undefined, wt = undefined) {
  500. const data = `${420}${JSON.stringify(["startplay", name, 2, "en", roomID, null, [null, "https://drawaria.online/", 1000, 1000, [null, uid, wt], null]])}`;
  501. return data;
  502. },
  503. clientnotify: {
  504. /**
  505. * @param {number|string} playerid
  506. * @returns {string}
  507. */
  508. requestcanvas: function (playerid) {
  509. const data = ["clientnotify", playerid, 10001];
  510. return `${42}${JSON.stringify(data)}`;
  511. },
  512. /**
  513. * @param {number|string} playerid
  514. * @param {string} base64
  515. * @returns {string}
  516. */
  517. respondcanvas: function (playerid, base64) {
  518. const data = ["clientnotify", playerid, 10002, [base64]];
  519. return `${42}${JSON.stringify(data)}`;
  520. },
  521. /**
  522. * @param {number|string} playerid
  523. * @param {number|string} imageid
  524. * @returns {string}
  525. */
  526. galleryupload: function (playerid, imageid) {
  527. const data = ["clientnotify", playerid, 11, [imageid]];
  528. return `${42}${JSON.stringify(data)}`;
  529. },
  530. /**
  531. * @param {number|string} playerid
  532. * @param {any} type
  533. * @returns {string}
  534. */
  535. warning: function (playerid, type) {
  536. const data = ["clientnotify", playerid, 100, [type]];
  537. return `${42}${JSON.stringify(data)}`;
  538. },
  539. /**
  540. * @param {number|string} playerid
  541. * @param {string} targetname
  542. * @param {boolean} mute
  543. * @returns {string}
  544. */
  545. mute: function (playerid, targetname, mute = false) {
  546. const data = ["clientnotify", playerid, 1, [mute, targetname]];
  547. return `${42}${JSON.stringify(data)}`;
  548. },
  549. /**
  550. * @param {number|string} playerid
  551. * @param {string} targetname
  552. * @param {boolean} hide
  553. * @returns {string}
  554. */
  555. hide: function (playerid, targetname, hide = false) {
  556. const data = ["clientnotify", playerid, 3, [hide, targetname]];
  557. return `${42}${JSON.stringify(data)}`;
  558. },
  559. /**
  560. * @param {number|string} playerid
  561. * @param {string} reason
  562. * @param {string} targetname
  563. * @returns {string}
  564. */
  565. report: function (playerid, reason, targetname) {
  566. const data = ["clientnotify", playerid, 2, [targetname, reason]];
  567. return `${42}${JSON.stringify(data)}`;
  568. },
  569. },
  570. drawcmd: {
  571. /**
  572. * @param {number|string} playerid
  573. * @param {number|string} x1
  574. * @param {number|string} y1
  575. * @param {number|string} x2
  576. * @param {number|string} y2
  577. * @param {number|string} size
  578. * @param {number|string} color
  579. * @param {boolean} ispixel
  580. * @returns {string}
  581. */
  582. line: function (x1, y1, x2, y2, color, size = 4, ispixel = true, playerid = 0) {
  583. const data = ["drawcmd", 0, [x1 * 0.001, y1 * 0.001, x2 * 0.001, y2 * 0.001, false, -size, color, playerid, ispixel]];
  584. return `${42}${JSON.stringify(data)}`;
  585. },
  586. /**
  587. * @param {number|string} playerid
  588. * @param {number|string} x1
  589. * @param {number|string} y1
  590. * @param {number|string} x2
  591. * @param {number|string} y2
  592. * @param {number|string} size
  593. * @param {number|string} color
  594. * @returns {string}
  595. */
  596. erase: function (x1, y1, x2, y2, color, size, ispixel = true, playerid = 0) {
  597. const data = ["drawcmd", 1, [x1 * 0.01, y1 * 0.01, x2 * 0.01, y2 * 0.01, false, -size, color, playerid, ispixel]];
  598. return `${42}${JSON.stringify(data)}`;
  599. },
  600. /**
  601. * @param {number|string} x
  602. * @param {number|string} y
  603. * @param {number|string} color
  604. * @param {number|string} tolerance
  605. * @param {number|string} r
  606. * @param {number|string} g
  607. * @param {number|string} b
  608. * @param {number|string} a
  609. * @returns {string}
  610. */
  611. flood: function (x, y, color, tolerance, r, g, b, a) {
  612. // 42["drawcmd",2,[x, y,color,{"0":r,"1":g,"2":b,"3":a},size]]
  613. const data = ["drawcmd", 2, [x * 0.01, y * 0.01, color, { 0: r, 1: g, 2: b, 3: a }, tolerance]];
  614. return `${42}${JSON.stringify(data)}`;
  615. },
  616. /**
  617. * @param {number|string} playerid
  618. * @returns {string}
  619. */
  620. undo: function (playerid) {
  621. // 42["drawcmd",3,[playerid]]
  622. const data = ["drawcmd", 3, [playerid]];
  623. return `${42}${JSON.stringify(data)}`;
  624. },
  625. /**
  626. * @returns {string}
  627. */
  628. clear: function () {
  629. // 42["drawcmd",4,[]]
  630. const data = ["drawcmd", 4, []];
  631. return `${42}${JSON.stringify(data)}`;
  632. },
  633. },
  634. };
  635.  
  636. /**
  637. * @param {string} message
  638. * @param {string} styles
  639. */
  640. const log = (message, styles = "color:#5090C1", application = undefined) => console.log("%c" + [`[${application ?? $name}]`, message].join(" "), styles);
  641.  
  642. /**
  643. * Generate radnow UID
  644. * @param {number} size
  645. * @returns {string}
  646. */
  647. const uid = (size = 8) => {
  648. const MASK = 0x3d;
  649. const LETTERS = "abcdefghijklmnopqrstuvwxyz";
  650. const NUMBERS = "1234567890";
  651. const SPECIALS = "-_";
  652. const charset = `${NUMBERS}${LETTERS}${LETTERS.toUpperCase()}${SPECIALS}`.split("");
  653.  
  654. const bytes = new Uint8Array(size);
  655. crypto.getRandomValues(bytes);
  656.  
  657. return bytes.reduce((acc, byte) => `${acc}${charset[byte & MASK]}`, "");
  658. };
  659.  
  660. /**
  661. * @param {number[]} byteArray
  662. * @returns {string}
  663. */
  664. const toHexString = (byteArray) => {
  665. return byteArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
  666. };
  667.  
  668. /**
  669. * @param {string} key
  670. * @param {string} value
  671. */
  672. const setCookie = (key, value) => {
  673. if (window.Cookie) Cookies.set(key, value);
  674. document.cookie = `${key}=${value}; Secure; Path=/; SameSite=None; Partitioned;`;
  675. };
  676.  
  677. /**
  678. * @param {(index:number,value:*)=>{}} callbackOnDelete
  679. * @param {(index:number,value:*)=>{}} callbackOnSet
  680. * @returns {Array<*>}
  681. */
  682. const makeObservableArray = (callbackOnDelete, callbackOnSet) => {
  683. if (!("Proxy" in window)) {
  684. console.warn("Your browser doesn't support Proxies.");
  685. return;
  686. }
  687.  
  688. // a proxy for our array
  689. const proxy = new Proxy([], {
  690. deleteProperty: function (target, property) {
  691. (callbackOnDelete ?? ___)(property, target[property]);
  692. delete target[property];
  693. return true;
  694. },
  695. set: function (target, property, value, receiver) {
  696. target[property] = value;
  697. (callbackOnSet ?? ___)(property, value);
  698. return true;
  699. },
  700. });
  701.  
  702. Object.defineProperty(proxy, "isProxy", {
  703. value: true,
  704. writable: false,
  705. });
  706.  
  707. return proxy;
  708. };
  709.  
  710. /**
  711. * @param {string} message
  712. * @returns {Array<any>|object}
  713. */
  714. const tryParseJSON = (message) => {
  715. try {
  716. return JSON.parse(message);
  717. } catch (error) {
  718. return [];
  719. }
  720. };
  721.  
  722. const clearScripts = (remove = true) => {
  723. try {
  724. let array = document.querySelectorAll('script[src]:not([data-codemaid="ignore"])');
  725. array.forEach((script) => {
  726. if (script.src != "") document.head.appendChild(script);
  727. });
  728. } catch (error) {
  729. console.error(error);
  730. }
  731.  
  732. try {
  733. let unifiedScript = DefinableCore.UI.createElement("script");
  734.  
  735. let scripts = document.querySelectorAll('script:not([src]):not([data-codemaid="ignore"])');
  736. let unifiedScriptContent = "";
  737. scripts.forEach((script) => {
  738. let content = script.textContent; //.replaceAll(/\s/g, '');
  739.  
  740. unifiedScriptContent += `try{${content}}catch(e){console.warn(e);}`;
  741. script.remove();
  742. });
  743.  
  744. unifiedScript.textContent = unifiedScriptContent;
  745.  
  746. if (!remove) document.head.appendChild(unifiedScript);
  747. } catch (error) {
  748. console.error(error);
  749. }
  750. };
  751.  
  752. const clearStyles = (remove = false) => {
  753. try {
  754. let unifiedStyles = DefinableCore.UI.createElement("style");
  755. unifiedStyles.textContet = "";
  756.  
  757. let styles = document.querySelectorAll('style:not([data-codemaid="ignore"])');
  758. styles.forEach((style) => {
  759. unifiedStyles.textContent += style.textContent;
  760. style.remove();
  761. });
  762. if (!remove) document.head.appendChild(unifiedStyles);
  763. } catch (error) {
  764. console.error(error);
  765. }
  766. };
  767.  
  768. const clearEmbeds = () => {
  769. try {
  770. let array = document.querySelectorAll("iframe");
  771. array.forEach((iframe) => {
  772. iframe.remove();
  773. });
  774. } catch (error) {
  775. console.error(error);
  776. }
  777. };
  778.  
  779. class Player {
  780. /** @type {Array<Player>} */
  781. static instances = [];
  782. static get noConflict() {
  783. return Player.instances[0] ?? new Player(uid(12));
  784. }
  785. /**
  786. * @param {string|undefined} inviteLink
  787. * @returns {URL}
  788. */
  789. static getSocketServerURL(inviteLink) {
  790. if (typeof inviteLink === "undefined") return `wss://sv3.drawaria.online/socket.io/?sid1=undefined&hostname=drawaria.online&EIO=3&transport=websocket`;
  791. const roomID = this.getRoomID(inviteLink);
  792. const [_voidable, serverPrefix] = roomID.split(".");
  793. return new URL(`wss://${typeof serverPrefix === "undefined" ? "" : "sv".concat(serverPrefix, ".")}drawaria.online/socket.io/?sid1=undefined&hostname=drawaria.online&EIO=3&transport=websocket`);
  794. }
  795. /**
  796. * @param {string|undefined} inviteLink
  797. * @returns {string}
  798. */
  799. static getRoomID(inviteLink) {
  800. const inviteURL = new URL(inviteLink);
  801. return inviteURL.pathname.slice(6);
  802. }
  803. static parseMessage = DrawariaOnlineMessageTypes;
  804.  
  805. /** @type {string} */
  806. name;
  807. /** @type {string} */
  808. uid;
  809. /** @type {string} */
  810. wt;
  811. /** @type {string} */
  812. roomID;
  813.  
  814. /** @type {WebSocket} */
  815. socket;
  816.  
  817. /** @type {Map<string, Function[]>} */
  818. events;
  819.  
  820. /**
  821. * @param {string} name
  822. */
  823. constructor(name = undefined) {
  824. this.name = name;
  825. this.uid = "_";
  826. this.wt = undefined;
  827. this.roomID = undefined;
  828.  
  829. this.events = new Map();
  830.  
  831. Player.instances.push(this);
  832. }
  833.  
  834. /**
  835. * @param {string} inviteLink
  836. */
  837. connect = (inviteLink) => {
  838. if (this.isConnected) return;
  839. this.#createNewConnection(inviteLink);
  840. };
  841.  
  842. disconnect = () => {
  843. this.send("41");
  844. if (this.isConnected) this.socket.close();
  845. };
  846.  
  847. reconnect = () => {
  848. if (!this.isConnected) {
  849. this.connect(`https://drawaria.online/room/${this.roomID}`);
  850. return;
  851. }
  852. this.send("41");
  853. this.send("40");
  854. };
  855.  
  856. /**
  857. * @param {string} inviteLink
  858. */
  859. enterRoom = (inviteLink = undefined) => {
  860. this.roomID = Player.getRoomID(inviteLink ?? document.querySelector("#invurl").value);
  861. this.reconnect();
  862. };
  863.  
  864. nextRoom = () => {
  865. this.send(Player.parseMessage.pgswtichroom());
  866. };
  867.  
  868. leaveRoom = () => {
  869. this.send("41");
  870. };
  871.  
  872. /**
  873. * @param {string} payload
  874. */
  875. send = (payload) => {
  876. if (this.isConnected) this.socket.send(payload);
  877. else debug(`send failed! Connection is closed`);
  878. };
  879.  
  880. /**
  881. * @returns {boolean}
  882. */
  883. get isConnected() {
  884. return typeof this.socket !== "undefined" && this.socket instanceof WebSocket && (this.socket.readyState === WebSocket.OPEN || this.socket.readyState === WebSocket.CONNECTING);
  885. }
  886.  
  887. #sendHeartbeat = () => {
  888. if (this.isConnected) this.socket.send(2);
  889. };
  890.  
  891. /**
  892. * @param {string} inviteLink
  893. */
  894. #createNewConnection = (inviteLink) => {
  895. this.socket = new WebSocket(Player.getSocketServerURL(inviteLink));
  896. this.roomID = Player.getRoomID(inviteLink);
  897. this.socket.addEventListener("open", this.#startHeartbeat);
  898. this.socket.addEventListener("message", this.#handleMessage);
  899. };
  900.  
  901. #startHeartbeat = () => {
  902. this.heartbeatInterval = setInterval(this.#sendHeartbeat, 25000);
  903. };
  904.  
  905. #handleMessage = (rawMessage) => {
  906. const [eventType, eventName, eventData] = this.#parseMessageEvent(rawMessage);
  907.  
  908. switch (eventType) {
  909. case "40":
  910. this.send(Player.parseMessage.startplay(this.roomID, this.name, this.uid, this.wt));
  911. break;
  912. case "430":
  913. break;
  914. case "42":
  915. this.__invokeEvent(eventName, eventData);
  916. break;
  917. default:
  918. }
  919. };
  920.  
  921. /**
  922. * @param {MessageEvent} messageEvent
  923. * @returns {[string, string, Array<any>|object]}
  924. */
  925. #parseMessageEvent = (messageEvent) => {
  926. const rawData = String(messageEvent.data);
  927. const messageType = (rawData.match(/^\d+/i) ?? [""])[0];
  928. const messageData = messageType.length === rawData.length ? [] : tryParseJSON(rawData.slice(messageType.length));
  929. return [messageType, Array.isArray(messageData) ? messageData.shift() : "", messageData];
  930. };
  931.  
  932. /**
  933. * @param {DrawariaOnlineEvents} event
  934. * @param {Function} callback
  935. */
  936. addEventListener = (event, callback, id = undefined) => {
  937. if (!this.hasEventListener(event)) {
  938. this.events.set(event, []);
  939. }
  940. callback.id = id ?? this.addEventListener.caller.name ?? uid(8);
  941. try {
  942. this.events.get(event).push(callback);
  943. } catch (error) {
  944. debug(`addEventListener returned error \"${error}\"`, "color:firebrick");
  945. }
  946. };
  947.  
  948. /**
  949. * @param {DrawariaOnlineEvents} event
  950. * @param {*} data
  951. */
  952. __invokeEvent = (event, data) => {
  953. if (this.hasEventListener(event)) {
  954. const listeners = this.events.get(event);
  955. listeners.forEach((listener) => listener(data));
  956. }
  957. };
  958.  
  959. /**
  960. * @param {DrawariaOnlineEvents} name
  961. * @returns {boolean}
  962. */
  963. hasEventListener = (name) => {
  964. return this.events.has(name);
  965. };
  966. }
  967.  
  968. class DefinableCore {
  969. static UI = UI;
  970. static Player = Player;
  971.  
  972. constructor(moduleName, moduleIcon = "code") {
  973. this.name = moduleName;
  974. this.__initializeHead(moduleIcon);
  975. this.__initializeBody();
  976. }
  977.  
  978. __initializeHead(moduleIcon) {
  979. const input = this.UI.createInput("checkbox");
  980. this.label = this.UI.createLabelFor(input, { title: this.name });
  981. const icon = this.UI.createIcon(moduleIcon);
  982. this.label.appendChild(icon);
  983. }
  984.  
  985. __initializeBody() {
  986. const submoduleSelectionContainer = this.UI.createElement("nav", { className: "row" });
  987. const wrapper = this.UI.createElement("div");
  988. this.wrapper = wrapper;
  989. this.__contaier = this.UI.createElement("div");
  990. this.__contaier.appendChild(this.UI.createElement("b", { textContent: this.name }));
  991. // this.wrapper.appendChild(submoduleSelectionContainer);
  992. this.__contaier.appendChild(submoduleSelectionContainer);
  993. this.wrapper.appendChild(this.__contaier);
  994. const input = this.UI.querySelect("&>input", this.label);
  995. input.addEventListener("input", function () {
  996. wrapper.classList[input.checked ? "remove" : "add"]("d-none");
  997. });
  998. wrapper.classList.add("d-none");
  999. }
  1000.  
  1001. createRow() {
  1002. const row = this.UI.createRow();
  1003. this.__contaier.appendChild(row);
  1004. return row;
  1005. }
  1006.  
  1007. /**
  1008. * @param {DefinableCore} submodule
  1009. */
  1010. registerModule(submodule) {
  1011. // const submoduleSelectionContainer = this.UI.querySelect("&>nav", this.wrapper);
  1012. const submoduleSelectionContainer = this.UI.querySelect("&>nav", this.__contaier);
  1013. submoduleSelectionContainer.appendChild(submodule.label);
  1014. this.wrapper.appendChild(submodule.wrapper);
  1015. }
  1016.  
  1017. get UI() {
  1018. return DefinableCore.UI;
  1019. }
  1020. get Player() {
  1021. return DefinableCore.Player;
  1022. }
  1023. }
  1024.  
  1025. window.addEventListener("definable:core:init", function () {
  1026. const ui = DefinableCore.UI;
  1027. const definable = new DefinableCore("Definable", "code");
  1028.  
  1029. const chatbox = ui.querySelect("#chatbox_messages");
  1030. /** @type {HTMLElement} */
  1031. const devider = chatbox.previousElementSibling;
  1032. devider.before(definable.wrapper);
  1033. definable.wrapper.before(devider.cloneNode(false));
  1034.  
  1035. setTimeout(() => {
  1036. window.dispatchEvent(new CustomEvent("definable:init", { detail: { main: definable, core: DefinableCore } }));
  1037. definable.wrapper.classList.remove("d-none");
  1038. definable.wrapper.classList.add("container");
  1039. definable.wrapper.id = "definable";
  1040. }, 500);
  1041. });
  1042.  
  1043. setTimeout(() => {
  1044. window.dispatchEvent(new CustomEvent("definable:core:init"));
  1045. clearEmbeds();
  1046. clearScripts();
  1047. clearStyles();
  1048. document.head.appendChild(
  1049. UI.createElement("style", { id: "definableStyles", textContent: ["#definable .row { gap: 0.125rem; margin-bottom: 0.125rem; }", "#definable label { margin: 0; }"].join("\n") })
  1050. );
  1051. // console.clear();
  1052. }, 500);
  1053.  
  1054. function launchScriptManager() {
  1055. const ui = DefinableCore.UI;
  1056. const container = ui.createContainer();
  1057.  
  1058. {
  1059. const row = ui.createRow();
  1060.  
  1061. const scriptSourceLinkInput = ui.createInput("text", { className: "form-control" });
  1062. const requestSourceInput = ui.createInput("button");
  1063. const requestSourceLabel = ui.createLabelFor(requestSourceInput);
  1064. const group = ui.createGroup(scriptSourceLinkInput, requestSourceLabel);
  1065. const spinnerIcon = ui.createSpinner("");
  1066. const defaultIcon = ui.createIcon("external-link-alt");
  1067.  
  1068. requestSourceLabel.appendChild(defaultIcon);
  1069. requestSourceLabel.appendChild(spinnerIcon);
  1070.  
  1071. requestSourceInput.addEventListener("click", function () {
  1072. defaultIcon.classList.add("d-none");
  1073. spinnerIcon.show();
  1074.  
  1075. if (!scriptSourceLinkInput.value.startsWith("http")) {
  1076. log("Not a valid Link: ".concat(scriptSourceLinkInput.value), "color: firebrick;");
  1077. spinnerIcon.hide();
  1078. defaultIcon.classList.remove("d-none");
  1079. return;
  1080. }
  1081.  
  1082. const requestSourceLink = new URL(scriptSourceLinkInput.value);
  1083. log("Requesting Module: ".concat(requestSourceLink), "color: rebeccapurple;");
  1084.  
  1085. fetch(requestSourceLink).then(async (response) => {
  1086. const copy = response.clone();
  1087. const content = await copy.text();
  1088.  
  1089. if (copy.status === 200 && content) {
  1090. cache.saveScript(requestSourceLink, content);
  1091. log("Module installed!", "color: forestgreen;");
  1092. spinnerIcon.hide();
  1093. defaultIcon.classList.remove("d-none");
  1094. scriptSourceLinkInput.value = "";
  1095. }
  1096. });
  1097. });
  1098.  
  1099. row.appendChild(group);
  1100. container.appendChild(row);
  1101. }
  1102.  
  1103. DefinableCore.UI.querySelect("#login-leftcol").appendChild(container);
  1104. }
  1105. })("definable");