Compare commits

...

110 Commits

Author SHA1 Message Date
Alexander Cerutti b585957a0a doc: Add telegram-keyboard-wrapper to community section (#529) 8 years ago
Daniel Pérez Fernández 8b7d10e01b doc: Add link to tutorial [ES] (#545) 8 years ago
Gocho Mugo 1224613fec
doc: Add link to tutorial (#533) 8 years ago
Gocho Mugo 0461c0bfe3
doc: Add missing full stop (#515) 8 years ago
Neetshin 71fbad31d9 Update tutorial articles link 8 years ago
Benny Neugebauer 091ef7a8a7
doc: Add missing full stop 8 years ago
Oleksandr Tryshchenko 435f06319e doc: Fixed type annotations for message id parameter (#491) 8 years ago
GochoMugo 119d892c34
release: v0.30.0 8 years ago
GochoMugo 1169f8e430
chore: Use npm script prepublishOnly instead of deprecated prepublish 8 years ago
GochoMugo 5cecffc34b
chore/deps: Downgrade eslint for Node v4 compatibility 8 years ago
GochoMugo 7e9e9b11b3
test: Fix previously-uncaught lint errors 8 years ago
GochoMugo 2bdd50fdc2
chore/deps: Update dependencies 8 years ago
GochoMugo 309696622d
doc: Showcase and update contributor list 8 years ago
GochoMugo 1f4c79ec8e
chore: Ignore package lock file 8 years ago
GochoMugo 8edf1fbc6d
doc: Update changelog 8 years ago
GochoMugo 55332c3c25
doc: Update changelog, version badge 8 years ago
GochoMugo 96c50ba1bd
doc: Improve deprecation messages 8 years ago
GochoMugo 4358f20dbb
doc: Minor fixes on documentation 8 years ago
GochoMugo 0781ae685d
src/telegram: Add TelegramBot#sendMediaGroup() 8 years ago
Oleg 584610baaf doc: Add tutorial [RUS] (#477) 8 years ago
GochoMugo 1a08221174
src/telegram: Improve sending files (#471) 8 years ago
GochoMugo d7197998dd
doc: Add note on specifying additional Telegram query options 8 years ago
GochoMugo 4051117ed0
src: Minor reorganisation, fixes 8 years ago
GochoMugo f28416fbaf
src/telegram: Implement downloadFile() in terms of getFileStream() 8 years ago
GochoMugo 04e8b892aa
src/telegram: Emit 'info' on stream from TelegramBot#getFileStream() 8 years ago
GochoMugo b968e893d3
src/telegram: Populate Stream#path from TelegramBot#getFileStream() 8 years ago
GochoMugo b91409a2b6
test: Fix path to ssl key, cert 8 years ago
GochoMugo 455a93cacb
examples: Add notice of openshift 2 shutdown 8 years ago
GochoMugo 17f839498e
chore: Re-organise examples 8 years ago
GochoMugo d9692f45a9
src/telegram: Add TelegramBot#getFileStream() 8 years ago
Gocho Mugo 0870684d83
src/polling: Fix the Offset Infinite Loop bug (#265) 8 years ago
GochoMugo 8edb687283
src: Add fileOptions to remaining file-sending methods 8 years ago
GochoMugo 3b603b1dbd
src: Minor fix, complete TODO 8 years ago
GochoMugo b774ff436f src: Minor fixes 8 years ago
GochoMugo d6083e4327 src: Support file options, defaults in sending files 8 years ago
GochoMugo fb1f4344b3
test: Disable tests on Node.js v0.12 8 years ago
GochoMugo b1f0ebaf17
src/polling: Add options to TelegramBot#stopPolling() 8 years ago
GochoMugo ce9ff57a63
src/telegram: Don't JSON-serialize parameter if already of String type 8 years ago
GochoMugo 012e7df35f
src: Support API v3.5 `provider_data` parameter 8 years ago
GochoMugo 57dec19bab
src: Support future additional Telegram parameters 8 years ago
GochoMugo fce9244dfd
doc: Update help information 8 years ago
GochoMugo 0008a39227 pr: Merge edited PR (#440) 8 years ago
GochoMugo 183b6bdb28
doc: Generate API reference, Update changelog 8 years ago
GochoMugo 816511b19f pr: Merge kamikazechaser/node-telegram-bot-api#feature/stickersets 8 years ago
GochoMugo cfde217488
test: Fix lint error 8 years ago
GochoMugo 13472899e4 pr: Merge edited PR (#439) 8 years ago
GochoMugo 59378d381b
test: Add tests 8 years ago
GochoMugo 79118b9fae
doc: Minor fixes, Update changelog 8 years ago
GochoMugo 65384a11d4
pr: Merge kamikazechaser/node-telegram-bot-api#feature/livelocations 8 years ago
GochoMugo 1f61b82aa8
doc: Fix documented types for params `chatId`, `userId` 8 years ago
GochoMugo 633bd83a0a
src/telegram: JSON-serialize 'mask_position's 8 years ago
drGOD 4586ebd10b src/telegram: JSON-serialize shipping_options in answerShippingQuery (#468) 8 years ago
Yago fd337e91f2
Merge pull request #463 from saeedhei/patch-1 8 years ago
Yago 20c5ab6710
Merge branch 'master' into patch-1 8 years ago
Yago 5510c07876
Merge branch 'master' into patch-1 8 years ago
Yago 3fbc6c85b0
Merge pull request #462 from Archakov06/patch-2 8 years ago
Sedric Heidarizarei 757ba2476a
Persian Language added 8 years ago
Archakov Dennis 16045fda6e
Add tutorial link RUS 8 years ago
GochoMugo ba4d207684
src: Improve performance when matching message types 8 years ago
GochoMugo 772609c334
doc: Update ParseError message; error might not be from Telegram 8 years ago
GochoMugo ea420edad9
doc/help: Add link to bug '502 Bad Gateway' 8 years ago
GochoMugo 8c965d3a14
src: Improve default error logging of `polling_error`, `webhook_error` 8 years ago
MCSH 6263dcb007
examples: Fix game to answer callback query correctly 8 years ago
Aaron Meese e03ffe5d04 doc: Update README.md (#457) 8 years ago
GochoMugo dc897d95e5
chore: Update npm script `gen-doc` to `doc` 8 years ago
GochoMugo bbac07ad2c
doc: Document TelegramBot.errors, TelegramBot.messageTypes 8 years ago
GochoMugo 49df0c6e02
src/telegram: Add metadata argument in `message` event (and friends) 8 years ago
GochoMugo 3722c7182c
src/telegram: Fix TelegramBot#answerCallbackQuery() signature 8 years ago
GochoMugo 9cf36a3b20
pkg: Add support for Node v9 8 years ago
GochoMugo 6b21ca3570
doc: Update contributing guidelines 8 years ago
GochoMugo b6349de67e
doc: Document TelegramBot#on() 8 years ago
GochoMugo 7f41992e62
pkg: Bump minor version (v0.28.0 -> v0.29.0) 8 years ago
GochoMugo 1475881a37
doc: Update changelog 8 years ago
GochoMugo 4f50f7350c
src/telegram: Add API v3.2 methods 8 years ago
Mohammed Sohail 4d90529492
[API v3.4] Support sticker sets 8 years ago
Mohammed Sohail b25bc6eddb
[Doc] Generate documentation 8 years ago
Mohammed Sohail 7315fcff3e
[Fix] Minor copypasta typo 8 years ago
Mohammed Sohail 962ce2af3c
[API v3.4] Support live locations 8 years ago
CapacitorSet 8fd243e6a8
src/telegram: Add TelegramBot#uploadStickerFile 8 years ago
CapacitorSet a2d85b889a
src/telegram: Add TelegramBot#getStickerSet() 8 years ago
GochoMugo f4f015a621
src/telegram: Use $NTBA_FIX_319 to apply fix for #319 9 years ago
GochoMugo ef48af49f5
src/telegram: Deprecate auto-enabling Promise cancellation 9 years ago
GochoMugo 28160c70cd
doc: Fix broken link in change-log 9 years ago
GochoMugo 580afc92c1
pkg: Bump minor version (0.27.1 -> 0.28.0) 9 years ago
GochoMugo 4bb1374435
doc: Update changelog 9 years ago
GochoMugo 832bf78c94
pkg: Add support for Node v7, v8 9 years ago
GochoMugo 5f7d306f60
test: Skip tests on TelegramBot#sendVideoNote() 9 years ago
GochoMugo 4ed6d945ab
pr/368: Update TelegramBot#answerCallbackQuery() signature 9 years ago
Misha 67839b6faa src/telegram: Update TelegramBot#answerCallbackQuery() signature 9 years ago
Yago f20a7f4ee5 Uppps, fix "{" 9 years ago
Yago 0bf148adb9 Check if test are running in CI and skip invoice if so 9 years ago
Yago e1f8fb25ec Skip sendInvoice test method 9 years ago
GochoMugo fd8251b90f
chore/syntax: Fix missing colons 9 years ago
GochoMugo d38dfd3a12
pr/362: Support Bot API v3.1 (#362, #369) 9 years ago
GochoMugo 8ed0a2dcef
test: Fix test for TelegramBot#setChatDescription() 9 years ago
GochoMugo 96b90c2d56
src/telegram: Add tests for Bot API v3.1 methods 9 years ago
Savely Krasovsky 83d3235cc5 src/telegram: Support Bot API v3.1 9 years ago
kucherenkovova 61e8f85368 src/telegram: Fix download file path on windows 9 years ago
GochoMugo 9a4298308e
doc: Add tutorials page 9 years ago
GochoMugo a3268164f5
examples: Add game example 9 years ago
Victor Le 54391e76a0 doc: Fix anchor webhook link (#346) 9 years ago
GochoMugo d082a2d7d9
src/telegram: Sort the message types for less cognitive load 9 years ago
Mohammed Sohail 5490a3a6e0 doc: Update API version badge (#339) 9 years ago
GochoMugo 73269e79e2
src/telegram: Add missing event 'video_note' 9 years ago
Mohammed Sohail fe527957e0 src/telegram: Support API v3 Payments (#335) 9 years ago
Mohammed Sohail 07a6e5ff23 src/telegram: Add missing Message sub-types, Deprecate old ones (#340) 9 years ago
Mohammed Sohail 177c951340 src/telegram: Add API v3 new update types and message sub-types (#333) 9 years ago
GochoMugo cda9d8d597
src/telegram: Add deleteMessage method 9 years ago
Mohammed Sohail 942fc4854b src/telegram: Add sendVideoNote method (#330) 9 years ago
Guidone 4653bb1b38 doc: Add RedBot to list of projects using telegram-bot-api (#341) 9 years ago
  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. 892
      doc/api.md
  9. 24
      doc/help.md
  10. 10
      doc/tutorials.md
  11. 76
      doc/usage.md
  12. 140
      examples/game/game.html
  13. 53
      examples/game/game.js
  14. 0
      examples/ssl/crt.pem
  15. 0
      examples/ssl/key.pem
  16. 3
      examples/webhook/express.js
  17. 2
      examples/webhook/heroku.js
  18. 6
      examples/webhook/https.js
  19. 2
      examples/webhook/now.js
  20. 14
      examples/webhook/openshift2.js
  21. 436
      package.json
  22. 6
      src/errors.js
  23. 1016
      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. 355
      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 gen-doc` - [ ] I have run `npm run doc`
### Description ### Description

1
.gitignore vendored

@ -2,6 +2,7 @@ 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,9 +1,12 @@
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,10 +5,86 @@ 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)
* * * * * *
@ -125,8 +201,13 @@ 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
[Unreleased]:https://github.com/yagop/node-telegram-bot-api/compare/v0.27.1...master [0.28.0]:https://github.com/yagop/node-telegram-bot-api/releases/tag/v0.28.0
[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,18 +10,29 @@ 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 gen-doc $ npm run 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](http://img.shields.io/badge/Bot%20API-v2.3.1-00aced.svg)](https://core.telegram.org/bots/api) [![Bot API](https://img.shields.io/badge/Bot%20API-v.3.5.0-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 needed, to obtain one, talk to [@botfather](https://telegram.me/BotFather) and create a new bot. 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).
## Install ## Install
@ -54,40 +54,49 @@ 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, and might interest you: Some things built using this library that 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 (c) 2017 Yago Copyright © 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,3 +15,5 @@
[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,6 +29,8 @@ 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?
@ -67,7 +69,9 @@ Sources:
<a name="error-meanings"></a> <a name="error-meanings"></a>
### What does this error mean? ### What does this error mean?
*Not done. PRs welcome!* * [502 Bad Gateway](https://github.com/yagop/node-telegram-bot-api/issues/377)
*Not complete. PRs welcome!*
Sources: Sources:
@ -125,4 +129,22 @@ 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

@ -0,0 +1,10 @@
# 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,21 +1,27 @@
# 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 event-emitter that emits the following events: *TelegramBot* is an [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
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 will **ALSO** be emitted: `text`, `audio`, `document`, `photo`, events may **ALSO** be emitted: `text`, `audio`, `document`, `photo`,
`sticker`, `video`, `voice`, `contact`, `location`, `sticker`, `video`, `voice`, `contact`, `location`,
`new_chat_participant`, `left_chat_participant`, `new_chat_title`, `new_chat_members`, `left_chat_member`, `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]
@ -26,8 +32,11 @@
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.
@ -107,6 +116,63 @@ 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

@ -0,0 +1,140 @@
<!-- 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>

@ -0,0 +1,53 @@
/**
* 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}`);
});

@ -2,12 +2,13 @@
* 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');

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

@ -10,6 +10,12 @@ 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,3 +1,4 @@
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;
@ -49,7 +50,7 @@ class TelegramBotPolling {
/** /**
* Stop polling * Stop polling
* @param {Object} [options] * @param {Object} [options] 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}
@ -79,6 +80,18 @@ 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
@ -93,18 +106,59 @@ 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 (this.bot.listeners('polling_error').length) { if (!err._processing) {
this.bot.emit('polling_error', err); return this._error(err);
} else {
console.error(err); // eslint-disable-line no-console
} }
return null; delete err._processing;
/*
* 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 = {
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); // eslint-disable-line no-console return console.error('error: [webhook_error] %j', error); // eslint-disable-line no-console
} }
return this.bot.emit('webhook_error', error); return this.bot.emit('webhook_error', error);
} }

@ -10,9 +10,17 @@ export TEST_USER_ID=<USER_ID>
# Group Id which to use in some of the tests, e.g. for TelegramBot#getChat() # Group Id which to use in some of the tests, e.g. for TelegramBot#getChat()
export TEST_GROUP_ID=<GROUP_ID> export TEST_GROUP_ID=<GROUP_ID>
# Game short name which to use in some of the tests, e.g. TelegramBot#sendGame() # Game short name to use in some 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
@ -20,3 +28,4 @@ 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.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

@ -5,8 +5,11 @@ 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';
@ -16,10 +19,16 @@ 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++;
@ -29,9 +38,12 @@ 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/key.pem`; const key = `${__dirname}/../examples/ssl/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 cert = `${__dirname}/../examples/crt.pem`; const lat = 47.5351072;
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;
@ -92,7 +104,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, `${__dirname}/data/photo.gif`).then(resp => { return bot.sendPhoto(USERID, FILE_PATH).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 => {
@ -103,6 +115,15 @@ 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);
@ -612,13 +633,6 @@ 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() {
@ -709,6 +723,44 @@ 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);
@ -769,8 +821,115 @@ 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);
@ -837,6 +996,21 @@ describe('TelegramBot', function telegramSuite() {
}); });
}); });
describe('#deleteMessage', function deleteMessageSuite() {
let messageId;
before(function before() {
utils.handleRatelimit(bot, 'deleteMessage', this);
return bot.sendMessage(USERID, 'To be deleted').then(resp => {
messageId = resp.message_id;
});
});
it('should delete message', function test() {
return bot.deleteMessage(USERID, messageId).then(resp => {
assert.equal(resp, true);
});
});
});
describe('#getUserProfilePhotos', function getUserProfilePhotosSuite() { describe('#getUserProfilePhotos', function getUserProfilePhotosSuite() {
const opts = { const opts = {
offset: 0, offset: 0,
@ -866,8 +1040,6 @@ 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));
@ -877,13 +1049,51 @@ 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 => {
@ -943,7 +1153,27 @@ 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(/https?:\/\/.*\/file\/bot.*\/.*/.test(fileURI)); assert.ok(utils.isTelegramFileURI(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();
}));
}); });
}); });
}); });
@ -1124,33 +1354,86 @@ describe('TelegramBot', function telegramSuite() {
}); });
}); });
describe('#_formatSendData', function _formatSendDataSuite() { describe('#sendInvoice', function sendInvoiceSuite() {
it('should handle buffer path from fs.readStream', function test() { before(function before() {
let photo; utils.handleRatelimit(bot, 'sendInvoice', this);
try { });
photo = fs.createReadStream(Buffer.from(`${__dirname}/data/photo.gif`)); it('should send an invoice', function test() {
} catch (ex) { if (isCI) {
// Older Node.js versions do not support passing a Buffer this.skip(); // Skip test for now
// representation of the path to fs.createReadStream()
if (ex instanceof TypeError) return Promise.resolve();
} }
return bot.sendPhoto(USERID, photo).then(resp => { const title = 'Demo product';
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));
assert.ok(is.array(resp.photo)); assert.ok(is.object(resp.invoice));
assert.ok(is.number(resp.invoice.total_amount));
}); });
}); });
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);
}); });
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.string(resp.file_id));
});
});
// Other tests (eg. Buffer, URL) are skipped, because they rely on the same features as sendPhoto.
});
describe('#sendMediaGroup', function sendMediaGroupSuite() {
before(function before() {
utils.handleRatelimit(bot, 'sendMediaGroup', this);
});
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

@ -0,0 +1,139 @@
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,6 +30,13 @@ 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
@ -217,3 +224,8 @@ function handleRatelimit(bot, methodName, suite) {
}; };
return bot; return bot;
} }
function isTelegramFileURI(uri) {
return /https?:\/\/.*\/file\/bot.*\/.*/.test(uri);
}

Loading…
Cancel
Save