mirror of
https://github.com/yagop/node-telegram-bot-api
synced 2025-08-29 05:17:41 +00:00
Merge PR #250: 2017, Major Changes
See https://github.com/yagop/node-telegram-bot-api/pull/250 for details on this PR!
This commit is contained in:
commit
ce4dff78fa
@ -1,8 +1,9 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
|
- "6"
|
||||||
- "5"
|
- "5"
|
||||||
- "4"
|
- "4"
|
||||||
- "0.12.12"
|
- "0.12"
|
||||||
after_success:
|
after_success:
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
cache:
|
cache:
|
||||||
|
64
README.md
64
README.md
@ -71,10 +71,16 @@ TelegramBot
|
|||||||
|
|
||||||
* [TelegramBot](#TelegramBot)
|
* [TelegramBot](#TelegramBot)
|
||||||
* [new TelegramBot(token, [options])](#new_TelegramBot_new)
|
* [new TelegramBot(token, [options])](#new_TelegramBot_new)
|
||||||
|
* [.initPolling()](#TelegramBot+initPolling)
|
||||||
* [.stopPolling()](#TelegramBot+stopPolling) ⇒ <code>Promise</code>
|
* [.stopPolling()](#TelegramBot+stopPolling) ⇒ <code>Promise</code>
|
||||||
|
* [.isPolling()](#TelegramBot+isPolling) ⇒ <code>Boolean</code>
|
||||||
|
* [.openWebHook()](#TelegramBot+openWebHook)
|
||||||
|
* [.closeWebHook()](#TelegramBot+closeWebHook) ⇒ <code>Promise</code>
|
||||||
|
* [.hasOpenWebHook()](#TelegramBot+hasOpenWebHook) ⇒ <code>Boolean</code>
|
||||||
* [.getMe()](#TelegramBot+getMe) ⇒ <code>Promise</code>
|
* [.getMe()](#TelegramBot+getMe) ⇒ <code>Promise</code>
|
||||||
* [.setWebHook(url, [cert])](#TelegramBot+setWebHook)
|
* [.setWebHook(url, [cert])](#TelegramBot+setWebHook)
|
||||||
* [.getUpdates([timeout], [limit], [offset])](#TelegramBot+getUpdates) ⇒ <code>Promise</code>
|
* [.getUpdates([timeout], [limit], [offset])](#TelegramBot+getUpdates) ⇒ <code>Promise</code>
|
||||||
|
* [.processUpdate(update)](#TelegramBot+processUpdate)
|
||||||
* [.sendMessage(chatId, text, [options])](#TelegramBot+sendMessage) ⇒ <code>Promise</code>
|
* [.sendMessage(chatId, text, [options])](#TelegramBot+sendMessage) ⇒ <code>Promise</code>
|
||||||
* [.answerInlineQuery(inlineQueryId, results, [options])](#TelegramBot+answerInlineQuery) ⇒ <code>Promise</code>
|
* [.answerInlineQuery(inlineQueryId, results, [options])](#TelegramBot+answerInlineQuery) ⇒ <code>Promise</code>
|
||||||
* [.forwardMessage(chatId, fromChatId, messageId)](#TelegramBot+forwardMessage) ⇒ <code>Promise</code>
|
* [.forwardMessage(chatId, fromChatId, messageId)](#TelegramBot+forwardMessage) ⇒ <code>Promise</code>
|
||||||
@ -122,14 +128,24 @@ Emits `message` when a message arrives.
|
|||||||
| token | <code>String</code> | | Bot Token |
|
| token | <code>String</code> | | Bot Token |
|
||||||
| [options] | <code>Object</code> | | |
|
| [options] | <code>Object</code> | | |
|
||||||
| [options.polling] | <code>Boolean</code> | <code>Object</code> | <code>false</code> | Set true to enable polling or set options |
|
| [options.polling] | <code>Boolean</code> | <code>Object</code> | <code>false</code> | Set true to enable polling or set options |
|
||||||
| [options.polling.timeout] | <code>String</code> | <code>Number</code> | <code>10</code> | Polling time in seconds |
|
| [options.polling.timeout] | <code>String</code> | <code>Number</code> | <code>10</code> | Timeout in seconds for long polling |
|
||||||
| [options.polling.interval] | <code>String</code> | <code>Number</code> | <code>2000</code> | Interval between requests in miliseconds |
|
| [options.polling.interval] | <code>String</code> | <code>Number</code> | <code>300</code> | Interval between requests in miliseconds |
|
||||||
|
| [options.polling.autoStart] | <code>Boolean</code> | <code>true</code> | Start polling immediately |
|
||||||
| [options.webHook] | <code>Boolean</code> | <code>Object</code> | <code>false</code> | Set true to enable WebHook or set options |
|
| [options.webHook] | <code>Boolean</code> | <code>Object</code> | <code>false</code> | Set true to enable WebHook or set options |
|
||||||
| [options.webHook.key] | <code>String</code> | | PEM private key to webHook server. |
|
| [options.webHook.port] | <code>Number</code> | <code>8443</code> | Port to bind to |
|
||||||
| [options.webHook.cert] | <code>String</code> | | PEM certificate (public) to webHook server. |
|
| [options.webHook.key] | <code>String</code> | | Path to file with PEM private key for webHook server. (Read synchronously!) |
|
||||||
|
| [options.webHook.cert] | <code>String</code> | | Path to file with PEM certificate (public) for webHook server. (Read synchronously!) |
|
||||||
|
| [options.webHook.autoOpen] | <code>Boolean</code> | <code>true</code> | Open webHook immediately |
|
||||||
| [options.onlyFirstMatch] | <code>Boolean</code> | <code>false</code> | Set to true to stop after first match. Otherwise, all regexps are executed |
|
| [options.onlyFirstMatch] | <code>Boolean</code> | <code>false</code> | Set to true to stop after first match. Otherwise, all regexps are executed |
|
||||||
| [options.request] | <code>Object</code> | | Options which will be added for all requests to telegram api. See https://github.com/request/request#requestoptions-callback for more information. |
|
| [options.request] | <code>Object</code> | | Options which will be added for all requests to telegram api. See https://github.com/request/request#requestoptions-callback for more information. |
|
||||||
|
| [options.baseApiUrl] | <code>String</code> | <code>https://api.telegram.org</code> | API Base URl; useful for proxying and testing |
|
||||||
|
|
||||||
|
<a name="TelegramBot+initPolling"></a>
|
||||||
|
|
||||||
|
### telegramBot.initPolling()
|
||||||
|
Start polling
|
||||||
|
|
||||||
|
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
|
||||||
<a name="TelegramBot+stopPolling"></a>
|
<a name="TelegramBot+stopPolling"></a>
|
||||||
|
|
||||||
### telegramBot.stopPolling() ⇒ <code>Promise</code>
|
### telegramBot.stopPolling() ⇒ <code>Promise</code>
|
||||||
@ -137,6 +153,32 @@ Stops polling after the last polling request resolves
|
|||||||
|
|
||||||
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
|
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
|
||||||
**Returns**: <code>Promise</code> - promise Promise, of last polling request
|
**Returns**: <code>Promise</code> - promise Promise, of last polling request
|
||||||
|
<a name="TelegramBot+isPolling"></a>
|
||||||
|
|
||||||
|
### telegramBot.isPolling() ⇒ <code>Boolean</code>
|
||||||
|
Return true if polling. Otherwise, false.
|
||||||
|
|
||||||
|
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
|
||||||
|
<a name="TelegramBot+openWebHook"></a>
|
||||||
|
|
||||||
|
### telegramBot.openWebHook()
|
||||||
|
Open webhook
|
||||||
|
|
||||||
|
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
|
||||||
|
<a name="TelegramBot+closeWebHook"></a>
|
||||||
|
|
||||||
|
### telegramBot.closeWebHook() ⇒ <code>Promise</code>
|
||||||
|
Close webhook after closing all current connections
|
||||||
|
|
||||||
|
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
|
||||||
|
**Returns**: <code>Promise</code> - promise
|
||||||
|
<a name="TelegramBot+hasOpenWebHook"></a>
|
||||||
|
|
||||||
|
### telegramBot.hasOpenWebHook() ⇒ <code>Boolean</code>
|
||||||
|
Return true if using webhook and it is open i.e. accepts connections.
|
||||||
|
Otherwise, false.
|
||||||
|
|
||||||
|
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
|
||||||
<a name="TelegramBot+getMe"></a>
|
<a name="TelegramBot+getMe"></a>
|
||||||
|
|
||||||
### telegramBot.getMe() ⇒ <code>Promise</code>
|
### telegramBot.getMe() ⇒ <code>Promise</code>
|
||||||
@ -172,6 +214,20 @@ Use this method to receive incoming updates using long polling
|
|||||||
| [limit] | <code>Number</code> | <code>String</code> | Limits the number of updates to be retrieved. |
|
| [limit] | <code>Number</code> | <code>String</code> | Limits the number of updates to be retrieved. |
|
||||||
| [offset] | <code>Number</code> | <code>String</code> | Identifier of the first update to be returned. |
|
| [offset] | <code>Number</code> | <code>String</code> | Identifier of the first update to be returned. |
|
||||||
|
|
||||||
|
<a name="TelegramBot+processUpdate"></a>
|
||||||
|
|
||||||
|
### telegramBot.processUpdate(update)
|
||||||
|
Process an update; emitting the proper events and executing regexp
|
||||||
|
callbacks. This method is useful should you be using a different
|
||||||
|
way to fetch updates, other than those provided by TelegramBot.
|
||||||
|
|
||||||
|
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
|
||||||
|
**See**: https://core.telegram.org/bots/api#update
|
||||||
|
|
||||||
|
| Param | Type |
|
||||||
|
| --- | --- |
|
||||||
|
| update | <code>Object</code> |
|
||||||
|
|
||||||
<a name="TelegramBot+sendMessage"></a>
|
<a name="TelegramBot+sendMessage"></a>
|
||||||
|
|
||||||
### telegramBot.sendMessage(chatId, text, [options]) ⇒ <code>Promise</code>
|
### telegramBot.sendMessage(chatId, text, [options]) ⇒ <code>Promise</code>
|
||||||
|
11
package.json
11
package.json
@ -14,11 +14,13 @@
|
|||||||
"bot"
|
"bot"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublish": "babel -d ./lib src",
|
"build": "babel -d ./lib src",
|
||||||
"test": "istanbul cover ./node_modules/mocha/bin/_mocha -- -R spec --timeout 10000",
|
"prepublish": "npm run build",
|
||||||
|
"test": "istanbul cover ./node_modules/mocha/bin/_mocha",
|
||||||
"prepublish:test": "npm run prepublish && npm run test",
|
"prepublish:test": "npm run prepublish && npm run test",
|
||||||
"gen-doc": "jsdoc2md --src src/telegram.js -t README.hbs > README.md",
|
"gen-doc": "jsdoc2md --src src/telegram.js -t README.hbs > README.md",
|
||||||
"eslint": "eslint ./src"
|
"eslint": "eslint ./src ./test",
|
||||||
|
"pretest": "npm run eslint && npm run build"
|
||||||
},
|
},
|
||||||
"author": "Yago Pérez <yagoperezs@gmail.com>",
|
"author": "Yago Pérez <yagoperezs@gmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -53,7 +55,8 @@
|
|||||||
"istanbul": "^1.1.0-alpha.1",
|
"istanbul": "^1.1.0-alpha.1",
|
||||||
"jsdoc-to-markdown": "^1.3.3",
|
"jsdoc-to-markdown": "^1.3.3",
|
||||||
"mocha": "^2.4.5",
|
"mocha": "^2.4.5",
|
||||||
"mocha-lcov-reporter": "^1.2.0"
|
"mocha-lcov-reporter": "^1.2.0",
|
||||||
|
"node-static": "^0.7.9"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
445
src/telegram.js
445
src/telegram.js
@ -20,6 +20,11 @@ const _messageTypes = [
|
|||||||
'new_chat_photo', 'delete_chat_photo', 'group_chat_created'
|
'new_chat_photo', 'delete_chat_photo', 'group_chat_created'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// enable cancellation
|
||||||
|
Promise.config({
|
||||||
|
cancellation: true,
|
||||||
|
});
|
||||||
|
|
||||||
class TelegramBot extends EventEmitter {
|
class TelegramBot extends EventEmitter {
|
||||||
|
|
||||||
static get messageTypes() {
|
static get messageTypes() {
|
||||||
@ -36,149 +41,75 @@ class TelegramBot extends EventEmitter {
|
|||||||
* @param {String} token Bot Token
|
* @param {String} token Bot Token
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @param {Boolean|Object} [options.polling=false] Set true to enable polling or set options
|
* @param {Boolean|Object} [options.polling=false] Set true to enable polling or set options
|
||||||
* @param {String|Number} [options.polling.timeout=10] Polling time in seconds
|
* @param {String|Number} [options.polling.timeout=10] Timeout in seconds for long polling
|
||||||
* @param {String|Number} [options.polling.interval=2000] Interval between requests in miliseconds
|
* @param {String|Number} [options.polling.interval=300] Interval between requests in miliseconds
|
||||||
|
* @param {Boolean} [options.polling.autoStart=true] Start polling immediately
|
||||||
* @param {Boolean|Object} [options.webHook=false] Set true to enable WebHook or set options
|
* @param {Boolean|Object} [options.webHook=false] Set true to enable WebHook or set options
|
||||||
* @param {String} [options.webHook.key] PEM private key to webHook server.
|
* @param {Number} [options.webHook.port=8443] Port to bind to
|
||||||
* @param {String} [options.webHook.cert] PEM certificate (public) to webHook server.
|
* @param {String} [options.webHook.key] Path to file with PEM private key for webHook server. (Read synchronously!)
|
||||||
|
* @param {String} [options.webHook.cert] Path to file with PEM certificate (public) for webHook server. (Read synchronously!)
|
||||||
|
* @param {Boolean} [options.webHook.autoOpen=true] Open webHook immediately
|
||||||
* @param {Boolean} [options.onlyFirstMatch=false] Set to true to stop after first match. Otherwise, all regexps are executed
|
* @param {Boolean} [options.onlyFirstMatch=false] Set to true to stop after first match. Otherwise, all regexps are executed
|
||||||
* @param {Object} [options.request] Options which will be added for all requests to telegram api.
|
* @param {Object} [options.request] Options which will be added for all requests to telegram api.
|
||||||
* See https://github.com/request/request#requestoptions-callback for more information.
|
* See https://github.com/request/request#requestoptions-callback for more information.
|
||||||
|
* @param {String} [options.baseApiUrl=https://api.telegram.org] API Base URl; useful for proxying and testing
|
||||||
* @see https://core.telegram.org/bots/api
|
* @see https://core.telegram.org/bots/api
|
||||||
*/
|
*/
|
||||||
constructor(token, options = {}) {
|
constructor(token, options = {}) {
|
||||||
super();
|
super();
|
||||||
this.options = options;
|
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.textRegexpCallbacks = [];
|
this.options = options;
|
||||||
this.onReplyToMessages = [];
|
this.options.baseApiUrl = options.baseApiUrl || 'https://api.telegram.org';
|
||||||
|
this._textRegexpCallbacks = [];
|
||||||
|
this._onReplyToMessages = [];
|
||||||
|
|
||||||
if (options.polling) {
|
if (options.polling) {
|
||||||
this.initPolling();
|
const autoStart = options.polling.autoStart;
|
||||||
|
if (typeof autoStart === 'undefined' || autoStart === true) {
|
||||||
|
this.initPolling();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.webHook) {
|
if (options.webHook) {
|
||||||
this._WebHook = new TelegramBotWebHook(token, options.webHook, this.processUpdate.bind(this));
|
const autoOpen = options.webHook.autoOpen;
|
||||||
|
if (typeof autoOpen === 'undefined' || autoOpen === true) {
|
||||||
|
this.openWebHook();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initPolling() {
|
|
||||||
if (this._polling) {
|
|
||||||
this._polling.abort = true;
|
|
||||||
this._polling.lastRequest.cancel('Polling restart');
|
|
||||||
}
|
|
||||||
this._polling = new TelegramBotPolling(this.token, this.options.polling, this.processUpdate.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops polling after the last polling request resolves
|
* Generates url with bot token and provided path/method you want to be got/executed by bot
|
||||||
*
|
* @param {String} path
|
||||||
* @return {Promise} promise Promise, of last polling request
|
* @return {String} url
|
||||||
|
* @private
|
||||||
|
* @see https://core.telegram.org/bots/api#making-requests
|
||||||
*/
|
*/
|
||||||
stopPolling() {
|
_buildURL(_path) {
|
||||||
if (this._polling) {
|
return `${this.options.baseApiUrl}/bot${this.token}/${_path}`;
|
||||||
return this._polling.stopPolling();
|
|
||||||
}
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
processUpdate(update) {
|
|
||||||
debug('Process Update %j', update);
|
|
||||||
const message = update.message;
|
|
||||||
const editedMessage = update.edited_message;
|
|
||||||
const channelPost = update.channel_post;
|
|
||||||
const editedChannelPost = update.edited_channel_post;
|
|
||||||
const inlineQuery = update.inline_query;
|
|
||||||
const chosenInlineResult = update.chosen_inline_result;
|
|
||||||
const callbackQuery = update.callback_query;
|
|
||||||
|
|
||||||
if (message) {
|
|
||||||
debug('Process Update message %j', message);
|
|
||||||
this.emit('message', message);
|
|
||||||
const processMessageType = messageType => {
|
|
||||||
if (message[messageType]) {
|
|
||||||
debug('Emtting %s: %j', messageType, message);
|
|
||||||
this.emit(messageType, message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
TelegramBot.messageTypes.forEach(processMessageType);
|
|
||||||
if (message.text) {
|
|
||||||
debug('Text message');
|
|
||||||
this.textRegexpCallbacks.some(reg => {
|
|
||||||
debug('Matching %s with %s', message.text, reg.regexp);
|
|
||||||
const result = reg.regexp.exec(message.text);
|
|
||||||
if (result) {
|
|
||||||
debug('Matches %s', reg.regexp);
|
|
||||||
reg.callback(message, result);
|
|
||||||
// returning truthy value exits .some
|
|
||||||
return this.options.onlyFirstMatch;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (message.reply_to_message) {
|
|
||||||
// Only callbacks waiting for this message
|
|
||||||
this.onReplyToMessages.forEach(reply => {
|
|
||||||
// Message from the same chat
|
|
||||||
if (reply.chatId === message.chat.id) {
|
|
||||||
// Responding to that message
|
|
||||||
if (reply.messageId === message.reply_to_message.message_id) {
|
|
||||||
// Resolve the promise
|
|
||||||
reply.callback(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (editedMessage) {
|
|
||||||
debug('Process Update edited_message %j', editedMessage);
|
|
||||||
this.emit('edited_message', editedMessage);
|
|
||||||
if (editedMessage.text) {
|
|
||||||
this.emit('edited_message_text', editedMessage);
|
|
||||||
}
|
|
||||||
if (editedMessage.caption) {
|
|
||||||
this.emit('edited_message_caption', editedMessage);
|
|
||||||
}
|
|
||||||
} else if (channelPost) {
|
|
||||||
debug('Process Update channel_post %j', channelPost);
|
|
||||||
this.emit('channel_post', channelPost);
|
|
||||||
} else if (editedChannelPost) {
|
|
||||||
debug('Process Update edited_channel_post %j', editedChannelPost);
|
|
||||||
this.emit('edited_channel_post', editedChannelPost);
|
|
||||||
if (editedChannelPost.text) {
|
|
||||||
this.emit('edited_channel_post_text', editedChannelPost);
|
|
||||||
}
|
|
||||||
if (editedChannelPost.caption) {
|
|
||||||
this.emit('edited_channel_post_caption', editedChannelPost);
|
|
||||||
}
|
|
||||||
} else if (inlineQuery) {
|
|
||||||
debug('Process Update inline_query %j', inlineQuery);
|
|
||||||
this.emit('inline_query', inlineQuery);
|
|
||||||
} else if (chosenInlineResult) {
|
|
||||||
debug('Process Update chosen_inline_result %j', chosenInlineResult);
|
|
||||||
this.emit('chosen_inline_result', chosenInlineResult);
|
|
||||||
} else if (callbackQuery) {
|
|
||||||
debug('Process Update callback_query %j', callbackQuery);
|
|
||||||
this.emit('callback_query', callbackQuery);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// used so that other funcs are not non-optimizable
|
|
||||||
_safeParse(json) {
|
|
||||||
try {
|
|
||||||
return JSON.parse(json);
|
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Error parsing Telegram response: ${String(json)}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix 'reply_markup' parameter by making it JSON-serialized, as
|
||||||
|
* required by the Telegram Bot API
|
||||||
|
* @param {Object} obj Object; either 'form' or 'qs'
|
||||||
|
* @private
|
||||||
|
* @see https://core.telegram.org/bots/api#sendmessage
|
||||||
|
*/
|
||||||
_fixReplyMarkup(obj) {
|
_fixReplyMarkup(obj) {
|
||||||
const replyMarkup = obj.reply_markup;
|
const replyMarkup = obj.reply_markup;
|
||||||
if (replyMarkup && typeof replyMarkup !== 'string') {
|
if (replyMarkup && typeof replyMarkup !== 'string') {
|
||||||
// reply_markup must be passed as JSON stringified to Telegram
|
|
||||||
obj.reply_markup = JSON.stringify(replyMarkup);
|
obj.reply_markup = JSON.stringify(replyMarkup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// request-promise
|
/**
|
||||||
|
* Make request against the API
|
||||||
|
* @param {String} _path API endpoint
|
||||||
|
* @param {Object} [options]
|
||||||
|
* @private
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
_request(_path, options = {}) {
|
_request(_path, options = {}) {
|
||||||
if (!this.token) {
|
if (!this.token) {
|
||||||
throw new Error('Telegram Bot Token not provided!');
|
throw new Error('Telegram Bot Token not provided!');
|
||||||
@ -194,6 +125,7 @@ class TelegramBot extends EventEmitter {
|
|||||||
if (options.qs) {
|
if (options.qs) {
|
||||||
this._fixReplyMarkup(options.qs);
|
this._fixReplyMarkup(options.qs);
|
||||||
}
|
}
|
||||||
|
|
||||||
options.url = this._buildURL(_path);
|
options.url = this._buildURL(_path);
|
||||||
options.simple = false;
|
options.simple = false;
|
||||||
options.resolveWithFullResponse = true;
|
options.resolveWithFullResponse = true;
|
||||||
@ -202,31 +134,148 @@ class TelegramBot extends EventEmitter {
|
|||||||
return request(options)
|
return request(options)
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
if (resp.statusCode !== 200) {
|
if (resp.statusCode !== 200) {
|
||||||
throw new Error(`${resp.statusCode} ${resp.body}`);
|
const error = new Error(`${resp.statusCode} ${resp.body}`);
|
||||||
|
error.response = resp;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data;
|
||||||
|
|
||||||
|
try {
|
||||||
|
data = JSON.parse(resp.body);
|
||||||
|
} catch (err) {
|
||||||
|
const error = new Error(`Error parsing Telegram response: ${resp.body}`);
|
||||||
|
error.response = resp;
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = this._safeParse(resp.body);
|
|
||||||
if (data.ok) {
|
if (data.ok) {
|
||||||
return data.result;
|
return data.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`${data.error_code} ${data.description}`);
|
const error = new Error(`${data.error_code} ${data.description}`);
|
||||||
|
error.response = resp;
|
||||||
|
error.response.body = data;
|
||||||
|
throw error;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates url with bot token and provided path/method you want to be got/executed by bot
|
* Format data to be uploaded; handles file paths, streams and buffers
|
||||||
* @return {String} url
|
* @param {String} type
|
||||||
* @param {String} path
|
* @param {String|stream.Stream|Buffer} data
|
||||||
|
* @return {Array} formatted
|
||||||
|
* @return {Object} formatted[0] formData
|
||||||
|
* @return {String} formatted[1] fileId
|
||||||
* @private
|
* @private
|
||||||
* @see https://core.telegram.org/bots/api#making-requests
|
|
||||||
*/
|
*/
|
||||||
_buildURL(_path) {
|
_formatSendData(type, data) {
|
||||||
return URL.format({
|
let formData;
|
||||||
protocol: 'https',
|
let fileName;
|
||||||
host: 'api.telegram.org',
|
let fileId;
|
||||||
pathname: `/bot${this.token}/${_path}`
|
if (data instanceof stream.Stream) {
|
||||||
});
|
fileName = URL.parse(path.basename(data.path.toString())).pathname;
|
||||||
|
formData = {};
|
||||||
|
formData[type] = {
|
||||||
|
value: data,
|
||||||
|
options: {
|
||||||
|
filename: qs.unescape(fileName),
|
||||||
|
contentType: mime.lookup(fileName)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (Buffer.isBuffer(data)) {
|
||||||
|
const filetype = fileType(data);
|
||||||
|
if (!filetype) {
|
||||||
|
throw new Error('Unsupported Buffer file type');
|
||||||
|
}
|
||||||
|
formData = {};
|
||||||
|
formData[type] = {
|
||||||
|
value: data,
|
||||||
|
options: {
|
||||||
|
filename: `data.${filetype.ext}`,
|
||||||
|
contentType: filetype.mime
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (fs.existsSync(data)) {
|
||||||
|
fileName = path.basename(data);
|
||||||
|
formData = {};
|
||||||
|
formData[type] = {
|
||||||
|
value: fs.createReadStream(data),
|
||||||
|
options: {
|
||||||
|
filename: fileName,
|
||||||
|
contentType: mime.lookup(fileName)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
fileId = data;
|
||||||
|
}
|
||||||
|
return [formData, fileId];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start polling
|
||||||
|
*/
|
||||||
|
initPolling() {
|
||||||
|
if (this._polling) {
|
||||||
|
this._polling.stopPolling({
|
||||||
|
cancel: true,
|
||||||
|
reason: 'Polling restart',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this._polling = new TelegramBotPolling(this._request.bind(this), this.options.polling, this.processUpdate.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops polling after the last polling request resolves
|
||||||
|
* @return {Promise} promise Promise, of last polling request
|
||||||
|
*/
|
||||||
|
stopPolling() {
|
||||||
|
if (!this._polling) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
const polling = this._polling;
|
||||||
|
delete this._polling;
|
||||||
|
return polling.stopPolling();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if polling. Otherwise, false.
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
isPolling() {
|
||||||
|
return !!this._polling;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open webhook
|
||||||
|
*/
|
||||||
|
openWebHook() {
|
||||||
|
if (this._webHook) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._webHook = new TelegramBotWebHook(this.token, this.options.webHook, this.processUpdate.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close webhook after closing all current connections
|
||||||
|
* @return {Promise} promise
|
||||||
|
*/
|
||||||
|
closeWebHook() {
|
||||||
|
if (!this._webHook) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
const webHook = this._webHook;
|
||||||
|
delete this._webHook;
|
||||||
|
return webHook.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if using webhook and it is open i.e. accepts connections.
|
||||||
|
* Otherwise, false.
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
hasOpenWebHook() {
|
||||||
|
return !!this._webHook;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -284,6 +333,93 @@ class TelegramBot extends EventEmitter {
|
|||||||
return this._request('getUpdates', { form });
|
return this._request('getUpdates', { form });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process an update; emitting the proper events and executing regexp
|
||||||
|
* callbacks. This method is useful should you be using a different
|
||||||
|
* way to fetch updates, other than those provided by TelegramBot.
|
||||||
|
* @param {Object} update
|
||||||
|
* @see https://core.telegram.org/bots/api#update
|
||||||
|
*/
|
||||||
|
processUpdate(update) {
|
||||||
|
debug('Process Update %j', update);
|
||||||
|
const message = update.message;
|
||||||
|
const editedMessage = update.edited_message;
|
||||||
|
const channelPost = update.channel_post;
|
||||||
|
const editedChannelPost = update.edited_channel_post;
|
||||||
|
const inlineQuery = update.inline_query;
|
||||||
|
const chosenInlineResult = update.chosen_inline_result;
|
||||||
|
const callbackQuery = update.callback_query;
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
debug('Process Update message %j', message);
|
||||||
|
this.emit('message', message);
|
||||||
|
const processMessageType = messageType => {
|
||||||
|
if (message[messageType]) {
|
||||||
|
debug('Emitting %s: %j', messageType, message);
|
||||||
|
this.emit(messageType, message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
TelegramBot.messageTypes.forEach(processMessageType);
|
||||||
|
if (message.text) {
|
||||||
|
debug('Text message');
|
||||||
|
this._textRegexpCallbacks.some(reg => {
|
||||||
|
debug('Matching %s with %s', message.text, reg.regexp);
|
||||||
|
const result = reg.regexp.exec(message.text);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
debug('Matches %s', reg.regexp);
|
||||||
|
reg.callback(message, result);
|
||||||
|
// returning truthy value exits .some
|
||||||
|
return this.options.onlyFirstMatch;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (message.reply_to_message) {
|
||||||
|
// Only callbacks waiting for this message
|
||||||
|
this._onReplyToMessages.forEach(reply => {
|
||||||
|
// Message from the same chat
|
||||||
|
if (reply.chatId === message.chat.id) {
|
||||||
|
// Responding to that message
|
||||||
|
if (reply.messageId === message.reply_to_message.message_id) {
|
||||||
|
// Resolve the promise
|
||||||
|
reply.callback(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (editedMessage) {
|
||||||
|
debug('Process Update edited_message %j', editedMessage);
|
||||||
|
this.emit('edited_message', editedMessage);
|
||||||
|
if (editedMessage.text) {
|
||||||
|
this.emit('edited_message_text', editedMessage);
|
||||||
|
}
|
||||||
|
if (editedMessage.caption) {
|
||||||
|
this.emit('edited_message_caption', editedMessage);
|
||||||
|
}
|
||||||
|
} else if (channelPost) {
|
||||||
|
debug('Process Update channel_post %j', channelPost);
|
||||||
|
this.emit('channel_post', channelPost);
|
||||||
|
} else if (editedChannelPost) {
|
||||||
|
debug('Process Update edited_channel_post %j', editedChannelPost);
|
||||||
|
this.emit('edited_channel_post', editedChannelPost);
|
||||||
|
if (editedChannelPost.text) {
|
||||||
|
this.emit('edited_channel_post_text', editedChannelPost);
|
||||||
|
}
|
||||||
|
if (editedChannelPost.caption) {
|
||||||
|
this.emit('edited_channel_post_caption', editedChannelPost);
|
||||||
|
}
|
||||||
|
} else if (inlineQuery) {
|
||||||
|
debug('Process Update inline_query %j', inlineQuery);
|
||||||
|
this.emit('inline_query', inlineQuery);
|
||||||
|
} else if (chosenInlineResult) {
|
||||||
|
debug('Process Update chosen_inline_result %j', chosenInlineResult);
|
||||||
|
this.emit('chosen_inline_result', chosenInlineResult);
|
||||||
|
} else if (callbackQuery) {
|
||||||
|
debug('Process Update callback_query %j', callbackQuery);
|
||||||
|
this.emit('callback_query', callbackQuery);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send text message.
|
* Send text message.
|
||||||
* @param {Number|String} chatId Unique identifier for the message recipient
|
* @param {Number|String} chatId Unique identifier for the message recipient
|
||||||
@ -330,49 +466,6 @@ class TelegramBot extends EventEmitter {
|
|||||||
return this._request('forwardMessage', { form });
|
return this._request('forwardMessage', { form });
|
||||||
}
|
}
|
||||||
|
|
||||||
_formatSendData(type, data) {
|
|
||||||
let formData;
|
|
||||||
let fileName;
|
|
||||||
let fileId;
|
|
||||||
if (data instanceof stream.Stream) {
|
|
||||||
fileName = URL.parse(path.basename(data.path.toString())).pathname;
|
|
||||||
formData = {};
|
|
||||||
formData[type] = {
|
|
||||||
value: data,
|
|
||||||
options: {
|
|
||||||
filename: qs.unescape(fileName),
|
|
||||||
contentType: mime.lookup(fileName)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else if (Buffer.isBuffer(data)) {
|
|
||||||
const filetype = fileType(data);
|
|
||||||
if (!filetype) {
|
|
||||||
throw new Error('Unsupported Buffer file type');
|
|
||||||
}
|
|
||||||
formData = {};
|
|
||||||
formData[type] = {
|
|
||||||
value: data,
|
|
||||||
options: {
|
|
||||||
filename: `data.${filetype.ext}`,
|
|
||||||
contentType: filetype.mime
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else if (fs.existsSync(data)) {
|
|
||||||
fileName = path.basename(data);
|
|
||||||
formData = {};
|
|
||||||
formData[type] = {
|
|
||||||
value: fs.createReadStream(data),
|
|
||||||
options: {
|
|
||||||
filename: fileName,
|
|
||||||
contentType: mime.lookup(fileName)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
fileId = data;
|
|
||||||
}
|
|
||||||
return [formData, fileId];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send photo
|
* Send photo
|
||||||
* @param {Number|String} chatId Unique identifier for the message recipient
|
* @param {Number|String} chatId Unique identifier for the message recipient
|
||||||
@ -737,11 +830,7 @@ class TelegramBot extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
getFileLink(fileId) {
|
getFileLink(fileId) {
|
||||||
return this.getFile(fileId)
|
return this.getFile(fileId)
|
||||||
.then(resp => URL.format({
|
.then(resp => `${this.options.baseApiUrl}/file/bot${this.token}/${resp.file_path}`);
|
||||||
protocol: 'https',
|
|
||||||
host: 'api.telegram.org',
|
|
||||||
pathname: `/file/bot${this.token}/${resp.file_path}`
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -776,7 +865,7 @@ class TelegramBot extends EventEmitter {
|
|||||||
* the `msg` and the result of executing `regexp.exec` on message text.
|
* the `msg` and the result of executing `regexp.exec` on message text.
|
||||||
*/
|
*/
|
||||||
onText(regexp, callback) {
|
onText(regexp, callback) {
|
||||||
this.textRegexpCallbacks.push({ regexp, callback });
|
this._textRegexpCallbacks.push({ regexp, callback });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -787,7 +876,7 @@ class TelegramBot extends EventEmitter {
|
|||||||
* message.
|
* message.
|
||||||
*/
|
*/
|
||||||
onReplyToMessage(chatId, messageId, callback) {
|
onReplyToMessage(chatId, messageId, callback) {
|
||||||
this.onReplyToMessages.push({
|
this._onReplyToMessages.push({
|
||||||
chatId,
|
chatId,
|
||||||
messageId,
|
messageId,
|
||||||
callback
|
callback
|
||||||
|
@ -1,48 +1,71 @@
|
|||||||
const Promise = require('bluebird');
|
|
||||||
const debug = require('debug')('node-telegram-bot-api');
|
const debug = require('debug')('node-telegram-bot-api');
|
||||||
const request = require('request-promise');
|
|
||||||
const URL = require('url');
|
|
||||||
const ANOTHER_WEB_HOOK_USED = 409;
|
const ANOTHER_WEB_HOOK_USED = 409;
|
||||||
|
|
||||||
|
|
||||||
class TelegramBotPolling {
|
class TelegramBotPolling {
|
||||||
|
/**
|
||||||
constructor(token, options = {}, callback) {
|
* Handles polling against the Telegram servers.
|
||||||
// enable cancellation
|
*
|
||||||
Promise.config({
|
* @param {Function} request Function used to make HTTP requests
|
||||||
cancellation: true,
|
* @param {Boolean|Object} options Polling options
|
||||||
});
|
* @param {Number} [options.timeout=10] Timeout in seconds for long polling
|
||||||
|
* @param {Number} [options.interval=300] Interval between requests in milliseconds
|
||||||
|
* @param {Function} callback Function for processing a new update
|
||||||
|
* @see https://core.telegram.org/bots/api#getupdates
|
||||||
|
*/
|
||||||
|
constructor(request, options = {}, callback) {
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
if (typeof options === 'function') {
|
if (typeof options === 'function') {
|
||||||
callback = options; // eslint-disable-line no-param-reassign
|
callback = options;
|
||||||
options = {}; // eslint-disable-line no-param-reassign
|
options = {};
|
||||||
|
} else if (typeof options === 'boolean') {
|
||||||
|
options = {};
|
||||||
}
|
}
|
||||||
|
/* eslint-enable no-param-reassign */
|
||||||
|
|
||||||
this.offset = 0;
|
this.request = request;
|
||||||
this.token = token;
|
this.options = options;
|
||||||
|
this.options.timeout = options.timeout || 10;
|
||||||
|
this.options.interval = (typeof options.interval === 'number') ? options.interval : 300;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.timeout = options.timeout || 10;
|
this._offset = 0;
|
||||||
this.interval = (typeof options.interval === 'number') ? options.interval : 300;
|
this._lastUpdate = 0;
|
||||||
this.lastUpdate = 0;
|
this._lastRequest = null;
|
||||||
this.lastRequest = null;
|
this._abort = false;
|
||||||
this.abort = false;
|
this._pollingTimeout = null;
|
||||||
this._polling();
|
this._polling();
|
||||||
}
|
}
|
||||||
|
|
||||||
stopPolling() {
|
/**
|
||||||
this.abort = true;
|
* Stop polling
|
||||||
|
* @param {Object} [options]
|
||||||
|
* @param {Boolean} [options.cancel] Cancel current request
|
||||||
|
* @param {String} [options.reason] Reason for stopping polling
|
||||||
|
*/
|
||||||
|
stopPolling(options = {}) {
|
||||||
|
this._abort = true;
|
||||||
|
clearTimeout(this._pollingTimeout);
|
||||||
|
if (options.cancel) {
|
||||||
|
const reason = options.reason || 'Polling stop';
|
||||||
|
return this._lastRequest.cancel(reason);
|
||||||
|
}
|
||||||
// wait until the last request is fulfilled
|
// wait until the last request is fulfilled
|
||||||
return this.lastRequest;
|
return this._lastRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes polling (with recursion!)
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
_polling() {
|
_polling() {
|
||||||
this.lastRequest = this
|
this._lastRequest = this
|
||||||
._getUpdates()
|
._getUpdates()
|
||||||
.then(updates => {
|
.then(updates => {
|
||||||
this.lastUpdate = Date.now();
|
this._lastUpdate = Date.now();
|
||||||
debug('polling data %j', updates);
|
debug('polling data %j', updates);
|
||||||
updates.forEach(update => {
|
updates.forEach(update => {
|
||||||
this.offset = update.update_id;
|
this._offset = update.update_id;
|
||||||
debug('updated offset: %s', this.offset);
|
debug('updated offset: %s', this._offset);
|
||||||
this.callback(update);
|
this.callback(update);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@ -51,83 +74,46 @@ class TelegramBotPolling {
|
|||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
if (this.abort) {
|
if (this._abort) {
|
||||||
debug('Polling is aborted!');
|
debug('Polling is aborted!');
|
||||||
} else {
|
} else {
|
||||||
debug('setTimeout for %s miliseconds', this.interval);
|
debug('setTimeout for %s miliseconds', this.options.interval);
|
||||||
setTimeout(() => this._polling(), this.interval);
|
this._pollingTimeout = setTimeout(() => this._polling(), this.options.interval);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// used so that other funcs are not non-optimizable
|
/**
|
||||||
_safeParse(json) {
|
* Unset current webhook. Used when we detect that a webhook has been set
|
||||||
try {
|
* and we are trying to poll. Polling and WebHook are mutually exclusive.
|
||||||
return JSON.parse(json);
|
* @see https://core.telegram.org/bots/api#getting-updates
|
||||||
} catch (err) {
|
* @private
|
||||||
throw new Error(`Error parsing Telegram response: ${String(json)}`);
|
*/
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_unsetWebHook() {
|
_unsetWebHook() {
|
||||||
return request({
|
return this.request('setWebHook');
|
||||||
url: URL.format({
|
|
||||||
protocol: 'https',
|
|
||||||
host: 'api.telegram.org',
|
|
||||||
pathname: `/bot${this.token}/setWebHook`
|
|
||||||
}),
|
|
||||||
simple: false,
|
|
||||||
resolveWithFullResponse: true
|
|
||||||
})
|
|
||||||
.promise()
|
|
||||||
.then(resp => {
|
|
||||||
if (!resp) {
|
|
||||||
throw new Error(resp);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve updates
|
||||||
|
*/
|
||||||
_getUpdates() {
|
_getUpdates() {
|
||||||
const opts = {
|
const opts = {
|
||||||
qs: {
|
qs: {
|
||||||
offset: this.offset + 1,
|
offset: this._offset + 1,
|
||||||
limit: this.limit,
|
limit: this.options.limit,
|
||||||
timeout: this.timeout
|
timeout: this.options.timeout
|
||||||
},
|
},
|
||||||
url: URL.format({
|
|
||||||
protocol: 'https',
|
|
||||||
host: 'api.telegram.org',
|
|
||||||
pathname: `/bot${this.token}/getUpdates`
|
|
||||||
}),
|
|
||||||
simple: false,
|
|
||||||
resolveWithFullResponse: true,
|
|
||||||
forever: true,
|
|
||||||
};
|
};
|
||||||
debug('polling with options: %j', opts);
|
debug('polling with options: %j', opts);
|
||||||
|
|
||||||
return request(opts)
|
return this.request('getUpdates', opts)
|
||||||
.promise()
|
.catch(err => {
|
||||||
.timeout((10 + this.timeout) * 1000)
|
if (err.response.statusCode === ANOTHER_WEB_HOOK_USED) {
|
||||||
.then(resp => {
|
|
||||||
if (resp.statusCode === ANOTHER_WEB_HOOK_USED) {
|
|
||||||
return this._unsetWebHook();
|
return this._unsetWebHook();
|
||||||
}
|
}
|
||||||
|
throw err;
|
||||||
if (resp.statusCode !== 200) {
|
|
||||||
throw new Error(`${resp.statusCode} ${resp.body}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = this._safeParse(resp.body);
|
|
||||||
|
|
||||||
if (data.ok) {
|
|
||||||
return data.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`${data.error_code} ${data.description}`);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = TelegramBotPolling;
|
module.exports = TelegramBotPolling;
|
||||||
|
@ -3,19 +3,30 @@ const https = require('https');
|
|||||||
const http = require('http');
|
const http = require('http');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const bl = require('bl');
|
const bl = require('bl');
|
||||||
|
const Promise = require('bluebird');
|
||||||
|
|
||||||
|
|
||||||
class TelegramBotWebHook {
|
class TelegramBotWebHook {
|
||||||
|
/**
|
||||||
|
* Sets up a webhook to receive updates
|
||||||
|
*
|
||||||
|
* @param {String} token Telegram API token
|
||||||
|
* @param {Boolean|Object} options WebHook options
|
||||||
|
* @param {Number} [options.port=8443] Port to bind to
|
||||||
|
* @param {Function} callback Function for process a new update
|
||||||
|
*/
|
||||||
constructor(token, options, callback) {
|
constructor(token, options, callback) {
|
||||||
this.token = token;
|
|
||||||
this.callback = callback;
|
|
||||||
this.regex = new RegExp(this.token);
|
|
||||||
|
|
||||||
// define opts
|
// define opts
|
||||||
if (typeof options === 'boolean') {
|
if (typeof options === 'boolean') {
|
||||||
options = {}; // eslint-disable-line no-param-reassign
|
options = {}; // eslint-disable-line no-param-reassign
|
||||||
}
|
}
|
||||||
options.port = options.port || 8443;
|
|
||||||
|
this.token = token;
|
||||||
|
this.options = options;
|
||||||
|
this.options.port = options.port || 8443;
|
||||||
|
this.callback = callback;
|
||||||
|
this._regex = new RegExp(this.token);
|
||||||
|
this._webServer = null;
|
||||||
|
|
||||||
if (options.key && options.cert) { // HTTPS Server
|
if (options.key && options.cert) { // HTTPS Server
|
||||||
debug('HTTPS WebHook enabled');
|
debug('HTTPS WebHook enabled');
|
||||||
@ -44,7 +55,10 @@ class TelegramBotWebHook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pipe+parse body
|
/**
|
||||||
|
* Handle request body by passing it to 'callback'
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
_parseBody = (err, body) => {
|
_parseBody = (err, body) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return debug(err);
|
return debug(err);
|
||||||
@ -58,13 +72,18 @@ class TelegramBotWebHook {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bound req listener
|
/**
|
||||||
|
* Listener for 'request' event on server
|
||||||
|
* @private
|
||||||
|
* @see https://nodejs.org/docs/latest/api/http.html#http_http_createserver_requestlistener
|
||||||
|
* @see https://nodejs.org/docs/latest/api/https.html#https_https_createserver_options_requestlistener
|
||||||
|
*/
|
||||||
_requestListener = (req, res) => {
|
_requestListener = (req, res) => {
|
||||||
debug('WebHook request URL: %s', req.url);
|
debug('WebHook request URL: %s', req.url);
|
||||||
debug('WebHook request headers: %j', req.headers);
|
debug('WebHook request headers: %j', req.headers);
|
||||||
|
|
||||||
// If there isn't token on URL
|
// If there isn't token on URL
|
||||||
if (!this.regex.test(req.url)) {
|
if (!this._regex.test(req.url)) {
|
||||||
debug('WebHook request unauthorized');
|
debug('WebHook request unauthorized');
|
||||||
res.statusCode = 401;
|
res.statusCode = 401;
|
||||||
res.end();
|
res.end();
|
||||||
@ -80,6 +99,19 @@ class TelegramBotWebHook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the webHook
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
close() {
|
||||||
|
const self = this;
|
||||||
|
return new Promise(function closePromise(resolve, reject) {
|
||||||
|
self._webServer.close(function closeCb(error) {
|
||||||
|
if (error) return reject(error);
|
||||||
|
return resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = TelegramBotWebHook;
|
module.exports = TelegramBotWebHook;
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
Running the tests:
|
Running the tests:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Token to be used
|
||||||
export TEST_TELEGRAM_TOKEN=<YOUR_BOT_TOKEN>
|
export TEST_TELEGRAM_TOKEN=<YOUR_BOT_TOKEN>
|
||||||
|
|
||||||
# User Id which you want to send the messages.
|
# User Id which you want to send the messages.
|
||||||
export TEST_USER_ID=<USER_ID>
|
export TEST_USER_ID=<USER_ID>
|
||||||
|
|
||||||
# Group Id which to use in some of the tests, e.g. for TelegramBot#getChat()
|
# Group Id which to use in some of the tests, e.g. for TelegramBot#getChat()
|
||||||
export TEST_GROUP_ID=<GROUP_ID>
|
export TEST_GROUP_ID=<GROUP_ID>
|
||||||
|
|
||||||
|
# Game short name which to use in some of the tests, e.g. TelegramBot#sendGame()
|
||||||
|
export TEST_GAME_SHORT_NAME=<GAME_SHORT_NAME>
|
||||||
|
|
||||||
|
# Run tests
|
||||||
npm run test
|
npm run test
|
||||||
```
|
```
|
||||||
|
BIN
test/data/audio.mp3
Normal file
BIN
test/data/audio.mp3
Normal file
Binary file not shown.
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
BIN
test/data/video.mp4
Normal file
BIN
test/data/video.mp4
Normal file
Binary file not shown.
BIN
test/data/voice.ogg
Normal file
BIN
test/data/voice.ogg
Normal file
Binary file not shown.
673
test/index.js
673
test/index.js
@ -1,673 +0,0 @@
|
|||||||
const TelegramPolling = require('../lib/telegramPolling');
|
|
||||||
const Telegram = require('../lib/telegram');
|
|
||||||
const Promise = require('bluebird');
|
|
||||||
const request = require('request-promise');
|
|
||||||
const assert = require('assert');
|
|
||||||
const fs = require('fs');
|
|
||||||
const is = require('is');
|
|
||||||
|
|
||||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
||||||
const TOKEN = process.env.TEST_TELEGRAM_TOKEN;
|
|
||||||
if (!TOKEN) {
|
|
||||||
throw new Error('Bot token not provided');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Telegram service if not User Id
|
|
||||||
const USERID = process.env.TEST_USER_ID || 777000;
|
|
||||||
const GROUPID = process.env.TEST_GROUP_ID || -1001075450562;
|
|
||||||
|
|
||||||
describe('Telegram', function telegramSuite() {
|
|
||||||
describe('#setWebHook', function setWebHookSuite() {
|
|
||||||
it('should set a webHook', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
// Google IP ¯\_(ツ)_/¯
|
|
||||||
return bot
|
|
||||||
.setWebHook('216.58.210.174')
|
|
||||||
.then(resp => {
|
|
||||||
assert.equal(resp, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set a webHook with certificate', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const cert = `${__dirname}/../examples/crt.pem`;
|
|
||||||
return bot
|
|
||||||
.setWebHook('216.58.210.174', cert)
|
|
||||||
.then(resp => {
|
|
||||||
assert.equal(resp, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should delete the webHook', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
return bot
|
|
||||||
.setWebHook('')
|
|
||||||
.then(resp => {
|
|
||||||
assert.equal(resp, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#WebHook', function WebHookSuite() {
|
|
||||||
it('should reject request if same token not provided', function test() {
|
|
||||||
const bot = new Telegram(TOKEN, { webHook: true });
|
|
||||||
|
|
||||||
return request({
|
|
||||||
url: 'http://localhost:8443/NOT_REAL_TOKEN',
|
|
||||||
method: 'POST',
|
|
||||||
simple: false,
|
|
||||||
resolveWithFullResponse: true
|
|
||||||
}).then(response => {
|
|
||||||
assert.notEqual(response.statusCode, 200);
|
|
||||||
bot._WebHook._webServer.close();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reject request if authorized but not a POST', function test() {
|
|
||||||
const bot = new Telegram(TOKEN, { webHook: true });
|
|
||||||
return request({
|
|
||||||
url: `http://localhost:8443/bot${TOKEN}`,
|
|
||||||
method: 'GET',
|
|
||||||
simple: false,
|
|
||||||
resolveWithFullResponse: true
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
assert.notEqual(response.statusCode, 200);
|
|
||||||
bot._WebHook._webServer.close();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should emit a `message` on HTTP WebHook', function test(done) {
|
|
||||||
const bot = new Telegram(TOKEN, { webHook: true });
|
|
||||||
bot.on('message', () => {
|
|
||||||
bot._WebHook._webServer.close();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
const url = `http://localhost:8443/bot${TOKEN}`;
|
|
||||||
request({
|
|
||||||
url,
|
|
||||||
method: 'POST',
|
|
||||||
json: true,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
body: { update_id: 0, message: { text: 'test' } }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should emit a `message` on HTTPS WebHook', function test(done) {
|
|
||||||
const opts = {
|
|
||||||
webHook: {
|
|
||||||
port: 8443,
|
|
||||||
key: `${__dirname}/../examples/key.pem`,
|
|
||||||
cert: `${__dirname}/../examples/crt.pem`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const bot = new Telegram(TOKEN, opts);
|
|
||||||
bot.on('message', () => {
|
|
||||||
bot._WebHook._webServer.close();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
const url = `https://localhost:8443/bot${TOKEN}`;
|
|
||||||
request({
|
|
||||||
url,
|
|
||||||
method: 'POST',
|
|
||||||
json: true,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
rejectUnhauthorized: false,
|
|
||||||
body: { update_id: 0, message: { text: 'test' } }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getMe', function getMeSuite() {
|
|
||||||
it('should return an User object', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
return bot.getMe().then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getChat', function getChatSuite() {
|
|
||||||
it('should return a Chat object', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
return bot.getChat(USERID).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getChatAdministrators', function getChatAdministratorsSuite() {
|
|
||||||
it('should return an Array', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
return bot.getChatAdministrators(GROUPID).then(resp => {
|
|
||||||
assert.ok(Array.isArray(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getChatMembersCount', function getChatMembersCountSuite() {
|
|
||||||
it('should return an Integer', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
return bot.getChatMembersCount(GROUPID).then(resp => {
|
|
||||||
assert.ok(Number.isInteger(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getChatMember', function getChatMemberSuite() {
|
|
||||||
it('should return a ChatMember', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
return bot.getChatMember(GROUPID, USERID).then(resp => {
|
|
||||||
assert.ok(is.object(resp.user));
|
|
||||||
assert.ok(is.string(resp.status));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getUpdates', function getUpdatesSuite() {
|
|
||||||
it('should return an Array', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
return bot.getUpdates().then(resp => {
|
|
||||||
assert.equal(Array.isArray(resp), true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#sendMessage', function sendMessageSuite() {
|
|
||||||
it('should send a message', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
return bot.sendMessage(USERID, 'test').then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#forwardMessage', function forwardMessageSuite() {
|
|
||||||
it('should forward a message', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
return bot.sendMessage(USERID, 'test').then(resp => {
|
|
||||||
const messageId = resp.message_id;
|
|
||||||
return bot.forwardMessage(USERID, USERID, messageId)
|
|
||||||
.then(forwarded => {
|
|
||||||
assert.ok(is.object(forwarded));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#_formatSendData', function _formatSendData() {
|
|
||||||
it('should handle buffer path from fs.readStream', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
let photo;
|
|
||||||
try {
|
|
||||||
photo = fs.createReadStream(Buffer.from(`${__dirname}/bot.gif`));
|
|
||||||
} catch(ex) {
|
|
||||||
// Older Node.js versions do not support passing a Buffer
|
|
||||||
// representation of the path to fs.createReadStream()
|
|
||||||
if (ex instanceof TypeError) return;
|
|
||||||
}
|
|
||||||
return bot.sendPhoto(USERID, photo).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#sendPhoto', function sendPhotoSuite() {
|
|
||||||
let photoId;
|
|
||||||
it('should send a photo from file', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const photo = `${__dirname}/bot.gif`;
|
|
||||||
return bot.sendPhoto(USERID, photo).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
photoId = resp.photo[0].file_id;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a photo from id', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
// Send the same photo as before
|
|
||||||
const photo = photoId;
|
|
||||||
return bot.sendPhoto(USERID, photo).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a photo from fs.readStream', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const photo = fs.createReadStream(`${__dirname}/bot.gif`);
|
|
||||||
return bot.sendPhoto(USERID, photo).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a photo from request Stream', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const photo = request('https://telegram.org/img/t_logo.png');
|
|
||||||
return bot.sendPhoto(USERID, photo).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a photo from a Buffer', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const photo = fs.readFileSync(`${__dirname}/bot.gif`);
|
|
||||||
return bot.sendPhoto(USERID, photo).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a photo along with reply_markup', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const photo = fs.readFileSync(`${__dirname}/bot.gif`);
|
|
||||||
return bot.sendPhoto(USERID, photo, {
|
|
||||||
reply_markup: {
|
|
||||||
hide_keyboard: true
|
|
||||||
}
|
|
||||||
}).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#sendChatAction', function sendChatActionSuite() {
|
|
||||||
it('should send a chat action', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const action = 'typing';
|
|
||||||
return bot.sendChatAction(USERID, action).then(resp => {
|
|
||||||
assert.equal(resp, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#editMessageText', function editMessageTextSuite() {
|
|
||||||
it('should edit a message sent by the bot', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
return bot.sendMessage(USERID, 'test').then(resp => {
|
|
||||||
assert.equal(resp.text, 'test');
|
|
||||||
const opts = {
|
|
||||||
chat_id: USERID,
|
|
||||||
message_id: resp.message_id
|
|
||||||
};
|
|
||||||
return bot.editMessageText('edit test', opts).then(msg => {
|
|
||||||
assert.equal(msg.text, 'edit test');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#editMessageCaption', function editMessageCaptionSuite() {
|
|
||||||
it('should edit a caption sent by the bot', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const photo = `${__dirname}/bot.gif`;
|
|
||||||
const options = { caption: 'test caption' };
|
|
||||||
return bot.sendPhoto(USERID, photo, options).then(resp => {
|
|
||||||
assert.equal(resp.caption, 'test caption');
|
|
||||||
const opts = {
|
|
||||||
chat_id: USERID,
|
|
||||||
message_id: resp.message_id
|
|
||||||
};
|
|
||||||
return bot.editMessageCaption('new test caption', opts).then(msg => {
|
|
||||||
assert.equal(msg.caption, 'new test caption');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#editMessageReplyMarkup', function editMessageReplyMarkupSuite() {
|
|
||||||
it('should edit previously-set reply markup', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
return bot.sendMessage(USERID, 'test').then(resp => {
|
|
||||||
const replyMarkup = JSON.stringify({
|
|
||||||
inline_keyboard: [[{
|
|
||||||
text: 'Test button',
|
|
||||||
callback_data: 'test'
|
|
||||||
}]]
|
|
||||||
});
|
|
||||||
const opts = {
|
|
||||||
chat_id: USERID,
|
|
||||||
message_id: resp.message_id
|
|
||||||
};
|
|
||||||
return bot.editMessageReplyMarkup(replyMarkup, opts).then(msg => {
|
|
||||||
// Keyboard markup is not returned, do a simple object check
|
|
||||||
assert.ok(is.object(msg));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#sendAudio', function sendAudioSuite() {
|
|
||||||
it('should send an OGG audio', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const audio = request('https://upload.wikimedia.org/wikipedia/commons/c/c8/Example.ogg');
|
|
||||||
return bot.sendAudio(USERID, audio).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#sendDocument', function sendDocumentSuite() {
|
|
||||||
let documentId;
|
|
||||||
it('should send a document from file', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const document = `${__dirname}/bot.gif`;
|
|
||||||
return bot.sendDocument(USERID, document).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
documentId = resp.document.file_id;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a document from id', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
// Send the same photo as before
|
|
||||||
const document = documentId;
|
|
||||||
return bot.sendDocument(USERID, document).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a document from fs.readStream', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const document = fs.createReadStream(`${__dirname}/bot.gif`);
|
|
||||||
return bot.sendDocument(USERID, document).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a document from request Stream', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const document = request('https://telegram.org/img/t_logo.png');
|
|
||||||
return bot.sendDocument(USERID, document).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a document from a Buffer', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const document = fs.readFileSync(`${__dirname}/bot.gif`);
|
|
||||||
return bot.sendDocument(USERID, document).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#sendSticker', function sendStickerSuite() {
|
|
||||||
let stickerId;
|
|
||||||
it('should send a sticker from file', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const sticker = `${__dirname}/sticker.webp`;
|
|
||||||
return bot.sendSticker(USERID, sticker).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
stickerId = resp.sticker.file_id;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a sticker from id', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
// Send the same photo as before
|
|
||||||
return bot.sendSticker(USERID, stickerId).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a sticker from fs.readStream', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const sticker = fs.createReadStream(`${__dirname}/sticker.webp`);
|
|
||||||
return bot.sendSticker(USERID, sticker).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a sticker from request Stream', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const sticker = request('https://www.gstatic.com/webp/gallery3/1_webp_ll.webp');
|
|
||||||
return bot.sendSticker(USERID, sticker).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a sticker from a Buffer', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const sticker = fs.readFileSync(`${__dirname}/sticker.webp`);
|
|
||||||
return bot.sendDocument(USERID, sticker).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#sendVideo', function sendVideoSuite() {
|
|
||||||
let videoId;
|
|
||||||
it('should send a video from file', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const video = `${__dirname}/video.mp4`;
|
|
||||||
return bot.sendVideo(USERID, video).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
videoId = resp.video.file_id;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a video from id', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
// Send the same photo as before
|
|
||||||
return bot.sendVideo(USERID, videoId).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a video from fs.readStream', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const video = fs.createReadStream(`${__dirname}/video.mp4`);
|
|
||||||
return bot.sendVideo(USERID, video).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a video from request Stream', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const sticker = request('http://techslides.com/demos/sample-videos/small.mp4');
|
|
||||||
return bot.sendVideo(USERID, sticker).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should send a video from a Buffer', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const video = fs.readFileSync(`${__dirname}/video.mp4`);
|
|
||||||
return bot.sendVideo(USERID, video).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#sendVoice', function sendVoiceSuite() {
|
|
||||||
it('should send an OGG audio as voice', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const voice = request('https://upload.wikimedia.org/wikipedia/commons/c/c8/Example.ogg');
|
|
||||||
return bot.sendVoice(USERID, voice).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getUserProfilePhotos', function getUserProfilePhotosSuite() {
|
|
||||||
it('should get user profile photos', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
return bot.getUserProfilePhotos(USERID).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
assert.ok(is.number(resp.total_count));
|
|
||||||
assert.ok(is.array(resp.photos));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#sendLocation', function sendLocationSuite() {
|
|
||||||
it('should send a location', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const lat = 47.5351072;
|
|
||||||
const long = -52.7508537;
|
|
||||||
return bot.sendLocation(USERID, lat, long).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
assert.ok(is.object(resp.location));
|
|
||||||
assert.ok(is.number(resp.location.latitude));
|
|
||||||
assert.ok(is.number(resp.location.longitude));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#sendVenue', function sendVenueSuite() {
|
|
||||||
it('should send a venue', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const lat = 47.5351072;
|
|
||||||
const long = -52.7508537;
|
|
||||||
const title = `The Village Shopping Centre`;
|
|
||||||
const address = `430 Topsail Rd,St. John's, NL A1E 4N1, Canada`;
|
|
||||||
return bot.sendVenue(USERID, lat, long, title, address).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
assert.ok(is.object(resp.venue));
|
|
||||||
assert.ok(is.object(resp.venue.location));
|
|
||||||
assert.ok(is.number(resp.venue.location.latitude));
|
|
||||||
assert.ok(is.number(resp.venue.location.longitude));
|
|
||||||
assert.ok(is.string(resp.venue.title));
|
|
||||||
assert.ok(is.string(resp.venue.address));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getFile', function getFileSuite() {
|
|
||||||
let fileId;
|
|
||||||
|
|
||||||
// To get a file we have to send any file first
|
|
||||||
it('should send a photo from file', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const photo = `${__dirname}/bot.gif`;
|
|
||||||
return bot.sendPhoto(USERID, photo).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
fileId = resp.photo[0].file_id;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get a file', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
|
|
||||||
return bot
|
|
||||||
.getFile(fileId)
|
|
||||||
.then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
assert.ok(is.string(resp.file_path));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getFileLink', function getFileLinkSuite() {
|
|
||||||
let fileId;
|
|
||||||
|
|
||||||
// To get a file we have to send any file first
|
|
||||||
it('should send a photo from file', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const photo = `${__dirname}/bot.gif`;
|
|
||||||
return bot.sendPhoto(USERID, photo).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
fileId = resp.photo[0].file_id;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get a file link', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
|
|
||||||
return bot
|
|
||||||
.getFileLink(fileId)
|
|
||||||
.then(fileURI => {
|
|
||||||
assert.ok(is.string(fileURI));
|
|
||||||
assert.equal(fileURI.indexOf('https'), 0);
|
|
||||||
// TODO: validate URL with some library or regexp
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#downloadFile', function downloadFileSuite() {
|
|
||||||
const downloadPath = __dirname;
|
|
||||||
|
|
||||||
it('should download a file', function test() {
|
|
||||||
const bot = new Telegram(TOKEN);
|
|
||||||
const photo = `${__dirname}/bot.gif`;
|
|
||||||
|
|
||||||
// Send a file to get the ID
|
|
||||||
return bot.sendPhoto(USERID, photo).then(resp => {
|
|
||||||
assert.ok(is.object(resp));
|
|
||||||
const fileId = resp.photo[0].file_id;
|
|
||||||
|
|
||||||
return bot
|
|
||||||
.downloadFile(fileId, downloadPath)
|
|
||||||
.then(filePath => {
|
|
||||||
assert.ok(is.string(filePath));
|
|
||||||
assert.ok(fs.existsSync(filePath));
|
|
||||||
fs.unlinkSync(filePath); // Delete file after test
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call `onText` callback on match', function test(done) {
|
|
||||||
const bot = new Telegram(TOKEN, { webHook: true });
|
|
||||||
bot.onText(/\/echo (.+)/, (msg, match) => {
|
|
||||||
bot._WebHook._webServer.close();
|
|
||||||
assert.equal(match[1], 'ECHO ALOHA');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
const url = `http://localhost:8443/bot${TOKEN}`;
|
|
||||||
request({
|
|
||||||
url,
|
|
||||||
method: 'POST',
|
|
||||||
json: true,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
body: { update_id: 0, message: { text: '/echo ECHO ALOHA' } }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}); // End Telegram
|
|
||||||
|
|
||||||
describe('#TelegramBotPolling', function TelegramBotPollingSuite() {
|
|
||||||
|
|
||||||
it('should call the callback on polling', function test(done) {
|
|
||||||
const opts = { interval: 100, timeout: 1 };
|
|
||||||
const polling = new TelegramPolling(TOKEN, opts, (msg) => {
|
|
||||||
if (msg.update_id === 10) {
|
|
||||||
polling.stopPolling().then(() => {
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// The second time _getUpdates is called it will return a message
|
|
||||||
// Really dirty but it works
|
|
||||||
polling._getUpdates = () => {
|
|
||||||
return new Promise.resolve([{ update_id: 10, message: {} }]);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#stopPolling', function stopPollingSuite() {
|
|
||||||
it('should stop polling after last poll request', function test(done) {
|
|
||||||
const opts = { interval: 200, timeout: 0.5 };
|
|
||||||
const polling = new TelegramPolling(TOKEN, opts, (msg) => {
|
|
||||||
// error if message received as only one poll will complete and there should be no more because of stopPolling
|
|
||||||
done(msg);
|
|
||||||
});
|
|
||||||
polling.stopPolling()
|
|
||||||
.then(() => {
|
|
||||||
setInterval(() => {
|
|
||||||
done();
|
|
||||||
}, 1000);
|
|
||||||
}).catch(done);
|
|
||||||
// The second time _getUpdates is called it will return a message
|
|
||||||
// Really dirty but it works
|
|
||||||
polling._getUpdates = () => {
|
|
||||||
return new Promise.resolve([{ update_id: 11, message: {} }]);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@ -1 +1,3 @@
|
|||||||
--require babel-register
|
--reporter spec
|
||||||
|
--require babel-register
|
||||||
|
--timeout 30000
|
||||||
|
878
test/telegram.js
Normal file
878
test/telegram.js
Normal file
@ -0,0 +1,878 @@
|
|||||||
|
const Telegram = require('../lib/telegram');
|
||||||
|
const Promise = require('bluebird');
|
||||||
|
const request = require('request-promise');
|
||||||
|
const assert = require('assert');
|
||||||
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
const is = require('is');
|
||||||
|
const utils = require('./utils');
|
||||||
|
|
||||||
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||||
|
const TOKEN = process.env.TEST_TELEGRAM_TOKEN;
|
||||||
|
if (!TOKEN) {
|
||||||
|
throw new Error('Bot token not provided');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Telegram service if not User Id
|
||||||
|
const USERID = process.env.TEST_USER_ID || 777000;
|
||||||
|
const GROUPID = process.env.TEST_GROUP_ID || -1001075450562;
|
||||||
|
const GAME_SHORT_NAME = process.env.TEST_GAME_SHORT_NAME || 'medusalab_test';
|
||||||
|
const timeout = 60 * 1000;
|
||||||
|
const staticPort = 8091;
|
||||||
|
const pollingPort = 8092;
|
||||||
|
const webHookPort = 8093;
|
||||||
|
const pollingPort2 = 8094;
|
||||||
|
const webHookPort2 = 8095;
|
||||||
|
const staticUrl = `http://127.0.0.1:${staticPort}`;
|
||||||
|
let FILE_ID;
|
||||||
|
let GAME_CHAT_ID;
|
||||||
|
let GAME_MSG_ID;
|
||||||
|
|
||||||
|
before(function beforeAll() {
|
||||||
|
utils.startStaticServer(staticPort);
|
||||||
|
return utils.startMockServer(pollingPort)
|
||||||
|
.then(() => {
|
||||||
|
return utils.startMockServer(pollingPort2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Telegram', function telegramSuite() {
|
||||||
|
let bot;
|
||||||
|
let testbot;
|
||||||
|
let botPolling;
|
||||||
|
let botWebHook;
|
||||||
|
|
||||||
|
before(function beforeAll() {
|
||||||
|
this.timeout(timeout);
|
||||||
|
bot = new Telegram(TOKEN);
|
||||||
|
testbot = new Telegram(TOKEN, {
|
||||||
|
baseApiUrl: `http://127.0.0.1:${pollingPort}`,
|
||||||
|
polling: {
|
||||||
|
autoStart: false,
|
||||||
|
},
|
||||||
|
webHook: {
|
||||||
|
autoOpen: false,
|
||||||
|
port: webHookPort,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
botPolling = new Telegram(TOKEN, {
|
||||||
|
baseApiUrl: `http://127.0.0.1:${pollingPort2}`,
|
||||||
|
polling: true,
|
||||||
|
});
|
||||||
|
botWebHook = new Telegram(TOKEN, {
|
||||||
|
webHook: {
|
||||||
|
port: webHookPort2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
utils.handleRatelimit(bot, 'sendPhoto', this);
|
||||||
|
utils.handleRatelimit(bot, 'sendMessage', this);
|
||||||
|
utils.handleRatelimit(bot, 'sendGame', this);
|
||||||
|
return bot.sendPhoto(USERID, `${__dirname}/data/photo.gif`).then(resp => {
|
||||||
|
FILE_ID = resp.photo[0].file_id;
|
||||||
|
return bot.sendMessage(USERID, 'chat');
|
||||||
|
}).then(resp => {
|
||||||
|
GAME_CHAT_ID = resp.chat.id;
|
||||||
|
return bot.sendGame(USERID, GAME_SHORT_NAME);
|
||||||
|
}).then(resp => {
|
||||||
|
GAME_MSG_ID = resp.message_id;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('automatically starts polling', function test() {
|
||||||
|
assert.equal(botPolling.isPolling(), true);
|
||||||
|
return utils.isPollingMockServer(pollingPort2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('automatically opens webhook', function test() {
|
||||||
|
assert.equal(botWebHook.hasOpenWebHook(), true);
|
||||||
|
return utils.hasOpenWebHook(webHookPort2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not automatically poll if "autoStart" is false', function test() {
|
||||||
|
assert.equal(testbot.isPolling(), false);
|
||||||
|
return utils.isPollingMockServer(pollingPort, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not automatically open webhook if "autoOpen" is false', function test() {
|
||||||
|
assert.equal(testbot.hasOpenWebHook(), false);
|
||||||
|
return utils.hasOpenWebHook(webHookPort, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Events', function eventsSuite() {
|
||||||
|
it('(polling) emits "message" on receiving message', function test(done) {
|
||||||
|
botPolling.once('message', () => {
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('(webhook) emits "message" on receiving message', function test(done) {
|
||||||
|
botWebHook.once('message', () => {
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
utils.sendWebHookMessage(webHookPort2, TOKEN);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('WebHook', function webHookSuite() {
|
||||||
|
it('returns 401 error if token is wrong', function test(done) {
|
||||||
|
utils.sendWebHookMessage(webHookPort2, 'wrong-token').catch(resp => {
|
||||||
|
assert.equal(resp.statusCode, 401);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('only accepts POST method', function test() {
|
||||||
|
const methods = ['GET', 'PUT', 'DELETE', 'OPTIONS'];
|
||||||
|
return Promise.each(methods, (method) => {
|
||||||
|
return utils.sendWebHookMessage(webHookPort2, TOKEN, {
|
||||||
|
method,
|
||||||
|
}).then(() => {
|
||||||
|
throw new Error(`expected error with webhook ${method} request`);
|
||||||
|
}).catch(resp => {
|
||||||
|
if (!resp.statusCode) throw resp;
|
||||||
|
if (resp.statusCode !== 418) throw new Error(`unexpected error: ${resp.body}`);
|
||||||
|
});
|
||||||
|
}); // Promise.each
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#initPolling', function initPollingSuite() {
|
||||||
|
it('initiates polling', function test() {
|
||||||
|
testbot.initPolling();
|
||||||
|
return utils.isPollingMockServer(pollingPort);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#isPolling', function isPollingSuite() {
|
||||||
|
it('returns true if bot is polling', function test() {
|
||||||
|
assert.equal(testbot.isPolling(), true);
|
||||||
|
return utils.isPollingMockServer(pollingPort);
|
||||||
|
});
|
||||||
|
it('returns false if bot is not polling', function test() {
|
||||||
|
return testbot.stopPolling().then(() => {
|
||||||
|
assert.equal(testbot.isPolling(), false);
|
||||||
|
utils.clearPollingCheck(pollingPort);
|
||||||
|
return utils.isPollingMockServer(pollingPort, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
after(function after() {
|
||||||
|
return testbot.initPolling();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#stopPolling', function stopPollingSuite() {
|
||||||
|
it('stops polling by bot', function test() {
|
||||||
|
return testbot.stopPolling().then(() => {
|
||||||
|
utils.clearPollingCheck(pollingPort);
|
||||||
|
return utils.isPollingMockServer(pollingPort, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#openWebHook', function openWebHookSuite() {
|
||||||
|
it('opens webhook', function test() {
|
||||||
|
testbot.openWebHook();
|
||||||
|
return utils.hasOpenWebHook(webHookPort);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#hasOpenWebHook', function hasOpenWebHookSuite() {
|
||||||
|
it('returns true if webhook is opened', function test() {
|
||||||
|
assert.equal(testbot.hasOpenWebHook(), true);
|
||||||
|
return utils.hasOpenWebHook(webHookPort);
|
||||||
|
});
|
||||||
|
it('returns false if webhook is closed', function test() {
|
||||||
|
testbot.closeWebHook().then(() => {
|
||||||
|
assert.equal(testbot.hasOpenWebHook(), false);
|
||||||
|
return utils.hasOpenWebHook(webHookPort, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
after(function after() {
|
||||||
|
return testbot.openWebHook();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#closeWebHook', function closeWebHookSuite() {
|
||||||
|
it('closes webhook', function test() {
|
||||||
|
testbot.closeWebHook().then(() => {
|
||||||
|
return utils.hasOpenWebHook(webHookPort, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getMe', function getMeSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'getMe', this);
|
||||||
|
});
|
||||||
|
it('should return an User object', function test() {
|
||||||
|
return bot.getMe().then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.number(resp.id));
|
||||||
|
assert.ok(is.string(resp.first_name));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#setWebHook', function setWebHookSuite() {
|
||||||
|
const ip = '216.58.210.174';
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'setWebHook', this);
|
||||||
|
});
|
||||||
|
it('should set a webHook', function test() {
|
||||||
|
// Google IP ¯\_(ツ)_/¯
|
||||||
|
return bot
|
||||||
|
.setWebHook(ip)
|
||||||
|
.then(resp => {
|
||||||
|
assert.equal(resp, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should set a webHook with certificate', function test() {
|
||||||
|
const cert = `${__dirname}/../examples/crt.pem`;
|
||||||
|
return bot
|
||||||
|
.setWebHook(ip, cert)
|
||||||
|
.then(resp => {
|
||||||
|
assert.equal(resp, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should delete the webHook', function test() {
|
||||||
|
return bot
|
||||||
|
.setWebHook('')
|
||||||
|
.then(resp => {
|
||||||
|
assert.equal(resp, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getUpdates', function getUpdatesSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'getUpdates', this);
|
||||||
|
});
|
||||||
|
it('should return an Array', function test() {
|
||||||
|
return bot.getUpdates().then(resp => {
|
||||||
|
assert.equal(Array.isArray(resp), true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#sendMessage', function sendMessageSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendMessage', this);
|
||||||
|
});
|
||||||
|
it('should send a message', function test() {
|
||||||
|
return bot.sendMessage(USERID, 'test').then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.number(resp.message_id));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe.skip('#answerInlineQuery', function answerInlineQuerySuite() {});
|
||||||
|
|
||||||
|
describe('#forwardMessage', function forwardMessageSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendMessage', this);
|
||||||
|
utils.handleRatelimit(bot, 'forwardMessage', this);
|
||||||
|
});
|
||||||
|
it('should forward a message', function test() {
|
||||||
|
return bot.sendMessage(USERID, 'test').then(resp => {
|
||||||
|
const messageId = resp.message_id;
|
||||||
|
return bot.forwardMessage(USERID, USERID, messageId)
|
||||||
|
.then(forwarded => {
|
||||||
|
assert.ok(is.object(forwarded));
|
||||||
|
assert.ok(is.number(forwarded.message_id));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#sendPhoto', function sendPhotoSuite() {
|
||||||
|
let photoId;
|
||||||
|
this.timeout(timeout);
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendPhoto', this);
|
||||||
|
});
|
||||||
|
it('should send a photo from file', function test() {
|
||||||
|
const photo = `${__dirname}/data/photo.gif`;
|
||||||
|
return bot.sendPhoto(USERID, photo).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.array(resp.photo));
|
||||||
|
photoId = resp.photo[0].file_id;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a photo from id', function test() {
|
||||||
|
// Send the same photo as before
|
||||||
|
const photo = photoId;
|
||||||
|
return bot.sendPhoto(USERID, photo).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.array(resp.photo));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a photo from fs.readStream', function test() {
|
||||||
|
const photo = fs.createReadStream(`${__dirname}/data/photo.gif`);
|
||||||
|
return bot.sendPhoto(USERID, photo).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.array(resp.photo));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a photo from request Stream', function test() {
|
||||||
|
const photo = request(`${staticUrl}/photo.gif`);
|
||||||
|
return bot.sendPhoto(USERID, photo).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.array(resp.photo));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a photo from a Buffer', function test() {
|
||||||
|
const photo = fs.readFileSync(`${__dirname}/data/photo.gif`);
|
||||||
|
return bot.sendPhoto(USERID, photo).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.array(resp.photo));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#sendAudio', function sendAudioSuite() {
|
||||||
|
let audioId;
|
||||||
|
this.timeout(timeout);
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendAudio', this);
|
||||||
|
});
|
||||||
|
it('should send an MP3 audio', function test() {
|
||||||
|
const audio = `${__dirname}/data/audio.mp3`;
|
||||||
|
return bot.sendAudio(USERID, audio).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.audio));
|
||||||
|
audioId = resp.audio.file_id;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send an audio from id', function test() {
|
||||||
|
// Send the same audio as before
|
||||||
|
const audio = audioId;
|
||||||
|
return bot.sendAudio(USERID, audio).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.audio));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send an audio from fs.readStream', function test() {
|
||||||
|
const audio = fs.createReadStream(`${__dirname}/data/audio.mp3`);
|
||||||
|
return bot.sendAudio(USERID, audio).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.audio));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send an audio from request Stream', function test() {
|
||||||
|
const audio = request(`${staticUrl}/audio.mp3`);
|
||||||
|
return bot.sendAudio(USERID, audio).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.audio));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send an audio from a Buffer', function test() {
|
||||||
|
const audio = fs.readFileSync(`${__dirname}/data/audio.mp3`);
|
||||||
|
return bot.sendAudio(USERID, audio).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.audio));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#sendDocument', function sendDocumentSuite() {
|
||||||
|
let documentId;
|
||||||
|
this.timeout(timeout);
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendDocument', this);
|
||||||
|
});
|
||||||
|
it('should send a document from file', function test() {
|
||||||
|
const document = `${__dirname}/data/photo.gif`;
|
||||||
|
return bot.sendDocument(USERID, document).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.document));
|
||||||
|
documentId = resp.document.file_id;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a document from id', function test() {
|
||||||
|
// Send the same document as before
|
||||||
|
const document = documentId;
|
||||||
|
return bot.sendDocument(USERID, document).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.document));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a document from fs.readStream', function test() {
|
||||||
|
const document = fs.createReadStream(`${__dirname}/data/photo.gif`);
|
||||||
|
return bot.sendDocument(USERID, document).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.document));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a document from request Stream', function test() {
|
||||||
|
const document = request(`${staticUrl}/photo.gif`);
|
||||||
|
return bot.sendDocument(USERID, document).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.document));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a document from a Buffer', function test() {
|
||||||
|
const document = fs.readFileSync(`${__dirname}/data/photo.gif`);
|
||||||
|
return bot.sendDocument(USERID, document).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.document));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#sendSticker', function sendStickerSuite() {
|
||||||
|
let stickerId;
|
||||||
|
this.timeout(timeout);
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendSticker', this);
|
||||||
|
});
|
||||||
|
it('should send a sticker from file', function test() {
|
||||||
|
const sticker = `${__dirname}/data/sticker.webp`;
|
||||||
|
return bot.sendSticker(USERID, sticker).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.sticker));
|
||||||
|
stickerId = resp.sticker.file_id;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a sticker from id', function test() {
|
||||||
|
// Send the same photo as before
|
||||||
|
return bot.sendSticker(USERID, stickerId).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.sticker));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a sticker from fs.readStream', function test() {
|
||||||
|
const sticker = fs.createReadStream(`${__dirname}/data/sticker.webp`);
|
||||||
|
return bot.sendSticker(USERID, sticker).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.sticker));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a sticker from request Stream', function test() {
|
||||||
|
const sticker = request(`${staticUrl}/sticker.webp`);
|
||||||
|
return bot.sendSticker(USERID, sticker).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.sticker));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a sticker from a Buffer', function test() {
|
||||||
|
const sticker = fs.readFileSync(`${__dirname}/data/sticker.webp`);
|
||||||
|
return bot.sendSticker(USERID, sticker).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.sticker));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#sendVideo', function sendVideoSuite() {
|
||||||
|
let videoId;
|
||||||
|
this.timeout(timeout);
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendVideo', this);
|
||||||
|
});
|
||||||
|
it('should send a video from file', function test() {
|
||||||
|
const video = `${__dirname}/data/video.mp4`;
|
||||||
|
return bot.sendVideo(USERID, video).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.video));
|
||||||
|
videoId = resp.video.file_id;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a video from id', function test() {
|
||||||
|
// Send the same video as before
|
||||||
|
return bot.sendVideo(USERID, videoId).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.video));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a video from fs.readStream', function test() {
|
||||||
|
const video = fs.createReadStream(`${__dirname}/data/video.mp4`);
|
||||||
|
return bot.sendVideo(USERID, video).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.video));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a video from request Stream', function test() {
|
||||||
|
const video = request(`${staticUrl}/video.mp4`);
|
||||||
|
return bot.sendVideo(USERID, video).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.video));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a video from a Buffer', function test() {
|
||||||
|
const video = fs.readFileSync(`${__dirname}/data/video.mp4`);
|
||||||
|
return bot.sendVideo(USERID, video).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.video));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#sendVoice', function sendVoiceSuite() {
|
||||||
|
let voiceId;
|
||||||
|
this.timeout(timeout);
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendVoice', this);
|
||||||
|
});
|
||||||
|
it('should send a voice from file', function test() {
|
||||||
|
const voice = `${__dirname}/data/voice.ogg`;
|
||||||
|
return bot.sendVoice(USERID, voice).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.voice));
|
||||||
|
voiceId = resp.voice.file_id;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a voice from id', function test() {
|
||||||
|
// Send the same voice as before
|
||||||
|
return bot.sendVoice(USERID, voiceId).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.voice));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a voice from fs.readStream', function test() {
|
||||||
|
const voice = fs.createReadStream(`${__dirname}/data/voice.ogg`);
|
||||||
|
return bot.sendVoice(USERID, voice).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.voice));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a voice from request Stream', function test() {
|
||||||
|
const voice = request(`${staticUrl}/voice.ogg`);
|
||||||
|
return bot.sendVoice(USERID, voice).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.voice));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send a voice from a Buffer', function test() {
|
||||||
|
const voice = fs.readFileSync(`${__dirname}/data/voice.ogg`);
|
||||||
|
return bot.sendVoice(USERID, voice).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.voice));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#sendChatAction', function sendChatActionSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendChatAction', this);
|
||||||
|
});
|
||||||
|
it('should send a chat action', function test() {
|
||||||
|
const action = 'typing';
|
||||||
|
return bot.sendChatAction(USERID, action).then(resp => {
|
||||||
|
assert.equal(resp, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe.skip('#kickChatMember', function kickChatMemberSuite() {});
|
||||||
|
|
||||||
|
describe.skip('#unbanChatMember', function unbanChatMemberSuite() {});
|
||||||
|
|
||||||
|
describe.skip('#answerCallbackQuery', function answerCallbackQuerySuite() {});
|
||||||
|
|
||||||
|
describe('#editMessageText', function editMessageTextSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendMessage', this);
|
||||||
|
utils.handleRatelimit(bot, 'editMessageText', this);
|
||||||
|
});
|
||||||
|
it('should edit a message sent by the bot', function test() {
|
||||||
|
return bot.sendMessage(USERID, 'test').then(resp => {
|
||||||
|
assert.equal(resp.text, 'test');
|
||||||
|
const opts = {
|
||||||
|
chat_id: USERID,
|
||||||
|
message_id: resp.message_id
|
||||||
|
};
|
||||||
|
return bot.editMessageText('edit test', opts).then(msg => {
|
||||||
|
assert.equal(msg.text, 'edit test');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#editMessageCaption', function editMessageCaptionSuite() {
|
||||||
|
this.timeout(timeout);
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendPhoto', this);
|
||||||
|
utils.handleRatelimit(bot, 'editMessageCaption', this);
|
||||||
|
});
|
||||||
|
it('should edit a caption sent by the bot', function test() {
|
||||||
|
const photo = `${__dirname}/data/photo.gif`;
|
||||||
|
const options = { caption: 'test caption' };
|
||||||
|
return bot.sendPhoto(USERID, photo, options).then(resp => {
|
||||||
|
assert.equal(resp.caption, 'test caption');
|
||||||
|
const opts = {
|
||||||
|
chat_id: USERID,
|
||||||
|
message_id: resp.message_id
|
||||||
|
};
|
||||||
|
return bot.editMessageCaption('new test caption', opts).then(msg => {
|
||||||
|
assert.equal(msg.caption, 'new test caption');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#editMessageReplyMarkup', function editMessageReplyMarkupSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendMessage', this);
|
||||||
|
utils.handleRatelimit(bot, 'editMessageReplyMarkup', this);
|
||||||
|
});
|
||||||
|
it('should edit previously-set reply markup', function test() {
|
||||||
|
return bot.sendMessage(USERID, 'test').then(resp => {
|
||||||
|
const replyMarkup = JSON.stringify({
|
||||||
|
inline_keyboard: [[{
|
||||||
|
text: 'Test button',
|
||||||
|
callback_data: 'test'
|
||||||
|
}]]
|
||||||
|
});
|
||||||
|
const opts = {
|
||||||
|
chat_id: USERID,
|
||||||
|
message_id: resp.message_id
|
||||||
|
};
|
||||||
|
return bot.editMessageReplyMarkup(replyMarkup, opts).then(msg => {
|
||||||
|
// Keyboard markup is not returned, do a simple object check
|
||||||
|
assert.ok(is.object(msg));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getUserProfilePhotos', function getUserProfilePhotosSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'getUserProfilePhotos', this);
|
||||||
|
});
|
||||||
|
it('should get user profile photos', function test() {
|
||||||
|
return bot.getUserProfilePhotos(USERID).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.number(resp.total_count));
|
||||||
|
assert.ok(is.array(resp.photos));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#sendLocation', function sendLocationSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendLocation', this);
|
||||||
|
});
|
||||||
|
it('should send a location', function test() {
|
||||||
|
const lat = 47.5351072;
|
||||||
|
const long = -52.7508537;
|
||||||
|
return bot.sendLocation(USERID, lat, long).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.location));
|
||||||
|
assert.ok(is.number(resp.location.latitude));
|
||||||
|
assert.ok(is.number(resp.location.longitude));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#sendVenue', function sendVenueSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendVenue', this);
|
||||||
|
});
|
||||||
|
it('should send a venue', function test() {
|
||||||
|
const lat = 47.5351072;
|
||||||
|
const long = -52.7508537;
|
||||||
|
const title = 'The Village Shopping Centre';
|
||||||
|
const address = '430 Topsail Rd,St. John\'s, NL A1E 4N1, Canada';
|
||||||
|
return bot.sendVenue(USERID, lat, long, title, address).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.venue));
|
||||||
|
assert.ok(is.object(resp.venue.location));
|
||||||
|
assert.ok(is.number(resp.venue.location.latitude));
|
||||||
|
assert.ok(is.number(resp.venue.location.longitude));
|
||||||
|
assert.ok(is.string(resp.venue.title));
|
||||||
|
assert.ok(is.string(resp.venue.address));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// NOTE: We are skipping TelegramBot#sendContact() as the
|
||||||
|
// corresponding rate-limits enforced by the Telegram servers
|
||||||
|
// are too strict! During our initial tests, we were required
|
||||||
|
// to retry after ~72000 secs (1200 mins / 20 hrs).
|
||||||
|
// We surely can NOT wait for that much time during testing
|
||||||
|
// (or in most practical cases for that matter!)
|
||||||
|
describe.skip('#sendContact', function sendContactSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendContact', this);
|
||||||
|
});
|
||||||
|
it('should send a contact', function test() {
|
||||||
|
const phoneNumber = '+1(000)000-000';
|
||||||
|
const firstName = 'John Doe';
|
||||||
|
return bot.sendContact(USERID, phoneNumber, firstName).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.contact));
|
||||||
|
assert.ok(is.string(resp.contact.phone_number));
|
||||||
|
assert.ok(is.string(resp.contact.first_name));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getFile', function getFileSuite() {
|
||||||
|
this.timeout(timeout);
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'getFile', this);
|
||||||
|
});
|
||||||
|
it('should get a file', function test() {
|
||||||
|
return bot.getFile(FILE_ID)
|
||||||
|
.then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.string(resp.file_path));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getFileLink', function getFileLinkSuite() {
|
||||||
|
this.timeout(timeout);
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'getFileLink', this);
|
||||||
|
});
|
||||||
|
it('should get a file link', function test() {
|
||||||
|
return bot.getFileLink(FILE_ID)
|
||||||
|
.then(fileURI => {
|
||||||
|
assert.ok(is.string(fileURI));
|
||||||
|
assert.ok(/https?:\/\/.*\/file\/bot.*\/.*/.test(fileURI));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#downloadFile', function downloadFileSuite() {
|
||||||
|
const downloadPath = os.tmpdir();
|
||||||
|
this.timeout(timeout);
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'downloadFile', this);
|
||||||
|
});
|
||||||
|
it('should download a file', function test() {
|
||||||
|
return bot.downloadFile(FILE_ID, downloadPath)
|
||||||
|
.then(filePath => {
|
||||||
|
assert.ok(is.string(filePath));
|
||||||
|
assert.equal(path.dirname(filePath), downloadPath);
|
||||||
|
assert.ok(fs.existsSync(filePath));
|
||||||
|
fs.unlinkSync(filePath); // Delete file after test
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#onText', function onTextSuite() {
|
||||||
|
it('should call `onText` callback on match', function test(done) {
|
||||||
|
botWebHook.onText(/\/onText (.+)/, (msg, match) => {
|
||||||
|
assert.equal(match[1], 'ECHO ALOHA');
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
utils.sendWebHookMessage(webHookPort2, TOKEN, {
|
||||||
|
message: { text: '/onText ECHO ALOHA' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe.skip('#onReplyToMessage', function onReplyToMessageSuite() {});
|
||||||
|
|
||||||
|
describe('#getChat', function getChatSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'getChat', this);
|
||||||
|
});
|
||||||
|
it('should return a Chat object', function test() {
|
||||||
|
return bot.getChat(USERID).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getChatAdministrators', function getChatAdministratorsSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'getChatAdministrators', this);
|
||||||
|
});
|
||||||
|
it('should return an Array', function test() {
|
||||||
|
return bot.getChatAdministrators(GROUPID).then(resp => {
|
||||||
|
assert.ok(Array.isArray(resp));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getChatMembersCount', function getChatMembersCountSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'getChatMembersCount', this);
|
||||||
|
});
|
||||||
|
it('should return an Integer', function test() {
|
||||||
|
return bot.getChatMembersCount(GROUPID).then(resp => {
|
||||||
|
assert.ok(Number.isInteger(resp));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getChatMember', function getChatMemberSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'getChatMember', this);
|
||||||
|
});
|
||||||
|
it('should return a ChatMember', function test() {
|
||||||
|
return bot.getChatMember(GROUPID, USERID).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.user));
|
||||||
|
assert.ok(is.string(resp.status));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe.skip('#leaveChat', function leaveChatSuite() {});
|
||||||
|
|
||||||
|
describe('#sendGame', function sendGameSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'sendGame', this);
|
||||||
|
});
|
||||||
|
it('should send a Game', function test() {
|
||||||
|
return bot.sendGame(USERID, GAME_SHORT_NAME).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.object(resp.game));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#setGameScore', function setGameScoreSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'setGameScore', this);
|
||||||
|
});
|
||||||
|
it('should set GameScore', function test() {
|
||||||
|
const score = Math.floor(Math.random() * 1000);
|
||||||
|
const opts = {
|
||||||
|
chat_id: GAME_CHAT_ID,
|
||||||
|
message_id: GAME_MSG_ID,
|
||||||
|
force: true
|
||||||
|
};
|
||||||
|
return bot.setGameScore(USERID, score, opts).then(resp => {
|
||||||
|
assert.ok(is.object(resp) || is.boolean(resp));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getGameHighScores', function getGameHighScoresSuite() {
|
||||||
|
before(function before() {
|
||||||
|
utils.handleRatelimit(bot, 'getGameHighScores', this);
|
||||||
|
});
|
||||||
|
it('should get GameHighScores', function test() {
|
||||||
|
const opts = {
|
||||||
|
chat_id: GAME_CHAT_ID,
|
||||||
|
message_id: GAME_MSG_ID,
|
||||||
|
};
|
||||||
|
return bot.getGameHighScores(USERID, opts).then(resp => {
|
||||||
|
assert.ok(is.array(resp));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#_formatSendData', function _formatSendDataSuite() {
|
||||||
|
it('should handle buffer path from fs.readStream', function test() {
|
||||||
|
let photo;
|
||||||
|
try {
|
||||||
|
photo = fs.createReadStream(Buffer.from(`${__dirname}/data/photo.gif`));
|
||||||
|
} catch (ex) {
|
||||||
|
// Older Node.js versions do not support passing a Buffer
|
||||||
|
// representation of the path to fs.createReadStream()
|
||||||
|
if (ex instanceof TypeError) return Promise.resolve();
|
||||||
|
}
|
||||||
|
return bot.sendPhoto(USERID, photo).then(resp => {
|
||||||
|
assert.ok(is.object(resp));
|
||||||
|
assert.ok(is.array(resp.photo));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}); // End Telegram
|
190
test/utils.js
Normal file
190
test/utils.js
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
/* eslint-disable no-use-before-define */
|
||||||
|
exports = module.exports = {
|
||||||
|
/**
|
||||||
|
* Clear polling check, so that 'isPollingMockServer()' returns false
|
||||||
|
* if the bot stopped polling the mock server.
|
||||||
|
* @param {Number} port
|
||||||
|
*/
|
||||||
|
clearPollingCheck,
|
||||||
|
/**
|
||||||
|
* Redefine a bot method to allow us to ignore 429 (rate-limit) errors
|
||||||
|
* @param {TelegramBot} bot
|
||||||
|
* @param {String} methodName
|
||||||
|
* @param {Suite} suite From mocha
|
||||||
|
* @return {TelegramBot}
|
||||||
|
*/
|
||||||
|
handleRatelimit,
|
||||||
|
/**
|
||||||
|
* Return true if a webhook has been opened at the specified port.
|
||||||
|
* Otherwise throw an error.
|
||||||
|
* @param {Number} port
|
||||||
|
* @param {Boolean} [reverse] Throw error when it should have returned true (and vice versa)
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
hasOpenWebHook,
|
||||||
|
/**
|
||||||
|
* Return true if the mock server is being polled by a bot.
|
||||||
|
* Otherwise throw an error.
|
||||||
|
* @param {Number} port
|
||||||
|
* @param {Boolean} [reverse] Throw error when it should have returned true (and vice versa)
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
isPollingMockServer,
|
||||||
|
/**
|
||||||
|
* Send a message to the webhook at the specified port.
|
||||||
|
* @param {Number} port
|
||||||
|
* @param {String} token
|
||||||
|
* @param {Object} [options]
|
||||||
|
* @param {String} [options.method=POST] Method to use
|
||||||
|
* @param {Object} [options.message] Message to send. Default to a generic text message
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
sendWebHookMessage,
|
||||||
|
/**
|
||||||
|
* Start a mock server at the specified port.
|
||||||
|
* @param {Number} port
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
startMockServer,
|
||||||
|
/**
|
||||||
|
* Start the static server, serving files in './data'
|
||||||
|
* @param {Number} port
|
||||||
|
*/
|
||||||
|
startStaticServer,
|
||||||
|
};
|
||||||
|
/* eslint-enable no-use-before-define */
|
||||||
|
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const http = require('http');
|
||||||
|
const Promise = require('bluebird');
|
||||||
|
const request = require('request-promise');
|
||||||
|
const statics = require('node-static');
|
||||||
|
|
||||||
|
const servers = {};
|
||||||
|
|
||||||
|
|
||||||
|
function startMockServer(port) {
|
||||||
|
assert.ok(port);
|
||||||
|
const server = http.Server((req, res) => {
|
||||||
|
servers[port].polling = true;
|
||||||
|
return res.end(JSON.stringify({
|
||||||
|
ok: true,
|
||||||
|
result: [{
|
||||||
|
update_id: 0,
|
||||||
|
message: { text: 'test' },
|
||||||
|
}],
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
servers[port] = { server, polling: false };
|
||||||
|
server.on('error', reject).listen(port, resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function startStaticServer(port) {
|
||||||
|
const fileServer = new statics.Server(`${__dirname}/data`);
|
||||||
|
http.Server((req, res) => {
|
||||||
|
req.addListener('end', () => {
|
||||||
|
fileServer.serve(req, res);
|
||||||
|
}).resume();
|
||||||
|
}).listen(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function isPollingMockServer(port, reverse) {
|
||||||
|
assert.ok(port);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// process.nextTick() does not wait until a poll request
|
||||||
|
// is complete!
|
||||||
|
setTimeout(() => {
|
||||||
|
let polling = servers[port] && servers[port].polling;
|
||||||
|
if (reverse) polling = !polling;
|
||||||
|
if (polling) return resolve(true);
|
||||||
|
return reject(new Error('polling-check failed'));
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function clearPollingCheck(port) {
|
||||||
|
assert.ok(port);
|
||||||
|
if (servers[port]) servers[port].polling = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function hasOpenWebHook(port, reverse) {
|
||||||
|
assert.ok(port);
|
||||||
|
const error = new Error('open-webhook-check failed');
|
||||||
|
let connected = false;
|
||||||
|
return request.get(`http://127.0.0.1:${port}`)
|
||||||
|
.then(() => {
|
||||||
|
connected = true;
|
||||||
|
}).catch(e => {
|
||||||
|
if (e.statusCode < 500) connected = true;
|
||||||
|
}).finally(() => {
|
||||||
|
if (reverse) {
|
||||||
|
if (connected) throw error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!connected) throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function sendWebHookMessage(port, token, options = {}) {
|
||||||
|
assert.ok(port);
|
||||||
|
assert.ok(token);
|
||||||
|
const url = `http://127.0.0.1:${port}/bot${token}`;
|
||||||
|
return request({
|
||||||
|
url,
|
||||||
|
method: options.method || 'POST',
|
||||||
|
body: {
|
||||||
|
update_id: 1,
|
||||||
|
message: options.message || { text: 'test' }
|
||||||
|
},
|
||||||
|
json: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleRatelimit(bot, methodName, suite) {
|
||||||
|
const backupMethodName = `__${methodName}`;
|
||||||
|
if (!bot[backupMethodName]) bot[backupMethodName] = bot[methodName];
|
||||||
|
|
||||||
|
const maxRetries = 3;
|
||||||
|
const addSecs = 5;
|
||||||
|
const method = bot[backupMethodName];
|
||||||
|
assert.equal(typeof method, 'function');
|
||||||
|
|
||||||
|
bot[methodName] = (...args) => {
|
||||||
|
let retry = 0;
|
||||||
|
function exec() {
|
||||||
|
return method.call(bot, ...args)
|
||||||
|
.catch(error => {
|
||||||
|
if (!error.response || error.response.statusCode !== 429) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
retry++;
|
||||||
|
if (retry > maxRetries) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
if (typeof error.response.body === 'string') {
|
||||||
|
error.response.body = JSON.parse(error.response.body);
|
||||||
|
}
|
||||||
|
const retrySecs = error.response.body.parameters.retry_after;
|
||||||
|
const timeout = (1000 * retrySecs) + (1000 * addSecs);
|
||||||
|
console.error('tests: Handling rate-limit error. Retrying after %d secs', timeout / 1000); // eslint-disable-line no-console
|
||||||
|
suite.timeout(timeout * 2);
|
||||||
|
return new Promise(function timeoutPromise(resolve, reject) {
|
||||||
|
setTimeout(function execTimeout() {
|
||||||
|
return exec().then(resolve).catch(reject);
|
||||||
|
}, timeout);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return exec();
|
||||||
|
};
|
||||||
|
return bot;
|
||||||
|
}
|
BIN
test/video.mp4
BIN
test/video.mp4
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user