Compare commits

..

2 Commits

  1. 2
      .github/PULL_REQUEST_TEMPLATE.md
  2. 1
      .gitignore
  3. 5
      .travis.yml
  4. 83
      CHANGELOG.md
  5. 13
      CONTRIBUTING.md
  6. 21
      README.md
  7. 2
      doc/api.hbs
  8. 883
      doc/api.md
  9. 24
      doc/help.md
  10. 10
      doc/tutorials.md
  11. 76
      doc/usage.md
  12. 0
      examples/crt.pem
  13. 3
      examples/expressWebhook.js
  14. 140
      examples/game/game.html
  15. 53
      examples/game/game.js
  16. 2
      examples/herokuWebHook.js
  17. 6
      examples/httpsWebHook.js
  18. 0
      examples/key.pem
  19. 2
      examples/nowWebHook.js
  20. 14
      examples/openShiftWebHook.js
  21. 436
      package.json
  22. 6
      src/errors.js
  23. 1006
      src/telegram.js
  24. 66
      src/telegramPolling.js
  25. 2
      src/telegramWebHook.js
  26. 11
      test/README.md
  27. BIN
      test/data/chat_photo.png
  28. BIN
      test/data/sticker.png
  29. 340
      test/telegram.js
  30. 139
      test/test.format-send-data.js
  31. 12
      test/utils.js

@ -5,7 +5,7 @@ For example, if your PR passes all tests, you would mark the option as so:
Note the 'x' in between the square brackets '[]' Note the 'x' in between the square brackets '[]'
--> -->
- [ ] All tests pass - [ ] All tests pass
- [ ] I have run `npm run doc` - [ ] I have run `npm run gen-doc`
### Description ### Description

1
.gitignore vendored

@ -2,7 +2,6 @@ node_modules
coverage/ coverage/
npm-debug.log npm-debug.log
.package.json .package.json
package-lock.json
output.md output.md
output/ output/
lib/ lib/

