diff --git a/README.md b/README.md index 5853544..e46ce0a 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,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 @@ -208,7 +208,7 @@ tb.polling(none_stop=False, interval=0, block=True) user = tb.get_me() # setWebhook -tb.set_webhook(url="http://example.com", cert=open('mycert.pem')) +tb.set_webhook(url="http://example.com", certificate=open('mycert.pem')) # unset webhook tb.remove_webhook() @@ -373,8 +373,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 @@ -423,4 +425,4 @@ We now have a Telegram Channel as well! Keep yourself up to date with API change ## 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. \ No newline at end of file +Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. diff --git a/README.rst b/README.rst index a0a9675..313dc49 100644 --- a/README.rst +++ b/README.rst @@ -52,6 +52,7 @@ 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 `__ | ++------------------+---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 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 `__ 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 `__. +We now have a Telegram Channel as well! Keep yourself up to date with +API changes, and `join it `__. More examples ============= @@ -524,5 +579,14 @@ More examples - `next\_step\_handler Example `__ +Bots using this API +=================== + +- `SiteAlert bot `__ + (`source `__) 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 diff --git a/examples/webhook_examples/README.md b/examples/webhook_examples/README.md new file mode 100644 index 0000000..2d47346 --- /dev/null +++ b/examples/webhook_examples/README.md @@ -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* diff --git a/examples/webhook_examples/webhook_cherrypy_echo_bot.py b/examples/webhook_examples/webhook_cherrypy_echo_bot.py new file mode 100644 index 0000000..8faac61 --- /dev/null +++ b/examples/webhook_examples/webhook_cherrypy_echo_bot.py @@ -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 = '' + +WEBHOOK_HOST = '' +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) + 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, {'/': {}}) diff --git a/examples/webhook_examples/webhook_cpython_echo_bot.py b/examples/webhook_examples/webhook_cpython_echo_bot.py new file mode 100644 index 0000000..5c1dfc9 --- /dev/null +++ b/examples/webhook_examples/webhook_cpython_echo_bot.py @@ -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 = '' + +WEBHOOK_HOST = '' +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() diff --git a/examples/webhook_examples/webhook_flask_echo_bot.py b/examples/webhook_examples/webhook_flask_echo_bot.py new file mode 100644 index 0000000..a20fcc0 --- /dev/null +++ b/examples/webhook_examples/webhook_flask_echo_bot.py @@ -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 = '' + +WEBHOOK_HOST = '' +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) diff --git a/telebot/__init__.py b/telebot/__init__.py index 86e2977..803a77c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -115,7 +115,7 @@ class TeleBot: for listener in self.update_listener: self.__exec_task(listener, new_messages) - def polling(self, none_stop=False, interval=0, timeout=3): + def polling(self, none_stop=False, interval=0, timeout=20): """ 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. @@ -149,8 +149,7 @@ class TeleBot: try: polling_thread.put(self.__retrieve_updates, timeout) - while not or_event.is_set(): - time.sleep(.05) # wait for polling thread finish, polling thread error or thread pool error + or_event.wait() # wait for polling thread finish, polling thread error or thread pool error polling_thread.raise_exceptions() self.worker_pool.raise_exceptions() diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3c8c529..4c0afb8 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -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']: diff --git a/telebot/types.py b/telebot/types.py index 0d5f2f3..d88ce1b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -29,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. @@ -44,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): """ @@ -71,7 +73,7 @@ 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: @@ -79,6 +81,7 @@ class JsonDeserializable: return six.text_type(d) + class Update(JsonDeserializable): @classmethod def de_json(cls, json_type): @@ -91,6 +94,7 @@ class Update(JsonDeserializable): self.update_id = update_id self.message = message + class User(JsonDeserializable): @classmethod def de_json(cls, json_string): @@ -125,13 +129,44 @@ class GroupChat(JsonDeserializable): 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 = None + username = None + first_name = None + last_name = None + if 'title' in obj: + title = obj['title'] + if 'username' in obj: + username = obj['username'] + if 'first_name'in obj: + first_name = obj['first_name'] + if 'last_name' in obj: + last_name = obj['last_name'] + return Chat(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 = {} @@ -409,8 +444,8 @@ class UserProfilePhotos(JsonDeserializable): 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) diff --git a/telebot/util.py b/telebot/util.py index ee8d13e..47580de 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -203,7 +203,6 @@ def orify(e, changed_callback): e.set = lambda: or_set(e) e.clear = lambda: or_clear(e) - def OrEvent(*events): or_event = threading.Event() def changed(): @@ -212,7 +211,14 @@ def OrEvent(*events): 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 diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 8c72302..dec456a 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -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) - diff --git a/tests/test_types.py b/tests/test_types.py index 75c2638..a77a873 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -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' +