From e2d34118dac6171d8e572633ad5a90b1a649a4f6 Mon Sep 17 00:00:00 2001 From: Mahdi Dibaiee Date: Fri, 3 Jul 2015 02:19:36 +0430 Subject: [PATCH] Switch from form-data to restler, a lot better :thumbs_up: --- README.md | 10 ++++ build/fetch.js | 103 +++++---------------------------- build/index.js | 2 - build/types/File.js | 137 ++++++++++++++++++++++++++++++++++++++++++++ demo.js | 2 +- lib/fetch.js | 46 ++++----------- lib/index.js | 1 - lib/types/File.js | 90 +++++++++++++++++++++++++++++ package.json | 5 +- types/File.js | 1 + 10 files changed, 268 insertions(+), 129 deletions(-) create mode 100644 build/types/File.js create mode 100644 lib/types/File.js create mode 120000 types/File.js diff --git a/README.md b/README.md index 095cde9..9f96462 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,16 @@ The code is well documented. I'm trying to integrate JSDoc / ESDoc into our repo --- +If you are using `babel/polyfill` and you get an error requiring this modules, try: + +```javascript +require('telegram-api/build'); +``` + +This will bypass the `babel/polyfill` required by the module. + +--- + All Telegram API methods are accessible through `api` property of bots. ```javascript diff --git a/build/fetch.js b/build/fetch.js index 61f6d69..97275ee 100644 --- a/build/fetch.js +++ b/build/fetch.js @@ -8,103 +8,28 @@ exports.getBody = getBody; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } -var _https = require('https'); +var _restler = require('restler'); -var _https2 = _interopRequireDefault(_https); - -var _qs = require('qs'); - -var _qs2 = _interopRequireDefault(_qs); - -var _formData = require('form-data'); - -var _formData2 = _interopRequireDefault(_formData); +var _restler2 = _interopRequireDefault(_restler); function fetch(path) { - var data = arguments[1] === undefined ? { test: 1 } : arguments[1]; - - var form = new _formData2['default'](); - var keys = Object.keys(data); - - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; - - try { - for (var _iterator = keys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var key = _step.value; - - console.log(key, data[key]); - form.append(key, data[key]); - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator['return']) { - _iterator['return'](); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } - } + var data = arguments[1] === undefined ? {} : arguments[1]; return new Promise(function (resolve, reject) { - form.getLength(function (err, length) { - if (err) { - return reject(err); + var method = Object.keys(data).length ? 'POST' : 'GET'; + var multipart = method === 'POST' ? true : false; + + _restler2['default'].request('https://api.telegram.org/bot' + path, { + data: data, method: method, multipart: multipart + }).on('complete', function (response) { + try { + var json = JSON.parse(response); + resolve(json); + } catch (e) { + reject(e); } - - form.submit({ - protocol: 'https:', - host: 'api.telegram.org', - path: '/bot' + path, - headers: { - 'Content-Length': length - } - }, function (error, response) { - if (error) { - return reject(error); - } - - return getBody(response).then(function (body) { - try { - var json = JSON.parse(body); - resolve(json); - } catch (e) { - reject(e); - } - })['catch'](reject); - }); }); }); - - // console.log(form.getHeaders()); - // return new Promise((resolve, reject) => { - // const req = https.request({ - // hostname: 'api.telegram.org', - // method: keys.length ? 'POST' : 'GET', - // path: '/bot' + path, - // headers: form.getHeaders() - // }, response => { - // return getBody(response).then(res => { - // console.log(res); - // try { - // let json = JSON.parse(res); - // resolve(json); - // } catch(e) { - // reject(e); - // } - // }).catch(reject); - // }); - // - // form.pipe(req); - // }).catch(err => { - // console.error('Error sending request', err); - // }); } function getBody(stream) { diff --git a/build/index.js b/build/index.js index 4aaaa4b..fea9e9a 100644 --- a/build/index.js +++ b/build/index.js @@ -14,8 +14,6 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } -require('babel/polyfill'); - var _api = require('./api'); var _api2 = _interopRequireDefault(_api); diff --git a/build/types/File.js b/build/types/File.js new file mode 100644 index 0000000..bf1cf7e --- /dev/null +++ b/build/types/File.js @@ -0,0 +1,137 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + +var _Base2 = require('./Base'); + +var _Base3 = _interopRequireDefault(_Base2); + +var _mime = require('mime'); + +var _mime2 = _interopRequireDefault(_mime); + +var TYPES = ['photo', 'video', 'document', 'audio']; + +/** + * File class, used to send pictures/movies/audios/documents to chat + */ + +var File = (function (_Base) { + /** + * Create a new file instance + * @param {object} properties File properties, as defined by Telegram API + */ + + function File() { + var properties = arguments[0] === undefined ? {} : arguments[0]; + + _classCallCheck(this, File); + + _get(Object.getPrototypeOf(File.prototype), 'constructor', this).call(this, 'sendMessage'); + + this.properties = properties; + this._keyboard = new _Base3['default'](); + } + + _inherits(File, _Base); + + _createClass(File, [{ + key: 'to', + + /** + * Set chat_id of the message + * @param {number} chat + * @return {object} returns the message object + */ + value: function to(chat) { + this.properties.chat_id = chat; + return this; + } + }, { + key: 'file', + + /** + * Set file of the message + * @param {ReadableStream} stream File Stream + * @param {string} fileType (optional) if the first argument is a + * file_id string, this option indicates file type + * @return {object} returns the message object + */ + value: function file(stream, fileType) { + if (typeof stream === 'string') { + this.properties[fileType] = stream; + } + + var type = _mime2['default'].lookup(stream.path).split('/')[0]; + if (type === 'image') { + type = 'photo'; + } + + if (TYPES.indexOf(type) === -1) { + type = 'document'; + } + + this.properties[type] = stream; + + return this; + } + }, { + key: 'caption', + + /** + * Set caption for photos + * @param {string} text caption's text + * @return {object} returns the message object + */ + value: function caption(text) { + this.properties.caption = text; + return this; + } + }, { + key: 'reply', + + /** + * Set reply_to_message_id of the message + * @param {number} id message_id of the message to reply to + * @return {object} returns the message object + */ + value: function reply(id) { + this.properties.reply_to_message_id = id; + return this; + } + }, { + key: 'keyboard', + + /** + * Sets keyboard of the message + * The value of reply_markup is set to the sanitized keyboard properties + * i.e. reply_markup = JSON.stringify(kb.getProperties()) + * @param {object} kb A Keyboard instance + * @return {object} returns the message object + */ + value: function keyboard(kb) { + this._keyboard = kb; + return this; + } + + // This class inherits Base's send method + + }]); + + return File; +})(_Base3['default']); + +exports['default'] = File; +module.exports = exports['default']; diff --git a/demo.js b/demo.js index 66e0a65..c283512 100644 --- a/demo.js +++ b/demo.js @@ -5,7 +5,7 @@ var Message = require('telegram-api/types/Message'); var Question = require('telegram-api/types/Question'); var bot = new Bot({ - token: 'YOUR_KEY' + token: '121143906:AAE6pcpBoARNZZjr3fUpvKuLInJ5Eee5Ajk' }); bot.start().catch(err => { diff --git a/lib/fetch.js b/lib/fetch.js index 37b9dbf..d0ebe96 100644 --- a/lib/fetch.js +++ b/lib/fetch.js @@ -1,41 +1,19 @@ -import FormData from 'form-data'; +import restler from 'restler'; export default function fetch(path, data = {}) { - const form = new FormData(); - const keys = Object.keys(data); - - for (let key of keys) { - form.append(key, data[key]); - } - - return new Promise((resolve, reject) => { - form.getLength((err, length) => { - if (err) { - return reject(err); + const method = Object.keys(data).length ? 'POST' : 'GET'; + const multipart = method === 'POST' ? true : false; + + restler.request('https://api.telegram.org/bot' + path, { + data, method, multipart + }).on('complete', response => { + try { + let json = JSON.parse(response); + resolve(json); + } catch(e) { + reject(e); } - - form.submit({ - protocol: 'https:', - host: 'api.telegram.org', - path: '/bot' + path, - headers: { - 'Content-Length': length - } - }, (error, response) => { - if (error) { - return reject(error); - } - - return getBody(response).then(body => { - try { - let json = JSON.parse(body); - resolve(json); - } catch(e) { - reject(e); - } - }).catch(reject); - }); }); }); } diff --git a/lib/index.js b/lib/index.js index 5f3fdc0..134ddf7 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,4 +1,3 @@ -import 'babel/polyfill'; import API from './api'; import webhook from './webhook'; import poll from './poll'; diff --git a/lib/types/File.js b/lib/types/File.js new file mode 100644 index 0000000..da30c15 --- /dev/null +++ b/lib/types/File.js @@ -0,0 +1,90 @@ +import Base from './Base'; +import mime from 'mime'; + +const TYPES = ['photo', 'video', 'document', 'audio']; + +/** + * File class, used to send pictures/movies/audios/documents to chat + */ +export default class File extends Base { + /** + * Create a new file instance + * @param {object} properties File properties, as defined by Telegram API + */ + constructor(properties = {}) { + super('sendMessage'); + + this.properties = properties; + this._keyboard = new Base(); + } + + /** + * Set chat_id of the message + * @param {number} chat + * @return {object} returns the message object + */ + to(chat) { + this.properties.chat_id = chat; + return this; + } + + /** + * Set file of the message + * @param {ReadableStream} stream File Stream + * @param {string} fileType (optional) if the first argument is a + * file_id string, this option indicates file type + * @return {object} returns the message object + */ + file(stream, fileType) { + if (typeof stream === 'string') { + this.properties[fileType] = stream; + } + + let type = mime.lookup(stream.path).split('/')[0]; + if (type === 'image') { + type = 'photo'; + } + + if (TYPES.indexOf(type) === -1) { + type = 'document'; + } + + this.properties[type] = stream; + + return this; + } + + /** + * Set caption for photos + * @param {string} text caption's text + * @return {object} returns the message object + */ + caption(text) { + this.properties.caption = text; + return this; + } + + /** + * Set reply_to_message_id of the message + * @param {number} id message_id of the message to reply to + * @return {object} returns the message object + */ + reply(id) { + this.properties.reply_to_message_id = id; + return this; + } + + /** + * Sets keyboard of the message + * The value of reply_markup is set to the sanitized keyboard properties + * i.e. reply_markup = JSON.stringify(kb.getProperties()) + * @param {object} kb A Keyboard instance + * @return {object} returns the message object + */ + keyboard(kb) { + this._keyboard = kb; + return this; + } + + // This class inherits Base's send method +} diff --git a/package.json b/package.json index a087e23..9ec1402 100644 --- a/package.json +++ b/package.json @@ -31,12 +31,13 @@ "homepage": "https://github.com/mdibaiee/node-telegram-api", "dependencies": { "babel": "^5.6.14", - "form-data": "^1.0.0-rc1", "grunt": "^0.4.5", "grunt-babel": "^5.0.1", "grunt-contrib-symlink": "^0.3.0", "grunt-contrib-watch": "^0.6.1", "grunt-eslint": "^16.0.0", - "qs": "^3.1.0" + "mime": "^1.3.4", + "qs": "^3.1.0", + "restler": "^3.3.0" } } diff --git a/types/File.js b/types/File.js new file mode 120000 index 0000000..a835995 --- /dev/null +++ b/types/File.js @@ -0,0 +1 @@ +../build/types/File.js \ No newline at end of file