Florr.io Server Selector

Press Tab to open Server Selector.

  1. // ==UserScript==
  2. // @name Florr.io Server Selector
  3. // @namespace Violentmonkey Scripts
  4. // @author ash
  5. // @version 3.0.0
  6. // @description Press Tab to open Server Selector.
  7. // @match *://florr.io/*
  8. // @grant unsafeWindow
  9. // @grant GM_addStyle
  10. // @grant GM_getResourceText
  11. // @resource modalCSS https://gist.githubusercontent.com/ashfr/11951eb0082892a30ef4538bbd7b972e/raw/5244f4efd286d21a03d2cb76c69cf9e590638a9e/modalCSS.txt
  12. // ==/UserScript==
  13. let modalCSS = GM_getResourceText("modalCSS");
  14. GM_addStyle(modalCSS);
  15.  
  16. (() => {
  17. "use strict";
  18.  
  19. const Config = {
  20. hotkey: {
  21. connectUI: 'Tab'
  22. },
  23. script: {
  24. m28nOverride: false,
  25. socket: null,
  26. currentId: '',
  27. ids: []
  28. }
  29. };
  30.  
  31. const modal = document.createElement("div");
  32. modal.classList.add("modal");
  33. modal.innerHTML = `
  34. <div class='modal-content'>
  35. <span class='close'>&times;</span>
  36. <div class='select'>
  37. <label for="serverSelect">Choose a server: </label>
  38. <select id="serverSelect" name="serverSelect"></select>
  39. </div>
  40. <span class='author'>Made by ash.</span>
  41. </div>
  42. `;
  43. document.body.appendChild(modal);
  44.  
  45. const closeModal = () => {
  46. if (modal.classList.contains('visible')) {
  47. modal.classList.remove("visible");
  48. }
  49. };
  50.  
  51. const toggleModal = () => {
  52. modal.classList.toggle("visible");
  53. };
  54.  
  55. const handleKeypress = (e) => {
  56. let key = e.key;
  57. switch (key) {
  58. case "Tab":
  59. e.preventDefault();
  60. e.stopPropagation();
  61. toggleModal();
  62. break;
  63. case "Escape":
  64. e.preventDefault();
  65. e.stopPropagation();
  66. closeModal();
  67. break;
  68. default:
  69. break;
  70. }
  71. };
  72.  
  73. document.addEventListener("keydown", handleKeypress);
  74. document.querySelector(".close").addEventListener("click", closeModal);
  75. window.addEventListener('click', (e) => {
  76. if (e.target === modal) {
  77. closeModal();
  78. }
  79. })
  80.  
  81. const serverSelect = document.getElementById('serverSelect');
  82. serverSelect.onchange = (e) => {
  83. connectServer();
  84. closeModal();
  85. }
  86.  
  87. const capitalizeFirstLetter = (s) => s.charAt(0).toUpperCase() + s.slice(1);
  88.  
  89. const fetchServers = async () => {
  90. let url = "https://api.n.m28.io";
  91.  
  92. try {
  93. let response = await fetch(`${url}/endpoint/florrio/findEach/`);
  94. let body = await response.json();
  95.  
  96. if (body.hasOwnProperty("servers")) {
  97. Object.entries(body.servers).forEach(([key, val]) => {
  98. if (!Config.script.ids.includes(val.id)) {
  99. Config.script.ids.push(val.id);
  100.  
  101. let name = key.replace(/(linode-|vultr-)/, "");
  102. let serverOption = document.createElement('option');
  103.  
  104. serverOption.setAttribute('data-value', JSON.stringify(val));
  105. serverOption.innerText = capitalizeFirstLetter(name);
  106. if (val.id === Config.script.currentId) {
  107. serverOption.setAttribute('selected', 'selected');
  108. }
  109.  
  110. serverSelect.appendChild(serverOption);
  111. }
  112. });
  113. }
  114. } catch (err) {
  115. console.error(err);
  116. }
  117. }
  118.  
  119. const findServerPreferenceProxy = new Proxy(unsafeWindow.m28n.findServerPreference, {
  120. apply(_target, _thisArgs, args) {
  121. if (Config.script.m28nOverride) {
  122. args[1](null, [JSON.parse(serverSelect.options[serverSelect.selectedIndex].dataset.value)]);
  123. return;
  124. }
  125. return Reflect.apply(...arguments);
  126. }
  127. })
  128.  
  129. unsafeWindow.m28n.findServerPreference = findServerPreferenceProxy;
  130.  
  131. const WebSocketProxy = new Proxy(unsafeWindow.WebSocket, {
  132. construct (Target, args) {
  133. const instance = Reflect.construct(...arguments);
  134.  
  135. const messageHandler = (e) => {
  136. let buffer = new DataView(e.data);
  137. if(buffer.getUint8(0) === 1) {
  138. instance.removeEventListener("message", messageHandler);
  139. Config.script.socket = instance;
  140. Config.script.currentId = instance.url.match(/wss:\/\/(\w{4})\./)[1];
  141. fetchServers();
  142. }
  143. }
  144.  
  145. instance.addEventListener("message", messageHandler);
  146.  
  147. return instance;
  148. }
  149. });
  150.  
  151. unsafeWindow.WebSocket = WebSocketProxy;
  152.  
  153. const connectServer = () => {
  154. Config.script.m28nOverride = true;
  155. Config.script.socket.close();
  156. }
  157. })()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址