hwmWidget

Виджет для главной страницы ГВД

目前为 2023-11-27 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name hwmWidget
  3. // @include /^https{0,1}:\/\/((www|qrator)\.heroeswm\.ru|178\.248\.235\.15)\/home\.php/
  4. // @description Виджет для главной страницы ГВД
  5. // @version 4.6
  6. // @author Tamozhnya1
  7. // @namespace Tamozhnya1
  8. // @grant GM.xmlHttpRequest
  9. // @grant unsafeWindow
  10. // @grant GM_log
  11. // @grant GM_setValue
  12. // @grant GM_getValue
  13. // @grant GM_addStyle
  14. // @license MIT
  15. // ==/UserScript==
  16. if(typeof GM_getValue != 'function') {
  17. this.GM_getValue = function (key,def) { return localStorage[key] || def; };
  18. this.GM_setValue = function (key,value) { return localStorage[key] = value; };
  19. this.GM_deleteValue = function (key) { return delete localStorage[key]; };
  20. }
  21. const LotType = { Purchase: 1, Auction: 2 };
  22. var ElementsTypes = { "42": "abrasive", "43": "snake_poison", "46": "tiger_tusk", "44": "ice_crystal", "45": "moon_stone", "40": "fire_crystal", "37": "meteorit", "41": "witch_flower", "39": "wind_flower", "78": "fern_flower", "38": "badgrib" }
  23. var ElementNames = ["abrasive", "snake_poison", "tiger_tusk", "ice_crystal", "moon_stone", "fire_crystal", "meteorit", "witch_flower", "wind_flower", "fern_flower", "badgrib"];
  24. var ResourcesTypes = { "wood": { Type: "1", ImageName: "wood" }, "ore": { Type: "2", ImageName: "ore" }, "mercury": { Type: "3", ImageName: "mercury" }, "sulphur": { Type: "4", ImageName: "sulfur" }, "crystal": { Type: "5", ImageName: "crystals" }, "gem": { Type: "6", ImageName: "gems" } };
  25. var forumNames = { "2": "ОиФ", "10": "ВиП", "24": "Турниры", "3": "ИиП", "12": "БиП", "11": "ФВТ", "27": "Встречи", "14": "Обычные артефакты", "21": "Аренда", "22": "УКиО", "23": "ПЭСиП", "25": "ПЗ(Бои)", "13": "ПЗ(Финансы)", "7": "ТП", "8": "ОиС" };
  26. const locations = {
  27. 1: [50,50,"Empire Capital","EmC","Столица Империи"],
  28. 2: [51,50,"East River","EsR","Восточная Река"],
  29. 3: [50,49,"Tiger Lake","TgL","Тигриное Озеро"],
  30. 4: [51,49,"Rogues' Wood","RgW","Лес Разбойников"],
  31. 5: [50,51,"Wolf Dale","WoD","Долина Волков"],
  32. 6: [50,48,"Peaceful Camp","PcC","Мирный Лагерь"],
  33. 7: [49,51,"Lizard Lowland","LzL","Равнина Ящеров"],
  34. 8: [49,50,"Green Wood","GrW","Зеленый Лес"],
  35. 9: [49,48,"Eagle Nest","EgN","Орлиное Гнездо"],
  36. 10: [50,52,"Portal Ruins","PoR","Руины Портала"],
  37. 11: [51,51,"Dragons' Caves","DrC","Пещеры Драконов"],
  38. 12: [49,49,"Shining Spring","ShS","Сияющий Родник"],
  39. 13: [48,49,"Sunny City","SnC","Солнечный Город"],
  40. 14: [52,50,"Magma Mines","MgM","Магма Шахты"],
  41. 15: [52,49,"Bear Mountain","BrM","Медвежья Гора"],
  42. 16: [52,48,"Fairy Trees","FrT","Магический Лес"],
  43. 17: [53,50,"Harbour City","HrC","Портовый Город"],
  44. 18: [53,49,"Mythril Coast","MfC","Мифриловый Берег"],
  45. 19: [51,52,"Great Wall","GtW","Великая Стена"],
  46. 20: [51,53,"Titans' Valley","TiV","Равнина Титанов"],
  47. 21: [52,53,"Fishing Village","FsV","Рыбачье село"],
  48. 22: [52,54,"Kingdom Castle","KiC","Замок Королевства"],
  49. 23: [48,48,"Ungovernable Steppe","UnS","Непокорная Степь"],
  50. 24: [51,48,"Crystal Garden","CrG","Кристальный Сад"],
  51. 25: [53,52,"East Island","EsI","Восточный Остров"],
  52. 26: [49,52,"The Wilderness","ThW","Дикие земли"],
  53. 27: [48,50,"Sublime Arbor","SbA","Великое Древо"]
  54. };
  55. const isEn = document.documentElement.lang == "en";
  56. const Strings = { "ru": { BuyNow: "Купить сразу!" }, "en": { BuyNow: "Buy now!" } };
  57. const LocalizedString = Strings[document.documentElement.lang];
  58. GM_addStyle(`
  59. .hover-link:hover{ color: red }
  60. .news-head {
  61. text-decoration:none;
  62. align-self: center;
  63. border-radius: 1.5rem;
  64. padding: 0.25rem .75rem;
  65. }
  66.  
  67. .active {
  68. background: #eae8dd;
  69. }
  70.  
  71. .active:hover {
  72. background: #eae8dd80;
  73. }
  74.  
  75. .news-head__title {
  76. display: inline;
  77. font-size: 12px;
  78. font-weight: normal;
  79. cursor: pointer;
  80. }
  81.  
  82. .news-head__switch {
  83. cursor: pointer;
  84. align-self: center;
  85. color: #5D413A40;
  86. margin-left:10px;
  87. }
  88.  
  89. .news-head__settings {
  90. cursor: pointer;
  91. align-self: center;
  92. color: #5D413A40;
  93. margin-left:10px;
  94. width: 1.5%;
  95. }
  96.  
  97. .mrgn-l{
  98. margin-left: 5px;
  99. }
  100.  
  101. .flex {
  102. display: flex;
  103. }
  104.  
  105. .div-style {
  106. margin: 0 auto 10px;
  107. padding: 15px 25px 20px;
  108. overflow: hidden;
  109. min-width: 400px;
  110. border-radius: 5px;
  111. border: 0 #adadad solid;
  112. background: url(../i/inv_im/corner_lt2.png) no-repeat top left, url(../i/inv_im/corner_rt2.png) no-repeat top right, url(../i/inv_im/corner_lb2.png) no-repeat bottom left, url(../i/inv_im/corner_rb2.png) no-repeat bottom right #f5f3ea;
  113. background-size: 14px;
  114. box-shadow: inset 0 0 0 1px #b19673, 0 2px 5px rgb(0 0 0 / 25%);
  115. }
  116.  
  117. .res-style {
  118. display: none;
  119. justify-content: space-around;
  120. height: 40px;
  121. background-color: #eae8dd;
  122. border-radius: 5px;
  123. border: 0 #adadad solid;
  124. margin-top: 10px;
  125. }
  126.  
  127. .modal {
  128. width: 100%;
  129. height: 100%;
  130. z-index: 1000;
  131. position: fixed;
  132. top:0;
  133. left:0;
  134. background: #00000050;
  135. display: none;
  136. align-items: center;
  137. }
  138.  
  139. .modal-block {
  140. display: flex;
  141. width: 800px;
  142. height: 500px;
  143. z-index: 120;
  144. margin: 0 auto;
  145. position: relative;
  146. border-radius: 5px;
  147. border: 0 #adadad solid;
  148. background: url(../i/inv_im/corner_lt2.png) no-repeat top left, url(../i/inv_im/corner_rt2.png) no-repeat top right, url(../i/inv_im/corner_lb2.png) no-repeat bottom left, url(../i/inv_im/corner_rb2.png) no-repeat bottom right #f5f3ea;
  149. background-size: 14px;
  150. box-shadow: inset 0 0 0 1px #b19673, 0 2px 5px rgb(0 0 0 / 25%);
  151. padding: 15px;
  152. align-items: flex-start;
  153. flex-direction: column;
  154. overflow: scroll;
  155. }
  156.  
  157. .modal-block__head {
  158. display: flex;
  159. align-items: center;
  160. }
  161.  
  162. .modal-block__text {
  163. margin-top: 20px;
  164. color: #6e6e6e;
  165. }
  166.  
  167. .modal-block__btn {
  168. padding: 5px 15px;
  169. white-space: nowrap;
  170. position: relative;
  171. text-align: center;
  172. color: #592C08;
  173. background: url(../i/shop_images/art_btn_bg_gold.png) #DAB761;
  174. background-size: 100% 100%;
  175. -webkit-border-radius: 5px;
  176. -moz-border-radius: 5px;
  177. border: 0 solid;
  178. border-radius: 5px;
  179. box-shadow: inset 0 0 0 1px #fce6b0, inset 0 0 0 2px #a78750, 0 0 0 1px rgb(0 0 0 / 13%);
  180. cursor: pointer;
  181. margin-left: 150px;
  182. }
  183.  
  184. .modal-block__setting {
  185. display: flex;
  186. flex-wrap: wrap;
  187. }
  188.  
  189. .modal-block__checkbox {
  190. display: flex;
  191. align-items: center;
  192. margin: 0 8px 8px 5px;
  193. padding: 0;
  194. }
  195.  
  196. .clan-block {
  197. flex-direction: column;
  198. }
  199.  
  200. .clan-style {
  201. display: inline-flex;
  202. background-color: #adadad40;
  203. padding: 3px 7px;
  204. margin-left: 7px;
  205. border: 0;
  206. border-radius: 4px;
  207. color: #592C08;
  208. }
  209.  
  210. .res-style__elem {
  211. align-self: center;
  212. display: flex;
  213. }
  214.  
  215. .text-title {
  216. text-align:left;
  217. padding-top: 6px;
  218. }
  219.  
  220. @media screen and (min-width: 320px) and (max-width: 600px) {
  221. .div-style {
  222. width: auto;
  223. }
  224. .news-head__title {
  225. font-size: 10px;
  226. }
  227. .res-style {
  228. flex-wrap: wrap;
  229. height: auto;
  230. padding: 5px;
  231. }
  232. .res-style__elem {
  233. margin: 0 10px 10px 0;
  234. font-size: 12px;
  235. }
  236. .text-title {
  237. font-size: 12px;
  238. }
  239. .news-head__settings {
  240. width: 10%;
  241. }
  242. .modal-block {
  243. width: 500px;
  244. height: 800px;
  245. }
  246. }`);
  247. const isNewPersonPage = document.querySelector("div#hwm_no_zoom") ? true : false;
  248. const isMobileInterface = document.querySelector("div#btnMenuGlobal") ? true : false;
  249.  
  250. main();
  251. async function main() {
  252. //const widgetContainer = isNewPersonPage ? document.querySelector("center > div") : document.querySelector("body > center > table:nth-child(2)");
  253. let widgetContainer = isNewPersonPage ? document.querySelector("body > center") : document.querySelector("body > center > table:nth-child(2) > tbody > tr > td > table > tbody > tr:nth-child(6) > td:nth-child(1)");
  254. if(isMobileInterface) {
  255. widgetContainer = isNewPersonPage ? document.querySelector("div#set_mobile_max_width") : document.querySelector("div#android_container > table > tbody > tr:nth-child(6) > td:nth-child(1)");
  256. if(isNewPersonPage) {
  257. widgetContainer.style.flexWrap = "wrap";
  258. addElement("div", widgetContainer, { style: "flex-basis: 100%; height: 0;"});
  259. }
  260. }
  261. if(widgetContainer) {
  262. let divOuterInnerHTML = `
  263. <div class="flex">
  264. <div id="widget" class="flex">
  265. <div id="prevDaily" class="news-head active">
  266. <span>📰</span>
  267. <h2 id="prevDaily_t" class="news-head__title" title="Новости HWM Daily">Новости HWM Daily</h2>
  268. </div>
  269. <h2 id="prevForum" class="news-head news-head__title mrgn-l" title="Последние темы форума">Последние темы форума</h2>
  270. <h2 id="prevClan" class="news-head news-head__title mrgn-l" title="Клановая рассылка">Клановая рассылка</h2>
  271. </div>
  272. <span id="switcher" class="news-head__switch"></span>
  273. <span id="hwm_settings" class="news-head__settings" title="Настройки"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="cog" class="svg-inline--fa fa-cog fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"></path></svg></span>
  274. </div>
  275. <div class="modal" id="modal">
  276. <div class="modal-block">
  277. <div class="modal-block__head">
  278. <h3 class="modal-block__title">Настройки виджета</h3>
  279. <button id="modal-close" class="modal-block__btn">Закрыть</button>
  280. </div>
  281. <form>
  282. <p class="modal-block__text">Выбор форума</p>
  283. <div class="modal-block__setting">`;
  284. for(const forumId of Object.keys(forumNames)) {
  285. divOuterInnerHTML += `<div class="modal-block__checkbox"><input type="radio" id="${forumId}" name="forum" ${GM_getValue("ForumId", "2") == forumId ? "checked" : ""}><label for="${forumId}">${forumNames[forumId]}</label></div>`;
  286. }
  287. divOuterInnerHTML += `</div>
  288. </form>
  289. <form>
  290. <p class="modal-block__text">Выбор клана</p>
  291. <div class="modal-block__setting clans-block">
  292. </div>
  293. </form>
  294. <label for="ShowItemsAmountInput">Количество отображаемых сообщений</label><input type="number" id="ShowItemsAmountInput" value="${GM_getValue("ShowItemsAmount", 5)}" onfocus="this.select();"/>
  295. </div>
  296. </div>`;
  297. const divOuter = addElement('div', widgetContainer, { class: "div-style", innerHTML: divOuterInnerHTML }, GM_getValue("top", false));
  298. fillClansList();
  299. addElement('div', divOuter, { id: "NewsPanel" });
  300. addElement('div', divOuter, { id: "ResourcesPanel", class: "res-style" });
  301. if(isNewPersonPage) {
  302. const anchorRect = document.querySelector("div#set_mobile_max_width").getBoundingClientRect();
  303. //console.log(anchorRect.width)
  304. divOuter.style.width = `${anchorRect.width}px`;
  305. }
  306. const switcher = document.getElementById('switcher');
  307. switcher.addEventListener("click", function(event) { GM_setValue("Shown", 1 - Number(GM_getValue("Shown", 1))); show(); }, false);
  308. const hwmSettings = document.getElementById('hwm_settings');
  309. hwmSettings.addEventListener("click", function(event) {
  310. document.querySelector("#modal").style.display = 'flex';
  311. document.querySelector("html").style.overflowY = 'hidden';
  312. }, false);
  313. const prevDaily = document.getElementById('prevDaily');
  314. prevDaily.addEventListener("click", function(event) { GM_setValue("SecondClick", parseInt(GM_getValue("SelectedTab", 0)) == 0); GM_setValue("SelectedTab", 0); show(); }, false);
  315. const prevForum = document.getElementById('prevForum');
  316. prevForum.addEventListener("click", function(event) { GM_setValue("SecondClick", parseInt(GM_getValue("SelectedTab", 0)) == 1); GM_setValue("SelectedTab", 1); show(); }, false);
  317. const prevClan = document.getElementById('prevClan');
  318. prevClan.addEventListener("click", function(event) { GM_setValue("SecondClick", parseInt(GM_getValue("SelectedTab", 0)) == 2); GM_setValue("SelectedTab", 2); show(); }, false);
  319.  
  320. const closeSettings = document.getElementById('modal-close');
  321. closeSettings.addEventListener("click", function(event) {
  322. document.querySelector("#modal").style.display = 'none';
  323. document.querySelector("html").style.overflowY = 'overlay';
  324. GM_setValue("ForumId", document.querySelector(`input[name='forum']:checked`).id);
  325. const selectedClan = document.querySelector(".clans-block input[name='clan']:checked");
  326. if(selectedClan) {
  327. GM_setValue("ClanId", selectedClan.id);
  328. } else {
  329. GM_deleteValue("ClanId");
  330. }
  331. GM_setValue("ShowItemsAmount", document.getElementById("ShowItemsAmountInput").value);
  332. show();
  333. }, false);
  334. GM_setValue("SecondClick", false);
  335. show();
  336. }
  337. }
  338. async function fillClansList() {
  339. const doc = await getRequest(`https://www.heroeswm.ru/pl_clans.php`);
  340. const clanInfos = Array.from(doc.querySelectorAll("td > li > a[href^='clan_info.php']")).map(x => { return { Id: getUrlParamValue(x.href, "id"), Name: x.firstChild.innerText }; });
  341. if(clanInfos.length > 0) {
  342. if(!GM_getValue("ClanId") || !clanInfos.find(x => x.Id == GM_getValue("ClanId"))) {
  343. GM_setValue("ClanId", clanInfos[0].Id);
  344. }
  345. let clans = '';
  346. for(const clanInfo of clanInfos) {
  347. clans += `
  348. <div class="modal-block__checkbox">
  349. <input type="radio" id="${clanInfo.Id}" name="clan" ${clanInfo.Id == GM_getValue("ClanId") ? "checked" : ""}>
  350. <label for="${clanInfo.Id}">#${clanInfo.Id} ${clanInfo.Name}</label>
  351. </div>`;
  352. }
  353. document.querySelector(".clans-block").innerHTML = clans;
  354. } else {
  355. GM_deleteValue("ClanId");
  356. document.querySelector(".clans-block").innerHTML = "Вы не состоите в кланах";
  357. }
  358. }
  359. async function show() {
  360. //console.log(`Shown: ${GM_getValue("Shown")}, SelectedTab: ${GM_getValue("SelectedTab")}, SecondClick: ${GM_getValue("SecondClick")}`);
  361. if(gmGetBool("SecondClick")) {
  362. switch(parseInt(GM_getValue("SelectedTab", 0))) {
  363. case 0: window.open("https://daily.heroeswm.ru/", "_blank"); break;
  364. case 1: window.open(`https://www.heroeswm.ru/forum_thread.php?id=${GM_getValue("ForumId", "2")}`, "_blank"); break;
  365. case 2: window.open(`https://www.heroeswm.ru/sms_clans.php?clan_id=${GM_getValue("ClanId")}`, "_blank"); break;
  366. }
  367. return;
  368. }
  369. const shown = GM_getValue("Shown", 1);
  370. const switcher = document.getElementById('switcher');
  371. const newsPanel = document.getElementById("NewsPanel");
  372. const resourcesPanel = document.getElementById("ResourcesPanel");
  373. if(shown == 0) {
  374. switcher.innerHTML = '<img src="https://dcdn3.heroeswm.ru/i/inv_im/btn_expand.svg" style="-webkit-transform: rotate(90deg); transform: rotate(90deg);">';
  375. // <img src="https://dcdn3.heroeswm.ru/i/inv_im/btn_expand.svg" class="home_scroll_content_expand_sign inv_rotate90">
  376. newsPanel.style.display = "none";
  377. resourcesPanel.style.display = "none";
  378. } else {
  379. const prevDaily = document.getElementById('prevDaily');
  380. const prevForum = document.getElementById('prevForum');
  381. const prevClan = document.getElementById('prevClan');
  382. prevDaily.style.background = "#eae8dd00";
  383. prevForum.style.background = "#eae8dd00";
  384. prevClan.style.background = "#eae8dd00";
  385. switch(parseInt(GM_getValue("SelectedTab", 0))) {
  386. case 0: prevDaily.style.background = "#eae8dd"; getDailyNews(); break;
  387. case 1: prevForum.style.background = "#eae8dd"; getForumNews(); break;
  388. case 2: prevClan.style.background = "#eae8dd"; getClanNews(); break;
  389. }
  390. switcher.innerHTML = '<img src="https://dcdn3.heroeswm.ru/i/inv_im/btn_expand.svg" style="-webkit-transform: rotate(270deg); transform: rotate(270deg);">';
  391. newsPanel.style.display = "block";
  392. await getResources();
  393. resourcesPanel.style.display = 'flex'
  394. }
  395. }
  396. function trimming(string, l) {
  397. //console.log(`string: ${string}, l: ${l}`);
  398. var s = string;
  399. if(string.length > l) {
  400. for(var i = l; i >= 0; i--) {
  401. if(string.charAt(i) == ' ') {
  402. s = string.substr(0, i) + '...';
  403. }
  404. }
  405. s = string.substr(0, l) + '...';
  406. }
  407. return s.replace(/&[^#]/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;");
  408. }
  409. async function getElementLots() {
  410. const doc = await getRequest(`https://www.heroeswm.ru/auction.php?cat=elements&sort=0`);
  411. const firstLotRow = doc.querySelector("center > table:nth-child(2) > tbody > tr > td > table > tbody > tr:nth-child(2) > td:nth-child(2) > table > tbody > tr:nth-child(3)");
  412. let arts = [];
  413. let row = firstLotRow;
  414. while(row = row.nextElementSibling) {
  415. const art = parseLotRow(row);
  416. if(art) {
  417. arts.push(art);
  418. }
  419. }
  420. const grouppedArts = groupBy(arts.filter(x => x.LotType == LotType.Purchase), "ArtId");
  421. return grouppedArts;
  422. }
  423. function parseLotRow(row) {
  424. if(!row || row.nodeName != "TR") {
  425. return;
  426. }
  427. const goldImageElement = row.querySelector("img[src*='gold.png']");
  428. if(!goldImageElement) {
  429. return;
  430. }
  431. const lotPrice = parseFloat(goldImageElement.parentNode.nextElementSibling.innerText.replace(/,/g, ""));
  432. let artId;
  433. let lotAmount = 1;
  434. const lotAmountExec = new RegExp(`(\\d+) ${isEn ? "pcs." : "шт."}`).exec(row.innerHTML);
  435. //console.log(lotAmountExec);
  436. if(lotAmountExec) {
  437. lotAmount = parseInt(lotAmountExec[1]);
  438. }
  439. const lotType = row.innerHTML.includes(LocalizedString.BuyNow) ? LotType.Purchase : LotType.Auction;
  440. const artImageRefElement = row.querySelector("a[href*='art_info.php']");
  441. if(!artImageRefElement) {
  442. const elementsList = Object.values(ElementsTypes).join("|");
  443. const elementParse = (new RegExp(`gn_res/(${elementsList}).png`)).exec(row.innerHTML);
  444. if(elementParse) {
  445. artId = elementParse[1];
  446. }
  447. if(row.innerHTML.includes("house_cert")) {
  448. const locationsList = Object.values(locations).map(x => x[2]).join("|");
  449. const sertParse = (new RegExp(`<br>(${locationsList})&nbsp;<b>`)).exec(row.innerHTML);
  450. if(sertParse) {
  451. artId = getSertIdByLocationName(sertParse[1]);
  452. }
  453. }
  454. const resourcesList = Object.values(ResourcesTypes).map(x => x.ImageName).join("|");
  455. const resourceParse = (new RegExp(`/(${resourcesList}).png`)).exec(row.innerHTML);
  456. if(resourceParse) {
  457. artId = "res_" + resourceParse[1];
  458. }
  459. if(row.innerHTML.includes("auc_dom")) {
  460. const locationsList = Object.values(locations).map(x => x[2]).join("|");
  461. const sertParse = (new RegExp(`<br>(${locationsList})&nbsp;<b>`)).exec(row.innerHTML);
  462. if(sertParse) {
  463. artId = getHouseIdByLocationName(sertParse[1]);
  464. }
  465. }
  466. if(row.innerHTML.includes("obj_share_pic")) {
  467. const locationsList = Object.values(locations).map(x => x[2]).join("|");
  468. const sertParse = (new RegExp(`<br>(${locationsList})&nbsp;<b>`)).exec(row.innerHTML);
  469. if(sertParse) {
  470. artId = getShaIdByLocationName(sertParse[1]);
  471. }
  472. }
  473. } else {
  474. artId = getUrlParamValue(artImageRefElement.href, "id");
  475. var artUid = getUrlParamValue(artImageRefElement.href, "uid");;
  476. const strengthData = row.innerText.match(/\d+\/\d+/);
  477. var restLotStrength = parseInt(strengthData[0].split("/")[0]);
  478. var lotStrength = parseInt(strengthData[0].split("/")[1]);
  479. }
  480. const lotRef = row.querySelector("a[href^='auction_lot_protocol.php']");
  481. const lotId = getUrlParamValue(lotRef.href, "id");
  482. const imgR = row.querySelector("td:nth-child(1) > table > tbody > tr > td:nth-child(1) > img");
  483. return { ArtUid: artUid, ArtId: artId, LotStrength: lotStrength, RestLotStrength: restLotStrength, LotPrice: lotPrice, LotType: lotType, LotAmount: lotAmount, LotId: lotId, ImageUrl: imgR.getAttribute('src'), Title: imgR.getAttribute('title') };
  484. }
  485. async function getResources() {
  486. const resourcesPanel = document.getElementById("ResourcesPanel");
  487. resourcesPanel.innerHTML = getWheelImage();
  488. const grouppedArts = await getElementLots();
  489. const elementsData = [];
  490. for(const elementName of ElementNames)
  491. {
  492. const arts = grouppedArts[elementName]
  493. if(arts.length == 0) {
  494. continue;
  495. }
  496. const art = arts[0];
  497. let price = art.LotPrice;
  498. let secondLotPrice = price;
  499. if(arts.length > 1) {
  500. secondLotPrice = arts[1].LotPrice;
  501. }
  502. elementsData.push({ ElementName: elementName,
  503. Price: price,
  504. ImageUrl: art.ImageUrl,
  505. Title: art.Title,
  506. Diffrence: secondLotPrice - price,
  507. AuctionUrl: `https://www.heroeswm.ru/auction.php?cat=elements&sort=0&art_type=${elementName}`,
  508. NewAuctionUrl: `https://www.heroeswm.ru/auction_new_lot.php?${elementName}=${(price - 1)}`
  509. });
  510. }
  511. //console.log(elementsData);
  512. let res = "";
  513. for(const elementData of elementsData)
  514. {
  515. res += `
  516. <div class='res-style__elem'>
  517. <div style='align-self: center;'>
  518. <a class='hover-link' href='${elementData.NewAuctionUrl}' target='_blank'>
  519. <img src='${elementData.ImageUrl}' width='20' heigth='20' border='0'>
  520. </a>
  521. </div>
  522. <a class='hover-link' target='_. blank' style='text-decoration: none; align-self: center; margin-left: 5px; font-size: 9px;' href='${elementData.AuctionUrl}' title='Разница первого и второго лотов: ${elementData.Diffrence}'>${elementData.Price}</a>
  523. <div style='${(elementData.Diffrence >= 150 ? 'display: inline-flex; background-color: #f33800; padding: 5px;margin-left: 5px; border: 0; border-radius: 4px; color: #fff;' : 'display: none;')}'>
  524. <span title='' style='font-size: 8px; font-weight: bold;'>${elementData.Diffrence}</span>
  525. </div>
  526. </div>`;
  527. }
  528. resourcesPanel.innerHTML = res;
  529. }
  530. async function getClanNews() {
  531. if(!GM_getValue("ClanId")) {
  532. document.getElementById("NewsPanel").innerHTML = "Вы не состоите в кланах";
  533. return;
  534. }
  535. const doc = await getRequest(`https://www.heroeswm.ru/sms_clans.php?clan_id=${GM_getValue("ClanId")}`);
  536. const letters = Array.from(doc.querySelectorAll("table.wbwhite a[href^='/sms_clans.php'][href*='read']")).map(x => { return {
  537. Title: x.innerHTML,
  538. Ref: x.href,
  539. DateText: x.parentNode.previousElementSibling.innerHTML,
  540. IsHot: (Date.now() - parseDate(x.parentNode.previousElementSibling.innerHTML, false, true).getTime()) / (1000 * 60 * 60) <= 12,
  541. }; }).slice(0, parseInt(GM_getValue("ShowItemsAmount", 5)));
  542. //console.log(letters);
  543. //console.log(`letters: ${letters.length}`);
  544. let clanLetters = "";
  545. for(const letter of letters) {
  546. clanLetters += `
  547. <div class='text-title'>
  548. <a class='hover-link' style='text-decoration:none; ${letter.IsHot ? "font-weight: bold; color: red;" : ""}' target='_blank' href='${letter.Ref}' title='${letter.Title}'>${letter.IsHot ? "📣 " : "• "}${letter.Title}</a>
  549. <div class='clan-style'>
  550. <span title='Дата' style='font-size:9px'>${letter.DateText}</span>
  551. </div>
  552. </div>
  553. `;
  554. }
  555. document.getElementById("NewsPanel").innerHTML = clanLetters;
  556. }
  557. async function getForumNews() {
  558. const forumId = GM_getValue("ForumId", "2");
  559. const doc = await getRequest(`https://www.heroeswm.ru/forum_thread.php?id=${forumId}`);
  560. const messages = Array.from(doc.querySelectorAll("tr > td:nth-child(1) > a[href^='forum_messages.php']")).map(x => { return {
  561. Fixed: getParent(x, "tr").querySelector("img[src*='skrepka.gif']") ? true : false,
  562. Title: x.innerHTML,
  563. Reference: x.href,
  564. LastCommentReference: getParent(x, "tr").querySelector("a[href^='forum_messages.php'][href*='page=last']"),
  565. CommentsAmount: parseInt(getParent(x, "tr").cells[2].innerHTML),
  566. IsHot: parseInt(getParent(x, "tr").cells[2].innerHTML) <= 20
  567. };}).filter(x => !x.Fixed).slice(0, parseInt(GM_getValue("ShowItemsAmount", 5)));
  568. let forumNews = "";
  569. for(const message of messages) {
  570. forumNews += `
  571. <div class='text-title'>
  572. <a class='hover-link' style='text-decoration: none;${(message.IsHot ? ' font-weight: bold; color: #ff4d00' : '')}' target='_blank' href='${message.Reference}' title='${message.Title}'>${(message.IsHot ? "🔥" : "•")} ${message.Title}
  573. </a>
  574. <div style='display: inline-flex; background-color: #adadad40;padding: 3px 7px; margin-left: 7px; border: 0; border-radius: 4px; color: #592C08;'>
  575. <a href="${message.LastCommentReference}" title='Количество комментариев. Перейти к последнему.' target='_blank' style='font-size: 9px'>${message.CommentsAmount}</a>
  576. </div>
  577. </div>`;
  578. }
  579. document.getElementById("NewsPanel").innerHTML = forumNews;
  580. }
  581. function getDailyNews() {
  582. const newsPanel = document.getElementById("NewsPanel");
  583. newsPanel.innerHTML = `${getWheelImage()}&nbsp;&nbsp;Загрузка списка новостей...`;
  584. GM.xmlHttpRequest({method: "GET", url: "https://daily.heroeswm.ru/news4script.txt?" + Date.now(), headers: {
  585. 'User-agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1)',
  586. 'Accept': 'text/xml,text/html',
  587. 'Content-Type': 'text/plain; charset=windows-1251'
  588. },
  589. synchronous: false,
  590. overrideMimeType: 'text/plain; charset=windows-1251',
  591. onload: function(response) {
  592. try {
  593. const dailyUrlRegExp = /\/\/daily\.heroeswm\.ru\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*$/i;
  594. const previousMessageIds = GM_getValue("PreviousDailyMessageIds", "").split("|");
  595. const messages = JSON.parse('[["' + response.responseText.replace(/"/g, "\\\"").replace(/\n/g, '"],["').replace(/;;/g, '","').replace(/'/g, "&#39;") + '"]]').filter(x => x.length == 6).map(x => { return {
  596. Code: x[0],
  597. ImageUrl: x[1],
  598. Title: trimming(x[2], 255),
  599. Url: x[3],
  600. Id: x[4],
  601. CommentsAmount: x[5],
  602. IsHot: !previousMessageIds.includes(x[4])
  603. };}).filter(x => dailyUrlRegExp.test(x.ImageUrl) && dailyUrlRegExp.test(x.Url) && /^[1-3]$/.test(x.Code));
  604. GM_setValue("PreviousDailyMessageIds", messages.map(x => x.Id).join("|"));
  605. //console.log(messages);
  606. let newsText = "";
  607. for(const message of messages) {
  608. newsText += `
  609. <div class='text-title'>
  610. <a class='hover-link' style='text-decoration: none;${message.IsHot ? 'font-weight: bold; color:red' : ''}' target='_blank' href='${message.Url}'>${message.IsHot ? "⚡" : "•"} ${message.Title}</a>
  611. <div style='display: inline-flex; background-color: #adadad40; padding: 3px 7px; margin-left: 7px; border: 0; border-radius: 4px; color: #592C08;'>
  612. <span title='комментариев' style='font-size:9px'>${message.CommentsAmount}</span>
  613. </div>
  614. </div>`;
  615. }
  616. newsPanel.innerHTML = newsText;
  617. } catch(e) {
  618. newsPanel.innerHTML = `Ошибка при обработке данных: ${e}`;
  619. }
  620. },
  621. onerror: function(response) { newsPanel.innerHTML = "Ошибка при получении данных с Daily"; }
  622. });
  623. }
  624. function GM_addStyle(css) {
  625. var head = document.getElementsByTagName('head')[0];
  626. if (!head)
  627. return;
  628. var style = document.createElement('style');
  629. style.type = 'text/css';
  630. style.innerHTML = css;
  631. head.appendChild(style);
  632. }
  633. function addElement(type, parent, data, insertFirst = false) {
  634. let el = createElement(type, data);
  635. if(parent) {
  636. if(insertFirst) {
  637. parent.insertBefore(el, parent.firstChild);
  638. } else {
  639. parent.appendChild(el);
  640. }
  641. }
  642. return el;
  643. }
  644. function createElement(type, data) {
  645. let el = document.createElement(type);
  646. if(data) {
  647. for(let key in data) {
  648. if(key == "innerText" || key == "innerHTML") {
  649. el[key] = data[key];
  650. } else {
  651. el.setAttribute(key, data[key]);
  652. }
  653. }
  654. }
  655. return el;
  656. }
  657. function getWheelImage() { return '<img border="0" align="absmiddle" height="11" src="https://dcdn.heroeswm.ru/css/loading.gif">'; }
  658. function getUrlParamValue(url, paramName) { return (new URLSearchParams(url.split("?")[1])).get(paramName); }
  659. function groupBy(list, key) { return list.reduce(function(rv, item) { (rv[item[key]] = rv[item[key]] || []).push(item); return rv; }, {}); };
  660. function getSertIdByLocationNumber(locationNumber) { return "sec_" + (locationNumber.toString()).padStart(2, "0"); }
  661. function getSertIdByLocationName(locationName) {
  662. const locationNumber = Object.keys(locations).find(x => locations[x][2] == locationName);
  663. return getSertIdByLocationNumber(locationNumber);
  664. }
  665. function getHouseIdByLocationNumber(locationNumber) { return "dom_" + (locationNumber.toString()).padStart(2, "0"); }
  666. function getHouseIdByLocationName(locationName) {
  667. const locationNumber = Object.keys(locations).find(x => locations[x][2] == locationName);
  668. return getHouseIdByLocationNumber(locationNumber);
  669. }
  670. function getRequest(url) {
  671. return new Promise((resolve, reject) => {
  672. GM.xmlHttpRequest({ method: "GET", url: url, overrideMimeType: "text/html; charset=windows-1251",
  673. onload: function(response) { resolve((new DOMParser).parseFromString(response.responseText, "text/html")); },
  674. onerror: function(error) { reject(error); }
  675. });
  676. });
  677. }
  678. function gmGetBool(valueName, defaultValue = false) {
  679. const value = GM_getValue(valueName);
  680. if(value) {
  681. if(typeof(value) == "string") {
  682. return value == "true";
  683. }
  684. if(typeof(value) == "boolean") {
  685. return value;
  686. }
  687. }
  688. return defaultValue;
  689. }
  690. function parseDate(dateString, isFuture = false, isPast = false) {
  691. //console.log(dateString)
  692. if(!dateString) {
  693. return;
  694. }
  695. const dateStrings = dateString.split(" ");
  696. let hours = 0;
  697. let minutes = 0;
  698. let seconds = 0;
  699. const timePart = dateStrings.find(x => x.includes(":"));
  700. if(timePart) {
  701. var time = timePart.split(":");
  702. hours = parseInt(time[0]);
  703. minutes = parseInt(time[1]);
  704. if(time.length > 2) {
  705. seconds = parseInt(time[2]);
  706. }
  707. }
  708.  
  709. const now = new Date();
  710. let year = now.getFullYear();
  711. let month = now.getMonth();
  712. let day = now.getDate();
  713. const datePart = dateStrings.find(x => x.includes("-"));
  714. if(datePart) {
  715. const date = datePart.split("-");
  716. month = parseInt(date[isEn ? (date.length == 3 ? 1 : 0) : 1]) - 1;
  717. day = parseInt(date[isEn ? (date.length == 3 ? 2 : 1) : 0]);
  718. if(date.length == 3) {
  719. year = isEn ? parseInt(date[0]) : parseInt(date[2]);
  720. if(year < 1000) {
  721. year += Math.floor((new Date()).getFullYear() / 1000) * 1000;
  722. }
  723. } else {
  724. if(isFuture && month == 0 && now.getMonth() == 11) {
  725. year += 1;
  726. }
  727. }
  728. }
  729. if(dateStrings.length > 2) {
  730. const letterDateExec = /(\d{2}):(\d{2}) (\d{2}) (.{3,4})/.exec(dateString);
  731. if(letterDateExec) {
  732. //console.log(letterDateExec)
  733. day = parseInt(letterDateExec[3]);
  734. //const monthNames = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'];
  735. const monthShortNames = ['янв', 'фев', 'март', 'апр', 'май', 'июнь', 'июль', 'авг', 'сент', 'окт', 'ноя', 'дек'];
  736. month = monthShortNames.findIndex(x => x.toLowerCase() == letterDateExec[4].toLowerCase());
  737. if(isPast && (new Date(year, month, day, hours, minutes, seconds)).getTime() > Date.now()) {
  738. year -= 1;
  739. }
  740. }
  741. }
  742. //console.log(`year: ${year}, month: ${month}, day: ${day}, time[0]: ${time[0]}, time[1]: ${time[1]}, ${new Date(year, month, day, parseInt(time[0]), parseInt(time[1]))}`);
  743. return new Date(year, month, day, hours, minutes, seconds);
  744. }
  745. function getParent(element, parentType, number = 1) {
  746. let result = element;
  747. let foundNumber = 0;
  748. while(result = result.parentNode) {
  749. if(result.nodeName.toLowerCase() == parentType.toLowerCase()) {
  750. foundNumber++;
  751. if(foundNumber == number) {
  752. return result;
  753. }
  754. }
  755. }
  756. }

QingJ © 2025

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