hwmTime

Время с секундами

  1. // ==UserScript==
  2. // @name hwmTime
  3. // @author Tamozhnya1
  4. // @namespace Tamozhnya1
  5. // @description Время с секундами
  6. // @version 5.7
  7. // @include *heroeswm.ru/*
  8. // @include *lordswm.com/*
  9. // @exclude */rightcol.php*
  10. // @exclude */ch_box.php*
  11. // @exclude */chat*
  12. // @exclude */ticker.html*
  13. // @exclude */frames*
  14. // @exclude */brd.php*
  15. // @grant GM_deleteValue
  16. // @grant GM_getValue
  17. // @grant GM_setValue
  18. // @grant GM.xmlHttpRequest
  19. // @license MIT
  20. // ==/UserScript==
  21.  
  22. this.GM_getValue = this.GM_getValue || function(key, def) { return localStorage[key] || def; };
  23. this.GM_setValue = this.GM_setValue || function(key, value) { localStorage[key] = value; };
  24. this.GM_deleteValue = this.GM_deleteValue || function(key) { return delete localStorage[key]; };
  25. let timePanel;
  26. let online = "";
  27. let pageEnterHours;
  28. let pageEnterMinutes;
  29. const isEn = document.documentElement.lang == "en";
  30.  
  31. main();
  32. async function main() {
  33. findTimePanel();
  34. if(!timePanel) {
  35. return;
  36. }
  37. //console.log(`pageEnterHours: ${pageEnterHours}, pageEnterMinutes: ${pageEnterMinutes}, online: ${online}`);
  38. timePanel.addEventListener("click", showSettings);
  39. timePanel.title = isEn ? "Settings" : "Настройки";
  40.  
  41. if(!GM_getValue("ClientServerTimeDifference")) {
  42. GM_deleteValue("LastClientServerTimeDifferenceRequestDate");
  43. }
  44. const gameDate = getGameDate();
  45. if(gameDate.getUTCHours() != pageEnterHours || gameDate.getUTCMinutes() != pageEnterMinutes) { // На клиенте часы идут не правильно (не путать с разницей в часовых поясах)
  46. await requestServerTime(true);
  47. }
  48. bindTimePanel();
  49. requestServerTime();
  50. }
  51. function findTimePanel() {
  52. const isNewInterface = document.querySelector("div.mm_item") ? true : false;
  53. if(isNewInterface) {
  54. timePanel = Array.from(document.querySelectorAll("div.sh_extra.sh_extra_right > div")).find(x => /\d+:\d+(:\d{2})?/.test(x.innerHTML));
  55. if(timePanel) {
  56. const timeTemplate = /(\d+):(\d+)(:\d{2})?/;
  57. const timeText = timeTemplate.exec(timePanel.innerText);
  58. //console.log([timePanel.innerHTML, timePanel.innerText, timeText]);
  59. if(timeText) {
  60. pageEnterHours = parseInt(timeText[1]);
  61. pageEnterMinutes = parseInt(timeText[2]);
  62. } else {
  63. console.log(timePanel.innerText);
  64. }
  65. return;
  66. }
  67. } else {
  68. timePanel = document.querySelector("td > a[href='https://gvdplayer.com/player.html']")?.closest("td");
  69. if(timePanel) {
  70. online = (/([^,]*)(, \d+ online.*)/.exec(timePanel.innerHTML))[2];
  71. const timeText = /(\d+):(\d+)(:\d{2})?, \d+ online/.exec(timePanel.firstChild.textContent);
  72. if(timeText) {
  73. pageEnterHours = parseInt(timeText[1]);
  74. pageEnterMinutes = parseInt(timeText[2]);
  75. } else {
  76. console.log(timePanel.firstChild.textContent);
  77. }
  78. return;
  79. }
  80. }
  81. const timeAndOnlineTemplate = /(\d+):(\d+)(:\d{2})?, \d+ online/;
  82. for(const currentCell of document.querySelectorAll('span')) {
  83. if(currentCell.innerHTML.indexOf("<span") != -1 || currentCell.innerHTML.search(timeAndOnlineTemplate) == -1) {
  84. continue;
  85. }
  86. timePanel = currentCell;
  87. online = (/([^,]*)(, \d+ online.*)/.exec(timePanel.innerHTML))[2];
  88. const timeText = timeAndOnlineTemplate.exec(timePanel.innerHTML);
  89. if(timeText) {
  90. pageEnterHours = parseInt(timeText[1]);
  91. pageEnterMinutes = parseInt(timeText[2]);
  92. } else {
  93. console.log(timePanel.innerHTML);
  94. }
  95. }
  96. }
  97. async function requestServerTime(force = false) {
  98. let requestInterval = 12 * 60 * 60 * 1000;
  99. const newRequestTime = parseInt(GM_getValue("LastClientServerTimeDifferenceRequestDate", 0)) + requestInterval; // Сделал раз в 12 часов. Все равно это только проверка спешки часов клиента
  100. if(force || newRequestTime < Date.now()) {
  101. await getRequestText("/time.php").then(function(responseText) {
  102. //throw "1";
  103. //console.log(responseText);
  104. const responseParcing = /now (\d+)/.exec(responseText); //responseText: now 1681711364 17-04-23 09:02
  105. if(responseParcing) {
  106. GM_setValue("ClientServerTimeDifference", Date.now() - (parseInt(responseParcing[1]) * 1000 + parseInt(GM_getValue("hwmTimeAddingIntervalMilliseconds", 0)))); // На сколько спешат часы клиента
  107. } else {
  108. throw (isEn ? `Parsing error: ${responseText}` : `Ошибка разбора: ${responseText}`);
  109. }
  110. }).catch(function(error) {
  111. console.log(error);
  112. const nowDate = new Date();
  113. const gameDate = new Date(Date.UTC(nowDate.getFullYear(), nowDate.getMonth(), nowDate.getDate(), pageEnterHours, pageEnterMinutes, 0)); // Конструируем приближенное время, зная часы и минуты. Дата здесь безразлична, а время московское
  114. const serverDate = gameDate;
  115. serverDate.setUTCHours(serverDate.getUTCHours() - 3); // Делаем из московского - универсальное.
  116. //console.log(serverDate)
  117. GM_setValue("ClientServerTimeDifference", nowDate.getTime() - serverDate.getTime());
  118. requestInterval = 3600000; // В случае ошибок запрашиваем каждый час
  119. }).finally(function() {
  120. //console.log(`requestInterval: ${requestInterval}`);
  121. GM_setValue("LastClientServerTimeDifferenceRequestDate", Date.now());
  122. setTimeout(requestServerTime, requestInterval);
  123. });
  124. } else {
  125. setTimeout(requestServerTime, newRequestTime - Date.now());
  126. }
  127. }
  128. function bindTimePanel() {
  129. const gameDate = getGameDate();
  130. timePanel.innerHTML = `${gameDate.getUTCHours()}:${gameDate.getUTCMinutes().toString().padStart(2, "0")}:${gameDate.getUTCSeconds().toString().padStart(2, "0")}${online}`;
  131. setTimeout(bindTimePanel, 1000);
  132. }
  133. function getGameDate() { return new Date(getServerTime() + 10800000); } // Игра в интерфейсе всегда показывает московское время
  134. function getServerTime() { return Date.now() - parseInt(GM_getValue("ClientServerTimeDifference", 0)); } // Компенсация спешки часов клиента. К разнице часовых поясов это не имеет отношения.
  135. function showSettings() {
  136. if(showPupupPanel(GM_info.script.name)) {
  137. return;
  138. }
  139. const fieldsMap = [];
  140. fieldsMap.push(["ClientServerLastRequestedTimeDifferenceMilliseconds:", `${parseInt(GM_getValue("ClientServerTimeDifference", 0))}`]);
  141. fieldsMap.push(["LastClientServerSyncDate:", `${new Date(parseInt(GM_getValue("LastClientServerTimeDifferenceRequestDate", 0))).toLocaleString()}`]);
  142.  
  143. const hwmTimeAddingIntervalInput = createElement("input", { id: "hwmTimeAddingIntervalInput", type: "number", value: `${ GM_getValue("hwmTimeAddingIntervalMilliseconds", 0) }`, onfocus: `this.select();` });
  144. hwmTimeAddingIntervalInput.addEventListener("change", function() { GM_setValue("hwmTimeAddingIntervalMilliseconds", parseInt(this.value)); });
  145.  
  146. fieldsMap.push([`${isEn ? 'Add time to page loading (ms.)' : "Добавлять время на загрузку страницы (мс.)"}`, hwmTimeAddingIntervalInput]);
  147.  
  148. const hwmTimeRestartScriptSubmit = createElement("input", { id: "hwmTimeRestartScriptSubmit", type: "submit", value: `${isEn ? 'Restart the script' : "Перезапустить скрипт"}` });
  149. hwmTimeRestartScriptSubmit.addEventListener("click", reset);
  150. fieldsMap.push([hwmTimeRestartScriptSubmit]);
  151.  
  152. createPupupPanel(GM_info.script.name, "Настройки " + GM_info.script.name, fieldsMap);
  153. }
  154. function reset() {
  155. GM_deleteValue("ClientServerTimeDifference");
  156. GM_deleteValue("LastClientServerTimeDifferenceRequestDate");
  157. hidePupupPanel(GM_info.script.name);
  158. location.reload(); //main(); // На панели замечены надписи NaN, а для начальной работы скрипта нужны нативные значения
  159. }
  160. // API
  161. function createPupupPanel(panelName, panelTitle, fieldsMap) {
  162. let backgroundPopupPanel = addElement("div", document.body, { id: panelName + "1", style: "position: absolute; left: 0pt; width: 100%; background: none repeat scroll 0% 0% gray; opacity: 0.5; top: 0px; height: 100%; display: block; z-index: 200;" });
  163. backgroundPopupPanel.addEventListener("click", function() { hidePupupPanel(panelName); }, false);
  164.  
  165. let popupPanel = addElement("div", document.body, { id: panelName + "2", style: `position: absolute; width: 650px; background: none repeat scroll 0% 0%; background-image: linear-gradient(to right, #eea2a2 0%, #bbc1bf 19%, #57c6e1 42%, #b49fda 79%, #7ac5d8 100%); left: ${((document.body.offsetWidth - 650) / 2)}px; top: 150px; display: block; z-index: 200; border: 4mm ridge rgba(211, 220, 50, .6);` });
  166. let contentDiv = addElement("div", popupPanel, { id: panelName + "3", style: "border: 1px solid #abc; padding: 5px; margin: 2px; display: flex; flex-wrap: wrap;" });
  167.  
  168. if(panelTitle) {
  169. addElement("b", contentDiv, { innerText: panelTitle, style: "text-align: center; margin: auto; width: 90%; display: block;" });
  170. }
  171. let divClose = addElement("div", contentDiv, {id: panelName + "close", title: "Close", innerText: "x", style: "border: 1px solid #abc; width: 15px; height: 15px; text-align: center; cursor: pointer;" });
  172. divClose.addEventListener("click", function() { hidePupupPanel(panelName); }, false);
  173.  
  174. addElement("div", contentDiv, { style: "flex-basis: 100%; height: 0;"});
  175.  
  176. if(fieldsMap) {
  177. let contentTable = addElement("table", contentDiv);
  178. for(let rowData of fieldsMap) {
  179. if(rowData.length == 0) { // Спомощью передачи пустой стороки-массива, указываем, что надо начать новую таблицу после брейка
  180. addElement("div", contentDiv, { style: "flex-basis: 100%; height: 0;"});
  181. contentTable = addElement("table", contentDiv);
  182. continue;
  183. }
  184. const row = addElement("tr", contentTable);
  185. for(let cellData of rowData) {
  186. const cell = addElement("td", row);
  187. if(cellData) {
  188. if(typeof(cellData) == "string") {
  189. cell.innerText = cellData;
  190. } else {
  191. cell.appendChild(cellData);
  192. }
  193. }
  194. }
  195. }
  196. }
  197. return contentDiv;
  198. }
  199. function showPupupPanel(panelName) {
  200. let backgroundPopupPanel = document.getElementById(panelName + "1");
  201. let popupPanel = document.getElementById(panelName + "2");
  202. if(backgroundPopupPanel) {
  203. backgroundPopupPanel.style.display = popupPanel.style.display = 'block';
  204. return true;
  205. }
  206. return false;
  207. }
  208. function hidePupupPanel(panelName) {
  209. let backgroundPopupPanel = document.getElementById(panelName + "1");
  210. let popupPanel = document.getElementById(panelName + "2");
  211. backgroundPopupPanel.style.display = popupPanel.style.display = 'none';
  212. }
  213. function addElement(type, parent, data) {
  214. let el = createElement(type, data);
  215. if(parent) {
  216. parent.appendChild(el);
  217. }
  218. return el;
  219. }
  220. function createElement(type, data) {
  221. let el = document.createElement(type);
  222. if(data) {
  223. for(let key in data) {
  224. if(key == "innerText" || key == "innerHTML") {
  225. el[key] = data[key];
  226. } else {
  227. el.setAttribute(key, data[key]);
  228. }
  229. }
  230. }
  231. return el;
  232. }
  233. function getRequestText(url, overrideMimeType = "text/html; charset=windows-1251") {
  234. return new Promise((resolve, reject) => {
  235. GM.xmlHttpRequest({ method: "GET", url: url, overrideMimeType: overrideMimeType,
  236. onload: function(response) { resolve(response.responseText); },
  237. onerror: function(error) { reject(error); }
  238. });
  239. });
  240. }
  241.