mirror of
https://github.com/eternnoir/pyTelegramBotAPI.git
synced 2023-08-10 21:12:57 +03:00
Compare commits
47 Commits
Author | SHA1 | Date | |
---|---|---|---|
528b23770f | |||
4e472dda48 | |||
17dec34923 | |||
a17b301f1c | |||
cf830a0fb4 | |||
7e9f5b09cf | |||
969c5e76ef | |||
7346326bc3 | |||
f355796b01 | |||
5175803d0b | |||
bf9939d40e | |||
07960fe348 | |||
f4be18e082 | |||
8e9837a587 | |||
361c043872 | |||
50432dbef1 | |||
37277d74cc | |||
9ee341f5e6 | |||
0bd284afc7 | |||
4ffdada427 | |||
d8effd3f9f | |||
303e15b88d | |||
3273aa9afa | |||
9e8b11051c | |||
b9d458e643 | |||
1e6361dd57 | |||
ca2019b8f1 | |||
a230665424 | |||
8eb6e034fe | |||
d6552eb4c6 | |||
117c5a1141 | |||
3f335c37ce | |||
e7e681928d | |||
855ff40070 | |||
29a42a398b | |||
b801728924 | |||
d14e9051d4 | |||
60ca1751ca | |||
941b8ac5d0 | |||
7a08102fad | |||
325061ee96 | |||
8839f36706 | |||
1c53955d5a | |||
9cd28b88be | |||
2fb2cd6f20 | |||
036d000b95 | |||
11d4bb02a8 |
@ -2,6 +2,7 @@ language: python
|
||||
python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "pypy"
|
||||
- "pypy3"
|
||||
|
51
README.md
51
README.md
@ -2,8 +2,9 @@
|
||||
|
||||
<p align="center">A simple, but extensible Python implementation for the [Telegram Bot API](https://core.telegram.org/bots/api).
|
||||
|
||||
|
||||
<p align="center">[](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
[](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
|
||||
* [Getting started.](#getting-started)
|
||||
* [Writing your first bot](#writing-your-first-bot)
|
||||
@ -27,6 +28,7 @@
|
||||
* [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)
|
||||
* [The Telegram Chat Group](#the-telegram-chat-group)
|
||||
* [More examples](#more-examples)
|
||||
* [Bots using this API](#bots-using-this-api)
|
||||
|
||||
## Getting started.
|
||||
|
||||
@ -118,7 +120,7 @@ To start the bot, simply open up a terminal and enter `python echo_bot.py` to ru
|
||||
|
||||
All types are defined in types.py. They are all completely in line with the [Telegram API's definition of the types](https://core.telegram.org/bots/api#available-types), except for the Message's `from` field, which is renamed to `from_user` (because `from` is a Python reserved token). Thus, attributes such as `message_id` can be accessed directly with `message.message_id`. Note that `message.chat` can be either an instance of `User` or `GroupChat` (see [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)).
|
||||
|
||||
The Message object also has a `content_type`attribute, which defines the type of the Message. `content_type` can be one of the following strings:
|
||||
The Message object also has a `content_type`attribute, which defines the type of the Message. `content_type` can be one of the following strings:
|
||||
'text', 'audio', 'document', 'photo', 'sticker', 'video', 'location', 'contact', 'new_chat_participant', 'left_chat_participant', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created'.
|
||||
|
||||
### Methods
|
||||
@ -181,6 +183,13 @@ def test_message(message):
|
||||
@bot.message_handler(func=test_message, content_types=['document'])
|
||||
def handle_text_doc(message)
|
||||
pass
|
||||
|
||||
# Handlers can be stacked to create a function which will be called if either message_handler is eligible
|
||||
# This handler will be called if the message starts with '/hello' OR is some emoji
|
||||
@bot.message_handler(commands=['hello'])
|
||||
@bot.message_handler(func=lambda msg: msg.text.encode("utf-8") == SOME_FANCY_EMOJI)
|
||||
def send_something(message):
|
||||
pass
|
||||
```
|
||||
**Important: all handlers are tested in the order in which they were declared**
|
||||
#### TeleBot
|
||||
@ -200,6 +209,11 @@ tb.polling(none_stop=False, interval=0, block=True)
|
||||
# getMe
|
||||
user = tb.get_me()
|
||||
|
||||
# setWebhook
|
||||
tb.set_webhook(url="http://example.com", certificate=open('mycert.pem'))
|
||||
# unset webhook
|
||||
tb.remove_webhook()
|
||||
|
||||
# getUpdates
|
||||
updates = tb.get_updates()
|
||||
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
|
||||
@ -361,8 +375,10 @@ bot.set_update_listener(handle_messages)
|
||||
bot.polling()
|
||||
```
|
||||
|
||||
### Using web hooks
|
||||
If you prefer using web hooks to the getUpdates method, you can use the `process_new_messages(messages)` function in TeleBot to make it process the messages that you supply. It takes a list of Message objects. This function is still incubating.
|
||||
### Using webhooks
|
||||
When using webhooks telegram sends one Update per call, for processing it you should call process_new_messages([update.message]) when you recieve it.
|
||||
|
||||
There are some examples using webhooks in the *examples/webhook_examples* directory.
|
||||
|
||||
### Logging
|
||||
|
||||
@ -379,21 +395,19 @@ telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
|
||||
## F.A.Q.
|
||||
|
||||
### How can I distinguish a User and a GroupChat in message.chat?
|
||||
There are two ways to do this:
|
||||
Telegram Bot API support new type Chat for message.chat.
|
||||
|
||||
- Checking the instance of message.chat with `isinstance`:
|
||||
- Check the ```type``` attribute in ```Chat``` object:
|
||||
```python
|
||||
def is_user(chat):
|
||||
return isinstance(chat, types.User)
|
||||
if message.chat.type == “private”:
|
||||
# private chat message
|
||||
|
||||
print is_user(message.chat) # True or False
|
||||
```
|
||||
- Checking whether the chat id is negative or positive. If the chat id is negative, the chat is a GroupChat, if it is positive, it is a User. Example:
|
||||
```python
|
||||
def is_user(chat):
|
||||
return chat.id > 0
|
||||
if message.chat.type == “group”:
|
||||
# group chat message
|
||||
|
||||
if message.chat.type == “channel”:
|
||||
# channel message
|
||||
|
||||
print is_user(message.chat) # True or False
|
||||
```
|
||||
|
||||
## The Telegram Chat Group
|
||||
@ -401,9 +415,14 @@ print is_user(message.chat) # True or False
|
||||
Get help. Discuss. Chat.
|
||||
|
||||
Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/joinchat/067e22c60035523fda8f6025ee87e30b).
|
||||
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
|
||||
Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh.
|
||||
|
100
README.rst
100
README.rst
@ -52,6 +52,7 @@ API <https://core.telegram.org/bots/api>`__.
|
||||
|
||||
- `The Telegram Chat Group <#the-telegram-chat-group>`__
|
||||
- `More examples <#more-examples>`__
|
||||
- `Bots using this API <#bots-using-this-api>`__
|
||||
|
||||
Getting started.
|
||||
================
|
||||
@ -213,9 +214,39 @@ Outlined below are some general use cases of the API.
|
||||
Message handlers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
A message handler is a function which is decorated with the
|
||||
``message_handler`` decorator of a TeleBot instance. The following
|
||||
examples illustrate the possibilities of message handlers:
|
||||
A message handler is a function that is decorated with the
|
||||
``message_handler`` decorator of a TeleBot instance. Message handlers
|
||||
consist of one or multiple filters. Each filter much return True for a
|
||||
certain message in order for a message handler to become eligible to
|
||||
handle that message. A message handler is declared in the following way
|
||||
(provided ``bot`` is an instance of TeleBot):
|
||||
|
||||
.. code:: python
|
||||
|
||||
@bot.message_handler(filters)
|
||||
def function_name(message):
|
||||
bot.reply_to(message, "This is a message handler")
|
||||
|
||||
``function_name`` is not bound to any restrictions. Any function name is
|
||||
permitted with message handlers. The function must accept at most one
|
||||
argument, which will be the message that the function must handle.
|
||||
``filters`` is a list of keyword arguments. A filter is declared in the
|
||||
following manner: ``name=argument``. One handler may have multiple
|
||||
filters. TeleBot supports the following filters:
|
||||
|
||||
+------------------+---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| name | argument(s) | Condition |
|
||||
+==================+=============================================+=================================================================================================================================================================================+
|
||||
| content\_types | list of strings (default ``['text']``) | ``True`` if message.content\_type is in the list of strings. |
|
||||
+------------------+---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| regexp | a regular expression as a string | ``True`` if ``re.search(regexp_arg)`` returns ``True`` and ``message.content_type == 'text'`` (See `Python Regular Expressions <https://docs.python.org/2/library/re.html>`__ |
|
||||
+------------------+---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| commands | list of strings | ``True`` if ``message.content_type == 'text'`` and ``message.text`` starts with a command that is in the list of strings. |
|
||||
+------------------+---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| func | a function (lambda or function reference) | ``True`` if the lambda or function reference returns ``True`` |
|
||||
+------------------+---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
Here are some examples of using the filters and message handlers:
|
||||
|
||||
.. code:: python
|
||||
|
||||
@ -250,8 +281,15 @@ examples illustrate the possibilities of message handlers:
|
||||
def handle_text_doc(message)
|
||||
pass
|
||||
|
||||
*Note: all handlers are tested in the order in which they were declared*
|
||||
#### TeleBot
|
||||
# Handlers can be stacked to create a function which will be called if either message_handler is eligible
|
||||
# This handler will be called if the message starts with '/hello' OR is some emoji
|
||||
@bot.message_handler(commands=['hello'])
|
||||
@bot.message_handler(func=lambda msg: msg.text.encode("utf-8") == SOME_FANCY_EMOJI)
|
||||
def send_something(message):
|
||||
pass
|
||||
|
||||
**Important: all handlers are tested in the order in which they were
|
||||
declared** #### TeleBot
|
||||
|
||||
.. code:: python
|
||||
|
||||
@ -270,6 +308,11 @@ examples illustrate the possibilities of message handlers:
|
||||
# getMe
|
||||
user = tb.get_me()
|
||||
|
||||
# setWebhook
|
||||
tb.set_webhook(url="http://example.com", certificate=open('mycert.pem'))
|
||||
# unset webhook
|
||||
tb.remove_webhook()
|
||||
|
||||
# getUpdates
|
||||
updates = tb.get_updates()
|
||||
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
|
||||
@ -322,6 +365,14 @@ examples illustrate the possibilities of message handlers:
|
||||
# 'record_audio', 'upload_audio', 'upload_document' or 'find_location'.
|
||||
tb.send_chat_action(chat_id, action_string)
|
||||
|
||||
# getFile
|
||||
# Downloading a file is straightforward
|
||||
# Returns a File object
|
||||
import requests
|
||||
file_info = tb.get_file(file_id)
|
||||
|
||||
file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN, file_info.file_path))
|
||||
|
||||
Reply markup
|
||||
~~~~~~~~~~~~
|
||||
|
||||
@ -465,29 +516,31 @@ function as a listener to TeleBot. Example:
|
||||
bot.set_update_listener(handle_messages)
|
||||
bot.polling()
|
||||
|
||||
Using web hooks
|
||||
---------------
|
||||
Using webhooks
|
||||
--------------
|
||||
|
||||
If you prefer using web hooks to the getUpdates method, you can use the
|
||||
``process_new_messages(messages)`` function in TeleBot to make it
|
||||
process the messages that you supply. It takes a list of Message
|
||||
objects. This function is still incubating.
|
||||
When using webhooks telegram sends one Update per call, for processing
|
||||
it you should call process\_new\_messages([update.message]) when you
|
||||
recieve it.
|
||||
|
||||
There are some examples using webhooks in the
|
||||
*examples/webhook\_examples* directory.
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
You can use the Telebot module logger to log debug info about Telebot.
|
||||
Use ``telebot.logger`` to get the logger of the TeleBot module.
|
||||
Use ``telebot.logger`` to get the logger of the TeleBot module. It is
|
||||
possible to add custom logging Handlers to the logger. Refer to the
|
||||
`Python logging module
|
||||
page <https://docs.python.org/2/library/logging.html>`__ for more info.
|
||||
|
||||
.. code:: python
|
||||
|
||||
import logging
|
||||
|
||||
logger = telebot.logger
|
||||
formatter = logging.Formatter('[%(asctime)s] %(thread)d {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',
|
||||
'%m-%d %H:%M:%S')
|
||||
ch = logging.StreamHandler(sys.stdout)
|
||||
logger.addHandler(ch)
|
||||
logger.setLevel(logging.DEBUG) # or use logging.INFO
|
||||
ch.setFormatter(formatter)
|
||||
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
|
||||
|
||||
F.A.Q.
|
||||
======
|
||||
@ -513,6 +566,8 @@ Get help. Discuss. Chat.
|
||||
|
||||
Join the `pyTelegramBotAPI Telegram Chat
|
||||
Group <https://telegram.me/joinchat/067e22c60035523fda8f6025ee87e30b>`__.
|
||||
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
|
||||
=============
|
||||
@ -524,5 +579,14 @@ More examples
|
||||
- `next\_step\_handler
|
||||
Example <https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py>`__
|
||||
|
||||
Bots using this API
|
||||
===================
|
||||
|
||||
- `SiteAlert bot <https://telegram.me/SiteAlert_bot>`__
|
||||
(`source <https://github.com/ilteoood/SiteAlert-Python>`__) by
|
||||
*ilteoood* - Monitors websites and sends a notification on changes
|
||||
Want to have your bot listed here? Send a Telegram message to
|
||||
@eternnoir or @pevdh.
|
||||
|
||||
.. |Build Status| image:: https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master
|
||||
:target: https://travis-ci.org/eternnoir/pyTelegramBotAPI
|
||||
|
36
examples/webhook_examples/README.md
Normal file
36
examples/webhook_examples/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
# Webhook examples using pyTelegramBotAPI
|
||||
|
||||
There are 3 examples in this directory using different libraries:
|
||||
|
||||
* **Python (CPython):** *webhook_cpython_echo_bot.py*
|
||||
* **Pros:**
|
||||
* Official python libraries, it works out of the box (doesn't require to
|
||||
install anything).
|
||||
* Works with Python 2 and Python 3 (need to be converted with 2to3).
|
||||
* **Cons:**
|
||||
* Ugly code.
|
||||
* Many things to handle yourself, this can lead to errors.
|
||||
* Not powerful, do the trick but the performance is low.
|
||||
|
||||
* **CherryPy (3.8.0):** *webhook_cherrypy_echo_bot.py*
|
||||
* **Pros:**
|
||||
* It's a web application framework, cleaner code, uses objects for defining
|
||||
the web application.
|
||||
* Very good performance.
|
||||
* The project seems to be active, latest version is recent.
|
||||
* Works with Python 2 and Python 3.
|
||||
* **Cons:**
|
||||
* Some things are not very intuitive, reading the doc is a must.
|
||||
|
||||
* **Flask (0.10.1):** *webhook_flask_echo_bot.py*
|
||||
* **Pros:**
|
||||
* It's a web application framework, cleaner code, uses decorator which can
|
||||
be nice.
|
||||
* Good performance.
|
||||
* It's intuitive if you know how web application works.
|
||||
* **Cons:**
|
||||
* The project seems not to be very active, latest version dates 2013.
|
||||
* They don't recommend to use it with Python 3, but may work.
|
||||
* May be a oversized for just handling webhook petitions.
|
||||
|
||||
*Latest update of this document: 2015-10-06*
|
85
examples/webhook_examples/webhook_cherrypy_echo_bot.py
Normal file
85
examples/webhook_examples/webhook_cherrypy_echo_bot.py
Normal file
@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This is a simple echo bot using decorators and webhook with CherryPy
|
||||
# It echoes any incoming text messages and does not use the polling method.
|
||||
|
||||
import cherrypy
|
||||
import telebot
|
||||
import logging
|
||||
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
WEBHOOK_HOST = '<ip/host where the bot is running>'
|
||||
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
|
||||
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
|
||||
|
||||
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
|
||||
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
|
||||
|
||||
# Quick'n'dirty SSL certificate generation:
|
||||
#
|
||||
# openssl genrsa -out webhook_pkey.pem 2048
|
||||
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
|
||||
#
|
||||
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
|
||||
# with the same value in you put in WEBHOOK_HOST
|
||||
|
||||
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
|
||||
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
|
||||
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.INFO)
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
|
||||
# WebhookServer, process webhook calls
|
||||
class WebhookServer(object):
|
||||
@cherrypy.expose
|
||||
def index(self):
|
||||
if 'content-length' in cherrypy.request.headers and \
|
||||
'content-type' in cherrypy.request.headers and \
|
||||
cherrypy.request.headers['content-type'] == 'application/json':
|
||||
length = int(cherrypy.request.headers['content-length'])
|
||||
json_string = cherrypy.request.body.read(length).decode("utf-8")
|
||||
update = telebot.types.Update.de_json(json_string)
|
||||
bot.process_new_messages([update.message])
|
||||
return ''
|
||||
else:
|
||||
raise cherrypy.HTTPError(403)
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message,
|
||||
("Hi there, I am EchoBot.\n"
|
||||
"I am here to echo your kind words back to you."))
|
||||
|
||||
|
||||
# Handle all other messages
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
# Remove webhook, it fails sometimes the set if there is a previous webhook
|
||||
bot.remove_webhook()
|
||||
|
||||
# Set webhook
|
||||
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
|
||||
certificate=open(WEBHOOK_SSL_CERT, 'r'))
|
||||
|
||||
# Start cherrypy server
|
||||
cherrypy.config.update({
|
||||
'server.socket_host': WEBHOOK_LISTEN,
|
||||
'server.socket_port': WEBHOOK_PORT,
|
||||
'server.ssl_module': 'builtin',
|
||||
'server.ssl_certificate': WEBHOOK_SSL_CERT,
|
||||
'server.ssl_private_key': WEBHOOK_SSL_PRIV
|
||||
})
|
||||
|
||||
cherrypy.quickstart(WebhookServer(), WEBHOOK_URL_PATH, {'/': {}})
|
99
examples/webhook_examples/webhook_cpython_echo_bot.py
Normal file
99
examples/webhook_examples/webhook_cpython_echo_bot.py
Normal file
@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This is a simple echo bot using decorators and webhook with BaseHTTPServer
|
||||
# It echoes any incoming text messages and does not use the polling method.
|
||||
|
||||
import BaseHTTPServer
|
||||
import ssl
|
||||
import telebot
|
||||
import logging
|
||||
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
WEBHOOK_HOST = '<ip/host where the bot is running>'
|
||||
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
|
||||
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
|
||||
|
||||
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
|
||||
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
|
||||
|
||||
# Quick'n'dirty SSL certificate generation:
|
||||
#
|
||||
# openssl genrsa -out webhook_pkey.pem 2048
|
||||
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
|
||||
#
|
||||
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
|
||||
# with the same value in you put in WEBHOOK_HOST
|
||||
|
||||
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
|
||||
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
|
||||
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.INFO)
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
|
||||
# WebhookHandler, process webhook calls
|
||||
class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
server_version = "WebhookHandler/1.0"
|
||||
|
||||
def do_HEAD(self):
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
def do_GET(self):
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
def do_POST(self):
|
||||
if self.path == WEBHOOK_URL_PATH and \
|
||||
'content-type' in self.headers and \
|
||||
'content-length' in self.headers and \
|
||||
self.headers['content-type'] == 'application/json':
|
||||
json_string = self.rfile.read(int(self.headers['content-length']))
|
||||
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
update = telebot.types.Update.de_json(json_string)
|
||||
bot.process_new_messages([update.message])
|
||||
else:
|
||||
self.send_error(403)
|
||||
self.end_headers()
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message,
|
||||
("Hi there, I am EchoBot.\n"
|
||||
"I am here to echo your kind words back to you."))
|
||||
|
||||
|
||||
# Handle all other messages
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
# Remove webhook, it fails sometimes the set if there is a previous webhook
|
||||
bot.remove_webhook()
|
||||
|
||||
# Set webhook
|
||||
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
|
||||
certificate=open(WEBHOOK_SSL_CERT, 'r'))
|
||||
|
||||
# Start server
|
||||
httpd = BaseHTTPServer.HTTPServer((WEBHOOK_LISTEN, WEBHOOK_PORT),
|
||||
WebhookHandler)
|
||||
|
||||
httpd.socket = ssl.wrap_socket(httpd.socket,
|
||||
certfile=WEBHOOK_SSL_CERT,
|
||||
keyfile=WEBHOOK_SSL_PRIV,
|
||||
server_side=True)
|
||||
|
||||
httpd.serve_forever()
|
84
examples/webhook_examples/webhook_flask_echo_bot.py
Normal file
84
examples/webhook_examples/webhook_flask_echo_bot.py
Normal file
@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This is a simple echo bot using decorators and webhook with flask
|
||||
# It echoes any incoming text messages and does not use the polling method.
|
||||
|
||||
import flask
|
||||
import telebot
|
||||
import logging
|
||||
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
WEBHOOK_HOST = '<ip/host where the bot is running>'
|
||||
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
|
||||
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
|
||||
|
||||
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
|
||||
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
|
||||
|
||||
# Quick'n'dirty SSL certificate generation:
|
||||
#
|
||||
# openssl genrsa -out webhook_pkey.pem 2048
|
||||
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
|
||||
#
|
||||
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
|
||||
# with the same value in you put in WEBHOOK_HOST
|
||||
|
||||
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
|
||||
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
|
||||
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.INFO)
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
|
||||
# Empty webserver index, return nothing, just http 200
|
||||
@app.route('/', methods=['GET', 'HEAD'])
|
||||
def index():
|
||||
return ''
|
||||
|
||||
|
||||
# Process webhook calls
|
||||
@app.route(WEBHOOK_URL_PATH, methods=['POST'])
|
||||
def webhook():
|
||||
if flask.request.headers.get('content-type') == 'application/json':
|
||||
json_string = flask.request.get_data()
|
||||
update = telebot.types.Update.de_json(json_string)
|
||||
bot.process_new_messages([update.message])
|
||||
return ''
|
||||
else:
|
||||
flask.abort(403)
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message,
|
||||
("Hi there, I am EchoBot.\n"
|
||||
"I am here to echo your kind words back to you."))
|
||||
|
||||
|
||||
# Handle all other messages
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
# Remove webhook, it fails sometimes the set if there is a previous webhook
|
||||
bot.remove_webhook()
|
||||
|
||||
# Set webhook
|
||||
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
|
||||
certificate=open(WEBHOOK_SSL_CERT, 'r'))
|
||||
|
||||
# Start flask server
|
||||
app.run(host=WEBHOOK_LISTEN,
|
||||
port=WEBHOOK_PORT,
|
||||
ssl_context=(WEBHOOK_SSL_CERT, WEBHOOK_SSL_PRIV),
|
||||
debug=True)
|
2
setup.py
2
setup.py
@ -6,7 +6,7 @@ def readme():
|
||||
return f.read()
|
||||
|
||||
setup(name='pyTelegramBotAPI',
|
||||
version='0.3.6',
|
||||
version='1.4.0',
|
||||
description='Python Telegram bot api. ',
|
||||
long_description=readme(),
|
||||
author='eternnoir',
|
||||
|
@ -8,8 +8,11 @@ import sys
|
||||
import six
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('TeleBot')
|
||||
formatter = logging.Formatter('%(asctime)s (%(filename)s:%(lineno)d) %(levelname)s - %(name)s: "%(message)s"')
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"'
|
||||
)
|
||||
|
||||
console_output_handler = logging.StreamHandler(sys.stderr)
|
||||
console_output_handler.setFormatter(formatter)
|
||||
@ -22,6 +25,8 @@ from telebot import apihelper, types, util
|
||||
"""
|
||||
Module : telebot
|
||||
"""
|
||||
|
||||
|
||||
class TeleBot:
|
||||
""" This is TeleBot Class
|
||||
Methods:
|
||||
@ -39,30 +44,37 @@ class TeleBot:
|
||||
getUpdates
|
||||
"""
|
||||
|
||||
def __init__(self, token, create_threads=True, num_threads=4):
|
||||
def __init__(self, token, threaded=True):
|
||||
"""
|
||||
:param token: bot API token
|
||||
:param create_threads: Create thread for message handler
|
||||
:param num_threads: Number of worker in thread pool.
|
||||
:return: Telebot object.
|
||||
"""
|
||||
self.token = token
|
||||
self.update_listener = []
|
||||
self.polling_thread = None
|
||||
|
||||
self.__stop_polling = threading.Event()
|
||||
self.last_update_id = 0
|
||||
self.num_threads = num_threads
|
||||
self.__create_threads = create_threads
|
||||
self.exc_info = None
|
||||
|
||||
self.message_subscribers_messages = []
|
||||
self.message_subscribers_callbacks = []
|
||||
self.message_subscribers_lock = threading.Lock()
|
||||
|
||||
# key: chat_id, value: handler list
|
||||
self.message_subscribers_next_step = {}
|
||||
self.pre_message_subscribers_next_step = {}
|
||||
|
||||
self.message_handlers = []
|
||||
if self.__create_threads:
|
||||
self.worker_pool = util.ThreadPool(num_threads)
|
||||
|
||||
self.threaded = threaded
|
||||
if self.threaded:
|
||||
self.worker_pool = util.ThreadPool()
|
||||
|
||||
def set_webhook(self, url=None, certificate=None):
|
||||
return apihelper.set_webhook(self.token, url, certificate)
|
||||
|
||||
def remove_webhook(self):
|
||||
return self.set_webhook() # No params resets webhook
|
||||
|
||||
def get_updates(self, offset=None, limit=None, timeout=20):
|
||||
"""
|
||||
@ -78,13 +90,13 @@ class TeleBot:
|
||||
ret.append(types.Update.de_json(ju))
|
||||
return ret
|
||||
|
||||
def get_update(self):
|
||||
def __retrieve_updates(self, timeout=20):
|
||||
"""
|
||||
Retrieves any updates from the Telegram API.
|
||||
Registered listeners and applicable message handlers will be notified when a new message arrives.
|
||||
:raises ApiException when a call has failed.
|
||||
"""
|
||||
updates = self.get_updates(offset=(self.last_update_id + 1), timeout=3)
|
||||
updates = self.get_updates(offset=(self.last_update_id + 1), timeout=timeout)
|
||||
new_messages = []
|
||||
for update in updates:
|
||||
if update.update_id > self.last_update_id:
|
||||
@ -95,6 +107,7 @@ class TeleBot:
|
||||
self.process_new_messages(new_messages)
|
||||
|
||||
def process_new_messages(self, new_messages):
|
||||
self._append_pre_next_step_handler()
|
||||
self.__notify_update(new_messages)
|
||||
self._notify_command_handlers(new_messages)
|
||||
self._notify_message_subscribers(new_messages)
|
||||
@ -102,59 +115,98 @@ class TeleBot:
|
||||
|
||||
def __notify_update(self, new_messages):
|
||||
for listener in self.update_listener:
|
||||
if self.__create_threads:
|
||||
self.worker_pool.put(listener, new_messages)
|
||||
else:
|
||||
listener(new_messages)
|
||||
self.__exec_task(listener, new_messages)
|
||||
|
||||
def polling(self, none_stop=False, interval=0, block=True):
|
||||
def polling(self, none_stop=False, interval=0, timeout=20):
|
||||
"""
|
||||
This function creates a new Thread that calls an internal __polling function.
|
||||
This function creates a new Thread that calls an internal __retrieve_updates function.
|
||||
This allows the bot to retrieve Updates automagically and notify listeners and message handlers accordingly.
|
||||
|
||||
Do not call this function more than once!
|
||||
Warning: Do not call this function more than once!
|
||||
|
||||
Always get updates.
|
||||
:param none_stop: Do not stop polling when Exception occur.
|
||||
:param none_stop: Do not stop polling when an ApiException occurs.
|
||||
:param timeout: Timeout in seconds for long polling.
|
||||
:return:
|
||||
"""
|
||||
self.__stop_polling.set()
|
||||
if self.polling_thread:
|
||||
self.polling_thread.join() # wait thread stop.
|
||||
self.__stop_polling.clear()
|
||||
self.polling_thread = threading.Thread(target=self.__polling, args=([none_stop, interval]))
|
||||
self.polling_thread.daemon = True
|
||||
self.polling_thread.start()
|
||||
if self.threaded:
|
||||
self.__threaded_polling(none_stop, interval, timeout)
|
||||
else:
|
||||
self.__non_threaded_polling(none_stop, interval, timeout)
|
||||
|
||||
if block:
|
||||
while self.polling_thread.is_alive:
|
||||
try:
|
||||
time.sleep(.1)
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Received KeyboardInterrupt. Stopping.")
|
||||
self.stop_polling()
|
||||
self.polling_thread.join()
|
||||
break
|
||||
|
||||
def __polling(self, none_stop, interval):
|
||||
def __threaded_polling(self, none_stop=False, interval=0, timeout=3):
|
||||
logger.info('Started polling.')
|
||||
|
||||
self.__stop_polling.clear()
|
||||
error_interval = .25
|
||||
|
||||
polling_thread = util.WorkerThread(name="PollingThread")
|
||||
or_event = util.OrEvent(
|
||||
polling_thread.done_event,
|
||||
polling_thread.exception_event,
|
||||
self.worker_pool.exception_event
|
||||
)
|
||||
|
||||
while not self.__stop_polling.wait(interval):
|
||||
or_event.clear()
|
||||
try:
|
||||
self.get_update()
|
||||
polling_thread.put(self.__retrieve_updates, timeout)
|
||||
|
||||
or_event.wait() # wait for polling thread finish, polling thread error or thread pool error
|
||||
|
||||
polling_thread.raise_exceptions()
|
||||
self.worker_pool.raise_exceptions()
|
||||
|
||||
error_interval = .25
|
||||
except apihelper.ApiException as e:
|
||||
logger.error(e)
|
||||
if not none_stop:
|
||||
self.__stop_polling.set()
|
||||
logger.info("Exception occurred. Stopping.")
|
||||
else:
|
||||
polling_thread.clear_exceptions()
|
||||
self.worker_pool.clear_exceptions()
|
||||
logger.info("Waiting for {0} seconds until retry".format(error_interval))
|
||||
time.sleep(error_interval)
|
||||
error_interval *= 2
|
||||
logger.error(e)
|
||||
except KeyboardInterrupt:
|
||||
logger.info("KeyboardInterrupt received.")
|
||||
self.__stop_polling.set()
|
||||
polling_thread.stop()
|
||||
break
|
||||
|
||||
logger.info('Stopped polling.')
|
||||
|
||||
def __non_threaded_polling(self, none_stop=False, interval=0, timeout=3):
|
||||
logger.info('Started polling.')
|
||||
self.__stop_polling.clear()
|
||||
error_interval = .25
|
||||
|
||||
while not self.__stop_polling.wait(interval):
|
||||
try:
|
||||
self.__retrieve_updates(timeout)
|
||||
error_interval = .25
|
||||
except apihelper.ApiException as e:
|
||||
logger.error(e)
|
||||
if not none_stop:
|
||||
self.__stop_polling.set()
|
||||
logger.info("Exception occurred. Stopping.")
|
||||
else:
|
||||
logger.info("Waiting for {0} seconds until retry".format(error_interval))
|
||||
time.sleep(error_interval)
|
||||
error_interval *= 2
|
||||
except KeyboardInterrupt:
|
||||
logger.info("KeyboardInterrupt received.")
|
||||
self.__stop_polling.set()
|
||||
break
|
||||
|
||||
logger.info('Stopped polling.')
|
||||
|
||||
def __exec_task(self, task, *args, **kwargs):
|
||||
if self.threaded:
|
||||
self.worker_pool.put(task, *args, **kwargs)
|
||||
else:
|
||||
task(*args, **kwargs)
|
||||
|
||||
def stop_polling(self):
|
||||
self.__stop_polling.set()
|
||||
|
||||
@ -336,11 +388,12 @@ class TeleBot:
|
||||
:param callback: The callback function to be called when a reply arrives. Must accept one `message`
|
||||
parameter, which will contain the replied message.
|
||||
"""
|
||||
self.message_subscribers_messages.insert(0, message.message_id)
|
||||
self.message_subscribers_callbacks.insert(0, callback)
|
||||
if len(self.message_subscribers_messages) > 10000:
|
||||
self.message_subscribers_messages.pop()
|
||||
self.message_subscribers_callbacks.pop()
|
||||
with self.message_subscribers_lock:
|
||||
self.message_subscribers_messages.insert(0, message.message_id)
|
||||
self.message_subscribers_callbacks.insert(0, callback)
|
||||
if len(self.message_subscribers_messages) > 10000:
|
||||
self.message_subscribers_messages.pop()
|
||||
self.message_subscribers_callbacks.pop()
|
||||
|
||||
def _notify_message_subscribers(self, new_messages):
|
||||
for message in new_messages:
|
||||
@ -352,8 +405,10 @@ class TeleBot:
|
||||
index = self.message_subscribers_messages.index(reply_msg_id)
|
||||
self.message_subscribers_callbacks[index](message)
|
||||
|
||||
del self.message_subscribers_messages[index]
|
||||
del self.message_subscribers_callbacks[index]
|
||||
with self.message_subscribers_lock:
|
||||
index = self.message_subscribers_messages.index(reply_msg_id)
|
||||
del self.message_subscribers_messages[index]
|
||||
del self.message_subscribers_callbacks[index]
|
||||
|
||||
def register_next_step_handler(self, message, callback):
|
||||
"""
|
||||
@ -363,10 +418,10 @@ class TeleBot:
|
||||
:param callback: The callback function which next new message arrives.
|
||||
"""
|
||||
chat_id = message.chat.id
|
||||
if chat_id in self.message_subscribers_next_step:
|
||||
self.message_subscribers_next_step[chat_id].append(callback)
|
||||
if chat_id in self.pre_message_subscribers_next_step:
|
||||
self.pre_message_subscribers_next_step[chat_id].append(callback)
|
||||
else:
|
||||
self.message_subscribers_next_step[chat_id] = [callback]
|
||||
self.pre_message_subscribers_next_step[chat_id] = [callback]
|
||||
|
||||
def _notify_message_next_handler(self, new_messages):
|
||||
for message in new_messages:
|
||||
@ -374,9 +429,17 @@ class TeleBot:
|
||||
if chat_id in self.message_subscribers_next_step:
|
||||
handlers = self.message_subscribers_next_step[chat_id]
|
||||
for handler in handlers:
|
||||
self.worker_pool.put(handler, message)
|
||||
self.__exec_task(handler, message)
|
||||
self.message_subscribers_next_step.pop(chat_id, None)
|
||||
|
||||
def _append_pre_next_step_handler(self):
|
||||
for k in self.pre_message_subscribers_next_step.keys():
|
||||
if k in self.message_subscribers_next_step:
|
||||
self.message_subscribers_next_step[k].extend(self.pre_message_subscribers_next_step[k])
|
||||
else:
|
||||
self.message_subscribers_next_step[k] = self.pre_message_subscribers_next_step[k]
|
||||
self.pre_message_subscribers_next_step = {}
|
||||
|
||||
def message_handler(self, commands=None, regexp=None, func=None, content_types=['text']):
|
||||
"""
|
||||
Message handler decorator.
|
||||
@ -406,6 +469,7 @@ class TeleBot:
|
||||
:param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message.
|
||||
:param content_types: This commands' supported content types. Must be a list. Defaults to ['text'].
|
||||
"""
|
||||
|
||||
def decorator(fn):
|
||||
handler_dict = {'function': fn}
|
||||
filters = {'content_types': content_types}
|
||||
@ -444,10 +508,7 @@ class TeleBot:
|
||||
for message in new_messages:
|
||||
for message_handler in self.message_handlers:
|
||||
if self._test_message_handler(message_handler, message):
|
||||
if self.__create_threads:
|
||||
self.worker_pool.put(message_handler['function'], message)
|
||||
else:
|
||||
message_handler['function'](message)
|
||||
self.__exec_task(message_handler['function'], message)
|
||||
break
|
||||
|
||||
|
||||
|
@ -43,14 +43,14 @@ def _check_result(method_name, result):
|
||||
"""
|
||||
if result.status_code != 200:
|
||||
msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]'\
|
||||
.format(result.status_code, result.reason, result.text)
|
||||
.format(result.status_code, result.reason, result.text.encode('utf8'))
|
||||
raise ApiException(msg, method_name, result)
|
||||
|
||||
try:
|
||||
result_json = result.json()
|
||||
except:
|
||||
msg = 'The server returned an invalid JSON response. Response body:\n[{0}]'\
|
||||
.format(result.text)
|
||||
.format(result.text.encode('utf8'))
|
||||
raise ApiException(msg, method_name, result)
|
||||
|
||||
if not result_json['ok']:
|
||||
@ -105,6 +105,18 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def set_webhook(token, url=None, certificate=None):
|
||||
method_url = 'setWebhook'
|
||||
payload = {
|
||||
'url': url if url else "",
|
||||
}
|
||||
files = None
|
||||
if certificate:
|
||||
files = {'certificate': certificate}
|
||||
|
||||
return _make_request(token, method_url, params=payload, files=files)
|
||||
|
||||
|
||||
def get_updates(token, offset=None, limit=None, timeout=None):
|
||||
method_url = r'getUpdates'
|
||||
payload = {}
|
||||
|
148
telebot/types.py
148
telebot/types.py
@ -21,6 +21,7 @@ ForceReply
|
||||
"""
|
||||
|
||||
import json
|
||||
import six
|
||||
|
||||
|
||||
class JsonSerializable:
|
||||
@ -28,6 +29,7 @@ class JsonSerializable:
|
||||
Subclasses of this class are guaranteed to be able to be converted to JSON format.
|
||||
All subclasses of this class must override to_json.
|
||||
"""
|
||||
|
||||
def to_json(self):
|
||||
"""
|
||||
Returns a JSON string representation of this class.
|
||||
@ -43,6 +45,7 @@ class JsonDeserializable:
|
||||
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.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, json_type):
|
||||
"""
|
||||
@ -70,13 +73,14 @@ class JsonDeserializable:
|
||||
|
||||
def __str__(self):
|
||||
d = {}
|
||||
for x, y in self.__dict__.iteritems():
|
||||
for x, y in six.iteritems(self.__dict__):
|
||||
if hasattr(y, '__dict__'):
|
||||
d[x] = y.__dict__
|
||||
else:
|
||||
d[x] = y
|
||||
|
||||
return unicode(d)
|
||||
return six.text_type(d)
|
||||
|
||||
|
||||
class Update(JsonDeserializable):
|
||||
@classmethod
|
||||
@ -84,25 +88,22 @@ class Update(JsonDeserializable):
|
||||
obj = cls.check_json(json_type)
|
||||
update_id = obj['update_id']
|
||||
message = Message.de_json(obj['message'])
|
||||
return Update(update_id, message)
|
||||
return cls(update_id, message)
|
||||
|
||||
def __init__(self, update_id, message):
|
||||
self.update_id = update_id
|
||||
self.message = message
|
||||
|
||||
|
||||
class User(JsonDeserializable):
|
||||
@classmethod
|
||||
def de_json(cls, json_string):
|
||||
obj = cls.check_json(json_string)
|
||||
id = obj['id']
|
||||
first_name = obj['first_name']
|
||||
last_name = None
|
||||
username = None
|
||||
if 'last_name' in obj:
|
||||
last_name = obj['last_name']
|
||||
if 'username' in obj:
|
||||
username = obj['username']
|
||||
return User(id, first_name, last_name, username)
|
||||
last_name = obj.get('last_name')
|
||||
username = obj.get('username')
|
||||
return cls(id, first_name, last_name, username)
|
||||
|
||||
def __init__(self, id, first_name, last_name=None, username=None):
|
||||
self.id = id
|
||||
@ -117,20 +118,43 @@ class GroupChat(JsonDeserializable):
|
||||
obj = cls.check_json(json_string)
|
||||
id = obj['id']
|
||||
title = obj['title']
|
||||
return GroupChat(id, title)
|
||||
return cls(id, title)
|
||||
|
||||
def __init__(self, id, title):
|
||||
self.id = id
|
||||
self.title = title
|
||||
|
||||
|
||||
class Chat(JsonDeserializable):
|
||||
@classmethod
|
||||
def de_json(cls, json_string):
|
||||
obj = cls.check_json(json_string)
|
||||
id = obj['id']
|
||||
type = obj['type']
|
||||
title = obj.get('title')
|
||||
username = obj.get('username')
|
||||
first_name = obj.get('first_name')
|
||||
last_name = obj.get('last_name')
|
||||
return cls(id, type, title, username, first_name, last_name)
|
||||
|
||||
def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None):
|
||||
self.type = type
|
||||
self.last_name = last_name
|
||||
self.first_name = first_name
|
||||
self.username = username
|
||||
self.id = id
|
||||
self.title = title
|
||||
|
||||
|
||||
class Message(JsonDeserializable):
|
||||
@classmethod
|
||||
def de_json(cls, json_string):
|
||||
obj = cls.check_json(json_string)
|
||||
message_id = obj['message_id']
|
||||
from_user = User.de_json(obj['from'])
|
||||
chat = Message.parse_chat(obj['chat'])
|
||||
from_user = None
|
||||
if 'from' in obj:
|
||||
from_user = User.de_json(obj['from'])
|
||||
chat = Chat.de_json(obj['chat'])
|
||||
date = obj['date']
|
||||
content_type = None
|
||||
opts = {}
|
||||
@ -187,7 +211,7 @@ class Message(JsonDeserializable):
|
||||
content_type = 'group_chat_created'
|
||||
if 'caption' in obj:
|
||||
opts['caption'] = obj['caption']
|
||||
return Message(message_id, from_user, date, chat, content_type, opts)
|
||||
return cls(message_id, from_user, date, chat, content_type, opts)
|
||||
|
||||
@classmethod
|
||||
def parse_chat(cls, chat):
|
||||
@ -220,10 +244,8 @@ class PhotoSize(JsonDeserializable):
|
||||
file_id = obj['file_id']
|
||||
width = obj['width']
|
||||
height = obj['height']
|
||||
file_size = None
|
||||
if 'file_size' in obj:
|
||||
file_size = obj['file_size']
|
||||
return PhotoSize(file_id, width, height, file_size)
|
||||
file_size = obj.get('file_size')
|
||||
return cls(file_id, width, height, file_size)
|
||||
|
||||
def __init__(self, file_id, width, height, file_size=None):
|
||||
self.file_size = file_size
|
||||
@ -238,19 +260,11 @@ class Audio(JsonDeserializable):
|
||||
obj = cls.check_json(json_string)
|
||||
file_id = obj['file_id']
|
||||
duration = obj['duration']
|
||||
performer = None
|
||||
title = None
|
||||
mime_type = None
|
||||
file_size = None
|
||||
if 'mime_type' in obj:
|
||||
mime_type = obj['mime_type']
|
||||
if 'file_size' in obj:
|
||||
file_size = obj['file_size']
|
||||
if 'performer' in obj:
|
||||
performer = obj['performer']
|
||||
if 'title' in obj:
|
||||
title = obj['title']
|
||||
return Audio(file_id, duration, performer, title, mime_type, file_size)
|
||||
performer = obj.get('performer')
|
||||
title = obj.get('title')
|
||||
mime_type = obj.get('mime_type')
|
||||
file_size = obj.get('file_size')
|
||||
return cls(file_id, duration, performer, title, mime_type, file_size)
|
||||
|
||||
def __init__(self, file_id, duration, performer=None, title=None, mime_type=None, file_size=None):
|
||||
self.file_id = file_id
|
||||
@ -267,13 +281,9 @@ class Voice(JsonDeserializable):
|
||||
obj = cls.check_json(json_string)
|
||||
file_id = obj['file_id']
|
||||
duration = obj['duration']
|
||||
mime_type = None
|
||||
file_size = None
|
||||
if 'mime_type' in obj:
|
||||
mime_type = obj['mime_type']
|
||||
if 'file_size' in obj:
|
||||
file_size = obj['file_size']
|
||||
return Voice(file_id, duration, mime_type, file_size)
|
||||
mime_type = obj.get('mime_type')
|
||||
file_size = obj.get('file_size')
|
||||
return cls(file_id, duration, mime_type, file_size)
|
||||
|
||||
def __init__(self, file_id, duration, mime_type=None, file_size=None):
|
||||
self.file_id = file_id
|
||||
@ -288,19 +298,12 @@ class Document(JsonDeserializable):
|
||||
obj = cls.check_json(json_string)
|
||||
file_id = obj['file_id']
|
||||
thumb = None
|
||||
if 'thumb' in obj:
|
||||
if 'file_id' in obj['thumb']:
|
||||
thumb = PhotoSize.de_json(obj['thumb'])
|
||||
file_name = None
|
||||
mime_type = None
|
||||
file_size = None
|
||||
if 'file_name' in obj:
|
||||
file_name = obj['file_name']
|
||||
if 'mime_type' in obj:
|
||||
mime_type = obj['mime_type']
|
||||
if 'file_size' in obj:
|
||||
file_size = obj['file_size']
|
||||
return Document(file_id, thumb, file_name, mime_type, file_size)
|
||||
if 'thumb' in obj and 'file_id' in obj['thumb']:
|
||||
thumb = PhotoSize.de_json(obj['thumb'])
|
||||
file_name = obj.get('file_name')
|
||||
mime_type = obj.get('mime_type')
|
||||
file_size = obj.get('file_size')
|
||||
return cls(file_id, thumb, file_name, mime_type, file_size)
|
||||
|
||||
def __init__(self, file_id, thumb, file_name=None, mime_type=None, file_size=None):
|
||||
self.file_id = file_id
|
||||
@ -320,10 +323,8 @@ class Sticker(JsonDeserializable):
|
||||
thumb = None
|
||||
if 'thumb' in obj:
|
||||
thumb = PhotoSize.de_json(obj['thumb'])
|
||||
file_size = None
|
||||
if 'file_size' in obj:
|
||||
file_size = obj['file_size']
|
||||
return Sticker(file_id, width, height, thumb, file_size)
|
||||
file_size = obj.get('file_size')
|
||||
return cls(file_id, width, height, thumb, file_size)
|
||||
|
||||
def __init__(self, file_id, width, height, thumb, file_size=None):
|
||||
self.file_id = file_id
|
||||
@ -342,15 +343,11 @@ class Video(JsonDeserializable):
|
||||
height = obj['height']
|
||||
duration = obj['duration']
|
||||
thumb = None
|
||||
mime_type = None
|
||||
file_size = None
|
||||
if 'thumb' in obj:
|
||||
thumb = PhotoSize.de_json(obj['thumb'])
|
||||
if 'mime_type' in obj:
|
||||
mime_type = obj['mime_type']
|
||||
if 'file_size' in obj:
|
||||
file_size = obj['file_size']
|
||||
return Video(file_id, width, height, duration, thumb, mime_type, file_size)
|
||||
mime_type = obj.get('mime_type')
|
||||
file_size = obj.get('file_size')
|
||||
return cls(file_id, width, height, duration, thumb, mime_type, file_size)
|
||||
|
||||
def __init__(self, file_id, width, height, duration, thumb=None, mime_type=None, file_size=None):
|
||||
self.file_id = file_id
|
||||
@ -368,13 +365,9 @@ class Contact(JsonDeserializable):
|
||||
obj = cls.check_json(json_string)
|
||||
phone_number = obj['phone_number']
|
||||
first_name = obj['first_name']
|
||||
last_name = None
|
||||
user_id = None
|
||||
if 'last_name' in obj:
|
||||
last_name = obj['last_name']
|
||||
if 'user_id' in obj:
|
||||
user_id = obj['user_id']
|
||||
return Contact(phone_number, first_name, last_name, user_id)
|
||||
last_name = obj.get('last_name')
|
||||
user_id = obj.get('user_id')
|
||||
return cls(phone_number, first_name, last_name, user_id)
|
||||
|
||||
def __init__(self, phone_number, first_name, last_name=None, user_id=None):
|
||||
self.phone_number = phone_number
|
||||
@ -389,7 +382,7 @@ class Location(JsonDeserializable):
|
||||
obj = cls.check_json(json_string)
|
||||
longitude = obj['longitude']
|
||||
latitude = obj['latitude']
|
||||
return Location(longitude, latitude)
|
||||
return cls(longitude, latitude)
|
||||
|
||||
def __init__(self, longitude, latitude):
|
||||
self.longitude = longitude
|
||||
@ -402,26 +395,21 @@ class UserProfilePhotos(JsonDeserializable):
|
||||
obj = cls.check_json(json_string)
|
||||
total_count = obj['total_count']
|
||||
photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']]
|
||||
return UserProfilePhotos(total_count, photos)
|
||||
return cls(total_count, photos)
|
||||
|
||||
def __init__(self, total_count, photos):
|
||||
self.total_count = total_count
|
||||
self.photos = photos
|
||||
|
||||
class File(JsonDeserializable):
|
||||
|
||||
class File(JsonDeserializable):
|
||||
@classmethod
|
||||
def de_json(cls, json_type):
|
||||
obj = cls.check_json(json_type)
|
||||
file_id = obj['file_id']
|
||||
|
||||
file_size = None
|
||||
file_path = None
|
||||
if 'file_size' in obj:
|
||||
file_size = obj['file_size']
|
||||
if 'file_path' in obj:
|
||||
file_path = obj['file_path']
|
||||
return File(file_id, file_size, file_path)
|
||||
file_size = obj.get('file_size')
|
||||
file_path = obj.get('file_path')
|
||||
return cls(file_id, file_size, file_path)
|
||||
|
||||
def __init__(self, file_id, file_size, file_path):
|
||||
self.file_id = file_id
|
||||
|
126
telebot/util.py
126
telebot/util.py
@ -1,54 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import threading
|
||||
import sys
|
||||
import six
|
||||
from six import string_types
|
||||
|
||||
# Python3 queue support.
|
||||
|
||||
try:
|
||||
import Queue
|
||||
except ImportError:
|
||||
import queue as Queue
|
||||
|
||||
from telebot import logger
|
||||
|
||||
class ThreadPool:
|
||||
class WorkerThread(threading.Thread):
|
||||
|
||||
class WorkerThread(threading.Thread):
|
||||
count = 0
|
||||
|
||||
def __init__(self, queue):
|
||||
threading.Thread.__init__(self, name="WorkerThread{0}".format(self.__class__.count + 1))
|
||||
self.__class__.count += 1
|
||||
def __init__(self, exception_callback=None, queue=None, name=None):
|
||||
if not name:
|
||||
name = "WorkerThread{0}".format(self.__class__.count + 1)
|
||||
self.__class__.count += 1
|
||||
if not queue:
|
||||
queue = Queue.Queue()
|
||||
|
||||
threading.Thread.__init__(self, name=name)
|
||||
self.queue = queue
|
||||
self.daemon = True
|
||||
|
||||
self.received_task_event = threading.Event()
|
||||
self.done_event = threading.Event()
|
||||
self.exception_event = threading.Event()
|
||||
self.continue_event = threading.Event()
|
||||
|
||||
self.exception_callback = exception_callback
|
||||
self.exc_info = None
|
||||
self._running = True
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
while self._running:
|
||||
try:
|
||||
task, args, kwargs = self.queue.get(block=True, timeout=.01)
|
||||
task, args, kwargs = self.queue.get(block=True, timeout=.5)
|
||||
self.continue_event.clear()
|
||||
self.received_task_event.clear()
|
||||
self.done_event.clear()
|
||||
self.exception_event.clear()
|
||||
logger.debug("Received task")
|
||||
self.received_task_event.set()
|
||||
|
||||
task(*args, **kwargs)
|
||||
logger.debug("Task complete")
|
||||
self.done_event.set()
|
||||
except Queue.Empty:
|
||||
pass
|
||||
except:
|
||||
logger.debug("Exception occurred")
|
||||
self.exc_info = sys.exc_info()
|
||||
self.exception_event.set()
|
||||
|
||||
if self.exception_callback:
|
||||
self.exception_callback(self, self.exc_info)
|
||||
self.continue_event.wait()
|
||||
|
||||
def put(self, task, *args, **kwargs):
|
||||
self.queue.put((task, args, kwargs))
|
||||
|
||||
def raise_exceptions(self):
|
||||
if self.exception_event.is_set():
|
||||
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
|
||||
|
||||
def clear_exceptions(self):
|
||||
self.exception_event.clear()
|
||||
self.continue_event.set()
|
||||
|
||||
def stop(self):
|
||||
self._running = False
|
||||
|
||||
def __init__(self, num_threads=4):
|
||||
self.tasks = Queue.Queue()
|
||||
self.workers = [self.WorkerThread(self.tasks) for _ in range(num_threads)]
|
||||
|
||||
class ThreadPool:
|
||||
|
||||
def __init__(self, num_threads=2):
|
||||
self.tasks = Queue.Queue()
|
||||
self.workers = [WorkerThread(self.on_exception, self.tasks) for _ in range(num_threads)]
|
||||
self.num_threads = num_threads
|
||||
|
||||
self.exception_event = threading.Event()
|
||||
self.exc_info = None
|
||||
|
||||
def put(self, func, *args, **kwargs):
|
||||
self.tasks.put((func, args, kwargs))
|
||||
|
||||
def on_exception(self, worker_thread, exc_info):
|
||||
self.exc_info = exc_info
|
||||
self.exception_event.set()
|
||||
worker_thread.continue_event.set()
|
||||
|
||||
def raise_exceptions(self):
|
||||
if self.exception_event.is_set():
|
||||
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
|
||||
|
||||
def clear_exceptions(self):
|
||||
self.exception_event.clear()
|
||||
|
||||
def close(self):
|
||||
for worker in self.workers:
|
||||
worker.stop()
|
||||
for worker in self.workers:
|
||||
worker.join()
|
||||
|
||||
|
||||
class AsyncTask:
|
||||
def __init__(self, target, *args, **kwargs):
|
||||
self.target = target
|
||||
@ -62,15 +124,15 @@ class AsyncTask:
|
||||
def _run(self):
|
||||
try:
|
||||
self.result = self.target(*self.args, **self.kwargs)
|
||||
except Exception as e:
|
||||
self.result = e
|
||||
except:
|
||||
self.result = sys.exc_info()
|
||||
self.done = True
|
||||
|
||||
def wait(self):
|
||||
if not self.done:
|
||||
self.thread.join()
|
||||
if isinstance(self.result, Exception):
|
||||
raise self.result
|
||||
if isinstance(self.result, BaseException):
|
||||
six.reraise(self.result[0], self.result[1], self.result[2])
|
||||
else:
|
||||
return self.result
|
||||
|
||||
@ -124,3 +186,41 @@ def split_string(text, chars_per_string):
|
||||
:return: The splitted text as a list of strings.
|
||||
"""
|
||||
return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)]
|
||||
|
||||
# CREDITS TO http://stackoverflow.com/questions/12317940#answer-12320352
|
||||
def or_set(self):
|
||||
self._set()
|
||||
self.changed()
|
||||
|
||||
|
||||
def or_clear(self):
|
||||
self._clear()
|
||||
self.changed()
|
||||
|
||||
|
||||
def orify(e, changed_callback):
|
||||
e._set = e.set
|
||||
e._clear = e.clear
|
||||
e.changed = changed_callback
|
||||
e.set = lambda: or_set(e)
|
||||
e.clear = lambda: or_clear(e)
|
||||
|
||||
def OrEvent(*events):
|
||||
or_event = threading.Event()
|
||||
def changed():
|
||||
bools = [e.is_set() for e in events]
|
||||
if any(bools):
|
||||
or_event.set()
|
||||
else:
|
||||
or_event.clear()
|
||||
|
||||
def busy_wait():
|
||||
while not or_event.is_set():
|
||||
or_event._wait(3)
|
||||
|
||||
for e in events:
|
||||
orify(e, changed)
|
||||
or_event._wait = or_event.wait
|
||||
or_event.wait = busy_wait
|
||||
changed()
|
||||
return or_event
|
||||
|
@ -101,7 +101,6 @@ class TestTeleBot:
|
||||
ret_msg = tb.send_message(CHAT_ID, markdown, parse_mode="Markdown")
|
||||
assert ret_msg.message_id
|
||||
|
||||
|
||||
def test_send_file(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
@ -204,6 +203,13 @@ class TestTeleBot:
|
||||
assert int(ret_msg.location.longitude) == int(lon)
|
||||
assert int(ret_msg.location.latitude) == int(lat)
|
||||
|
||||
def test_Chat(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
me = tb.get_me()
|
||||
msg = tb.send_message(CHAT_ID, 'Test')
|
||||
assert me.id == msg.from_user.id
|
||||
assert msg.chat.id == int(CHAT_ID)
|
||||
|
||||
def create_text_message(self, text):
|
||||
params = {'text': text}
|
||||
chat = types.User(11, 'test')
|
||||
@ -220,4 +226,3 @@ class TestTeleBot:
|
||||
def test_not_string(self):
|
||||
i1 = 10
|
||||
assert not util.is_string(i1)
|
||||
|
||||
|
@ -12,13 +12,13 @@ def test_json_user():
|
||||
|
||||
|
||||
def test_json_message():
|
||||
jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"chat":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"date":1435296025,"text":"HIHI"}'
|
||||
jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"chat":{"id":1734,"first_name":"F","type":"private","last_name":"Wa","username":"oir"},"date":1435296025,"text":"HIHI"}'
|
||||
msg = types.Message.de_json(jsonstring)
|
||||
assert msg.text == 'HIHI'
|
||||
|
||||
|
||||
def test_json_message_group():
|
||||
json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG"},"chat":{"id":-866,"title":"\u4ea4"},"date":1435303157,"text":"HIHI"}'
|
||||
json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG"},"chat":{"id":-866,"type":"private","title":"\u4ea4"},"date":1435303157,"text":"HIHI"}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.text == 'HIHI'
|
||||
assert len(msg.chat.title) != 0
|
||||
@ -39,7 +39,7 @@ def test_json_Document():
|
||||
|
||||
|
||||
def test_json_Message_Audio():
|
||||
json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd"},"chat":{"id":10834,"first_name":"dd","last_name":"dd","username":"dd"},"date":1439978364,"audio":{"duration":1,"mime_type":"audio\/mpeg","title":"pyTelegram","performer":"eternnoir","file_id":"BQADBQADDH1JaB8-1KyWUss2-Ag","file_size":20096}}'
|
||||
json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd"},"chat":{"id":10834,"first_name":"dd","type":"private","type":"private","last_name":"dd","username":"dd"},"date":1439978364,"audio":{"duration":1,"mime_type":"audio\/mpeg","title":"pyTelegram","performer":"eternnoir","file_id":"BQADBQADDH1JaB8-1KyWUss2-Ag","file_size":20096}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.audio.duration == 1
|
||||
assert msg.content_type == 'audio'
|
||||
@ -48,7 +48,7 @@ def test_json_Message_Audio():
|
||||
|
||||
|
||||
def test_json_Message_Sticker():
|
||||
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.sticker.height == 368
|
||||
assert msg.sticker.thumb.height == 60
|
||||
@ -56,7 +56,7 @@ def test_json_Message_Sticker():
|
||||
|
||||
|
||||
def test_json_Message_Sticker_without_thumb():
|
||||
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.sticker.height == 368
|
||||
assert msg.sticker.thumb == None
|
||||
@ -64,21 +64,21 @@ def test_json_Message_Sticker_without_thumb():
|
||||
|
||||
|
||||
def test_json_Message_Document():
|
||||
json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10,"first_name":"Fd","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}'
|
||||
json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.document.file_name == 'Text File'
|
||||
assert msg.content_type == 'document'
|
||||
|
||||
|
||||
def test_json_Message_Photo():
|
||||
json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}'
|
||||
json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert len(msg.photo) == 3
|
||||
assert msg.content_type == 'photo'
|
||||
|
||||
|
||||
def test_json_Message_Video():
|
||||
json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}'
|
||||
json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":109734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.video
|
||||
assert msg.video.duration == 3
|
||||
@ -87,7 +87,7 @@ def test_json_Message_Video():
|
||||
|
||||
|
||||
def test_json_Message_Location():
|
||||
json_string = r'{"message_id":102,"from":{"id":108734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":1089734,"first_name":"dd","last_name":"dd","username":"dd"},"date":1535482469,"location":{"longitude":127.479471,"latitude":26.090577}}'
|
||||
json_string = r'{"message_id":102,"from":{"id":108734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":1089734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1535482469,"location":{"longitude":127.479471,"latitude":26.090577}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.location.latitude == 26.090577
|
||||
assert msg.content_type == 'location'
|
||||
@ -114,9 +114,16 @@ def test_json_voice():
|
||||
assert voice.file_size == 10481
|
||||
|
||||
def test_json_update():
|
||||
json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}'
|
||||
json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","type":"private","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}'
|
||||
update = types.Update.de_json(json_string)
|
||||
assert update.update_id == 938203
|
||||
assert update.message.message_id == 241
|
||||
assert update.message.from_user.id == 9734
|
||||
|
||||
def test_json_chat():
|
||||
json_string = r'{"id": -111111,"title": "Test Title","type": "group"}'
|
||||
chat = types.Chat.de_json(json_string)
|
||||
assert chat.id == -111111
|
||||
assert chat.type == 'group'
|
||||
assert chat.title == 'Test Title'
|
||||
|
||||
|
Reference in New Issue
Block a user