Xat Plus

Unaffiliated extension for the Xat HTML5 Chatbox. RIP Xat Edition.

  1. // ==UserScript==
  2. // @name Xat Plus
  3. // @namespace http://sulptax.io/
  4. // @version 0.4.7
  5. // @description Unaffiliated extension for the Xat HTML5 Chatbox. RIP Xat Edition.
  6. // @author sulptax
  7. // @match https://xat.com/content/web/*/box/www/classic.html
  8. // @require http://code.jquery.com/jquery-3.5.1.js
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js
  10. // @grant unsafeWindow
  11. // ==/UserScript==
  12. const secret = 'sulptax';
  13.  
  14. function xp_encrypt(str) {
  15. return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(str));
  16. }
  17.  
  18. function xp_decrypt(str) {
  19. return CryptoJS.enc.Base64.parse(str).toString(CryptoJS.enc.Utf8);
  20. }
  21.  
  22. function xp_aes_encrypt(str) {
  23. return CryptoJS.AES.encrypt(str, secret).toString();
  24. }
  25.  
  26. function xp_aes_decrypt(str) {
  27. var bytes = CryptoJS.AES.decrypt(str, secret);
  28. return bytes.toString(CryptoJS.enc.Utf8);
  29. }
  30.  
  31. // Allow functions below to be used in the console, not like these are used for storing important data anyways.
  32. // You must be at the chat directly for these to work:
  33. // https://xat.com/content/web/R00022/box/www/classic.html
  34. unsafeWindow.xp_encrypt = xp_encrypt;
  35. unsafeWindow.xp_decrypt = xp_decrypt;
  36.  
  37. var users = {
  38. "585435788": {
  39. "type": "color", // color, pawn, pawn2
  40. "reghide": true, // like reghide power
  41. "v": "000001", // the color value
  42. },
  43. "400444434": {
  44. "type": "color",
  45. "v": "y",
  46. "effective": true, // overwrites the message pawn as well if true, This is the case by default.
  47. "persistent": true // keep the pawn color regardless of the pawn used in the list
  48. },
  49. "287773358": { // Shadez bot is here for demonstration purposes, because it's everywhere.
  50. "type": "pawn2", // pawn2 changes the pawn type
  51. "v": "p1bot" // examples: p1bot, p1emerald, p1pwn
  52. },
  53. "981989": {
  54. "type": "pawn2",
  55. "v": "p1emerald#y" // can also use color codes in pawn2 value
  56. },
  57. "30000013": {
  58. "type":"pawn2",
  59. "v": "p1emerald#r#r"
  60. }
  61. }
  62.  
  63. var effectiveChanges = {}
  64. const observer = new MutationObserver((mutationList, observer) => {
  65. mutationList.forEach((mutation) => {
  66. if (mutation.type === "childList") {
  67. if (mutation.target.id == "idvisitors" || mutation.target.id == "idfriends" || mutation.target.id == "messages") {
  68. mutation.addedNodes.forEach((node) => {
  69. Object.keys(users).forEach(function(user) {
  70. var color = "";
  71. var holder = $(node).find(".holder:first");
  72. var res = holder.attr("data-sm");
  73. if ($(node).attr("data-user") == user) {
  74. var str = res
  75. if (res.split("#")[1] != "ff0000" && res.split("#")[1] != "ff0000)_20") {
  76. if (users[$(node).attr("data-user")]['type'] == "color") {
  77. color = users[$(node).attr("data-user")]['v']
  78. res = str.split("#");
  79. res[1] = color;
  80. res = res.join("#");
  81. res = res + ")_20"
  82. }
  83. if (users[$(node).attr("data-user")]['type'] == "pawn") {
  84. res = users[$(node).attr("data-user")]['v']
  85. }
  86. var res3 = "";
  87. if (users[$(node).attr("data-user")]['type'] == "pawn2") {
  88. var res2 = str.split("#");
  89. res2[0] = res2[0].split("(")[0] + "(" + users[$(node).attr("data-user")]['v'];
  90. res = res2.join("#")
  91. res3 = res.split("#", 2).join("#") + ")_20";
  92. } else {
  93. res3 = res;
  94. }
  95. if (Object.keys(users[$(node).attr("data-user")]).includes("reghide")) {
  96. $(node).find(".relIcon2").hide();
  97. }
  98. var filters = "";
  99. if (Object.keys(users[$(node).attr("data-user")]).includes("glow")) {
  100. if(isColor(users[$(node).attr("data-user")]['glow'])) {
  101. filters = filters + "drop-shadow(0px 0px 0.2rem " + users[$(node).attr("data-user")]['glow'] + ") "
  102. }
  103. }
  104. if(filters) {
  105. holder.css("filter", filters);
  106. }
  107. if (!(Object.keys(users[$(node).attr("data-user")]).includes("effective") && !(users[$(node).attr("data-user")]['effective'] == true))) {
  108. effectiveChanges[$(node).find(".message").text()] = res3
  109. } else if (color && (Object.keys(users[$(node).attr("data-user")]).includes("persistent") && users[$(node).attr("data-user")]['persistent'])) {
  110. effectiveChanges[$(node).find(".message").text()] = "s_(p1pwn#" + color + ")_20"
  111. }
  112. holder.attr("data-sm", res)
  113. }
  114. }
  115. });
  116. });
  117. }
  118. }
  119. });
  120. });
  121. messages.addMessageP = messages.addMessage
  122. messages.addMessage = function(t) {
  123. messages.addMessageP(t);
  124. }
  125. visitors.addVisitorP = visitors.addVisitor
  126. visitors.addVisitor = function(e, l) {
  127. visitors.addVisitorP(e, l);
  128. var e_parsed = JSON.parse(e);
  129. if (Object.keys(e_parsed).includes("image")) {
  130. e_parsed['image'].split("#").forEach((im) => {
  131. if (true) { // im.toString(CryptoJS.enc.Utf8)
  132. try {
  133. var checkForPawn = xp_decrypt(im)
  134. if (checkForPawn && (checkForPawn.startsWith('{') && checkForPawn.endsWith('}'))) {
  135. // example JSON
  136. // {"type":"pawn2", "v":"p1emerald#y#hat3#w1i0ic#y", "reghide": true}
  137. // {"type":"pawn2", "v":"p1emerald#y#hat3#w1i0ic#y", "reghide": true, "glow":"red"}
  138. console.log("JSON PAWN DETECTED IN avatar/image:", checkForPawn);
  139. try {
  140. users[e_parsed['id']] = JSON.parse(checkForPawn);
  141. } catch (e) {
  142. console.log("Error parsing pawn code");
  143. }
  144. } else if (checkForPawn && checkForPawn.substring(0, 2) == "p1") {
  145. console.log("NORMAL PAWN DETECTED IN avatar/image:", checkForPawn);
  146. users[e_parsed['id']] = {
  147. "type": "pawn2",
  148. "v": checkForPawn
  149. }
  150. }
  151. } catch(e) {
  152. // decrypt error?
  153. }
  154. }
  155. })
  156. }
  157. }
  158. const observer2 = new MutationObserver((mutationList, observer) => {
  159. mutationList.forEach((mutation) => {
  160. if (mutation.type === "childList") {
  161. mutation.addedNodes.forEach((node) => {
  162. if (Object.keys(effectiveChanges).includes($(node).find(".messagesName").find(".message").text())) {
  163. $(node).find(".messagesName").find(".holder:first").attr("data-sm", effectiveChanges[$(node).find(".messagesName").find(".message").text()])
  164. }
  165. });
  166. }
  167. });
  168. });
  169. observer.observe(document.querySelector("#visitorsContainer"), {
  170. childList: true,
  171. subtree: true
  172. });
  173. observer2.observe(document.querySelector("#messagesContainer"), {
  174. childList: true,
  175. subtree: true
  176. });
  177. function isColor(color) {
  178. var div = $("<div>");
  179. div.css("border", "1px solid " + color);
  180. return (div.css("border-color") != "")
  181. }