From 83e42201ee78b383c48f220259a2ee3ae037795c Mon Sep 17 00:00:00 2001 From: AVVS Date: Thu, 17 Mar 2016 02:44:34 +0300 Subject: [PATCH] refactor: rewrite to babel, es6 & eslint --- .babelrc | 11 + .editorconfig | 13 + .eslintignore | 2 + .eslintrc | 18 + .gitignore | 1 + .jshintrc | 11 - .travis.yml | 5 +- package.json | 31 +- src/telegram.js | 1046 ++++++++++++++++++++-------------------- src/telegramPolling.js | 183 +++---- src/telegramWebHook.js | 134 ++--- test/.jshintrc | 12 - test/index.js | 613 +++++++++++------------ test/mocha.opts | 1 + 14 files changed, 1062 insertions(+), 1019 deletions(-) create mode 100644 .babelrc create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc delete mode 100644 .jshintrc delete mode 100644 test/.jshintrc create mode 100644 test/mocha.opts diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..d9fd222 --- /dev/null +++ b/.babelrc @@ -0,0 +1,11 @@ +{ + "plugins": [ + "transform-es2015-spread", + "transform-es2015-destructuring", + "transform-strict-mode", + "transform-es2015-parameters", + "transform-es2015-shorthand-properties", + "transform-object-rest-spread", + "transform-class-properties" + ] +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e717f5e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..417342d --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +bin +*.md diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..1c4aa24 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,18 @@ +{ + "extends": "airbnb/base", + "parser": "babel-eslint", + "rules": { + "new-cap": 0, + "prefer-arrow-callback": 0, + "no-param-reassign": [2,{"props":false}], + "max-len": [2, 150], + "arrow-body-style": 0, + "comma-dangle": 0 + }, + "plugins": [ + "mocha" + ], + "env": { + "mocha": true + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6898e47..875ff98 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ npm-debug.log .package.json output.md output/ +lib/ \ No newline at end of file diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index cb72458..0000000 --- a/.jshintrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "curly" : true, - "eqeqeq" : true, - "indent" : 2, - "latedef" : true, - "newcap" : true, - "quotmark": "single", - "strict" : true, - "undef" : true, - "node" : true -} diff --git a/.travis.yml b/.travis.yml index 68fe765..4ef24e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ language: node_js node_js: - - "stable" - - "0.12" - - "0.10" + - "5" + - "4" after_success: - npm run test-cov cache: diff --git a/package.json b/package.json index db02329..8d7b724 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "node-telegram-bot-api", "version": "0.21.1", "description": "Telegram Bot API", - "main": "index.js", + "main": "./lib/telegram.js", "directories": { "example": "examples", "test": "test" @@ -14,29 +14,46 @@ "bot" ], "scripts": { + "prepublish": "./node_modules/.bin/babel -d ./lib src", "test": "./node_modules/.bin/mocha test/index.js", "test-cov": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage", "gen-doc": "./node_modules/.bin/jsdoc2md --src src/telegram.js -t README.hbs > README.md", - "jshint": "./node_modules/.bin/jshint src test" + "eslint": "./node_modules/.bin/eslint ./src" }, "author": "Yago Pérez ", "license": "MIT", "dependencies": { - "bluebird": "^2.10.2", + "bl": "^1.1.2", + "bluebird": "^3.3.4", "debug": "^2.2.0", + "eventemitter3": "^1.2.0", "file-type": "^3.8.0", "mime": "^1.3.4", - "request": "^2.69.0" + "pump": "^1.0.1", + "request": "^2.69.0", + "request-promise": "^2.0.1" }, "devDependencies": { + "babel-cli": "^6.6.5", + "babel-eslint": "^5.0.0", + "babel-plugin-transform-class-properties": "^6.6.0", + "babel-plugin-transform-es2015-destructuring": "^6.6.5", + "babel-plugin-transform-es2015-parameters": "^6.7.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.5.0", + "babel-plugin-transform-es2015-spread": "^6.6.5", + "babel-plugin-transform-object-rest-spread": "^6.6.5", + "babel-plugin-transform-strict-mode": "^6.6.5", + "babel-register": "^6.7.2", "contributor": "^0.1.25", "coveralls": "^2.11.8", + "eslint": "^2.4.0", + "eslint-config-airbnb": "^6.1.0", + "eslint-plugin-mocha": "^2.0.0", + "is": "^3.1.0", "istanbul": "^0.4.2", "jsdoc-to-markdown": "^1.3.3", - "jshint": "^2.9.1", "mocha": "^2.4.5", - "mocha-lcov-reporter": "^1.2.0", - "should": "^8.2.2" + "mocha-lcov-reporter": "^1.2.0" }, "repository": { "type": "git", diff --git a/src/telegram.js b/src/telegram.js index ce4e937..f2c8568 100644 --- a/src/telegram.js +++ b/src/telegram.js @@ -1,21 +1,18 @@ -'use strict'; - -var TelegramBotWebHook = require('./telegramWebHook'); -var TelegramBotPolling = require('./telegramPolling'); -var debug = require('debug')('node-telegram-bot-api'); -var EventEmitter = require('events').EventEmitter; -var fileType = require('file-type'); -var Promise = require('bluebird'); -var request = require('request'); -var qs = require('querystring'); -var stream = require('stream'); -var util = require('util'); -var mime = require('mime'); -var path = require('path'); -var URL = require('url'); -var fs = require('fs'); - -var requestPromise = Promise.promisify(request); +const TelegramBotWebHook = require('./telegramWebHook'); +const TelegramBotPolling = require('./telegramPolling'); +const debug = require('debug')('node-telegram-bot-api'); +const EventEmitter = require('eventemitter3'); +const fileType = require('file-type'); +const Promise = require('bluebird'); +const request = require('request-promise'); +const streamedRequest = require('request'); +const qs = require('querystring'); +const stream = require('stream'); +const mime = require('mime'); +const path = require('path'); +const URL = require('url'); +const fs = require('fs'); +const pump = require('pump'); /** * Both request method to obtain messages are implemented. To use standard polling, set `polling: true` @@ -34,547 +31,550 @@ var requestPromise = Promise.promisify(request); * @param {String} [options.webHook.cert] PEM certificate (public) to webHook server. * @see https://core.telegram.org/bots/api */ -var TelegramBot = function (token, options) { - options = options || {}; - this.options = options; - this.token = token; - this.messageTypes = [ +class TelegramBot extends EventEmitter { + + // Telegram message events + static messageTypes = [ 'text', 'audio', 'document', 'photo', 'sticker', 'video', 'voice', 'contact', 'location', 'new_chat_participant', 'left_chat_participant', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created' - ]; // Telegram message events - this.textRegexpCallbacks = []; - this.onReplyToMessages = []; + ]; - this.processUpdate = this._processUpdate.bind(this); + constructor(token, options = {}) { + super(); + this.options = options; + this.token = token; + this.textRegexpCallbacks = []; + this.onReplyToMessages = []; - if (options.polling) { - this.initPolling(); - } - - if (options.webHook) { - this._WebHook = new TelegramBotWebHook(token, options.webHook, this.processUpdate); - } -}; - -util.inherits(TelegramBot, EventEmitter); - -TelegramBot.prototype.initPolling = function() { - if (this._polling) { - this._polling.abort = true; - this._polling.lastRequest.cancel('Polling restart'); - } - this._polling = new TelegramBotPolling(this.token, this.options.polling, this.processUpdate); -}; - -TelegramBot.prototype._processUpdate = function (update) { - debug('Process Update %j', update); - var message = update.message; - var inline_query = update.inline_query; - var chosen_inline_result = update.chosen_inline_result; - - if (message) { - debug('Process Update message %j', message); - this.emit('message', message); - var processMessageType = function (messageType) { - if (message[messageType]) { - debug('Emtting %s: %j', messageType, message); - this.emit(messageType, message); - } - }; - this.messageTypes.forEach(processMessageType.bind(this)); - if (message.text) { - debug('Text message'); - this.textRegexpCallbacks.forEach(function (reg) { - debug('Matching %s whith', message.text, reg.regexp); - var result = reg.regexp.exec(message.text); - if (result) { - debug('Matches', reg.regexp); - reg.callback(message, result); - } - }); + if (options.polling) { + this.initPolling(); } - if (message.reply_to_message) { - // Only callbacks waiting for this message - this.onReplyToMessages.forEach(function (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); + + if (options.webHook) { + this._WebHook = new TelegramBotWebHook(token, options.webHook, this.processUpdate); + } + } + + 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); + } + + processUpdate = (update) => { + debug('Process Update %j', update); + const message = update.message; + const inlineQuery = update.inline_query; + const chosenInlineResult = update.chosen_inline_result; + + 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.forEach(reg => { + debug('Matching %s whith', message.text, reg.regexp); + const result = reg.regexp.exec(message.text); + if (result) { + debug('Matches', reg.regexp); + reg.callback(message, result); } + }); + } + 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 (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); + } + } + + // 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)}`); + } + } + + // request-promise + _request(_path, options = {}) { + if (!this.token) { + throw new Error('Telegram Bot Token not provided!'); + } + + if (options.form) { + const replyMarkup = options.form.reply_markup; + if (replyMarkup && typeof replyMarkup !== 'string') { + // reply_markup must be passed as JSON stringified to Telegram + options.form.reply_markup = JSON.stringify(replyMarkup); + } + } + options.url = this._buildURL(_path); + options.simple = false; + options.resolveWithFullResponse = true; + debug('HTTP request: %j', options); + return request(options) + .then(resp => { + 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}`); }); - } - } else if (inline_query) { - debug('Process Update inline_query %j', inline_query); - this.emit('inline_query', inline_query); - } else if (chosen_inline_result) { - debug('Process Update chosen_inline_result %j', chosen_inline_result); - this.emit('chosen_inline_result', chosen_inline_result); } -}; -TelegramBot.prototype._request = function (path, options) { - if (!this.token) { - throw new Error('Telegram Bot Token not provided!'); - } - options = options || {}; - if (options.form) { - var replyMarkup = options.form.reply_markup; - if (replyMarkup && typeof replyMarkup !== 'string') { - // reply_markup must be passed as JSON stringified to Telegram - options.form.reply_markup = JSON.stringify(replyMarkup); - } - } - options.url = this._buildURL(path); - debug('HTTP request: %j', options); - return requestPromise(options) - .then(function (resp) { - if (resp[0].statusCode !== 200) { - throw new Error(resp[0].statusCode+' '+resp[0].body); - } - var data; - try { - data = JSON.parse(resp[0].body); - } catch (err) { - throw new Error('Error parsing Telegram response: %s', resp[0].body); - } - if (data.ok) { - return data.result; - } else { - throw new Error(data.error_code+' '+data.description); - } + /** + * Generates url with bot token and provided path/method you want to be got/executed by bot + * @return {String} url + * @param {String} path + * @private + * @see https://core.telegram.org/bots/api#making-requests + */ + _buildURL(_path) { + return URL.format({ + protocol: 'https', + host: 'api.telegram.org', + pathname: `/bot${this.token}/${_path}` }); -}; + } -/** - * Generates url with bot token and provided path/method you want to be got/executed by bot - * @return {String} url - * @param {String} path - * @private - * @see https://core.telegram.org/bots/api#making-requests - */ -TelegramBot.prototype._buildURL = function(path) { - return URL.format({ - protocol: 'https', - host: 'api.telegram.org', - pathname: '/bot' + this.token + '/' + path - }); -}; + /** + * Returns basic information about the bot in form of a `User` object. + * @return {Promise} + * @see https://core.telegram.org/bots/api#getme + */ + getMe() { + const _path = 'getMe'; + return this._request(_path); + } -/** - * Returns basic information about the bot in form of a `User` object. - * @return {Promise} - * @see https://core.telegram.org/bots/api#getme - */ -TelegramBot.prototype.getMe = function () { - var path = 'getMe'; - return this._request(path); -}; + /** + * Specify an url to receive incoming updates via an outgoing webHook. + * @param {String} url URL where Telegram will make HTTP Post. Leave empty to + * delete webHook. + * @param {String|stream.Stream} [cert] PEM certificate key (public). + * @see https://core.telegram.org/bots/api#setwebhook + */ + setWebHook(url, cert) { + const _path = 'setWebHook'; + const opts = { qs: { url } }; -/** - * Specify an url to receive incoming updates via an outgoing webHook. - * @param {String} url URL where Telegram will make HTTP Post. Leave empty to - * delete webHook. - * @param {String|stream.Stream} [cert] PEM certificate key (public). - * @see https://core.telegram.org/bots/api#setwebhook - */ -TelegramBot.prototype.setWebHook = function (url, cert) { - var path = 'setWebHook'; - var opts = { - qs: {url: url} - }; + if (cert) { + const [formData, certificate] = this._formatSendData('certificate', cert); + opts.formData = formData; + opts.qs.certificate = certificate; + } - if (cert) { - var content = this._formatSendData('certificate', cert); + return this._request(_path, opts) + .then(resp => { + if (!resp) { + throw new Error(resp); + } + + return resp; + }); + } + + /** + * Use this method to receive incoming updates using long polling + * @param {Number|String} [timeout] Timeout in seconds for long polling. + * @param {Number|String} [limit] Limits the number of updates to be retrieved. + * @param {Number|String} [offset] Identifier of the first update to be returned. + * @return {Promise} Updates + * @see https://core.telegram.org/bots/api#getupdates + */ + getUpdates(timeout, limit, offset) { + const form = { + offset, + limit, + timeout, + }; + + return this._request('getUpdates', { form }); + } + + /** + * Send text message. + * @param {Number|String} chatId Unique identifier for the message recipient + * @param {String} text Text of the message to be sent + * @param {Object} [options] Additional Telegram query options + * @return {Promise} + * @see https://core.telegram.org/bots/api#sendmessage + */ + sendMessage(chatId, text, form = {}) { + form.chat_id = chatId; + form.text = text; + return this._request('sendMessage', { form }); + } + + /** + * Send answers to an inline query. + * @param {String} inlineQueryId Unique identifier of the query + * @param {InlineQueryResult[]} results An array of results for the inline query + * @param {Object} [options] Additional Telegram query options + * @return {Promise} + * @see https://core.telegram.org/bots/api#answerinlinequery + */ + answerInlineQuery(inlineQueryId, results, form = {}) { + form.inline_query_id = inlineQueryId; + form.results = JSON.stringify(results); + return this._request('answerInlineQuery', { form }); + } + + /** + * Forward messages of any kind. + * @param {Number|String} chatId Unique identifier for the message recipient + * @param {Number|String} fromChatId Unique identifier for the chat where the + * original message was sent + * @param {Number|String} messageId Unique message identifier + * @return {Promise} + */ + forwardMessage(chatId, fromChatId, messageId) { + const form = { + chat_id: chatId, + from_chat_id: fromChatId, + message_id: messageId + }; + + 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)).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 + * @param {Number|String} chatId Unique identifier for the message recipient + * @param {String|stream.Stream|Buffer} photo A file path or a Stream. Can + * also be a `file_id` previously uploaded + * @param {Object} [options] Additional Telegram query options + * @return {Promise} + * @see https://core.telegram.org/bots/api#sendphoto + */ + sendPhoto(chatId, photo, options = {}) { + const opts = { + qs: options, + }; + opts.qs.chat_id = chatId; + const content = this._formatSendData('photo', photo); opts.formData = content[0]; - opts.qs.certificate = content[1]; + opts.qs.photo = content[1]; + return this._request('sendPhoto', opts); } - return this._request(path, opts) - .then(function (resp) { - if (!resp) { - throw new Error(resp); - } - return resp; - }); -}; - -/** - * Use this method to receive incoming updates using long polling - * @param {Number|String} [timeout] Timeout in seconds for long polling. - * @param {Number|String} [limit] Limits the number of updates to be retrieved. - * @param {Number|String} [offset] Identifier of the first update to be returned. - * @return {Promise} Updates - * @see https://core.telegram.org/bots/api#getupdates - */ -TelegramBot.prototype.getUpdates = function (timeout, limit, offset) { - var form = { - offset: offset, - limit: limit, - timeout: timeout - }; - - return this._request('getUpdates', {form: form}); -}; - -/** - * Send text message. - * @param {Number|String} chatId Unique identifier for the message recipient - * @param {String} text Text of the message to be sent - * @param {Object} [options] Additional Telegram query options - * @return {Promise} - * @see https://core.telegram.org/bots/api#sendmessage - */ -TelegramBot.prototype.sendMessage = function (chatId, text, options) { - var form = options || {}; - form.chat_id = chatId; - form.text = text; - return this._request('sendMessage', {form: form}); -}; - -/** - * Send answers to an inline query. - * @param {String} inlineQueryId Unique identifier of the query - * @param {InlineQueryResult[]} results An array of results for the inline query - * @param {Object} [options] Additional Telegram query options - * @return {Promise} - * @see https://core.telegram.org/bots/api#answerinlinequery - */ -TelegramBot.prototype.answerInlineQuery = function (inlineQueryId, results, options) { - var form = options || {}; - form.inline_query_id = inlineQueryId; - form.results = JSON.stringify(results); - return this._request('answerInlineQuery', {form: form}); -}; - - -/** - * Forward messages of any kind. - * @param {Number|String} chatId Unique identifier for the message recipient - * @param {Number|String} fromChatId Unique identifier for the chat where the - * original message was sent - * @param {Number|String} messageId Unique message identifier - * @return {Promise} - */ -TelegramBot.prototype.forwardMessage = function (chatId, fromChatId, messageId) { - var form = { - chat_id: chatId, - from_chat_id: fromChatId, - message_id: messageId - }; - return this._request('forwardMessage', {form: form}); -}; - -TelegramBot.prototype._formatSendData = function (type, data) { - var formData; - var fileName; - var fileId; - if (data instanceof stream.Stream) { - fileName = URL.parse(path.basename(data.path)).pathname; - formData = {}; - formData[type] = { - value: data, - options: { - filename: qs.unescape(fileName), - contentType: mime.lookup(fileName) - } + /** + * Send audio + * @param {Number|String} chatId Unique identifier for the message recipient + * @param {String|stream.Stream|Buffer} audio A file path, Stream or Buffer. + * Can also be a `file_id` previously uploaded. + * @param {Object} [options] Additional Telegram query options + * @return {Promise} + * @see https://core.telegram.org/bots/api#sendaudio + */ + sendAudio(chatId, audio, options = {}) { + const opts = { + qs: options }; - } else if (Buffer.isBuffer(data)) { - var 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; + opts.qs.chat_id = chatId; + const content = this._formatSendData('audio', audio); + opts.formData = content[0]; + opts.qs.audio = content[1]; + return this._request('sendAudio', opts); } - return [formData, fileId]; -}; -/** - * Send photo - * @param {Number|String} chatId Unique identifier for the message recipient - * @param {String|stream.Stream|Buffer} photo A file path or a Stream. Can - * also be a `file_id` previously uploaded - * @param {Object} [options] Additional Telegram query options - * @return {Promise} - * @see https://core.telegram.org/bots/api#sendphoto - */ -TelegramBot.prototype.sendPhoto = function (chatId, photo, options) { - var opts = { - qs: options || {} - }; - opts.qs.chat_id = chatId; - var content = this._formatSendData('photo', photo); - opts.formData = content[0]; - opts.qs.photo = content[1]; - return this._request('sendPhoto', opts); -}; + /** + * Send Document + * @param {Number|String} chatId Unique identifier for the message recipient + * @param {String|stream.Stream|Buffer} doc A file path, Stream or Buffer. + * Can also be a `file_id` previously uploaded. + * @param {Object} [options] Additional Telegram query options + * @return {Promise} + * @see https://core.telegram.org/bots/api#sendDocument + */ + sendDocument(chatId, doc, options = {}) { + const opts = { + qs: options + }; + opts.qs.chat_id = chatId; + const content = this._formatSendData('document', doc); + opts.formData = content[0]; + opts.qs.document = content[1]; + return this._request('sendDocument', opts); + } -/** - * Send audio - * @param {Number|String} chatId Unique identifier for the message recipient - * @param {String|stream.Stream|Buffer} audio A file path, Stream or Buffer. - * Can also be a `file_id` previously uploaded. - * @param {Object} [options] Additional Telegram query options - * @return {Promise} - * @see https://core.telegram.org/bots/api#sendaudio - */ -TelegramBot.prototype.sendAudio = function (chatId, audio, options) { - var opts = { - qs: options || {} - }; - opts.qs.chat_id = chatId; - var content = this._formatSendData('audio', audio); - opts.formData = content[0]; - opts.qs.audio = content[1]; - return this._request('sendAudio', opts); -}; + /** + * Send .webp stickers. + * @param {Number|String} chatId Unique identifier for the message recipient + * @param {String|stream.Stream|Buffer} sticker A file path, Stream or Buffer. + * Can also be a `file_id` previously uploaded. Stickers are WebP format files. + * @param {Object} [options] Additional Telegram query options + * @return {Promise} + * @see https://core.telegram.org/bots/api#sendsticker + */ + sendSticker(chatId, sticker, options = {}) { + const opts = { + qs: options + }; + opts.qs.chat_id = chatId; + const content = this._formatSendData('sticker', sticker); + opts.formData = content[0]; + opts.qs.sticker = content[1]; + return this._request('sendSticker', opts); + } -/** - * Send Document - * @param {Number|String} chatId Unique identifier for the message recipient - * @param {String|stream.Stream|Buffer} doc A file path, Stream or Buffer. - * Can also be a `file_id` previously uploaded. - * @param {Object} [options] Additional Telegram query options - * @return {Promise} - * @see https://core.telegram.org/bots/api#sendDocument - */ -TelegramBot.prototype.sendDocument = function (chatId, doc, options) { - var opts = { - qs: options || {} - }; - opts.qs.chat_id = chatId; - var content = this._formatSendData('document', doc); - opts.formData = content[0]; - opts.qs.document = content[1]; - return this._request('sendDocument', opts); -}; + /** + * Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). + * @param {Number|String} chatId Unique identifier for the message recipient + * @param {String|stream.Stream|Buffer} video A file path or Stream. + * Can also be a `file_id` previously uploaded. + * @param {Object} [options] Additional Telegram query options + * @return {Promise} + * @see https://core.telegram.org/bots/api#sendvideo + */ + sendVideo(chatId, video, options = {}) { + const opts = { + qs: options + }; + opts.qs.chat_id = chatId; + const content = this._formatSendData('video', video); + opts.formData = content[0]; + opts.qs.video = content[1]; + return this._request('sendVideo', opts); + } -/** - * Send .webp stickers. - * @param {Number|String} chatId Unique identifier for the message recipient - * @param {String|stream.Stream|Buffer} sticker A file path, Stream or Buffer. - * Can also be a `file_id` previously uploaded. Stickers are WebP format files. - * @param {Object} [options] Additional Telegram query options - * @return {Promise} - * @see https://core.telegram.org/bots/api#sendsticker - */ -TelegramBot.prototype.sendSticker = function (chatId, sticker, options) { - var opts = { - qs: options || {} - }; - opts.qs.chat_id = chatId; - var content = this._formatSendData('sticker', sticker); - opts.formData = content[0]; - opts.qs.sticker = content[1]; - return this._request('sendSticker', opts); -}; - -/** - * Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). - * @param {Number|String} chatId Unique identifier for the message recipient - * @param {String|stream.Stream|Buffer} video A file path or Stream. - * Can also be a `file_id` previously uploaded. - * @param {Object} [options] Additional Telegram query options - * @return {Promise} - * @see https://core.telegram.org/bots/api#sendvideo - */ -TelegramBot.prototype.sendVideo = function (chatId, video, options) { - var opts = { - qs: options || {} - }; - opts.qs.chat_id = chatId; - var content = this._formatSendData('video', video); - opts.formData = content[0]; - opts.qs.video = content[1]; - return this._request('sendVideo', opts); -}; - -/** - * Send voice - * @param {Number|String} chatId Unique identifier for the message recipient - * @param {String|stream.Stream|Buffer} voice A file path, Stream or Buffer. - * Can also be a `file_id` previously uploaded. - * @param {Object} [options] Additional Telegram query options - * @return {Promise} - * @see https://core.telegram.org/bots/api#sendvoice - */ -TelegramBot.prototype.sendVoice = function (chatId, voice, options) { - var opts = { - qs: options || {} - }; - opts.qs.chat_id = chatId; - var content = this._formatSendData('voice', voice); - opts.formData = content[0]; - opts.qs.voice = content[1]; - return this._request('sendVoice', opts); -}; + /** + * Send voice + * @param {Number|String} chatId Unique identifier for the message recipient + * @param {String|stream.Stream|Buffer} voice A file path, Stream or Buffer. + * Can also be a `file_id` previously uploaded. + * @param {Object} [options] Additional Telegram query options + * @return {Promise} + * @see https://core.telegram.org/bots/api#sendvoice + */ + sendVoice(chatId, voice, options = {}) { + const opts = { + qs: options + }; + opts.qs.chat_id = chatId; + const content = this._formatSendData('voice', voice); + opts.formData = content[0]; + opts.qs.voice = content[1]; + return this._request('sendVoice', opts); + } -/** - * Send chat action. - * `typing` for text messages, - * `upload_photo` for photos, `record_video` or `upload_video` for videos, - * `record_audio` or `upload_audio` for audio files, `upload_document` for general files, - * `find_location` for location data. - * - * @param {Number|String} chatId Unique identifier for the message recipient - * @param {String} action Type of action to broadcast. - * @return {Promise} - * @see https://core.telegram.org/bots/api#sendchataction - */ -TelegramBot.prototype.sendChatAction = function (chatId, action) { - var form = { - chat_id: chatId, - action: action - }; - return this._request('sendChatAction', {form: form}); -}; + /** + * Send chat action. + * `typing` for text messages, + * `upload_photo` for photos, `record_video` or `upload_video` for videos, + * `record_audio` or `upload_audio` for audio files, `upload_document` for general files, + * `find_location` for location data. + * + * @param {Number|String} chatId Unique identifier for the message recipient + * @param {String} action Type of action to broadcast. + * @return {Promise} + * @see https://core.telegram.org/bots/api#sendchataction + */ + sendChatAction(chatId, action) { + const form = { + action, + chat_id: chatId + }; + return this._request('sendChatAction', { form }); + } -/** - * Use this method to get a list of profile pictures for a user. - * Returns a [UserProfilePhotos](https://core.telegram.org/bots/api#userprofilephotos) object. - * - * @param {Number|String} userId Unique identifier of the target user - * @param {Number} [offset] Sequential number of the first photo to be returned. By default, all photos are returned. - * @param {Number} [limit] Limits the number of photos to be retrieved. Values between 1—100 are accepted. Defaults to 100. - * @return {Promise} - * @see https://core.telegram.org/bots/api#getuserprofilephotos - */ -TelegramBot.prototype.getUserProfilePhotos = function (userId, offset, limit) { - var form = { - user_id: userId, - offset: offset, - limit: limit - }; - return this._request('getUserProfilePhotos', {form: form}); -}; + /** + * Use this method to get a list of profile pictures for a user. + * Returns a [UserProfilePhotos](https://core.telegram.org/bots/api#userprofilephotos) object. + * + * @param {Number|String} userId Unique identifier of the target user + * @param {Number} [offset] Sequential number of the first photo to be returned. By default, all photos are returned. + * @param {Number} [limit] Limits the number of photos to be retrieved. Values between 1—100 are accepted. Defaults to 100. + * @return {Promise} + * @see https://core.telegram.org/bots/api#getuserprofilephotos + */ + getUserProfilePhotos(userId, offset, limit) { + const form = { + user_id: userId, + offset, + limit + }; + return this._request('getUserProfilePhotos', { form }); + } -/** - * Send location. - * Use this method to send point on the map. - * - * @param {Number|String} chatId Unique identifier for the message recipient - * @param {Float} latitude Latitude of location - * @param {Float} longitude Longitude of location - * @param {Object} [options] Additional Telegram query options - * @return {Promise} - * @see https://core.telegram.org/bots/api#sendlocation - */ -TelegramBot.prototype.sendLocation = function (chatId, latitude, longitude, options) { - var form = options || {}; - form.chat_id = chatId; - form.latitude = latitude; - form.longitude = longitude; - return this._request('sendLocation', {form: form}); -}; + /** + * Send location. + * Use this method to send point on the map. + * + * @param {Number|String} chatId Unique identifier for the message recipient + * @param {Float} latitude Latitude of location + * @param {Float} longitude Longitude of location + * @param {Object} [options] Additional Telegram query options + * @return {Promise} + * @see https://core.telegram.org/bots/api#sendlocation + */ + sendLocation(chatId, latitude, longitude, form = {}) { + form.chat_id = chatId; + form.latitude = latitude; + form.longitude = longitude; + return this._request('sendLocation', { form }); + } -/** - * Get file. - * Use this method to get basic info about a file and prepare it for downloading. - * Attention: link will be valid for 1 hour. - * - * @param {String} fileId File identifier to get info about - * @return {Promise} - * @see https://core.telegram.org/bots/api#getfile - */ -TelegramBot.prototype.getFile = function(fileId) { - var form = {file_id: fileId}; - return this._request('getFile', {form: form}); -}; + /** + * Get file. + * Use this method to get basic info about a file and prepare it for downloading. + * Attention: link will be valid for 1 hour. + * + * @param {String} fileId File identifier to get info about + * @return {Promise} + * @see https://core.telegram.org/bots/api#getfile + */ + getFile(fileId) { + const form = { file_id: fileId }; + return this._request('getFile', { form }); + } -/** - * Get link for file. - * Use this method to get link for file for subsequent use. - * Attention: link will be valid for 1 hour. - * - * This method is a sugar extension of the (getFile)[#getfilefileid] method, which returns just path to file on remote server (you will have to manually build full uri after that). - * - * @param {String} fileId File identifier to get info about - * @return {Promise} promise Promise which will have *fileURI* in resolve callback - * @see https://core.telegram.org/bots/api#getfile - */ -TelegramBot.prototype.getFileLink = function(fileId) { - return this.getFile(fileId) - .then(function (resp) { - return URL.format({ + /** + * Get link for file. + * Use this method to get link for file for subsequent use. + * Attention: link will be valid for 1 hour. + * + * This method is a sugar extension of the (getFile)[#getfilefileid] method, + * which returns just path to file on remote server (you will have to manually build full uri after that). + * + * @param {String} fileId File identifier to get info about + * @return {Promise} promise Promise which will have *fileURI* in resolve callback + * @see https://core.telegram.org/bots/api#getfile + */ + getFileLink(fileId) { + return this.getFile(fileId) + .then(resp => URL.format({ protocol: 'https', host: 'api.telegram.org', - pathname: '/file/bot' + this.token + '/' + resp.file_path + pathname: `/file/bot${this.token}/${resp.file_path}` + })); + } + + /** + * Downloads file in the specified folder. + * This is just a sugar for (getFile)[#getfilefiled] method + * + * @param {String} fileId File identifier to get info about + * @param {String} downloadDir Absolute path to the folder in which file will be saved + * @return {Promise} promise Promise, which will have *filePath* of downloaded file in resolve callback + */ + downloadFile(fileId, downloadDir) { + return this + .getFileLink(fileId) + .then(fileURI => { + const fileName = fileURI.slice(fileURI.lastIndexOf('/') + 1); + // TODO: Ensure fileName doesn't contains slashes + const filePath = `${downloadDir}/${fileName}`; + + // properly handles errors and closes all streams + return Promise + .fromCallback(next => { + pump(streamedRequest({ uri: fileURI }), fs.createWriteStream(filePath), next); + }) + .return(filePath); }); - }.bind(this)); -}; + } -/** - * Downloads file in the specified folder. - * This is just a sugar for (getFile)[#getfilefiled] method - * - * @param {String} fileId File identifier to get info about - * @param {String} downloadDir Absolute path to the folder in which file will be saved - * @return {Promise} promise Promise, which will have *filePath* of downloaded file in resolve callback - */ -TelegramBot.prototype.downloadFile = function(fileId, downloadDir) { + /** + * Register a RegExp to test against an incomming text message. + * @param {RegExp} regexp RegExp to be executed with `exec`. + * @param {Function} callback Callback will be called with 2 parameters, + * the `msg` and the result of executing `regexp.exec` on message text. + */ + onText(regexp, callback) { + this.textRegexpCallbacks.push({ regexp, callback }); + } - return this.getFileLink(fileId).then(function (fileURI) { - var fileName = fileURI.slice(fileURI.lastIndexOf('/') + 1); - // TODO: Ensure fileName doesn't contains slashes - var filePath = downloadDir + '/' + fileName; - return new Promise(function (resolve, reject) { - request({uri: fileURI}) - .pipe(fs.createWriteStream(filePath)) - .on('error', reject) - .on('close', function() { - resolve(filePath); - }); + /** + * Register a reply to wait for a message response. + * @param {Number|String} chatId The chat id where the message cames from. + * @param {Number|String} messageId The message id to be replied. + * @param {Function} callback Callback will be called with the reply + * message. + */ + onReplyToMessage(chatId, messageId, callback) { + this.onReplyToMessages.push({ + chatId, + messageId, + callback }); - }); - -}; - -/** - * Register a RegExp to test against an incomming text message. - * @param {RegExp} regexp RegExp to be executed with `exec`. - * @param {Function} callback Callback will be called with 2 parameters, - * the `msg` and the result of executing `regexp.exec` on message text. - */ -TelegramBot.prototype.onText = function (regexp, callback) { - this.textRegexpCallbacks.push({regexp: regexp, callback: callback}); -}; - -/** - * Register a reply to wait for a message response. - * @param {Number|String} chatId The chat id where the message cames from. - * @param {Number|String} messageId The message id to be replied. - * @param {Function} callback Callback will be called with the reply - * message. - */ -TelegramBot.prototype.onReplyToMessage = function (chatId, messageId, callback) { - this.onReplyToMessages.push({ - chatId: chatId, - messageId: messageId, - callback: callback - }); -}; + } +} module.exports = TelegramBot; diff --git a/src/telegramPolling.js b/src/telegramPolling.js index a5da59f..11e2eb6 100644 --- a/src/telegramPolling.js +++ b/src/telegramPolling.js @@ -1,90 +1,103 @@ -'use strict'; +const Promise = require('bluebird'); +const debug = require('debug')('node-telegram-bot-api'); +const request = require('request-promise'); +const URL = require('url'); -var debug = require('debug')('node-telegram-bot-api'); -var Promise = require('bluebird'); -var request = require('request'); -var util = require('util'); -var URL = require('url'); +class TelegramBotPolling { -var requestPromise = Promise.promisify(request); - -var TelegramBotPolling = function (token, options, callback) { - options = options || {}; - if (typeof options === 'function') { - callback = options; - options = {}; - } - this.offset = 0; - this.token = token; - this.callback = callback; - this.timeout = options.timeout || 10; - this.interval = (typeof options.interval === 'number') ? options.interval : 300; - this.lastUpdate = 0; - this.lastRequest = null; - this.abort = false; - this._polling(); -}; - -TelegramBotPolling.prototype._polling = function () { - this.lastRequest = this._getUpdates() - .then(function (updates) { - this.lastUpdate = Date.now(); - debug('polling data %j', updates); - updates.forEach(function (update, index) { - this.offset = update.update_id; - debug('updated offset: %s', this.offset); - this.callback(update); - }.bind(this)); - }.bind(this)) - .catch(function (err) { - debug('polling error: %s', err.message); - throw err; - }) - .finally(function () { - if (this.abort) { - debug('Polling is aborted!'); - } else { - debug('setTimeout for %s miliseconds', this.interval); - setTimeout(this._polling.bind(this), this.interval); - } - }.bind(this)); -}; - -TelegramBotPolling.prototype._getUpdates = function () { - var opts = { - qs: { - offset: this.offset+1, - limit: this.limit, - timeout: this.timeout - }, - url: URL.format({ - protocol: 'https', - host: 'api.telegram.org', - pathname: '/bot'+this.token+'/getUpdates' - }) - }; - debug('polling with options: %j', opts); - return requestPromise(opts) - .cancellable() - .timeout((10 + this.timeout) * 1000) - .then(function (resp) { - if (resp[0].statusCode !== 200) { - throw new Error(resp[0].statusCode+' '+resp[0].body); - } - var data; - try { - data = JSON.parse(resp[0].body); - } catch (err) { - var text = util.format('Error parsing Telegram response: %s', - resp[0].body); - throw new Error(text); - } - if (data.ok) { - return data.result; - } else { - throw new Error(data.error_code+' '+data.description); - } + constructor(token, options = {}, callback) { + // enable cancellation + Promise.config({ + cancellation: true, }); -}; + + if (typeof options === 'function') { + callback = options; // eslint-disable-line no-param-reassign + options = {}; // eslint-disable-line no-param-reassign + } + + this.offset = 0; + this.token = token; + this.callback = callback; + this.timeout = options.timeout || 10; + this.interval = (typeof options.interval === 'number') ? options.interval : 300; + this.lastUpdate = 0; + this.lastRequest = null; + this.abort = false; + this._polling(); + } + + _polling() { + this.lastRequest = this + ._getUpdates() + .then(updates => { + this.lastUpdate = Date.now(); + debug('polling data %j', updates); + updates.forEach(update => { + this.offset = update.update_id; + debug('updated offset: %s', this.offset); + this.callback(update); + }); + }) + .catch(err => { + debug('polling error: %s', err.message); + throw err; + }) + .finally(() => { + if (this.abort) { + debug('Polling is aborted!'); + } else { + debug('setTimeout for %s miliseconds', this.interval); + setTimeout(() => this._polling(), this.interval); + } + }); + } + + // 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)}`); + } + } + + _getUpdates() { + const opts = { + qs: { + offset: this.offset + 1, + limit: this.limit, + timeout: this.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); + + return request(opts) + .promise() + .timeout((10 + this.timeout) * 1000) + .then(resp => { + 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; diff --git a/src/telegramWebHook.js b/src/telegramWebHook.js index cd03940..6bf265f 100644 --- a/src/telegramWebHook.js +++ b/src/telegramWebHook.js @@ -1,67 +1,85 @@ -'use strict'; +const debug = require('debug')('node-telegram-bot-api'); +const https = require('https'); +const http = require('http'); +const fs = require('fs'); +const bl = require('bl'); -var debug = require('debug')('node-telegram-bot-api'); -var https = require('https'); -var http = require('http'); -var fs = require('fs'); +class TelegramBotWebHook { -var TelegramBotWebHook = function (token, options, callback) { - this.token = token; - this.callback = callback; - if (typeof options === 'boolean') { - options = {}; - } - options.port = options.port || 8443; - var binded = this._requestListener.bind(this); + constructor(token, options, callback) { + this.token = token; + this.callback = callback; + this.regex = new RegExp(this.token); - if (options.key && options.cert) { // HTTPS Server - debug('HTTPS WebHook enabled'); - var opts = { - key: fs.readFileSync(options.key), - cert: fs.readFileSync(options.cert) - }; - this._webServer = https.createServer(opts, binded); - } else { - debug('HTTP WebHook enabled'); - this._webServer = http.createServer(binded); - } + // define opts + if (typeof options === 'boolean') { + options = {}; // eslint-disable-line no-param-reassign + } + options.port = options.port || 8443; - this._webServer.listen(options.port, options.host, function () { - debug('WebHook listening on port %s', options.port); - }); -}; + if (options.key && options.cert) { // HTTPS Server + debug('HTTPS WebHook enabled'); + const opts = { + key: fs.readFileSync(options.key), + cert: fs.readFileSync(options.cert) + }; + this._webServer = https.createServer(opts, this._requestListener); + } else { + debug('HTTP WebHook enabled'); + this._webServer = http.createServer(this._requestListener); + } -TelegramBotWebHook.prototype._requestListener = function (req, res) { - var self = this; - var regex = new RegExp(this.token); - - debug('WebHook request URL:', req.url); - debug('WebHook request headers: %j', req.headers); - // If there isn't token on URL - if (!regex.test(req.url)) { - debug('WebHook request unauthorized'); - res.statusCode = 401; - res.end(); - } else if (req.method === 'POST') { - var fullBody = ''; - req.on('data', function (chunk) { - fullBody += chunk.toString(); + this._webServer.listen(options.port, options.host, () => { + debug('WebHook listening on port %s', options.port); }); - req.on('end', function () { - try { - debug('WebHook request fullBody', fullBody); - var data = JSON.parse(fullBody); - self.callback(data); - } catch (error) { - debug(error); - } - res.end('OK'); - }); - } else { // Authorized but not a POST - debug('WebHook request isn\'t a POST'); - res.statusCode = 418; // I'm a teabot! - res.end(); } -}; + + // used so that other funcs are not non-optimizable + _safeParse(json) { + try { + return JSON.parse(json); + } catch (err) { + debug(err); + return null; + } + } + + // pipe+parse body + _parseBody = (err, body) => { + if (err) { + return debug(err); + } + + const data = this._safeParse(body); + if (data) { + return this.callback(data); + } + + return null; + } + + // bound req listener + _requestListener = (req, res) => { + debug('WebHook request URL:', req.url); + debug('WebHook request headers: %j', req.headers); + + // If there isn't token on URL + if (!this.regex.test(req.url)) { + debug('WebHook request unauthorized'); + res.statusCode = 401; + res.end(); + } else if (req.method === 'POST') { + req + .pipe(bl(this._parseBody)) + .on('end', () => res.end('OK')); + } else { + // Authorized but not a POST + debug('WebHook request isn\'t a POST'); + res.statusCode = 418; // I'm a teabot! + res.end(); + } + } + +} module.exports = TelegramBotWebHook; diff --git a/test/.jshintrc b/test/.jshintrc deleted file mode 100644 index a0d4f0e..0000000 --- a/test/.jshintrc +++ /dev/null @@ -1,12 +0,0 @@ -{ - "mocha" : true, - "curly" : true, - "eqeqeq" : true, - "indent" : 2, - "latedef" : true, - "newcap" : true, - "quotmark": "single", - "strict" : true, - "undef" : true, - "node" : true -} diff --git a/test/index.js b/test/index.js index 5222973..6baa469 100644 --- a/test/index.js +++ b/test/index.js @@ -1,533 +1,506 @@ -'use strict'; - -var TelegramPolling = require('../src/telegramPolling'); -var Telegram = require('../index'); -var Promise = require('bluebird'); -var request = require('request'); -var should = require('should'); -var fs = require('fs'); +const TelegramPolling = require('../src/telegramPolling'); +const Telegram = require('../index'); +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'; -var TOKEN = process.env.TEST_TELEGRAM_TOKEN; +const TOKEN = process.env.TEST_TELEGRAM_TOKEN; if (!TOKEN) { throw new Error('Bot token not provided'); } + // Telegram service if not User Id -var USERID = process.env.TEST_USER_ID || 777000; +const USERID = process.env.TEST_USER_ID || 777000; -describe('Telegram', function () { - - describe('#setWebHook', function () { - it('should set a webHook', function (done) { - var bot = new Telegram(TOKEN); +describe('Telegram', function telegramSuite() { + describe('#setWebHook', function setWebHookSuite() { + it('should set a webHook', function test() { + const bot = new Telegram(TOKEN); // Google IP ¯\_(ツ)_/¯ - bot.setWebHook('216.58.210.174').then(function (resp) { - resp.should.be.exactly(true); - done(); - }); + return bot + .setWebHook('216.58.210.174') + .then(resp => { + assert.equal(resp, true); + }); }); - it('should set a webHook with certificate', function (done) { - var bot = new Telegram(TOKEN); - var cert = __dirname+'/../examples/crt.pem'; - bot.setWebHook('216.58.210.174', cert).then(function (resp) { - resp.should.be.exactly(true); - done(); - }); + 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 (done) { - var bot = new Telegram(TOKEN); - bot.setWebHook('').then(function (resp) { - resp.should.be.exactly(true); - done(); - }); + it('should delete the webHook', function test() { + const bot = new Telegram(TOKEN); + return bot + .setWebHook('') + .then(resp => { + assert.equal(resp, true); + }); }); }); - describe('#WebHook', function () { - it('should reject request if same token not provided', function (done) { - var bot = new Telegram(TOKEN, {webHook: true}); - request({ + 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' - }, function (error, response, body) { - response.statusCode.should.not.be.equal(200); + method: 'POST', + simple: false, + resolveWithFullResponse: true + }).then(response => { + assert.notEqual(response.statusCode, 200); bot._WebHook._webServer.close(); - done(); }); }); - it('should reject request if authorized but not a POST', function (done) { - var bot = new Telegram(TOKEN, {webHook: true}); - request({ - url: 'http://localhost:8443/bot'+TOKEN, - method: 'GET' - }, function (error, response, body) { - response.statusCode.should.not.be.equal(200); + 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(); - done(); }); }); - it('should emit a `message` on HTTP WebHook', function (done) { - var bot = new Telegram(TOKEN, {webHook: true}); - bot.on('message', function (msg) { + 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(); }); - var url = 'http://localhost:8443/bot'+TOKEN; + + const url = `http://localhost:8443/bot${TOKEN}`; request({ - url: url, + url, method: 'POST', json: true, headers: { 'content-type': 'application/json', }, - body: {update_id: 0, message: {text: 'test'}} + body: { update_id: 0, message: { text: 'test' } } }); }); - it('should emit a `message` on HTTPS WebHook', function (done) { - var opts = { + 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' + key: `${__dirname}/../examples/key.pem`, + cert: `${__dirname}/../examples/crt.pem` } }; - var bot = new Telegram(TOKEN, opts); - bot.on('message', function (msg) { + const bot = new Telegram(TOKEN, opts); + bot.on('message', () => { bot._WebHook._webServer.close(); done(); }); - var url = 'https://localhost:8443/bot'+TOKEN; + const url = `https://localhost:8443/bot${TOKEN}`; request({ - url: url, + url, method: 'POST', json: true, headers: { 'content-type': 'application/json', }, rejectUnhauthorized: false, - body: {update_id: 0, message: {text: 'test'}} + body: { update_id: 0, message: { text: 'test' } } }); }); }); - describe('#getMe', function () { - it('should return an User object', function (done) { - var bot = new Telegram(TOKEN); - bot.getMe().then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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('#getUpdates', function () { - it('should return an Array', function (done) { - var bot = new Telegram(TOKEN); - bot.getUpdates().then(function (resp) { - resp.should.be.an.instanceOf(Array); - done(); + 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 () { - it('should send a message', function (done) { - var bot = new Telegram(TOKEN); - bot.sendMessage(USERID, 'test').then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 () { - it('should forward a message', function (done) { - var bot = new Telegram(TOKEN); - bot.sendMessage(USERID, 'test').then(function (resp) { - var messageId = resp.message_id; - bot.forwardMessage(USERID, USERID, messageId) - .then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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('#sendPhoto', function () { - var photoId; - it('should send a photo from file', function (done) { - var bot = new Telegram(TOKEN); - var photo = __dirname+'/bot.gif'; - bot.sendPhoto(USERID, photo).then(function (resp) { - resp.should.be.an.instanceOf(Object); + 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; - done(); }); }); - it('should send a photo from id', function (done) { - var bot = new Telegram(TOKEN); + it('should send a photo from id', function test() { + const bot = new Telegram(TOKEN); // Send the same photo as before - var photo = photoId; - bot.sendPhoto(USERID, photo).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + const photo = photoId; + return bot.sendPhoto(USERID, photo).then(resp => { + assert.ok(is.object(resp)); }); }); - it('should send a photo from fs.readStream', function (done) { - var bot = new Telegram(TOKEN); - var photo = fs.createReadStream(__dirname+'/bot.gif'); - bot.sendPhoto(USERID, photo).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 (done) { - var bot = new Telegram(TOKEN); - var photo = request('https://telegram.org/img/t_logo.png'); - bot.sendPhoto(USERID, photo).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 (done) { - var bot = new Telegram(TOKEN); - var photo = fs.readFileSync(__dirname+'/bot.gif'); - bot.sendPhoto(USERID, photo).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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)); }); }); }); - describe('#sendChatAction', function () { - it('should send a chat action', function (done) { - var bot = new Telegram(TOKEN); - var action = 'typing'; - bot.sendChatAction(USERID, action).then(function (resp) { - resp.should.be.exactly(true); - done(); + 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('#sendAudio', function () { - it('should send an OGG audio', function (done) { - var bot = new Telegram(TOKEN); - var audio = request('https://upload.wikimedia.org/wikipedia/commons/c/c8/Example.ogg'); - bot.sendAudio(USERID, audio).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 () { - var documentId; - it('should send a document from file', function (done) { - var bot = new Telegram(TOKEN); - var document = __dirname+'/bot.gif'; - bot.sendDocument(USERID, document).then(function (resp) { - resp.should.be.an.instanceOf(Object); + 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; - done(); }); }); - it('should send a document from id', function (done) { - var bot = new Telegram(TOKEN); + it('should send a document from id', function test() { + const bot = new Telegram(TOKEN); // Send the same photo as before - var document = documentId; - bot.sendDocument(USERID, document).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + const document = documentId; + return bot.sendDocument(USERID, document).then(resp => { + assert.ok(is.object(resp)); }); }); - it('should send a document from fs.readStream', function (done) { - var bot = new Telegram(TOKEN); - var document = fs.createReadStream(__dirname+'/bot.gif'); - bot.sendDocument(USERID, document).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 (done) { - var bot = new Telegram(TOKEN); - var document = request('https://telegram.org/img/t_logo.png'); - bot.sendDocument(USERID, document).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 (done) { - var bot = new Telegram(TOKEN); - var document = fs.readFileSync(__dirname+'/bot.gif'); - bot.sendDocument(USERID, document).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 () { - var stickerId; - it('should send a sticker from file', function (done) { - var bot = new Telegram(TOKEN); - var sticker = __dirname+'/sticker.webp'; - bot.sendSticker(USERID, sticker).then(function (resp) { - resp.should.be.an.instanceOf(Object); + 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; - done(); }); }); - it('should send a sticker from id', function (done) { - var bot = new Telegram(TOKEN); + it('should send a sticker from id', function test() { + const bot = new Telegram(TOKEN); // Send the same photo as before - bot.sendSticker(USERID, stickerId).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + return bot.sendSticker(USERID, stickerId).then(resp => { + assert.ok(is.object(resp)); }); }); - it('should send a sticker from fs.readStream', function (done) { - var bot = new Telegram(TOKEN); - var sticker = fs.createReadStream(__dirname+'/sticker.webp'); - bot.sendSticker(USERID, sticker).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 (done) { - var bot = new Telegram(TOKEN); - var sticker = request('https://www.gstatic.com/webp/gallery3/1_webp_ll.webp'); - bot.sendSticker(USERID, sticker).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 (done) { - var bot = new Telegram(TOKEN); - var sticker = fs.readFileSync(__dirname+'/sticker.webp'); - bot.sendDocument(USERID, sticker).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 () { - var videoId; - it('should send a video from file', function (done) { - var bot = new Telegram(TOKEN); - var video = __dirname+'/video.mp4'; - bot.sendVideo(USERID, video).then(function (resp) { - resp.should.be.an.instanceOf(Object); + 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; - done(); }); }); - it('should send a video from id', function (done) { - var bot = new Telegram(TOKEN); + it('should send a video from id', function test() { + const bot = new Telegram(TOKEN); // Send the same photo as before - bot.sendVideo(USERID, videoId).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + return bot.sendVideo(USERID, videoId).then(resp => { + assert.ok(is.object(resp)); }); }); - it('should send a video from fs.readStream', function (done) { - var bot = new Telegram(TOKEN); - var video = fs.createReadStream(__dirname+'/video.mp4'); - bot.sendVideo(USERID, video).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 (done) { - var bot = new Telegram(TOKEN); - var sticker = request('http://techslides.com/demos/sample-videos/small.mp4'); - bot.sendVideo(USERID, sticker).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 (done) { - var bot = new Telegram(TOKEN); - var video = fs.readFileSync(__dirname+'/video.mp4'); - bot.sendVideo(USERID, video).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 () { - it('should send an OGG audio as voice', function (done) { - var bot = new Telegram(TOKEN); - var voice = request('https://upload.wikimedia.org/wikipedia/commons/c/c8/Example.ogg'); - bot.sendVoice(USERID, voice).then(function (resp) { - resp.should.be.an.instanceOf(Object); - done(); + 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 () { - it('should get user profile photos', function (done) { - var bot = new Telegram(TOKEN); - bot.getUserProfilePhotos(USERID).then(function (resp) { - resp.should.be.an.instanceOf(Object); - resp.total_count.should.be.an.instanceOf(Number); - resp.photos.should.be.an.instanceOf(Array); - done(); + 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 () { - it('should send a location', function (done) { - var bot = new Telegram(TOKEN); - var lat = 47.5351072; - var long = -52.7508537; - bot.sendLocation(USERID, lat, long).then(function (resp) { - resp.should.be.an.instanceOf(Object); - resp.location.should.be.an.instanceOf(Object); - resp.location.latitude.should.be.an.instanceOf(Number); - resp.location.longitude.should.be.an.instanceOf(Number); - done(); + 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('#getFile', function () { - var fileId; + 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 (done) { - var bot = new Telegram(TOKEN); - var photo = __dirname + '/bot.gif'; - bot.sendPhoto(USERID, photo).then(function (resp) { - resp.should.be.an.instanceOf(Object); + 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; - done(); }); }); - it('should get a file', function (done) { + it('should get a file', function test() { + const bot = new Telegram(TOKEN); - var bot = new Telegram(TOKEN); - - bot.getFile(fileId).then(function (resp) { - resp.should.be.an.instanceOf(Object); - resp.file_path.should.be.an.instanceOf(String); - done(); - }); + return bot + .getFile(fileId) + .then(resp => { + assert.ok(is.object(resp)); + assert.ok(is.string(resp.file_path)); + }); }); }); - describe('#getFileLink', function () { - var fileId; + 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 (done) { - var bot = new Telegram(TOKEN); - var photo = __dirname + '/bot.gif'; - bot.sendPhoto(USERID, photo).then(function (resp) { - resp.should.be.an.instanceOf(Object); + 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; - done(); }); }); - it('should get a file link', function (done) { + it('should get a file link', function test() { + const bot = new Telegram(TOKEN); - var bot = new Telegram(TOKEN); - - bot.getFileLink(fileId).then(function (fileURI) { - fileURI.should.be.an.instanceOf(String); - fileURI.should.startWith('https'); - done(); // TODO: validate URL with some library or regexp - }); + 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 () { + describe('#downloadFile', function downloadFileSuite() { + const downloadPath = __dirname; - var downloadPath = __dirname; - - it('should download a file', function (done) { - - var bot = new Telegram(TOKEN); - var photo = __dirname + '/bot.gif'; + it('should download a file', function test() { + const bot = new Telegram(TOKEN); + const photo = `${__dirname}/bot.gif`; // Send a file to get the ID - bot.sendPhoto(USERID, photo).then(function (resp) { - resp.should.be.an.instanceOf(Object); - var fileId = resp.photo[0].file_id; - - bot.downloadFile(fileId, downloadPath) - .then(function (filePath) { - filePath.should.be.an.instanceOf(String); - fs.existsSync(filePath).should.be.true(); + 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 - done(); - }); + }); }); }); - }); - it('should call `onText` callback on match', function (done) { - var bot = new Telegram(TOKEN, {webHook: true}); - bot.onText(/\/echo (.+)/, function (msg, match) { + 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(); - match[1].should.be.exactly('ECHO ALOHA'); + assert.equal(match[1], 'ECHO ALOHA'); done(); }); - var url = 'http://localhost:8443/bot'+TOKEN; + const url = `http://localhost:8443/bot${TOKEN}`; request({ - url: url, + url, method: 'POST', json: true, headers: { 'content-type': 'application/json', }, - body: {update_id: 0, message: {text: '/echo ECHO ALOHA'}} + body: { update_id: 0, message: { text: '/echo ECHO ALOHA' } } }); }); }); // End Telegram - -describe('#TelegramBotPolling', function () { - it('should call the callback on polling', function (done) { - var opts = {interval: 100, timeout: 1}; - var polling = new TelegramPolling(TOKEN, opts, function (msg) { +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) { done(); } }); // The second time _getUpdates is called it will return a message // Really dirty but it works - polling._getUpdates = function () { - return new Promise.resolve([{update_id: 10, message: {}}]); + polling._getUpdates = () => { + return new Promise.resolve([{ update_id: 10, message: {} }]); }; }); }); diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..7903b88 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1 @@ +--require babel-register \ No newline at end of file