1
0
mirror of https://github.com/eternnoir/pyTelegramBotAPI.git synced 2023-08-10 21:12:57 +03:00
This commit is contained in:
Badiboy 2018-10-19 13:40:14 +03:00
commit ad4be5c0ae
23 changed files with 538 additions and 979 deletions

View File

@ -584,5 +584,9 @@ Get help. Discuss. Chat.
* [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)
* [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm).
Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh.

View File

@ -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

View File

@ -1,3 +1,5 @@
#!/usr/bin/python
# This example shows how to implement deep linking (https://core.telegram.org/bots#deep-linking)
# with the pyTelegramBotAPI.
# Note: This is not a working, production-ready sample.
@ -31,34 +33,38 @@
# steps are not shown here. Only steps 5 to 7 are illustrated, some in pseudo-code, with this example.
import telebot
import time
bot = telebot.TeleBot('TOKEN')
def extract_unique_code(text):
# Extracts the unique_code from the sent /start command.
return text.split()[1] if len(text.split()) > 1 else None
def in_storage(unique_code):
# (pseudo-code) Should check if a unique code exists in storage
return True
def get_username_from_storage(unique_code):
# (pseudo-code) Does a query to the storage, retrieving the associated username
# Should be replaced by a real database-lookup.
return "ABC" if in_storage(unique_code) else None
def save_chat_id(chat_id, username):
# (pseudo-code) Save the chat_id->username to storage
# Should be replaced by a real database query.
pass
@bot.message_handler(commands=['start'])
def send_welcome(message):
unique_code = extract_unique_code(message.text)
if unique_code: # if the '/start' command contains a unique_code
if unique_code: # if the '/start' command contains a unique_code
username = get_username_from_storage(unique_code)
if username: # if the username exists in our database
if username: # if the username exists in our database
save_chat_id(message.chat.id, username)
reply = "Hello {0}, how are you?".format(username)
else:
@ -67,4 +73,5 @@ def send_welcome(message):
reply = "Please visit me via a provided URL from the website."
bot.reply_to(message, reply)
bot.polling()

View File

@ -2,9 +2,10 @@
This is a detailed example using almost every command of the API
"""
import time
import telebot
from telebot import types
import time
TOKEN = '<token_string>'
@ -12,10 +13,10 @@ knownUsers = [] # todo: save these in a file,
userStep = {} # so they won't reset every time the bot restarts
commands = { # command description used in the "help" command
'start': 'Get used to the bot',
'help': 'Gives you information about the available commands',
'sendLongText': 'A test using the \'send_chat_action\' command',
'getImage': 'A test using multi-stage messages, custom keyboard, and media sending'
'start' : 'Get used to the bot',
'help' : 'Gives you information about the available commands',
'sendLongText': 'A test using the \'send_chat_action\' command',
'getImage' : 'A test using multi-stage messages, custom keyboard, and media sending'
}
imageSelect = types.ReplyKeyboardMarkup(one_time_keyboard=True) # create the image selection keyboard
@ -33,7 +34,7 @@ def get_user_step(uid):
else:
knownUsers.append(uid)
userStep[uid] = 0
print "New user detected, who hasn't used \"/start\" yet"
print("New user detected, who hasn't used \"/start\" yet")
return 0
@ -45,7 +46,7 @@ def listener(messages):
for m in messages:
if m.content_type == 'text':
# print the sent message to the console
print str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text
print(str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text)
bot = telebot.TeleBot(TOKEN)
@ -128,4 +129,5 @@ def command_default(m):
# this is the standard reply to a normal message
bot.send_message(m.chat.id, "I don't understand \"" + m.text + "\"\nMaybe try the help page at /help")
bot.polling()

View File

@ -1,3 +1,5 @@
#!/usr/bin/python
# This is a simple echo bot using the decorator mechanism.
# It echoes any incoming text messages.
@ -7,6 +9,7 @@ API_TOKEN = '<api_token>'
bot = telebot.TeleBot(API_TOKEN)
# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
@ -21,4 +24,5 @@ I am here to echo your kind words back to you. Just say anything nice and I'll s
def echo_message(message):
bot.reply_to(message, message.text)
bot.polling()

View File

@ -1,8 +1,9 @@
# This example show how to write an inline mode telegramt bot use pyTelegramBotAPI.
import telebot
import time
import sys
# This example show how to write an inline mode telegram bot use pyTelegramBotAPI.
import logging
import sys
import time
import telebot
from telebot import types
API_TOKEN = '<TOKEN>'
@ -69,5 +70,5 @@ if __name__ == '__main__':
try:
main_loop()
except KeyboardInterrupt:
print >> sys.stderr, '\nExiting by user request.\n'
print('\nExiting by user request.\n')
sys.exit(0)

View 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)

View File

@ -1,6 +1,5 @@
import telebot
from telebot.types import LabeledPrice
from telebot.types import ShippingOption
from telebot.types import LabeledPrice, ShippingOption
token = '1234567890:AAAABBBBCCCCDDDDeeeeFFFFgggGHHHH'
provider_token = '1234567890:TEST:AAAABBBBCCCCDDDD' # @BotFather -> Bot Settings -> Payments

View File

@ -2,7 +2,6 @@
"""
This Example will show you how to use register_next_step handler.
"""
import time
import telebot
from telebot import types
@ -75,4 +74,13 @@ 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()

View File

@ -3,9 +3,10 @@
# and goes by the name 'TeleBot (@pyTeleBot)'. Join our group to talk to him!
# WARNING: Tested with Python 2.7
import telebot
import os
import telebot
text_messages = {
'welcome':
u'Please welcome {name}!\n\n'
@ -33,8 +34,10 @@ if "TELEBOT_BOT_TOKEN" not in os.environ or "GROUP_CHAT_ID" not in os.environ:
bot = telebot.AsyncTeleBot(os.environ["TELEBOT_BOT_TOKEN"])
GROUP_CHAT_ID = int(os.environ["GROUP_CHAT_ID"])
def is_api_group(chat_id):
return chat_id== GROUP_CHAT_ID
return chat_id == GROUP_CHAT_ID
@bot.message_handler(func=lambda m: True, content_types=['new_chat_participant'])
def on_user_joins(message):
@ -50,6 +53,7 @@ def on_user_joins(message):
bot.reply_to(message, text_messages['welcome'].format(name=name))
@bot.message_handler(commands=['info', 'help'])
def on_info(message):
if not is_api_group(message.chat.id):
@ -58,21 +62,23 @@ def on_info(message):
bot.reply_to(message, text_messages['info'])
@bot.message_handler(commands=["ping"])
def on_ping(message):
bot.reply_to(message, "Still alive and kicking!")
@bot.message_handler(commands=['start'])
def on_start(message):
if not is_api_group(message.chat.id):
bot.reply_to(message, text_messages['wrong_chat'])
return
def listener(messages):
for m in messages:
print str(m)
print(str(m))
bot.set_update_listener(listener)
bot.polling()

View File

@ -11,7 +11,6 @@ from aiohttp import web
import telebot
API_TOKEN = '<api_token>'
WEBHOOK_HOST = '<ip/host where the bot is running>'
@ -32,7 +31,6 @@ WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
WEBHOOK_URL_BASE = "https://{}:{}".format(WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/{}/".format(API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
@ -51,6 +49,7 @@ async def handle(request):
else:
return web.Response(status=403)
app.router.add_post('/{token}/', handle)
@ -72,7 +71,7 @@ def echo_message(message):
bot.remove_webhook()
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Build ssl context

View File

@ -4,10 +4,11 @@
# This is a simple echo bot using decorators and webhook with CherryPy
# It echoes any incoming text messages and does not use the polling method.
import cherrypy
import telebot
import logging
import cherrypy
import telebot
API_TOKEN = '<api_token>'
@ -29,7 +30,6 @@ WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
@ -70,7 +70,7 @@ def echo_message(message):
bot.remove_webhook()
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Disable CherryPy requests log
@ -80,9 +80,9 @@ for handler in tuple(access_log.handlers):
# Start cherrypy server
cherrypy.config.update({
'server.socket_host': WEBHOOK_LISTEN,
'server.socket_port': WEBHOOK_PORT,
'server.ssl_module': 'builtin',
'server.socket_host' : WEBHOOK_LISTEN,
'server.socket_port' : WEBHOOK_PORT,
'server.ssl_module' : 'builtin',
'server.ssl_certificate': WEBHOOK_SSL_CERT,
'server.ssl_private_key': WEBHOOK_SSL_PRIV
})

View File

@ -4,11 +4,17 @@
# This is a simple echo bot using decorators and webhook with BaseHTTPServer
# It echoes any incoming text messages and does not use the polling method.
import BaseHTTPServer
import ssl
import telebot
import logging
try:
# Python 2
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
except ImportError:
# Python 3
from http.server import BaseHTTPRequestHandler, HTTPServer
import logging
import ssl
import telebot
API_TOKEN = '<api_token>'
@ -30,7 +36,6 @@ WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
@ -38,7 +43,7 @@ bot = telebot.TeleBot(API_TOKEN)
# WebhookHandler, process webhook calls
class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler):
class WebhookHandler(BaseHTTPRequestHandler):
server_version = "WebhookHandler/1.0"
def do_HEAD(self):
@ -84,12 +89,12 @@ def echo_message(message):
bot.remove_webhook()
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Start server
httpd = BaseHTTPServer.HTTPServer((WEBHOOK_LISTEN, WEBHOOK_PORT),
WebhookHandler)
httpd = HTTPServer((WEBHOOK_LISTEN, WEBHOOK_PORT),
WebhookHandler)
httpd.socket = ssl.wrap_socket(httpd.socket,
certfile=WEBHOOK_SSL_CERT,

View File

@ -4,11 +4,12 @@
# This is a simple echo bot using decorators and webhook with flask
# It echoes any incoming text messages and does not use the polling method.
import flask
import telebot
import logging
import time
import flask
import telebot
API_TOKEN = '<api_token>'
@ -30,7 +31,6 @@ WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
@ -77,7 +77,7 @@ bot.remove_webhook()
time.sleep(0.1)
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Start flask server

View File

@ -1,8 +1,9 @@
import os
import telebot
from flask import Flask, request
import telebot
TOKEN = '<api_token>'
bot = telebot.TeleBot(TOKEN)
server = Flask(__name__)

View File

@ -4,13 +4,15 @@
# This example shows webhook echo bot with Tornado web framework
# Documenation to Tornado: http://tornadoweb.org
import telebot
import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
import signal
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import telebot
API_TOKEN = '<api_token>'
WEBHOOK_CERT = "./cert.pem"
WEBHOOK_PKEY = "./pkey.pem"
@ -29,15 +31,18 @@ WEBHOOK_URL_BASE = "https://{0}:{1}/{2}".format(WEBHOOK_HOST, str(WEBHOOK_PORT),
bot = telebot.TeleBot(API_TOKEN)
class Root(tornado.web.RequestHandler):
def get(self):
self.write("Hi! This is webhook example!")
self.finish()
class webhook_serv(tornado.web.RequestHandler):
def get(self):
self.write("What are you doing here?")
self.finish()
def post(self):
if "Content-Length" in self.request.headers and \
"Content-Type" in self.request.headers and \
@ -52,21 +57,26 @@ class webhook_serv(tornado.web.RequestHandler):
else:
self.write("What are you doing here?")
self.finish()
tornado.options.define("port", default=WEBHOOK_PORT, help="run on the given port", type=int)
is_closing = False
def signal_handler(signum, frame):
global is_closing
print("Exiting...")
is_closing = True
def try_exit():
global is_closing
if is_closing:
# clean up here
tornado.ioloop.IOLoop.instance().stop()
print("Exit success!")
# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
@ -74,6 +84,7 @@ def send_welcome(message):
("Hi there, I am EchoBot.\n"
"I am here to echo your kind words back to you."))
bot.remove_webhook()
bot.set_webhook(url=WEBHOOK_URL_BASE,
certificate=open(WEBHOOK_CERT, 'r'))
@ -86,9 +97,9 @@ application = tornado.web.Application([
])
http_server = tornado.httpserver.HTTPServer(application, ssl_options={
"certfile": WEBHOOK_CERT,
"keyfile": WEBHOOK_PKEY,
})
"certfile": WEBHOOK_CERT,
"keyfile" : WEBHOOK_PKEY,
})
http_server.listen(tornado.options.options.port)
tornado.ioloop.PeriodicCallback(try_exit, 100).start()
tornado.ioloop.IOLoop.instance().start()

View File

@ -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.6.3',
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',

View File

@ -1,13 +1,15 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import threading
import time
import logging
import os
import pickle
import re
import sys
import six
import threading
import time
import logging
import six
logger = logging.getLogger('TeleBot')
formatter = logging.Formatter(
@ -27,6 +29,72 @@ 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:
@ -85,6 +153,9 @@ class TeleBot:
# key: chat_id, value: handler list
self.next_step_handlers = {}
self.next_step_saver = None
self.reply_saver = None
self.message_handlers = []
self.edited_message_handlers = []
self.channel_post_handlers = []
@ -99,6 +170,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)
@ -880,6 +999,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:
@ -984,7 +1109,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:
"""
@ -1052,8 +1176,7 @@ class TeleBot:
"""
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`
@ -1066,17 +1189,18 @@ class TeleBot:
"""
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 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({"callback": callback, "args": args, "kwargs": kwargs})
self.reply_handlers[message_id].append(Handler(callback, *args, **kwargs))
else:
self.reply_handlers[message_id] = [{"callback": callback, "args": args, "kwargs": kwargs}]
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:
@ -1087,11 +1211,15 @@ class TeleBot:
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()
def register_next_step_handler(self, message, 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 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
@ -1104,15 +1232,20 @@ class TeleBot:
"""
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({"callback": callback, "args": args, "kwargs": kwargs})
self.next_step_handlers[chat_id].append(Handler(callback, *args, **kwargs))
else:
self.next_step_handlers[chat_id] = [{"callback": callback, "args": args, "kwargs": kwargs}]
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):
"""
@ -1131,11 +1264,14 @@ class TeleBot:
"""
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_id: The message for which we want to clear reply handlers
: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)
@ -1148,6 +1284,9 @@ class TeleBot:
"""
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):
@ -1155,21 +1294,22 @@ class TeleBot:
chat_id = message.chat.id
was_poped = False
if chat_id in self.next_step_handlers.keys():
handlers = self.next_step_handlers[chat_id]
if (handlers):
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
self.next_step_handlers.pop(chat_id, None)
if (not was_poped):
if self.next_step_saver is not None:
self.next_step_saver.start_save_timer()
if not was_poped:
i += 1
@staticmethod
def _build_handler_dict(handler, **filters):
return {
'function': handler,
'filters': filters
'filters' : filters
}
def message_handler(self, commands=None, regexp=None, func=None, content_types=['text'], **kwargs):
@ -1332,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),
@ -1354,6 +1495,30 @@ class AsyncTeleBot(TeleBot):
def __init__(self, *args, **kwargs):
TeleBot.__init__(self, *args, **kwargs)
@util.async_dec()
def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"):
return TeleBot.enable_save_next_step_handlers(self, delay, filename)
@util.async_dec()
def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"):
return TeleBot.enable_save_reply_handlers(self, delay, filename)
@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", del_file_after_loading=True):
return TeleBot.load_next_step_handlers(self, filename, del_file_after_loading)
@util.async_dec()
def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True):
return TeleBot.load_reply_handlers(self, filename, del_file_after_loading)
@util.async_dec()
def get_me(self):
return TeleBot.get_me(self)
@ -1514,6 +1679,10 @@ class AsyncTeleBot(TeleBot):
def edit_message_text(self, *args, **kwargs):
return TeleBot.edit_message_text(self, *args, **kwargs)
@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)

View File

@ -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):
@ -99,7 +99,6 @@ def get_file(token, 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)
@ -123,6 +122,8 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m
:param disable_web_page_preview:
:param reply_to_message_id:
:param reply_markup:
:param parse_mode:
:param disable_notification:
:return:
"""
method_url = r'sendMessage'
@ -266,7 +267,7 @@ 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, files = _convert_input_media(media)
media_json, files = _convert_input_media_array(media)
payload = {'chat_id': chat_id, 'media': media_json}
if disable_notification:
payload['disable_notification'] = disable_notification
@ -640,6 +641,21 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m
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 = {}
@ -756,7 +772,8 @@ def send_invoice(token, chat_id, title, description, invoice_payload, provider_t
:param disable_notification: Sends the message silently. Users will receive a notification with no sound.
:param reply_to_message_id: If the message is a reply, ID of the original message
:param reply_markup: A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button
:return:
:param provider_data:
:return:
"""
method_url = r'sendInvoice'
payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload,
@ -939,11 +956,17 @@ def _convert_markup(markup):
return markup
def _convert_input_media(array):
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.JsonSerializable):
if isinstance(input_media, types.InputMedia):
media_dict = input_media.to_dic()
if media_dict['media'].startswith('attach://'):
key = media_dict['media'].replace('attach://', '')

View File

@ -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.
@ -457,11 +457,11 @@ class Message(JsonDeserializable):
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>",
"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"):
@ -469,6 +469,7 @@ class Message(JsonDeserializable):
_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":
@ -501,6 +502,7 @@ class Message(JsonDeserializable):
def html_caption(self):
return self.__html_text(self.caption, self.caption_entities)
class MessageEntity(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
@ -598,29 +600,6 @@ class Document(JsonDeserializable):
self.file_size = file_size
class Sticker(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
file_id = obj['file_id']
width = obj['width']
height = obj['height']
thumb = None
if 'thumb' in obj:
thumb = PhotoSize.de_json(obj['thumb'])
emoji = obj.get('emoji')
file_size = obj.get('file_size')
return cls(file_id, width, height, thumb, emoji, file_size)
def __init__(self, file_id, width, height, thumb, emoji=None, file_size=None):
self.file_id = file_id
self.width = width
self.height = height
self.thumb = thumb
self.emoji = emoji
self.file_size = file_size
class Video(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
@ -1092,7 +1071,7 @@ class InputVenueMessageContent(Dictionaryable):
def to_dic(self):
json_dic = {'latitude': self.latitude, 'longitude': self.longitude, 'title': self.title,
'address': self.address}
'address' : self.address}
if self.foursquare_id:
json_dic['foursquare_id'] = self.foursquare_id
return json_dic
@ -1191,7 +1170,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
@ -1202,6 +1181,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:
@ -1215,6 +1196,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
@ -1230,6 +1212,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:
@ -1286,7 +1270,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
@ -1296,6 +1280,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:
@ -1308,6 +1294,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
@ -1322,6 +1309,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:
@ -1333,8 +1322,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
@ -1342,6 +1331,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
@ -1358,6 +1349,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
@ -1375,6 +1367,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:
@ -1383,13 +1377,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
@ -1399,6 +1394,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:
@ -1411,13 +1408,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
@ -1427,6 +1425,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:
@ -1439,14 +1439,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
@ -1459,6 +1460,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:
@ -1581,6 +1584,7 @@ class BaseInlineQueryResultCached(JsonSerializable):
self.caption = None
self.reply_markup = None
self.input_message_content = None
self.parse_mode = None
self.payload_dic = {}
def to_json(self):
@ -1597,12 +1601,14 @@ class BaseInlineQueryResultCached(JsonSerializable):
json_dict['reply_markup'] = self.reply_markup.to_dic()
if self.input_message_content:
json_dict['input_message_content'] = self.input_message_content.to_dic()
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
return json.dumps(json_dict)
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
@ -1612,11 +1618,12 @@ class InlineQueryResultCachedPhoto(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['photo_file_id'] = photo_file_id
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'
@ -1627,12 +1634,13 @@ class InlineQueryResultCachedGif(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['gif_file_id'] = gif_file_id
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
@ -1642,6 +1650,7 @@ class InlineQueryResultCachedMpeg4Gif(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['mpeg4_file_id'] = mpeg4_file_id
@ -1657,7 +1666,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'
@ -1668,11 +1677,12 @@ class InlineQueryResultCachedDocument(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['document_file_id'] = document_file_id
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'
@ -1683,11 +1693,13 @@ class InlineQueryResultCachedVideo(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['video_file_id'] = video_file_id
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
@ -1696,11 +1708,12 @@ class InlineQueryResultCachedVoice(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['voice_file_id'] = voice_file_id
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
@ -1708,6 +1721,7 @@ class InlineQueryResultCachedAudio(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['audio_file_id'] = audio_file_id
@ -2041,48 +2055,61 @@ class MaskPosition(JsonDeserializable, JsonSerializable):
# InputMedia
class InputMediaPhoto(JsonSerializable):
def __init__(self, media, caption=None, parse_mode=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': 'attach://' + util.generate_random_token()
if not util.is_string(self.media) else 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
class InputMediaVideo(JsonSerializable):
def __init__(self, media, caption=None, parse_mode=None, width=None, height=None, duration=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(InputMedia):
def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None,
supports_streaming=None):
self.type = "video"
self.media = media
self.caption = caption
self.parse_mode = parse_mode
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_json(self):
return json.dumps(self.to_dic())
def to_dic(self):
ret = {'type': self.type, 'media': 'attach://' + util.generate_random_token()
if not util.is_string(self.media) else self.media}
if self.caption:
ret['caption'] = self.caption
if self.parse_mode:
ret['parse_mode'] = self.parse_mode
ret = super(InputMediaVideo, self).to_dic()
if self.thumb:
ret['thumb'] = self.thumb
if self.width:
ret['width'] = self.width
if self.height:
@ -2092,3 +2119,57 @@ class InputMediaVideo(JsonSerializable):
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_dic(self):
ret = super(InputMediaAnimation, 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
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

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
import random
import re
import string
import sys
import threading
import traceback
import re
import sys
import six
from six import string_types
@ -243,18 +244,17 @@ def extract_arguments(text):
:param text: String to extract the arguments from a command
:return: the arguments if `text` is a command (according to is_command), else None.
"""
regexp = re.compile("\/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE)
regexp = re.compile("/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE)
result = regexp.match(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():

View File

@ -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)
@ -388,7 +402,8 @@ class TestTeleBot:
new_msg = tb.edit_message_reply_markup(chat_id=CHAT_ID, message_id=ret_msg.message_id, reply_markup=markup)
assert new_msg.message_id
def create_text_message(self, text):
@staticmethod
def create_text_message(text):
params = {'text': text}
chat = types.User(11, False, 'test')
return types.Message(1, None, None, chat, 'text', params, "")

View File

@ -59,7 +59,7 @@ def test_json_Message_Sticker_without_thumb():
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
msg = types.Message.de_json(json_string)
assert msg.sticker.height == 368
assert msg.sticker.thumb == None
assert msg.sticker.thumb is None
assert msg.content_type == 'sticker'
@ -113,6 +113,7 @@ def test_json_voice():
assert voice.duration == 0
assert voice.file_size == 10481
def test_json_update():
json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"is_bot":true,"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","type":"private","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}'
update = types.Update.de_json(json_string)
@ -120,6 +121,7 @@ def test_json_update():
assert update.message.message_id == 241
assert update.message.from_user.id == 9734
def test_json_chat():
json_string = r'{"id": -111111,"title": "Test Title","type": "group"}'
chat = types.Chat.de_json(json_string)
@ -127,6 +129,7 @@ def test_json_chat():
assert chat.type == 'group'
assert chat.title == 'Test Title'
def test_InlineQueryResultCachedPhoto():
iq = types.InlineQueryResultCachedPhoto('aaa', 'Fileid')
json_str = iq.to_json()
@ -143,6 +146,7 @@ def test_InlineQueryResultCachedPhoto_with_title():
assert 'Title' in json_str
assert 'caption' not in json_str
def test_InlineQueryResultCachedPhoto_with_markup():
markup = types.InlineKeyboardMarkup()
markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com"))
@ -154,4 +158,3 @@ def test_InlineQueryResultCachedPhoto_with_markup():
assert 'Title' in json_str
assert 'caption' not in json_str
assert 'reply_markup' in json_str