@ -1,12 +1,9 @@
language: node_js language: node_js
node_js: node_js:
- "9"
- "8"
- "7"
- "6" - "6"
- "5" - "5"
- "4" - "4"
# - "0.12" - "0.12"
after_success: after_success:
- bash <(curl -s https://codecov.io/bash) - bash <(curl -s https://codecov.io/bash)
cache: cache:

@ -5,86 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased][Unreleased] ## [Unreleased][Unreleased]
* * *
## [0.30.0][0.30.0] - 2017-12-21
Added: Added:
1. Support Bot API v3.5: (by @GochoMugo)
* Allow `provider_data` parameter in *TelegramBot#sendInvoice*
* Add method *TelegramBot#sendMediaGroup()*
1. Support Bot API v3.4: (by @kamikazechaser)
* Add methods *TelegramBot#editMessageLiveLocation*, *TelegramBot#stopMessageLiveLocation* (#439)
* Add methods *TelegramBot#setChatStickerSet*, *TelegramBot#deleteChatStickerSet* (#440)
1. Add methods:
* *TelegramBot#getFileStream* (#442) (by @GochoMugo, requested-by @Xaqron)
1. Add options to *TelegramBot#stopPolling()* (by @GochoMugo)
1. Add `metadata` argument in `message` event (and friends e.g. `text`, `audio`, etc.) (#409) (by @jlsjonas, @GochoMugo)
1. Add forward-compatibility i.e. support future additional Telegram options (by @GochoMugo)
1. Add support for Node.js v9 (by @GochoMugo)
1. Document *TelegramBot.errors*, *TelegramBot.messageTypes* (by @GochoMugo)
Changed:
1. Update *TelegramBot#answerCallbackQuery()* signature (by @GochoMugo)
1. Improve default error logging of `polling_error` and `webhook_error` (#377)
1. Update dependencies
Deprecated:
1. Sending files: *(See [usage guide][usage-sending-files])* (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 the offset infinite loop bug (#265, #36) (by @GochoMugo)
1. Fix game example (#449, #418) (by @MCSH)
* * *
## [0.29.0][0.29.0] - 2017-10-22
Added:
1. Add Bot API v3.2 methods:
* (#429) *TelegramBot#getStickerSet* (by @CapacitorSet, @LibertyLocked)
* (#430) *TelegramBot#uploadStickerFile* (by @CapacitorSet)
* *TelegramBot#createNewStickerSet*, *TelegramBot#addStickerToSet*, *TelegramBot#setStickerPositionInSet*, *TelegramBot#deleteStickerFromSet* (by @GochoMugo)
1. Supports API v3.3
Deprecated:
1. Auto-enabling Promise cancellation (#319) (by @GochoMugo)
* * *
## [0.28.0][0.28.0] - 2017-08-06
Added:
1. (#361) Support Bot API v3.1 (by @Lord-Protector, @kamikazechaser)
1. (#332) Support Bot API v3.0 (by @kamikazechaser, @GochoMugo)
1. Add *TelegramBot#removeTextListener()* (by @GochoMugo)
1. (#342) Add game example (by @MCSH)
1. (#315) List 'bot-brother' project in community section in README (by @saeedhei) 1. (#315) List 'bot-brother' project in community section in README (by @saeedhei)
Changed:
1. (#367) Update *TelegramBot#answerCallbackQuery()* signature (by @mnb3000)
Fixed:
1. (#325) Fix global regexp state reset (by @Sirius-A)
1. (#363) Fix download file path on windows (by @kucherenkovova)
1. (#346) Fix anchor webhook link in docs (by @Coac)
* * * * * *
@ -201,13 +125,8 @@ Fixed:
1. Fix typos (by oflisback) 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.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.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 [0.27.0]:https://github.com/yagop/node-telegram-bot-api/releases/tag/v0.27.0
[0.27.1]:https://github.com/yagop/node-telegram-bot-api/releases/tag/v0.27.1 [0.27.1]:https://github.com/yagop/node-telegram-bot-api/releases/tag/v0.27.1
[0.28.0]:https://github.com/yagop/node-telegram-bot-api/releases/tag/v0.28.0 [Unreleased]:https://github.com/yagop/node-telegram-bot-api/compare/v0.27.1...master
[0.29.0]:https://github.com/yagop/node-telegram-bot-api/releases/tag/v0.29.0
[0.30.0]:https://github.com/yagop/node-telegram-bot-api/releases/tag/v0.30.0
[Unreleased]:https://github.com/yagop/node-telegram-bot-api/compare/v0.30.0...master

@ -10,29 +10,18 @@ Before proceeding any further, read the following documents:
## General Information ## General Information
### Creating a Github issue:
1. Ensure that your issue does **not** already exist. [Do a search](https://github.com/yagop/node-telegram-bot-api/issues).
2. Browse through [StackOverflow](https://stackoverflow.com/search?q=telegram+nodejs) and other similar platforms.
3. Should you open your issue, ensure you use the English language for
the wider target audience.
4. Be patient please.
### Updating API Reference i.e. generating `doc/api.md` ### Updating API Reference i.e. generating `doc/api.md`
Run: Run:
```bash ```bash
$ npm run doc $ npm run gen-doc
``` ```
### Running tests ### Running tests
Please read `test/README.md` for more information. Please read `test/README.md` for more information.
### Transpiling ES2015 for older Node.js versions ### Transpiling ES2015 for older Node.js versions
We use babel to transpile the code: We use babel to transpile the code:

@ -1,6 +1,6 @@
# Node.js Telegram Bot API # Node.js Telegram Bot API
[![Bot API](https://img.shields.io/badge/Bot%20API-v.3.5.0-00aced.svg)](https://core.telegram.org/bots/api) [![Bot API](http://img.shields.io/badge/Bot%20API-v2.3.1-00aced.svg)](https://core.telegram.org/bots/api)
[![Build Status](https://travis-ci.org/yagop/node-telegram-bot-api.svg?branch=master)](https://travis-ci.org/yagop/node-telegram-bot-api) [![Build Status](https://travis-ci.org/yagop/node-telegram-bot-api.svg?branch=master)](https://travis-ci.org/yagop/node-telegram-bot-api)
[![Build status](https://ci.appveyor.com/api/projects/status/ujko6bsum3g5msjh/branch/master?svg=true)](https://ci.appveyor.com/project/yagop/node-telegram-bot-api/branch/master) [![Build status](https://ci.appveyor.com/api/projects/status/ujko6bsum3g5msjh/branch/master?svg=true)](https://ci.appveyor.com/project/yagop/node-telegram-bot-api/branch/master)
[![Coverage Status](https://coveralls.io/repos/yagop/node-telegram-bot-api/badge.svg?branch=master)](https://coveralls.io/r/yagop/node-telegram-bot-api?branch=master) [![Coverage Status](https://coveralls.io/repos/yagop/node-telegram-bot-api/badge.svg?branch=master)](https://coveralls.io/r/yagop/node-telegram-bot-api?branch=master)
@ -8,7 +8,7 @@
[![https://telegram.me/node_telegram_bot_api](https://img.shields.io/badge/💬%20Telegram-node__telegram__bot__api-blue.svg)](https://telegram.me/node_telegram_bot_api) [![https://telegram.me/node_telegram_bot_api](https://img.shields.io/badge/💬%20Telegram-node__telegram__bot__api-blue.svg)](https://telegram.me/node_telegram_bot_api)
[![https://telegram.me/Yago_Perez](https://img.shields.io/badge/💬%20Telegram-Yago__Perez-blue.svg)](https://telegram.me/Yago_Perez) [![https://telegram.me/Yago_Perez](https://img.shields.io/badge/💬%20Telegram-Yago__Perez-blue.svg)](https://telegram.me/Yago_Perez)
Node.js module to interact with official [Telegram Bot API](https://core.telegram.org/bots/api). A bot token is **required** and can be obtained by talking to [@botfather](https://telegram.me/BotFather). Node.js module to interact with official [Telegram Bot API](https://core.telegram.org/bots/api). A bot token is needed, to obtain one, talk to [@botfather](https://telegram.me/BotFather) and create a new bot.
## Install ## Install
@ -54,49 +54,40 @@ bot.on('message', (msg) => {
* [Usage][usage] * [Usage][usage]
* [Examples][examples] * [Examples][examples]
* [Tutorials][tutorials]
* [Help Information][help] * [Help Information][help]
* API Reference ([release][api-release] / [development][api-dev] / [experimental][api-experimental]) * API Reference ([release][api-release] / [development][api-dev] / [experimental][api-experimental])
* [Contributing to the Project][contributing] * [Contributing to the Project][contributing]
* [Experimental Features][experimental] * [Experimental Features][experimental]
_**Note**: Development is done against the **master** branch. _**Note**: Development is done against the **master** branch.
Code for the latest release resides on the **release** branch. Code for the latest release resides on the **release** branch
Experimental features reside on the **experimental** branch._ Experimental features reside on the **experimental** branch._
## Community ## Community
We thank all the developers in the Open-Source community who continuously
take their time and effort in advancing this project.
See our [list of contributors][contributors].
We have a [Telegram channel][tg-channel] where we post updates on We have a [Telegram channel][tg-channel] where we post updates on
the Project. Head over and subscribe! the Project. Head over and subscribe!
Some things built using this library that might interest you: Some things built using this library, and might interest you:
* [tgfancy](https://github.com/GochoMugo/tgfancy): A fancy, higher-level wrapper for Telegram Bot API * [tgfancy](https://github.com/GochoMugo/tgfancy): A Fancy, Higher-Level Wrapper for Telegram Bot API
* [node-telegram-bot-api-middleware](https://github.com/idchlife/node-telegram-bot-api-middleware): Middleware for node-telegram-bot-api * [node-telegram-bot-api-middleware](https://github.com/idchlife/node-telegram-bot-api-middleware): Middleware for node-telegram-bot-api
* [teleirc](https://github.com/FruitieX/teleirc): A simple Telegram ↔ IRC gateway * [teleirc](https://github.com/FruitieX/teleirc): A simple Telegram ↔ IRC gateway
* [bot-brother](https://github.com/SerjoPepper/bot-brother): Node.js library to help you easily create telegram bots * [bot-brother](https://github.com/SerjoPepper/bot-brother): Node.js library to help you easily create telegram bots
* [redbot](https://github.com/guidone/node-red-contrib-chatbot): A Node-RED plugin to create telegram bots visually
* [node-telegram-keyboard-wrapper](https://github.com/alexandercerutti/node-telegram-keyboard-wrapper): A wrapper to improve `reply_markup` structures creation in an easy way (supports Inline Keyboards, Reply Keyboard, Remove Keyboard and Force Reply)
## License ## License
**The MIT License (MIT)** **The MIT License (MIT)**
Copyright © 2017 Yago Copyright (c) 2017 Yago
[usage]:https://github.com/yagop/node-telegram-bot-api/tree/master/doc/usage.md [usage]:https://github.com/yagop/node-telegram-bot-api/tree/master/doc/usage.md
[examples]:https://github.com/yagop/node-telegram-bot-api/tree/master/examples [examples]:https://github.com/yagop/node-telegram-bot-api/tree/master/examples
[help]:https://github.com/yagop/node-telegram-bot-api/tree/master/doc/help.md [help]:https://github.com/yagop/node-telegram-bot-api/tree/master/doc/help.md
[tutorials]:https://github.com/yagop/node-telegram-bot-api/tree/master/doc/tutorials.md
[api-dev]:https://github.com/yagop/node-telegram-bot-api/tree/master/doc/api.md [api-dev]:https://github.com/yagop/node-telegram-bot-api/tree/master/doc/api.md
[api-release]:https://github.com/yagop/node-telegram-bot-api/tree/release/doc/api.md [api-release]:https://github.com/yagop/node-telegram-bot-api/tree/release/doc/api.md
[api-experimental]:https://github.com/yagop/node-telegram-bot-api/tree/experimental/doc/api.md [api-experimental]:https://github.com/yagop/node-telegram-bot-api/tree/experimental/doc/api.md
[contributing]:https://github.com/yagop/node-telegram-bot-api/tree/master/CONTRIBUTING.md [contributing]:https://github.com/yagop/node-telegram-bot-api/tree/master/CONTRIBUTING.md
[contributors]:https://github.com/yagop/node-telegram-bot-api/graphs/contributors
[experimental]:https://github.com/yagop/node-telegram-bot-api/tree/master/doc/experimental.md [experimental]:https://github.com/yagop/node-telegram-bot-api/tree/master/doc/experimental.md
[tg-channel]:https://telegram.me/node_telegram_bot_api [tg-channel]:https://telegram.me/node_telegram_bot_api

@ -15,5 +15,3 @@
[setWebHook-v0.25.0]:https://github.com/yagop/node-telegram-bot-api/tree/4e5a493cadfaad5589a8d79e55d9e0d103000ce4#telegrambotsetwebhookurl-cert [setWebHook-v0.25.0]:https://github.com/yagop/node-telegram-bot-api/tree/4e5a493cadfaad5589a8d79e55d9e0d103000ce4#telegrambotsetwebhookurl-cert
[getUpdates-v0.25.0]:https://github.com/yagop/node-telegram-bot-api/tree/4e5a493cadfaad5589a8d79e55d9e0d103000ce4#TelegramBot+getUpdates [getUpdates-v0.25.0]:https://github.com/yagop/node-telegram-bot-api/tree/4e5a493cadfaad5589a8d79e55d9e0d103000ce4#TelegramBot+getUpdates
[getUserProfilePhotos-v0.25.0]:https://github.com/yagop/node-telegram-bot-api/tree/4e5a493cadfaad5589a8d79e55d9e0d103000ce4#TelegramBot+getUserProfilePhotos [getUserProfilePhotos-v0.25.0]:https://github.com/yagop/node-telegram-bot-api/tree/4e5a493cadfaad5589a8d79e55d9e0d103000ce4#TelegramBot+getUserProfilePhotos
[answerCallbackQuery-v0.27.1]:https://github.com/yagop/node-telegram-bot-api/blob/v0.27.1/doc/api.md#TelegramBot+answerCallbackQuery
[answerCallbackQuery-v0.29.0]:https://github.com/yagop/node-telegram-bot-api/blob/v0.29.0/doc/api.md#TelegramBot+answerCallbackQuery

File diff suppressed because it is too large Load Diff

@ -29,8 +29,6 @@ Sources:
1. [How do I run my bot behind a proxy?](#proxy) 1. [How do I run my bot behind a proxy?](#proxy)
1. [Can you add feature X to the library?](#new-feature) 1. [Can you add feature X to the library?](#new-feature)
1. [Is this scalable?](#scalable) 1. [Is this scalable?](#scalable)
1. [How do I listen for messages in a chat group?](#messages-in-chat)
1. [How do I know when a user blocks the bot?](#blocked-bot)
<a name="gifs"></a> <a name="gifs"></a>
### How do I send GIFs? ### How do I send GIFs?
@ -69,9 +67,7 @@ Sources:
<a name="error-meanings"></a> <a name="error-meanings"></a>
### What does this error mean? ### What does this error mean?
* [502 Bad Gateway](https://github.com/yagop/node-telegram-bot-api/issues/377) *Not done. PRs welcome!*
*Not complete. PRs welcome!*
Sources: Sources:
@ -129,22 +125,4 @@ Sources:
* Issue [#219](https://github.com/yagop/node-telegram-bot-api/issues/219) * Issue [#219](https://github.com/yagop/node-telegram-bot-api/issues/219)
<a name="messages-in-chat"></a>
### How do I listen for messages in a chat group?
*Not done. PRs welcome!*
Sources:
* Issue [#304](https://github.com/yagop/node-telegram-bot-api/issues/304)
<a name="blocked-bot"></a>
### How do I know when a user blocks the bot?
*Not done. PRs welcome!*
Sources:
* Issue [#273](https://github.com/yagop/node-telegram-bot-api/issues/273)
[questions]:https://github.com/yagop/node-telegram-bot-api/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3Aquestion%20 [questions]:https://github.com/yagop/node-telegram-bot-api/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3Aquestion%20

@ -1,10 +0,0 @@
# Tutorials
* [node-telegram-bot-api-tutorial by @hosein2398](https://github.com/hosein2398/node-telegram-bot-api-tutorial)
* [node-telegram-bot-api-persian-language by @saeedhei](https://github.com/saeedhei/node-telegram-bot-api-persian-language)
* [Node.JS: Делаем своего Telegram бота [RUS]](https://archakov.im/post/telegram-bot-on-nodejs.html)
* [YouTube: Пишем Telegram бота на NodeJS [RUS]](https://www.youtube.com/watch?v=RS1nmDMf69U&list=PL6AOr-PZtK-mM2QC1ixyfa5CtJZGK61aN)
* [Node.jsでTelegramのチャットボットを作る - Qiita](https://qiita.com/neetshin/items/0e2f6fa3ade41adb77bc)
* [Guía: Creación de bots de Telegram en Nodejs [ES]](https://tecnonucleous.com/creacion-de-bots-de-telegram-en-nodejs/)
> Send a PR with useful links **not** listed above

@ -1,27 +1,21 @@
# Usage # Usage
* [Events](#events) * [Events](#events)
* [WebHooks](#webhooks) * [WebHooks](#WebHooks)
* [Sending files](#sending-files) * [Sending files](#sending-files)
* [Error handling](#error-handling) * [Error handling](#error-handling)
<a name="events"></a> <a name="events"></a>
## Events ## Events
*TelegramBot* is an [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) *TelegramBot* is an event-emitter that emits the following events:
that emits the following events:
1. `message`: Received a new incoming [Message][message] of any kind 1. `message`: Received a new incoming [Message][message] of any kind
1. Depending on the properties of the [Message][message], one of these 1. Depending on the properties of the [Message][message], one of these
events may **ALSO** be emitted: `text`, `audio`, `document`, `photo`, events will **ALSO** be emitted: `text`, `audio`, `document`, `photo`,
`sticker`, `video`, `voice`, `contact`, `location`, `sticker`, `video`, `voice`, `contact`, `location`,
`new_chat_members`, `left_chat_member`, `new_chat_title`, `new_chat_participant`, `left_chat_participant`, `new_chat_title`,
`new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`
`game`, `pinned_message`, `migrate_from_chat_id`, `migrate_to_chat_id`,
`channel_chat_created`, `supergroup_chat_created`,
`successful_payment`, `invoice`, `video_note`
1. **Arguments**: `message` ([Message][message]), `metadata` (`{ type?:string }`)
1. `new_chat_participant`, `left_chat_participant` are **deprecated**
1. `callback_query`: Received a new incoming [Callback Query][callback-query] 1. `callback_query`: Received a new incoming [Callback Query][callback-query]
1. `inline_query`: Received a new incoming [Inline Query][inline-query] 1. `inline_query`: Received a new incoming [Inline Query][inline-query]
1. `chosen_inline_result`: Received result of an inline query i.e. [ChosenInlineResult][chosen-inline-result] 1. `chosen_inline_result`: Received result of an inline query i.e. [ChosenInlineResult][chosen-inline-result]
@ -32,11 +26,8 @@ that emits the following events:
1. `edited_channel_post`: Received a new version of a channel post that is known to the bot and was edited 1. `edited_channel_post`: Received a new version of a channel post that is known to the bot and was edited
1. `edited_channel_post_text` 1. `edited_channel_post_text`
1. `edited_channel_post_caption` 1. `edited_channel_post_caption`
1. `shipping_query`: Received a new incoming shipping query
1. `pre_checkout_query`: Received a new incoming pre-checkout query
1. `polling_error`: Error occurred during polling. See [polling errors](#polling-errors). 1. `polling_error`: Error occurred during polling. See [polling errors](#polling-errors).
1. `webhook_error`: Error occurred handling a webhook request. See [webhook errors](#webhook-errors). 1. `webhook_error`: Error occurred handling a webhook request. See [webhook errors](#webhook-errors).
1. `error`: Unexpected error occurred, usually fatal!
**Tip:** Its much better to listen a specific event rather than on **Tip:** Its much better to listen a specific event rather than on
`message` in order to stay safe from the content. `message` in order to stay safe from the content.
@ -116,63 +107,6 @@ const url = 'https://telegram.org/img/t_logo.png';
bot.sendPhoto(chatId, url); 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 an additional argument as file options, like so:
```js
const fileOptions = {
// Explicitly specify the file name.
filename: 'customfilename',
// Explicitly specify the MIME type.
contentType: 'audio/mpeg',
};
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);
```
<a name="sending-files-options"></a>
### 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`:
*(`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?
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"`
<a name="sending-files-performance"></a> <a name="sending-files-performance"></a>
### Performance Issue ### Performance Issue

@ -2,13 +2,12 @@
* This example demonstrates setting up a webook, and receiving * This example demonstrates setting up a webook, and receiving
* updates in your express app * updates in your express app
*/ */
/* eslint-disable no-console */
const TOKEN = process.env.TELEGRAM_TOKEN || 'YOUR_TELEGRAM_BOT_TOKEN'; const TOKEN = process.env.TELEGRAM_TOKEN || 'YOUR_TELEGRAM_BOT_TOKEN';
const url = 'https://<PUBLIC-URL>'; const url = 'https://<PUBLIC-URL>';
const port = process.env.PORT; const port = process.env.PORT;
const TelegramBot = require('../..'); const TelegramBot = require('..');
const express = require('express'); const express = require('express');
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');

@ -1,140 +0,0 @@
<!-- Game example taken from w3schools (https://www.w3schools.com/graphics/game_intro.asp) -->
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
body {
font-family: 'Courier ';
text-align: center;
}
canvas {
border:1px solid #d3d3d3;
background-color: #f1f1f1;
}
</style>
</head>
<body onload="startGame()">
<p><button onmousedown="accelerate(-0.2)" onmouseup="accelerate(0.05)">ACCELERATE</button></p>
<p>Use the ACCELERATE button to stay in the air</p>
<p>How long can you stay alive?</p>
<script>
var myGamePiece;
var myObstacles = [];
var myScore;
function startGame() {
myGamePiece = new component(30, 30, "red", 10, 120);
myGamePiece.gravity = 0.05;
myScore = new component("30px", "Consolas", "black", 280, 40, "text");
myGameArea.start();
}
var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.frameNo = 0;
this.interval = setInterval(updateGameArea, 20);
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y, type) {
this.type = type;
this.score = 0;
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.gravity = 0;
this.gravitySpeed = 0;
this.update = function() {
ctx = myGameArea.context;
if (this.type == "text") {
ctx.font = this.width + " " + this.height;
ctx.fillStyle = color;
ctx.fillText(this.text, this.x, this.y);
} else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
this.newPos = function() {
this.gravitySpeed += this.gravity;
this.x += this.speedX;
this.y += this.speedY + this.gravitySpeed;
this.hitBottom();
}
this.hitBottom = function() {
var rockbottom = myGameArea.canvas.height - this.height;
if (this.y > rockbottom) {
this.y = rockbottom;
this.gravitySpeed = 0;
}
}
this.crashWith = function(otherobj) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) {
crash = false;
}
return crash;
}
}
function updateGameArea() {
var x, height, gap, minHeight, maxHeight, minGap, maxGap;
for (i = 0; i < myObstacles.length; i += 1) {
if (myGamePiece.crashWith(myObstacles[i])) {
return;
}
}
myGameArea.clear();
myGameArea.frameNo += 1;
if (myGameArea.frameNo == 1 || everyinterval(150)) {
x = myGameArea.canvas.width;
minHeight = 20;
maxHeight = 200;
height = Math.floor(Math.random()*(maxHeight-minHeight+1)+minHeight);
minGap = 50;
maxGap = 200;
gap = Math.floor(Math.random()*(maxGap-minGap+1)+minGap);
myObstacles.push(new component(10, height, "green", x, 0));
myObstacles.push(new component(10, x - height - gap, "green", x, height + gap));
}
for (i = 0; i < myObstacles.length; i += 1) {
myObstacles[i].x += -1;
myObstacles[i].update();
}
myScore.text="SCORE: " + myGameArea.frameNo;
myScore.update();
myGamePiece.newPos();
myGamePiece.update();
}
function everyinterval(n) {
if ((myGameArea.frameNo / n) % 1 == 0) {return true;}
return false;
}
function accelerate(n) {
myGamePiece.gravity = n;
}
</script>
</body>
</html>

@ -1,53 +0,0 @@
/**
* This example demonstrates using HTML5 games with Telegram.
*/
/* eslint-disable no-console */
const TOKEN = process.env.TELEGRAM_TOKEN || 'YOUR_TELEGRAM_BOT_TOKEN';
const gameName = process.env.TELEGRAM_GAMENAME || 'YOUR_TELEGRAM_GAMENAME';
// Specify '0' to use ngrok i.e. localhost tunneling
let url = process.env.URL || 'https://<PUBLIC-URL>';
const port = process.env.PORT || 8080;
const TelegramBot = require('../..');
const express = require('express');
const path = require('path');
const bot = new TelegramBot(TOKEN, { polling: true });
const app = express();
// Basic configurations
app.set('view engine', 'ejs');
// Tunnel to localhost.
// This is just for demo purposes.
// In your application, you will be using a static URL, probably that
// you paid for. :)
if (url === '0') {
const ngrok = require('ngrok');
ngrok.connect(port, function onConnect(error, u) {
if (error) throw error;
url = u;
console.log(`Game tunneled at ${url}`);
});
}
// Matches /start
bot.onText(/\/start/, function onPhotoText(msg) {
bot.sendGame(msg.chat.id, gameName);
});
// Handle callback queries
bot.on('callback_query', function onCallbackQuery(callbackQuery) {
bot.answerCallbackQuery(callbackQuery.id, { url });
});
// Render the HTML game
app.get('/', function requestListener(req, res) {
res.sendFile(path.join(__dirname, 'game.html'));
});
// Bind server to port
app.listen(port, function listen() {
console.log(`Server is listening at http://localhost:${port}`);
});

@ -5,7 +5,7 @@
const TOKEN = process.env.TELEGRAM_TOKEN || 'YOUR_TELEGRAM_BOT_TOKEN'; const TOKEN = process.env.TELEGRAM_TOKEN || 'YOUR_TELEGRAM_BOT_TOKEN';
const TelegramBot = require('../..'); const TelegramBot = require('..');
const options = { const options = {
webHook: { webHook: {
// Port to which you should bind is assigned to $PORT variable // Port to which you should bind is assigned to $PORT variable

@ -5,12 +5,12 @@
const TOKEN = process.env.TELEGRAM_TOKEN || 'YOUR_TELEGRAM_BOT_TOKEN'; const TOKEN = process.env.TELEGRAM_TOKEN || 'YOUR_TELEGRAM_BOT_TOKEN';
const TelegramBot = require('../..'); const TelegramBot = require('..');
const options = { const options = {
webHook: { webHook: {
port: 443, port: 443,
key: `${__dirname}/../ssl/key.pem`, // Path to file with PEM private key key: `${__dirname}/key.pem`, // Path to file with PEM private key
cert: `${__dirname}/../ssl/crt.pem` // Path to file with PEM certificate cert: `${__dirname}/crt.pem` // Path to file with PEM certificate
} }
}; };
// This URL must route to the port set above (i.e. 443) // This URL must route to the port set above (i.e. 443)

@ -6,7 +6,7 @@
const TOKEN = process.env.TELEGRAM_TOKEN || 'YOUR_TELEGRAM_BOT_TOKEN'; const TOKEN = process.env.TELEGRAM_TOKEN || 'YOUR_TELEGRAM_BOT_TOKEN';
const TelegramBot = require('../..'); const TelegramBot = require('..');
const options = { const options = {
webHook: { webHook: {
// Just use 443 directly // Just use 443 directly

@ -1,23 +1,11 @@
/** /**
* This example demonstrates setting up webhook * This example demonstrates setting up webhook
* on the OpenShift platform. * on the OpenShift platform.
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* NOTE:
*
* Openshift 2 has been shut down.
*
* This example is kept here for historical/educational purposes.
* No changes are expected to be made to the source code below.
*
* See https://github.com/yagop/node-telegram-bot-api/issues/426 for
* more information.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/ */
const TOKEN = process.env.TELEGRAM_TOKEN || 'YOUR_TELEGRAM_BOT_TOKEN'; const TOKEN = process.env.TELEGRAM_TOKEN || 'YOUR_TELEGRAM_BOT_TOKEN';
const TelegramBot = require('../..'); const TelegramBot = require('..');
// See https://developers.openshift.com/en/node-js-environment-variables.html // See https://developers.openshift.com/en/node-js-environment-variables.html
const options = { const options = {
webHook: { webHook: {

@ -1,6 +1,6 @@
{ {
"name": "node-telegram-bot-api", "name": "node-telegram-bot-api",
"version": "0.30.0", "version": "0.27.1",
"description": "Telegram Bot API", "description": "Telegram Bot API",
"main": "./index.js", "main": "./index.js",
"directories": { "directories": {
@ -14,10 +14,9 @@
"bot" "bot"
], ],
"scripts": { "scripts": {
"gen-doc": "echo 'WARNING: `npm run gen-doc` is deprecated. Use `npm run doc` instead.' && npm run doc", "gen-doc": "jsdoc2md --files src/telegram.js --template doc/api.hbs > doc/api.md",
"doc": "jsdoc2md --files src/telegram.js --template doc/api.hbs > doc/api.md",
"build": "babel -d ./lib src", "build": "babel -d ./lib src",
"prepublishOnly": "npm run build && npm run gen-doc", "prepublish": "npm run build && npm run gen-doc",
"eslint": "eslint ./src ./test ./examples", "eslint": "eslint ./src ./test ./examples",
"mocha": "mocha", "mocha": "mocha",
"pretest": "npm run build", "pretest": "npm run build",
@ -29,42 +28,40 @@
"node": ">=0.12" "node": ">=0.12"
}, },
"dependencies": { "dependencies": {
"array.prototype.findindex": "^2.0.2", "array.prototype.findindex": "^2.0.0",
"bl": "^1.2.1", "bl": "^1.1.2",
"bluebird": "^3.5.1", "bluebird": "^3.3.4",
"debug": "^3.1.0", "debug": "^2.2.0",
"depd": "^1.1.1", "depd": "^1.1.0",
"eventemitter3": "^3.0.0", "eventemitter3": "^2.0.2",
"file-type": "^3.9.0", "file-type": "^3.9.0",
"mime": "^1.6.0", "mime": "^1.3.4",
"pump": "^2.0.0", "pump": "^1.0.1",
"request": "^2.83.0", "request": "^2.69.0",
"request-promise": "^4.2.2" "request-promise": "^4.1.1"
}, },
"devDependencies": { "devDependencies": {
"babel-cli": "^6.26.0", "babel-cli": "^6.6.5",
"babel-eslint": "^8.0.3", "babel-eslint": "^6.1.2",
"babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-class-properties": "^6.6.0",
"babel-plugin-transform-es2015-destructuring": "^6.23.0", "babel-plugin-transform-es2015-destructuring": "^6.6.5",
"babel-plugin-transform-es2015-parameters": "^6.24.1", "babel-plugin-transform-es2015-parameters": "^6.7.0",
"babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", "babel-plugin-transform-es2015-shorthand-properties": "^6.5.0",
"babel-plugin-transform-es2015-spread": "^6.22.0", "babel-plugin-transform-es2015-spread": "^6.6.5",
"babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-plugin-transform-object-rest-spread": "^6.6.5",
"babel-plugin-transform-strict-mode": "^6.24.1", "babel-plugin-transform-strict-mode": "^6.6.5",
"babel-preset-es2015": "^6.24.1", "babel-preset-es2015": "^6.6.0",
"babel-register": "^6.26.0", "babel-register": "^6.7.2",
"concat-stream": "^1.6.0",
"contributor": "^0.1.25", "contributor": "^0.1.25",
"eslint": "^2.13.1", "eslint": "^2.13.1",
"eslint-config-airbnb": "^6.2.0", "eslint-config-airbnb": "^6.2.0",
"eslint-plugin-mocha": "^4.11.0", "eslint-plugin-mocha": "^4.8.0",
"is": "^3.2.1", "is": "^3.1.0",
"is-ci": "^1.0.10",
"istanbul": "^1.1.0-alpha.1", "istanbul": "^1.1.0-alpha.1",
"jsdoc-to-markdown": "^3.0.3", "jsdoc-to-markdown": "^2.0.1",
"mocha": "^3.5.3", "mocha": "^3.2.0",
"mocha-lcov-reporter": "^1.3.0", "mocha-lcov-reporter": "^1.2.0",
"node-static": "^0.7.10" "node-static": "^0.7.9"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -76,158 +73,59 @@
"homepage": "https://github.com/yagop/node-telegram-bot-api", "homepage": "https://github.com/yagop/node-telegram-bot-api",
"contributors": [ "contributors": [
{ {
"name": "Oleg", "name": "Anton Mironov",
"email": null, "email": "ant.mironov@gmail.com",
"url": "https://github.com/olegmdev", "url": "https://github.com/mironov",
"contributions": 1, "contributions": 1,
"additions": 1, "additions": 51,
"deletions": 0, "deletions": 15,
"hireable": true "hireable": true
}, },
{ {
"name": "Archakov Dennis", "name": "Daniil Yastremskiy",
"email": null, "email": "Catharsis@post.cz",
"url": "https://github.com/Archakov06", "url": "https://github.com/TheBeastOfCaerbannog",
"contributions": 1, "contributions": 1,
"additions": 1, "additions": 36,
"deletions": 0, "deletions": 0,
"hireable": true "hireable": true
}, },
{ {
"name": "drGOD", "name": null,
"email": null,
"url": "https://github.com/drGOD",
"contributions": 1,
"additions": 1,
"deletions": 0,
"hireable": null
},
{
"name": "MCSH",
"email": null,
"url": "https://github.com/MCSH",
"contributions": 1,
"additions": 2,
"deletions": 1,
"hireable": true
},
{
"name": "Aaron Meese",
"email": null, "email": null,
"url": "https://github.com/ajmeese7", "url": "https://github.com/Ni2c2k",
"contributions": 1, "contributions": 1,
"additions": 4, "additions": 4,
"deletions": 4, "deletions": 4,
"hireable": true
},
{
"name": "Misha",
"email": null,
"url": "https://github.com/mnb3000",
"contributions": 1,
"additions": 24,
"deletions": 12,
"hireable": null
},
{
"name": "Savely Krasovsky",
"email": null,
"url": "https://github.com/L11R",
"contributions": 1,
"additions": 297,
"deletions": 1,
"hireable": null
},
{
"name": "Victor Le",
"email": null,
"url": "https://github.com/Coac",
"contributions": 1,
"additions": 1,
"deletions": 1,
"hireable": null "hireable": null
}, },
{ {
"name": null, "name": "Alexander Tarmolov",
"email": null, "email": "tarmolov@gmail.com",
"url": "https://github.com/kucherenkovova", "url": "https://github.com/tarmolov",
"contributions": 1, "contributions": 1,
"additions": 1, "additions": 5,
"deletions": 1, "deletions": 0,
"hireable": null "hireable": null
}, },
{ {
"name": "Guidone", "name": "Plusb Preco",
"email": null, "email": "plusb21@gmail.com",
"url": "https://github.com/guidone", "url": "https://github.com/preco21",
"contributions": 1, "contributions": 1,
"additions": 1, "additions": 1,
"deletions": 0, "deletions": 0,
"hireable": true "hireable": null
}, },
{ {
"name": "Jaakko Lipsanen", "name": "Ola Flisbäck",
"email": null, "email": null,
"url": "https://github.com/JaakkoLipsanen", "url": "https://github.com/oflisback",
"contributions": 1, "contributions": 1,
"additions": 3, "additions": 3,
"deletions": 3, "deletions": 3,
"hireable": null
},
{
"name": "Fabio Zuber",
"email": null,
"url": "https://github.com/Sirius-A",
"contributions": 1,
"additions": 12,
"deletions": 0,
"hireable": null
},
{
"name": "Evgeny Bondarenko",
"email": null,
"url": "https://github.com/jehy",
"contributions": 1,
"additions": 8,
"deletions": 8,
"hireable": true "hireable": true
}, },
{
"name": "Anton Mironov",
"email": null,
"url": "https://github.com/mironov",
"contributions": 1,
"additions": 51,
"deletions": 15,
"hireable": null
},
{
"name": "Daniil Yastremskiy",
"email": null,
"url": "https://github.com/TheBeastOfCaerbannog",
"contributions": 1,
"additions": 36,
"deletions": 0,
"hireable": true
},
{
"name": "Nikolay Ershov",
"email": null,
"url": "https://github.com/Ni2c2k",
"contributions": 1,
"additions": 4,
"deletions": 4,
"hireable": null
},
{
"name": "Alexander Tarmolov",
"email": null,
"url": "https://github.com/tarmolov",
"contributions": 1,
"additions": 5,
"deletions": 0,
"hireable": null
},
{ {
"name": null, "name": null,
"email": null, "email": null,
@ -237,18 +135,9 @@
"deletions": 2, "deletions": 2,
"hireable": null "hireable": null
}, },
{
"name": "Mikhail Burshteyn",
"email": null,
"url": "https://github.com/m-burst",
"contributions": 1,
"additions": 12,
"deletions": 5,
"hireable": null
},
{ {
"name": "Sergey Bogdanov", "name": "Sergey Bogdanov",
"email": null, "email": "sergey.bogdanov@gmail.com",
"url": "https://github.com/desunit", "url": "https://github.com/desunit",
"contributions": 1, "contributions": 1,
"additions": 1, "additions": 1,
@ -256,17 +145,17 @@
"hireable": null "hireable": null
}, },
{ {
"name": "Ola Flisbäck", "name": "Mikhail Burshteyn",
"email": null, "email": null,
"url": "https://github.com/oflisback", "url": "https://github.com/m-burst",
"contributions": 1, "contributions": 1,
"additions": 3, "additions": 12,
"deletions": 3, "deletions": 5,
"hireable": true "hireable": null
}, },
{ {
"name": "Horus Lugo", "name": "Horus Lugo",
"email": null, "email": "horusgoul@gmail.com",
"url": "https://github.com/HorusGoul", "url": "https://github.com/HorusGoul",
"contributions": 1, "contributions": 1,
"additions": 108, "additions": 108,
@ -274,26 +163,26 @@
"hireable": true "hireable": true
}, },
{ {
"name": "Conor Fennell", "name": "Serhii Dmytruk",
"email": null, "email": "dmitruksergey@gmail.com",
"url": "https://github.com/conorfennell", "url": "https://github.com/serhiidmytruk",
"contributions": 1, "contributions": 1,
"additions": 50, "additions": 35,
"deletions": 1, "deletions": 1,
"hireable": null "hireable": null
}, },
{ {
"name": "Serhii Dmytruk", "name": "Conor Fennell",
"email": null, "email": null,
"url": "https://github.com/imserhii", "url": "https://github.com/conorfennell",
"contributions": 1, "contributions": 1,
"additions": 35, "additions": 50,
"deletions": 1, "deletions": 1,
"hireable": null "hireable": null
}, },
{ {
"name": "Aleksandr L.", "name": "Aleksandr L.",
"email": null, "email": "w.siteee@gmail.com",
"url": "https://github.com/w-site", "url": "https://github.com/w-site",
"contributions": 1, "contributions": 1,
"additions": 24, "additions": 24,
@ -302,22 +191,13 @@
}, },
{ {
"name": "Matthew Brandly", "name": "Matthew Brandly",
"email": null, "email": "matt@brandly.me",
"url": "https://github.com/brandly", "url": "https://github.com/brandly",
"contributions": 1, "contributions": 1,
"additions": 1, "additions": 1,
"deletions": 1, "deletions": 1,
"hireable": null "hireable": null
}, },
{
"name": "Patricio López Juri",
"email": null,
"url": "https://github.com/mrpatiwi",
"contributions": 1,
"additions": 49,
"deletions": 2,
"hireable": true
},
{ {
"name": "Anton", "name": "Anton",
"email": null, "email": null,
@ -325,11 +205,20 @@
"contributions": 1, "contributions": 1,
"additions": 23, "additions": 23,
"deletions": 5, "deletions": 5,
"hireable": null
},
{
"name": "Patricio López Juri",
"email": "patricio@lopezjuri.com",
"url": "https://github.com/mrpatiwi",
"contributions": 1,
"additions": 49,
"deletions": 2,
"hireable": true "hireable": true
}, },
{ {
"name": "Guido García", "name": "Guido García",
"email": null, "email": "palmerabollo@gmail.com",
"url": "https://github.com/palmerabollo", "url": "https://github.com/palmerabollo",
"contributions": 1, "contributions": 1,
"additions": 1, "additions": 1,
@ -338,7 +227,7 @@
}, },
{ {
"name": "Sebastian Troć", "name": "Sebastian Troć",
"email": null, "email": "sebastian.troc@gucman.pl",
"url": "https://github.com/SebastianTroc", "url": "https://github.com/SebastianTroc",
"contributions": 1, "contributions": 1,
"additions": 1, "additions": 1,
@ -346,26 +235,17 @@
"hireable": null "hireable": null
}, },
{ {
"name": "CapacitorSet", "name": "Mohammed Sohail",
"email": null, "email": "sohail@forfuture.tech",
"url": "https://github.com/CapacitorSet", "url": "https://github.com/kamikazechaser",
"contributions": 2, "contributions": 2,
"additions": 110, "additions": 20,
"deletions": 2, "deletions": 5,
"hireable": true "hireable": true
}, },
{
"name": "Plusb Preco",
"email": null,
"url": "https://github.com/preco21",
"contributions": 2,
"additions": 111,
"deletions": 200,
"hireable": null
},
{ {
"name": "Jishnu Mohan", "name": "Jishnu Mohan",
"email": null, "email": "jishnu7@gmail.com",
"url": "https://github.com/jishnu7", "url": "https://github.com/jishnu7",
"contributions": 2, "contributions": 2,
"additions": 84, "additions": 84,
@ -373,17 +253,17 @@
"hireable": true "hireable": true
}, },
{ {
"name": "TJ Horner", "name": "Jérémy Gotteland",
"email": null, "email": null,
"url": "https://github.com/tjhorner", "url": "https://github.com/Tketa",
"contributions": 2, "contributions": 2,
"additions": 223, "additions": 81,
"deletions": 1, "deletions": 3,
"hireable": null "hireable": null
}, },
{ {
"name": "Alex Godko", "name": "Alex Godko",
"email": null, "email": "koloboid@gmail.com",
"url": "https://github.com/koloboid", "url": "https://github.com/koloboid",
"contributions": 2, "contributions": 2,
"additions": 2, "additions": 2,
@ -392,7 +272,7 @@
}, },
{ {
"name": "Dardan Neziri", "name": "Dardan Neziri",
"email": null, "email": "dard.ne@gmail.com",
"url": "https://github.com/knock-in", "url": "https://github.com/knock-in",
"contributions": 2, "contributions": 2,
"additions": 22, "additions": 22,
@ -400,26 +280,26 @@
"hireable": true "hireable": true
}, },
{ {
"name": "Jérémy Gotteland", "name": "Cristian Baldi",
"email": null, "email": "bld.cris.96@gmail.com",
"url": "https://github.com/Tketa", "url": "https://github.com/crisbal",
"contributions": 2, "contributions": 2,
"additions": 81, "additions": 26,
"deletions": 3, "deletions": 1,
"hireable": null "hireable": true
}, },
{ {
"name": "Iiro Jäppinen", "name": "Vitaly Aminev",
"email": null, "email": null,
"url": "https://github.com/iiroj", "url": "https://github.com/AVVS",
"contributions": 2, "contributions": 2,
"additions": 40, "additions": 1065,
"deletions": 0, "deletions": 1001,
"hireable": null "hireable": true
}, },
{ {
"name": null, "name": null,
"email": null, "email": "plo.cav@gmail.com",
"url": "https://github.com/evolun", "url": "https://github.com/evolun",
"contributions": 2, "contributions": 2,
"additions": 7, "additions": 7,
@ -427,31 +307,40 @@
"hireable": null "hireable": null
}, },
{ {
"name": "Vitaly Aminev", "name": "Iiro Jäppinen",
"email": null, "email": null,
"url": "https://github.com/AVVS", "url": "https://github.com/iiroj",
"contributions": 2, "contributions": 2,
"additions": 1065, "additions": 40,
"deletions": 1001, "deletions": 0,
"hireable": true "hireable": null
}, },
{ {
"name": "Cristian Baldi", "name": "TJ Horner",
"email": null, "email": "me@tjhorner.com",
"url": "https://github.com/crisbal", "url": "https://github.com/tjhorner",
"contributions": 2, "contributions": 2,
"additions": 26, "additions": 223,
"deletions": 1, "deletions": 1,
"hireable": true "hireable": null
}, },
{ {
"name": "Vítor Augusto da Silva Vasconcellos", "name": "Rafael Kr",
"email": null, "email": null,
"url": "https://github.com/RafaelKr",
"contributions": 3,
"additions": 3,
"deletions": 2,
"hireable": null
},
{
"name": "Vítor Augusto da Silva Vasconcellos",
"email": "vasvas10@gmail.com",
"url": "https://github.com/HeavenVolkoff", "url": "https://github.com/HeavenVolkoff",
"contributions": 2, "contributions": 2,
"additions": 12, "additions": 12,
"deletions": 0, "deletions": 0,
"hireable": null "hireable": true
}, },
{ {
"name": "Rey", "name": "Rey",
@ -464,7 +353,7 @@
}, },
{ {
"name": "Ivan Skorokhodov", "name": "Ivan Skorokhodov",
"email": null, "email": "iskorokhodov@gmail.com",
"url": "https://github.com/universome", "url": "https://github.com/universome",
"contributions": 3, "contributions": 3,
"additions": 219, "additions": 219,
@ -472,17 +361,17 @@
"hireable": null "hireable": null
}, },
{ {
"name": "Rafael Kr", "name": "Riddler",
"email": null, "email": null,
"url": "https://github.com/RafaelKr", "url": "https://github.com/Waterloo",
"contributions": 3, "contributions": 3,
"additions": 3, "additions": 64,
"deletions": 2, "deletions": 2,
"hireable": null "hireable": true
}, },
{ {
"name": null, "name": null,
"email": null, "email": "exlmotodev@gmail.com",
"url": "https://github.com/EXL", "url": "https://github.com/EXL",
"contributions": 4, "contributions": 4,
"additions": 4, "additions": 4,
@ -490,26 +379,17 @@
"hireable": true "hireable": true
}, },
{ {
"name": "Riddler", "name": "Yago",
"email": null, "email": "yago@yago.me",
"url": "https://github.com/Waterloo", "url": "https://github.com/yagop",
"contributions": 3, "contributions": 194,
"additions": 64, "additions": 3014,
"deletions": 2, "deletions": 1173,
"hireable": true "hireable": true
}, },
{
"name": "Chris54721",
"email": null,
"url": "https://github.com/chris54721",
"contributions": 5,
"additions": 22,
"deletions": 6,
"hireable": null
},
{ {
"name": "Ilias Ismanalijev", "name": "Ilias Ismanalijev",
"email": null, "email": "hello@illyism.com",
"url": "https://github.com/Illyism", "url": "https://github.com/Illyism",
"contributions": 7, "contributions": 7,
"additions": 140, "additions": 140,
@ -517,40 +397,22 @@
"hireable": true "hireable": true
}, },
{ {
"name": "Gocho Mugo", "name": "Chris54721",
"email": null,
"url": "https://github.com/GochoMugo",
"contributions": 152,
"additions": 7186,
"deletions": 3367,
"hireable": true
},
{
"name": "Mohammed Sohail",
"email": null, "email": null,
"url": "https://github.com/kamikazechaser", "url": "https://github.com/chris54721",
"contributions": 12, "contributions": 5,
"additions": 523, "additions": 22,
"deletions": 127, "deletions": 6,
"hireable": true "hireable": null
}, },
{ {
"name": "Yago", "name": "Gocho Mugo",
"email": null, "email": "mugo@forfuture.co.ke",
"url": "https://github.com/yagop", "url": "https://github.com/GochoMugo",
"contributions": 197, "contributions": 80,
"additions": 3023, "additions": 4590,
"deletions": 1177, "deletions": 2377,
"hireable": true "hireable": true
},
{
"name": "Sedric Heidarizarei",
"email": null,
"url": "https://github.com/saeedhei",
"contributions": 1,
"additions": 1,
"deletions": 0,
"hireable": null
} }
] ]
} }

@ -10,12 +10,6 @@ exports.BaseError = class BaseError extends Error {
super(`${code}: ${message}`); super(`${code}: ${message}`);
this.code = code; this.code = code;
} }
toJSON() {
return {
code: this.code,
message: this.message,
};
}
}; };

File diff suppressed because it is too large Load Diff

@ -1,4 +1,3 @@
const errors = require('./errors');
const debug = require('debug')('node-telegram-bot-api'); const debug = require('debug')('node-telegram-bot-api');
const deprecate = require('depd')('node-telegram-bot-api'); const deprecate = require('depd')('node-telegram-bot-api');
const ANOTHER_WEB_HOOK_USED = 409; const ANOTHER_WEB_HOOK_USED = 409;
@ -50,7 +49,7 @@ class TelegramBotPolling {
/** /**
* Stop polling * Stop polling
* @param {Object} [options] Options * @param {Object} [options]
* @param {Boolean} [options.cancel] Cancel current request * @param {Boolean} [options.cancel] Cancel current request
* @param {String} [options.reason] Reason for stopping polling * @param {String} [options.reason] Reason for stopping polling
* @return {Promise} * @return {Promise}
@ -80,18 +79,6 @@ class TelegramBotPolling {
return !!this._lastRequest; return !!this._lastRequest;
} }
/**
* Handle error thrown during polling.
* @private
* @param {Error} error
*/
_error(error) {
if (!this.bot.listeners('polling_error').length) {
return console.error('error: [polling_error] %j', error); // eslint-disable-line no-console
}
return this.bot.emit('polling_error', error);
}
/** /**
* Invokes polling (with recursion!) * Invokes polling (with recursion!)
* @return {Promise} promise of the current request * @return {Promise} promise of the current request
@ -106,59 +93,18 @@ class TelegramBotPolling {
updates.forEach(update => { updates.forEach(update => {
this.options.params.offset = update.update_id + 1; this.options.params.offset = update.update_id + 1;
debug('updated offset: %s', this.options.params.offset); debug('updated offset: %s', this.options.params.offset);
try {
this.bot.processUpdate(update); this.bot.processUpdate(update);
} catch (err) {
err._processing = true;
throw err;
}
}); });
return null; return null;
}) })
.catch(err => { .catch(err => {
debug('polling error: %s', err.message); debug('polling error: %s', err.message);
if (!err._processing) { if (this.bot.listeners('polling_error').length) {
return this._error(err); this.bot.emit('polling_error', err);
} } else {
delete err._processing; console.error(err); // eslint-disable-line no-console
/*
* An error occured while processing the items,
* i.e. in `this.bot.processUpdate()` above.
* We need to mark the already-processed items
* to avoid fetching them again once the application
* is restarted, or moves to next polling interval
* (in cases where unhandled rejections do not terminate
* the process).
* See https://github.com/yagop/node-telegram-bot-api/issues/36#issuecomment-268532067
*/
if (!this.bot.options.badRejection) {
return this._error(err);
} }
const opts = { return null;
offset: this.options.params.offset,
limit: 1,
timeout: 0,
};
return this.bot.getUpdates(opts).then(() => {
return this._error(err);
}).catch(requestErr => {
/*
* We have been unable to handle this error.
* We have to log this to stderr to ensure devops
* understands that they may receive already-processed items
* on app restart.
* We simply can not rescue this situation, emit "error"
* event, with the hope that the application exits.
*/
/* eslint-disable no-console */
const bugUrl = 'https://github.com/yagop/node-telegram-bot-api/issues/36#issuecomment-268532067';
console.error('error: Internal handling of The Offset Infinite Loop failed');
console.error(`error: Due to error '${requestErr}'`);
console.error('error: You may receive already-processed updates on app restart');
console.error(`error: Please see ${bugUrl} for more information`);
/* eslint-enable no-console */
return this.bot.emit('error', new errors.FatalError(err));
});
}) })
.finally(() => { .finally(() => {
if (this._abort) { if (this._abort) {

@ -97,7 +97,7 @@ class TelegramBotWebHook {
*/ */
_error(error) { _error(error) {
if (!this.bot.listeners('webhook_error').length) { if (!this.bot.listeners('webhook_error').length) {
return console.error('error: [webhook_error] %j', error); // eslint-disable-line no-console return console.error(error); // eslint-disable-line no-console
} }
return this.bot.emit('webhook_error', error); return this.bot.emit('webhook_error', error);
} }

@ -10,17 +10,9 @@ export TEST_USER_ID=<USER_ID>
# Group Id which to use in some of the tests, e.g. for TelegramBot#getChat() # Group Id which to use in some of the tests, e.g. for TelegramBot#getChat()
export TEST_GROUP_ID=<GROUP_ID> export TEST_GROUP_ID=<GROUP_ID>
# Game short name to use in some tests, e.g. TelegramBot#sendGame() # Game short name which to use in some of the tests, e.g. TelegramBot#sendGame()
# Defaults to "medusalab_test".
export TEST_GAME_SHORT_NAME=<GAME_SHORT_NAME> export TEST_GAME_SHORT_NAME=<GAME_SHORT_NAME>
# Sticker set name to use in some tests, e.g. TelegramBot#getStickerSet()
# Defaults to "pusheen".
export TEST_STICKER_SET_NAME=<STICKER_SET_NAME>
# Payment provider token to be used
export TEST_PROVIDER_TOKEN=<YOUR_PROVIDER_TOKEN>
# Run ALL tests # Run ALL tests
npm run test npm run test
@ -28,4 +20,3 @@ npm run test
npm run eslint # static-analysis npm run eslint # static-analysis
npm run mocha # mocha tests npm run mocha # mocha tests
``` ```
Note: The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

@ -5,11 +5,8 @@ const assert = require('assert');
const fs = require('fs'); const fs = require('fs');
const os = require('os'); const os = require('os');
const path = require('path'); const path = require('path');
const stream = require('stream');
const is = require('is'); const is = require('is');
const utils = require('./utils'); const utils = require('./utils');
const isCI = require('is-ci');
const concat = require('concat-stream');
// Allows self-signed certificates to be used in our tests // Allows self-signed certificates to be used in our tests
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
@ -19,16 +16,10 @@ if (!TOKEN) {
throw new Error('Bot token not provided'); throw new Error('Bot token not provided');
} }
const PROVIDER_TOKEN = process.env.TEST_PROVIDER_TOKEN;
if (!PROVIDER_TOKEN && !isCI) { // If is not running in Travis / Appveyor
throw new Error('Provider token not supplied');
}
// Telegram service if not User Id // Telegram service if not User Id
const USERID = process.env.TEST_USER_ID || 777000; const USERID = process.env.TEST_USER_ID || 777000;
const GROUPID = process.env.TEST_GROUP_ID || -1001075450562; const GROUPID = process.env.TEST_GROUP_ID || -1001075450562;
const GAME_SHORT_NAME = process.env.TEST_GAME_SHORT_NAME || 'medusalab_test'; const GAME_SHORT_NAME = process.env.TEST_GAME_SHORT_NAME || 'medusalab_test';
const STICKER_SET_NAME = process.env.TEST_STICKER_SET_NAME || 'pusheen';
const timeout = 60 * 1000; const timeout = 60 * 1000;
let portindex = 8091; let portindex = 8091;
const staticPort = portindex++; const staticPort = portindex++;
@ -38,12 +29,9 @@ const pollingPort2 = portindex++;
const webHookPort2 = portindex++; const webHookPort2 = portindex++;
const badTgServerPort = portindex++; const badTgServerPort = portindex++;
const staticUrl = `http://127.0.0.1:${staticPort}`; const staticUrl = `http://127.0.0.1:${staticPort}`;
const key = `${__dirname}/../examples/ssl/key.pem`; const key = `${__dirname}/../examples/key.pem`;
const cert = `${__dirname}/../examples/ssl/crt.pem`;
const ip = '216.58.210.174'; // Google IP ¯\_(ツ)_/¯ const ip = '216.58.210.174'; // Google IP ¯\_(ツ)_/¯
const lat = 47.5351072; const cert = `${__dirname}/../examples/crt.pem`;
const long = -52.7508537;
const FILE_PATH = `${__dirname}/data/photo.gif`;
let FILE_ID; let FILE_ID;
let GAME_CHAT_ID; let GAME_CHAT_ID;
let GAME_MSG_ID; let GAME_MSG_ID;
@ -104,7 +92,7 @@ describe('TelegramBot', function telegramSuite() {
utils.handleRatelimit(bot, 'sendPhoto', this); utils.handleRatelimit(bot, 'sendPhoto', this);
utils.handleRatelimit(bot, 'sendMessage', this); utils.handleRatelimit(bot, 'sendMessage', this);
utils.handleRatelimit(bot, 'sendGame', this); utils.handleRatelimit(bot, 'sendGame', this);
return bot.sendPhoto(USERID, FILE_PATH).then(resp => { return bot.sendPhoto(USERID, `${__dirname}/data/photo.gif`).then(resp => {
FILE_ID = resp.photo[0].file_id; FILE_ID = resp.photo[0].file_id;
return bot.sendMessage(USERID, 'chat'); return bot.sendMessage(USERID, 'chat');
}).then(resp => { }).then(resp => {
@ -115,15 +103,6 @@ describe('TelegramBot', function telegramSuite() {
}); });
}); });
it('allows providing custom Promise library', function test() {
TelegramBot.Promise = global.Promise;
const promise = bot.stopPolling();
assert.ok(promise instanceof global.Promise);
assert.ok(!(promise instanceof Promise));
// revert
TelegramBot.Promise = Promise;
});
it('automatically starts polling', function test() { it('automatically starts polling', function test() {
assert.equal(botPolling.isPolling(), true); assert.equal(botPolling.isPolling(), true);
return utils.isPollingMockServer(pollingPort2); return utils.isPollingMockServer(pollingPort2);
@ -633,6 +612,13 @@ describe('TelegramBot', function telegramSuite() {
assert.ok(is.object(resp.document)); 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() { describe('#sendSticker', function sendStickerSuite() {
@ -723,44 +709,6 @@ describe('TelegramBot', function telegramSuite() {
}); });
}); });
describe.skip('#sendVideoNote', function sendVideoNoteSuite() {
let videoNoteId;
this.timeout(timeout);
before(function before() {
utils.handleRatelimit(bot, 'sendVideoNote', this);
});
it('should send a video from file', function test() {
const video = `${__dirname}/data/video.mp4`;
return bot.sendVideoNote(USERID, video, { length: 5 }).then(resp => {
assert.ok(is.object(resp));
assert.ok(is.object(resp.video_note));
videoNoteId = resp.video_note.file_id;
});
});
it('should send a video from id', function test() {
// Send the same videonote as before
assert.ok(videoNoteId);
return bot.sendVideoNote(USERID, videoNoteId, { length: 5 }).then(resp => {
assert.ok(is.object(resp));
assert.ok(is.object(resp.video_note));
});
});
it('should send a video from fs.readStream', function test() {
const video = fs.createReadStream(`${__dirname}/data/video.mp4`);
return bot.sendVideoNote(USERID, video, { length: 5 }).then(resp => {
assert.ok(is.object(resp));
assert.ok(is.object(resp.video_note));
});
});
it('should send a video from a Buffer', function test() {
const video = fs.readFileSync(`${__dirname}/data/video.mp4`);
return bot.sendVideoNote(USERID, video, { length: 5 }).then(resp => {
assert.ok(is.object(resp));
assert.ok(is.object(resp.video_note));
});
});
});
describe('#sendVoice', function sendVoiceSuite() { describe('#sendVoice', function sendVoiceSuite() {
let voiceId; let voiceId;
this.timeout(timeout); this.timeout(timeout);
@ -821,115 +769,8 @@ describe('TelegramBot', function telegramSuite() {
describe.skip('#unbanChatMember', function unbanChatMemberSuite() {}); describe.skip('#unbanChatMember', function unbanChatMemberSuite() {});
describe.skip('#restrictChatMember', function restrictChatMemberSuite() {});
describe.skip('#promoteChatMember', function promoteChatMemberSuite() {});
describe.skip('#answerCallbackQuery', function answerCallbackQuerySuite() {}); describe.skip('#answerCallbackQuery', function answerCallbackQuerySuite() {});
describe('#exportChatInviteLink', function exportChatInviteLinkSuite() {
before(function before() {
utils.handleRatelimit(bot, 'exportChatInviteLink', this);
});
it('should export the group invite link', function test() {
return bot.exportChatInviteLink(GROUPID).then(resp => {
assert(resp.match(/^https:\/\/t\.me\/joinchat\/.+$/i), 'is a telegram invite link');
});
});
});
describe('#setChatPhoto', function setChatPhotoSuite() {
this.timeout(timeout);
before(function before() {
utils.handleRatelimit(bot, 'setChatPhoto', this);
});
it('should set a chat photo from file', function test() {
const photo = `${__dirname}/data/chat_photo.png`;
return bot.setChatPhoto(GROUPID, photo).then(resp => {
assert.equal(resp, true);
});
});
it('should set a chat photo from fs.readStream', function test() {
const photo = fs.createReadStream(`${__dirname}/data/chat_photo.png`);
return bot.setChatPhoto(GROUPID, photo).then(resp => {
assert.equal(resp, true);
});
});
it('should set a chat photo from request Stream', function test() {
const photo = request(`${staticUrl}/chat_photo.png`);
return bot.setChatPhoto(GROUPID, photo).then(resp => {
assert.equal(resp, true);
});
});
it('should set a chat photo from a Buffer', function test() {
const photo = fs.readFileSync(`${__dirname}/data/chat_photo.png`);
return bot.setChatPhoto(GROUPID, photo).then(resp => {
assert.equal(resp, true);
});
});
});
describe('#deleteChatPhoto', function deleteChatPhotoSuite() {
before(function before() {
utils.handleRatelimit(bot, 'deleteChatPhoto', this);
});
it('should delete the chat photo', function test() {
return bot.deleteChatPhoto(GROUPID).then(resp => {
assert.equal(resp, true);
});
});
});
describe('#setChatTitle', function setChatTitleSuite() {
before(function before() {
utils.handleRatelimit(bot, 'setChatTitle', this);
});
it('should set the chat title', function test() {
return bot.setChatTitle(GROUPID, 'ntba test group').then(resp => {
assert.equal(resp, true);
});
});
});
describe('#setChatDescription', function setChatDescriptionSuite() {
before(function before() {
utils.handleRatelimit(bot, 'setChatDescription', this);
});
it('should set the chat description', function test() {
const random = Math.floor(Math.random() * 1000);
const description = `node-telegram-bot-api test group (random: ${random})`;
return bot.setChatDescription(GROUPID, description).then(resp => {
assert.equal(resp, true);
});
});
});
describe('#pinChatMessage', function pinChatMessageSuite() {
let messageId;
before(function before() {
utils.handleRatelimit(bot, 'pinChatMessage', this);
return bot.sendMessage(GROUPID, 'To be pinned').then(resp => {
messageId = resp.message_id;
});
});
it('should pin chat message', function test() {
return bot.pinChatMessage(GROUPID, messageId).then(resp => {
assert.equal(resp, true);
});
});
});
describe('#unpinChatMessage', function unpinChatMessageSuite() {
before(function before() {
utils.handleRatelimit(bot, 'unpinChatMessage', this);
});
it('should unpin chat message', function test() {
return bot.unpinChatMessage(GROUPID).then(resp => {
assert.equal(resp, true);
});
});
});
describe('#editMessageText', function editMessageTextSuite() { describe('#editMessageText', function editMessageTextSuite() {
before(function before() { before(function before() {
utils.handleRatelimit(bot, 'sendMessage', this); utils.handleRatelimit(bot, 'sendMessage', this);
@ -1040,6 +881,8 @@ describe('TelegramBot', function telegramSuite() {
utils.handleRatelimit(bot, 'sendLocation', this); utils.handleRatelimit(bot, 'sendLocation', this);
}); });
it('should send a location', function test() { it('should send a location', function test() {
const lat = 47.5351072;
const long = -52.7508537;
return bot.sendLocation(USERID, lat, long).then(resp => { return bot.sendLocation(USERID, lat, long).then(resp => {
assert.ok(is.object(resp)); assert.ok(is.object(resp));
assert.ok(is.object(resp.location)); assert.ok(is.object(resp.location));
@ -1049,51 +892,13 @@ describe('TelegramBot', function telegramSuite() {
}); });
}); });
describe('#editMessageLiveLocation', function editMessageLiveLocationSuite() {
let message;
before(function before() {
utils.handleRatelimit(bot, 'editMessageLiveLocation', this);
const opts = { live_period: 86400 };
return bot.sendLocation(USERID, lat, long, opts).then(resp => { message = resp; });
});
it('edits live location', function test() {
const opts = { chat_id: USERID, message_id: message.message_id };
return bot.editMessageLiveLocation(lat + 1, long + 1, opts).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('#stopMessageLiveLocation', function editMessageLiveLocationSuite() {
let message;
before(function before() {
utils.handleRatelimit(bot, 'stopMessageLiveLocation', this);
return bot.sendLocation(USERID, lat, long, { live_period: 86400 })
.then((resp) => {
message = resp;
const opts = { chat_id: USERID, message_id: message.message_id };
return bot.editMessageLiveLocation(lat + 1, long + 1, opts);
});
});
it('stops location updates', function test() {
const opts = { chat_id: USERID, message_id: message.message_id };
return bot.stopMessageLiveLocation(opts).then(resp => {
assert.ok(is.object(resp));
assert.ok(is.object(resp.location));
assert.ok(is.number(resp.location.latitude));
assert.ok(is.number(resp.location.longitude));
});
});
});
describe('#sendVenue', function sendVenueSuite() { describe('#sendVenue', function sendVenueSuite() {
before(function before() { before(function before() {
utils.handleRatelimit(bot, 'sendVenue', this); utils.handleRatelimit(bot, 'sendVenue', this);
}); });
it('should send a venue', function test() { it('should send a venue', function test() {
const lat = 47.5351072;
const long = -52.7508537;
const title = 'The Village Shopping Centre'; const title = 'The Village Shopping Centre';
const address = '430 Topsail Rd,St. John\'s, NL A1E 4N1, Canada'; const address = '430 Topsail Rd,St. John\'s, NL A1E 4N1, Canada';
return bot.sendVenue(USERID, lat, long, title, address).then(resp => { return bot.sendVenue(USERID, lat, long, title, address).then(resp => {
@ -1153,27 +958,7 @@ describe('TelegramBot', function telegramSuite() {
return bot.getFileLink(FILE_ID) return bot.getFileLink(FILE_ID)
.then(fileURI => { .then(fileURI => {
assert.ok(is.string(fileURI)); assert.ok(is.string(fileURI));
assert.ok(utils.isTelegramFileURI(fileURI)); assert.ok(/https?:\/\/.*\/file\/bot.*\/.*/.test(fileURI));
});
});
});
describe('#getFileStream', function getFileStreamSuite() {
this.timeout(timeout);
before(function before() {
// utils.handleRatelimit(bot, 'getFileStream', this);
});
it('should get a file stream', function test(done) {
const fileStream = bot.getFileStream(FILE_ID);
assert.ok(fileStream instanceof stream.Readable);
assert.equal(fileStream.path, FILE_ID);
fileStream.on('info', (info) => {
assert.ok(info);
assert.ok(utils.isTelegramFileURI(info.uri), `${info.uri} is not a file URI`);
fileStream.pipe(concat(function readFile(buffer) {
buffer.equals(fs.readFileSync(FILE_PATH)); // sync :(
return done();
}));
}); });
}); });
}); });
@ -1354,86 +1139,33 @@ describe('TelegramBot', function telegramSuite() {
}); });
}); });
describe('#sendInvoice', function sendInvoiceSuite() { describe('#_formatSendData', function _formatSendDataSuite() {
before(function before() { it('should handle buffer path from fs.readStream', function test() {
utils.handleRatelimit(bot, 'sendInvoice', this); let photo;
}); try {
it('should send an invoice', function test() { photo = fs.createReadStream(Buffer.from(`${__dirname}/data/photo.gif`));
if (isCI) { } catch (ex) {
this.skip(); // Skip test for now // Older Node.js versions do not support passing a Buffer
// representation of the path to fs.createReadStream()
if (ex instanceof TypeError) return Promise.resolve();
} }
const title = 'Demo product'; return bot.sendPhoto(USERID, photo).then(resp => {
const description = 'our test product';
const payload = 'sku-p001';
const providerToken = PROVIDER_TOKEN;
const startParameter = 'pay';
const currency = 'KES';
const prices = [{ label: 'product', amount: 11000 }, { label: 'tax', amount: 11000 }];
return bot.sendInvoice(USERID, title, description, payload, providerToken, startParameter, currency, prices).then(resp => {
assert.ok(is.object(resp));
assert.ok(is.object(resp.invoice));
assert.ok(is.number(resp.invoice.total_amount));
});
});
});
describe.skip('#answerShippingQuery', function answerShippingQuerySuite() {});
describe.skip('#answerPreCheckoutQuery', function answerPreCheckoutQuerySuite() {});
describe('#getStickerSet', function getStickerSetSuite() {
before(function before() {
utils.handleRatelimit(bot, 'getStickerSet', this);
});
it('should get the sticker set given the name of the set', function test() {
return bot.getStickerSet(STICKER_SET_NAME).then(resp => {
assert.ok(is.object(resp));
assert.equal(resp.name.toLowerCase(), STICKER_SET_NAME);
assert.ok(is.string(resp.title));
assert.ok(is.boolean(resp.contains_masks));
assert.ok(is.array(resp.stickers));
});
});
});
describe('#uploadStickerFile', function sendPhotoSuite() {
before(function before() {
utils.handleRatelimit(bot, 'uploadStickerFile', this);
});
it('should upload a sticker from file', function test() {
const sticker = `${__dirname}/data/sticker.png`;
return bot.uploadStickerFile(USERID, sticker).then(resp => {
assert.ok(is.object(resp)); assert.ok(is.object(resp));
assert.ok(is.string(resp.file_id)); assert.ok(is.array(resp.photo));
});
}); });
// Other tests (eg. Buffer, URL) are skipped, because they rely on the same features as sendPhoto.
}); });
it('should not accept file-paths if disallowed with constructor option', function test() {
describe('#sendMediaGroup', function sendMediaGroupSuite() { const tgbot = new TelegramBot(TOKEN, { filepath: false });
before(function before() { const photo = `${__dirname}/data/photo.gif`;
utils.handleRatelimit(bot, 'sendMediaGroup', this); 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 send group of photos/videos as album', function test() {
return bot.sendMediaGroup(USERID, [
{
type: 'photo',
media: `${__dirname}/data/photo.gif`,
},
{
type: 'video',
media: `${__dirname}/data/video.mp4`,
},
{
type: 'photo',
media: FILE_ID,
},
], {
disable_notification: true,
}).then(resp => {
assert.ok(is.array(resp));
assert.equal(resp.length, 3);
}); });
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);
}); });
}); });
}); // End Telegram }); // End Telegram

@ -1,139 +0,0 @@
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const TelegramBot = require('..');
const paths = {
audio: path.join(__dirname, 'data/audio.mp3'),
};
describe('#_formatSendData', function sendfileSuite() {
const bot = new TelegramBot('token');
const type = 'file';
before(function beforeSuite() {
process.env.NTBA_FIX_350 = 1;
});
after(function afterSuite() {
delete process.env.NTBA_FIX_350;
});
describe('using fileOptions', function sendfileOptionsSuite() {
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 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) {
this.skip('Stream#path unsupported');
return;
}
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) {
this.skip('Stream#path unsupported');
return;
}
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');
});
});
});
});
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);
});
});
});

@ -30,13 +30,6 @@ exports = module.exports = {
* @return {Promise} * @return {Promise}
*/ */
isPollingMockServer, isPollingMockServer,
/**
* Return true if the string is a URI to a file
* on Telegram servers.
* @param {String} uri
* @return {Boolean}
*/
isTelegramFileURI,
/** /**
* Send a message to the webhook at the specified port and path. * Send a message to the webhook at the specified port and path.
* @param {Number} port * @param {Number} port
@ -224,8 +217,3 @@ function handleRatelimit(bot, methodName, suite) {
}; };
return bot; return bot;
} }
function isTelegramFileURI(uri) {
return /https?:\/\/.*\/file\/bot.*\/.*/.test(uri);
}

Loading…
Cancel
Save