mirror of
https://github.com/eternnoir/pyTelegramBotAPI.git
synced 2023-08-10 21:12:57 +03:00
Compare commits
128 Commits
latest_rel
...
3.6.6
Author | SHA1 | Date | |
---|---|---|---|
2c57c5c01c | |||
07b82dc9b0 | |||
a850a0d94f | |||
d4f1444503 | |||
bab9f7bbb9 | |||
d9ace2adc8 | |||
2b3c86b647 | |||
e419214b49 | |||
fe6959c38e | |||
7dd53b1396 | |||
421118d9d8 | |||
cf69a06ab8 | |||
8ac6e664c5 | |||
12dbcb56d3 | |||
a46975d038 | |||
494b535a91 | |||
74f75884f3 | |||
4eae469528 | |||
41f7c07959 | |||
35ea2a2b7e | |||
522b2b487b | |||
5035e0ce80 | |||
7061091c1c | |||
5c199bd246 | |||
44dd89881d | |||
76dbb05259 | |||
578a9383b2 | |||
85093bded5 | |||
f251def304 | |||
2b822f782d | |||
8bc5b74495 | |||
70426ac274 | |||
a3a2bd5793 | |||
c3b6ee9dc0 | |||
4079772fd3 | |||
9547a8d7b1 | |||
c8b2b14157 | |||
3d5ef5b1d8 | |||
776a699a8d | |||
78afd045d8 | |||
06faed887c | |||
bc855f7610 | |||
893d5386c5 | |||
909d570dca | |||
424c77fd2c | |||
333949683f | |||
fa038c2e42 | |||
d61de35a32 | |||
13df7b5908 | |||
1de356dcc3 | |||
47e6dfd6bc | |||
3c890a7846 | |||
17971ff48b | |||
b989b7601b | |||
8c574a786a | |||
7e5f51e4ab | |||
018e4597a2 | |||
7df6b3d4c9 | |||
4facc5f7d7 | |||
4bcfc34a50 | |||
b1d5cb2129 | |||
00c8dcc19b | |||
ed7e33b4c6 | |||
74a952846c | |||
e99fb8f84f | |||
49aee14fca | |||
9267da205d | |||
9c79ba2f87 | |||
3be21ae361 | |||
42343c3a7f | |||
5a102ed8fa | |||
e1e109bef1 | |||
b5a217013a | |||
3ba9799b98 | |||
91f213ff34 | |||
8f55460924 | |||
f6b999053d | |||
99ff104a3f | |||
662c2c8797 | |||
72a0199a2f | |||
989cae597b | |||
5dd88f8223 | |||
28111bdf4e | |||
10ec897fb5 | |||
ffe3a0c3d7 | |||
f5f48db6ba | |||
183230e927 | |||
7957bc45a8 | |||
373d4d37ff | |||
0d0e37dae5 | |||
36d088dfbf | |||
9ae20b4815 | |||
e761e1e1d9 | |||
cb0256b37d | |||
ff3cbaf45b | |||
d231b1fbaa | |||
7f47f11444 | |||
0422e62f65 | |||
82e252ec46 | |||
c11a9f810c | |||
dadcd5a577 | |||
afc9abc269 | |||
e01f17e3a0 | |||
48e6757686 | |||
8495229ce1 | |||
3b60f7ca67 | |||
a1930e05c2 | |||
bc067662dc | |||
518c49f23a | |||
903b1dfd50 | |||
2e199a5684 | |||
55302cb972 | |||
f47653d2e4 | |||
94b4a25980 | |||
afac177d7d | |||
2637e29dbe | |||
6d180e30f0 | |||
e2ed4cf065 | |||
8b2dea1d56 | |||
41e31de034 | |||
ae074fd5c9 | |||
60596a95b8 | |||
44531bcedf | |||
8aa8fa5986 | |||
f0e64b3653 | |||
8444ea588a | |||
b2f376a906 | |||
af991ea76e |
@ -5,6 +5,7 @@ python:
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
- "pypy"
|
||||
- "pypy3"
|
||||
install: "pip install -r requirements.txt"
|
||||
|
19
README.md
19
README.md
@ -291,8 +291,8 @@ tb.send_video(chat_id, "FILEID")
|
||||
|
||||
# sendVideoNote
|
||||
videonote = open('/tmp/videonote.mp4', 'rb')
|
||||
tb.send_video(chat_id, videonote)
|
||||
tb.send_video(chat_id, "FILEID")
|
||||
tb.send_video_note(chat_id, videonote)
|
||||
tb.send_video_note(chat_id, "FILEID")
|
||||
|
||||
# sendLocation
|
||||
tb.send_location(chat_id, lat, lon)
|
||||
@ -500,16 +500,13 @@ You can use proxy for request. `apihelper.proxy` object will use by call `reques
|
||||
```python
|
||||
from telebot import apihelper
|
||||
|
||||
apihelper.proxy = {'http', 'http://10.10.1.10:3128'}
|
||||
apihelper.proxy = {'http':'http://10.10.1.10:3128'}
|
||||
```
|
||||
|
||||
If you want to use socket5 proxy you need install dependency `pip install requests[socks]`.
|
||||
If you want to use socket5 proxy you need install dependency `pip install requests[socks]` and make sure, that you have the latest version of `gunicorn`, `PySocks`, `pyTelegramBotAPI`, `requests` and `urllib3`.
|
||||
|
||||
```python
|
||||
proxies = {
|
||||
'http': 'socks5://user:pass@host:port',
|
||||
'https': 'socks5://user:pass@host:port'
|
||||
}
|
||||
apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
|
||||
```
|
||||
|
||||
|
||||
@ -584,5 +581,11 @@ Get help. Discuss. Chat.
|
||||
* [RastreioBot](http://t.me/RastreioBot) ([source](https://github.com/GabrielRF/RastreioBot)) by *GabrielRF* - Bot used to track packages on the Brazilian Mail Service.
|
||||
* [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram))
|
||||
* [Spbu4UBot](http://t.me/Spbu4UBot)([link](https://github.com/EeOneDown/spbu4u)) by *EeOneDown* - Bot with timetables for SPbU students.
|
||||
* [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students.
|
||||
* [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free.
|
||||
* [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages’ vocabulary.
|
||||
* [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audiosamles and try to name the performer of the song.
|
||||
* [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon)
|
||||
|
||||
|
||||
Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh.
|
||||
|
807
README.rst
807
README.rst
@ -1,807 +0,0 @@
|
||||
#
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<p align="center">
|
||||
|
||||
pyTelegramBotAPI
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<p align="center">
|
||||
|
||||
A simple, but extensible Python implementation for the `Telegram Bot
|
||||
API <https://core.telegram.org/bots/api>`__.
|
||||
|
||||
|Download Month| |Build Status| |Download Month|
|
||||
|
||||
- `Getting started. <#getting-started>`__
|
||||
- `Writing your first bot <#writing-your-first-bot>`__
|
||||
|
||||
- `Prerequisites <#prerequisites>`__
|
||||
- `A simple echo bot <#a-simple-echo-bot>`__
|
||||
|
||||
- `General API Documentation <#general-api-documentation>`__
|
||||
|
||||
- `Types <#types>`__
|
||||
- `Methods <#methods>`__
|
||||
- `General use of the API <#general-use-of-the-api>`__
|
||||
- `Message handlers <#message-handlers>`__
|
||||
- `Callback Query handlers <#callback-query-handler>`__
|
||||
- `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>`__
|
||||
- `Controlling the amount of Threads used by
|
||||
TeleBot <#controlling-the-amount-of-threads-used-by-telebot>`__
|
||||
- `The listener mechanism <#the-listener-mechanism>`__
|
||||
- `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.
|
||||
----------------
|
||||
|
||||
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:
|
||||
|
||||
- Installation using pip (a Python package manager)\*:
|
||||
|
||||
::
|
||||
|
||||
$ pip install pyTelegramBotAPI
|
||||
|
||||
- Installation from source (requires git):
|
||||
|
||||
::
|
||||
|
||||
$ git clone https://github.com/eternnoir/pyTelegramBotAPI.git
|
||||
$ cd pyTelegramBotAPI
|
||||
$ python setup.py install
|
||||
|
||||
It is generally recommended to use the first option.
|
||||
|
||||
\*\*While the API is production-ready, it is still under development and
|
||||
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
|
||||
token ``TOKEN``. Furthermore, you have basic knowledge of the Python
|
||||
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``
|
||||
(``send_message``, ``send_document`` etc.) and several ways to listen
|
||||
for incoming messages.
|
||||
|
||||
Create a file called ``echo_bot.py``. Then, open the file and create an
|
||||
instance of the TeleBot class.
|
||||
|
||||
.. code:: python
|
||||
|
||||
import telebot
|
||||
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
|
||||
*Note: Make sure to actually replace TOKEN with your own API token.*
|
||||
|
||||
After that declaration, we need to register some so-called message
|
||||
handlers. Message handlers define filters which a message must pass. If
|
||||
a message passes the filter, the decorated function is called and the
|
||||
incoming message is passed as an argument.
|
||||
|
||||
Let's define a message handler which handles incoming ``/start`` and
|
||||
``/help`` commands.
|
||||
|
||||
.. code:: python
|
||||
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message, "Howdy, how are you doing?")
|
||||
|
||||
A function which is decorated by a message handler **can have an
|
||||
arbitrary name, however, it must have only one parameter (the
|
||||
message)**.
|
||||
|
||||
Let's add another handler:
|
||||
|
||||
.. code:: python
|
||||
|
||||
@bot.message_handler(func=lambda m: True)
|
||||
def echo_all(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
This one echoes all incoming text messages back to the sender. It uses a
|
||||
lambda function to test a message. If the lambda returns True, the
|
||||
message is handled by the decorated function. Since we want all messages
|
||||
to be handled by this function, we simply always return True.
|
||||
|
||||
*Note: all handlers are tested in the order in which they were declared*
|
||||
|
||||
We now have a basic bot which replies a static message to "/start" and
|
||||
"/help" commands and which echoes the rest of the sent messages. To
|
||||
start the bot, add the following to our source file:
|
||||
|
||||
.. code:: python
|
||||
|
||||
bot.polling()
|
||||
|
||||
Alright, that's it! Our source file now looks like this:
|
||||
|
||||
.. code:: python
|
||||
|
||||
import telebot
|
||||
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message, "Howdy, how are you doing?")
|
||||
|
||||
@bot.message_handler(func=lambda message: True)
|
||||
def echo_all(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
bot.polling()
|
||||
|
||||
To start the bot, simply open up a terminal and enter
|
||||
``python echo_bot.py`` to run the bot! Test it by sending commands
|
||||
('/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
|
||||
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_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
|
||||
located in the TeleBot class. They are renamed to follow common Python
|
||||
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 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
|
||||
|
||||
import telebot
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
|
||||
# Handles all text messages that contains the commands '/start' or '/help'.
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def handle_start_help(message):
|
||||
pass
|
||||
|
||||
# Handles all sent documents and audio files
|
||||
@bot.message_handler(content_types=['document', 'audio'])
|
||||
def handle_docs_audio(message):
|
||||
pass
|
||||
|
||||
# Handles all text messages that match the regular expression
|
||||
@bot.message_handler(regexp="SOME_REGEXP")
|
||||
def handle_message(message):
|
||||
pass
|
||||
|
||||
#Handles all messages for which the lambda returns True
|
||||
@bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document'])
|
||||
def handle_text_doc(message):
|
||||
pass
|
||||
|
||||
#Which could also be defined as:
|
||||
def test_message(message):
|
||||
return message.document.mime_type == 'text/plan'
|
||||
|
||||
@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**
|
||||
|
||||
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
|
||||
|
||||
import telebot
|
||||
|
||||
TOKEN = '<token_string>'
|
||||
tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object
|
||||
|
||||
# Upon calling this function, TeleBot starts polling the Telegram servers for new messages.
|
||||
# - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers
|
||||
# - interval: True/False (default False) - The interval between polling requests
|
||||
# Note: Editing this parameter harms the bot's response time
|
||||
# - timeout: integer (default 20) - Timeout in seconds for long polling.
|
||||
tb.polling(none_stop=False, interval=0, timeout=20)
|
||||
|
||||
# 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):
|
||||
|
||||
# sendMessage
|
||||
tb.send_message(chatid, text)
|
||||
|
||||
# forwardMessage
|
||||
tb.forward_message(to_chat_id, from_chat_id, message_id)
|
||||
|
||||
# All send_xyz functions which can take a file as an argument, can also take a file_id instead of a file.
|
||||
# sendPhoto
|
||||
photo = open('/tmp/photo.png', 'rb')
|
||||
tb.send_photo(chat_id, photo)
|
||||
tb.send_photo(chat_id, "FILEID")
|
||||
|
||||
# sendAudio
|
||||
audio = open('/tmp/audio.mp3', 'rb')
|
||||
tb.send_audio(chat_id, audio)
|
||||
tb.send_audio(chat_id, "FILEID")
|
||||
|
||||
## sendAudio with duration, performer and title.
|
||||
tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram')
|
||||
|
||||
# sendVoice
|
||||
voice = open('/tmp/voice.ogg', 'rb')
|
||||
tb.send_voice(chat_id, voice)
|
||||
tb.send_voice(chat_id, "FILEID")
|
||||
|
||||
# sendDocument
|
||||
doc = open('/tmp/file.txt', 'rb')
|
||||
tb.send_document(chat_id, doc)
|
||||
tb.send_document(chat_id, "FILEID")
|
||||
|
||||
# sendSticker
|
||||
sti = open('/tmp/sti.webp', 'rb')
|
||||
tb.send_sticker(chat_id, sti)
|
||||
tb.send_sticker(chat_id, "FILEID")
|
||||
|
||||
# sendVideo
|
||||
video = open('/tmp/video.mp4', 'rb')
|
||||
tb.send_video(chat_id, video)
|
||||
tb.send_video(chat_id, "FILEID")
|
||||
|
||||
# sendLocation
|
||||
tb.send_location(chat_id, lat, lon)
|
||||
|
||||
# sendChatAction
|
||||
# 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``,
|
||||
``ReplyKeyboardRemove`` or ``ForceReply``, which are defined in types.py.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from telebot import types
|
||||
|
||||
# Using the ReplyKeyboardMarkup class
|
||||
# It's constructor can take the following optional arguments:
|
||||
# - resize_keyboard: True/False (default False)
|
||||
# - one_time_keyboard: True/False (default False)
|
||||
# - selective: True/False (default False)
|
||||
# - row_width: integer (default 3)
|
||||
# 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)
|
||||
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()
|
||||
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:
|
||||
|
||||
.. figure:: https://pp.vk.me/c624430/v624430512/473e5/_mxxW7FPe4U.jpg
|
||||
:alt: ReplyKeyboardMarkup
|
||||
|
||||
ReplyKeyboardMarkup
|
||||
|
||||
.. code:: python
|
||||
|
||||
# ReplyKeyboardRemove: hides a previously sent ReplyKeyboardMarkup
|
||||
# Takes an optional selective argument (True/False, default False)
|
||||
markup = types.ReplyKeyboardRemove(selective=False)
|
||||
tb.send_message(chat_id, message, reply_markup=markup)
|
||||
|
||||
.. code:: python
|
||||
|
||||
# ForceReply: forces a user to reply to a message
|
||||
# Takes an optional selective argument (True/False, default False)
|
||||
markup = types.ForceReply(selective=False)
|
||||
tb.send_message(chat_id, "Send me another word:", reply_markup=markup)
|
||||
|
||||
ForceReply:
|
||||
|
||||
.. figure:: https://pp.vk.me/c624430/v624430512/473ec/602byyWUHcs.jpg
|
||||
:alt: 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
|
||||
up you bot **significantly**, but it has unwanted side effects if used
|
||||
without caution. To enable this behaviour, create an instance of
|
||||
AsyncTeleBot instead of TeleBot.
|
||||
|
||||
.. code:: python
|
||||
|
||||
tb = telebot.AsyncTeleBot("TOKEN")
|
||||
|
||||
Now, every function that calls the Telegram API is executed in a
|
||||
separate Thread. The functions are modified to return an AsyncTask
|
||||
instance (defined in util.py). Using AsyncTeleBot allows you to do the
|
||||
following:
|
||||
|
||||
.. code:: python
|
||||
|
||||
import telebot
|
||||
|
||||
tb = telebot.AsyncTeleBot("TOKEN")
|
||||
task = tb.get_me() # Execute an API call
|
||||
# Do some other operations...
|
||||
a = 0
|
||||
for a in range(100):
|
||||
a += 10
|
||||
|
||||
result = task.wait() # Get the result of the execution
|
||||
|
||||
*Note: if you execute send\_xyz functions after eachother without
|
||||
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
|
||||
need to split the message in multiples. Here is how to do that using the
|
||||
API:
|
||||
|
||||
.. code:: python
|
||||
|
||||
from telebot import util
|
||||
large_text = open("large_text.txt", "rb").read()
|
||||
|
||||
# Split the text each 3000 characters.
|
||||
# split_string returns a list with the splitted text.
|
||||
splitted_text = util.split_string(large_text, 3000)
|
||||
for text in splitted_text:
|
||||
tb.send_message(chat_id, text)
|
||||
|
||||
Controlling the amount of Threads used by TeleBot
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The TeleBot constructor takes the following optional arguments:
|
||||
|
||||
- create\_threads: True/False (default True). A flag to indicate
|
||||
whether TeleBot should execute message handlers on it's polling
|
||||
Thread.
|
||||
- num\_threads: integer (default 4). Controls the amount of
|
||||
WorkerThreads created for the internal thread pool that TeleBot uses
|
||||
to execute message handlers. Is not used when create\_threads is
|
||||
False.
|
||||
|
||||
The listener mechanism
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As an alternative to the message handlers, one can also register a
|
||||
function as a listener to TeleBot. Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
def handle_messages(messages):
|
||||
for message in messsages:
|
||||
# Do something with the message
|
||||
bot.reply_to(message, 'Hi')
|
||||
|
||||
bot.set_update_listener(handle_messages)
|
||||
bot.polling()
|
||||
|
||||
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
|
||||
~~~~~~~
|
||||
|
||||
You can use the Telebot module logger to log debug info about Telebot.
|
||||
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
|
||||
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?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Telegram Bot API support new type Chat for message.chat.
|
||||
|
||||
- Check the ``type`` attribute in ``Chat`` object:
|
||||
- \`\`\`python if message.chat.type == “private”: # private chat
|
||||
message
|
||||
|
||||
if message.chat.type == “group”: # group chat message
|
||||
|
||||
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
|
||||
- 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>`__
|
||||
- `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*
|
||||
- `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
|
@ -1,4 +1,4 @@
|
||||
# This example show how to write an inline mode telegramt bot use pyTelegramBotAPI.
|
||||
# This example show how to write an inline mode telegram bot use pyTelegramBotAPI.
|
||||
import telebot
|
||||
import time
|
||||
import sys
|
||||
|
27
examples/inline_keyboard_example.py
Normal file
27
examples/inline_keyboard_example.py
Normal file
@ -0,0 +1,27 @@
|
||||
# This example show how to use inline keyboards and process button presses
|
||||
import telebot
|
||||
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
|
||||
TELEGRAM_TOKEN = '<TOKEN>'
|
||||
|
||||
bot = telebot.TeleBot(TELEGRAM_TOKEN)
|
||||
|
||||
def gen_markup():
|
||||
markup = InlineKeyboardMarkup()
|
||||
markup.row_width = 2
|
||||
markup.add(InlineKeyboardButton("Yes", callback_data=f"cb_yes"),
|
||||
InlineKeyboardButton("No", callback_data=f"cb_no"))
|
||||
return markup
|
||||
|
||||
@bot.callback_query_handler(func=lambda call: True)
|
||||
def callback_query(call):
|
||||
if call.data == "cb_yes":
|
||||
bot.answer_callback_query(call.id, "Answer is Yes")
|
||||
elif call.data == "cb_no":
|
||||
bot.answer_callback_query(call.id, "Answer is No")
|
||||
|
||||
@bot.message_handler(func=lambda message: True)
|
||||
def message_handler(message):
|
||||
bot.send_message(message.chat.id, "Yes/no?", reply_markup=gen_markup())
|
||||
|
||||
bot.polling(none_stop=True)
|
@ -75,4 +75,14 @@ def process_sex_step(message):
|
||||
bot.reply_to(message, 'oooops')
|
||||
|
||||
|
||||
# Enable saving next step handlers to file "./.handlers-saves/step.save".
|
||||
# Delay=2 means that after any change in next step handlers (e.g. calling register_next_step_handler())
|
||||
# saving will hapen after delay 2 seconds.
|
||||
bot.enable_save_next_step_handlers(delay=2)
|
||||
|
||||
# Load next_step_handlers from save file (default "./.handlers-saves/step.save")
|
||||
# WARNING It will work only if enable_save_next_step_handlers was called!
|
||||
bot.load_next_step_handlers()
|
||||
|
||||
|
||||
bot.polling()
|
||||
|
@ -7,6 +7,7 @@
|
||||
import flask
|
||||
import telebot
|
||||
import logging
|
||||
import time
|
||||
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
@ -73,6 +74,8 @@ def echo_message(message):
|
||||
# Remove webhook, it fails sometimes the set if there is a previous webhook
|
||||
bot.remove_webhook()
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
# Set webhook
|
||||
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
|
||||
certificate=open(WEBHOOK_SSL_CERT, 'r'))
|
||||
|
@ -1,29 +1,35 @@
|
||||
import telebot
|
||||
import os
|
||||
|
||||
import telebot
|
||||
from flask import Flask, request
|
||||
|
||||
bot = telebot.TeleBot('<api_token>')
|
||||
|
||||
TOKEN = '<api_token>'
|
||||
bot = telebot.TeleBot(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'])
|
||||
|
||||
@server.route('/' + TOKEN, methods=['POST'])
|
||||
def getMessage():
|
||||
bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))])
|
||||
return "!", 200
|
||||
|
||||
|
||||
@server.route("/")
|
||||
def webhook():
|
||||
bot.remove_webhook()
|
||||
bot.set_webhook(url="https://herokuProject_url/bot")
|
||||
bot.set_webhook(url='https://your_heroku_project.com/' + TOKEN)
|
||||
return "!", 200
|
||||
|
||||
server.run(host="0.0.0.0", port=os.environ.get('PORT', 5000))
|
||||
server = Flask(__name__)
|
||||
|
||||
if __name__ == "__main__":
|
||||
server.run(host="0.0.0.0", port=int(os.environ.get('PORT', 5000)))
|
||||
|
11
setup.py
11
setup.py
@ -2,14 +2,15 @@
|
||||
from setuptools import setup
|
||||
from io import open
|
||||
|
||||
def readme():
|
||||
with open('README.rst', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
def read(filename):
|
||||
with open(filename, encoding='utf-8') as file:
|
||||
return file.read()
|
||||
|
||||
setup(name='pyTelegramBotAPI',
|
||||
version='3.5.0',
|
||||
version='3.6.6',
|
||||
description='Python Telegram bot api. ',
|
||||
long_description=readme(),
|
||||
long_description=read('README.md'),
|
||||
long_description_content_type="text/markdown",
|
||||
author='eternnoir',
|
||||
author_email='eternnoir@gmail.com',
|
||||
url='https://github.com/eternnoir/pyTelegramBotAPI',
|
||||
|
@ -7,6 +7,9 @@ import re
|
||||
import sys
|
||||
import six
|
||||
|
||||
import os
|
||||
import pickle
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('TeleBot')
|
||||
@ -27,6 +30,70 @@ Module : telebot
|
||||
"""
|
||||
|
||||
|
||||
class Handler:
|
||||
"""
|
||||
Class for (next step|reply) handlers
|
||||
"""
|
||||
def __init__(self, callback, *args, **kwargs):
|
||||
self.callback = callback
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __getitem__(self, item):
|
||||
return getattr(self, item)
|
||||
|
||||
|
||||
class Saver:
|
||||
"""
|
||||
Class for saving (next step|reply) handlers
|
||||
"""
|
||||
def __init__(self, handlers, filename, delay):
|
||||
self.handlers = handlers
|
||||
self.filename = filename
|
||||
self.delay = delay
|
||||
self.timer = threading.Timer(delay, self.save_handlers)
|
||||
|
||||
def start_save_timer(self):
|
||||
if not self.timer.is_alive():
|
||||
if self.delay <= 0:
|
||||
self.save_handlers()
|
||||
else:
|
||||
self.timer = threading.Timer(self.delay, self.save_handlers)
|
||||
self.timer.start()
|
||||
|
||||
def save_handlers(self):
|
||||
self.dump_handlers(self.handlers, self.filename)
|
||||
|
||||
def load_handlers(self, filename, del_file_after_loading=True):
|
||||
tmp = self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading)
|
||||
if tmp is not None:
|
||||
self.handlers.update(tmp)
|
||||
|
||||
@staticmethod
|
||||
def dump_handlers(handlers, filename, file_mode="wb"):
|
||||
dirs = filename.rsplit('/', maxsplit=1)[0]
|
||||
os.makedirs(dirs, exist_ok=True)
|
||||
|
||||
with open(filename + ".tmp", file_mode) as file:
|
||||
pickle.dump(handlers, file)
|
||||
|
||||
if os.path.isfile(filename):
|
||||
os.remove(filename)
|
||||
|
||||
os.rename(filename + ".tmp", filename)
|
||||
|
||||
@staticmethod
|
||||
def return_load_handlers(filename, del_file_after_loading=True):
|
||||
if os.path.isfile(filename) and os.path.getsize(filename) > 0:
|
||||
with open(filename, "rb") as file:
|
||||
handlers = pickle.load(file)
|
||||
|
||||
if del_file_after_loading:
|
||||
os.remove(filename)
|
||||
|
||||
return handlers
|
||||
|
||||
|
||||
class TeleBot:
|
||||
""" This is TeleBot Class
|
||||
Methods:
|
||||
@ -70,6 +137,7 @@ class TeleBot:
|
||||
:param token: bot API token
|
||||
:return: Telebot object.
|
||||
"""
|
||||
|
||||
self.token = token
|
||||
self.update_listener = []
|
||||
self.skip_pending = skip_pending
|
||||
@ -78,13 +146,14 @@ class TeleBot:
|
||||
self.last_update_id = 0
|
||||
self.exc_info = None
|
||||
|
||||
self.message_subscribers_messages = []
|
||||
self.message_subscribers_callbacks = []
|
||||
self.message_subscribers_lock = threading.Lock()
|
||||
# key: message_id, value: handler list
|
||||
self.reply_handlers = {}
|
||||
|
||||
# key: chat_id, value: handler list
|
||||
self.message_subscribers_next_step = {}
|
||||
self.pre_message_subscribers_next_step = {}
|
||||
self.next_step_handlers = {}
|
||||
|
||||
self.next_step_saver = None
|
||||
self.reply_saver = None
|
||||
|
||||
self.message_handlers = []
|
||||
self.edited_message_handlers = []
|
||||
@ -100,6 +169,54 @@ class TeleBot:
|
||||
if self.threaded:
|
||||
self.worker_pool = util.ThreadPool(num_threads=num_threads)
|
||||
|
||||
def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"):
|
||||
"""
|
||||
Enable saving next step handlers (by default saving disable)
|
||||
|
||||
:param delay: Delay between changes in handlers and saving
|
||||
:param filename: Filename of save file
|
||||
"""
|
||||
self.next_step_saver = Saver(self.next_step_handlers, filename, delay)
|
||||
|
||||
def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"):
|
||||
"""
|
||||
Enable saving reply handlers (by default saving disable)
|
||||
|
||||
:param delay: Delay between changes in handlers and saving
|
||||
:param filename: Filename of save file
|
||||
"""
|
||||
self.reply_saver = Saver(self.reply_handlers, filename, delay)
|
||||
|
||||
def disable_save_next_step_handlers(self):
|
||||
"""
|
||||
Disable saving next step handlers (by default saving disable)
|
||||
"""
|
||||
self.next_step_saver = None
|
||||
|
||||
def disable_save_reply_handlers(self):
|
||||
"""
|
||||
Disable saving next step handlers (by default saving disable)
|
||||
"""
|
||||
self.reply_saver = None
|
||||
|
||||
def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_file_after_loading=True):
|
||||
"""
|
||||
Load next step handlers from save file
|
||||
|
||||
:param filename: Filename of the file where handlers was saved
|
||||
:param del_file_after_loading: Is passed True, after loading save file will be deleted
|
||||
"""
|
||||
self.next_step_saver.load_handlers(filename, del_file_after_loading)
|
||||
|
||||
def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True):
|
||||
"""
|
||||
Load reply handlers from save file
|
||||
|
||||
:param filename: Filename of the file where handlers was saved
|
||||
:param del_file_after_loading: Is passed True, after loading save file will be deleted
|
||||
"""
|
||||
self.reply_saver.load_handlers(filename)
|
||||
|
||||
def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None):
|
||||
return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates)
|
||||
|
||||
@ -213,11 +330,10 @@ class TeleBot:
|
||||
self.process_new_shipping_query(new_shipping_querys)
|
||||
|
||||
def process_new_messages(self, new_messages):
|
||||
self._append_pre_next_step_handler()
|
||||
self._notify_next_handlers(new_messages)
|
||||
self._notify_reply_handlers(new_messages)
|
||||
self.__notify_update(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)
|
||||
@ -247,6 +363,15 @@ class TeleBot:
|
||||
for listener in self.update_listener:
|
||||
self._exec_task(listener, new_messages)
|
||||
|
||||
def infinity_polling(self, *args, **kwargs):
|
||||
while not self.__stop_polling.is_set():
|
||||
try:
|
||||
self.polling(*args, **kwargs)
|
||||
except Exception as e:
|
||||
time.sleep(5)
|
||||
pass
|
||||
logger.info("Break infinity polling")
|
||||
|
||||
def polling(self, none_stop=False, interval=0, timeout=20):
|
||||
"""
|
||||
This function creates a new Thread that calls an internal __retrieve_updates function.
|
||||
@ -302,9 +427,9 @@ class TeleBot:
|
||||
except KeyboardInterrupt:
|
||||
logger.info("KeyboardInterrupt received.")
|
||||
self.__stop_polling.set()
|
||||
polling_thread.stop()
|
||||
break
|
||||
|
||||
polling_thread.stop()
|
||||
logger.info('Stopped polling.')
|
||||
|
||||
def __non_threaded_polling(self, none_stop=False, interval=0, timeout=3):
|
||||
@ -341,6 +466,11 @@ class TeleBot:
|
||||
def stop_polling(self):
|
||||
self.__stop_polling.set()
|
||||
|
||||
def stop_bot(self):
|
||||
self.stop_polling()
|
||||
if self.worker_pool:
|
||||
self.worker_pool.close()
|
||||
|
||||
def set_update_listener(self, listener):
|
||||
self.update_listener.append(listener)
|
||||
|
||||
@ -351,6 +481,9 @@ class TeleBot:
|
||||
def get_file(self, file_id):
|
||||
return types.File.de_json(apihelper.get_file(self.token, file_id))
|
||||
|
||||
def get_file_url(self, file_id):
|
||||
return apihelper.get_file_url(self.token, file_id)
|
||||
|
||||
def download_file(self, file_path):
|
||||
return apihelper.download_file(self.token, file_path)
|
||||
|
||||
@ -478,7 +611,7 @@ class TeleBot:
|
||||
|
||||
def delete_message(self, chat_id, message_id):
|
||||
"""
|
||||
Use this method to delete message. Returns True on success.
|
||||
Use this method to delete message. Returns True on success.
|
||||
:param chat_id: in which chat to delete
|
||||
:param message_id: which message to delete
|
||||
:return: API reply.
|
||||
@ -486,24 +619,25 @@ class TeleBot:
|
||||
return apihelper.delete_message(self.token, chat_id, message_id)
|
||||
|
||||
def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
parse_mode=None, disable_notification=None):
|
||||
"""
|
||||
Use this method to send photos.
|
||||
:param disable_notification:
|
||||
:param chat_id:
|
||||
:param photo:
|
||||
:param caption:
|
||||
:param parse_mode
|
||||
:param reply_to_message_id:
|
||||
:param reply_markup:
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup,
|
||||
disable_notification))
|
||||
parse_mode, disable_notification))
|
||||
|
||||
def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None,
|
||||
reply_to_message_id=None,
|
||||
reply_markup=None, disable_notification=None, timeout=None):
|
||||
reply_to_message_id=None, reply_markup=None, parse_mode=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
|
||||
@ -511,16 +645,17 @@ class TeleBot:
|
||||
:param duration:Duration of the audio in seconds
|
||||
:param performer:Performer
|
||||
:param title:Track name
|
||||
:param parse_mode
|
||||
:param reply_to_message_id:If the message is a reply, ID of the original message
|
||||
:param reply_markup:
|
||||
:return: Message
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_audio(self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id,
|
||||
reply_markup, disable_notification, timeout))
|
||||
reply_markup, parse_mode, disable_notification, timeout))
|
||||
|
||||
def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None):
|
||||
parse_mode=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.
|
||||
@ -528,25 +663,28 @@ class TeleBot:
|
||||
:param duration:Duration of sent audio in seconds
|
||||
:param reply_to_message_id:
|
||||
:param reply_markup:
|
||||
:param parse_mode
|
||||
:return: Message
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_voice(self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup,
|
||||
disable_notification, timeout))
|
||||
parse_mode, disable_notification, timeout))
|
||||
|
||||
def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None):
|
||||
parse_mode=None, disable_notification=None, timeout=None):
|
||||
"""
|
||||
Use this method to send general files.
|
||||
:param chat_id:
|
||||
:param data:
|
||||
:param reply_to_message_id:
|
||||
:param reply_markup:
|
||||
:param parse_mode:
|
||||
:param disable_notification:
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup,
|
||||
disable_notification, timeout, caption=caption))
|
||||
parse_mode, disable_notification, timeout, caption=caption))
|
||||
|
||||
def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None,
|
||||
timeout=None):
|
||||
@ -563,20 +701,22 @@ class TeleBot:
|
||||
disable_notification, timeout))
|
||||
|
||||
def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None):
|
||||
parse_mode=None, supports_streaming=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
|
||||
:param data: InputFile or String : Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram server
|
||||
:param duration: Integer : Duration of sent video in seconds
|
||||
:param caption: String : Video caption (may also be used when resending videos by file_id).
|
||||
:param parse_mode:
|
||||
:param supports_streaming:
|
||||
:param reply_to_message_id:
|
||||
:param reply_markup:
|
||||
:return:
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup,
|
||||
disable_notification, timeout))
|
||||
parse_mode, supports_streaming, disable_notification, timeout))
|
||||
|
||||
def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None):
|
||||
@ -639,7 +779,7 @@ class TeleBot:
|
||||
:return:
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.edit_message_live_location(self, latitude, longitude, chat_id, message_id,
|
||||
apihelper.edit_message_live_location(self.token, latitude, longitude, chat_id, message_id,
|
||||
inline_message_id, reply_markup))
|
||||
|
||||
def stop_message_live_location(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
|
||||
@ -653,7 +793,7 @@ class TeleBot:
|
||||
:return:
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.stop_message_live_location(self, chat_id, message_id, inline_message_id, reply_markup))
|
||||
apihelper.stop_message_live_location(self.token, chat_id, message_id, inline_message_id, reply_markup))
|
||||
|
||||
def send_venue(self, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None,
|
||||
reply_to_message_id=None, reply_markup=None):
|
||||
@ -858,6 +998,12 @@ class TeleBot:
|
||||
return result
|
||||
return types.Message.de_json(result)
|
||||
|
||||
def edit_message_media(self, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
|
||||
result = apihelper.edit_message_media(self.token, media, chat_id, message_id, inline_message_id, 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):
|
||||
result = apihelper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup)
|
||||
if type(result) == bool:
|
||||
@ -888,8 +1034,8 @@ class TeleBot:
|
||||
def send_invoice(self, chat_id, title, description, invoice_payload, provider_token, currency, prices,
|
||||
start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None,
|
||||
need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None,
|
||||
is_flexible=None,
|
||||
disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None):
|
||||
is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None,
|
||||
provider_data=None):
|
||||
result = apihelper.send_invoice(self.token, chat_id, title, description, invoice_payload, provider_token,
|
||||
currency, prices, start_parameter, photo_url, photo_size, photo_width,
|
||||
photo_height,
|
||||
@ -903,9 +1049,10 @@ class TeleBot:
|
||||
def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=None):
|
||||
return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message)
|
||||
|
||||
def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
|
||||
def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None,
|
||||
parse_mode=None, reply_markup=None):
|
||||
result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id,
|
||||
reply_markup)
|
||||
parse_mode, reply_markup)
|
||||
if type(result) == bool:
|
||||
return result
|
||||
return types.Message.de_json(result)
|
||||
@ -961,7 +1108,6 @@ class TeleBot:
|
||||
def get_sticker_set(self, name):
|
||||
"""
|
||||
Use this method to get a sticker set. On success, a StickerSet object is returned.
|
||||
:param token:
|
||||
:param name:
|
||||
:return:
|
||||
"""
|
||||
@ -996,7 +1142,7 @@ class TeleBot:
|
||||
return apihelper.create_new_sticker_set(self.token, user_id, name, title, png_sticker, emojis, contains_masks,
|
||||
mask_position)
|
||||
|
||||
def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position):
|
||||
def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position=None):
|
||||
"""
|
||||
Use this method to add a new sticker to a set created by the bot. Returns True on success.
|
||||
:param user_id:
|
||||
@ -1025,51 +1171,80 @@ class TeleBot:
|
||||
"""
|
||||
return apihelper.delete_sticker_from_set(self.token, sticker)
|
||||
|
||||
def register_for_reply(self, message, callback):
|
||||
def register_for_reply(self, message, callback, *args, **kwargs):
|
||||
"""
|
||||
Registers a callback function to be notified when a reply to `message` arrives.
|
||||
|
||||
Warning: `message` must be sent with reply_markup=types.ForceReply(), otherwise TeleBot will not be able to see
|
||||
the difference between a reply to `message` and an ordinary message.
|
||||
Warning: In case `callback` as lambda function, saving reply handlers will not work.
|
||||
|
||||
:param message: The message for which we are awaiting a reply.
|
||||
:param callback: The callback function to be called when a reply arrives. Must accept one `message`
|
||||
parameter, which will contain the replied message.
|
||||
"""
|
||||
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()
|
||||
message_id = message.message_id
|
||||
self.register_for_reply_by_message_id(message_id, callback, *args, **kwargs)
|
||||
|
||||
def _notify_message_subscribers(self, new_messages):
|
||||
def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs):
|
||||
"""
|
||||
Registers a callback function to be notified when a reply to `message` arrives.
|
||||
|
||||
Warning: In case `callback` as lambda function, saving reply handlers will not work.
|
||||
|
||||
:param message_id: The id of the message for which we are awaiting a reply.
|
||||
:param callback: The callback function to be called when a reply arrives. Must accept one `message`
|
||||
parameter, which will contain the replied message.
|
||||
"""
|
||||
if message_id in self.reply_handlers.keys():
|
||||
self.reply_handlers[message_id].append(Handler(callback, *args, **kwargs))
|
||||
else:
|
||||
self.reply_handlers[message_id] = [Handler(callback, *args, **kwargs)]
|
||||
if self.reply_saver is not None:
|
||||
self.reply_saver.start_save_timer()
|
||||
|
||||
def _notify_reply_handlers(self, new_messages):
|
||||
for message in new_messages:
|
||||
if not message.reply_to_message:
|
||||
continue
|
||||
if hasattr(message, "reply_to_message") and message.reply_to_message is not None:
|
||||
reply_msg_id = message.reply_to_message.message_id
|
||||
if reply_msg_id in self.reply_handlers.keys():
|
||||
handlers = self.reply_handlers[reply_msg_id]
|
||||
for handler in handlers:
|
||||
self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"])
|
||||
self.reply_handlers.pop(reply_msg_id)
|
||||
if self.reply_saver is not None:
|
||||
self.reply_saver.start_save_timer()
|
||||
|
||||
reply_msg_id = message.reply_to_message.message_id
|
||||
if reply_msg_id in self.message_subscribers_messages:
|
||||
index = self.message_subscribers_messages.index(reply_msg_id)
|
||||
self.message_subscribers_callbacks[index](message)
|
||||
|
||||
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):
|
||||
def register_next_step_handler(self, message, callback, *args, **kwargs):
|
||||
"""
|
||||
Registers a callback function to be notified when new message arrives after `message`.
|
||||
|
||||
:param message: The message for which we want to handle new message after that in same chat.
|
||||
Warning: In case `callback` as lambda function, saving next step handlers will not work.
|
||||
|
||||
:param message: The message for which we want to handle new message in the same chat.
|
||||
:param callback: The callback function which next new message arrives.
|
||||
:param args: Args to pass in callback func
|
||||
:param kwargs: Args to pass in callback func
|
||||
"""
|
||||
chat_id = message.chat.id
|
||||
if chat_id in self.pre_message_subscribers_next_step:
|
||||
self.pre_message_subscribers_next_step[chat_id].append(callback)
|
||||
self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs)
|
||||
|
||||
def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwargs):
|
||||
"""
|
||||
Registers a callback function to be notified when new message arrives after `message`.
|
||||
|
||||
Warning: In case `callback` as lambda function, saving next step handlers will not work.
|
||||
|
||||
:param chat_id: The chat for which we want to handle new message.
|
||||
:param callback: The callback function which next new message arrives.
|
||||
:param args: Args to pass in callback func
|
||||
:param kwargs: Args to pass in callback func
|
||||
"""
|
||||
if chat_id in self.next_step_handlers.keys():
|
||||
self.next_step_handlers[chat_id].append(Handler(callback, *args, **kwargs))
|
||||
else:
|
||||
self.pre_message_subscribers_next_step[chat_id] = [callback]
|
||||
self.next_step_handlers[chat_id] = [Handler(callback, *args, **kwargs)]
|
||||
|
||||
if self.next_step_saver is not None:
|
||||
self.next_step_saver.start_save_timer()
|
||||
|
||||
def clear_step_handler(self, message):
|
||||
"""
|
||||
@ -1078,26 +1253,60 @@ class TeleBot:
|
||||
:param message: The message for which we want to handle new message after that in same chat.
|
||||
"""
|
||||
chat_id = message.chat.id
|
||||
self.pre_message_subscribers_next_step[chat_id] = []
|
||||
self.clear_step_handler_by_chat_id(chat_id)
|
||||
|
||||
def _notify_message_next_handler(self, new_messages):
|
||||
for message in new_messages:
|
||||
def clear_step_handler_by_chat_id(self, chat_id):
|
||||
"""
|
||||
Clears all callback functions registered by register_next_step_handler().
|
||||
|
||||
:param chat_id: The chat for which we want to clear next step handlers
|
||||
"""
|
||||
self.next_step_handlers[chat_id] = []
|
||||
|
||||
if self.next_step_saver is not None:
|
||||
self.next_step_saver.start_save_timer()
|
||||
|
||||
def clear_reply_handlers(self, message):
|
||||
"""
|
||||
Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id().
|
||||
|
||||
:param message: The message for which we want to clear reply handlers
|
||||
"""
|
||||
message_id = message.message_id
|
||||
self.clear_reply_handlers_by_message_id(message_id)
|
||||
|
||||
def clear_reply_handlers_by_message_id(self, message_id):
|
||||
"""
|
||||
Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id().
|
||||
|
||||
:param message_id: The message id for which we want to clear reply handlers
|
||||
"""
|
||||
self.reply_handlers[message_id] = []
|
||||
|
||||
if self.reply_saver is not None:
|
||||
self.reply_saver.start_save_timer()
|
||||
|
||||
def _notify_next_handlers(self, new_messages):
|
||||
i = 0
|
||||
while i < len(new_messages):
|
||||
message = new_messages[i]
|
||||
chat_id = message.chat.id
|
||||
if chat_id in self.message_subscribers_next_step:
|
||||
handlers = self.message_subscribers_next_step[chat_id]
|
||||
for handler in handlers:
|
||||
self._exec_task(handler, message)
|
||||
self.message_subscribers_next_step.pop(chat_id, None)
|
||||
was_poped = False
|
||||
if chat_id in self.next_step_handlers.keys():
|
||||
handlers = self.next_step_handlers.pop(chat_id, None)
|
||||
if (handlers):
|
||||
for handler in handlers:
|
||||
self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"])
|
||||
new_messages.pop(i) # removing message that detects with next_step_handler
|
||||
was_poped = True
|
||||
if self.next_step_saver is not None:
|
||||
self.next_step_saver.start_save_timer()
|
||||
if (not was_poped):
|
||||
i += 1
|
||||
|
||||
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 _build_handler_dict(self, handler, **filters):
|
||||
@staticmethod
|
||||
def _build_handler_dict(handler, **filters):
|
||||
return {
|
||||
'function': handler,
|
||||
'filters': filters
|
||||
@ -1263,7 +1472,8 @@ class TeleBot:
|
||||
|
||||
return True
|
||||
|
||||
def _test_filter(self, filter, filter_value, message):
|
||||
@staticmethod
|
||||
def _test_filter(filter, filter_value, message):
|
||||
test_cases = {
|
||||
'content_types': lambda msg: msg.content_type in filter_value,
|
||||
'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE),
|
||||
@ -1275,9 +1485,6 @@ class TeleBot:
|
||||
|
||||
def _notify_command_handlers(self, handlers, new_messages):
|
||||
for message in new_messages:
|
||||
# if message has next step handler, dont exec command handlers
|
||||
if hasattr(message, 'chat') and message.chat and (message.chat.id in self.message_subscribers_next_step):
|
||||
continue
|
||||
for message_handler in handlers:
|
||||
if self._test_message_handler(message_handler, message):
|
||||
self._exec_task(message_handler['function'], message)
|
||||
@ -1288,230 +1495,260 @@ class AsyncTeleBot(TeleBot):
|
||||
def __init__(self, *args, **kwargs):
|
||||
TeleBot.__init__(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save", del_file_after_loading=True):
|
||||
return TeleBot.enable_save_next_step_handlers(self, delay, filename, del_file_after_loading)
|
||||
|
||||
@util.async_dec()
|
||||
def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save", del_file_after_loading=True):
|
||||
return TeleBot.enable_save_reply_handlers(self, delay, filename, del_file_after_loading)
|
||||
|
||||
@util.async_dec()
|
||||
def disable_save_next_step_handlers(self):
|
||||
return TeleBot.disable_save_next_step_handlers(self)
|
||||
|
||||
@util.async_dec()
|
||||
def disable_save_reply_handlers(self):
|
||||
return TeleBot.enable_save_reply_handlers(self)
|
||||
|
||||
@util.async_dec()
|
||||
def load_next_step_handlers(self, filename="./.handler-saves/step.save"):
|
||||
return TeleBot.load_next_step_handlers(self, filename)
|
||||
|
||||
@util.async_dec()
|
||||
def load_reply_handlers(self, filename="./.handler-saves/reply.save"):
|
||||
return TeleBot.load_reply_handlers(self, filename)
|
||||
|
||||
@util.async_dec()
|
||||
|
||||
@util.async_dec()
|
||||
def get_me(self):
|
||||
return TeleBot.get_me(self)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def get_file(self, *args):
|
||||
return TeleBot.get_file(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def download_file(self, *args):
|
||||
return TeleBot.download_file(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def get_user_profile_photos(self, *args, **kwargs):
|
||||
return TeleBot.get_user_profile_photos(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def get_chat(self, *args):
|
||||
return TeleBot.get_chat(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def leave_chat(self, *args):
|
||||
return TeleBot.leave_chat(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def get_chat_administrators(self, *args):
|
||||
return TeleBot.get_chat_administrators(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def get_chat_members_count(self, *args):
|
||||
return TeleBot.get_chat_members_count(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def set_chat_sticker_set(self, *args):
|
||||
return TeleBot.set_chat_sticker_set(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def delete_chat_sticker_set(self, *args):
|
||||
return TeleBot.delete_chat_sticker_set(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def get_chat_member(self, *args):
|
||||
return TeleBot.get_chat_member(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_message(self, *args, **kwargs):
|
||||
return TeleBot.send_message(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def forward_message(self, *args, **kwargs):
|
||||
return TeleBot.forward_message(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def delete_message(self, *args):
|
||||
return TeleBot.delete_message(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_photo(self, *args, **kwargs):
|
||||
return TeleBot.send_photo(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_audio(self, *args, **kwargs):
|
||||
return TeleBot.send_audio(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_voice(self, *args, **kwargs):
|
||||
return TeleBot.send_voice(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_document(self, *args, **kwargs):
|
||||
return TeleBot.send_document(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_sticker(self, *args, **kwargs):
|
||||
return TeleBot.send_sticker(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_video(self, *args, **kwargs):
|
||||
return TeleBot.send_video(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_video_note(self, *args, **kwargs):
|
||||
return TeleBot.send_video_note(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_media_group(self, *args, **kwargs):
|
||||
return TeleBot.send_media_group(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_location(self, *args, **kwargs):
|
||||
return TeleBot.send_location(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def edit_message_live_location(self, *args, **kwargs):
|
||||
return TeleBot.edit_message_live_location(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def stop_message_live_location(self, *args, **kwargs):
|
||||
return TeleBot.stop_message_live_location(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_venue(self, *args, **kwargs):
|
||||
return TeleBot.send_venue(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_contact(self, *args, **kwargs):
|
||||
return TeleBot.send_contact(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_chat_action(self, *args, **kwargs):
|
||||
return TeleBot.send_chat_action(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def kick_chat_member(self, *args, **kwargs):
|
||||
return TeleBot.kick_chat_member(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def unban_chat_member(self, *args):
|
||||
return TeleBot.unban_chat_member(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def restrict_chat_member(self, *args, **kwargs):
|
||||
return TeleBot.restrict_chat_member(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def promote_chat_member(self, *args, **kwargs):
|
||||
return TeleBot.promote_chat_member(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def export_chat_invite_link(self, *args):
|
||||
return TeleBot.export_chat_invite_link(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def set_chat_photo(self, *args):
|
||||
return TeleBot.set_chat_photo(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def delete_chat_photo(self, *args):
|
||||
return TeleBot.delete_chat_photo(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def set_chat_title(self, *args):
|
||||
return TeleBot.set_chat_title(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def set_chat_description(self, *args):
|
||||
return TeleBot.set_chat_description(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def pin_chat_message(self, *args, **kwargs):
|
||||
return TeleBot.pin_chat_message(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def unpin_chat_message(self, *args):
|
||||
return TeleBot.unpin_chat_message(self, *args)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def edit_message_text(self, *args, **kwargs):
|
||||
return TeleBot.edit_message_text(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def edit_message_media(self, *args, **kwargs):
|
||||
return TeleBot.edit_message_media(self, *args, **kwargs)
|
||||
|
||||
@util.async_dec()
|
||||
def edit_message_reply_markup(self, *args, **kwargs):
|
||||
return TeleBot.edit_message_reply_markup(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_game(self, *args, **kwargs):
|
||||
return TeleBot.send_game(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def set_game_score(self, *args, **kwargs):
|
||||
return TeleBot.set_game_score(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def get_game_high_scores(self, *args, **kwargs):
|
||||
return TeleBot.get_game_high_scores(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_invoice(self, *args, **kwargs):
|
||||
return TeleBot.send_invoice(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def answer_shipping_query(self, *args, **kwargs):
|
||||
return TeleBot.answer_shipping_query(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def answer_pre_checkout_query(self, *args, **kwargs):
|
||||
return TeleBot.answer_pre_checkout_query(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def edit_message_caption(self, *args, **kwargs):
|
||||
return TeleBot.edit_message_caption(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def answer_inline_query(self, *args, **kwargs):
|
||||
return TeleBot.answer_inline_query(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def answer_callback_query(self, *args, **kwargs):
|
||||
return TeleBot.answer_callback_query(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def send_sticker(self, *args, **kwargs):
|
||||
return TeleBot.send_sticker(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def get_sticker_set(self, *args, **kwargs):
|
||||
return TeleBot.get_sticker_set(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def upload_sticker_file(self, *args, **kwargs):
|
||||
return TeleBot.upload_sticker_file(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def create_new_sticker_set(self, *args, **kwargs):
|
||||
return TeleBot.create_new_sticker_set(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def add_sticker_to_set(self, *args, **kwargs):
|
||||
return TeleBot.add_sticker_to_set(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def set_sticker_position_in_set(self, *args, **kwargs):
|
||||
return TeleBot.set_sticker_position_in_set(self, *args, **kwargs)
|
||||
|
||||
@util.async()
|
||||
@util.async_dec()
|
||||
def delete_sticker_from_set(self, *args, **kwargs):
|
||||
return TeleBot.delete_sticker_from_set(self, *args, **kwargs)
|
||||
|
@ -27,8 +27,8 @@ CONNECT_TIMEOUT = 3.5
|
||||
READ_TIMEOUT = 9999
|
||||
|
||||
|
||||
def _get_req_session():
|
||||
return util.per_thread('req_session', lambda: requests.session())
|
||||
def _get_req_session(reset=False):
|
||||
return util.per_thread('req_session', lambda: requests.session(), reset)
|
||||
|
||||
|
||||
def _make_request(token, method_name, method='get', params=None, files=None, base_url=API_URL):
|
||||
@ -98,9 +98,14 @@ def get_file(token, file_id):
|
||||
return _make_request(token, method_url, params={'file_id': file_id})
|
||||
|
||||
|
||||
def get_file_url(token, file_id):
|
||||
method_url = r'getFile'
|
||||
return FILE_URL.format(token, get_file(token, file_id).file_path)
|
||||
|
||||
|
||||
def download_file(token, file_path):
|
||||
url = FILE_URL.format(token, file_path)
|
||||
result = _get_req_session().get(url)
|
||||
result = _get_req_session().get(url, proxies=proxy)
|
||||
if result.status_code != 200:
|
||||
msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \
|
||||
.format(result.status_code, result.reason, result.text)
|
||||
@ -236,7 +241,7 @@ def forward_message(token, chat_id, from_chat_id, message_id, disable_notificati
|
||||
|
||||
|
||||
def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
parse_mode=None, disable_notification=None):
|
||||
method_url = r'sendPhoto'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -250,6 +255,8 @@ 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 parse_mode:
|
||||
payload['parse_mode'] = parse_mode
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
@ -257,14 +264,14 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re
|
||||
|
||||
def send_media_group(token, chat_id, media, disable_notification=None, reply_to_message_id=None):
|
||||
method_url = r'sendMediaGroup'
|
||||
media_json = _convert_list_json_serializable(media)
|
||||
print(media_json)
|
||||
media_json, files = _convert_input_media_array(media)
|
||||
payload = {'chat_id': chat_id, 'media': media_json}
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
return _make_request(token, method_url, params=payload)
|
||||
return _make_request(token, method_url, params=payload, method='post' if files else 'get',
|
||||
files=files if files else None)
|
||||
|
||||
|
||||
def send_location(token, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, reply_markup=None,
|
||||
@ -272,7 +279,7 @@ def send_location(token, chat_id, latitude, longitude, live_period=None, reply_t
|
||||
method_url = r'sendLocation'
|
||||
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude}
|
||||
if live_period:
|
||||
payload['live_perion'] = live_period
|
||||
payload['live_period'] = live_period
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
@ -349,7 +356,7 @@ def send_chat_action(token, chat_id, action):
|
||||
|
||||
|
||||
def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None):
|
||||
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None):
|
||||
method_url = r'sendVideo'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -365,6 +372,10 @@ 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 parse_mode:
|
||||
payload['parse_mode'] = parse_mode
|
||||
if supports_streaming:
|
||||
payload['supports_streaming'] = supports_streaming
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
@ -373,7 +384,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
|
||||
|
||||
|
||||
def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None):
|
||||
parse_mode=None, disable_notification=None, timeout=None):
|
||||
method_url = r'sendVoice'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -389,6 +400,8 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if parse_mode:
|
||||
payload['parse_mode'] = parse_mode
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
@ -423,7 +436,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m
|
||||
|
||||
|
||||
def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None,
|
||||
reply_markup=None, disable_notification=None, timeout=None):
|
||||
reply_markup=None, parse_mode=None, disable_notification=None, timeout=None):
|
||||
method_url = r'sendAudio'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -443,6 +456,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if parse_mode:
|
||||
payload['parse_mode'] = parse_mode
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
@ -450,8 +465,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non
|
||||
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, disable_notification=None,
|
||||
timeout=None, caption=None):
|
||||
def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None,
|
||||
disable_notification=None, timeout=None, caption=None):
|
||||
method_url = get_method_by_type(data_type)
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -463,6 +478,8 @@ 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 parse_mode and data_type == 'document':
|
||||
payload['parse_mode'] = parse_mode
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
@ -604,7 +621,8 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message
|
||||
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):
|
||||
def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None,
|
||||
parse_mode=None, reply_markup=None):
|
||||
method_url = r'editMessageCaption'
|
||||
payload = {'caption': caption}
|
||||
if chat_id:
|
||||
@ -613,11 +631,28 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m
|
||||
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 reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
|
||||
method_url = r'editMessageMedia'
|
||||
media_json, file = _convert_input_media(media)
|
||||
payload = {'media': media_json}
|
||||
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, files=file, method='post' if file else 'get')
|
||||
|
||||
|
||||
def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
|
||||
method_url = r'editMessageReplyMarkup'
|
||||
payload = {}
|
||||
@ -827,7 +862,7 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None,
|
||||
payload['show_alert'] = show_alert
|
||||
if url:
|
||||
payload['url'] = url
|
||||
if cache_time:
|
||||
if cache_time is not None:
|
||||
payload['cache_time'] = cache_time
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
@ -836,7 +871,7 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per
|
||||
switch_pm_text=None, switch_pm_parameter=None):
|
||||
method_url = 'answerInlineQuery'
|
||||
payload = {'inline_query_id': inline_query_id, 'results': _convert_list_json_serializable(results)}
|
||||
if cache_time:
|
||||
if cache_time is not None:
|
||||
payload['cache_time'] = cache_time
|
||||
if is_personal:
|
||||
payload['is_personal'] = is_personal
|
||||
@ -917,10 +952,29 @@ def _convert_markup(markup):
|
||||
return markup
|
||||
|
||||
|
||||
def _convert_input_media(media):
|
||||
if isinstance(media, types.InputMedia):
|
||||
return media._convert_input_media()
|
||||
return None, None
|
||||
|
||||
|
||||
def _convert_input_media_array(array):
|
||||
media = []
|
||||
files = {}
|
||||
for input_media in array:
|
||||
if isinstance(input_media, types.InputMedia):
|
||||
media_dict = input_media.to_dic()
|
||||
if media_dict['media'].startswith('attach://'):
|
||||
key = media_dict['media'].replace('attach://', '')
|
||||
files[key] = input_media.media
|
||||
media.append(media_dict)
|
||||
return json.dumps(media), files
|
||||
|
||||
|
||||
def _no_encode(func):
|
||||
def wrapper(key, val):
|
||||
if key == 'filename':
|
||||
return '{0}={1}'.format(key, val)
|
||||
return u'{0}={1}'.format(key, val)
|
||||
else:
|
||||
return func(key, val)
|
||||
|
||||
|
268
telebot/types.py
268
telebot/types.py
@ -10,7 +10,7 @@ import six
|
||||
from telebot import util
|
||||
|
||||
|
||||
class JsonSerializable:
|
||||
class JsonSerializable(object):
|
||||
"""
|
||||
Subclasses of this class are guaranteed to be able to be converted to JSON format.
|
||||
All subclasses of this class must override to_json.
|
||||
@ -26,7 +26,7 @@ class JsonSerializable:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Dictionaryable:
|
||||
class Dictionaryable(object):
|
||||
"""
|
||||
Subclasses of this class are guaranteed to be able to be converted to dictionary.
|
||||
All subclasses of this class must override to_dic.
|
||||
@ -42,7 +42,7 @@ class Dictionaryable:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class JsonDeserializable:
|
||||
class JsonDeserializable(object):
|
||||
"""
|
||||
Subclasses of this class are guaranteed to be able to be created from a json-style dict or json formatted string.
|
||||
All subclasses of this class must override de_json.
|
||||
@ -127,7 +127,6 @@ class Update(JsonDeserializable):
|
||||
def __init__(self, update_id, message, edited_message, channel_post, edited_channel_post, inline_query,
|
||||
chosen_inline_result, callback_query, shipping_query, pre_checkout_query):
|
||||
self.update_id = update_id
|
||||
self.edited_message = edited_message
|
||||
self.message = message
|
||||
self.edited_message = edited_message
|
||||
self.channel_post = channel_post
|
||||
@ -274,6 +273,8 @@ class Message(JsonDeserializable):
|
||||
opts['reply_to_message'] = Message.de_json(obj['reply_to_message'])
|
||||
if 'edit_date' in obj:
|
||||
opts['edit_date'] = obj.get('edit_date')
|
||||
if 'media_group_id' in obj:
|
||||
opts['media_group_id'] = obj.get('media_group_id')
|
||||
if 'author_signature' in obj:
|
||||
opts['author_signature'] = obj.get('author_signature')
|
||||
if 'text' in obj:
|
||||
@ -333,10 +334,13 @@ class Message(JsonDeserializable):
|
||||
content_type = 'left_chat_member'
|
||||
if 'new_chat_title' in obj:
|
||||
opts['new_chat_title'] = obj['new_chat_title']
|
||||
content_type = 'new_chat_title'
|
||||
if 'new_chat_photo' in obj:
|
||||
opts['new_chat_photo'] = Message.parse_photo(obj['new_chat_photo'])
|
||||
content_type = 'new_chat_photo'
|
||||
if 'delete_chat_photo' in obj:
|
||||
opts['delete_chat_photo'] = obj['delete_chat_photo']
|
||||
content_type = 'delete_chat_photo'
|
||||
if 'group_chat_created' in obj:
|
||||
opts['group_chat_created'] = obj['group_chat_created']
|
||||
content_type = 'group_chat_created'
|
||||
@ -348,8 +352,10 @@ class Message(JsonDeserializable):
|
||||
content_type = 'channel_chat_created'
|
||||
if 'migrate_to_chat_id' in obj:
|
||||
opts['migrate_to_chat_id'] = obj['migrate_to_chat_id']
|
||||
content_type = 'migrate_to_chat_id'
|
||||
if 'migrate_from_chat_id' in obj:
|
||||
opts['migrate_from_chat_id'] = obj['migrate_from_chat_id']
|
||||
content_type = 'migrate_from_chat_id'
|
||||
if 'pinned_message' in obj:
|
||||
opts['pinned_message'] = Message.de_json(obj['pinned_message'])
|
||||
content_type = 'pinned_message'
|
||||
@ -359,7 +365,10 @@ class Message(JsonDeserializable):
|
||||
if 'successful_payment' in obj:
|
||||
opts['successful_payment'] = SuccessfulPayment.de_json(obj['successful_payment'])
|
||||
content_type = 'successful_payment'
|
||||
return cls(message_id, from_user, date, chat, content_type, opts)
|
||||
if 'connected_website' in obj:
|
||||
opts['connected_website'] = obj['connected_website']
|
||||
content_type = 'connected_website'
|
||||
return cls(message_id, from_user, date, chat, content_type, opts, json_string)
|
||||
|
||||
@classmethod
|
||||
def parse_chat(cls, chat):
|
||||
@ -382,7 +391,7 @@ class Message(JsonDeserializable):
|
||||
ret.append(MessageEntity.de_json(me))
|
||||
return ret
|
||||
|
||||
def __init__(self, message_id, from_user, date, chat, content_type, options):
|
||||
def __init__(self, message_id, from_user, date, chat, content_type, options, json_string):
|
||||
self.content_type = content_type
|
||||
self.message_id = message_id
|
||||
self.from_user = from_user
|
||||
@ -393,6 +402,7 @@ class Message(JsonDeserializable):
|
||||
self.forward_date = None
|
||||
self.reply_to_message = None
|
||||
self.edit_date = None
|
||||
self.media_group_id = None
|
||||
self.author_signature = None
|
||||
self.text = None
|
||||
self.entities = None
|
||||
@ -422,9 +432,74 @@ class Message(JsonDeserializable):
|
||||
self.pinned_message = None
|
||||
self.invoice = None
|
||||
self.successful_payment = None
|
||||
self.connected_website = None
|
||||
for key in options:
|
||||
setattr(self, key, options[key])
|
||||
self.json = json_string
|
||||
|
||||
def __html_text(self, text, entities):
|
||||
"""
|
||||
Author: @sviat9440
|
||||
Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username"
|
||||
|
||||
Example:
|
||||
message.html_text
|
||||
>> "<b>Test</b> parse <i>formatting</i>, <a href=\"https://example.com\">url</a>, <a href=\"tg://user?id=123456\">text_mention</a> and mention @username"
|
||||
|
||||
Cusom subs:
|
||||
You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity.
|
||||
Example:
|
||||
message.custom_subs = {"bold": "<strong class=\"example\">{text}</strong>", "italic": "<i class=\"example\">{text}</i>", "mention": "<a href={url}>{text}</a>"}
|
||||
message.html_text
|
||||
>> "<strong class=\"example\">Test</strong> parse <i class=\"example\">formatting</i>, <a href=\"https://example.com\">url</a> and <a href=\"tg://user?id=123456\">text_mention</a> and mention <a href=\"https://t.me/username\">@username</a>"
|
||||
"""
|
||||
|
||||
if not entities:
|
||||
return text
|
||||
_subs = {
|
||||
"bold": "<b>{text}</b>",
|
||||
"italic": "<i>{text}</i>",
|
||||
"pre": "<pre>{text}</pre>",
|
||||
"code": "<code>{text}</code>",
|
||||
"url": "<a href=\"{url}\">{text}</a>",
|
||||
"text_link": "<a href=\"{url}\">{text}</a>"
|
||||
}
|
||||
if hasattr(self, "custom_subs"):
|
||||
for type in self.custom_subs:
|
||||
_subs[type] = self.custom_subs[type]
|
||||
utf16_text = text.encode("utf-16-le")
|
||||
html_text = ""
|
||||
def func(text, type=None, url=None, user=None):
|
||||
text = text.decode("utf-16-le")
|
||||
if type == "text_mention":
|
||||
type = "url"
|
||||
url = "tg://user?id={0}".format(user.id)
|
||||
elif type == "mention":
|
||||
url = "https://t.me/{0}".format(text[1:])
|
||||
if not type or not _subs.get(type):
|
||||
return text
|
||||
subs = _subs.get(type)
|
||||
text = text.replace("&", "&").replace("<", "<").replace(">", ">")
|
||||
return subs.format(text=text, url=url)
|
||||
|
||||
offset = 0
|
||||
for entity in entities:
|
||||
if entity.offset > offset:
|
||||
html_text += func(utf16_text[offset * 2 : entity.offset * 2])
|
||||
offset = entity.offset
|
||||
html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user)
|
||||
offset += entity.length
|
||||
if offset * 2 < len(utf16_text):
|
||||
html_text += func(utf16_text[offset * 2:])
|
||||
return html_text
|
||||
|
||||
@property
|
||||
def html_text(self):
|
||||
return self.__html_text(self.text, self.entities)
|
||||
|
||||
@property
|
||||
def html_caption(self):
|
||||
return self.__html_text(self.caption, self.caption_entities)
|
||||
|
||||
class MessageEntity(JsonDeserializable):
|
||||
@classmethod
|
||||
@ -1116,7 +1191,7 @@ class InlineQueryResultArticle(JsonSerializable):
|
||||
|
||||
class InlineQueryResultPhoto(JsonSerializable):
|
||||
def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=None,
|
||||
description=None, caption=None, reply_markup=None, input_message_content=None):
|
||||
description=None, caption=None, parse_mode=None, reply_markup=None, input_message_content=None):
|
||||
"""
|
||||
Represents a link to a photo.
|
||||
:param id: Unique identifier for this result, 1-64 bytes
|
||||
@ -1127,6 +1202,8 @@ class InlineQueryResultPhoto(JsonSerializable):
|
||||
:param title: Title for the result.
|
||||
:param description: Short description of the result.
|
||||
:param caption: Caption of the photo to be sent, 0-200 characters.
|
||||
:param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or
|
||||
inline URLs in the media caption.
|
||||
:param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message
|
||||
:param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo
|
||||
:return:
|
||||
@ -1140,6 +1217,7 @@ class InlineQueryResultPhoto(JsonSerializable):
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@ -1155,6 +1233,8 @@ class InlineQueryResultPhoto(JsonSerializable):
|
||||
json_dict['description'] = self.description
|
||||
if self.caption:
|
||||
json_dict['caption'] = self.caption
|
||||
if self.parse_mode:
|
||||
json_dict['parse_mode'] = self.parse_mode
|
||||
if self.reply_markup:
|
||||
json_dict['reply_markup'] = self.reply_markup.to_dic()
|
||||
if self.input_message_content:
|
||||
@ -1211,7 +1291,7 @@ class InlineQueryResultGif(JsonSerializable):
|
||||
|
||||
class InlineQueryResultMpeg4Gif(JsonSerializable):
|
||||
def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None, title=None, caption=None,
|
||||
reply_markup=None, input_message_content=None, mpeg4_duration=None):
|
||||
parse_mode=None, reply_markup=None, input_message_content=None, mpeg4_duration=None):
|
||||
"""
|
||||
Represents a link to a video animation (H.264/MPEG-4 AVC video without sound).
|
||||
:param id: Unique identifier for this result, 1-64 bytes
|
||||
@ -1221,6 +1301,8 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
|
||||
:param mpeg4_height: Video height
|
||||
:param title: Title for the result
|
||||
:param caption: Caption of the MPEG-4 file to be sent, 0-200 characters
|
||||
:param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text
|
||||
or inline URLs in the media caption.
|
||||
:param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message
|
||||
:param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo
|
||||
:return:
|
||||
@ -1233,6 +1315,7 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
|
||||
self.thumb_url = thumb_url
|
||||
self.title = title
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
self.mpeg4_duration = mpeg4_duration
|
||||
@ -1247,6 +1330,8 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
|
||||
json_dict['title'] = self.title
|
||||
if self.caption:
|
||||
json_dict['caption'] = self.caption
|
||||
if self.parse_mode:
|
||||
json_dict['parse_mode'] = self.parse_mode
|
||||
if self.reply_markup:
|
||||
json_dict['reply_markup'] = self.reply_markup.to_dic()
|
||||
if self.input_message_content:
|
||||
@ -1258,8 +1343,8 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
|
||||
|
||||
class InlineQueryResultVideo(JsonSerializable):
|
||||
def __init__(self, id, video_url, mime_type, thumb_url, title,
|
||||
caption=None, video_width=None, video_height=None, video_duration=None, description=None,
|
||||
reply_markup=None, input_message_content=None):
|
||||
caption=None, parse_mode=None, video_width=None, video_height=None, video_duration=None,
|
||||
description=None, reply_markup=None, input_message_content=None):
|
||||
"""
|
||||
Represents link to a page containing an embedded video player or a video file.
|
||||
:param id: Unique identifier for this result, 1-64 bytes
|
||||
@ -1267,6 +1352,8 @@ class InlineQueryResultVideo(JsonSerializable):
|
||||
:param mime_type: Mime type of the content of video url, “text/html” or “video/mp4”
|
||||
:param thumb_url: URL of the thumbnail (jpeg only) for the video
|
||||
:param title: Title for the result
|
||||
:param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or
|
||||
inline URLs in the media caption.
|
||||
:param video_width: Video width
|
||||
:param video_height: Video height
|
||||
:param video_duration: Video duration in seconds
|
||||
@ -1283,6 +1370,7 @@ class InlineQueryResultVideo(JsonSerializable):
|
||||
self.thumb_url = thumb_url
|
||||
self.title = title
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.description = description
|
||||
self.input_message_content = input_message_content
|
||||
self.reply_markup = reply_markup
|
||||
@ -1300,6 +1388,8 @@ class InlineQueryResultVideo(JsonSerializable):
|
||||
json_dict['description'] = self.description
|
||||
if self.caption:
|
||||
json_dict['caption'] = self.caption
|
||||
if self.parse_mode:
|
||||
json_dict['parse_mode'] = self.parse_mode
|
||||
if self.reply_markup:
|
||||
json_dict['reply_markup'] = self.reply_markup.to_dic()
|
||||
if self.input_message_content:
|
||||
@ -1308,13 +1398,14 @@ class InlineQueryResultVideo(JsonSerializable):
|
||||
|
||||
|
||||
class InlineQueryResultAudio(JsonSerializable):
|
||||
def __init__(self, id, audio_url, title, caption=None, performer=None, audio_duration=None, reply_markup=None,
|
||||
input_message_content=None):
|
||||
def __init__(self, id, audio_url, title, caption=None, parse_mode=None, performer=None, audio_duration=None,
|
||||
reply_markup=None, input_message_content=None):
|
||||
self.type = 'audio'
|
||||
self.id = id
|
||||
self.audio_url = audio_url
|
||||
self.title = title
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.performer = performer
|
||||
self.audio_duration = audio_duration
|
||||
self.reply_markup = reply_markup
|
||||
@ -1324,6 +1415,8 @@ class InlineQueryResultAudio(JsonSerializable):
|
||||
json_dict = {'type': self.type, 'id': self.id, 'audio_url': self.audio_url, 'title': self.title}
|
||||
if self.caption:
|
||||
json_dict['caption'] = self.caption
|
||||
if self.parse_mode:
|
||||
json_dict['parse_mode'] = self.parse_mode
|
||||
if self.performer:
|
||||
json_dict['performer'] = self.performer
|
||||
if self.audio_duration:
|
||||
@ -1336,13 +1429,14 @@ class InlineQueryResultAudio(JsonSerializable):
|
||||
|
||||
|
||||
class InlineQueryResultVoice(JsonSerializable):
|
||||
def __init__(self, id, voice_url, title, caption=None, performer=None, voice_duration=None, reply_markup=None,
|
||||
input_message_content=None):
|
||||
def __init__(self, id, voice_url, title, caption=None, parse_mode=None, performer=None, voice_duration=None,
|
||||
reply_markup=None, input_message_content=None):
|
||||
self.type = 'voice'
|
||||
self.id = id
|
||||
self.voice_url = voice_url
|
||||
self.title = title
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.performer = performer
|
||||
self.voice_duration = voice_duration
|
||||
self.reply_markup = reply_markup
|
||||
@ -1352,6 +1446,8 @@ class InlineQueryResultVoice(JsonSerializable):
|
||||
json_dict = {'type': self.type, 'id': self.id, 'voice_url': self.voice_url, 'title': self.title}
|
||||
if self.caption:
|
||||
json_dict['caption'] = self.caption
|
||||
if self.parse_mode:
|
||||
json_dict['parse_mode'] = self.parse_mode
|
||||
if self.performer:
|
||||
json_dict['performer'] = self.performer
|
||||
if self.voice_duration:
|
||||
@ -1364,14 +1460,15 @@ class InlineQueryResultVoice(JsonSerializable):
|
||||
|
||||
|
||||
class InlineQueryResultDocument(JsonSerializable):
|
||||
def __init__(self, id, title, document_url, mime_type, caption=None, description=None, reply_markup=None,
|
||||
input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None):
|
||||
def __init__(self, id, title, document_url, mime_type, caption=None, parse_mode=None, description=None,
|
||||
reply_markup=None, input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None):
|
||||
self.type = 'document'
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.document_url = document_url
|
||||
self.mime_type = mime_type
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.description = description
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
@ -1384,6 +1481,8 @@ class InlineQueryResultDocument(JsonSerializable):
|
||||
'mime_type': self.mime_type}
|
||||
if self.caption:
|
||||
json_dict['caption'] = self.caption
|
||||
if self.parse_mode:
|
||||
json_dict['parse_mode'] = self.parse_mode
|
||||
if self.description:
|
||||
json_dict['description'] = self.description
|
||||
if self.thumb_url:
|
||||
@ -1526,8 +1625,8 @@ class BaseInlineQueryResultCached(JsonSerializable):
|
||||
|
||||
|
||||
class InlineQueryResultCachedPhoto(BaseInlineQueryResultCached):
|
||||
def __init__(self, id, photo_file_id, title=None, description=None, caption=None, reply_markup=None,
|
||||
input_message_content=None):
|
||||
def __init__(self, id, photo_file_id, title=None, description=None, caption=None, parse_mode=None,
|
||||
reply_markup=None, input_message_content=None):
|
||||
BaseInlineQueryResultCached.__init__(self)
|
||||
self.type = 'photo'
|
||||
self.id = id
|
||||
@ -1538,10 +1637,11 @@ class InlineQueryResultCachedPhoto(BaseInlineQueryResultCached):
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
self.payload_dic['photo_file_id'] = photo_file_id
|
||||
self.payload_dic['parse_mode'] = parse_mode
|
||||
|
||||
|
||||
class InlineQueryResultCachedGif(BaseInlineQueryResultCached):
|
||||
def __init__(self, id, gif_file_id, title=None, description=None, caption=None, reply_markup=None,
|
||||
def __init__(self, id, gif_file_id, title=None, description=None, caption=None, parse_mode=None, reply_markup=None,
|
||||
input_message_content=None):
|
||||
BaseInlineQueryResultCached.__init__(self)
|
||||
self.type = 'gif'
|
||||
@ -1553,11 +1653,12 @@ class InlineQueryResultCachedGif(BaseInlineQueryResultCached):
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
self.payload_dic['gif_file_id'] = gif_file_id
|
||||
self.payload_dic['parse_mode'] = parse_mode
|
||||
|
||||
|
||||
class InlineQueryResultCachedMpeg4Gif(BaseInlineQueryResultCached):
|
||||
def __init__(self, id, mpeg4_file_id, title=None, description=None, caption=None, reply_markup=None,
|
||||
input_message_content=None):
|
||||
def __init__(self, id, mpeg4_file_id, title=None, description=None, caption=None, parse_mode=None,
|
||||
reply_markup=None, input_message_content=None):
|
||||
BaseInlineQueryResultCached.__init__(self)
|
||||
self.type = 'mpeg4_gif'
|
||||
self.id = id
|
||||
@ -1568,6 +1669,7 @@ class InlineQueryResultCachedMpeg4Gif(BaseInlineQueryResultCached):
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
self.payload_dic['mpeg4_file_id'] = mpeg4_file_id
|
||||
self.payload_dic['parse_mode'] = parse_mode
|
||||
|
||||
|
||||
class InlineQueryResultCachedSticker(BaseInlineQueryResultCached):
|
||||
@ -1582,7 +1684,7 @@ class InlineQueryResultCachedSticker(BaseInlineQueryResultCached):
|
||||
|
||||
|
||||
class InlineQueryResultCachedDocument(BaseInlineQueryResultCached):
|
||||
def __init__(self, id, document_file_id, title, description=None, caption=None, reply_markup=None,
|
||||
def __init__(self, id, document_file_id, title, description=None, caption=None, parse_mode=None, reply_markup=None,
|
||||
input_message_content=None):
|
||||
BaseInlineQueryResultCached.__init__(self)
|
||||
self.type = 'document'
|
||||
@ -1594,10 +1696,11 @@ class InlineQueryResultCachedDocument(BaseInlineQueryResultCached):
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
self.payload_dic['document_file_id'] = document_file_id
|
||||
self.payload_dic['parse_mode'] = parse_mode
|
||||
|
||||
|
||||
class InlineQueryResultCachedVideo(BaseInlineQueryResultCached):
|
||||
def __init__(self, id, video_file_id, title, description=None, caption=None, reply_markup=None,
|
||||
def __init__(self, id, video_file_id, title, description=None, caption=None, parse_mode=None, reply_markup=None,
|
||||
input_message_content=None):
|
||||
BaseInlineQueryResultCached.__init__(self)
|
||||
self.type = 'video'
|
||||
@ -1609,10 +1712,12 @@ class InlineQueryResultCachedVideo(BaseInlineQueryResultCached):
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
self.payload_dic['video_file_id'] = video_file_id
|
||||
self.payload_dic['parse_mode'] = parse_mode
|
||||
|
||||
|
||||
class InlineQueryResultCachedVoice(BaseInlineQueryResultCached):
|
||||
def __init__(self, id, voice_file_id, title, caption=None, reply_markup=None, input_message_content=None):
|
||||
def __init__(self, id, voice_file_id, title, caption=None, parse_mode=None, reply_markup=None,
|
||||
input_message_content=None):
|
||||
BaseInlineQueryResultCached.__init__(self)
|
||||
self.type = 'voice'
|
||||
self.id = id
|
||||
@ -1622,10 +1727,11 @@ class InlineQueryResultCachedVoice(BaseInlineQueryResultCached):
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
self.payload_dic['voice_file_id'] = voice_file_id
|
||||
self.payload_dic['parse_mode'] = parse_mode
|
||||
|
||||
|
||||
class InlineQueryResultCachedAudio(BaseInlineQueryResultCached):
|
||||
def __init__(self, id, audio_file_id, caption=None, reply_markup=None, input_message_content=None):
|
||||
def __init__(self, id, audio_file_id, caption=None, parse_mode=None, reply_markup=None, input_message_content=None):
|
||||
BaseInlineQueryResultCached.__init__(self)
|
||||
self.type = 'audio'
|
||||
self.id = id
|
||||
@ -1634,6 +1740,7 @@ class InlineQueryResultCachedAudio(BaseInlineQueryResultCached):
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
self.payload_dic['audio_file_id'] = audio_file_id
|
||||
self.payload_dic['parse_mode'] = parse_mode
|
||||
|
||||
|
||||
# Games
|
||||
@ -1809,7 +1916,7 @@ class ShippingOption(JsonSerializable):
|
||||
def add_price(self, *args):
|
||||
"""
|
||||
Add LabeledPrice to ShippingOption
|
||||
:param args: LabeledPrices
|
||||
:param args: LabeledPrices
|
||||
"""
|
||||
for price in args:
|
||||
self.prices.append(price)
|
||||
@ -1966,38 +2073,84 @@ class MaskPosition(JsonDeserializable, JsonSerializable):
|
||||
|
||||
# InputMedia
|
||||
|
||||
class InputMediaPhoto(JsonSerializable):
|
||||
def __init__(self, media, caption=None):
|
||||
self.type = "photo"
|
||||
class InputMedia(JsonSerializable):
|
||||
def __init__(self, type, media, caption=None, parse_mode=None):
|
||||
self.type = type
|
||||
self.media = media
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
|
||||
if util.is_string(self.media):
|
||||
self._media_name = ''
|
||||
self._media_dic = self.media
|
||||
else:
|
||||
self._media_name = util.generate_random_token()
|
||||
self._media_dic = 'attach://{0}'.format(self._media_name)
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(self.to_dic())
|
||||
|
||||
def to_dic(self):
|
||||
ret = {'type': self.type, 'media': self.media}
|
||||
ret = {'type': self.type, 'media': self._media_dic}
|
||||
if self.caption:
|
||||
ret['caption'] = self.caption
|
||||
if self.parse_mode:
|
||||
ret['parse_mode'] = self.parse_mode
|
||||
return ret
|
||||
|
||||
def _convert_input_media(self):
|
||||
if util.is_string(self.media):
|
||||
return self.to_json(), None
|
||||
|
||||
return self.to_json(), {self._media_name: self.media}
|
||||
|
||||
|
||||
class InputMediaPhoto(InputMedia):
|
||||
def __init__(self, media, caption=None, parse_mode=None):
|
||||
super(InputMediaPhoto, self).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode)
|
||||
|
||||
def to_dic(self):
|
||||
ret = super(InputMediaPhoto, self).to_dic()
|
||||
return ret
|
||||
|
||||
|
||||
class InputMediaVideo(JsonSerializable):
|
||||
def __init__(self, media, caption=None, width=None, height=None, duration=None):
|
||||
self.type = "video"
|
||||
self.media = media
|
||||
self.caption = caption
|
||||
class InputMediaVideo(InputMedia):
|
||||
def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None,
|
||||
supports_streaming=None):
|
||||
super(InputMediaVideo, self).__init__(type="video", media=media, caption=caption, parse_mode=parse_mode)
|
||||
self.thumb = thumb
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.duration = duration
|
||||
self.supports_streaming = supports_streaming
|
||||
|
||||
def to_dic(self):
|
||||
ret = super(InputMediaVideo, self).to_dic()
|
||||
if self.thumb:
|
||||
ret['thumb'] = self.thumb
|
||||
if self.width:
|
||||
ret['width'] = self.width
|
||||
if self.height:
|
||||
ret['height'] = self.height
|
||||
if self.duration:
|
||||
ret['duration'] = self.duration
|
||||
if self.supports_streaming:
|
||||
ret['supports_streaming'] = self.supports_streaming
|
||||
return ret
|
||||
|
||||
|
||||
class InputMediaAnimation(InputMedia):
|
||||
def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None):
|
||||
super(InputMediaAnimation, self).__init__(type="animation", media=media, caption=caption, parse_mode=parse_mode)
|
||||
self.thumb = thumb
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.duration = duration
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(self.to_dic())
|
||||
|
||||
def to_dic(self):
|
||||
ret = {'type': self.type, 'media': self.media}
|
||||
if self.caption:
|
||||
ret['caption'] = self.caption
|
||||
ret = super(InputMediaAnimation, self).to_dic()
|
||||
if self.thumb:
|
||||
ret['thumb'] = self.thumb
|
||||
if self.width:
|
||||
ret['width'] = self.width
|
||||
if self.height:
|
||||
@ -2005,3 +2158,36 @@ class InputMediaVideo(JsonSerializable):
|
||||
if self.duration:
|
||||
ret['duration'] = self.duration
|
||||
return ret
|
||||
|
||||
|
||||
class InputMediaAudio(InputMedia):
|
||||
def __init__(self, media, thumb=None, caption=None, parse_mode=None, duration=None, performer=None, title=None):
|
||||
super(InputMediaAudio, self).__init__(type="audio", media=media, caption=caption, parse_mode=parse_mode)
|
||||
self.thumb = thumb
|
||||
self.duration = duration
|
||||
self.performer = performer
|
||||
self.title = title
|
||||
|
||||
def to_dic(self):
|
||||
ret = super(InputMediaAudio, self).to_dic()
|
||||
if self.thumb:
|
||||
ret['thumb'] = self.thumb
|
||||
if self.duration:
|
||||
ret['duration'] = self.duration
|
||||
if self.performer:
|
||||
ret['performer'] = self.performer
|
||||
if self.title:
|
||||
ret['title'] = self.title
|
||||
return ret
|
||||
|
||||
|
||||
class InputMediaDocument(InputMedia):
|
||||
def __init__(self, media, thumb=None, caption=None, parse_mode=None):
|
||||
super(InputMediaDocument, self).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode)
|
||||
self.thumb = thumb
|
||||
|
||||
def to_dic(self):
|
||||
ret = super(InputMediaDocument, self).to_dic()
|
||||
if self.thumb:
|
||||
ret['thumb'] = self.thumb
|
||||
return ret
|
||||
|
@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import random
|
||||
import string
|
||||
import threading
|
||||
import traceback
|
||||
import re
|
||||
@ -13,9 +14,9 @@ try:
|
||||
import Queue
|
||||
except ImportError:
|
||||
import queue as Queue
|
||||
import logging
|
||||
|
||||
from telebot import logger
|
||||
|
||||
logger = logging.getLogger('TeleBot')
|
||||
|
||||
thread_local = threading.local()
|
||||
|
||||
@ -142,7 +143,7 @@ class AsyncTask:
|
||||
return self.result
|
||||
|
||||
|
||||
def async():
|
||||
def async_dec():
|
||||
def decorator(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
return AsyncTask(fn, *args, **kwargs)
|
||||
@ -247,10 +248,13 @@ def extract_arguments(text):
|
||||
return result.group(2) if is_command(text) else None
|
||||
|
||||
|
||||
def per_thread(key, construct_value):
|
||||
try:
|
||||
return getattr(thread_local, key)
|
||||
except AttributeError:
|
||||
def per_thread(key, construct_value, reset=False):
|
||||
if reset or not hasattr(thread_local, key):
|
||||
value = construct_value()
|
||||
setattr(thread_local, key, value)
|
||||
return value
|
||||
|
||||
return getattr(thread_local, key)
|
||||
|
||||
|
||||
def generate_random_token():
|
||||
return ''.join(random.sample(string.ascii_letters, 16))
|
@ -361,6 +361,20 @@ class TestTeleBot:
|
||||
new_msg = tb.edit_message_caption(caption='Edit test', chat_id=CHAT_ID, message_id=msg.message_id)
|
||||
assert new_msg.caption == 'Edit test'
|
||||
|
||||
def test_edit_message_media(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
file_data_2 = open('../examples/detailed_example/rooster.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_photo(CHAT_ID, file_data)
|
||||
new_msg = tb.edit_message_media(chat_id=CHAT_ID, message_id=msg.message_id,
|
||||
media=types.InputMediaPhoto(file_data_2, caption='Test editMessageMedia 0'))
|
||||
assert type(new_msg) != bool
|
||||
|
||||
new_msg = tb.edit_message_media(chat_id=CHAT_ID, message_id=msg.message_id,
|
||||
media=types.InputMediaPhoto(msg.photo[0].file_id, caption='Test editMessageMedia'))
|
||||
assert type(new_msg) != bool
|
||||
assert new_msg.caption == 'Test editMessageMedia'
|
||||
|
||||
def test_get_chat(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ch = tb.get_chat(GROUP_ID)
|
||||
@ -391,7 +405,7 @@ class TestTeleBot:
|
||||
def create_text_message(self, text):
|
||||
params = {'text': text}
|
||||
chat = types.User(11, False, 'test')
|
||||
return types.Message(1, None, None, chat, 'text', params)
|
||||
return types.Message(1, None, None, chat, 'text', params, "")
|
||||
|
||||
def test_is_string_unicode(self):
|
||||
s1 = u'string'
|
||||
@ -418,3 +432,59 @@ class TestTeleBot:
|
||||
medias = [types.InputMediaPhoto(img1, "View"), types.InputMediaPhoto(img2, "Dog")]
|
||||
result = tb.send_media_group(CHAT_ID, medias)
|
||||
assert len(result) == 2
|
||||
assert result[0].media_group_id is not None
|
||||
assert result[0].media_group_id == result[1].media_group_id
|
||||
|
||||
def test_send_media_group_local_files(self):
|
||||
photo = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
video = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
medias = [types.InputMediaPhoto(photo, "View"),
|
||||
types.InputMediaVideo(video)]
|
||||
result = tb.send_media_group(CHAT_ID, medias)
|
||||
assert len(result) == 2
|
||||
assert result[0].media_group_id is not None
|
||||
assert result[1].media_group_id is not None
|
||||
|
||||
def test_send_photo_formating_caption(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_photo(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
|
||||
assert ret_msg.caption_entities[0].type == 'italic'
|
||||
|
||||
def test_send_video_formatting_caption(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
|
||||
assert ret_msg.caption_entities[0].type == 'italic'
|
||||
|
||||
def test_send_audio_formatting_caption(self):
|
||||
file_data = open('./test_data/record.mp3', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_audio(CHAT_ID, file_data, caption='<b>bold</b>', parse_mode='HTML')
|
||||
assert ret_msg.caption_entities[0].type == 'bold'
|
||||
|
||||
def test_send_voice_formatting_caprion(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data, caption='<b>bold</b>', parse_mode='HTML')
|
||||
assert ret_msg.caption_entities[0].type == 'bold'
|
||||
assert ret_msg.voice.mime_type == 'audio/ogg'
|
||||
|
||||
def test_send_media_group_formatting_caption(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
img1 = 'https://i.imgur.com/CjXjcnU.png'
|
||||
img2 = 'https://i.imgur.com/CjXjcnU.png'
|
||||
medias = [types.InputMediaPhoto(img1, "*View*", parse_mode='Markdown'),
|
||||
types.InputMediaPhoto(img2, "_Dog_", parse_mode='Markdown')]
|
||||
result = tb.send_media_group(CHAT_ID, medias)
|
||||
assert len(result) == 2
|
||||
assert result[0].media_group_id is not None
|
||||
assert result[0].caption_entities[0].type == 'bold'
|
||||
assert result[1].caption_entities[0].type == 'italic'
|
||||
|
||||
def test_send_document_formating_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='_italic_', parse_mode='Markdown')
|
||||
assert ret_msg.caption_entities[0].type == 'italic'
|
||||
|
Reference in New Issue
Block a user