mirror of
https://github.com/eternnoir/pyTelegramBotAPI.git
synced 2023-08-10 21:12:57 +03:00
Compare commits
223 Commits
Author | SHA1 | Date | |
---|---|---|---|
25ad3f055f | |||
cdb6d6760d | |||
5bd8e9d3f5 | |||
fab2f324d0 | |||
9bf4be2caf | |||
d701fd6e1d | |||
8e3c9d8d24 | |||
0b9f91c6fb | |||
468a535257 | |||
d2e7f4d8f2 | |||
ddd3664329 | |||
9e397c41cf | |||
b528b0bcf0 | |||
2f20d70e89 | |||
ac740e4755 | |||
2cf8ffcbc0 | |||
87ad4a62ff | |||
6b255deed7 | |||
ce24aa25f2 | |||
a0f25089e0 | |||
1bfdf7cf35 | |||
5b1de9f339 | |||
e956800e34 | |||
b9a57f630a | |||
851d76888b | |||
05f1c87c7d | |||
59b19fbbc1 | |||
e87907f0b8 | |||
d84aa796c0 | |||
8c20f63022 | |||
f9c7497c5c | |||
3413669a23 | |||
1a45b4844a | |||
8a9d89591b | |||
234dd8cf9f | |||
6a98d27f1a | |||
04df139efb | |||
747f14216b | |||
aee9255568 | |||
fbaf88c237 | |||
3ebc47de8b | |||
8017c8d919 | |||
2545724a6f | |||
b8770e2c04 | |||
1da5d1e0e2 | |||
bc73f345b2 | |||
a824ff967b | |||
0c420ee5e4 | |||
16838c30b6 | |||
022cdada49 | |||
fb1052824c | |||
769bb55bbe | |||
655940dcf8 | |||
3b52e5c49b | |||
700b869555 | |||
5906d632d6 | |||
bd513cd343 | |||
0cb99b42e4 | |||
dc1869167d | |||
ef22fafa7e | |||
94d1b3f7a8 | |||
ef68bed01f | |||
0eb21aa673 | |||
e6105d851c | |||
7ec33d43b3 | |||
0a856f4e83 | |||
bbe53b5e17 | |||
84257b6ba9 | |||
5b234920c7 | |||
16d4c64b00 | |||
4fb85e25f6 | |||
0ae20e1a49 | |||
e913635755 | |||
225cbf2c61 | |||
ae63b8029f | |||
e47790ded8 | |||
d0666071d8 | |||
eb8e87fdec | |||
1c98e1108c | |||
8a0bf8ca7e | |||
7490f63f25 | |||
22ab50989e | |||
1e320a5b0d | |||
96e06c248b | |||
ae92d1998e | |||
d87e0821c9 | |||
ee52241cc2 | |||
a6d35fd1de | |||
016819cd44 | |||
036441b8f6 | |||
228683aeda | |||
4fe4061a0f | |||
f873658aac | |||
13327c371a | |||
4dc7af71a0 | |||
209764a5d7 | |||
c1247249c7 | |||
a6b0e9598c | |||
7958264d64 | |||
74c3a3545d | |||
7b007dab99 | |||
8316a57845 | |||
2eb914d329 | |||
2449a3ea64 | |||
a2d91808ef | |||
23dd31752b | |||
7770fcbb33 | |||
c64fdf149e | |||
17184697ea | |||
78d86752fb | |||
7669781737 | |||
ddc1a4d66f | |||
3d9e012c40 | |||
9b9c0287ec | |||
292191038f | |||
f0e157213c | |||
18330275bd | |||
d173681a7c | |||
0830f2a0f9 | |||
45d6f8980c | |||
28417d18af | |||
e0ae119512 | |||
e851f37712 | |||
40cf8d6903 | |||
e507463741 | |||
3a055734f9 | |||
16ec573c9e | |||
2cd19a1a39 | |||
5f9cf881e6 | |||
baacafe3c0 | |||
734767177d | |||
1e7f71029c | |||
02c22c990b | |||
00d6481818 | |||
a74ed6e3ab | |||
38eb2ba833 | |||
93dc1cd92e | |||
a7469e6b14 | |||
b8f251140d | |||
e2e6b07fdb | |||
a2ceffecc8 | |||
805e3554de | |||
94d34747d1 | |||
8278eef7f3 | |||
94f1bbd402 | |||
c706a7aba3 | |||
c214f8000e | |||
81a201f19f | |||
0f0d76ca82 | |||
fa6f16ca5e | |||
a1267a7670 | |||
79c46bccae | |||
e912546680 | |||
7eeda3bc4d | |||
b5680a1c1f | |||
7514c617fc | |||
32f3a64720 | |||
3a08111f10 | |||
eeff4e7bf8 | |||
c37b3625aa | |||
a3519ff539 | |||
43ad415320 | |||
fb7c79fdfb | |||
413f081b19 | |||
cea0604ede | |||
be639ef4dd | |||
528b23770f | |||
4e472dda48 | |||
17dec34923 | |||
a17b301f1c | |||
cf830a0fb4 | |||
7e9f5b09cf | |||
969c5e76ef | |||
7346326bc3 | |||
f355796b01 | |||
5175803d0b | |||
bf9939d40e | |||
07960fe348 | |||
f4be18e082 | |||
8e9837a587 | |||
361c043872 | |||
50432dbef1 | |||
37277d74cc | |||
9ee341f5e6 | |||
0bd284afc7 | |||
4ffdada427 | |||
d8effd3f9f | |||
303e15b88d | |||
3273aa9afa | |||
9e8b11051c | |||
b9d458e643 | |||
1e6361dd57 | |||
ca2019b8f1 | |||
a230665424 | |||
8eb6e034fe | |||
d6552eb4c6 | |||
117c5a1141 | |||
3f335c37ce | |||
e7e681928d | |||
855ff40070 | |||
29a42a398b | |||
b801728924 | |||
d14e9051d4 | |||
60ca1751ca | |||
941b8ac5d0 | |||
7a08102fad | |||
325061ee96 | |||
8839f36706 | |||
1c53955d5a | |||
9cd28b88be | |||
2fb2cd6f20 | |||
036d000b95 | |||
11d4bb02a8 | |||
f4b0b61b9d | |||
4b8989f3d2 | |||
19b4e35ee5 | |||
61b4ca8a37 | |||
1038d4fafa | |||
fd1f16598b | |||
07c28830db | |||
a8fccdbeb3 | |||
c213b7732b | |||
2ca5c0d6f3 |
@ -2,6 +2,7 @@ language: python
|
||||
python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "pypy"
|
||||
- "pypy3"
|
||||
|
180
README.md
180
README.md
@ -2,8 +2,9 @@
|
||||
|
||||
<p align="center">A simple, but extensible Python implementation for the [Telegram Bot API](https://core.telegram.org/bots/api).
|
||||
|
||||
|
||||
<p align="center">[](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
[](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
|
||||
* [Getting started.](#getting-started)
|
||||
* [Writing your first bot](#writing-your-first-bot)
|
||||
@ -14,8 +15,10 @@
|
||||
* [Methods](#methods)
|
||||
* [General use of the API](#general-use-of-the-api)
|
||||
* [Message handlers](#message-handlers)
|
||||
* [Callback Query handlers](#message-handlers)
|
||||
* [TeleBot](#telebot)
|
||||
* [Reply markup](#reply-markup)
|
||||
* [Inline Mode](#inline-mode)
|
||||
* [Advanced use of the API](#advanced-use-of-the-api)
|
||||
* [Asynchronous delivery of messages](#asynchronous-delivery-of-messages)
|
||||
* [Sending large text messages](#sending-large-text-messages)
|
||||
@ -24,9 +27,11 @@
|
||||
* [Using web hooks](#using-web-hooks)
|
||||
* [Logging](#logging)
|
||||
* [F.A.Q.](#faq)
|
||||
* [Bot 2.0](#bot-20)
|
||||
* [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)
|
||||
* [The Telegram Chat Group](#the-telegram-chat-group)
|
||||
* [More examples](#more-examples)
|
||||
* [Bots using this API](#bots-using-this-api)
|
||||
|
||||
## Getting started.
|
||||
|
||||
@ -118,8 +123,8 @@ To start the bot, simply open up a terminal and enter `python echo_bot.py` to ru
|
||||
|
||||
All types are defined in types.py. They are all completely in line with the [Telegram API's definition of the types](https://core.telegram.org/bots/api#available-types), except for the Message's `from` field, which is renamed to `from_user` (because `from` is a Python reserved token). Thus, attributes such as `message_id` can be accessed directly with `message.message_id`. Note that `message.chat` can be either an instance of `User` or `GroupChat` (see [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)).
|
||||
|
||||
The Message object also has a `content_type`attribute, which defines the type of the Message. `content_type` can be one of the following strings:
|
||||
'text', 'audio', 'document', 'photo', 'sticker', 'video', 'location', 'contact', 'new_chat_participant', 'left_chat_participant', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created'.
|
||||
The Message object also has a `content_types`attribute, which defines the type of the Message. `content_types` can be one of the following strings:
|
||||
'text', 'audio', 'document', 'photo', 'sticker', 'video', 'voice', 'location', 'contact', 'new_chat_participant', 'left_chat_participant', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created'.
|
||||
|
||||
### Methods
|
||||
|
||||
@ -150,6 +155,7 @@ TeleBot supports the following filters:
|
||||
|func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True`
|
||||
|
||||
Here are some examples of using the filters and message handlers:
|
||||
|
||||
```python
|
||||
import telebot
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
@ -181,8 +187,30 @@ def test_message(message):
|
||||
@bot.message_handler(func=test_message, content_types=['document'])
|
||||
def handle_text_doc(message)
|
||||
pass
|
||||
|
||||
# Handlers can be stacked to create a function which will be called if either message_handler is eligible
|
||||
# This handler will be called if the message starts with '/hello' OR is some emoji
|
||||
@bot.message_handler(commands=['hello'])
|
||||
@bot.message_handler(func=lambda msg: msg.text.encode("utf-8") == SOME_FANCY_EMOJI)
|
||||
def send_something(message):
|
||||
pass
|
||||
```
|
||||
**Important: all handlers are tested in the order in which they were declared**
|
||||
|
||||
#### Edited Message handlers
|
||||
|
||||
Same as Message handlers
|
||||
|
||||
#### Callback Query Handler
|
||||
|
||||
In bot2.0 update. You can get `callback_query` in update object. In telebot use `callback_query_handler` to process callback_querys.
|
||||
|
||||
```python
|
||||
@bot.callback_query_handler(func=lambda call: True)
|
||||
def test_callback(call):
|
||||
logger.info(call)
|
||||
```
|
||||
|
||||
#### TeleBot
|
||||
```python
|
||||
import telebot
|
||||
@ -200,6 +228,11 @@ tb.polling(none_stop=False, interval=0, block=True)
|
||||
# getMe
|
||||
user = tb.get_me()
|
||||
|
||||
# setWebhook
|
||||
tb.set_webhook(url="http://example.com", certificate=open('mycert.pem'))
|
||||
# unset webhook
|
||||
tb.remove_webhook()
|
||||
|
||||
# getUpdates
|
||||
updates = tb.get_updates()
|
||||
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
|
||||
@ -251,6 +284,16 @@ tb.send_location(chat_id, lat, lon)
|
||||
# action_string can be one of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video',
|
||||
# 'record_audio', 'upload_audio', 'upload_document' or 'find_location'.
|
||||
tb.send_chat_action(chat_id, action_string)
|
||||
|
||||
# getFile
|
||||
# Downloading a file is straightforward
|
||||
# Returns a File object
|
||||
import requests
|
||||
file_info = tb.get_file(file_id)
|
||||
|
||||
file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN, file_info.file_path))
|
||||
|
||||
|
||||
```
|
||||
#### Reply markup
|
||||
All `send_xyz` functions of TeleBot take an optional `reply_markup` argument. This argument must be an instance of `ReplyKeyboardMarkup`, `ReplyKeyboardHide` or `ForceReply`, which are defined in types.py.
|
||||
@ -267,13 +310,21 @@ from telebot import types
|
||||
# row_width is used in combination with the add() function.
|
||||
# It defines how many buttons are fit on each row before continuing on the next row.
|
||||
markup = types.ReplyKeyboardMarkup(row_width=2)
|
||||
markup.add('a', 'v', 'd')
|
||||
itembtn1 = types.KeyboardButton('a')
|
||||
itembtn2 = types.KeyboardButton('v')
|
||||
itembtn3 = types.KeyboardButton('d')
|
||||
markup.add(itembtn1, itembtn2, itembtn3)
|
||||
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
|
||||
|
||||
# or add strings one row at a time:
|
||||
# or add KeyboardButton one row at a time:
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
markup.row('a', 'v')
|
||||
markup.row('c', 'd', 'e')
|
||||
itembtna = types.KeyboardButton('a')
|
||||
itembtnv = types.KeyboardButton('v')
|
||||
itembtnc = types.KeyboardButton('c')
|
||||
itembtnd = types.KeyboardButton('d')
|
||||
itembtne = types.KeyboardButton('e')
|
||||
markup.row(itembtna, itembtnv)
|
||||
markup.row(itembtnc, itembtnd, itembtne)
|
||||
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
|
||||
```
|
||||
The last example yields this result:
|
||||
@ -297,6 +348,64 @@ ForceReply:
|
||||
|
||||

|
||||
|
||||
### Inline Mode
|
||||
|
||||
More information about [Inline mode](https://core.telegram.org/bots/inline).
|
||||
|
||||
#### inline_handler
|
||||
|
||||
Now, you can use inline_handler to get inline_query in telebot.
|
||||
|
||||
```python
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'text')
|
||||
def query_text(inline_query):
|
||||
# Query message is text
|
||||
```
|
||||
|
||||
|
||||
#### chosen_inline_handler
|
||||
|
||||
Use chosen_inline_handler to get chosen_inline_result in telebot. Don't forgot add the /setinlinefeedback
|
||||
command for @Botfather.
|
||||
|
||||
More information : [collecting-feedback](https://core.telegram.org/bots/inline#collecting-feedback)
|
||||
|
||||
```python
|
||||
@bot.chosen_inline_handler(func=lambda chosen_inline_result: True)
|
||||
def test_chosen(chosen_inline_result):
|
||||
# Process all chosen_inline_result.
|
||||
```
|
||||
|
||||
#### answer_inline_query
|
||||
|
||||
```python
|
||||
@bot.inline_handler(lambda query: query.query == 'text')
|
||||
def query_text(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultArticle('1', 'Result', types.InputTextMessageContent('Result message.'))
|
||||
r2 = types.InlineQueryResultArticle('2', 'Result2', types.InputTextMessageContent('Result message2.'))
|
||||
bot.answer_inline_query(inline_query.id, [r, r2])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
```
|
||||
###Working with entities:
|
||||
This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.
|
||||
Attributes:
|
||||
* `type`
|
||||
* `url`
|
||||
* `offset`
|
||||
* `length`
|
||||
* `user`
|
||||
|
||||
|
||||
**Here's an Example:**`message.entities[num].<attribute>`<br>
|
||||
Here `num` is the entity number or order of entity in a reply, for if incase there are multiple entities in the reply/message.<br>
|
||||
`message.entities` returns a list of entities object. <br>
|
||||
`message.entities[0].type` would give the type of the first entity<br>
|
||||
Refer [Bot Api](https://core.telegram.org/bots/api#messageentity) for extra details
|
||||
|
||||
## Advanced use of the API
|
||||
|
||||
### Asynchronous delivery of messages
|
||||
@ -351,8 +460,10 @@ bot.set_update_listener(handle_messages)
|
||||
bot.polling()
|
||||
```
|
||||
|
||||
### Using web hooks
|
||||
If you prefer using web hooks to the getUpdates method, you can use the `process_new_messages(messages)` function in TeleBot to make it process the messages that you supply. It takes a list of Message objects. This function is still incubating.
|
||||
### Using webhooks
|
||||
When using webhooks telegram sends one Update per call, for processing it you should call process_new_messages([update.message]) when you recieve it.
|
||||
|
||||
There are some examples using webhooks in the *examples/webhook_examples* directory.
|
||||
|
||||
### Logging
|
||||
|
||||
@ -368,32 +479,57 @@ telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
|
||||
|
||||
## F.A.Q.
|
||||
|
||||
### Bot 2.0
|
||||
|
||||
April 9,2016 Telegram release new bot 2.0 API, which has a drastic revision especially for the change of method's interface.If you want to update to the latest version, please make sure you've switched bot's code to bot 2.0 method interface.
|
||||
|
||||
[More information about pyTelegramBotAPI support bot2.0](https://github.com/eternnoir/pyTelegramBotAPI/issues/130)
|
||||
|
||||
### How can I distinguish a User and a GroupChat in message.chat?
|
||||
There are two ways to do this:
|
||||
Telegram Bot API support new type Chat for message.chat.
|
||||
|
||||
- Checking the instance of message.chat with `isinstance`:
|
||||
- Check the ```type``` attribute in ```Chat``` object:
|
||||
-
|
||||
```python
|
||||
def is_user(chat):
|
||||
return isinstance(chat, types.User)
|
||||
if message.chat.type == “private”:
|
||||
# private chat message
|
||||
|
||||
print is_user(message.chat) # True or False
|
||||
```
|
||||
- Checking whether the chat id is negative or positive. If the chat id is negative, the chat is a GroupChat, if it is positive, it is a User. Example:
|
||||
```python
|
||||
def is_user(chat):
|
||||
return chat.id > 0
|
||||
if message.chat.type == “group”:
|
||||
# group chat message
|
||||
|
||||
if message.chat.type == “supergroup”:
|
||||
# supergroup chat message
|
||||
|
||||
if message.chat.type == “channel”:
|
||||
# channel message
|
||||
|
||||
print is_user(message.chat) # True or False
|
||||
```
|
||||
|
||||
## The Telegram Chat Group
|
||||
|
||||
Get help. Discuss. Chat.
|
||||
|
||||
Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/joinchat/067e22c60035523fda8f6025ee87e30b).
|
||||
* Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/joinchat/Bn4ixj84FIZVkwhk2jag6A)
|
||||
* We now have a Telegram Channel as well! Keep yourself up to date with API changes, and [join it](https://telegram.me/pytelegrambotapi).
|
||||
|
||||
## More examples
|
||||
|
||||
* [Echo Bot](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/echo_bot.py)
|
||||
* [Deep Linking](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/deep_linking.py)
|
||||
* [next_step_handler Example](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py)
|
||||
|
||||
## Bots using this API
|
||||
* [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes
|
||||
* [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger*
|
||||
* [Send to Kindle Bot](https://telegram.me/Send2KindleBot) by *GabrielRF* - Send to Kindle files or links to files.
|
||||
* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) ([source](https://github.com/GabrielRF/telegram-lmgtfy_bot)) by *GabrielRF* - Let me Google that for you.
|
||||
* [Telegram UrlProBot](https://github.com/GabrielRF/telegram-urlprobot) ([source](https://github.com/GabrielRF/telegram-urlprobot)) by *GabrielRF* - URL shortener and URL expander.
|
||||
* [Telegram Proxy Bot](https://bitbucket.org/master_groosha/telegram-proxy-bot) by *Groosha* - A simple BITM (bot-in-the-middle) for Telegram acting as some kind of "proxy".
|
||||
* [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte* - `Credits for the original version of this bot goes to` **Groosha** `, simply added certain features which I thought were needed`.
|
||||
* [RadRetroRobot](https://github.com/Tronikart/RadRetroRobot) by *Tronikart* - Multifunctional Telegram Bot RadRetroRobot.
|
||||
* [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie*
|
||||
* [NeoBot](https://github.com/neoranger/NeoBot) by *neoranger*
|
||||
* [TagAlertBot](https://github.com/pitasi/TagAlertBot) by *pitasi*
|
||||
* [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall.
|
||||
|
||||
Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh.
|
||||
|
397
README.rst
397
README.rst
@ -13,11 +13,7 @@ pyTelegramBotAPI
|
||||
A simple, but extensible Python implementation for the `Telegram Bot
|
||||
API <https://core.telegram.org/bots/api>`__.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<p align="center">
|
||||
|
||||
|Build Status|
|
||||
|Download Month| |Build Status| |Download Month|
|
||||
|
||||
- `Getting started. <#getting-started>`__
|
||||
- `Writing your first bot <#writing-your-first-bot>`__
|
||||
@ -31,8 +27,10 @@ API <https://core.telegram.org/bots/api>`__.
|
||||
- `Methods <#methods>`__
|
||||
- `General use of the API <#general-use-of-the-api>`__
|
||||
- `Message handlers <#message-handlers>`__
|
||||
- `Callback Query handlers <#message-handlers>`__
|
||||
- `TeleBot <#telebot>`__
|
||||
- `Reply markup <#reply-markup>`__
|
||||
- `Inline Mode <#inline-mode>`__
|
||||
|
||||
- `Advanced use of the API <#advanced-use-of-the-api>`__
|
||||
|
||||
@ -47,14 +45,16 @@ API <https://core.telegram.org/bots/api>`__.
|
||||
|
||||
- `F.A.Q. <#faq>`__
|
||||
|
||||
- `Bot 2.0 <#bot-20>`__
|
||||
- `How can I distinguish a User and a GroupChat in
|
||||
message.chat? <#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat>`__
|
||||
|
||||
- `The Telegram Chat Group <#the-telegram-chat-group>`__
|
||||
- `More examples <#more-examples>`__
|
||||
- `Bots using this API <#bots-using-this-api>`__
|
||||
|
||||
Getting started.
|
||||
================
|
||||
----------------
|
||||
|
||||
This API is tested with Python 2.6, Python 2.7, Python 3.4, Pypy and
|
||||
Pypy 3. There are two ways to install the library:
|
||||
@ -80,10 +80,10 @@ it has regular updates, do not forget to update it regularly by calling
|
||||
``pip install pytelegrambotapi --upgrade``\ \*
|
||||
|
||||
Writing your first bot
|
||||
======================
|
||||
----------------------
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
It is presumed that you [have obtained an API token with
|
||||
@BotFather](https://core.telegram.org/bots#botfather). We will call this
|
||||
@ -92,7 +92,7 @@ programming language and more importantly `the Telegram Bot
|
||||
API <https://core.telegram.org/bots/api>`__.
|
||||
|
||||
A simple echo bot
|
||||
-----------------
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The TeleBot class (defined in \_\_init\_\_.py) encapsulates all API
|
||||
calls in a single class. It provides functions such as ``send_xyz``
|
||||
@ -174,10 +174,10 @@ To start the bot, simply open up a terminal and enter
|
||||
('/start' and '/help') and arbitrary text messages.
|
||||
|
||||
General API Documentation
|
||||
=========================
|
||||
-------------------------
|
||||
|
||||
Types
|
||||
-----
|
||||
~~~~~
|
||||
|
||||
All types are defined in types.py. They are all completely in line with
|
||||
the `Telegram API's definition of the
|
||||
@ -189,15 +189,15 @@ Note that ``message.chat`` can be either an instance of ``User`` or
|
||||
``GroupChat`` (see `How can I distinguish a User and a GroupChat in
|
||||
message.chat? <#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat>`__).
|
||||
|
||||
The Message object also has a ``content_type``\ attribute, which defines
|
||||
the type of the Message. ``content_type`` can be one of the following
|
||||
strings: 'text', 'audio', 'document', 'photo', 'sticker', 'video',
|
||||
'location', 'contact', 'new\_chat\_participant',
|
||||
The Message object also has a ``content_types``\ attribute, which
|
||||
defines the type of the Message. ``content_types`` can be one of the
|
||||
following strings: 'text', 'audio', 'document', 'photo', 'sticker',
|
||||
'video', 'voice', 'location', 'contact', 'new\_chat\_participant',
|
||||
'left\_chat\_participant', 'new\_chat\_title', 'new\_chat\_photo',
|
||||
'delete\_chat\_photo', 'group\_chat\_created'.
|
||||
|
||||
Methods
|
||||
-------
|
||||
~~~~~~~
|
||||
|
||||
All `API
|
||||
methods <https://core.telegram.org/bots/api#available-methods>`__ are
|
||||
@ -206,16 +206,151 @@ naming conventions. E.g. ``getMe`` is renamed to ``get_me`` and
|
||||
``sendMessage`` to ``send_message``.
|
||||
|
||||
General use of the API
|
||||
----------------------
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Outlined below are some general use cases of the API.
|
||||
|
||||
Message handlers
|
||||
~~~~~~~~~~~~~~~~
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
A message handler is a function which is decorated with the
|
||||
``message_handler`` decorator of a TeleBot instance. The following
|
||||
examples illustrate the possibilities of message handlers:
|
||||
A message handler is a function that is decorated with the
|
||||
``message_handler`` decorator of a TeleBot instance. Message handlers
|
||||
consist of one or multiple filters. Each filter much return True for a
|
||||
certain message in order for a message handler to become eligible to
|
||||
handle that message. A message handler is declared in the following way
|
||||
(provided ``bot`` is an instance of TeleBot):
|
||||
|
||||
.. code:: python
|
||||
|
||||
@bot.message_handler(filters)
|
||||
def function_name(message):
|
||||
bot.reply_to(message, "This is a message handler")
|
||||
|
||||
``function_name`` is not bound to any restrictions. Any function name is
|
||||
permitted with message handlers. The function must accept at most one
|
||||
argument, which will be the message that the function must handle.
|
||||
``filters`` is a list of keyword arguments. A filter is declared in the
|
||||
following manner: ``name=argument``. One handler may have multiple
|
||||
filters. TeleBot supports the following filters:
|
||||
|
||||
+--------+------+------+
|
||||
| name | argu | Cond |
|
||||
| | ment | itio |
|
||||
| | (s) | n |
|
||||
+========+======+======+
|
||||
| conten | list | ``Tr |
|
||||
| t\_typ | of | ue`` |
|
||||
| es | stri | if |
|
||||
| | ngs | mess |
|
||||
| | (def | age. |
|
||||
| | ault | cont |
|
||||
| | ``[' | ent\ |
|
||||
| | text | _typ |
|
||||
| | ']`` | e |
|
||||
| | ) | is |
|
||||
| | | in |
|
||||
| | | the |
|
||||
| | | list |
|
||||
| | | of |
|
||||
| | | stri |
|
||||
| | | ngs. |
|
||||
+--------+------+------+
|
||||
| regexp | a | ``Tr |
|
||||
| | regu | ue`` |
|
||||
| | lar | if |
|
||||
| | expr | ``re |
|
||||
| | essi | .sea |
|
||||
| | on | rch( |
|
||||
| | as a | rege |
|
||||
| | stri | xp_a |
|
||||
| | ng | rg)` |
|
||||
| | | ` |
|
||||
| | | retu |
|
||||
| | | rns |
|
||||
| | | ``Tr |
|
||||
| | | ue`` |
|
||||
| | | and |
|
||||
| | | ``me |
|
||||
| | | ssag |
|
||||
| | | e.co |
|
||||
| | | nten |
|
||||
| | | t_ty |
|
||||
| | | pe = |
|
||||
| | | = 't |
|
||||
| | | ext' |
|
||||
| | | `` |
|
||||
| | | (See |
|
||||
| | | `Pyt |
|
||||
| | | hon |
|
||||
| | | Regu |
|
||||
| | | lar |
|
||||
| | | Expr |
|
||||
| | | essi |
|
||||
| | | ons |
|
||||
| | | <htt |
|
||||
| | | ps:/ |
|
||||
| | | /doc |
|
||||
| | | s.py |
|
||||
| | | thon |
|
||||
| | | .org |
|
||||
| | | /2/l |
|
||||
| | | ibra |
|
||||
| | | ry/r |
|
||||
| | | e.ht |
|
||||
| | | ml>` |
|
||||
| | | __ |
|
||||
+--------+------+------+
|
||||
| comman | list | ``Tr |
|
||||
| ds | of | ue`` |
|
||||
| | stri | if |
|
||||
| | ngs | ``me |
|
||||
| | | ssag |
|
||||
| | | e.co |
|
||||
| | | nten |
|
||||
| | | t_ty |
|
||||
| | | pe = |
|
||||
| | | = 't |
|
||||
| | | ext' |
|
||||
| | | `` |
|
||||
| | | and |
|
||||
| | | ``me |
|
||||
| | | ssag |
|
||||
| | | e.te |
|
||||
| | | xt`` |
|
||||
| | | star |
|
||||
| | | ts |
|
||||
| | | with |
|
||||
| | | a |
|
||||
| | | comm |
|
||||
| | | and |
|
||||
| | | that |
|
||||
| | | is |
|
||||
| | | in |
|
||||
| | | the |
|
||||
| | | list |
|
||||
| | | of |
|
||||
| | | stri |
|
||||
| | | ngs. |
|
||||
+--------+------+------+
|
||||
| func | a | ``Tr |
|
||||
| | func | ue`` |
|
||||
| | tion | if |
|
||||
| | (lam | the |
|
||||
| | bda | lamb |
|
||||
| | or | da |
|
||||
| | func | or |
|
||||
| | tion | func |
|
||||
| | refe | tion |
|
||||
| | renc | refe |
|
||||
| | e) | renc |
|
||||
| | | e |
|
||||
| | | retu |
|
||||
| | | rns |
|
||||
| | | ``Tr |
|
||||
| | | ue`` |
|
||||
+--------+------+------+
|
||||
|
||||
Here are some examples of using the filters and message handlers:
|
||||
|
||||
.. code:: python
|
||||
|
||||
@ -250,8 +385,30 @@ examples illustrate the possibilities of message handlers:
|
||||
def handle_text_doc(message)
|
||||
pass
|
||||
|
||||
*Note: all handlers are tested in the order in which they were declared*
|
||||
#### TeleBot
|
||||
# Handlers can be stacked to create a function which will be called if either message_handler is eligible
|
||||
# This handler will be called if the message starts with '/hello' OR is some emoji
|
||||
@bot.message_handler(commands=['hello'])
|
||||
@bot.message_handler(func=lambda msg: msg.text.encode("utf-8") == SOME_FANCY_EMOJI)
|
||||
def send_something(message):
|
||||
pass
|
||||
|
||||
**Important: all handlers are tested in the order in which they were
|
||||
declared**
|
||||
|
||||
Callback Query Handler
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In bot2.0 update. You can get ``callback_query`` in update object. In
|
||||
telebot use ``callback_query_handler`` to process callback\_querys.
|
||||
|
||||
.. code:: python
|
||||
|
||||
@bot.callback_query_handler(func=lambda call: True)
|
||||
def test_callback(call):
|
||||
logger.info(call)
|
||||
|
||||
TeleBot
|
||||
^^^^^^^
|
||||
|
||||
.. code:: python
|
||||
|
||||
@ -270,6 +427,11 @@ examples illustrate the possibilities of message handlers:
|
||||
# getMe
|
||||
user = tb.get_me()
|
||||
|
||||
# setWebhook
|
||||
tb.set_webhook(url="http://example.com", certificate=open('mycert.pem'))
|
||||
# unset webhook
|
||||
tb.remove_webhook()
|
||||
|
||||
# getUpdates
|
||||
updates = tb.get_updates()
|
||||
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
|
||||
@ -322,8 +484,16 @@ examples illustrate the possibilities of message handlers:
|
||||
# 'record_audio', 'upload_audio', 'upload_document' or 'find_location'.
|
||||
tb.send_chat_action(chat_id, action_string)
|
||||
|
||||
# getFile
|
||||
# Downloading a file is straightforward
|
||||
# Returns a File object
|
||||
import requests
|
||||
file_info = tb.get_file(file_id)
|
||||
|
||||
file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN, file_info.file_path))
|
||||
|
||||
Reply markup
|
||||
~~~~~~~~~~~~
|
||||
^^^^^^^^^^^^
|
||||
|
||||
All ``send_xyz`` functions of TeleBot take an optional ``reply_markup``
|
||||
argument. This argument must be an instance of ``ReplyKeyboardMarkup``,
|
||||
@ -342,13 +512,21 @@ argument. This argument must be an instance of ``ReplyKeyboardMarkup``,
|
||||
# row_width is used in combination with the add() function.
|
||||
# It defines how many buttons are fit on each row before continuing on the next row.
|
||||
markup = types.ReplyKeyboardMarkup(row_width=2)
|
||||
markup.add('a', 'v', 'd')
|
||||
itembtn1 = types.KeyboardButton('a')
|
||||
itembtn2 = types.KeyboardButton('v')
|
||||
itembtn3 = types.KeyboardButton('d')
|
||||
markup.add(itembtn1, itembtn2, itembtn3)
|
||||
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
|
||||
|
||||
# or add strings one row at a time:
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
markup.row('a', 'v')
|
||||
markup.row('c', 'd', 'e')
|
||||
itembtna = types.KeyboardButton('a')
|
||||
itembtnv = types.KeyboardButton('v')
|
||||
itembtnc = types.KeyboardButton('c')
|
||||
itembtnd = types.KeyboardButton('d')
|
||||
itembtne = types.KeyboardButton('e')
|
||||
markup.row(itembtna, itembtnv)
|
||||
markup.row(itembtnc, itembtnd, itembtne)
|
||||
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
|
||||
|
||||
The last example yields this result:
|
||||
@ -379,11 +557,58 @@ ForceReply:
|
||||
|
||||
ForceReply
|
||||
|
||||
Inline Mode
|
||||
~~~~~~~~~~~
|
||||
|
||||
More information about `Inline
|
||||
mode <https://core.telegram.org/bots/inline>`__.
|
||||
|
||||
inline\_handler
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Now, you can use inline\_handler to get inline\_query in telebot.
|
||||
|
||||
.. code:: python
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'text')
|
||||
def query_text(inline_query):
|
||||
# Query message is text
|
||||
|
||||
chosen\_inline\_handler
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Use chosen\_inline\_handler to get chosen\_inline\_result in telebot.
|
||||
Don't forgot add the /setinlinefeedback command for @Botfather.
|
||||
|
||||
More information :
|
||||
`collecting-feedback <https://core.telegram.org/bots/inline#collecting-feedback>`__
|
||||
|
||||
.. code:: python
|
||||
|
||||
@bot.chosen_inline_handler(func=lambda chosen_inline_result: True)
|
||||
def test_chosen(chosen_inline_result):
|
||||
# Process all chosen_inline_result.
|
||||
|
||||
answer\_inline\_query
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code:: python
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'text')
|
||||
def query_text(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultArticle('1', 'Result', 'Result message.')
|
||||
r2 = types.InlineQueryResultArticle('2', 'Result2', 'Result message2.')
|
||||
bot.answer_inline_query(inline_query.id, [r, r2])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
Advanced use of the API
|
||||
=======================
|
||||
-----------------------
|
||||
|
||||
Asynchronous delivery of messages
|
||||
---------------------------------
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There exists an implementation of TeleBot which executes all
|
||||
``send_xyz`` and the ``get_me`` functions asynchronously. This can speed
|
||||
@ -418,7 +643,7 @@ calling wait(), the order in which messages are delivered might be
|
||||
wrong.*
|
||||
|
||||
Sending large text messages
|
||||
---------------------------
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sometimes you must send messages that exceed 5000 characters. The
|
||||
Telegram API can not handle that many characters in one request, so we
|
||||
@ -437,7 +662,7 @@ API:
|
||||
tb.send_message(chat_id, text)
|
||||
|
||||
Controlling the amount of Threads used by TeleBot
|
||||
-------------------------------------------------
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The TeleBot constructor takes the following optional arguments:
|
||||
|
||||
@ -450,7 +675,7 @@ The TeleBot constructor takes the following optional arguments:
|
||||
False.
|
||||
|
||||
The listener mechanism
|
||||
----------------------
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As an alternative to the message handlers, one can also register a
|
||||
function as a listener to TeleBot. Example:
|
||||
@ -465,57 +690,75 @@ function as a listener to TeleBot. Example:
|
||||
bot.set_update_listener(handle_messages)
|
||||
bot.polling()
|
||||
|
||||
Using web hooks
|
||||
---------------
|
||||
Using webhooks
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
If you prefer using web hooks to the getUpdates method, you can use the
|
||||
``process_new_messages(messages)`` function in TeleBot to make it
|
||||
process the messages that you supply. It takes a list of Message
|
||||
objects. This function is still incubating.
|
||||
When using webhooks telegram sends one Update per call, for processing
|
||||
it you should call process\_new\_messages([update.message]) when you
|
||||
recieve it.
|
||||
|
||||
There are some examples using webhooks in the
|
||||
*examples/webhook\_examples* directory.
|
||||
|
||||
Logging
|
||||
-------
|
||||
~~~~~~~
|
||||
|
||||
You can use the Telebot module logger to log debug info about Telebot.
|
||||
Use ``telebot.logger`` to get the logger of the TeleBot module.
|
||||
Use ``telebot.logger`` to get the logger of the TeleBot module. It is
|
||||
possible to add custom logging Handlers to the logger. Refer to the
|
||||
`Python logging module
|
||||
page <https://docs.python.org/2/library/logging.html>`__ for more info.
|
||||
|
||||
.. code:: python
|
||||
|
||||
import logging
|
||||
|
||||
logger = telebot.logger
|
||||
formatter = logging.Formatter('[%(asctime)s] %(thread)d {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',
|
||||
'%m-%d %H:%M:%S')
|
||||
ch = logging.StreamHandler(sys.stdout)
|
||||
logger.addHandler(ch)
|
||||
logger.setLevel(logging.DEBUG) # or use logging.INFO
|
||||
ch.setFormatter(formatter)
|
||||
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
|
||||
|
||||
F.A.Q.
|
||||
======
|
||||
------
|
||||
|
||||
Bot 2.0
|
||||
~~~~~~~
|
||||
|
||||
April 9,2016 Telegram release new bot 2.0 API, which has a drastic
|
||||
revision especially for the change of method's interface.If you want to
|
||||
update to the latest version, please make sure you've switched bot's
|
||||
code to bot 2.0 method interface.
|
||||
|
||||
`More information about pyTelegramBotAPI support
|
||||
bot2.0 <https://github.com/eternnoir/pyTelegramBotAPI/issues/130>`__
|
||||
|
||||
How can I distinguish a User and a GroupChat in message.chat?
|
||||
-------------------------------------------------------------
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are two ways to do this:
|
||||
Telegram Bot API support new type Chat for message.chat.
|
||||
|
||||
- Checking the instance of message.chat with ``isinstance``:
|
||||
\`\`\`python def is\_user(chat): return isinstance(chat, types.User)
|
||||
- Check the ``type`` attribute in ``Chat`` object:
|
||||
- \`\`\`python if message.chat.type == “private”: # private chat
|
||||
message
|
||||
|
||||
print is\_user(message.chat) # True or False
|
||||
``- Checking whether the chat id is negative or positive. If the chat id is negative, the chat is a GroupChat, if it is positive, it is a User. Example:``\ python
|
||||
def is\_user(chat): return chat.id > 0
|
||||
if message.chat.type == “group”: # group chat message
|
||||
|
||||
print is\_user(message.chat) # True or False \`\`\`
|
||||
if message.chat.type == “supergroup”: # supergroup chat message
|
||||
|
||||
if message.chat.type == “channel”: # channel message
|
||||
|
||||
\`\`\`
|
||||
|
||||
The Telegram Chat Group
|
||||
=======================
|
||||
-----------------------
|
||||
|
||||
Get help. Discuss. Chat.
|
||||
|
||||
Join the `pyTelegramBotAPI Telegram Chat
|
||||
Group <https://telegram.me/joinchat/067e22c60035523fda8f6025ee87e30b>`__.
|
||||
- Join the pyTelegramBotAPI Telegram Chat Group
|
||||
- Messge to @eternnoir by telegram for Invitation.
|
||||
- We now have a Telegram Channel as well! Keep yourself up to date with
|
||||
API changes, and `join it <https://telegram.me/pytelegrambotapi>`__.
|
||||
|
||||
More examples
|
||||
=============
|
||||
-------------
|
||||
|
||||
- `Echo
|
||||
Bot <https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/echo_bot.py>`__
|
||||
@ -524,5 +767,41 @@ More examples
|
||||
- `next\_step\_handler
|
||||
Example <https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py>`__
|
||||
|
||||
Bots using this API
|
||||
-------------------
|
||||
|
||||
- `SiteAlert bot <https://telegram.me/SiteAlert_bot>`__
|
||||
(`source <https://github.com/ilteoood/SiteAlert-Python>`__) by
|
||||
*ilteoood* - Monitors websites and sends a notification on changes
|
||||
- `TelegramLoggingBot <https://github.com/aRandomStranger/TelegramLoggingBot>`__
|
||||
by *aRandomStranger*
|
||||
- `Telegram
|
||||
LMGTFY\_bot <https://github.com/GabrielRF/telegram-lmgtfy_bot>`__ by
|
||||
*GabrielRF*
|
||||
- `Telegram
|
||||
UrlProBot <https://github.com/GabrielRF/telegram-urlprobot>`__ by
|
||||
*GabrielRF*
|
||||
- `Telegram Proxy
|
||||
Bot <https://bitbucket.org/master_groosha/telegram-proxy-bot>`__ by
|
||||
*Groosha* - A simple BITM (bot-in-the-middle) for Telegram acting as
|
||||
some kind of "proxy".
|
||||
- `Telegram Proxy Bot <https://github.com/mrgigabyte/proxybot>`__ by
|
||||
*mrgigabyte* -
|
||||
``Credits for the original version of this bot goes to`` **Groosha**
|
||||
``, simply added certain features which I thought were needed``.
|
||||
- `RadRetroRobot <https://github.com/Tronikart/RadRetroRobot>`__ by
|
||||
*Tronikart* - Multifunctional Telegram Bot RadRetroRobot.
|
||||
- `League of Legends bot <https://telegram.me/League_of_Legends_bot>`__
|
||||
(`source <https://github.com/i32ropie/lol>`__) by *i32ropie*
|
||||
- `NeoBot <https://github.com/neoranger/NeoBot>`__ by *neoranger*
|
||||
- `TagAlertBot <https://github.com/pitasi/TagAlertBot>`__ by *pitasi*
|
||||
|
||||
Want to have your bot listed here? Send a Telegram message to @eternnoir
|
||||
or @pevdh.
|
||||
|
||||
.. |Download Month| image:: https://img.shields.io/pypi/v/pyTelegramBotAPI.svg
|
||||
:target: https://pypi.python.org/pypi/pyTelegramBotAPI
|
||||
.. |Build Status| image:: https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master
|
||||
:target: https://travis-ci.org/eternnoir/pyTelegramBotAPI
|
||||
.. |Download Month| image:: https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg
|
||||
:target: https://pypi.python.org/pypi/pyTelegramBotAPI
|
||||
|
15
examples/download_file_example.py
Normal file
15
examples/download_file_example.py
Normal file
@ -0,0 +1,15 @@
|
||||
import telebot
|
||||
|
||||
TOKEN = 'YOUR BOT TOKEN'
|
||||
CHAT_ID = 'YOUR CHAT ID'
|
||||
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
|
||||
ret_msg = bot.send_voice(CHAT_ID, open('tests/test_data/record.ogg'))
|
||||
|
||||
file_info = bot.get_file(ret_msg.voice.file_id)
|
||||
|
||||
downloaded_file = bot.download_file(file_info.file_path)
|
||||
|
||||
with open('new_file.ogg', 'wb') as new_file:
|
||||
new_file.write(downloaded_file)
|
73
examples/inline_example.py
Normal file
73
examples/inline_example.py
Normal file
@ -0,0 +1,73 @@
|
||||
# This example show how to write an inline mode telegramt bot use pyTelegramBotAPI.
|
||||
import telebot
|
||||
import time
|
||||
import sys
|
||||
import logging
|
||||
from telebot import types
|
||||
|
||||
API_TOKEN = '<TOKEN>'
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
telebot.logger.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'text')
|
||||
def query_text(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultArticle('1', 'Result1', types.InputTextMessageContent('hi'))
|
||||
r2 = types.InlineQueryResultArticle('2', 'Result2', types.InputTextMessageContent('hi'))
|
||||
bot.answer_inline_query(inline_query.id, [r, r2])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'photo1')
|
||||
def query_photo(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultPhoto('1',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/kitten.jpg',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/kitten.jpg',
|
||||
input_message_content=types.InputTextMessageContent('hi'))
|
||||
r2 = types.InlineQueryResultPhoto('2',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg')
|
||||
bot.answer_inline_query(inline_query.id, [r, r2], cache_time=1)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'video')
|
||||
def query_video(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultVideo('1',
|
||||
'https://github.com/eternnoir/pyTelegramBotAPI/blob/master/tests/test_data/test_video.mp4?raw=true',
|
||||
'video/mp4', 'Video',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg',
|
||||
'Title'
|
||||
)
|
||||
bot.answer_inline_query(inline_query.id, [r])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: len(query.query) is 0)
|
||||
def default_query(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultArticle('1', 'default', 'default')
|
||||
bot.answer_inline_query(inline_query.id, [r])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
def main_loop():
|
||||
bot.polling(True)
|
||||
while 1:
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main_loop()
|
||||
except KeyboardInterrupt:
|
||||
print >> sys.stderr, '\nExiting by user request.\n'
|
||||
sys.exit(0)
|
36
examples/webhook_examples/README.md
Normal file
36
examples/webhook_examples/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
# Webhook examples using pyTelegramBotAPI
|
||||
|
||||
There are 3 examples in this directory using different libraries:
|
||||
|
||||
* **Python (CPython):** *webhook_cpython_echo_bot.py*
|
||||
* **Pros:**
|
||||
* Official python libraries, it works out of the box (doesn't require to
|
||||
install anything).
|
||||
* Works with Python 2 and Python 3 (need to be converted with 2to3).
|
||||
* **Cons:**
|
||||
* Ugly code.
|
||||
* Many things to handle yourself, this can lead to errors.
|
||||
* Not powerful, do the trick but the performance is low.
|
||||
|
||||
* **CherryPy (3.8.0):** *webhook_cherrypy_echo_bot.py*
|
||||
* **Pros:**
|
||||
* It's a web application framework, cleaner code, uses objects for defining
|
||||
the web application.
|
||||
* Very good performance.
|
||||
* The project seems to be active, latest version is recent.
|
||||
* Works with Python 2 and Python 3.
|
||||
* **Cons:**
|
||||
* Some things are not very intuitive, reading the doc is a must.
|
||||
|
||||
* **Flask (0.10.1):** *webhook_flask_echo_bot.py*
|
||||
* **Pros:**
|
||||
* It's a web application framework, cleaner code, uses decorator which can
|
||||
be nice.
|
||||
* Good performance.
|
||||
* It's intuitive if you know how web application works.
|
||||
* **Cons:**
|
||||
* The project seems not to be very active, latest version dates 2013.
|
||||
* They don't recommend to use it with Python 3, but may work.
|
||||
* May be a oversized for just handling webhook petitions.
|
||||
|
||||
*Latest update of this document: 2015-10-06*
|
85
examples/webhook_examples/webhook_cherrypy_echo_bot.py
Normal file
85
examples/webhook_examples/webhook_cherrypy_echo_bot.py
Normal file
@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This is a simple echo bot using decorators and webhook with CherryPy
|
||||
# It echoes any incoming text messages and does not use the polling method.
|
||||
|
||||
import cherrypy
|
||||
import telebot
|
||||
import logging
|
||||
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
WEBHOOK_HOST = '<ip/host where the bot is running>'
|
||||
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
|
||||
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
|
||||
|
||||
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
|
||||
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
|
||||
|
||||
# Quick'n'dirty SSL certificate generation:
|
||||
#
|
||||
# openssl genrsa -out webhook_pkey.pem 2048
|
||||
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
|
||||
#
|
||||
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
|
||||
# with the same value in you put in WEBHOOK_HOST
|
||||
|
||||
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
|
||||
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
|
||||
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.INFO)
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
|
||||
# WebhookServer, process webhook calls
|
||||
class WebhookServer(object):
|
||||
@cherrypy.expose
|
||||
def index(self):
|
||||
if 'content-length' in cherrypy.request.headers and \
|
||||
'content-type' in cherrypy.request.headers and \
|
||||
cherrypy.request.headers['content-type'] == 'application/json':
|
||||
length = int(cherrypy.request.headers['content-length'])
|
||||
json_string = cherrypy.request.body.read(length).decode("utf-8")
|
||||
update = telebot.types.Update.de_json(json_string)
|
||||
bot.process_new_updates([update])
|
||||
return ''
|
||||
else:
|
||||
raise cherrypy.HTTPError(403)
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message,
|
||||
("Hi there, I am EchoBot.\n"
|
||||
"I am here to echo your kind words back to you."))
|
||||
|
||||
|
||||
# Handle all other messages
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
# Remove webhook, it fails sometimes the set if there is a previous webhook
|
||||
bot.remove_webhook()
|
||||
|
||||
# Set webhook
|
||||
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
|
||||
certificate=open(WEBHOOK_SSL_CERT, 'r'))
|
||||
|
||||
# Start cherrypy server
|
||||
cherrypy.config.update({
|
||||
'server.socket_host': WEBHOOK_LISTEN,
|
||||
'server.socket_port': WEBHOOK_PORT,
|
||||
'server.ssl_module': 'builtin',
|
||||
'server.ssl_certificate': WEBHOOK_SSL_CERT,
|
||||
'server.ssl_private_key': WEBHOOK_SSL_PRIV
|
||||
})
|
||||
|
||||
cherrypy.quickstart(WebhookServer(), WEBHOOK_URL_PATH, {'/': {}})
|
99
examples/webhook_examples/webhook_cpython_echo_bot.py
Normal file
99
examples/webhook_examples/webhook_cpython_echo_bot.py
Normal file
@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This is a simple echo bot using decorators and webhook with BaseHTTPServer
|
||||
# It echoes any incoming text messages and does not use the polling method.
|
||||
|
||||
import BaseHTTPServer
|
||||
import ssl
|
||||
import telebot
|
||||
import logging
|
||||
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
WEBHOOK_HOST = '<ip/host where the bot is running>'
|
||||
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
|
||||
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
|
||||
|
||||
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
|
||||
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
|
||||
|
||||
# Quick'n'dirty SSL certificate generation:
|
||||
#
|
||||
# openssl genrsa -out webhook_pkey.pem 2048
|
||||
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
|
||||
#
|
||||
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
|
||||
# with the same value in you put in WEBHOOK_HOST
|
||||
|
||||
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
|
||||
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
|
||||
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.INFO)
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
|
||||
# WebhookHandler, process webhook calls
|
||||
class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
server_version = "WebhookHandler/1.0"
|
||||
|
||||
def do_HEAD(self):
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
def do_GET(self):
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
def do_POST(self):
|
||||
if self.path == WEBHOOK_URL_PATH and \
|
||||
'content-type' in self.headers and \
|
||||
'content-length' in self.headers and \
|
||||
self.headers['content-type'] == 'application/json':
|
||||
json_string = self.rfile.read(int(self.headers['content-length']))
|
||||
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
update = telebot.types.Update.de_json(json_string)
|
||||
bot.process_new_messages([update.message])
|
||||
else:
|
||||
self.send_error(403)
|
||||
self.end_headers()
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message,
|
||||
("Hi there, I am EchoBot.\n"
|
||||
"I am here to echo your kind words back to you."))
|
||||
|
||||
|
||||
# Handle all other messages
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
# Remove webhook, it fails sometimes the set if there is a previous webhook
|
||||
bot.remove_webhook()
|
||||
|
||||
# Set webhook
|
||||
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
|
||||
certificate=open(WEBHOOK_SSL_CERT, 'r'))
|
||||
|
||||
# Start server
|
||||
httpd = BaseHTTPServer.HTTPServer((WEBHOOK_LISTEN, WEBHOOK_PORT),
|
||||
WebhookHandler)
|
||||
|
||||
httpd.socket = ssl.wrap_socket(httpd.socket,
|
||||
certfile=WEBHOOK_SSL_CERT,
|
||||
keyfile=WEBHOOK_SSL_PRIV,
|
||||
server_side=True)
|
||||
|
||||
httpd.serve_forever()
|
84
examples/webhook_examples/webhook_flask_echo_bot.py
Normal file
84
examples/webhook_examples/webhook_flask_echo_bot.py
Normal file
@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This is a simple echo bot using decorators and webhook with flask
|
||||
# It echoes any incoming text messages and does not use the polling method.
|
||||
|
||||
import flask
|
||||
import telebot
|
||||
import logging
|
||||
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
WEBHOOK_HOST = '<ip/host where the bot is running>'
|
||||
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
|
||||
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
|
||||
|
||||
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
|
||||
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
|
||||
|
||||
# Quick'n'dirty SSL certificate generation:
|
||||
#
|
||||
# openssl genrsa -out webhook_pkey.pem 2048
|
||||
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
|
||||
#
|
||||
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
|
||||
# with the same value in you put in WEBHOOK_HOST
|
||||
|
||||
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
|
||||
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
|
||||
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.INFO)
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
|
||||
# Empty webserver index, return nothing, just http 200
|
||||
@app.route('/', methods=['GET', 'HEAD'])
|
||||
def index():
|
||||
return ''
|
||||
|
||||
|
||||
# Process webhook calls
|
||||
@app.route(WEBHOOK_URL_PATH, methods=['POST'])
|
||||
def webhook():
|
||||
if flask.request.headers.get('content-type') == 'application/json':
|
||||
json_string = flask.request.get_data().encode('utf-8')
|
||||
update = telebot.types.Update.de_json(json_string)
|
||||
bot.process_new_messages([update.message])
|
||||
return ''
|
||||
else:
|
||||
flask.abort(403)
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message,
|
||||
("Hi there, I am EchoBot.\n"
|
||||
"I am here to echo your kind words back to you."))
|
||||
|
||||
|
||||
# Handle all other messages
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
# Remove webhook, it fails sometimes the set if there is a previous webhook
|
||||
bot.remove_webhook()
|
||||
|
||||
# Set webhook
|
||||
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
|
||||
certificate=open(WEBHOOK_SSL_CERT, 'r'))
|
||||
|
||||
# Start flask server
|
||||
app.run(host=WEBHOOK_LISTEN,
|
||||
port=WEBHOOK_PORT,
|
||||
ssl_context=(WEBHOOK_SSL_CERT, WEBHOOK_SSL_PRIV),
|
||||
debug=True)
|
30
examples/webhook_examples/webhook_flask_heroku_echo.py
Normal file
30
examples/webhook_examples/webhook_flask_heroku_echo.py
Normal file
@ -0,0 +1,30 @@
|
||||
import telebot
|
||||
import os
|
||||
from flask import Flask, request
|
||||
|
||||
bot = telebot.TeleBot('<api_token>')
|
||||
|
||||
server = Flask(__name__)
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
bot.reply_to(message, 'Hello, ' + message.from_user.first_name)
|
||||
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
@server.route("/bot", methods=['POST'])
|
||||
def getMessage():
|
||||
bot.process_new_messages(
|
||||
[telebot.types.Update.de_json(request.stream.read().decode("utf-8")).message])
|
||||
return "!", 200
|
||||
|
||||
@server.route("/")
|
||||
def webhook():
|
||||
bot.remove_webhook()
|
||||
bot.set_webhook(url="https://herokuProject_url/bot")
|
||||
return "!", 200
|
||||
|
||||
server.run(host="0.0.0.0", port=os.environ.get('PORT', 5000))
|
||||
server = Flask(__name__)
|
7
setup.py
7
setup.py
@ -1,12 +1,13 @@
|
||||
#!/usr/bin/env python
|
||||
from setuptools import setup
|
||||
from io import open
|
||||
|
||||
def readme():
|
||||
with open('README.rst') as f:
|
||||
with open('README.rst', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
|
||||
setup(name='pyTelegramBotAPI',
|
||||
version='0.3.4',
|
||||
version='2.1.3',
|
||||
description='Python Telegram bot api. ',
|
||||
long_description=readme(),
|
||||
author='eternnoir',
|
||||
@ -15,7 +16,7 @@ setup(name='pyTelegramBotAPI',
|
||||
packages=['telebot'],
|
||||
license='GPL2',
|
||||
keywords='telegram bot api tools',
|
||||
install_requires=['pytest', 'requests', 'six'],
|
||||
install_requires=['requests', 'six'],
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Programming Language :: Python :: 2',
|
||||
|
@ -8,8 +8,11 @@ import sys
|
||||
import six
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('TeleBot')
|
||||
formatter = logging.Formatter('%(asctime)s (%(filename)s:%(lineno)d) %(levelname)s - %(name)s: "%(message)s"')
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"'
|
||||
)
|
||||
|
||||
console_output_handler = logging.StreamHandler(sys.stderr)
|
||||
console_output_handler.setFormatter(formatter)
|
||||
@ -23,8 +26,6 @@ from telebot import apihelper, types, util
|
||||
Module : telebot
|
||||
"""
|
||||
|
||||
API_URL = r"https://api.telegram.org/"
|
||||
|
||||
|
||||
class TeleBot:
|
||||
""" This is TeleBot Class
|
||||
@ -43,30 +44,42 @@ class TeleBot:
|
||||
getUpdates
|
||||
"""
|
||||
|
||||
def __init__(self, token, create_threads=True, num_threads=4):
|
||||
def __init__(self, token, threaded=True, skip_pending=False):
|
||||
"""
|
||||
:param token: bot API token
|
||||
:param create_threads: Create thread for message handler
|
||||
:param num_threads: Number of worker in thread pool.
|
||||
:return: Telebot object.
|
||||
"""
|
||||
self.token = token
|
||||
self.update_listener = []
|
||||
self.polling_thread = None
|
||||
self.skip_pending = skip_pending
|
||||
|
||||
self.__stop_polling = threading.Event()
|
||||
self.last_update_id = 0
|
||||
self.num_threads = num_threads
|
||||
self.__create_threads = create_threads
|
||||
self.exc_info = None
|
||||
|
||||
self.message_subscribers_messages = []
|
||||
self.message_subscribers_callbacks = []
|
||||
self.message_subscribers_lock = threading.Lock()
|
||||
|
||||
# key: chat_id, value: handler list
|
||||
self.message_subscribers_next_step = {}
|
||||
self.pre_message_subscribers_next_step = {}
|
||||
|
||||
self.message_handlers = []
|
||||
if self.__create_threads:
|
||||
self.worker_pool = util.ThreadPool(num_threads)
|
||||
self.edited_message_handlers = []
|
||||
self.inline_handlers = []
|
||||
self.chosen_inline_handlers = []
|
||||
self.callback_query_handlers = []
|
||||
|
||||
self.threaded = threaded
|
||||
if self.threaded:
|
||||
self.worker_pool = util.ThreadPool()
|
||||
|
||||
def set_webhook(self, url=None, certificate=None):
|
||||
return apihelper.set_webhook(self.token, url, certificate)
|
||||
|
||||
def remove_webhook(self):
|
||||
return self.set_webhook() # No params resets webhook
|
||||
|
||||
def get_updates(self, offset=None, limit=None, timeout=20):
|
||||
"""
|
||||
@ -82,83 +95,177 @@ class TeleBot:
|
||||
ret.append(types.Update.de_json(ju))
|
||||
return ret
|
||||
|
||||
def get_update(self):
|
||||
def __skip_updates(self):
|
||||
"""
|
||||
Get and discard all pending updates before first poll of the bot
|
||||
:return: total updates skipped
|
||||
"""
|
||||
total = 0
|
||||
updates = self.get_updates(offset=self.last_update_id, timeout=1)
|
||||
while updates:
|
||||
total += len(updates)
|
||||
for update in updates:
|
||||
if update.update_id > self.last_update_id:
|
||||
self.last_update_id = update.update_id
|
||||
updates = self.get_updates(offset=self.last_update_id + 1, timeout=1)
|
||||
return total
|
||||
|
||||
def __retrieve_updates(self, timeout=20):
|
||||
"""
|
||||
Retrieves any updates from the Telegram API.
|
||||
Registered listeners and applicable message handlers will be notified when a new message arrives.
|
||||
:raises ApiException when a call has failed.
|
||||
"""
|
||||
updates = self.get_updates(offset=(self.last_update_id + 1), timeout=3)
|
||||
if self.skip_pending:
|
||||
logger.debug('Skipped {0} pending messages'.format(self.__skip_updates()))
|
||||
self.skip_pending = False
|
||||
updates = self.get_updates(offset=(self.last_update_id + 1), timeout=timeout)
|
||||
self.process_new_updates(updates)
|
||||
|
||||
def process_new_updates(self, updates):
|
||||
new_messages = []
|
||||
edited_new_messages = []
|
||||
new_inline_querys = []
|
||||
new_chosen_inline_results = []
|
||||
new_callback_querys = []
|
||||
for update in updates:
|
||||
if update.update_id > self.last_update_id:
|
||||
self.last_update_id = update.update_id
|
||||
new_messages.append(update.message)
|
||||
logger.debug('Received {0} new messages'.format(len(new_messages)))
|
||||
if update.message:
|
||||
new_messages.append(update.message)
|
||||
if update.edited_message:
|
||||
edited_new_messages.append(update.edited_message)
|
||||
if update.inline_query:
|
||||
new_inline_querys.append(update.inline_query)
|
||||
if update.chosen_inline_result:
|
||||
new_chosen_inline_results.append(update.chosen_inline_result)
|
||||
if update.callback_query:
|
||||
new_callback_querys.append(update.callback_query)
|
||||
logger.debug('Received {0} new updates'.format(len(updates)))
|
||||
if len(new_messages) > 0:
|
||||
self.process_new_messages(new_messages)
|
||||
if len(edited_new_messages) > 0:
|
||||
self.process_new_edited_messages(edited_new_messages)
|
||||
if len(new_inline_querys) > 0:
|
||||
self.process_new_inline_query(new_inline_querys)
|
||||
if len(new_chosen_inline_results) > 0:
|
||||
self.process_new_chosen_inline_query(new_chosen_inline_results)
|
||||
if len(new_callback_querys) > 0:
|
||||
self.process_new_callback_query(new_callback_querys)
|
||||
|
||||
def process_new_messages(self, new_messages):
|
||||
self._append_pre_next_step_handler()
|
||||
self.__notify_update(new_messages)
|
||||
self._notify_command_handlers(new_messages)
|
||||
self._notify_command_handlers(self.message_handlers, new_messages)
|
||||
self._notify_message_subscribers(new_messages)
|
||||
self._notify_message_next_handler(new_messages)
|
||||
|
||||
def process_new_edited_messages(self, edited_message):
|
||||
self._notify_command_handlers(self.edited_message_handlers, edited_message)
|
||||
|
||||
def process_new_inline_query(self, new_inline_querys):
|
||||
self._notify_command_handlers(self.inline_handlers, new_inline_querys)
|
||||
|
||||
def process_new_chosen_inline_query(self, new_chosen_inline_querys):
|
||||
self._notify_command_handlers(self.chosen_inline_handlers, new_chosen_inline_querys)
|
||||
|
||||
def process_new_callback_query(self, new_callback_querys):
|
||||
self._notify_command_handlers(self.callback_query_handlers, new_callback_querys)
|
||||
|
||||
def __notify_update(self, new_messages):
|
||||
for listener in self.update_listener:
|
||||
if self.__create_threads:
|
||||
self.worker_pool.put(listener, new_messages)
|
||||
else:
|
||||
listener(new_messages)
|
||||
self.__exec_task(listener, new_messages)
|
||||
|
||||
def polling(self, none_stop=False, interval=0, block=True):
|
||||
def polling(self, none_stop=False, interval=0, timeout=20):
|
||||
"""
|
||||
This function creates a new Thread that calls an internal __polling function.
|
||||
This function creates a new Thread that calls an internal __retrieve_updates function.
|
||||
This allows the bot to retrieve Updates automagically and notify listeners and message handlers accordingly.
|
||||
|
||||
Do not call this function more than once!
|
||||
Warning: Do not call this function more than once!
|
||||
|
||||
Always get updates.
|
||||
:param none_stop: Do not stop polling when Exception occur.
|
||||
:param none_stop: Do not stop polling when an ApiException occurs.
|
||||
:param timeout: Timeout in seconds for long polling.
|
||||
:return:
|
||||
"""
|
||||
self.__stop_polling.set()
|
||||
if self.polling_thread:
|
||||
self.polling_thread.join() # wait thread stop.
|
||||
self.__stop_polling.clear()
|
||||
self.polling_thread = threading.Thread(target=self.__polling, args=([none_stop, interval]))
|
||||
self.polling_thread.daemon = True
|
||||
self.polling_thread.start()
|
||||
if self.threaded:
|
||||
self.__threaded_polling(none_stop, interval, timeout)
|
||||
else:
|
||||
self.__non_threaded_polling(none_stop, interval, timeout)
|
||||
|
||||
if block:
|
||||
while self.polling_thread.is_alive:
|
||||
try:
|
||||
time.sleep(.1)
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Received KeyboardInterrupt. Stopping.")
|
||||
self.stop_polling()
|
||||
self.polling_thread.join()
|
||||
break
|
||||
|
||||
def __polling(self, none_stop, interval):
|
||||
def __threaded_polling(self, none_stop=False, interval=0, timeout=3):
|
||||
logger.info('Started polling.')
|
||||
|
||||
self.__stop_polling.clear()
|
||||
error_interval = .25
|
||||
|
||||
polling_thread = util.WorkerThread(name="PollingThread")
|
||||
or_event = util.OrEvent(
|
||||
polling_thread.done_event,
|
||||
polling_thread.exception_event,
|
||||
self.worker_pool.exception_event
|
||||
)
|
||||
|
||||
while not self.__stop_polling.wait(interval):
|
||||
or_event.clear()
|
||||
try:
|
||||
self.get_update()
|
||||
polling_thread.put(self.__retrieve_updates, timeout)
|
||||
|
||||
or_event.wait() # wait for polling thread finish, polling thread error or thread pool error
|
||||
|
||||
polling_thread.raise_exceptions()
|
||||
self.worker_pool.raise_exceptions()
|
||||
|
||||
error_interval = .25
|
||||
except apihelper.ApiException as e:
|
||||
logger.error(e)
|
||||
if not none_stop:
|
||||
self.__stop_polling.set()
|
||||
logger.info("Exception occurred. Stopping.")
|
||||
else:
|
||||
polling_thread.clear_exceptions()
|
||||
self.worker_pool.clear_exceptions()
|
||||
logger.info("Waiting for {0} seconds until retry".format(error_interval))
|
||||
time.sleep(error_interval)
|
||||
error_interval *= 2
|
||||
logger.error(e)
|
||||
except KeyboardInterrupt:
|
||||
logger.info("KeyboardInterrupt received.")
|
||||
self.__stop_polling.set()
|
||||
polling_thread.stop()
|
||||
break
|
||||
|
||||
logger.info('Stopped polling.')
|
||||
|
||||
def __non_threaded_polling(self, none_stop=False, interval=0, timeout=3):
|
||||
logger.info('Started polling.')
|
||||
self.__stop_polling.clear()
|
||||
error_interval = .25
|
||||
|
||||
while not self.__stop_polling.wait(interval):
|
||||
try:
|
||||
self.__retrieve_updates(timeout)
|
||||
error_interval = .25
|
||||
except apihelper.ApiException as e:
|
||||
logger.error(e)
|
||||
if not none_stop:
|
||||
self.__stop_polling.set()
|
||||
logger.info("Exception occurred. Stopping.")
|
||||
else:
|
||||
logger.info("Waiting for {0} seconds until retry".format(error_interval))
|
||||
time.sleep(error_interval)
|
||||
error_interval *= 2
|
||||
except KeyboardInterrupt:
|
||||
logger.info("KeyboardInterrupt received.")
|
||||
self.__stop_polling.set()
|
||||
break
|
||||
|
||||
logger.info('Stopped polling.')
|
||||
|
||||
def __exec_task(self, task, *args, **kwargs):
|
||||
if self.threaded:
|
||||
self.worker_pool.put(task, *args, **kwargs)
|
||||
else:
|
||||
task(*args, **kwargs)
|
||||
|
||||
def stop_polling(self):
|
||||
self.__stop_polling.set()
|
||||
|
||||
@ -169,6 +276,12 @@ class TeleBot:
|
||||
result = apihelper.get_me(self.token)
|
||||
return types.User.de_json(result)
|
||||
|
||||
def get_file(self, file_id):
|
||||
return types.File.de_json(apihelper.get_file(self.token, file_id))
|
||||
|
||||
def download_file(self, file_path):
|
||||
return apihelper.download_file(self.token, file_path)
|
||||
|
||||
def get_user_profile_photos(self, user_id, offset=None, limit=None):
|
||||
"""
|
||||
Retrieves the user profile photos of the person with 'user_id'
|
||||
@ -181,8 +294,59 @@ class TeleBot:
|
||||
result = apihelper.get_user_profile_photos(self.token, user_id, offset, limit)
|
||||
return types.UserProfilePhotos.de_json(result)
|
||||
|
||||
def get_chat(self, chat_id):
|
||||
"""
|
||||
Use this method to get up to date information about the chat (current name of the user for one-on-one
|
||||
conversations, current username of a user, group or channel, etc.). Returns a Chat object on success.
|
||||
:param chat_id:
|
||||
:return:
|
||||
"""
|
||||
result = apihelper.get_chat(self.token, chat_id)
|
||||
return types.Chat.de_json(result)
|
||||
|
||||
def leave_chat(self, chat_id):
|
||||
"""
|
||||
Use this method for your bot to leave a group, supergroup or channel. Returns True on success.
|
||||
:param chat_id:
|
||||
:return:
|
||||
"""
|
||||
result = apihelper.leave_chat(self.token, chat_id)
|
||||
return result
|
||||
|
||||
def get_chat_administrators(self, chat_id):
|
||||
"""
|
||||
Use this method to get a list of administrators in a chat. On success, returns an Array of ChatMember objects
|
||||
that contains information about all chat administrators except other bots.
|
||||
:param chat_id:
|
||||
:return:
|
||||
"""
|
||||
result = apihelper.get_chat_administrators(self.token, chat_id)
|
||||
ret = []
|
||||
for r in result:
|
||||
ret.append(types.ChatMember.de_json(r))
|
||||
return ret
|
||||
|
||||
def get_chat_members_count(self, chat_id):
|
||||
"""
|
||||
Use this method to get the number of members in a chat. Returns Int on success.
|
||||
:param chat_id:
|
||||
:return:
|
||||
"""
|
||||
result = apihelper.get_chat_members_count(self.token, chat_id)
|
||||
return result
|
||||
|
||||
def get_chat_member(self, chat_id, user_id):
|
||||
"""
|
||||
Use this method to get information about a member of a chat. Returns a ChatMember object on success.
|
||||
:param chat_id:
|
||||
:param user_id:
|
||||
:return:
|
||||
"""
|
||||
result = apihelper.get_chat_member(self.token, chat_id, user_id)
|
||||
return types.ChatMember.de_json(result)
|
||||
|
||||
def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None,
|
||||
parse_mode=None):
|
||||
parse_mode=None, disable_notification=None):
|
||||
"""
|
||||
Use this method to send text messages.
|
||||
|
||||
@ -195,23 +359,27 @@ class TeleBot:
|
||||
:param reply_to_message_id:
|
||||
:param reply_markup:
|
||||
:param parse_mode:
|
||||
:param disable_notification: Boolean, Optional. Sends the message silently.
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id,
|
||||
reply_markup, parse_mode))
|
||||
reply_markup, parse_mode, disable_notification))
|
||||
|
||||
def forward_message(self, chat_id, from_chat_id, message_id):
|
||||
def forward_message(self, chat_id, from_chat_id, message_id, disable_notification=None):
|
||||
"""
|
||||
Use this method to forward messages of any kind.
|
||||
:param disable_notification:
|
||||
:param chat_id: which chat to forward
|
||||
:param from_chat_id: which chat message from
|
||||
:param message_id: message id
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(apihelper.forward_message(self.token, chat_id, from_chat_id, message_id))
|
||||
return types.Message.de_json(
|
||||
apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification))
|
||||
|
||||
def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None):
|
||||
def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
"""
|
||||
Use this method to send photos.
|
||||
:param chat_id:
|
||||
@ -222,10 +390,11 @@ class TeleBot:
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup))
|
||||
apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup,
|
||||
disable_notification))
|
||||
|
||||
def send_audio(self, chat_id, audio, duration=None, performer=None, title=None, reply_to_message_id=None,
|
||||
reply_markup=None):
|
||||
reply_markup=None, disable_notification=None, timeout=None):
|
||||
"""
|
||||
Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format.
|
||||
:param chat_id:Unique identifier for the message recipient
|
||||
@ -239,9 +408,10 @@ class TeleBot:
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_audio(self.token, chat_id, audio, duration, performer, title, reply_to_message_id,
|
||||
reply_markup))
|
||||
reply_markup, disable_notification, timeout))
|
||||
|
||||
def send_voice(self, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None):
|
||||
def send_voice(self, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None):
|
||||
"""
|
||||
Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message.
|
||||
:param chat_id:Unique identifier for the message recipient.
|
||||
@ -252,9 +422,11 @@ class TeleBot:
|
||||
:return: Message
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_voice(self.token, chat_id, voice, duration, reply_to_message_id, reply_markup))
|
||||
apihelper.send_voice(self.token, chat_id, voice, duration, reply_to_message_id, reply_markup,
|
||||
disable_notification, timeout))
|
||||
|
||||
def send_document(self, chat_id, data, reply_to_message_id=None, reply_markup=None):
|
||||
def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None):
|
||||
"""
|
||||
Use this method to send general files.
|
||||
:param chat_id:
|
||||
@ -264,9 +436,11 @@ class TeleBot:
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup))
|
||||
apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup,
|
||||
disable_notification, timeout, caption=caption))
|
||||
|
||||
def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None):
|
||||
def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None,
|
||||
timeout=None):
|
||||
"""
|
||||
Use this method to send .webp stickers.
|
||||
:param chat_id:
|
||||
@ -276,9 +450,11 @@ class TeleBot:
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_data(self.token, chat_id, data, 'sticker', reply_to_message_id, reply_markup))
|
||||
apihelper.send_data(self.token, chat_id, data, 'sticker', reply_to_message_id, reply_markup,
|
||||
disable_notification, timeout))
|
||||
|
||||
def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None):
|
||||
def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None):
|
||||
"""
|
||||
Use this method to send video files, Telegram clients support mp4 videos.
|
||||
:param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id
|
||||
@ -290,9 +466,11 @@ class TeleBot:
|
||||
:return:
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup))
|
||||
apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup,
|
||||
disable_notification, timeout))
|
||||
|
||||
def send_location(self, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None):
|
||||
def send_location(self, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
"""
|
||||
Use this method to send point on the map.
|
||||
:param chat_id:
|
||||
@ -303,7 +481,35 @@ class TeleBot:
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_location(self.token, chat_id, latitude, longitude, reply_to_message_id, reply_markup))
|
||||
apihelper.send_location(self.token, chat_id, latitude, longitude, reply_to_message_id, reply_markup,
|
||||
disable_notification))
|
||||
|
||||
def send_venue(self, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None,
|
||||
reply_to_message_id=None, reply_markup=None):
|
||||
"""
|
||||
Use this method to send information about a venue.
|
||||
:param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel
|
||||
:param latitude: Float : Latitude of the venue
|
||||
:param longitude: Float : Longitude of the venue
|
||||
:param title: String : Name of the venue
|
||||
:param address: String : Address of the venue
|
||||
:param foursquare_id: String : Foursquare identifier of the venue
|
||||
:param disable_notification:
|
||||
:param reply_to_message_id:
|
||||
:param reply_markup:
|
||||
:return:
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_venue(self.token, chat_id, latitude, longitude, title, address, foursquare_id,
|
||||
disable_notification, reply_to_message_id, reply_markup)
|
||||
)
|
||||
|
||||
def send_contact(self, chat_id, phone_number, first_name, last_name=None, disable_notification=None,
|
||||
reply_to_message_id=None, reply_markup=None):
|
||||
return types.Message.de_json(
|
||||
apihelper.send_contact(self.token, chat_id, phone_number, first_name, last_name, disable_notification,
|
||||
reply_to_message_id, reply_markup)
|
||||
)
|
||||
|
||||
def send_chat_action(self, chat_id, action):
|
||||
"""
|
||||
@ -317,12 +523,74 @@ class TeleBot:
|
||||
"""
|
||||
return apihelper.send_chat_action(self.token, chat_id, action)
|
||||
|
||||
def kick_chat_member(self, chat_id, user_id):
|
||||
"""
|
||||
Use this method to kick a user from a group or a supergroup.
|
||||
:param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup
|
||||
:param user_id: Int : Unique identifier of the target user
|
||||
:return: types.Message
|
||||
"""
|
||||
return apihelper.kick_chat_member(self.token, chat_id, user_id)
|
||||
|
||||
def unban_chat_member(self, chat_id, user_id):
|
||||
return apihelper.unban_chat_member(self.token, chat_id, user_id)
|
||||
|
||||
def answer_callback_query(self, callback_query_id, text=None, show_alert=None):
|
||||
return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert)
|
||||
|
||||
def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None,
|
||||
disable_web_page_preview=None, reply_markup=None):
|
||||
result = apihelper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode,
|
||||
disable_web_page_preview, reply_markup)
|
||||
if type(result) == bool: # if edit inline message return is bool not Message.
|
||||
return result
|
||||
return types.Message.de_json(result)
|
||||
|
||||
def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
|
||||
return types.Message.de_json(
|
||||
apihelper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup)
|
||||
)
|
||||
|
||||
def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
|
||||
return types.Message.de_json(
|
||||
apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, reply_markup)
|
||||
)
|
||||
|
||||
def reply_to(self, message, text, **kwargs):
|
||||
"""
|
||||
Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)`
|
||||
"""
|
||||
return self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)
|
||||
|
||||
def answer_inline_query(self, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None,
|
||||
switch_pm_text=None, switch_pm_parameter=None):
|
||||
"""
|
||||
Use this method to send answers to an inline query. On success, True is returned.
|
||||
No more than 50 results per query are allowed.
|
||||
:param inline_query_id: Unique identifier for the answered query
|
||||
:param results: Array of results for the inline query
|
||||
:param cache_time: The maximum amount of time in seconds that the result of the inline query may be cached on the server.
|
||||
:param is_personal: Pass True, if results may be cached on the server side only for the user that sent the query.
|
||||
:param next_offset: Pass the offset that a client should send in the next query with the same text to receive more results.
|
||||
:param switch_pm_parameter: If passed, clients will display a button with specified text that switches the user
|
||||
to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter
|
||||
:param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button
|
||||
:return: True means success.
|
||||
"""
|
||||
return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset,
|
||||
switch_pm_text, switch_pm_parameter)
|
||||
|
||||
def answer_callback_query(self, callback_query_id, text=None, show_alert=None):
|
||||
"""
|
||||
Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to
|
||||
the user as a notification at the top of the chat screen or as an alert.
|
||||
:param callback_query_id:
|
||||
:param text:
|
||||
:param show_alert:
|
||||
:return:
|
||||
"""
|
||||
return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert)
|
||||
|
||||
def register_for_reply(self, message, callback):
|
||||
"""
|
||||
Registers a callback function to be notified when a reply to `message` arrives.
|
||||
@ -334,15 +602,16 @@ class TeleBot:
|
||||
:param callback: The callback function to be called when a reply arrives. Must accept one `message`
|
||||
parameter, which will contain the replied message.
|
||||
"""
|
||||
self.message_subscribers_messages.insert(0, message.message_id)
|
||||
self.message_subscribers_callbacks.insert(0, callback)
|
||||
if len(self.message_subscribers_messages) > 10000:
|
||||
self.message_subscribers_messages.pop()
|
||||
self.message_subscribers_callbacks.pop()
|
||||
with self.message_subscribers_lock:
|
||||
self.message_subscribers_messages.insert(0, message.message_id)
|
||||
self.message_subscribers_callbacks.insert(0, callback)
|
||||
if len(self.message_subscribers_messages) > 10000:
|
||||
self.message_subscribers_messages.pop()
|
||||
self.message_subscribers_callbacks.pop()
|
||||
|
||||
def _notify_message_subscribers(self, new_messages):
|
||||
for message in new_messages:
|
||||
if not hasattr(message, 'reply_to_message'):
|
||||
if not message.reply_to_message:
|
||||
continue
|
||||
|
||||
reply_msg_id = message.reply_to_message.message_id
|
||||
@ -350,8 +619,10 @@ class TeleBot:
|
||||
index = self.message_subscribers_messages.index(reply_msg_id)
|
||||
self.message_subscribers_callbacks[index](message)
|
||||
|
||||
del self.message_subscribers_messages[index]
|
||||
del self.message_subscribers_callbacks[index]
|
||||
with self.message_subscribers_lock:
|
||||
index = self.message_subscribers_messages.index(reply_msg_id)
|
||||
del self.message_subscribers_messages[index]
|
||||
del self.message_subscribers_callbacks[index]
|
||||
|
||||
def register_next_step_handler(self, message, callback):
|
||||
"""
|
||||
@ -361,10 +632,10 @@ class TeleBot:
|
||||
:param callback: The callback function which next new message arrives.
|
||||
"""
|
||||
chat_id = message.chat.id
|
||||
if chat_id in self.message_subscribers_next_step:
|
||||
self.message_subscribers_next_step[chat_id].append(callback)
|
||||
if chat_id in self.pre_message_subscribers_next_step:
|
||||
self.pre_message_subscribers_next_step[chat_id].append(callback)
|
||||
else:
|
||||
self.message_subscribers_next_step[chat_id] = [callback]
|
||||
self.pre_message_subscribers_next_step[chat_id] = [callback]
|
||||
|
||||
def _notify_message_next_handler(self, new_messages):
|
||||
for message in new_messages:
|
||||
@ -372,9 +643,17 @@ class TeleBot:
|
||||
if chat_id in self.message_subscribers_next_step:
|
||||
handlers = self.message_subscribers_next_step[chat_id]
|
||||
for handler in handlers:
|
||||
self.worker_pool.put(handler, message)
|
||||
self.__exec_task(handler, message)
|
||||
self.message_subscribers_next_step.pop(chat_id, None)
|
||||
|
||||
def _append_pre_next_step_handler(self):
|
||||
for k in self.pre_message_subscribers_next_step.keys():
|
||||
if k in self.message_subscribers_next_step:
|
||||
self.message_subscribers_next_step[k].extend(self.pre_message_subscribers_next_step[k])
|
||||
else:
|
||||
self.message_subscribers_next_step[k] = self.pre_message_subscribers_next_step[k]
|
||||
self.pre_message_subscribers_next_step = {}
|
||||
|
||||
def message_handler(self, commands=None, regexp=None, func=None, content_types=['text']):
|
||||
"""
|
||||
Message handler decorator.
|
||||
@ -404,21 +683,108 @@ class TeleBot:
|
||||
:param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message.
|
||||
:param content_types: This commands' supported content types. Must be a list. Defaults to ['text'].
|
||||
"""
|
||||
def decorator(fn):
|
||||
handler_dict = {'function': fn}
|
||||
filters = {'content_types': content_types}
|
||||
if regexp:
|
||||
filters['regexp'] = regexp
|
||||
if func:
|
||||
filters['lambda'] = func
|
||||
if commands:
|
||||
filters['commands'] = commands
|
||||
handler_dict['filters'] = filters
|
||||
self.message_handlers.append(handler_dict)
|
||||
return fn
|
||||
|
||||
def decorator(handler):
|
||||
self.add_message_handler(handler, commands, regexp, func, content_types)
|
||||
return handler
|
||||
|
||||
return decorator
|
||||
|
||||
def add_message_handler(self, handler, commands=None, regexp=None, func=None, content_types=None):
|
||||
if content_types is None:
|
||||
content_types = ['text']
|
||||
|
||||
filters = {'content_types': content_types}
|
||||
if regexp:
|
||||
filters['regexp'] = regexp
|
||||
if func:
|
||||
filters['lambda'] = func
|
||||
if commands:
|
||||
filters['commands'] = commands
|
||||
|
||||
handler_dict = {
|
||||
'function': handler,
|
||||
'filters': filters
|
||||
}
|
||||
|
||||
self.message_handlers.append(handler_dict)
|
||||
|
||||
def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=['text']):
|
||||
def decorator(handler):
|
||||
self.add_edited_message_handler(handler, commands, regexp, func, content_types)
|
||||
return handler
|
||||
|
||||
return decorator
|
||||
|
||||
def add_edited_message_handler(self, handler, commands=None, regexp=None, func=None, content_types=None):
|
||||
if content_types is None:
|
||||
content_types = ['text']
|
||||
|
||||
filters = {'content_types': content_types}
|
||||
if regexp:
|
||||
filters['regexp'] = regexp
|
||||
if func:
|
||||
filters['lambda'] = func
|
||||
if commands:
|
||||
filters['commands'] = commands
|
||||
|
||||
handler_dict = {
|
||||
'function': handler,
|
||||
'filters': filters
|
||||
}
|
||||
|
||||
self.edited_message_handlers.append(handler_dict)
|
||||
|
||||
def inline_handler(self, func):
|
||||
def decorator(handler):
|
||||
self.add_inline_handler(handler, func)
|
||||
return handler
|
||||
|
||||
return decorator
|
||||
|
||||
def add_inline_handler(self, handler, func):
|
||||
filters = {'lambda': func}
|
||||
|
||||
handler_dict = {
|
||||
'function': handler,
|
||||
'filters': filters
|
||||
}
|
||||
|
||||
self.inline_handlers.append(handler_dict)
|
||||
|
||||
def chosen_inline_handler(self, func):
|
||||
def decorator(handler):
|
||||
self.add_chosen_inline_handler(handler, func)
|
||||
return handler
|
||||
|
||||
return decorator
|
||||
|
||||
def add_chosen_inline_handler(self, handler, func):
|
||||
filters = {'lambda': func}
|
||||
|
||||
handler_dict = {
|
||||
'function': handler,
|
||||
'filters': filters
|
||||
}
|
||||
|
||||
self.chosen_inline_handlers.append(handler_dict)
|
||||
|
||||
def callback_query_handler(self, func):
|
||||
def decorator(handler):
|
||||
self.add_callback_query_handler(handler, func)
|
||||
|
||||
return decorator
|
||||
|
||||
def add_callback_query_handler(self, handler, func):
|
||||
filters = {'lambda': func}
|
||||
|
||||
handler_dict = {
|
||||
'function': handler,
|
||||
'filters': filters
|
||||
}
|
||||
|
||||
self.callback_query_handlers.append(handler_dict)
|
||||
|
||||
@staticmethod
|
||||
def _test_message_handler(message_handler, message):
|
||||
for filter, filter_value in six.iteritems(message_handler['filters']):
|
||||
@ -434,18 +800,15 @@ class TeleBot:
|
||||
return message.content_type == 'text' and re.search(filter_value, message.text)
|
||||
if filter == 'commands':
|
||||
return message.content_type == 'text' and util.extract_command(message.text) in filter_value
|
||||
if filter == 'func':
|
||||
if filter == 'lambda':
|
||||
return filter_value(message)
|
||||
return False
|
||||
|
||||
def _notify_command_handlers(self, new_messages):
|
||||
def _notify_command_handlers(self, handlers, new_messages):
|
||||
for message in new_messages:
|
||||
for message_handler in self.message_handlers:
|
||||
for message_handler in handlers:
|
||||
if self._test_message_handler(message_handler, message):
|
||||
if self.__create_threads:
|
||||
self.worker_pool.put(message_handler['function'], message)
|
||||
else:
|
||||
message_handler['function'](message)
|
||||
self.__exec_task(message_handler['function'], message)
|
||||
break
|
||||
|
||||
|
||||
|
@ -7,8 +7,14 @@ from telebot import util
|
||||
|
||||
logger = telebot.logger
|
||||
|
||||
API_URL = "https://api.telegram.org/bot{0}/{1}"
|
||||
FILE_URL = "https://api.telegram.org/file/bot{0}/{1}"
|
||||
|
||||
def _make_request(token, method_name, method='get', params=None, files=None):
|
||||
CONNECT_TIMEOUT = 3.5
|
||||
READ_TIMEOUT = 9999
|
||||
|
||||
|
||||
def _make_request(token, method_name, method='get', params=None, files=None, base_url=API_URL):
|
||||
"""
|
||||
Makes a request to the Telegram API.
|
||||
:param token: The bot's API token. (Created with @BotFather)
|
||||
@ -18,10 +24,15 @@ def _make_request(token, method_name, method='get', params=None, files=None):
|
||||
:param files: Optional files.
|
||||
:return: The result parsed to a JSON dictionary.
|
||||
"""
|
||||
request_url = telebot.API_URL + 'bot' + token + '/' + method_name
|
||||
request_url = base_url.format(token, method_name)
|
||||
logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files))
|
||||
result = requests.request(method, request_url, params=params, files=files)
|
||||
logger.debug("The server returned: '{0}'".format(result.text))
|
||||
read_timeout = READ_TIMEOUT
|
||||
connect_timeout = CONNECT_TIMEOUT
|
||||
if params:
|
||||
if 'timeout' in params: read_timeout = params['timeout'] + 10
|
||||
if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10
|
||||
result = requests.request(method, request_url, params=params, files=files, timeout=(connect_timeout, read_timeout))
|
||||
logger.debug("The server returned: '{0}'".format(result.text.encode('utf8')))
|
||||
return _check_result(method_name, result)['result']
|
||||
|
||||
|
||||
@ -39,31 +50,46 @@ def _check_result(method_name, result):
|
||||
:return: The result parsed to a JSON dictionary.
|
||||
"""
|
||||
if result.status_code != 200:
|
||||
msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]'\
|
||||
.format(result.status_code, result.reason, result.text)
|
||||
msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \
|
||||
.format(result.status_code, result.reason, result.text.encode('utf8'))
|
||||
raise ApiException(msg, method_name, result)
|
||||
|
||||
try:
|
||||
result_json = result.json()
|
||||
except:
|
||||
msg = 'The server returned an invalid JSON response. Response body:\n[{0}]'\
|
||||
.format(result.text)
|
||||
msg = 'The server returned an invalid JSON response. Response body:\n[{0}]' \
|
||||
.format(result.text.encode('utf8'))
|
||||
raise ApiException(msg, method_name, result)
|
||||
|
||||
if not result_json['ok']:
|
||||
msg = 'Error code: {0} Description: {1}'\
|
||||
msg = 'Error code: {0} Description: {1}' \
|
||||
.format(result_json['error_code'], result_json['description'])
|
||||
raise ApiException(msg, method_name, result)
|
||||
return result_json
|
||||
|
||||
|
||||
def get_me(token):
|
||||
method_url = 'getMe'
|
||||
method_url = r'getMe'
|
||||
return _make_request(token, method_url)
|
||||
|
||||
|
||||
def get_file(token, file_id):
|
||||
method_url = r'getFile'
|
||||
return _make_request(token, method_url, params={'file_id': file_id})
|
||||
|
||||
|
||||
def download_file(token, file_path):
|
||||
url = FILE_URL.format(token, file_path)
|
||||
result = requests.get(url)
|
||||
if result.status_code != 200:
|
||||
msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \
|
||||
.format(result.status_code, result.reason, result.text)
|
||||
raise ApiException(msg, 'Download file', result)
|
||||
return result.content
|
||||
|
||||
|
||||
def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None,
|
||||
parse_mode=None):
|
||||
parse_mode=None, disable_notification=None):
|
||||
"""
|
||||
Use this method to send text messages. On success, the sent Message is returned.
|
||||
:param token:
|
||||
@ -84,9 +110,23 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if parse_mode:
|
||||
payload['parse_mode'] = parse_mode
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def set_webhook(token, url=None, certificate=None):
|
||||
method_url = r'setWebhook'
|
||||
payload = {
|
||||
'url': url if url else "",
|
||||
}
|
||||
files = None
|
||||
if certificate:
|
||||
files = {'certificate': certificate}
|
||||
|
||||
return _make_request(token, method_url, params=payload, files=files)
|
||||
|
||||
|
||||
def get_updates(token, offset=None, limit=None, timeout=None):
|
||||
method_url = r'getUpdates'
|
||||
payload = {}
|
||||
@ -109,13 +149,46 @@ def get_user_profile_photos(token, user_id, offset=None, limit=None):
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def forward_message(token, chat_id, from_chat_id, message_id):
|
||||
method_url = r'forwardMessage'
|
||||
payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id}
|
||||
def get_chat(token, chat_id):
|
||||
method_url = r'getChat'
|
||||
payload = {'chat_id': chat_id}
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None):
|
||||
def leave_chat(token, chat_id):
|
||||
method_url = r'leaveChat'
|
||||
payload = {'chat_id': chat_id}
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def get_chat_administrators(token, chat_id):
|
||||
method_url = r'getChatAdministrators'
|
||||
payload = {'chat_id': chat_id}
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def get_chat_members_count(token, chat_id):
|
||||
method_url = r'getChatMembersCount'
|
||||
payload = {'chat_id': chat_id}
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def get_chat_member(token, chat_id, user_id):
|
||||
method_url = r'getChatMember'
|
||||
payload = {'chat_id': chat_id, 'user_id': user_id}
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def forward_message(token, chat_id, from_chat_id, message_id, disable_notification=None):
|
||||
method_url = r'forwardMessage'
|
||||
payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id}
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
method_url = r'sendPhoto'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -129,12 +202,47 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_location(token, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None):
|
||||
def send_location(token, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
method_url = r'sendLocation'
|
||||
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude}
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def send_venue(token, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None,
|
||||
reply_to_message_id=None, reply_markup=None):
|
||||
method_url = r'sendVenue'
|
||||
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address}
|
||||
if foursquare_id:
|
||||
payload['foursquare_id'] = foursquare_id
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def send_contact(token, chat_id, phone_number, first_name, last_name=None, disable_notification=None,
|
||||
reply_to_message_id=None, reply_markup=None):
|
||||
method_url = r'sendContact'
|
||||
payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name}
|
||||
if last_name:
|
||||
payload['last_name'] = last_name
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
@ -148,7 +256,8 @@ def send_chat_action(token, chat_id, action):
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None):
|
||||
def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None):
|
||||
method_url = r'sendVideo'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -164,10 +273,15 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None):
|
||||
def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None):
|
||||
method_url = r'sendVoice'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -181,11 +295,15 @@ def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, r
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_audio(token, chat_id, audio, duration=None, performer=None, title=None, reply_to_message_id=None,
|
||||
reply_markup=None):
|
||||
reply_markup=None, disable_notification=None, timeout=None):
|
||||
method_url = r'sendAudio'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -203,10 +321,15 @@ def send_audio(token, chat_id, audio, duration=None, performer=None, title=None,
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None):
|
||||
def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, disable_notification=None,
|
||||
timeout=None, caption=None):
|
||||
method_url = get_method_by_type(data_type)
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -218,14 +341,120 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
if caption:
|
||||
payload['caption'] = caption
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def get_method_by_type(data_type):
|
||||
if data_type == 'document':
|
||||
return 'sendDocument'
|
||||
return r'sendDocument'
|
||||
if data_type == 'sticker':
|
||||
return 'sendSticker'
|
||||
return r'sendSticker'
|
||||
|
||||
|
||||
def kick_chat_member(token, chat_id, user_id):
|
||||
method_url = 'kickChatMember'
|
||||
payload = {'chat_id': chat_id, 'user_id': user_id}
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def unban_chat_member(token, chat_id, user_id):
|
||||
method_url = 'unbanChatMember'
|
||||
payload = {'chat_id': chat_id, 'user_id': user_id}
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
# Updating messages
|
||||
|
||||
def edit_message_text(token, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None,
|
||||
disable_web_page_preview=None, reply_markup=None):
|
||||
method_url = r'editMessageText'
|
||||
payload = {'text': text}
|
||||
if chat_id:
|
||||
payload['chat_id'] = chat_id
|
||||
if message_id:
|
||||
payload['message_id'] = message_id
|
||||
if inline_message_id:
|
||||
payload['inline_message_id'] = inline_message_id
|
||||
if parse_mode:
|
||||
payload['parse_mode'] = parse_mode
|
||||
if disable_web_page_preview:
|
||||
payload['disable_web_page_preview'] = disable_web_page_preview
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
|
||||
method_url = r'editMessageCaption'
|
||||
payload = {'caption': caption}
|
||||
if chat_id:
|
||||
payload['chat_id'] = chat_id
|
||||
if message_id:
|
||||
payload['message_id'] = message_id
|
||||
if inline_message_id:
|
||||
payload['inline_message_id'] = inline_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
|
||||
method_url = r'editMessageReplyMarkup'
|
||||
payload = {}
|
||||
if chat_id:
|
||||
payload['chat_id'] = chat_id
|
||||
if message_id:
|
||||
payload['message_id'] = message_id
|
||||
if inline_message_id:
|
||||
payload['inline_message_id'] = inline_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
# InlineQuery
|
||||
|
||||
def answer_callback_query(token, callback_query_id, text=None, show_alert=None):
|
||||
method_url = 'answerCallbackQuery'
|
||||
payload = {'callback_query_id': callback_query_id}
|
||||
if text:
|
||||
payload['text'] = text
|
||||
if show_alert:
|
||||
payload['show_alert'] = show_alert
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def answer_inline_query(token, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None,
|
||||
switch_pm_text=None, switch_pm_parameter=None):
|
||||
method_url = 'answerInlineQuery'
|
||||
payload = {'inline_query_id': inline_query_id, 'results': _convert_inline_results(results)}
|
||||
if cache_time:
|
||||
payload['cache_time'] = cache_time
|
||||
if is_personal:
|
||||
payload['is_personal'] = is_personal
|
||||
if next_offset is not None:
|
||||
payload['next_offset'] = next_offset
|
||||
if switch_pm_text:
|
||||
payload['switch_pm_text'] = switch_pm_text
|
||||
if switch_pm_parameter:
|
||||
payload['switch_pm_parameter'] = switch_pm_parameter
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def _convert_inline_results(results):
|
||||
ret = ''
|
||||
for r in results:
|
||||
if isinstance(r, types.JsonSerializable):
|
||||
ret = ret + r.to_json() + ','
|
||||
if len(ret) > 0:
|
||||
ret = ret[:-1]
|
||||
return '[' + ret + ']'
|
||||
|
||||
|
||||
def _convert_markup(markup):
|
||||
@ -233,6 +462,7 @@ def _convert_markup(markup):
|
||||
return markup.to_json()
|
||||
return markup
|
||||
|
||||
|
||||
class ApiException(Exception):
|
||||
"""
|
||||
This class represents an Exception thrown when a call to the Telegram API fails.
|
||||
|
1169
telebot/types.py
1169
telebot/types.py
File diff suppressed because it is too large
Load Diff
143
telebot/util.py
143
telebot/util.py
@ -1,54 +1,117 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import threading
|
||||
import re
|
||||
import sys
|
||||
import six
|
||||
from six import string_types
|
||||
|
||||
# Python3 queue support.
|
||||
|
||||
try:
|
||||
import Queue
|
||||
except ImportError:
|
||||
import queue as Queue
|
||||
|
||||
from telebot import logger
|
||||
|
||||
class ThreadPool:
|
||||
class WorkerThread(threading.Thread):
|
||||
|
||||
class WorkerThread(threading.Thread):
|
||||
count = 0
|
||||
|
||||
def __init__(self, queue):
|
||||
threading.Thread.__init__(self, name="WorkerThread{0}".format(self.__class__.count + 1))
|
||||
self.__class__.count += 1
|
||||
def __init__(self, exception_callback=None, queue=None, name=None):
|
||||
if not name:
|
||||
name = "WorkerThread{0}".format(self.__class__.count + 1)
|
||||
self.__class__.count += 1
|
||||
if not queue:
|
||||
queue = Queue.Queue()
|
||||
|
||||
threading.Thread.__init__(self, name=name)
|
||||
self.queue = queue
|
||||
self.daemon = True
|
||||
|
||||
self.received_task_event = threading.Event()
|
||||
self.done_event = threading.Event()
|
||||
self.exception_event = threading.Event()
|
||||
self.continue_event = threading.Event()
|
||||
|
||||
self.exception_callback = exception_callback
|
||||
self.exc_info = None
|
||||
self._running = True
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
while self._running:
|
||||
try:
|
||||
task, args, kwargs = self.queue.get(block=True, timeout=.01)
|
||||
task, args, kwargs = self.queue.get(block=True, timeout=.5)
|
||||
self.continue_event.clear()
|
||||
self.received_task_event.clear()
|
||||
self.done_event.clear()
|
||||
self.exception_event.clear()
|
||||
logger.debug("Received task")
|
||||
self.received_task_event.set()
|
||||
|
||||
task(*args, **kwargs)
|
||||
logger.debug("Task complete")
|
||||
self.done_event.set()
|
||||
except Queue.Empty:
|
||||
pass
|
||||
except:
|
||||
logger.debug("Exception occurred")
|
||||
self.exc_info = sys.exc_info()
|
||||
self.exception_event.set()
|
||||
|
||||
if self.exception_callback:
|
||||
self.exception_callback(self, self.exc_info)
|
||||
self.continue_event.wait()
|
||||
|
||||
def put(self, task, *args, **kwargs):
|
||||
self.queue.put((task, args, kwargs))
|
||||
|
||||
def raise_exceptions(self):
|
||||
if self.exception_event.is_set():
|
||||
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
|
||||
|
||||
def clear_exceptions(self):
|
||||
self.exception_event.clear()
|
||||
self.continue_event.set()
|
||||
|
||||
def stop(self):
|
||||
self._running = False
|
||||
|
||||
def __init__(self, num_threads=4):
|
||||
self.tasks = Queue.Queue()
|
||||
self.workers = [self.WorkerThread(self.tasks) for _ in range(num_threads)]
|
||||
|
||||
class ThreadPool:
|
||||
|
||||
def __init__(self, num_threads=2):
|
||||
self.tasks = Queue.Queue()
|
||||
self.workers = [WorkerThread(self.on_exception, self.tasks) for _ in range(num_threads)]
|
||||
self.num_threads = num_threads
|
||||
|
||||
self.exception_event = threading.Event()
|
||||
self.exc_info = None
|
||||
|
||||
def put(self, func, *args, **kwargs):
|
||||
self.tasks.put((func, args, kwargs))
|
||||
|
||||
def on_exception(self, worker_thread, exc_info):
|
||||
self.exc_info = exc_info
|
||||
self.exception_event.set()
|
||||
worker_thread.continue_event.set()
|
||||
|
||||
def raise_exceptions(self):
|
||||
if self.exception_event.is_set():
|
||||
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
|
||||
|
||||
def clear_exceptions(self):
|
||||
self.exception_event.clear()
|
||||
|
||||
def close(self):
|
||||
for worker in self.workers:
|
||||
worker.stop()
|
||||
for worker in self.workers:
|
||||
worker.join()
|
||||
|
||||
|
||||
class AsyncTask:
|
||||
def __init__(self, target, *args, **kwargs):
|
||||
self.target = target
|
||||
@ -62,15 +125,15 @@ class AsyncTask:
|
||||
def _run(self):
|
||||
try:
|
||||
self.result = self.target(*self.args, **self.kwargs)
|
||||
except Exception as e:
|
||||
self.result = e
|
||||
except:
|
||||
self.result = sys.exc_info()
|
||||
self.done = True
|
||||
|
||||
def wait(self):
|
||||
if not self.done:
|
||||
self.thread.join()
|
||||
if isinstance(self.result, Exception):
|
||||
raise self.result
|
||||
if isinstance(self.result, BaseException):
|
||||
six.reraise(self.result[0], self.result[1], self.result[2])
|
||||
else:
|
||||
return self.result
|
||||
|
||||
@ -124,3 +187,57 @@ def split_string(text, chars_per_string):
|
||||
:return: The splitted text as a list of strings.
|
||||
"""
|
||||
return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)]
|
||||
|
||||
# CREDITS TO http://stackoverflow.com/questions/12317940#answer-12320352
|
||||
def or_set(self):
|
||||
self._set()
|
||||
self.changed()
|
||||
|
||||
|
||||
def or_clear(self):
|
||||
self._clear()
|
||||
self.changed()
|
||||
|
||||
|
||||
def orify(e, changed_callback):
|
||||
e._set = e.set
|
||||
e._clear = e.clear
|
||||
e.changed = changed_callback
|
||||
e.set = lambda: or_set(e)
|
||||
e.clear = lambda: or_clear(e)
|
||||
|
||||
def OrEvent(*events):
|
||||
or_event = threading.Event()
|
||||
def changed():
|
||||
bools = [e.is_set() for e in events]
|
||||
if any(bools):
|
||||
or_event.set()
|
||||
else:
|
||||
or_event.clear()
|
||||
|
||||
def busy_wait():
|
||||
while not or_event.is_set():
|
||||
or_event._wait(3)
|
||||
|
||||
for e in events:
|
||||
orify(e, changed)
|
||||
or_event._wait = or_event.wait
|
||||
or_event.wait = busy_wait
|
||||
changed()
|
||||
return or_event
|
||||
|
||||
def extract_arguments(text):
|
||||
"""
|
||||
Returns the argument after the command.
|
||||
|
||||
Examples:
|
||||
extract_arguments("/get name"): 'name'
|
||||
extract_arguments("/get"): ''
|
||||
extract_arguments("/get@botName name"): 'name'
|
||||
|
||||
:param text: String to extract the arguments from a command
|
||||
:return: the arguments if `text` is a command (according to is_command), else None.
|
||||
"""
|
||||
regexp = re.compile("\/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE)
|
||||
result = regexp.match(text)
|
||||
return result.group(2) if is_command(text) else None
|
||||
|
@ -9,7 +9,6 @@ import os
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
from telebot import apihelper
|
||||
from telebot import util
|
||||
|
||||
should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ
|
||||
@ -17,6 +16,7 @@ should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ
|
||||
if not should_skip:
|
||||
TOKEN = os.environ['TOKEN']
|
||||
CHAT_ID = os.environ['CHAT_ID']
|
||||
GROUP_ID = os.environ['GROUP_ID']
|
||||
|
||||
|
||||
@pytest.mark.skipif(should_skip, reason="No environment variables configured")
|
||||
@ -56,6 +56,30 @@ class TestTeleBot:
|
||||
time.sleep(1)
|
||||
assert msg.text == 'got'
|
||||
|
||||
def test_message_handler_lambda(self):
|
||||
bot = telebot.TeleBot('')
|
||||
msg = self.create_text_message(r'lambda_text')
|
||||
|
||||
@bot.message_handler(func=lambda message: r'lambda' in message.text)
|
||||
def command_url(message):
|
||||
msg.text = 'got'
|
||||
|
||||
bot.process_new_messages([msg])
|
||||
time.sleep(1)
|
||||
assert msg.text == 'got'
|
||||
|
||||
def test_message_handler_lambda_fail(self):
|
||||
bot = telebot.TeleBot('')
|
||||
msg = self.create_text_message(r'text')
|
||||
|
||||
@bot.message_handler(func=lambda message: r'lambda' in message.text)
|
||||
def command_url(message):
|
||||
msg.text = 'got'
|
||||
|
||||
bot.process_new_messages([msg])
|
||||
time.sleep(1)
|
||||
assert not msg.text == 'got'
|
||||
|
||||
def test_message_handler_reg_fail(self):
|
||||
bot = telebot.TeleBot('')
|
||||
msg = self.create_text_message(r'web.telegram.org/')
|
||||
@ -78,6 +102,15 @@ class TestTeleBot:
|
||||
ret_msg = tb.send_message(CHAT_ID, markdown, parse_mode="Markdown")
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_message_with_disable_notification(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markdown = """
|
||||
*bold text*
|
||||
_italic text_
|
||||
[text](URL)
|
||||
"""
|
||||
ret_msg = tb.send_message(CHAT_ID, markdown, parse_mode="Markdown", disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_file(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
@ -88,18 +121,48 @@ class TestTeleBot:
|
||||
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_file_dis_noti(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_document(CHAT_ID, file_data, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_file_caption(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_document(CHAT_ID, file_data, caption="Test")
|
||||
assert ret_msg.message_id
|
||||
|
||||
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_video(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_video_dis_noti(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_video_more_params(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data, 1)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_video_more_params_dis_noti(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data, 1, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_file_exception(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
try:
|
||||
@ -118,6 +181,15 @@ class TestTeleBot:
|
||||
ret_msg = tb.send_photo(CHAT_ID, ret_msg.photo[0].file_id)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_photo_dis_noti(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_photo(CHAT_ID, file_data)
|
||||
assert ret_msg.message_id
|
||||
|
||||
ret_msg = tb.send_photo(CHAT_ID, ret_msg.photo[0].file_id, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_audio(self):
|
||||
file_data = open('./test_data/record.mp3', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
@ -126,18 +198,83 @@ class TestTeleBot:
|
||||
assert ret_msg.audio.performer == 'eternnoir'
|
||||
assert ret_msg.audio.title == 'pyTelegram'
|
||||
|
||||
def test_send_audio_dis_noti(self):
|
||||
file_data = open('./test_data/record.mp3', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram', disable_notification=True)
|
||||
assert ret_msg.content_type == 'audio'
|
||||
assert ret_msg.audio.performer == 'eternnoir'
|
||||
assert ret_msg.audio.title == 'pyTelegram'
|
||||
|
||||
def test_send_voice(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data)
|
||||
assert ret_msg.voice.mime_type == 'audio/ogg'
|
||||
|
||||
def test_send_voice_dis_noti(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data, disable_notification=True)
|
||||
assert ret_msg.voice.mime_type == 'audio/ogg'
|
||||
|
||||
def test_get_file(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data)
|
||||
file_id = ret_msg.voice.file_id
|
||||
file_info = tb.get_file(file_id)
|
||||
assert file_info.file_id == file_id
|
||||
|
||||
def test_get_file_dis_noti(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data, disable_notification=True)
|
||||
file_id = ret_msg.voice.file_id
|
||||
file_info = tb.get_file(file_id)
|
||||
assert file_info.file_id == file_id
|
||||
|
||||
def test_send_message(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_message(CHAT_ID, text)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_message_dis_noti(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_message_with_markup(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
markup.add(types.KeyboardButton("1"))
|
||||
markup.add(types.KeyboardButton("2"))
|
||||
ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True, reply_markup=markup)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_message_with_markup_use_string(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
markup.add("1")
|
||||
markup.add("2")
|
||||
markup.add("3")
|
||||
markup.add("4")
|
||||
ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True, reply_markup=markup)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_message_with_inlinemarkup(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com"))
|
||||
markup.add(types.InlineKeyboardButton("Yahoo", url="http://www.yahoo.com"))
|
||||
ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True, reply_markup=markup)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_forward_message(self):
|
||||
text = 'CI forward_message Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
@ -145,6 +282,13 @@ class TestTeleBot:
|
||||
ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id)
|
||||
assert ret_msg.forward_from
|
||||
|
||||
def test_forward_message_dis_noti(self):
|
||||
text = 'CI forward_message Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_message(CHAT_ID, text)
|
||||
ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id, disable_notification=True)
|
||||
assert ret_msg.forward_from
|
||||
|
||||
def test_reply_to(self):
|
||||
text = 'CI reply_to Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
@ -173,6 +317,68 @@ class TestTeleBot:
|
||||
assert int(ret_msg.location.longitude) == int(lon)
|
||||
assert int(ret_msg.location.latitude) == int(lat)
|
||||
|
||||
def test_send_location_dis_noti(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
lat = 26.3875591
|
||||
lon = -161.2901042
|
||||
ret_msg = tb.send_location(CHAT_ID, lat, lon, disable_notification=True)
|
||||
assert int(ret_msg.location.longitude) == int(lon)
|
||||
assert int(ret_msg.location.latitude) == int(lat)
|
||||
|
||||
def test_send_venue(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
lat = 26.3875591
|
||||
lon = -161.2901042
|
||||
ret_msg = tb.send_venue(CHAT_ID, lat, lon, "Test Venue", "1123 Test Venue address")
|
||||
assert ret_msg.venue.title == "Test Venue"
|
||||
|
||||
def test_send_venue_dis_noti(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
lat = 26.3875591
|
||||
lon = -161.2901042
|
||||
ret_msg = tb.send_venue(CHAT_ID, lat, lon, "Test Venue", "1123 Test Venue address", disable_notification=True)
|
||||
assert ret_msg.venue.title == "Test Venue"
|
||||
|
||||
def test_Chat(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
me = tb.get_me()
|
||||
msg = tb.send_message(CHAT_ID, 'Test')
|
||||
assert me.id == msg.from_user.id
|
||||
assert msg.chat.id == int(CHAT_ID)
|
||||
|
||||
def test_edit_message_text(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_message(CHAT_ID, 'Test')
|
||||
new_msg = tb.edit_message_text('Edit test', chat_id=CHAT_ID, message_id=msg.message_id)
|
||||
assert new_msg.text == 'Edit test'
|
||||
|
||||
def test_get_chat(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ch = tb.get_chat(GROUP_ID)
|
||||
assert str(ch.id) == GROUP_ID
|
||||
|
||||
def test_get_chat_administrators(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
cas = tb.get_chat_administrators(GROUP_ID)
|
||||
assert len(cas) > 0
|
||||
|
||||
def test_get_chat_members_count(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
cn = tb.get_chat_members_count(GROUP_ID)
|
||||
assert cn > 1
|
||||
|
||||
def test_edit_markup(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com"))
|
||||
markup.add(types.InlineKeyboardButton("Yahoo", url="http://www.yahoo.com"))
|
||||
ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True, reply_markup=markup)
|
||||
markup.add(types.InlineKeyboardButton("Google2", url="http://www.google.com"))
|
||||
markup.add(types.InlineKeyboardButton("Yahoo2", url="http://www.yahoo.com"))
|
||||
new_msg = tb.edit_message_reply_markup(chat_id=CHAT_ID, message_id=ret_msg.message_id, reply_markup=markup)
|
||||
assert new_msg.message_id
|
||||
|
||||
def create_text_message(self, text):
|
||||
params = {'text': text}
|
||||
chat = types.User(11, 'test')
|
||||
@ -189,4 +395,3 @@ class TestTeleBot:
|
||||
def test_not_string(self):
|
||||
i1 = 10
|
||||
assert not util.is_string(i1)
|
||||
|
||||
|
@ -12,13 +12,13 @@ def test_json_user():
|
||||
|
||||
|
||||
def test_json_message():
|
||||
jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"chat":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"date":1435296025,"text":"HIHI"}'
|
||||
jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"chat":{"id":1734,"first_name":"F","type":"private","last_name":"Wa","username":"oir"},"date":1435296025,"text":"HIHI"}'
|
||||
msg = types.Message.de_json(jsonstring)
|
||||
assert msg.text == 'HIHI'
|
||||
|
||||
|
||||
def test_json_message_group():
|
||||
json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG"},"chat":{"id":-866,"title":"\u4ea4"},"date":1435303157,"text":"HIHI"}'
|
||||
json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG"},"chat":{"id":-866,"type":"private","title":"\u4ea4"},"date":1435303157,"text":"HIHI"}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.text == 'HIHI'
|
||||
assert len(msg.chat.title) != 0
|
||||
@ -39,7 +39,7 @@ def test_json_Document():
|
||||
|
||||
|
||||
def test_json_Message_Audio():
|
||||
json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd"},"chat":{"id":10834,"first_name":"dd","last_name":"dd","username":"dd"},"date":1439978364,"audio":{"duration":1,"mime_type":"audio\/mpeg","title":"pyTelegram","performer":"eternnoir","file_id":"BQADBQADDH1JaB8-1KyWUss2-Ag","file_size":20096}}'
|
||||
json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd"},"chat":{"id":10834,"first_name":"dd","type":"private","type":"private","last_name":"dd","username":"dd"},"date":1439978364,"audio":{"duration":1,"mime_type":"audio\/mpeg","title":"pyTelegram","performer":"eternnoir","file_id":"BQADBQADDH1JaB8-1KyWUss2-Ag","file_size":20096}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.audio.duration == 1
|
||||
assert msg.content_type == 'audio'
|
||||
@ -48,7 +48,7 @@ def test_json_Message_Audio():
|
||||
|
||||
|
||||
def test_json_Message_Sticker():
|
||||
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.sticker.height == 368
|
||||
assert msg.sticker.thumb.height == 60
|
||||
@ -56,7 +56,7 @@ def test_json_Message_Sticker():
|
||||
|
||||
|
||||
def test_json_Message_Sticker_without_thumb():
|
||||
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.sticker.height == 368
|
||||
assert msg.sticker.thumb == None
|
||||
@ -64,21 +64,21 @@ def test_json_Message_Sticker_without_thumb():
|
||||
|
||||
|
||||
def test_json_Message_Document():
|
||||
json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10,"first_name":"Fd","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}'
|
||||
json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.document.file_name == 'Text File'
|
||||
assert msg.content_type == 'document'
|
||||
|
||||
|
||||
def test_json_Message_Photo():
|
||||
json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}'
|
||||
json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert len(msg.photo) == 3
|
||||
assert msg.content_type == 'photo'
|
||||
|
||||
|
||||
def test_json_Message_Video():
|
||||
json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}'
|
||||
json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":109734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.video
|
||||
assert msg.video.duration == 3
|
||||
@ -87,7 +87,7 @@ def test_json_Message_Video():
|
||||
|
||||
|
||||
def test_json_Message_Location():
|
||||
json_string = r'{"message_id":102,"from":{"id":108734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":1089734,"first_name":"dd","last_name":"dd","username":"dd"},"date":1535482469,"location":{"longitude":127.479471,"latitude":26.090577}}'
|
||||
json_string = r'{"message_id":102,"from":{"id":108734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":1089734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1535482469,"location":{"longitude":127.479471,"latitude":26.090577}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.location.latitude == 26.090577
|
||||
assert msg.content_type == 'location'
|
||||
@ -114,9 +114,44 @@ def test_json_voice():
|
||||
assert voice.file_size == 10481
|
||||
|
||||
def test_json_update():
|
||||
json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}'
|
||||
json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","type":"private","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}'
|
||||
update = types.Update.de_json(json_string)
|
||||
assert update.update_id == 938203
|
||||
assert update.message.message_id == 241
|
||||
assert update.message.from_user.id == 9734
|
||||
|
||||
def test_json_chat():
|
||||
json_string = r'{"id": -111111,"title": "Test Title","type": "group"}'
|
||||
chat = types.Chat.de_json(json_string)
|
||||
assert chat.id == -111111
|
||||
assert chat.type == 'group'
|
||||
assert chat.title == 'Test Title'
|
||||
|
||||
def test_InlineQueryResultCachedPhoto():
|
||||
iq = types.InlineQueryResultCachedPhoto('aaa', 'Fileid')
|
||||
json_str = iq.to_json()
|
||||
assert 'aa' in json_str
|
||||
assert 'Fileid' in json_str
|
||||
assert 'caption' not in json_str
|
||||
|
||||
|
||||
def test_InlineQueryResultCachedPhoto_with_title():
|
||||
iq = types.InlineQueryResultCachedPhoto('aaa', 'Fileid', title='Title')
|
||||
json_str = iq.to_json()
|
||||
assert 'aa' in json_str
|
||||
assert 'Fileid' in json_str
|
||||
assert 'Title' in json_str
|
||||
assert 'caption' not in json_str
|
||||
|
||||
def test_InlineQueryResultCachedPhoto_with_markup():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com"))
|
||||
markup.add(types.InlineKeyboardButton("Yahoo", url="http://www.yahoo.com"))
|
||||
iq = types.InlineQueryResultCachedPhoto('aaa', 'Fileid', title='Title', reply_markup=markup)
|
||||
json_str = iq.to_json()
|
||||
assert 'aa' in json_str
|
||||
assert 'Fileid' in json_str
|
||||
assert 'Title' in json_str
|
||||
assert 'caption' not in json_str
|
||||
assert 'reply_markup' in json_str
|
||||
|
||||
|
Reference in New Issue
Block a user