From 8d280e157c97958e0ce26572f0185a7a163b2c67 Mon Sep 17 00:00:00 2001 From: Laurynas Karvelis Date: Wed, 21 Mar 2018 14:39:46 +0200 Subject: [PATCH] Introduce internal API call queueing mechanism to send messages sequentially (no more random order) --- src/functions/api.js | 57 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/src/functions/api.js b/src/functions/api.js index b505f81..e56a7d4 100644 --- a/src/functions/api.js +++ b/src/functions/api.js @@ -1,6 +1,17 @@ // API methods import fetch from './fetch'; +/** + * Simple replacement for Bluebird's Promise.mapSeries() implementation + * @param {Array} tasks to run serially + * @param {Function} task function to execute + * @return {Promise} + */ +function sequence(tasks, fn) { + return tasks.reduce((promise, task) => promise.then(() => fn(task)), Promise.resolve()); +} + + /** * API class, has a function for each method of the Telegram API which take * an object argument, and send request to the API server @@ -8,6 +19,7 @@ import fetch from './fetch'; * Methods: getMe, sendMessage, forwardMessage, sendPhoto, sendAudio, * sendDocument, sendSticker, sendVideo, sendLocation, sendChatAction, * getUserProfilePhotos, getUpdates + * */ export default class API { /** @@ -16,6 +28,29 @@ export default class API { */ constructor(token) { this.token = token; + this._queue = []; + this._inUseQueue = []; + } + + /** + * Run Telegram API calls serially using internal queueing mechanism + * @private + */ + _runQueue() { + // implementation taken from https://github.com/yagop/node-telegram-bot-api/issues/192#issuecomment-249488807 + if (this._inUseQueue.length || !this._queue.length) return; + + this._inUseQueue = this._queue; + this._queue = []; + + sequence(this._inUseQueue, request => { //eslint-disable-line + return this.request(request.method, request.data) + .then(request.resolve) + .catch(request.reject); + }).then(() => { + this._inUseQueue = []; + this._runQueue(); + }); } } @@ -24,12 +59,24 @@ API.prototype.request = function request(method, data) { }; const methods = ['getMe', 'sendMessage', 'forwardMessage', 'sendPhoto', -'sendAudio', 'sendDocument', 'sendSticker', 'sendVideo', -'sendLocation', 'sendChatAction', 'getUserProfilePhotos', -'getUpdates', 'setWebhook', 'deleteMessage']; + 'sendAudio', 'sendDocument', 'sendSticker', 'sendVideo', + 'sendLocation', 'sendChatAction', 'getUserProfilePhotos', + 'getUpdates', 'setWebhook', 'deleteMessage']; methods.forEach(method => { - API.prototype[method] = function(data) { //eslint-disable-line - return this.request(method, data); + API.prototype[method] = function (data) { //eslint-disable-line + // implementation taken from https://github.com/yagop/node-telegram-bot-api/issues/192#issuecomment-249488807 + let resolve; + let reject; + + const promise = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }); + + this._queue.push({ method, data, resolve, reject }); + process.nextTick(this._runQueue.bind(this)); + + return promise; }; });