Advertisement
stuppid_bot

Заставляем асинхронные запросы выполняться синхрнонно при ра

Jan 31st, 2016
216
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // Форма для ввода капчи
  2. ;(function(){var s=document.createElement('style');s.textContent="\n    body {\n      color: #212121;\n      margin: 0;\n      font: normal 13px/1em Arial, sans-serif;\n    }\n    #mainWrapper {\n      margin: auto;\n      width: 200px;\n      padding: 10px 20px;\n    }\n    .inputBox {\n      padding: 2px 4px;\n    }\n    .inputBox input {\n      width: 100%;\n      box-sizing: border-box;\n      text-align: center;\n      display: block;\n      margin: auto;\n    }\n    .captchaBox {\n      padding: 2px 4px;\n      height: 50px;\n      width: 130px;\n      display: block;\n      margin: auto;\n    }\n    #captchaKey {\n      width: 130px;\n    }\n    .buttonBox {\n      padding: 2px 4px;\n    }\n    .buttonBox button {\n      width: 130px;\n      box-sizing: border-box;\n      display: block;\n      margin: auto;\n    }\n    #captchaForm {\n      display: none;\n    }\n  ";document.head.appendChild(s);document.body.innerHTML="<div id=\"mainWrapper\">\n    <form id=\"captchaForm\">\n      <div class=\"captchaBox\">\n        <img id=\"captchaImg\">\n      </div>\n      <div class=\"inputBox\">\n        <input type=\"text\" id=\"captchaKey\" name=\"captchaKey\" placeholder=\"Captcha Code\">\n      </div>\n      <div class=\"buttonBox\">\n        <button>Send</button>\n      </div>\n    </form>\n  </div>";s=document.createElement('script');s.textContent="\n    var captchaForm = document.getElementById(\"captchaForm\");\n    captchaForm.addEventListener(\"submit\", function(ev) {\n      ev.preventDefault();\n    });\n    var captchaImg = document.getElementById(\"captchaImg\");\n    var captchaSrc = captchaImg.src;\n    captchaImg.addEventListener(\"click\", function() {\n      this.src = this.src.replace(/&t=\\d+/, '') + '&t=' + Date.now();\n    });\n  ";document.body.appendChild(s)})();
  3.  
  4. // event-emitter.js
  5. //
  6. // @author Sergei Snegirev (tz4678@gmail.com)
  7. //
  8.  
  9. /**
  10.  * EventEmitter constructor
  11.  */
  12. function EventEmitter() {
  13.   this._listeners = {};
  14. }
  15.  
  16. EventEmitter.prototype = {
  17.   /**
  18.    * Add event listener
  19.    *
  20.    * @param event {string}
  21.    * @param fn {function}
  22.    * @param scope {object} (optional)
  23.    * @param once {boolean} (optional)
  24.    * @return {this}
  25.    */
  26.   on: function(event, fn, scope, once) {
  27.     (this._hasOwn(event) ? this._listeners[event] :
  28.       this._listeners[event] = []).push({
  29.       fn: fn,
  30.       scope: scope != null ? scope : this,
  31.       once: once
  32.     });
  33.     return this;
  34.   },
  35.   /**
  36.    * Add one-shot event listener
  37.    *
  38.    * @param event {string}
  39.    * @param fn {function}
  40.    * @param scope {object} (optional)
  41.    * @return {this}
  42.    */
  43.   once: function(event, fn, scope) {
  44.     return this.on(event, fn, scope, true);
  45.   },
  46.   /**
  47.    * Remove event listeners
  48.    *
  49.    *
  50.    * @param event {string} (optional)
  51.    * @param fn {function} (optional)
  52.    * @param scope {object} (optional)
  53.    * @return {this}
  54.    */
  55.   off: function(event, fn, scope) {
  56.     if (!arguments.length) {
  57.       // .off() remove all listeners
  58.       this._listeners = {};
  59.     } else if (arguments.length == 1) {
  60.       // .off(event) remove all listeners for event
  61.       if (this._hasOwn(event)) {
  62.         delete this._listeners[event];
  63.       }
  64.     } else {
  65.       // .off(event, fn[, scope]) remove all listeners fn for event or
  66.       // remove all listeners fn for event in the scope
  67.       if (this._hasOwn(event)) {
  68.         var listeners = this._listeners[event];
  69.         var i = 0;
  70.         while (i < listeners.length) {
  71.           var listener = listeners[i];
  72.           if (fn === listener.fn &&
  73.               (scope == null || scope === listener.scope)) {
  74.             listeners.splice(i, 1);
  75.           } else {
  76.             ++i
  77.           }
  78.         }
  79.         if (!listeners.length) {
  80.           delete this._listeners[event];
  81.         }
  82.       }
  83.     }
  84.     return this;
  85.   },
  86.   /**
  87.    * Emit event
  88.    *
  89.    * <code>.emit(event[, arg1[, arg2[, ...]]])</code>
  90.    *
  91.    * @param event {string}
  92.    * @param *args
  93.    */
  94.   emit: function(event, args) {
  95.     if (!this._hasOwn(event)) {
  96.       return;
  97.     }
  98.     args = [].slice.call(arguments, 1);
  99.     var listeners = this._listeners[event];
  100.     var i = 0;
  101.     while (i < listeners.length) {
  102.       var listener = listeners[i];
  103.       listener.fn.apply(listener.scope, args)
  104.       if (listener.once) {
  105.         listeners.splice(i, 1);
  106.       } else {
  107.         ++i
  108.       }
  109.     }
  110.     if (!listeners.length) {
  111.       delete this._listeners[event];
  112.     }
  113.  
  114.   },
  115.   _hasOwn: function(event) {
  116.     return this._listeners.hasOwnProperty(event);
  117.   }
  118. };
  119.  
  120. /**
  121.  * Add mixin to obj
  122.  *
  123.  * <code>
  124.  * function Foo() {}
  125.  *
  126.  * EventEmitter.mixin(Foo);
  127.  * var foo = new Foo();
  128.  * foo.on("test", () => console.log("It's works!"));
  129.  * foo.emit("test");
  130.  * </code>
  131.  *
  132.  * @param obj {object}
  133.  * @return {object}
  134.  */
  135. EventEmitter.mixin = function(obj) {
  136.   if (typeof obj == "function") {
  137.     obj = obj.prototype;
  138.   }
  139.   var proto = new EventEmitter();
  140.   for (var i in proto) {
  141.     obj[i] = proto[i];
  142.   }
  143.   return obj;
  144. };
  145.  
  146. // http.js
  147. var PARAM_SEP = "&";
  148. var PARAM_EQ = "=";
  149.  
  150. function encodeQueryParams(params, sep, eq) {
  151.   var pairs = [];
  152.   sep = sep || PARAM_SEP;
  153.   eq = eq || PARAM_EQ;
  154.   for (var i in params) {
  155.     if (params.hasOwnProperty(i)) {
  156.       var name = encodeURIComponent(i);
  157.       var value = encodeURIComponent(params[i]);
  158.       pairs.push(name + eq + value);
  159.     }
  160.   }
  161.   return pairs.join(sep);
  162. }
  163.  
  164. function parseQueryString(str, sep, eq) {
  165.   sep = sep || PARAM_SEP;
  166.   eq = eq || PARAM_EQ;
  167.   var pairs = str.split(sep);
  168.   var ret = {};
  169.   for (var i = 0; i < pairs.length; ++i) {
  170.     var pair = pairs[i].split(eq);
  171.     var name = decodeURIComponent(pair[0]);
  172.     var value = pair.length == 2 ? decodeURIComponent(pair[1]) : '';
  173.     ret[name] = value;
  174.   }
  175.   return ret;
  176. }
  177.  
  178. function request(method, url, callback, data, params, headers) {
  179.   method = method.toUpperCase();
  180.   if (params) {
  181.     params = encodeQueryParams(params);
  182.     url += (url.indexOf('?') == -1 ? '?' : '&') + params;
  183.   }
  184.   var xhr = new XMLHttpRequest();
  185.   xhr.open(method, url);
  186.   xhr.onload = function() {
  187.     if (typeof callback != "function") {
  188.       return;
  189.     }
  190.     try {
  191.       xhr.responseJSON = JSON.parse(xhr.responseText);
  192.     } catch (e) {}
  193.     var data = xhr.responseJSON || xhr.responseText;
  194.     callback(data, xhr.status, xhr);
  195.   };
  196.   var contentType;
  197.   // В Qml typeof FormData == "undefined"
  198.   if (data && (typeof FormData == "undefined" || !(data instanceof FormData))) {
  199.     data = encodeQueryParams(data);
  200.     contentType = "application/x-www-form-urlencoded";
  201.   }
  202.   if (headers) {
  203.     for (var i in headers) {
  204.       if (headers.hasOwnProperty(i)) {
  205.         if (i.toLowerCase() == "content-type") {
  206.             contentType = headers[i];
  207.         } else {
  208.             xhr.setRequestHeader(i, headers[i]);
  209.         }
  210.       }
  211.     }
  212.   }
  213.   contentType && xhr.setRequestHeader("Content-Type", contentType);
  214.   xhr.send(data);
  215. }
  216.  
  217. function get(url, callback, params, headers) {
  218.   request("GET", url, callback, null, params, headers);
  219. }
  220.  
  221. function post(url, callback, data, params, headers) {
  222.   request("POST", url, callback, data, params, headers);
  223. }
  224.  
  225. // Utils
  226. // Fast copy
  227. // В консоли какой-то баг Хром, если пробуешь переопределить встроенный метод
  228. // copy
  229. function clone(o) {
  230.   return JSON.parse(JSON.stringify(o));
  231. }
  232. //
  233.  
  234. var API_VERSION = 5.44;
  235. var API_URL = "https://api.vk.com/method/";
  236. var API_DELAY = 334;
  237.  
  238. var ERRORS = {
  239.   // ...
  240.   CAPTCHA_NEEDED: 14,
  241.   // ...
  242. }
  243.  
  244. function Api(accessToken, userId, delay, version) {
  245.     this.accessToken = accessToken;
  246.     this.userId = userId;
  247.     // Искуственная задержка перед выполнением запроса
  248.     // У Вконтакте есть ограничение на количество запросов в секунду
  249.     this.delay = delay || API_DELAY;
  250.     this.version = version || API_VERSION;
  251.     // Для отладки
  252.     this.lastResponse = null;
  253.     // Очередь запросов
  254.     this._q = [];
  255.     this._waiting = false;
  256.     this._lastRequest = 0;
  257. }
  258.  
  259. Api.prototype = {
  260.   call: function(method, callback, params, delay) {
  261.     // Мы не отправляем запрос сразу, а только добавляем его в очередь
  262.     // ((s)=>{var q=s.split(/\s*,\s*/),o={};for(var i=0;i<q.length;++i)o[q[i]]=q[i];prompt("Ctrl+C:",JSON.stringify(o,0,2).replace(/"/g,''))})("method, callback, params, delay");
  263.     params = clone(params || {});
  264.     params.v = this.version;
  265.     if (this.accessToken) {
  266.       params.access_token = this.accessToken;
  267.     }
  268.     delay || (delay = this.delay)
  269.     this._q.push({
  270.       method: method,
  271.       callback: callback,
  272.       params: params,
  273.       delay: delay
  274.     });
  275.     console.log("Добавили запрос очередь");
  276.     this._handle();
  277.     // .call(...).call(...)
  278.     return this;
  279.   },
  280.   current: function() {
  281.     return this._q[0];
  282.   },
  283.   next: function() {
  284.     console.log("Переходим к следующему запросу");
  285.     this._q.shift(); // в dev/null отправили к праотцам
  286.     this._waiting = false;
  287.     console.log("Сняли блокировку с очереди");
  288.     this._handle();
  289.   },
  290.   retry: function() {
  291.     console.log("Повторяем попытку");
  292.     this._waiting = false;
  293.     this._handle();
  294.   },
  295.   _handle: function() {
  296.     console.log("Пробуем обработать запрос");
  297.     if (this._waiting) {
  298.       console.log("Очередь заблокирована!");
  299.       return;
  300.     }
  301.     var cur = this.current();
  302.     if (cur === undefined) {
  303.       console.log("Очередь пуста");
  304.       return;
  305.     }
  306.     this._waiting = true;
  307.     console.log("Блокируем очередь");
  308.     console.log("Вызываем метод %s с параметрами %s", cur.method,
  309.       JSON.stringify(cur.params).replace(
  310.         /("access_token":")[^"]+/, "$1**censored**"));
  311.     var endpoint = API_URL.replace(/(\/|)$/, '/') + cur.method;
  312.     var delay = cur.delay + this._lastRequest - Date.now();
  313.     delay = delay > 0 ? delay : 0;
  314.     console.log("Ждем %s миллисекунд", delay);
  315.     var self = this;
  316.     setTimeout(function() {
  317.       post(endpoint, function(response) {
  318.         self._lastRequest = Date.now();
  319.         if (response.error) {
  320.           var error = response.error;
  321.           if (error.error_code == ERRORS.CAPTCHA_NEEDED) {
  322.             console.log("Требуется ввод капчи");
  323.             cur.params.captcha_sid = error.captcha_sid;
  324.             self.emit("captcha", error.captcha_img);
  325.             // Ожидаем ввода капчи
  326.             // self.next() не выполнится
  327.             return;
  328.           }
  329.         }
  330.         self.next(); // Выполняем запросы далее
  331.         if (typeof cur.callback == "function") {
  332.           self.lastResponse = response;
  333.           cur.callback(response.error, response.response);
  334.         }
  335.       }, cur.params);
  336.     }, delay); // если отрицательный выполнится сразу
  337.   },
  338. };
  339.  
  340. EventEmitter.mixin(Api);
  341.  
  342. var cookies = parseQueryString(document.cookie, "; ");
  343. var api = new Api(cookies.access_token, parseInt(cookies.user_id));
  344.  
  345. api.on("captcha", function(url) {
  346.   captchaImg.src = url;
  347. });
  348.  
  349. captchaImg.onload = function() {
  350.   captchaForm.style.display = "block";
  351.   captchaKey.value = "";
  352.   captchaKey.focus();
  353.   captchaKey.scrollIntoView();
  354. };
  355.  
  356. captchaForm.addEventListener('submit', function() {
  357.   this.style.display = "none";
  358.   api.current().params.captcha_key = captchaKey.value;
  359.   api.retry();
  360. });
  361.  
  362. for (var i = 1; i <= 20; ++i) {
  363.   api.call("wall.post", function(error, response) {
  364.     console.log(response.post_id);
  365.   }, {message: "Сообщение #" + i});
  366. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement