From d6083e4327bcf43196a7305da7e7a93b4b006a0a Mon Sep 17 00:00:00 2001 From: GochoMugo Date: Thu, 16 Nov 2017 20:27:17 +0300 Subject: [PATCH 1/6] src: Support file options, defaults in sending files --- .eslintignore | 1 + doc/api.md | 61 ++++++++++++++----- doc/usage.md | 39 ++++++++++++ src/telegram.js | 138 +++++++++++++++++++++++++----------------- test/telegram.js | 7 --- test/test.sendfile.js | 109 +++++++++++++++++++++++++++++++++ 6 files changed, 275 insertions(+), 80 deletions(-) create mode 100644 test/test.sendfile.js diff --git a/.eslintignore b/.eslintignore index 417342d..66758cc 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ bin +lib *.md diff --git a/doc/api.md b/doc/api.md index adb8c25..031db25 100644 --- a/doc/api.md +++ b/doc/api.md @@ -30,13 +30,13 @@ TelegramBot * [.sendMessage(chatId, text, [options])](#TelegramBot+sendMessage) ⇒ Promise * [.answerInlineQuery(inlineQueryId, results, [options])](#TelegramBot+answerInlineQuery) ⇒ Promise * [.forwardMessage(chatId, fromChatId, messageId, [options])](#TelegramBot+forwardMessage) ⇒ Promise - * [.sendPhoto(chatId, photo, [options])](#TelegramBot+sendPhoto) ⇒ Promise - * [.sendAudio(chatId, audio, [options])](#TelegramBot+sendAudio) ⇒ Promise + * [.sendPhoto(chatId, photo, [options], [fileOpts])](#TelegramBot+sendPhoto) ⇒ Promise + * [.sendAudio(chatId, audio, [options], [fileOpts])](#TelegramBot+sendAudio) ⇒ Promise * [.sendDocument(chatId, doc, [options], [fileOpts])](#TelegramBot+sendDocument) ⇒ Promise * [.sendSticker(chatId, sticker, [options])](#TelegramBot+sendSticker) ⇒ Promise - * [.sendVideo(chatId, video, [options])](#TelegramBot+sendVideo) ⇒ Promise - * [.sendVideoNote(chatId, videoNote, [options])](#TelegramBot+sendVideoNote) ⇒ Promise - * [.sendVoice(chatId, voice, [options])](#TelegramBot+sendVoice) ⇒ Promise + * [.sendVideo(chatId, video, [options], [fileOpts])](#TelegramBot+sendVideo) ⇒ Promise + * [.sendVideoNote(chatId, videoNote, [options], [fileOpts])](#TelegramBot+sendVideoNote) ⇒ Promise + * [.sendVoice(chatId, voice, [options], [fileOpts])](#TelegramBot+sendVoice) ⇒ Promise * [.sendChatAction(chatId, action, [options])](#TelegramBot+sendChatAction) ⇒ Promise * [.kickChatMember(chatId, userId, [options])](#TelegramBot+kickChatMember) ⇒ Promise * [.unbanChatMember(chatId, userId, [options])](#TelegramBot+unbanChatMember) ⇒ Promise @@ -339,31 +339,41 @@ Forward messages of any kind. -### telegramBot.sendPhoto(chatId, photo, [options]) ⇒ Promise +### telegramBot.sendPhoto(chatId, photo, [options], [fileOpts]) ⇒ Promise Send photo **Kind**: instance method of [TelegramBot](#TelegramBot) -**See**: https://core.telegram.org/bots/api#sendphoto +**See** + +- https://core.telegram.org/bots/api#sendphoto +- https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files + | Param | Type | Description | | --- | --- | --- | | chatId | Number | String | Unique identifier for the message recipient | | photo | String | stream.Stream | Buffer | A file path or a Stream. Can also be a `file_id` previously uploaded | | [options] | Object | Additional Telegram query options | +| [fileOpts] | Object | Optional file related meta-data | -### telegramBot.sendAudio(chatId, audio, [options]) ⇒ Promise +### telegramBot.sendAudio(chatId, audio, [options], [fileOpts]) ⇒ Promise Send audio **Kind**: instance method of [TelegramBot](#TelegramBot) -**See**: https://core.telegram.org/bots/api#sendaudio +**See** + +- https://core.telegram.org/bots/api#sendaudio +- https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files + | Param | Type | Description | | --- | --- | --- | | chatId | Number | String | Unique identifier for the message recipient | | audio | String | stream.Stream | Buffer | A file path, Stream or Buffer. Can also be a `file_id` previously uploaded. | | [options] | Object | Additional Telegram query options | +| [fileOpts] | Object | Optional file related meta-data | @@ -371,7 +381,11 @@ Send audio Send Document **Kind**: instance method of [TelegramBot](#TelegramBot) -**See**: https://core.telegram.org/bots/api#sendDocument +**See** + +- https://core.telegram.org/bots/api#sendDocument +- https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files + | Param | Type | Description | | --- | --- | --- | @@ -396,46 +410,61 @@ Send .webp stickers. -### telegramBot.sendVideo(chatId, video, [options]) ⇒ Promise +### telegramBot.sendVideo(chatId, video, [options], [fileOpts]) ⇒ Promise Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). **Kind**: instance method of [TelegramBot](#TelegramBot) -**See**: https://core.telegram.org/bots/api#sendvideo +**See** + +- https://core.telegram.org/bots/api#sendvideo +- https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files + | Param | Type | Description | | --- | --- | --- | | chatId | Number | String | Unique identifier for the message recipient | | video | String | stream.Stream | Buffer | A file path or Stream. Can also be a `file_id` previously uploaded. | | [options] | Object | Additional Telegram query options | +| [fileOpts] | Object | Optional file related meta-data | -### telegramBot.sendVideoNote(chatId, videoNote, [options]) ⇒ Promise +### telegramBot.sendVideoNote(chatId, videoNote, [options], [fileOpts]) ⇒ Promise Use this method to send rounded square videos of upto 1 minute long. **Kind**: instance method of [TelegramBot](#TelegramBot) **Info**: The length parameter is actually optional. However, the API (at time of writing) requires you to always provide it until it is fixed. -**See**: https://core.telegram.org/bots/api#sendvideonote +**See** + +- https://core.telegram.org/bots/api#sendvideonote +- https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files + | Param | Type | Description | | --- | --- | --- | | chatId | Number | String | Unique identifier for the message recipient | | videoNote | String | stream.Stream | Buffer | A file path or Stream. Can also be a `file_id` previously uploaded. | | [options] | Object | Additional Telegram query options | +| [fileOpts] | Object | Optional file related meta-data | -### telegramBot.sendVoice(chatId, voice, [options]) ⇒ Promise +### telegramBot.sendVoice(chatId, voice, [options], [fileOpts]) ⇒ Promise Send voice **Kind**: instance method of [TelegramBot](#TelegramBot) -**See**: https://core.telegram.org/bots/api#sendvoice +**See** + +- https://core.telegram.org/bots/api#sendvoice +- https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files + | Param | Type | Description | | --- | --- | --- | | chatId | Number | String | Unique identifier for the message recipient | | voice | String | stream.Stream | Buffer | A file path, Stream or Buffer. Can also be a `file_id` previously uploaded. | | [options] | Object | Additional Telegram query options | +| [fileOpts] | Object | Optional file related meta-data | diff --git a/doc/usage.md b/doc/usage.md index 3a17700..e3499d3 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -115,6 +115,45 @@ const url = 'https://telegram.org/img/t_logo.png'; bot.sendPhoto(chatId, url); ``` +If you wish to explicitly specify the filename or +[MIME type](http://en.wikipedia.org/wiki/Internet_media_type), +you may pass the an additional argument as file options, like so: + +```js +const fileOpts = { + // Explicitly specify the file name. + filename: 'customfilename', + // Explicitly specify the MIME type. + contentType: 'audio/mpeg' +}; +bot.sendAudio(chatId, data, {}, fileOpts); +``` + + +### File Options (metadata) + +When sending files, the library automatically resolves +the `filename` and `contentType` properties. +**For now, this has to be manually activated using environment +variable `NTBA_FIX_350`.** + +In order of highest-to-lowest precedence in searching for +a value, when resolving the `filename`: + +1. Is `fileOptions.filename` explictly defined? +1. Does `Stream#path` exist? +1. Is `filepath` provided? +1. Default to `"filename"` + +And the `contentType`: + +1. Is `fileOptions.contentType` explictly-defined? +1. Does `Stream#path` exist? +1. Try detecting file-type from the `Buffer` +1. Is `filepath` provided? +1. Is `fileOptions.filename` explicitly defined? +1. Default to `"application/octet-stream` + ### Performance Issue diff --git a/src/telegram.js b/src/telegram.js index b69fa59..3d011bd 100644 --- a/src/telegram.js +++ b/src/telegram.js @@ -284,6 +284,9 @@ class TelegramBot extends EventEmitter { * Format data to be uploaded; handles file paths, streams and buffers * @param {String} type * @param {String|stream.Stream|Buffer} data + * @param {Object} fileOpts File options + * @param {String} [fileOpts.filename] File name + * @param {String} [fileOpts.contentType] Content type (i.e. MIME) * @return {Array} formatted * @return {Object} formatted[0] formData * @return {String} formatted[1] fileId @@ -291,55 +294,68 @@ class TelegramBot extends EventEmitter { * @see https://npmjs.com/package/file-type * @private */ - _formatSendData(type, data) { - let formData; - let fileName; - let fileId; + _formatSendData(type, data, fileOpts = {}) { + let filedata = data; + let filename = fileOpts.filename; + let contentType = fileOpts.contentType; + if (data instanceof stream.Stream) { - // Will be 'null' if could not be parsed. Default to 'filename'. - // For example, 'data.path' === '/?id=123' from 'request("https://example.com/?id=123")' - fileName = URL.parse(path.basename(data.path.toString())).pathname || 'filename'; - 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 errors.FatalError('Unsupported Buffer file type'); + if (!filename && data.path) { + // Will be 'null' if could not be parsed. + // For example, 'data.path' === '/?id=123' from 'request("https://example.com/?id=123")' + const url = URL.parse(path.basename(data.path.toString())); + filename = qs.unescape(url.pathname); } - formData = {}; - formData[type] = { - value: data, - options: { - filename: `data.${filetype.ext}`, - contentType: filetype.mime + } else if (Buffer.isBuffer(data)) { + if (!filename && !process.env.NTBA_FIX_350) { + deprecate('Buffers will have their filenames default to "filename" instead of "data".'); + filename = 'data'; + } + if (!contentType) { + const filetype = fileType(data); + if (filetype) { + contentType = filetype.mime; + const ext = filetype.ext; + if (ext && !process.env.NTBA_FIX_350) { + filename = `${filename}.${ext}`; + } + } else if (!process.env.NTBA_FIX_350) { + deprecate('An error will no longer be thrown if file-type of buffer could not be detected.'); + throw new errors.FatalError('Unsupported Buffer file-type'); } - }; - } else if (!this.options.filepath) { - /** - * When the constructor option 'filepath' is set to - * 'false', we do not support passing file-paths. - */ - fileId = data; - } else if (fs.existsSync(data)) { - fileName = path.basename(data); - formData = {}; - formData[type] = { - value: fs.createReadStream(data), - options: { - filename: fileName, - contentType: mime.lookup(fileName) + } + } else if (data) { + if (this.options.filepath && fs.existsSync(data)) { + filedata = fs.createReadStream(data); + if (!filename) { + filename = path.basename(data); } - }; + } else { + return [null, data]; + } } else { - fileId = data; + return [null, data]; } - return [formData, fileId]; + + filename = filename || 'filename'; + contentType = contentType || mime.lookup(filename); + if (process.env.NTBA_FIX_350) { + contentType = contentType || 'application/octet-stream'; + } else { + deprecate('In the future, content-type of files you send will default to "application/octet-stream".'); + } + + // TODO: Add missing file extension. + + return [{ + [type]: { + value: filedata, + options: { + filename, + contentType, + }, + }, + }, null]; } /** @@ -685,16 +701,18 @@ class TelegramBot extends EventEmitter { * @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 + * @param {Object} [fileOpts] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#sendphoto + * @see https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files */ - sendPhoto(chatId, photo, options = {}) { + sendPhoto(chatId, photo, options = {}, fileOpts = {}) { const opts = { qs: options, }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('photo', photo); + const sendData = this._formatSendData('photo', photo, fileOpts); opts.formData = sendData[0]; opts.qs.photo = sendData[1]; } catch (ex) { @@ -709,16 +727,18 @@ class TelegramBot extends EventEmitter { * @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 + * @param {Object} [fileOpts] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#sendaudio + * @see https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files */ - sendAudio(chatId, audio, options = {}) { + sendAudio(chatId, audio, options = {}, fileOpts = {}) { const opts = { qs: options }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('audio', audio); + const sendData = this._formatSendData('audio', audio, fileOpts); opts.formData = sendData[0]; opts.qs.audio = sendData[1]; } catch (ex) { @@ -736,6 +756,7 @@ class TelegramBot extends EventEmitter { * @param {Object} [fileOpts] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#sendDocument + * @see https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files */ sendDocument(chatId, doc, options = {}, fileOpts = {}) { const opts = { @@ -743,15 +764,12 @@ class TelegramBot extends EventEmitter { }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('document', doc); + const sendData = this._formatSendData('document', doc, fileOpts); opts.formData = sendData[0]; opts.qs.document = sendData[1]; } catch (ex) { return Promise.reject(ex); } - if (opts.formData && Object.keys(fileOpts).length) { - opts.formData.document.options = fileOpts; - } return this._request('sendDocument', opts); } @@ -785,16 +803,18 @@ class TelegramBot extends EventEmitter { * @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 + * @param {Object} [fileOpts] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#sendvideo + * @see https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files */ - sendVideo(chatId, video, options = {}) { + sendVideo(chatId, video, options = {}, fileOpts = {}) { const opts = { qs: options }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('video', video); + const sendData = this._formatSendData('video', video, fileOpts); opts.formData = sendData[0]; opts.qs.video = sendData[1]; } catch (ex) { @@ -809,17 +829,19 @@ class TelegramBot extends EventEmitter { * @param {String|stream.Stream|Buffer} videoNote A file path or Stream. * Can also be a `file_id` previously uploaded. * @param {Object} [options] Additional Telegram query options + * @param {Object} [fileOpts] Optional file related meta-data * @return {Promise} * @info The length parameter is actually optional. However, the API (at time of writing) requires you to always provide it until it is fixed. * @see https://core.telegram.org/bots/api#sendvideonote + * @see https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files */ - sendVideoNote(chatId, videoNote, options = {}) { + sendVideoNote(chatId, videoNote, options = {}, fileOpts = {}) { const opts = { qs: options }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('video_note', videoNote); + const sendData = this._formatSendData('video_note', videoNote, fileOpts); opts.formData = sendData[0]; opts.qs.video_note = sendData[1]; } catch (ex) { @@ -834,16 +856,18 @@ class TelegramBot extends EventEmitter { * @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 + * @param {Object} [fileOpts] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#sendvoice + * @see https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files */ - sendVoice(chatId, voice, options = {}) { + sendVoice(chatId, voice, options = {}, fileOpts = {}) { const opts = { qs: options }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('voice', voice); + const sendData = this._formatSendData('voice', voice, fileOpts); opts.formData = sendData[0]; opts.qs.voice = sendData[1]; } catch (ex) { diff --git a/test/telegram.js b/test/telegram.js index b300ab2..c0501cf 100644 --- a/test/telegram.js +++ b/test/telegram.js @@ -630,13 +630,6 @@ describe('TelegramBot', function telegramSuite() { assert.ok(is.object(resp.document)); }); }); - it('should send a document with custom file options', function test() { - const document = fs.createReadStream(`${__dirname}/data/photo.gif`); - const fileOpts = { filename: 'customfilename.gif' }; - return bot.sendDocument(USERID, document, {}, fileOpts).then(resp => { - assert.equal(resp.document.file_name, fileOpts.filename); - }); - }); }); describe('#sendSticker', function sendStickerSuite() { diff --git a/test/test.sendfile.js b/test/test.sendfile.js new file mode 100644 index 0000000..864d900 --- /dev/null +++ b/test/test.sendfile.js @@ -0,0 +1,109 @@ +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const TelegramBot = require('..'); + +const paths = { + audio: path.join(__dirname, "data/audio.mp3"), +}; + + +// TODO:Enable all other tests +describe.only('sending files', function sendfileSuite() { + const bot = new TelegramBot("token"); + + before(function beforeSuite() { + process.env.NTBA_FIX_350 = 1; + }); + after(function afterSuite() { + delete process.env.NTBA_FIX_350; + }); + + describe('using fileOptions', function sendfileOptionsSuite() { + const type = 'file'; + const stream = fs.createReadStream(paths.audio); + const nonPathStream = fs.createReadStream(paths.audio); + const buffer = fs.readFileSync(paths.audio); + const nonDetectableBuffer = fs.readFileSync(__filename); + const filepath = paths.audio; + const fileId = 'fileId'; + const files = [stream, nonPathStream, buffer, nonDetectableBuffer, filepath]; + + delete nonPathStream.path; + + describe('filename', function filenameSuite() { + it('(1) fileOptions.filename', function test() { + const filename = 'custom-filename'; + files.forEach((file) => { + const [{ [type]: data }] = bot._formatSendData(type, file, { filename }); + assert.equal(data.options.filename, filename); + }); + }); + + it('(2) Stream#path', function test() { + if (!stream.path) { + return this.skip('Stream#path unsupported'); + } + const [{ [type]: data }] = bot._formatSendData(type, stream); + assert.equal(data.options.filename, path.basename(paths.audio)); + }); + + it('(3) filepath', function test() { + const [{ [type]: data }] = bot._formatSendData(type, filepath); + assert.equal(data.options.filename, path.basename(paths.audio)); + }); + + it('(4) final default', function test() { + [nonPathStream, buffer, nonDetectableBuffer].forEach((file) => { + const [{ [type]: data }] = bot._formatSendData(type, file); + assert.equal(data.options.filename, 'filename'); + }); + }); + }); + + describe('contentType', function contentTypeSuite() { + it('(1) fileOpts.contentType', function test() { + const contentType = 'application/custom-type'; + files.forEach((file) => { + const [{ [type]: data }] = bot._formatSendData(type, file, { contentType }); + assert.equal(data.options.contentType, contentType); + }); + }); + + it('(2) Stream#path', function test() { + if (!stream.path) { + return this.skip('Stream#path unsupported'); + } + const [{ [type]: data }] = bot._formatSendData(type, stream); + assert.equal(data.options.contentType, 'audio/mpeg'); + }); + + it('(3) Buffer file-type', function test() { + const [{ [type]: data }] = bot._formatSendData(type, buffer); + assert.equal(data.options.contentType, 'audio/mpeg'); + }); + + it('(4) filepath', function test() { + const [{ [type]: data }] = bot._formatSendData(type, filepath); + assert.equal(data.options.contentType, 'audio/mpeg'); + }); + + it('(5) fileOptions.filename', function test() { + [nonPathStream, nonDetectableBuffer].forEach((file) => { + const [{ [type]: data }] = bot._formatSendData(type, file, { + filename: 'image.gif', + }); + assert.equal(data.options.contentType, 'image/gif'); + }); + }); + + it('(6) Final default', function test() { + [nonPathStream, nonDetectableBuffer].forEach((file) => { + const [{ [type]: data }] = bot._formatSendData(type, file); + assert.equal(data.options.contentType, 'application/octet-stream'); + }); + }); + }); + + }); +}); From b774ff436fb30fa2f51dd6780a641e91cdf5b728 Mon Sep 17 00:00:00 2001 From: GochoMugo Date: Thu, 16 Nov 2017 20:43:55 +0300 Subject: [PATCH 2/6] src: Minor fixes --- CHANGELOG.md | 9 ++++++++ doc/api.md | 36 +++++++++++++++--------------- doc/usage.md | 2 +- src/telegram.js | 52 ++++++++++++++++++++++--------------------- test/test.sendfile.js | 12 +++++----- 5 files changed, 61 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08deb09..54d0a5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,13 @@ Changed: 1. Update *TelegramBot#answerCallbackQuery()* signature (by @GochoMugo) 1. Improve default error logging of `polling_error` and `webhook_error` +Deprecated: + +1. Sending files: *(See [usage guide][usage-sending-file])* (by @hufan-akari, @GochoMugo) + * Error will **not** be thrown if `Buffer` is used and file-type could **not** be detected. + * Filename will **not** be set to `data.${ext}` if `Buffer` is used + * Content type will **not** default to `null` or `undefined` + Fixed: 1. Fix game example (by @MCSH) @@ -184,6 +191,8 @@ Fixed: 1. Fix typos (by oflisback) +[usage-sending-files]:https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files-options + [0.25.0]:https://github.com/yagop/node-telegram-bot-api/releases/tag/v0.25.0 [0.26.0]:https://github.com/yagop/node-telegram-bot-api/releases/tag/v0.26.0 [0.27.0]:https://github.com/yagop/node-telegram-bot-api/releases/tag/v0.27.0 diff --git a/doc/api.md b/doc/api.md index 031db25..7834fbe 100644 --- a/doc/api.md +++ b/doc/api.md @@ -30,13 +30,13 @@ TelegramBot * [.sendMessage(chatId, text, [options])](#TelegramBot+sendMessage) ⇒ Promise * [.answerInlineQuery(inlineQueryId, results, [options])](#TelegramBot+answerInlineQuery) ⇒ Promise * [.forwardMessage(chatId, fromChatId, messageId, [options])](#TelegramBot+forwardMessage) ⇒ Promise - * [.sendPhoto(chatId, photo, [options], [fileOpts])](#TelegramBot+sendPhoto) ⇒ Promise - * [.sendAudio(chatId, audio, [options], [fileOpts])](#TelegramBot+sendAudio) ⇒ Promise - * [.sendDocument(chatId, doc, [options], [fileOpts])](#TelegramBot+sendDocument) ⇒ Promise + * [.sendPhoto(chatId, photo, [options], [fileOptions])](#TelegramBot+sendPhoto) ⇒ Promise + * [.sendAudio(chatId, audio, [options], [fileOptions])](#TelegramBot+sendAudio) ⇒ Promise + * [.sendDocument(chatId, doc, [options], [fileOptions])](#TelegramBot+sendDocument) ⇒ Promise * [.sendSticker(chatId, sticker, [options])](#TelegramBot+sendSticker) ⇒ Promise - * [.sendVideo(chatId, video, [options], [fileOpts])](#TelegramBot+sendVideo) ⇒ Promise - * [.sendVideoNote(chatId, videoNote, [options], [fileOpts])](#TelegramBot+sendVideoNote) ⇒ Promise - * [.sendVoice(chatId, voice, [options], [fileOpts])](#TelegramBot+sendVoice) ⇒ Promise + * [.sendVideo(chatId, video, [options], [fileOptions])](#TelegramBot+sendVideo) ⇒ Promise + * [.sendVideoNote(chatId, videoNote, [options], [fileOptions])](#TelegramBot+sendVideoNote) ⇒ Promise + * [.sendVoice(chatId, voice, [options], [fileOptions])](#TelegramBot+sendVoice) ⇒ Promise * [.sendChatAction(chatId, action, [options])](#TelegramBot+sendChatAction) ⇒ Promise * [.kickChatMember(chatId, userId, [options])](#TelegramBot+kickChatMember) ⇒ Promise * [.unbanChatMember(chatId, userId, [options])](#TelegramBot+unbanChatMember) ⇒ Promise @@ -339,7 +339,7 @@ Forward messages of any kind. -### telegramBot.sendPhoto(chatId, photo, [options], [fileOpts]) ⇒ Promise +### telegramBot.sendPhoto(chatId, photo, [options], [fileOptions]) ⇒ Promise Send photo **Kind**: instance method of [TelegramBot](#TelegramBot) @@ -354,11 +354,11 @@ Send photo | chatId | Number | String | Unique identifier for the message recipient | | photo | String | stream.Stream | Buffer | A file path or a Stream. Can also be a `file_id` previously uploaded | | [options] | Object | Additional Telegram query options | -| [fileOpts] | Object | Optional file related meta-data | +| [fileOptions] | Object | Optional file related meta-data | -### telegramBot.sendAudio(chatId, audio, [options], [fileOpts]) ⇒ Promise +### telegramBot.sendAudio(chatId, audio, [options], [fileOptions]) ⇒ Promise Send audio **Kind**: instance method of [TelegramBot](#TelegramBot) @@ -373,11 +373,11 @@ Send audio | chatId | Number | String | Unique identifier for the message recipient | | audio | String | stream.Stream | Buffer | A file path, Stream or Buffer. Can also be a `file_id` previously uploaded. | | [options] | Object | Additional Telegram query options | -| [fileOpts] | Object | Optional file related meta-data | +| [fileOptions] | Object | Optional file related meta-data | -### telegramBot.sendDocument(chatId, doc, [options], [fileOpts]) ⇒ Promise +### telegramBot.sendDocument(chatId, doc, [options], [fileOptions]) ⇒ Promise Send Document **Kind**: instance method of [TelegramBot](#TelegramBot) @@ -392,7 +392,7 @@ Send Document | chatId | Number | String | Unique identifier for the message recipient | | doc | String | stream.Stream | Buffer | A file path, Stream or Buffer. Can also be a `file_id` previously uploaded. | | [options] | Object | Additional Telegram query options | -| [fileOpts] | Object | Optional file related meta-data | +| [fileOptions] | Object | Optional file related meta-data | @@ -410,7 +410,7 @@ Send .webp stickers. -### telegramBot.sendVideo(chatId, video, [options], [fileOpts]) ⇒ Promise +### telegramBot.sendVideo(chatId, video, [options], [fileOptions]) ⇒ Promise Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). **Kind**: instance method of [TelegramBot](#TelegramBot) @@ -425,11 +425,11 @@ Use this method to send video files, Telegram clients support mp4 videos (other | chatId | Number | String | Unique identifier for the message recipient | | video | String | stream.Stream | Buffer | A file path or Stream. Can also be a `file_id` previously uploaded. | | [options] | Object | Additional Telegram query options | -| [fileOpts] | Object | Optional file related meta-data | +| [fileOptions] | Object | Optional file related meta-data | -### telegramBot.sendVideoNote(chatId, videoNote, [options], [fileOpts]) ⇒ Promise +### telegramBot.sendVideoNote(chatId, videoNote, [options], [fileOptions]) ⇒ Promise Use this method to send rounded square videos of upto 1 minute long. **Kind**: instance method of [TelegramBot](#TelegramBot) @@ -445,11 +445,11 @@ Use this method to send rounded square videos of upto 1 minute long. | chatId | Number | String | Unique identifier for the message recipient | | videoNote | String | stream.Stream | Buffer | A file path or Stream. Can also be a `file_id` previously uploaded. | | [options] | Object | Additional Telegram query options | -| [fileOpts] | Object | Optional file related meta-data | +| [fileOptions] | Object | Optional file related meta-data | -### telegramBot.sendVoice(chatId, voice, [options], [fileOpts]) ⇒ Promise +### telegramBot.sendVoice(chatId, voice, [options], [fileOptions]) ⇒ Promise Send voice **Kind**: instance method of [TelegramBot](#TelegramBot) @@ -464,7 +464,7 @@ Send voice | chatId | Number | String | Unique identifier for the message recipient | | voice | String | stream.Stream | Buffer | A file path, Stream or Buffer. Can also be a `file_id` previously uploaded. | | [options] | Object | Additional Telegram query options | -| [fileOpts] | Object | Optional file related meta-data | +| [fileOptions] | Object | Optional file related meta-data | diff --git a/doc/usage.md b/doc/usage.md index e3499d3..d6247b1 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -129,7 +129,7 @@ const fileOpts = { bot.sendAudio(chatId, data, {}, fileOpts); ``` - + ### File Options (metadata) When sending files, the library automatically resolves diff --git a/src/telegram.js b/src/telegram.js index 3d011bd..426737c 100644 --- a/src/telegram.js +++ b/src/telegram.js @@ -284,9 +284,9 @@ class TelegramBot extends EventEmitter { * Format data to be uploaded; handles file paths, streams and buffers * @param {String} type * @param {String|stream.Stream|Buffer} data - * @param {Object} fileOpts File options - * @param {String} [fileOpts.filename] File name - * @param {String} [fileOpts.contentType] Content type (i.e. MIME) + * @param {Object} fileOptions File options + * @param {String} [fileOptions.filename] File name + * @param {String} [fileOptions.contentType] Content type (i.e. MIME) * @return {Array} formatted * @return {Object} formatted[0] formData * @return {String} formatted[1] fileId @@ -294,17 +294,19 @@ class TelegramBot extends EventEmitter { * @see https://npmjs.com/package/file-type * @private */ - _formatSendData(type, data, fileOpts = {}) { + _formatSendData(type, data, fileOptions = {}) { let filedata = data; - let filename = fileOpts.filename; - let contentType = fileOpts.contentType; + let filename = fileOptions.filename; + let contentType = fileOptions.contentType; if (data instanceof stream.Stream) { if (!filename && data.path) { // Will be 'null' if could not be parsed. // For example, 'data.path' === '/?id=123' from 'request("https://example.com/?id=123")' const url = URL.parse(path.basename(data.path.toString())); - filename = qs.unescape(url.pathname); + if (url.pathname) { + filename = qs.unescape(url.pathname); + } } } else if (Buffer.isBuffer(data)) { if (!filename && !process.env.NTBA_FIX_350) { @@ -701,18 +703,18 @@ class TelegramBot extends EventEmitter { * @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 - * @param {Object} [fileOpts] Optional file related meta-data + * @param {Object} [fileOptions] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#sendphoto * @see https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files */ - sendPhoto(chatId, photo, options = {}, fileOpts = {}) { + sendPhoto(chatId, photo, options = {}, fileOptions = {}) { const opts = { qs: options, }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('photo', photo, fileOpts); + const sendData = this._formatSendData('photo', photo, fileOptions); opts.formData = sendData[0]; opts.qs.photo = sendData[1]; } catch (ex) { @@ -727,18 +729,18 @@ class TelegramBot extends EventEmitter { * @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 - * @param {Object} [fileOpts] Optional file related meta-data + * @param {Object} [fileOptions] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#sendaudio * @see https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files */ - sendAudio(chatId, audio, options = {}, fileOpts = {}) { + sendAudio(chatId, audio, options = {}, fileOptions = {}) { const opts = { qs: options }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('audio', audio, fileOpts); + const sendData = this._formatSendData('audio', audio, fileOptions); opts.formData = sendData[0]; opts.qs.audio = sendData[1]; } catch (ex) { @@ -753,18 +755,18 @@ class TelegramBot extends EventEmitter { * @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 - * @param {Object} [fileOpts] Optional file related meta-data + * @param {Object} [fileOptions] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#sendDocument * @see https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files */ - sendDocument(chatId, doc, options = {}, fileOpts = {}) { + sendDocument(chatId, doc, options = {}, fileOptions = {}) { const opts = { qs: options }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('document', doc, fileOpts); + const sendData = this._formatSendData('document', doc, fileOptions); opts.formData = sendData[0]; opts.qs.document = sendData[1]; } catch (ex) { @@ -803,18 +805,18 @@ class TelegramBot extends EventEmitter { * @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 - * @param {Object} [fileOpts] Optional file related meta-data + * @param {Object} [fileOptions] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#sendvideo * @see https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files */ - sendVideo(chatId, video, options = {}, fileOpts = {}) { + sendVideo(chatId, video, options = {}, fileOptions = {}) { const opts = { qs: options }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('video', video, fileOpts); + const sendData = this._formatSendData('video', video, fileOptions); opts.formData = sendData[0]; opts.qs.video = sendData[1]; } catch (ex) { @@ -829,19 +831,19 @@ class TelegramBot extends EventEmitter { * @param {String|stream.Stream|Buffer} videoNote A file path or Stream. * Can also be a `file_id` previously uploaded. * @param {Object} [options] Additional Telegram query options - * @param {Object} [fileOpts] Optional file related meta-data + * @param {Object} [fileOptions] Optional file related meta-data * @return {Promise} * @info The length parameter is actually optional. However, the API (at time of writing) requires you to always provide it until it is fixed. * @see https://core.telegram.org/bots/api#sendvideonote * @see https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files */ - sendVideoNote(chatId, videoNote, options = {}, fileOpts = {}) { + sendVideoNote(chatId, videoNote, options = {}, fileOptions = {}) { const opts = { qs: options }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('video_note', videoNote, fileOpts); + const sendData = this._formatSendData('video_note', videoNote, fileOptions); opts.formData = sendData[0]; opts.qs.video_note = sendData[1]; } catch (ex) { @@ -856,18 +858,18 @@ class TelegramBot extends EventEmitter { * @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 - * @param {Object} [fileOpts] Optional file related meta-data + * @param {Object} [fileOptions] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#sendvoice * @see https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files */ - sendVoice(chatId, voice, options = {}, fileOpts = {}) { + sendVoice(chatId, voice, options = {}, fileOptions = {}) { const opts = { qs: options }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('voice', voice, fileOpts); + const sendData = this._formatSendData('voice', voice, fileOptions); opts.formData = sendData[0]; opts.qs.voice = sendData[1]; } catch (ex) { diff --git a/test/test.sendfile.js b/test/test.sendfile.js index 864d900..41fa94c 100644 --- a/test/test.sendfile.js +++ b/test/test.sendfile.js @@ -4,13 +4,13 @@ const path = require('path'); const TelegramBot = require('..'); const paths = { - audio: path.join(__dirname, "data/audio.mp3"), + audio: path.join(__dirname, 'data/audio.mp3'), }; // TODO:Enable all other tests describe.only('sending files', function sendfileSuite() { - const bot = new TelegramBot("token"); + const bot = new TelegramBot('token '); before(function beforeSuite() { process.env.NTBA_FIX_350 = 1; @@ -26,7 +26,6 @@ describe.only('sending files', function sendfileSuite() { const buffer = fs.readFileSync(paths.audio); const nonDetectableBuffer = fs.readFileSync(__filename); const filepath = paths.audio; - const fileId = 'fileId'; const files = [stream, nonPathStream, buffer, nonDetectableBuffer, filepath]; delete nonPathStream.path; @@ -42,7 +41,8 @@ describe.only('sending files', function sendfileSuite() { it('(2) Stream#path', function test() { if (!stream.path) { - return this.skip('Stream#path unsupported'); + this.skip('Stream#path unsupported'); + return; } const [{ [type]: data }] = bot._formatSendData(type, stream); assert.equal(data.options.filename, path.basename(paths.audio)); @@ -72,7 +72,8 @@ describe.only('sending files', function sendfileSuite() { it('(2) Stream#path', function test() { if (!stream.path) { - return this.skip('Stream#path unsupported'); + this.skip('Stream#path unsupported'); + return; } const [{ [type]: data }] = bot._formatSendData(type, stream); assert.equal(data.options.contentType, 'audio/mpeg'); @@ -104,6 +105,5 @@ describe.only('sending files', function sendfileSuite() { }); }); }); - }); }); From 3b603b1dbdca5fbc59124041a15113c5e2366202 Mon Sep 17 00:00:00 2001 From: GochoMugo Date: Tue, 5 Dec 2017 13:59:32 +0300 Subject: [PATCH 3/6] src: Minor fix, complete TODO --- .eslintignore | 1 - test/test.sendfile.js | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.eslintignore b/.eslintignore index 66758cc..417342d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,2 @@ bin -lib *.md diff --git a/test/test.sendfile.js b/test/test.sendfile.js index 41fa94c..7b69db7 100644 --- a/test/test.sendfile.js +++ b/test/test.sendfile.js @@ -8,8 +8,7 @@ const paths = { }; -// TODO:Enable all other tests -describe.only('sending files', function sendfileSuite() { +describe('sending files', function sendfileSuite() { const bot = new TelegramBot('token '); before(function beforeSuite() { From 8edb687283853202cfd1bd924bc6802e483e6658 Mon Sep 17 00:00:00 2001 From: GochoMugo Date: Tue, 5 Dec 2017 14:30:45 +0300 Subject: [PATCH 4/6] src: Add fileOptions to remaining file-sending methods --- doc/api.md | 30 ++++++++++++++++++------------ src/telegram.js | 30 ++++++++++++++++++------------ 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/doc/api.md b/doc/api.md index 7834fbe..46993ef 100644 --- a/doc/api.md +++ b/doc/api.md @@ -22,7 +22,7 @@ TelegramBot * [.closeWebHook()](#TelegramBot+closeWebHook) ⇒ Promise * [.hasOpenWebHook()](#TelegramBot+hasOpenWebHook) ⇒ Boolean * [.getMe([options])](#TelegramBot+getMe) ⇒ Promise - * [.setWebHook(url, [options])](#TelegramBot+setWebHook) ⇒ Promise + * [.setWebHook(url, [options], [fileOptions])](#TelegramBot+setWebHook) ⇒ Promise * [.deleteWebHook([options])](#TelegramBot+deleteWebHook) ⇒ Promise * [.getWebHookInfo([options])](#TelegramBot+getWebHookInfo) ⇒ Promise * [.getUpdates([options])](#TelegramBot+getUpdates) ⇒ Promise @@ -33,7 +33,7 @@ TelegramBot * [.sendPhoto(chatId, photo, [options], [fileOptions])](#TelegramBot+sendPhoto) ⇒ Promise * [.sendAudio(chatId, audio, [options], [fileOptions])](#TelegramBot+sendAudio) ⇒ Promise * [.sendDocument(chatId, doc, [options], [fileOptions])](#TelegramBot+sendDocument) ⇒ Promise - * [.sendSticker(chatId, sticker, [options])](#TelegramBot+sendSticker) ⇒ Promise + * [.sendSticker(chatId, sticker, [options], [fileOptions])](#TelegramBot+sendSticker) ⇒ Promise * [.sendVideo(chatId, video, [options], [fileOptions])](#TelegramBot+sendVideo) ⇒ Promise * [.sendVideoNote(chatId, videoNote, [options], [fileOptions])](#TelegramBot+sendVideoNote) ⇒ Promise * [.sendVoice(chatId, voice, [options], [fileOptions])](#TelegramBot+sendVoice) ⇒ Promise @@ -43,7 +43,7 @@ TelegramBot * [.restrictChatMember(chatId, userId, [options])](#TelegramBot+restrictChatMember) ⇒ Promise * [.promoteChatMember(chatId, userId, [options])](#TelegramBot+promoteChatMember) ⇒ Promise * [.exportChatInviteLink(chatId, [options])](#TelegramBot+exportChatInviteLink) ⇒ Promise - * [.setChatPhoto(chatId, photo, [options])](#TelegramBot+setChatPhoto) ⇒ Promise + * [.setChatPhoto(chatId, photo, [options], [fileOptions])](#TelegramBot+setChatPhoto) ⇒ Promise * [.deleteChatPhoto(chatId, [options])](#TelegramBot+deleteChatPhoto) ⇒ Promise * [.setChatTitle(chatId, title, [options])](#TelegramBot+setChatTitle) ⇒ Promise * [.setChatDescription(chatId, description, [options])](#TelegramBot+setChatDescription) ⇒ Promise @@ -81,9 +81,9 @@ TelegramBot * [.answerShippingQuery(shippingQueryId, ok, [options])](#TelegramBot+answerShippingQuery) ⇒ Promise * [.answerPreCheckoutQuery(preCheckoutQueryId, ok, [options])](#TelegramBot+answerPreCheckoutQuery) ⇒ Promise * [.getStickerSet(name, [options])](#TelegramBot+getStickerSet) ⇒ Promise - * [.uploadStickerFile(userId, pngSticker, [options])](#TelegramBot+uploadStickerFile) ⇒ Promise - * [.createNewStickerSet(userId, name, title, pngSticker, emojis, [options])](#TelegramBot+createNewStickerSet) ⇒ Promise - * [.addStickerToSet(userId, name, pngSticker, emojis, [options])](#TelegramBot+addStickerToSet) ⇒ Promise + * [.uploadStickerFile(userId, pngSticker, [options], [fileOptions])](#TelegramBot+uploadStickerFile) ⇒ Promise + * [.createNewStickerSet(userId, name, title, pngSticker, emojis, [options], [fileOptions])](#TelegramBot+createNewStickerSet) ⇒ Promise + * [.addStickerToSet(userId, name, pngSticker, emojis, [options], [fileOptions])](#TelegramBot+addStickerToSet) ⇒ Promise * [.setStickerPositionInSet(sticker, position, [options])](#TelegramBot+setStickerPositionInSet) ⇒ Promise * [.deleteStickerFromSet(sticker, [options])](#TelegramBot+deleteStickerFromSet) ⇒ Promise * _static_ @@ -225,7 +225,7 @@ Returns basic information about the bot in form of a `User` object. -### telegramBot.setWebHook(url, [options]) ⇒ Promise +### telegramBot.setWebHook(url, [options], [fileOptions]) ⇒ Promise Specify an url to receive incoming updates via an outgoing webHook. This method has an [older, compatible signature][setWebHook-v0.25.0] that is being deprecated. @@ -238,6 +238,7 @@ that is being deprecated. | url | String | URL where Telegram will make HTTP Post. Leave empty to delete webHook. | | [options] | Object | Additional Telegram query options | | [options.certificate] | String | stream.Stream | PEM certificate key (public). | +| [fileOptions] | Object | Optional file related meta-data | @@ -396,7 +397,7 @@ Send Document -### telegramBot.sendSticker(chatId, sticker, [options]) ⇒ Promise +### telegramBot.sendSticker(chatId, sticker, [options], [fileOptions]) ⇒ Promise Send .webp stickers. **Kind**: instance method of [TelegramBot](#TelegramBot) @@ -407,6 +408,7 @@ Send .webp stickers. | chatId | Number | String | Unique identifier for the message recipient | | sticker | String | stream.Stream | Buffer | A file path, Stream or Buffer. Can also be a `file_id` previously uploaded. Stickers are WebP format files. | | [options] | Object | Additional Telegram query options | +| [fileOptions] | Object | Optional file related meta-data | @@ -570,7 +572,7 @@ Returns exported invite link as String on success. -### telegramBot.setChatPhoto(chatId, photo, [options]) ⇒ Promise +### telegramBot.setChatPhoto(chatId, photo, [options], [fileOptions]) ⇒ Promise Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. @@ -583,6 +585,7 @@ Returns True on success. | chatId | Number | String | Unique identifier for the message recipient | | photo | stream.Stream | Buffer | A file path or a Stream. | | [options] | Object | Additional Telegram query options | +| [fileOptions] | Object | Optional file related meta-data | @@ -1154,7 +1157,7 @@ Use this method to get a sticker set. On success, a [StickerSet](https://core.te -### telegramBot.uploadStickerFile(userId, pngSticker, [options]) ⇒ Promise +### telegramBot.uploadStickerFile(userId, pngSticker, [options], [fileOptions]) ⇒ Promise Use this method to upload a .png file with a sticker for later use in *createNewStickerSet* and *addStickerToSet* methods (can be used multiple times). Returns the uploaded [File](https://core.telegram.org/bots/api#file) on success. @@ -1166,10 +1169,11 @@ times). Returns the uploaded [File](https://core.telegram.org/bots/api#file) on | userId | Number | User identifier of sticker file owner | | pngSticker | String | stream.Stream | Buffer | A file path or a Stream. Can also be a `file_id` previously uploaded. **Png** image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. | | [options] | Object | Additional Telegram query options | +| [fileOptions] | Object | Optional file related meta-data | -### telegramBot.createNewStickerSet(userId, name, title, pngSticker, emojis, [options]) ⇒ Promise +### telegramBot.createNewStickerSet(userId, name, title, pngSticker, emojis, [options], [fileOptions]) ⇒ Promise Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. Returns True on success. @@ -1189,10 +1193,11 @@ Returns True on success. | pngSticker | String | stream.Stream | Buffer | Png image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. | | emojis | String | One or more emoji corresponding to the sticker | | [options] | Object | Additional Telegram query options | +| [fileOptions] | Object | Optional file related meta-data | -### telegramBot.addStickerToSet(userId, name, pngSticker, emojis, [options]) ⇒ Promise +### telegramBot.addStickerToSet(userId, name, pngSticker, emojis, [options], [fileOptions]) ⇒ Promise Use this method to add a new sticker to a set created by the bot. Returns True on success. @@ -1210,6 +1215,7 @@ Returns True on success. | pngSticker | String | stream.Stream | Buffer | Png image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px | | emojis | String | One or more emoji corresponding to the sticker | | [options] | Object | Additional Telegram query options | +| [fileOptions] | Object | Optional file related meta-data | diff --git a/src/telegram.js b/src/telegram.js index 426737c..5df8468 100644 --- a/src/telegram.js +++ b/src/telegram.js @@ -469,10 +469,11 @@ class TelegramBot extends EventEmitter { * delete webHook. * @param {Object} [options] Additional Telegram query options * @param {String|stream.Stream} [options.certificate] PEM certificate key (public). + * @param {Object} [fileOptions] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#setwebhook */ - setWebHook(url, options = {}) { + setWebHook(url, options = {}, fileOptions = {}) { /* The older method signature was setWebHook(url, cert). * We need to ensure backwards-compatibility while maintaining * consistency of the method signatures throughout the library */ @@ -493,7 +494,7 @@ class TelegramBot extends EventEmitter { if (cert) { try { - const sendData = this._formatSendData('certificate', cert); + const sendData = this._formatSendData('certificate', cert, fileOptions); opts.formData = sendData[0]; opts.qs.certificate = sendData[1]; } catch (ex) { @@ -781,16 +782,17 @@ class TelegramBot extends EventEmitter { * @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 + * @param {Object} [fileOptions] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#sendsticker */ - sendSticker(chatId, sticker, options = {}) { + sendSticker(chatId, sticker, options = {}, fileOptions = {}) { const opts = { qs: options }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('sticker', sticker); + const sendData = this._formatSendData('sticker', sticker, fileOptions); opts.formData = sendData[0]; opts.qs.sticker = sendData[1]; } catch (ex) { @@ -994,16 +996,17 @@ class TelegramBot extends EventEmitter { * @param {Number|String} chatId Unique identifier for the message recipient * @param {stream.Stream|Buffer} photo A file path or a Stream. * @param {Object} [options] Additional Telegram query options + * @param {Object} [fileOptions] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#setchatphoto */ - setChatPhoto(chatId, photo, options = {}) { + setChatPhoto(chatId, photo, options = {}, fileOptions = {}) { const opts = { qs: options, }; opts.qs.chat_id = chatId; try { - const sendData = this._formatSendData('photo', photo); + const sendData = this._formatSendData('photo', photo, fileOptions); opts.formData = sendData[0]; opts.qs.photo = sendData[1]; } catch (ex) { @@ -1659,16 +1662,17 @@ class TelegramBot extends EventEmitter { * @param {String|stream.Stream|Buffer} pngSticker A file path or a Stream. Can also be a `file_id` previously uploaded. **Png** image with the * sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. * @param {Object} [options] Additional Telegram query options + * @param {Object} [fileOptions] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#uploadstickerfile */ - uploadStickerFile(userId, pngSticker, options = {}) { + uploadStickerFile(userId, pngSticker, options = {}, fileOptions = {}) { const opts = { qs: options, }; opts.qs.user_id = userId; try { - const sendData = this._formatSendData('png_sticker', pngSticker); + const sendData = this._formatSendData('png_sticker', pngSticker, fileOptions); opts.formData = sendData[0]; opts.qs.png_sticker = sendData[1]; } catch (ex) { @@ -1689,11 +1693,12 @@ class TelegramBot extends EventEmitter { * dimensions must not exceed 512px, and either width or height must be exactly 512px. * @param {String} emojis One or more emoji corresponding to the sticker * @param {Object} [options] Additional Telegram query options + * @param {Object} [fileOptions] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#createnewstickerset * @todo Add tests for this method! */ - createNewStickerSet(userId, name, title, pngSticker, emojis, options = {}) { + createNewStickerSet(userId, name, title, pngSticker, emojis, options = {}, fileOptions = {}) { const opts = { qs: options, }; @@ -1703,7 +1708,7 @@ class TelegramBot extends EventEmitter { opts.qs.emojis = emojis; opts.qs.mask_position = stringify(options.mask_position); try { - const sendData = this._formatSendData('png_sticker', pngSticker); + const sendData = this._formatSendData('png_sticker', pngSticker, fileOptions); opts.formData = sendData[0]; opts.qs.png_sticker = sendData[1]; } catch (ex) { @@ -1722,11 +1727,12 @@ class TelegramBot extends EventEmitter { * dimensions must not exceed 512px, and either width or height must be exactly 512px * @param {String} emojis One or more emoji corresponding to the sticker * @param {Object} [options] Additional Telegram query options + * @param {Object} [fileOptions] Optional file related meta-data * @return {Promise} * @see https://core.telegram.org/bots/api#addstickertoset * @todo Add tests for this method! */ - addStickerToSet(userId, name, pngSticker, emojis, options = {}) { + addStickerToSet(userId, name, pngSticker, emojis, options = {}, fileOptions = {}) { const opts = { qs: options, }; @@ -1735,7 +1741,7 @@ class TelegramBot extends EventEmitter { opts.qs.emojis = emojis; opts.qs.mask_position = stringify(options.mask_position); try { - const sendData = this._formatSendData('png_sticker', pngSticker); + const sendData = this._formatSendData('png_sticker', pngSticker, fileOptions); opts.formData = sendData[0]; opts.qs.png_sticker = sendData[1]; } catch (ex) { From 4051117ed023c4e59c073091679ffe684be99599 Mon Sep 17 00:00:00 2001 From: GochoMugo Date: Mon, 11 Dec 2017 18:24:22 +0300 Subject: [PATCH 5/6] src: Minor reorganisation, fixes --- doc/usage.md | 14 ++++--- test/telegram.js | 28 +------------- ...t.sendfile.js => test.format-send-data.js} | 37 +++++++++++++++++-- 3 files changed, 44 insertions(+), 35 deletions(-) rename test/{test.sendfile.js => test.format-send-data.js} (74%) diff --git a/doc/usage.md b/doc/usage.md index d6247b1..657846b 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -117,16 +117,16 @@ bot.sendPhoto(chatId, url); If you wish to explicitly specify the filename or [MIME type](http://en.wikipedia.org/wiki/Internet_media_type), -you may pass the an additional argument as file options, like so: +you may pass an additional argument as file options, like so: ```js -const fileOpts = { +const fileOptions = { // Explicitly specify the file name. filename: 'customfilename', // Explicitly specify the MIME type. - contentType: 'audio/mpeg' + contentType: 'audio/mpeg', }; -bot.sendAudio(chatId, data, {}, fileOpts); +bot.sendAudio(chatId, data, {}, fileOptions); ``` @@ -140,6 +140,10 @@ variable `NTBA_FIX_350`.** In order of highest-to-lowest precedence in searching for a value, when resolving the `filename`: +*(`fileOptions` is the Object argument passed to the method. +The "file" argument passed to the method can be a `Stream`, +`Buffer` or `filepath`.)* + 1. Is `fileOptions.filename` explictly defined? 1. Does `Stream#path` exist? 1. Is `filepath` provided? @@ -152,7 +156,7 @@ And the `contentType`: 1. Try detecting file-type from the `Buffer` 1. Is `filepath` provided? 1. Is `fileOptions.filename` explicitly defined? -1. Default to `"application/octet-stream` +1. Default to `"application/octet-stream"` ### Performance Issue diff --git a/test/telegram.js b/test/telegram.js index c0501cf..b69727e 100644 --- a/test/telegram.js +++ b/test/telegram.js @@ -1332,33 +1332,7 @@ describe('TelegramBot', function telegramSuite() { }); 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)); - }); - }); - it('should not accept file-paths if disallowed with constructor option', function test() { - const tgbot = new TelegramBot(TOKEN, { filepath: false }); - const photo = `${__dirname}/data/photo.gif`; - return tgbot.sendPhoto(USERID, photo).catch(err => { - // TODO: check for error in a better way - assert.ok(err.response.body.description.indexOf('Bad Request') !== -1); - }); - }); - it('should allow stream.path that can not be parsed', function test() { - const stream = fs.createReadStream(`${__dirname}/data/photo.gif`); - stream.path = '/?id=123'; // for example, 'http://example.com/?id=666' - return bot.sendPhoto(USERID, stream); - }); + }); describe('#sendInvoice', function sendInvoiceSuite() { diff --git a/test/test.sendfile.js b/test/test.format-send-data.js similarity index 74% rename from test/test.sendfile.js rename to test/test.format-send-data.js index 7b69db7..9782842 100644 --- a/test/test.sendfile.js +++ b/test/test.format-send-data.js @@ -8,8 +8,9 @@ const paths = { }; -describe('sending files', function sendfileSuite() { - const bot = new TelegramBot('token '); +describe('#_formatSendData', function sendfileSuite() { + const bot = new TelegramBot('token'); + const type = 'file'; before(function beforeSuite() { process.env.NTBA_FIX_350 = 1; @@ -19,7 +20,6 @@ describe('sending files', function sendfileSuite() { }); describe('using fileOptions', function sendfileOptionsSuite() { - const type = 'file'; const stream = fs.createReadStream(paths.audio); const nonPathStream = fs.createReadStream(paths.audio); const buffer = fs.readFileSync(paths.audio); @@ -105,4 +105,35 @@ describe('sending files', function sendfileSuite() { }); }); }); + + it('should handle buffer path from fs.readStream', function test() { + let file; + try { + file = fs.createReadStream(Buffer.from(paths.audio)); + } catch (ex) { + // Older Node.js versions do not support passing a Buffer + // representation of the path to fs.createReadStream() + if (ex instanceof TypeError) { + Promise.resolve(); + return; + } + } + const [{ [type]: data }] = bot._formatSendData('file', file); + assert.equal(data.options.filename, path.basename(paths.audio)); + }); + + it('should not accept file-paths if disallowed with constructor option', function test() { + const tgbot = new TelegramBot('token', { filepath: false }); + const [formData, fileId] = tgbot._formatSendData('file', paths.audio); + assert.ok(fileId); + assert.ok(!formData); + }); + + it('should allow stream.path that can not be parsed', function test() { + const stream = fs.createReadStream(paths.audio); + stream.path = '/?id=123'; // for example, 'http://example.com/?id=666' + assert.doesNotThrow(function assertDoesNotThrow() { + bot._formatSendData('file', stream); + }); + }); }); From d7197998ddda32f4a69562372487a1ceaf090987 Mon Sep 17 00:00:00 2001 From: GochoMugo Date: Mon, 11 Dec 2017 18:32:19 +0300 Subject: [PATCH 6/6] doc: Add note on specifying additional Telegram query options --- doc/usage.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/usage.md b/doc/usage.md index 657846b..9f73897 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -129,6 +129,20 @@ const fileOptions = { bot.sendAudio(chatId, data, {}, fileOptions); ``` +**NOTE:** You **MUST** provide an empty object (`{}`) in place of +*Additional Telegram query options*, if you have **no** query options +to specify. For example, + +```js +// WRONG! +// 'fileOptions' will be taken as additional Telegram query options!!! +bot.sendAudio(chatId, data, fileOptions); + +// RIGHT! +bot.sendAudio(chatId, data, {}, fileOptions); +``` + + ### File Options (metadata)