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