🏠 返回首頁 

Greasy Fork is available in English.

HWM_CommitMultipleTransfers

Отправить несколько переводов золота за раз

  1. // ==UserScript==
  2. // @name HWM_CommitMultipleTransfers
  3. // @namespace Небылица
  4. // @version 1.28
  5. // @description Отправить несколько переводов золота за раз
  6. // @author Небылица
  7. // @include /^https{0,1}:\/\/((www|qrator)\.heroeswm\.ru|178\.248\.235\.15)\/transfer\.php.*/
  8. // @grant GM_setValue
  9. // @grant GM_getValue
  10. // ==/UserScript==
  11. (function() {
  12. "use strict";
  13. // Настройки – указание времени ожидания между запросами в мс
  14. var delay = 1000;
  15. //
  16. // Вспомогательные функции
  17. function sendGETRequest(url, mimeType, callback){ // Универсалка для отправки GET-запроса к url с выставлением заданного MIME Type и исполнением функции callback при получении ответа
  18. var xhr = new XMLHttpRequest();
  19. xhr.open("GET", url, true);
  20. if (typeof mimeType === "string"){
  21. xhr.overrideMimeType(mimeType);
  22. }
  23. if (typeof callback === "function"){
  24. xhr.onreadystatechange = function(){
  25. if (xhr.readyState === 4 && xhr.status === 200){
  26. callback.apply(xhr);
  27. }
  28. };
  29. }
  30. xhr.send();
  31. }
  32. function sendPOSTRequest(url, mimeType, params, callback){ // Универсалка для отправки POST-запроса к url с выставлением заданного MIME Type, параметрами params и исполнением функции callback при получении ответа
  33. var xhr = new XMLHttpRequest();
  34. xhr.open("POST", url, true);
  35. if (typeof mimeType === "string"){
  36. xhr.overrideMimeType(mimeType);
  37. }
  38. xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  39. if (typeof callback === "function"){
  40. xhr.onreadystatechange = function(){
  41. if (xhr.readyState === 4 && xhr.status === 200){
  42. callback.apply(xhr);
  43. }
  44. };
  45. }
  46. xhr.send(params);
  47. }
  48. function encodeCP1251(text){ // Перекодирует русский текст так, чтобы при отправке запроса не выходили кракозябры (на базе функции из кода Gradient'a)
  49. var r###lt = "",
  50. CP1251toUTF8 = {
  51. "А": "%C0",
  52. "Б": "%C1",
  53. "В": "%C2",
  54. "Г": "%C3",
  55. "Д": "%C4",
  56. "Е": "%C5",
  57. "Ж": "%C6",
  58. "З": "%C7",
  59. "И": "%C8",
  60. "Й": "%C9",
  61. "К": "%CA",
  62. "Л": "%CB",
  63. "М": "%CC",
  64. "Н": "%CD",
  65. "О": "%CE",
  66. "П": "%CF",
  67. "Р": "%D0",
  68. "С": "%D1",
  69. "Т": "%D2",
  70. "У": "%D3",
  71. "Ф": "%D4",
  72. "Х": "%D5",
  73. "Ц": "%D6",
  74. "Ч": "%D7",
  75. "Ш": "%D8",
  76. "Щ": "%D9",
  77. "Ъ": "%DA",
  78. "Ы": "%DB",
  79. "Ь": "%DC",
  80. "Э": "%DD",
  81. "Ю": "%DE",
  82. "Я": "%DF",
  83. "а": "%E0",
  84. "б": "%E1",
  85. "в": "%E2",
  86. "г": "%E3",
  87. "д": "%E4",
  88. "е": "%E5",
  89. "ж": "%E6",
  90. "з": "%E7",
  91. "и": "%E8",
  92. "й": "%E9",
  93. "к": "%EA",
  94. "л": "%EB",
  95. "м": "%EC",
  96. "н": "%ED",
  97. "о": "%EE",
  98. "п": "%EF",
  99. "р": "%F0",
  100. "с": "%F1",
  101. "т": "%F2",
  102. "у": "%F3",
  103. "ф": "%F4",
  104. "х": "%F5",
  105. "ц": "%F6",
  106. "ч": "%F7",
  107. "ш": "%F8",
  108. "щ": "%F9",
  109. "ъ": "%FA",
  110. "ы": "%FB",
  111. "ь": "%FC",
  112. "э": "%FD",
  113. "ю": "%FE",
  114. "я": "%FF",
  115. "Ё": "%A8",
  116. "ё": "%B8",
  117. " ": "%20",
  118. "!": "%21",
  119. "(": "%28",
  120. ")": "%29",
  121. "*": "%2A",
  122. "+": "%2B",
  123. ",": "%2C",
  124. "-": "%2D",
  125. ".": "%2E",
  126. "/": "%2F"
  127. };
  128. var i,
  129. maxI = text.length;
  130. for (i=0;i<maxI;i++){
  131. if (CP1251toUTF8[text[i]] !== undefined){
  132. r###lt += CP1251toUTF8[text[i]];
  133. } else{
  134. r###lt += text[i];
  135. }
  136. }
  137. return r###lt;
  138. }
  139. function isNaturalNumber(n){ // Проверка натуральности числа
  140. n = n.toString();
  141. var n1 = Math.abs(n),
  142. n2 = parseInt(n);
  143. return !isNaN(n1) && n2 === n1 && n1.toString() === n && n1 !== 0;
  144. }
  145. function queryWrapper(nicknames, amounts, descriptions, i, sign, delay, urlAfter, event){ // Обёртка для цикличного отправщика запросов
  146. i++;
  147. if (i < nicknames.length){
  148. sendPOSTRequest("transfer.php",
  149. "text/html; charset=windows-1251",
  150. "nick=" + encodeCP1251(nicknames[i]) + "&gold=" + amounts[i] + "&wood=0&ore=0&mercury=0&sulphur=0&crystal=0&gem=0&desc=" + encodeCP1251(descriptions[i]) + "&sign=" + sign);
  151. event.target.innerHTML = "Отправляем... " + (i+1).toString() + "/" + nicknames.length.toString();
  152. window.setTimeout(function(){queryWrapper(nicknames, amounts, descriptions, i, sign, delay, urlAfter, event);}, delay);
  153. } else{ // закончили отправку, открываем страницу urlAfter
  154. window.open(urlAfter, "_self");
  155. }
  156. }
  157. function getSign(plId){ // Определение sign персонажа
  158. // отправляем запрос к странице магазина с зельями
  159. sendGETRequest("shop.php?cat=other", "text/html; charset=windows-1251", function(){
  160. // получаем ответ в виде текста и достаём sign из кода страницы
  161. var responseHTMLString = this.responseText,
  162. signExec = /cat=other&sign=(.+?)['"&]/.exec(responseHTMLString);
  163. if (signExec){ // при успехе записываем его в хранилище
  164. GM_setValue("sign|#" + plId, signExec[1]);
  165. } else{ // если на странице зелий недоступно, делаем то же самое с домашней страницы
  166. sendGETRequest("home.php", "text/html; charset=windows-1251", function(){
  167. // получаем ответ в виде HTML и достаём sign из ссылки на сброс параметров при её наличии
  168. var parser = new DOMParser(),
  169. responseHTML = parser.parseFromString(this.responseText, "text/html"),
  170. signLink = responseHTML.querySelector("a[href^='shop.php?b=reset_tube&reset=2&sign=']"),
  171. sign = "";
  172. if (signLink){
  173. sign = signLink.getAttribute("href").split("sign=")[1];
  174. // и записываем его в хранилище
  175. GM_setValue("sign|#" + plId, sign);
  176. } else{ // иначе выбрасываем уведомление
  177. alert("Не удалось записать sign, убедитесь, что на счету персонажа есть деньги, на домашней странице нет непрочитанных новостей или вы не находитесь в заявке.");
  178. }
  179. });
  180. }
  181. });
  182. }
  183. //
  184. // Вывод формы
  185. var transferForm = document.getElementsByName("f")[0].parentNode;
  186. transferForm.innerHTML =
  187. "<br>" +
  188. "<textarea id='nicknames' cols='20' rows='10' placeholder='Ники'></textarea>" +
  189. "<textarea id='amounts' cols='8' rows='10' placeholder='Суммы'></textarea>" +
  190. "<textarea id='descriptions' cols='50' rows='10' placeholder='Подписи'></textarea>" +
  191. "<br>" +
  192. "<form id='mode'>" +
  193. "<input type='radio' name='mode' value='fromtop' checked>Сверху вниз</input>" +
  194. "<br>" +
  195. "<input type='radio' name='mode' value='frombottom'>Снизу вверх</input>" +
  196. "</form>" +
  197. "<button type='button' id='submit'>Отправить</button>";
  198. transferForm.style = "text-align: center;";
  199. // Определение id персонажа и его sign (в случае отсутствия сохранённого значения)
  200. var plId = document.querySelector("li > a[href^='pl_hunter_stat.php']").getAttribute("href").split("id=")[1];
  201. if (GM_getValue("sign|#" + plId) === undefined){
  202. // после исполнения запроса к ключу "sign|#" + plId будет приписан sign, кнопку отправки не следует нажимать ранее
  203. getSign(plId);
  204. }
  205. // Обработка данных формы
  206. var submitButton = document.getElementById("submit");
  207. submitButton.onclick = function(event){
  208. // выключаем для подстраховки дефолтное действие кнопки, ставим ей стили состояния отправки
  209. event.preventDefault();
  210. event.target.innerHTML = "Отправляем...";
  211. event.target.disabled = true;
  212. // берём из хранилища sign персонажа
  213. var sign = GM_getValue("sign|#" + plId);
  214. // вытаскиваем из страницы заполненные поля формы и собираем адрес для перехода после отправки переводов
  215. var nicknames = document.getElementById("nicknames").value.split(/\r?\n/),
  216. amounts = document.getElementById("amounts").value.split(/\r?\n/),
  217. descriptions = document.getElementById("descriptions").value.split(/\r?\n/),
  218. mode = document.querySelector("input[name='mode']:checked").value,
  219. urlAfter = "https://" + location.hostname + "/pl_transfers.php?id=" + plId;
  220. // проверка одинаковости количества строк в полях
  221. if (nicknames.length === amounts.length && amounts.length === descriptions.length){
  222. // проверка того, что все указанные суммы золота – натуральные числа
  223. var amountsCorrect = true,
  224. i,
  225. maxI = amounts.length;
  226. for (i=0;i<maxI;i++){
  227. if (!isNaturalNumber(amounts[i])){
  228. // если встречаем некорректное значение, то прекращаем обработку данных и выбрасываем сообщение об ошибке
  229. event.target.innerHTML = "Отправить";
  230. event.target.disabled = false;
  231. amountsCorrect = false;
  232. alert("Суммы золота для переводов должны быть натуральными числами!");
  233. break;
  234. }
  235. }
  236. // если всё норм, идём дальше
  237. if (amountsCorrect){
  238. // переворачиваем массивы, если выбран вариант "снизу вверх"
  239. if (mode === "frombottom") {
  240. nicknames.reverse();
  241. amounts.reverse();
  242. descriptions.reverse();
  243. }
  244. // делаем POST-запросы по количеству строк в полях и переходим на заданную страницу после
  245. queryWrapper(nicknames, amounts, descriptions, -1, sign, delay, urlAfter, event);
  246. }
  247. } else{ // если кол-во строк не равно, то возвращаем кнопку в прежнее состояние и выбрасываем сообщение об ошибке
  248. event.target.innerHTML = "Отправить";
  249. event.target.disabled = false;
  250. alert("Не совпадает количество строк в полях!");
  251. }
  252. };
  253. })();