mirror of
https://github.com/eternnoir/pyTelegramBotAPI.git
synced 2023-08-10 21:12:57 +03:00
Compare commits
257 Commits
Author | SHA1 | Date | |
---|---|---|---|
ddc1a4d66f | |||
3d9e012c40 | |||
9b9c0287ec | |||
292191038f | |||
f0e157213c | |||
18330275bd | |||
d173681a7c | |||
0830f2a0f9 | |||
45d6f8980c | |||
28417d18af | |||
e0ae119512 | |||
e851f37712 | |||
40cf8d6903 | |||
e507463741 | |||
3a055734f9 | |||
16ec573c9e | |||
2cd19a1a39 | |||
5f9cf881e6 | |||
baacafe3c0 | |||
734767177d | |||
1e7f71029c | |||
02c22c990b | |||
00d6481818 | |||
a74ed6e3ab | |||
38eb2ba833 | |||
93dc1cd92e | |||
a7469e6b14 | |||
b8f251140d | |||
e2e6b07fdb | |||
a2ceffecc8 | |||
805e3554de | |||
94d34747d1 | |||
8278eef7f3 | |||
94f1bbd402 | |||
c706a7aba3 | |||
c214f8000e | |||
81a201f19f | |||
0f0d76ca82 | |||
fa6f16ca5e | |||
a1267a7670 | |||
79c46bccae | |||
e912546680 | |||
7eeda3bc4d | |||
b5680a1c1f | |||
7514c617fc | |||
32f3a64720 | |||
3a08111f10 | |||
eeff4e7bf8 | |||
c37b3625aa | |||
a3519ff539 | |||
43ad415320 | |||
fb7c79fdfb | |||
413f081b19 | |||
cea0604ede | |||
be639ef4dd | |||
528b23770f | |||
4e472dda48 | |||
17dec34923 | |||
a17b301f1c | |||
cf830a0fb4 | |||
7e9f5b09cf | |||
969c5e76ef | |||
7346326bc3 | |||
f355796b01 | |||
5175803d0b | |||
bf9939d40e | |||
07960fe348 | |||
f4be18e082 | |||
8e9837a587 | |||
361c043872 | |||
50432dbef1 | |||
37277d74cc | |||
9ee341f5e6 | |||
0bd284afc7 | |||
4ffdada427 | |||
d8effd3f9f | |||
303e15b88d | |||
3273aa9afa | |||
9e8b11051c | |||
b9d458e643 | |||
1e6361dd57 | |||
ca2019b8f1 | |||
a230665424 | |||
8eb6e034fe | |||
d6552eb4c6 | |||
117c5a1141 | |||
3f335c37ce | |||
e7e681928d | |||
855ff40070 | |||
29a42a398b | |||
b801728924 | |||
d14e9051d4 | |||
60ca1751ca | |||
941b8ac5d0 | |||
7a08102fad | |||
325061ee96 | |||
8839f36706 | |||
1c53955d5a | |||
9cd28b88be | |||
2fb2cd6f20 | |||
036d000b95 | |||
11d4bb02a8 | |||
f4b0b61b9d | |||
4b8989f3d2 | |||
19b4e35ee5 | |||
61b4ca8a37 | |||
1038d4fafa | |||
fd1f16598b | |||
07c28830db | |||
a8fccdbeb3 | |||
c213b7732b | |||
2ca5c0d6f3 | |||
2d0c54a7f2 | |||
bb0d028750 | |||
e7b4e95fbc | |||
85ffe9936d | |||
99f6829ede | |||
55053fe413 | |||
f2971c5d6c | |||
89288b166e | |||
88bd6dcbb0 | |||
710fc273d6 | |||
9f04f0ece2 | |||
cc7ab58ed8 | |||
edf1694606 | |||
f9cbcf9b65 | |||
3a148c6e85 | |||
4522b37b9d | |||
149c54cc31 | |||
93aa37768a | |||
da729069c2 | |||
f7c4b14b77 | |||
2d5cb4fd75 | |||
f2f556ed12 | |||
b1e659da28 | |||
4526c4f8a7 | |||
efc98c084f | |||
cf61577e3e | |||
3c8faa155f | |||
6f34a22c4b | |||
d118e9edcc | |||
c33c116488 | |||
e4bb2ff4f9 | |||
92fb982a57 | |||
e734e0c974 | |||
bf91d605ea | |||
e736c59896 | |||
94844054e4 | |||
00f7c1ddc0 | |||
7f68bbbdb9 | |||
2221b59b6f | |||
3b61c7cd8a | |||
f2664d8232 | |||
4ed7be19ad | |||
93f1d9e6f8 | |||
2a103e5bbc | |||
645270861c | |||
ee4061561e | |||
b7a4d3f0b3 | |||
ee0ff8b1fb | |||
8412be1336 | |||
596df2ef2c | |||
5c6de8aa04 | |||
7dc1c5095a | |||
b17831b726 | |||
1517979bf2 | |||
571a0fb320 | |||
c3300af656 | |||
cb4a58a1e8 | |||
57ed31da71 | |||
fb3b63d441 | |||
f06bec1a44 | |||
b25435f29a | |||
a12ea63858 | |||
98f126d249 | |||
821a63e3a7 | |||
b745088a05 | |||
8abd3fe140 | |||
004f6917aa | |||
d546a4f1e9 | |||
9f6a3bfb26 | |||
a82fece950 | |||
8f13b636ed | |||
740090640a | |||
9219404a9a | |||
d5c1601bf0 | |||
83ab8a39c9 | |||
3ffdd427dd | |||
f41cc8134a | |||
20e3f731f7 | |||
97f2c0c110 | |||
b484baea37 | |||
2647777b2b | |||
b468e8c943 | |||
7b919852fa | |||
60a7595b72 | |||
893b414e33 | |||
0932e603fb | |||
2047b04a4c | |||
f513270971 | |||
53769f9e8c | |||
ba1693dcf0 | |||
f7168770ab | |||
43e1d26696 | |||
99b82b6219 | |||
968d30e246 | |||
f183575d10 | |||
edddab8956 | |||
766b45e223 | |||
dd94b8624d | |||
1350e19391 | |||
6bdc313fa6 | |||
4870764b66 | |||
754d0ad558 | |||
dbeec88ddf | |||
621b191e8d | |||
634f88323c | |||
55f6844d1f | |||
d5b7c2b49b | |||
1544efbbda | |||
185b3e007e | |||
aeeb94d386 | |||
56d1dfc045 | |||
6c8d30b063 | |||
c1bb900d91 | |||
0ee4fc8528 | |||
bded9e021d | |||
913f01dfe8 | |||
7144c79efe | |||
a9b51e039a | |||
e5a17caa9f | |||
292bb307f0 | |||
4a3e989391 | |||
09a39fcb89 | |||
efdf35796d | |||
bbf8b5d5f9 | |||
30e3fbaa1a | |||
8cf8661b7e | |||
2e1143e3d8 | |||
f6a0037f8d | |||
3a574b4596 | |||
45544d7b54 | |||
a82f4d780f | |||
814dbd2666 | |||
da176b770c | |||
b316e699e1 | |||
e095a2ffe9 | |||
3d678707ac | |||
9b3f5f5ba0 | |||
d71fdc633d | |||
0a2d1394b2 | |||
7dae316789 | |||
bc181572d9 | |||
e061aa051a | |||
666ab7609d | |||
0af4051dfd | |||
645c0b7fb4 |
@ -2,6 +2,11 @@ language: python
|
||||
python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "pypy"
|
||||
- "pypy3"
|
||||
install: "pip install -r requirements.txt"
|
||||
script: cd tests && py.test
|
||||
script:
|
||||
- python setup.py install
|
||||
- cd tests && py.test
|
||||
|
513
README.md
513
README.md
@ -1,14 +1,47 @@
|
||||
# pyTelegramBotAPI
|
||||
# <p align="center">pyTelegramBotAPI
|
||||
|
||||
A Python implementation for the Telegram Bot API.
|
||||
<p align="center">A simple, but extensible Python implementation for the [Telegram Bot API](https://core.telegram.org/bots/api).
|
||||
|
||||
See [https://core.telegram.org/bots/api](https://core.telegram.org/bots/api)
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
[](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
|
||||
## How to install
|
||||
* [Getting started.](#getting-started)
|
||||
* [Writing your first bot](#writing-your-first-bot)
|
||||
* [Prerequisites](#prerequisites)
|
||||
* [A simple echo bot](#a-simple-echo-bot)
|
||||
* [General API Documentation](#general-api-documentation)
|
||||
* [Types](#types)
|
||||
* [Methods](#methods)
|
||||
* [General use of the API](#general-use-of-the-api)
|
||||
* [Message handlers](#message-handlers)
|
||||
* [TeleBot](#telebot)
|
||||
* [Reply markup](#reply-markup)
|
||||
* [Inline Mode](#inline-mode)
|
||||
* [Advanced use of the API](#advanced-use-of-the-api)
|
||||
* [Asynchronous delivery of messages](#asynchronous-delivery-of-messages)
|
||||
* [Sending large text messages](#sending-large-text-messages)
|
||||
* [Controlling the amount of Threads used by TeleBot](#controlling-the-amount-of-threads-used-by-telebot)
|
||||
* [The listener mechanism](#the-listener-mechanism)
|
||||
* [Using web hooks](#using-web-hooks)
|
||||
* [Logging](#logging)
|
||||
* [F.A.Q.](#faq)
|
||||
* [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)
|
||||
|
||||
Python 2 or Python 3 is required.
|
||||
## Getting started.
|
||||
|
||||
* Install from source
|
||||
This API is tested with Python 2.6, Python 2.7, Python 3.4, Pypy and Pypy 3.
|
||||
There are two ways to install the library:
|
||||
|
||||
* Installation using pip (a Python package manager)*:
|
||||
|
||||
```
|
||||
$ pip install pyTelegramBotAPI
|
||||
```
|
||||
* Installation from source (requires git):
|
||||
|
||||
```
|
||||
$ git clone https://github.com/eternnoir/pyTelegramBotAPI.git
|
||||
@ -16,92 +49,215 @@ $ cd pyTelegramBotAPI
|
||||
$ python setup.py install
|
||||
```
|
||||
|
||||
* or install with pip
|
||||
It is generally recommended to use the first option.
|
||||
|
||||
```
|
||||
$ pip install pyTelegramBotAPI
|
||||
```
|
||||
**While the API is production-ready, it is still under development and it has regular updates, do not forget to update it regularly by calling `pip install pytelegrambotapi --upgrade`*
|
||||
|
||||
## Example
|
||||
## Writing your first bot
|
||||
|
||||
* Sending a message.
|
||||
### Prerequisites
|
||||
|
||||
It is presumed that you [have obtained an API token with @BotFather](https://core.telegram.org/bots#botfather). We will call this token `TOKEN`.
|
||||
Furthermore, you have basic knowledge of the Python programming language and more importantly [the Telegram Bot API](https://core.telegram.org/bots/api).
|
||||
|
||||
### A simple echo bot
|
||||
|
||||
The TeleBot class (defined in \__init__.py) encapsulates all API calls in a single class. It provides functions such as `send_xyz` (`send_message`, `send_document` etc.) and several ways to listen for incoming messages.
|
||||
|
||||
Create a file called `echo_bot.py`.
|
||||
Then, open the file and create an instance of the TeleBot class.
|
||||
```python
|
||||
import telebot
|
||||
|
||||
TOKEN = '<token string>'
|
||||
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
# tb.send_message(chatid, message)
|
||||
tb.send_message(281281, 'gogo power ranger')
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
```
|
||||
*Note: Make sure to actually replace TOKEN with your own API token.*
|
||||
|
||||
* Echo Bot
|
||||
After that declaration, we need to register some so-called message handlers. Message handlers define filters which a message must pass. If a message passes the filter, the decorated function is called and the incoming message is passed as an argument.
|
||||
|
||||
Let's define a message handler which handles incoming `/start` and `/help` commands.
|
||||
```python
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message, "Howdy, how are you doing?")
|
||||
```
|
||||
A function which is decorated by a message handler __can have an arbitrary name, however, it must have only one parameter (the message)__.
|
||||
|
||||
Let's add another handler:
|
||||
```python
|
||||
@bot.message_handler(func=lambda m: True)
|
||||
def echo_all(message):
|
||||
bot.reply_to(message, message.text)
|
||||
```
|
||||
This one echoes all incoming text messages back to the sender. It uses a lambda function to test a message. If the lambda returns True, the message is handled by the decorated function. Since we want all messages to be handled by this function, we simply always return True.
|
||||
|
||||
*Note: all handlers are tested in the order in which they were declared*
|
||||
|
||||
We now have a basic bot which replies a static message to "/start" and "/help" commands and which echoes the rest of the sent messages. To start the bot, add the following to our source file:
|
||||
```python
|
||||
bot.polling()
|
||||
```
|
||||
Alright, that's it! Our source file now looks like this:
|
||||
```python
|
||||
import telebot
|
||||
import time
|
||||
|
||||
TOKEN = '<token_string>'
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message, "Howdy, how are you doing?")
|
||||
|
||||
def listener(*messages):
|
||||
"""
|
||||
When new messages arrive TeleBot will call this function.
|
||||
"""
|
||||
for m in messages:
|
||||
chatid = m.chat.id
|
||||
if m.content_type == 'text':
|
||||
text = m.text
|
||||
tb.send_message(chatid, text)
|
||||
@bot.message_handler(func=lambda message: True)
|
||||
def echo_all(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
bot.polling()
|
||||
```
|
||||
To start the bot, simply open up a terminal and enter `python echo_bot.py` to run the bot! Test it by sending commands ('/start' and '/help') and arbitrary text messages.
|
||||
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
tb.set_update_listener(listener) #register listener
|
||||
tb.polling()
|
||||
## General API Documentation
|
||||
|
||||
while True: # Don't let the main Thread end.
|
||||
### Types
|
||||
|
||||
All types are defined in types.py. They are all completely in line with the [Telegram API's definition of the types](https://core.telegram.org/bots/api#available-types), except for the Message's `from` field, which is renamed to `from_user` (because `from` is a Python reserved token). Thus, attributes such as `message_id` can be accessed directly with `message.message_id`. Note that `message.chat` can be either an instance of `User` or `GroupChat` (see [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)).
|
||||
|
||||
The Message object also has a `content_types`attribute, which defines the type of the Message. `content_types` can be one of the following strings:
|
||||
'text', 'audio', 'document', 'photo', 'sticker', 'video', 'voice', 'location', 'contact', 'new_chat_participant', 'left_chat_participant', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created'.
|
||||
|
||||
### Methods
|
||||
|
||||
All [API methods](https://core.telegram.org/bots/api#available-methods) are located in the TeleBot class. They are renamed to follow common Python naming conventions. E.g. `getMe` is renamed to `get_me` and `sendMessage` to `send_message`.
|
||||
|
||||
### General use of the API
|
||||
|
||||
Outlined below are some general use cases of the API.
|
||||
|
||||
#### Message handlers
|
||||
A message handler is a function that is decorated with the `message_handler` decorator of a TeleBot instance. Message handlers consist of one or multiple filters.
|
||||
Each filter much return True for a certain message in order for a message handler to become eligible to handle that message. A message handler is declared in the following way (provided `bot` is an instance of TeleBot):
|
||||
```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:
|
||||
```python
|
||||
import telebot
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
|
||||
# Handles all text messages that contains the commands '/start' or '/help'.
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def handle_start_help(message):
|
||||
pass
|
||||
|
||||
# Handles all sent documents and audio files
|
||||
@bot.message_handler(content_types=['document', 'audio'])
|
||||
def handle_docs_audio(message):
|
||||
pass
|
||||
|
||||
# Handles all text messages that match the regular expression
|
||||
@bot.message_handler(regexp="SOME_REGEXP")
|
||||
def handle_message(message):
|
||||
pass
|
||||
|
||||
#Handles all messages for which the lambda returns True
|
||||
@bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document'])
|
||||
def handle_text_doc(message):
|
||||
pass
|
||||
|
||||
#Which could also be defined as:
|
||||
def test_message(message):
|
||||
return message.document.mime_type == 'text/plan'
|
||||
|
||||
@bot.message_handler(func=test_message, content_types=['document'])
|
||||
def handle_text_doc(message)
|
||||
pass
|
||||
|
||||
# Handlers can be stacked to create a function which will be called if either message_handler is eligible
|
||||
# This handler will be called if the message starts with '/hello' OR is some emoji
|
||||
@bot.message_handler(commands=['hello'])
|
||||
@bot.message_handler(func=lambda msg: msg.text.encode("utf-8") == SOME_FANCY_EMOJI)
|
||||
def send_something(message):
|
||||
pass
|
||||
```
|
||||
|
||||
## TeleBot API usage
|
||||
|
||||
**Important: all handlers are tested in the order in which they were declared**
|
||||
#### TeleBot
|
||||
```python
|
||||
import telebot
|
||||
import time
|
||||
|
||||
TOKEN = '<token_string>'
|
||||
tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object
|
||||
|
||||
# Upon calling this function, TeleBot starts polling the Telegram servers for new messages.
|
||||
# - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers
|
||||
# - interval: True/False (default False) - The interval between polling requests
|
||||
# Note: Editing this parameter harms the bot's response time
|
||||
# - block: True/False (default True) - Blocks upon calling this function
|
||||
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):
|
||||
|
||||
# sendMessage
|
||||
tb.send_message(chatid, text)
|
||||
|
||||
# forwardMessage
|
||||
# tb.forward_message(10894,926,3)
|
||||
tb.forward_message(to_chat_id, from_chat_id, message_id)
|
||||
|
||||
# All send_xyz functions which can take a file as an argument, can also take a file_id instead of a file.
|
||||
# sendPhoto
|
||||
photo = open('/tmp/photo.png', 'rb')
|
||||
tb.send_photo(chat_id, photo)
|
||||
tb.send_photo(chat_id, "FILEID")
|
||||
|
||||
# sendAudio
|
||||
audio = open('/tmp/audio.ogg', 'rb')
|
||||
audio = open('/tmp/audio.mp3', 'rb')
|
||||
tb.send_audio(chat_id, audio)
|
||||
tb.send_audio(chat_id, "FILEID")
|
||||
|
||||
## sendAudio with duration, performer and title.
|
||||
tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram')
|
||||
|
||||
# sendVoice
|
||||
voice = open('/tmp/voice.ogg', 'rb')
|
||||
tb.send_voice(chat_id, voice)
|
||||
tb.send_voice(chat_id, "FILEID")
|
||||
|
||||
# sendDocument
|
||||
doc = open('/tmp/file.txt', 'rb')
|
||||
tb.send_document(chat_id, doc)
|
||||
tb.send_document(chat_id, "FILEID")
|
||||
|
||||
# sendSticker
|
||||
sti = open('/tmp/sti.webp', 'rb')
|
||||
tb.send_sticker(chat_id, sti)
|
||||
tb.send_sticker(chat_id, "FILEID")
|
||||
|
||||
# sendVideo
|
||||
video = open('/tmp/video.mp4', 'rb')
|
||||
tb.send_video(chat_id, video)
|
||||
tb.send_video(chat_id, "FILEID")
|
||||
|
||||
# sendLocation
|
||||
tb.send_location(chat_id, lat, lon)
|
||||
@ -111,123 +267,218 @@ tb.send_location(chat_id, lat, lon)
|
||||
# 'record_audio', 'upload_audio', 'upload_document' or 'find_location'.
|
||||
tb.send_chat_action(chat_id, action_string)
|
||||
|
||||
# Use the ReplyKeyboardMarkup class.
|
||||
# Thanks pevdh.
|
||||
# getFile
|
||||
# Downloading a file is straightforward
|
||||
# Returns a File object
|
||||
import requests
|
||||
file_info = tb.get_file(file_id)
|
||||
|
||||
file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN, file_info.file_path))
|
||||
|
||||
|
||||
```
|
||||
#### Reply markup
|
||||
All `send_xyz` functions of TeleBot take an optional `reply_markup` argument. This argument must be an instance of `ReplyKeyboardMarkup`, `ReplyKeyboardHide` or `ForceReply`, which are defined in types.py.
|
||||
|
||||
```python
|
||||
from telebot import types
|
||||
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
# Using the ReplyKeyboardMarkup class
|
||||
# It's constructor can take the following optional arguments:
|
||||
# - resize_keyboard: True/False (default False)
|
||||
# - one_time_keyboard: True/False (default False)
|
||||
# - selective: True/False (default False)
|
||||
# - row_width: integer (default 3)
|
||||
# row_width is used in combination with the add() function.
|
||||
# It defines how many buttons are fit on each row before continuing on the next row.
|
||||
markup = types.ReplyKeyboardMarkup(row_width=2)
|
||||
markup.add('a', 'v', 'd')
|
||||
tb.send_message(chat_id, message, reply_markup=markup)
|
||||
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
|
||||
|
||||
# or add strings one row at a time:
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
markup.row('a', 'v')
|
||||
markup.row('c', 'd', 'e')
|
||||
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
|
||||
```
|
||||
The last example yields this result:
|
||||
|
||||

|
||||
|
||||
```python
|
||||
# ReplyKeyboardHide: hides a previously sent ReplyKeyboardMarkup
|
||||
# Takes an optional selective argument (True/False, default False)
|
||||
markup = types.ReplyKeyboardHide(selective=False)
|
||||
tb.send_message(chat_id, message, reply_markup=markup)
|
||||
```
|
||||
|
||||
```python
|
||||
# ForceReply: forces a user to reply to a message
|
||||
# Takes an optional selective argument (True/False, default False)
|
||||
markup = types.ForceReply(selective=False)
|
||||
tb.send_message(chat_id, "Send me another word:", reply_markup=markup)
|
||||
```
|
||||
ForceReply:
|
||||
|
||||

|
||||
|
||||
### Inline Mode
|
||||
|
||||
More information about [Inline mode](https://core.telegram.org/bots/inline).
|
||||
|
||||
#### inline_handler
|
||||
|
||||
Now, you can use inline_handler to get inline_query in telebot.
|
||||
|
||||
```python
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'text')
|
||||
def query_text(inline_query):
|
||||
# Query message is text
|
||||
```
|
||||
|
||||
|
||||
#### chosen_inline_handler
|
||||
|
||||
Use chosen_inline_handler to get chosen_inline_result in telebot. Don't forgot add the /setinlinefeedback
|
||||
command for @Botfather.
|
||||
|
||||
More information : [collecting-feedback](https://core.telegram.org/bots/inline#collecting-feedback)
|
||||
|
||||
```python
|
||||
@bot.chosen_inline_handler(func=lambda chosen_inline_result: True)
|
||||
def test_chosen(chosen_inline_result):
|
||||
# Process all chosen_inline_result.
|
||||
```
|
||||
|
||||
#### answer_inline_query
|
||||
|
||||
```python
|
||||
@bot.inline_handler(lambda query: query.query == 'text')
|
||||
def query_text(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultArticle('1', 'Result', 'Result message.')
|
||||
r2 = types.InlineQueryResultArticle('2', 'Result2', 'Result message2.')
|
||||
bot.answer_inline_query(inline_query.id, [r, r2])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
```
|
||||
|
||||
## Creating a Telegram bot with the pyTelegramBotAPI
|
||||
There are two ways to define a Telegram Bot with the pyTelegramBotAPI.
|
||||
## Advanced use of the API
|
||||
|
||||
### Asynchronous delivery of messages
|
||||
There exists an implementation of TeleBot which executes all `send_xyz` and the `get_me` functions asynchronously. This can speed up you bot __significantly__, but it has unwanted side effects if used without caution.
|
||||
To enable this behaviour, create an instance of AsyncTeleBot instead of TeleBot.
|
||||
```python
|
||||
tb = telebot.AsyncTeleBot("TOKEN")
|
||||
```
|
||||
Now, every function that calls the Telegram API is executed in a separate Thread. The functions are modified to return an AsyncTask instance (defined in util.py). Using AsyncTeleBot allows you to do the following:
|
||||
```python
|
||||
import telebot
|
||||
|
||||
tb = telebot.AsyncTeleBot("TOKEN")
|
||||
task = tb.get_me() # Execute an API call
|
||||
# Do some other operations...
|
||||
a = 0
|
||||
for a in range(100):
|
||||
a += 10
|
||||
|
||||
result = task.wait() # Get the result of the execution
|
||||
```
|
||||
*Note: if you execute send_xyz functions after eachother without calling wait(), the order in which messages are delivered might be wrong.*
|
||||
|
||||
### Sending large text messages
|
||||
Sometimes you must send messages that exceed 5000 characters. The Telegram API can not handle that many characters in one request, so we need to split the message in multiples. Here is how to do that using the API:
|
||||
```python
|
||||
from telebot import util
|
||||
large_text = open("large_text.txt", "rb").read()
|
||||
|
||||
# Split the text each 3000 characters.
|
||||
# split_string returns a list with the splitted text.
|
||||
splitted_text = util.split_string(large_text, 3000)
|
||||
for text in splitted_text:
|
||||
tb.send_message(chat_id, text)
|
||||
```
|
||||
### Controlling the amount of Threads used by TeleBot
|
||||
The TeleBot constructor takes the following optional arguments:
|
||||
|
||||
- create_threads: True/False (default True). A flag to indicate whether
|
||||
TeleBot should execute message handlers on it's polling Thread.
|
||||
- num_threads: integer (default 4). Controls the amount of WorkerThreads created for the internal thread pool that TeleBot uses to execute message handlers. Is not used when create_threads is False.
|
||||
|
||||
### The listener mechanism
|
||||
* First, create a TeleBot instance.
|
||||
As an alternative to the message handlers, one can also register a function as a listener to TeleBot. Example:
|
||||
```python
|
||||
import telebot
|
||||
def handle_messages(messages):
|
||||
for message in messsages:
|
||||
# Do something with the message
|
||||
bot.reply_to(message, 'Hi')
|
||||
|
||||
TOKEN = '<token string>'
|
||||
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
```
|
||||
* Then, define a listener function.
|
||||
```python
|
||||
def echo_messages(*messages):
|
||||
"""
|
||||
Echoes all incoming messages of content_type 'text'.
|
||||
"""
|
||||
for m in messages:
|
||||
chatid = m.chat.id
|
||||
if m.content_type == 'text':
|
||||
text = m.text
|
||||
bot.send_message(chatid, text)
|
||||
```
|
||||
* Now, register your listener with the TeleBot instance and call TeleBot#polling()
|
||||
```python
|
||||
bot.set_update_listener(echo_messages)
|
||||
bot.set_update_listener(handle_messages)
|
||||
bot.polling()
|
||||
|
||||
while True: # Don't let the main Thread end.
|
||||
pass
|
||||
```
|
||||
* use Message's content_type attribute to check the type of Message. Now Message supports content types:
|
||||
* 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
|
||||
* That's it!
|
||||
|
||||
### The decorator mechanism
|
||||
* First, create a TeleBot instance.
|
||||
### Using webhooks
|
||||
When using webhooks telegram sends one Update per call, for processing it you should call process_new_messages([update.message]) when you recieve it.
|
||||
|
||||
There are some examples using webhooks in the *examples/webhook_examples* directory.
|
||||
|
||||
### Logging
|
||||
|
||||
You can use the Telebot module logger to log debug info about Telebot. Use `telebot.logger` to get the logger of the TeleBot module.
|
||||
It is possible to add custom logging Handlers to the logger. Refer to the [Python logging module page](https://docs.python.org/2/library/logging.html) for more info.
|
||||
|
||||
```python
|
||||
import telebot
|
||||
import logging
|
||||
|
||||
TOKEN = '<token string>'
|
||||
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
|
||||
```
|
||||
* Next, define all of your so-called message handlers and decorate them with @bot.message_handler
|
||||
|
||||
## F.A.Q.
|
||||
|
||||
### How can I distinguish a User and a GroupChat in message.chat?
|
||||
Telegram Bot API support new type Chat for message.chat.
|
||||
|
||||
- Check the ```type``` attribute in ```Chat``` object:
|
||||
```python
|
||||
# Handle /start and /help
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def command_help(message):
|
||||
bot.reply_to(message, "Hello, did someone call for help?")
|
||||
|
||||
# Handles all messages which text matches the regex regexp.
|
||||
# See https://en.wikipedia.org/wiki/Regular_expression
|
||||
# This regex matches all sent url's.
|
||||
@bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
|
||||
def command_url(message):
|
||||
bot.reply_to(message, "I shouldn't open that url, should I?")
|
||||
if message.chat.type == “private”:
|
||||
# private chat message
|
||||
|
||||
# Handle all sent documents of type 'text/plain'.
|
||||
@bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document'])
|
||||
def command_handle_document(message):
|
||||
bot.reply_to(message, "Document received, sir!")
|
||||
if message.chat.type == “group”:
|
||||
# group chat message
|
||||
|
||||
if message.chat.type == “supergroup”:
|
||||
# supergroup chat message
|
||||
|
||||
if message.chat.type == “channel”:
|
||||
# channel message
|
||||
|
||||
# Default command handler. A lambda expression which always returns True is used for this purpose.
|
||||
@bot.message_handler(func=lambda message: True, content_types=['audio', 'video', 'document', 'text', 'location', 'contact', 'sticker'])
|
||||
def default_command(message):
|
||||
bot.reply_to(message, "This is the default command handler.")
|
||||
```
|
||||
* And finally, call bot.polling()
|
||||
```python
|
||||
bot.polling()
|
||||
|
||||
while True: # Don't end the main thread.
|
||||
pass
|
||||
```
|
||||
Use whichever mechanism fits your purpose! It is even possible to mix and match.
|
||||
## The Telegram Chat Group
|
||||
|
||||
## TODO
|
||||
Get help. Discuss. Chat.
|
||||
|
||||
- [x] getMe
|
||||
- [x] sendMessage
|
||||
- [x] forwardMessage
|
||||
- [x] sendPhoto
|
||||
- [x] sendAudio
|
||||
- [x] sendDocument
|
||||
- [x] sendSticker
|
||||
- [x] sendVideo
|
||||
- [x] sendLocation
|
||||
- [x] sendChatAction
|
||||
- [x] getUserProfilePhotos
|
||||
- [x] getUpdate
|
||||
* Join the pyTelegramBotAPI Telegram Chat Group
|
||||
* Messge to @eternnoir by telegram for Invitation.
|
||||
* We now have a Telegram Channel as well! Keep yourself up to date with API changes, and [join it](https://telegram.me/pytelegrambotapi).
|
||||
|
||||
## More examples
|
||||
|
||||
* [Echo Bot](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/echo_bot.py)
|
||||
* [Deep Linking](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/deep_linking.py)
|
||||
* [next_step_handler Example](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py)
|
||||
|
||||
## Bots using this API
|
||||
* [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes
|
||||
* [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger*
|
||||
* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF*
|
||||
* [Telegram UrlProBot](https://github.com/GabrielRF/telegram-urlprobot) by *GabrielRF*
|
||||
* [Telegram Proxy Bot](https://bitbucket.org/master_groosha/telegram-proxy-bot) by *Groosha* - A simple BITM (bot-in-the-middle) for Telegram acting as some kind of "proxy".
|
||||
* [RadRetroRobot](https://github.com/Tronikart/RadRetroRobot) by *Tronikart* - Multifunctional Telegram Bot RadRetroRobot.
|
||||
* [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie*
|
||||
* [NeoBot](https://github.com/neoranger/NeoBot) by *neoranger*
|
||||
|
||||
Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh.
|
||||
|
592
README.rst
Normal file
592
README.rst
Normal file
@ -0,0 +1,592 @@
|
||||
#
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<p align="center">
|
||||
|
||||
pyTelegramBotAPI
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<p align="center">
|
||||
|
||||
A simple, but extensible Python implementation for the `Telegram Bot
|
||||
API <https://core.telegram.org/bots/api>`__.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<p align="center">
|
||||
|
||||
|Build Status|
|
||||
|
||||
- `Getting started. <#getting-started>`__
|
||||
- `Writing your first bot <#writing-your-first-bot>`__
|
||||
|
||||
- `Prerequisites <#prerequisites>`__
|
||||
- `A simple echo bot <#a-simple-echo-bot>`__
|
||||
|
||||
- `General API Documentation <#general-api-documentation>`__
|
||||
|
||||
- `Types <#types>`__
|
||||
- `Methods <#methods>`__
|
||||
- `General use of the API <#general-use-of-the-api>`__
|
||||
- `Message handlers <#message-handlers>`__
|
||||
- `TeleBot <#telebot>`__
|
||||
- `Reply markup <#reply-markup>`__
|
||||
|
||||
- `Advanced use of the API <#advanced-use-of-the-api>`__
|
||||
|
||||
- `Asynchronous delivery of
|
||||
messages <#asynchronous-delivery-of-messages>`__
|
||||
- `Sending large text messages <#sending-large-text-messages>`__
|
||||
- `Controlling the amount of Threads used by
|
||||
TeleBot <#controlling-the-amount-of-threads-used-by-telebot>`__
|
||||
- `The listener mechanism <#the-listener-mechanism>`__
|
||||
- `Using web hooks <#using-web-hooks>`__
|
||||
- `Logging <#logging>`__
|
||||
|
||||
- `F.A.Q. <#faq>`__
|
||||
|
||||
- `How can I distinguish a User and a GroupChat in
|
||||
message.chat? <#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat>`__
|
||||
|
||||
- `The Telegram Chat Group <#the-telegram-chat-group>`__
|
||||
- `More examples <#more-examples>`__
|
||||
- `Bots using this API <#bots-using-this-api>`__
|
||||
|
||||
Getting started.
|
||||
================
|
||||
|
||||
This API is tested with Python 2.6, Python 2.7, Python 3.4, Pypy and
|
||||
Pypy 3. There are two ways to install the library:
|
||||
|
||||
- Installation using pip (a Python package manager)\*:
|
||||
|
||||
::
|
||||
|
||||
$ pip install pyTelegramBotAPI
|
||||
|
||||
- Installation from source (requires git):
|
||||
|
||||
::
|
||||
|
||||
$ git clone https://github.com/eternnoir/pyTelegramBotAPI.git
|
||||
$ cd pyTelegramBotAPI
|
||||
$ python setup.py install
|
||||
|
||||
It is generally recommended to use the first option.
|
||||
|
||||
\*\*While the API is production-ready, it is still under development and
|
||||
it has regular updates, do not forget to update it regularly by calling
|
||||
``pip install pytelegrambotapi --upgrade``\ \*
|
||||
|
||||
Writing your first bot
|
||||
======================
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
It is presumed that you [have obtained an API token with
|
||||
@BotFather](https://core.telegram.org/bots#botfather). We will call this
|
||||
token ``TOKEN``. Furthermore, you have basic knowledge of the Python
|
||||
programming language and more importantly `the Telegram Bot
|
||||
API <https://core.telegram.org/bots/api>`__.
|
||||
|
||||
A simple echo bot
|
||||
-----------------
|
||||
|
||||
The TeleBot class (defined in \_\_init\_\_.py) encapsulates all API
|
||||
calls in a single class. It provides functions such as ``send_xyz``
|
||||
(``send_message``, ``send_document`` etc.) and several ways to listen
|
||||
for incoming messages.
|
||||
|
||||
Create a file called ``echo_bot.py``. Then, open the file and create an
|
||||
instance of the TeleBot class.
|
||||
|
||||
.. code:: python
|
||||
|
||||
import telebot
|
||||
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
|
||||
*Note: Make sure to actually replace TOKEN with your own API token.*
|
||||
|
||||
After that declaration, we need to register some so-called message
|
||||
handlers. Message handlers define filters which a message must pass. If
|
||||
a message passes the filter, the decorated function is called and the
|
||||
incoming message is passed as an argument.
|
||||
|
||||
Let's define a message handler which handles incoming ``/start`` and
|
||||
``/help`` commands.
|
||||
|
||||
.. code:: python
|
||||
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message, "Howdy, how are you doing?")
|
||||
|
||||
A function which is decorated by a message handler **can have an
|
||||
arbitrary name, however, it must have only one parameter (the
|
||||
message)**.
|
||||
|
||||
Let's add another handler:
|
||||
|
||||
.. code:: python
|
||||
|
||||
@bot.message_handler(func=lambda m: True)
|
||||
def echo_all(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
This one echoes all incoming text messages back to the sender. It uses a
|
||||
lambda function to test a message. If the lambda returns True, the
|
||||
message is handled by the decorated function. Since we want all messages
|
||||
to be handled by this function, we simply always return True.
|
||||
|
||||
*Note: all handlers are tested in the order in which they were declared*
|
||||
|
||||
We now have a basic bot which replies a static message to "/start" and
|
||||
"/help" commands and which echoes the rest of the sent messages. To
|
||||
start the bot, add the following to our source file:
|
||||
|
||||
.. code:: python
|
||||
|
||||
bot.polling()
|
||||
|
||||
Alright, that's it! Our source file now looks like this:
|
||||
|
||||
.. code:: python
|
||||
|
||||
import telebot
|
||||
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message, "Howdy, how are you doing?")
|
||||
|
||||
@bot.message_handler(func=lambda message: True)
|
||||
def echo_all(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
bot.polling()
|
||||
|
||||
To start the bot, simply open up a terminal and enter
|
||||
``python echo_bot.py`` to run the bot! Test it by sending commands
|
||||
('/start' and '/help') and arbitrary text messages.
|
||||
|
||||
General API Documentation
|
||||
=========================
|
||||
|
||||
Types
|
||||
-----
|
||||
|
||||
All types are defined in types.py. They are all completely in line with
|
||||
the `Telegram API's definition of the
|
||||
types <https://core.telegram.org/bots/api#available-types>`__, except
|
||||
for the Message's ``from`` field, which is renamed to ``from_user``
|
||||
(because ``from`` is a Python reserved token). Thus, attributes such as
|
||||
``message_id`` can be accessed directly with ``message.message_id``.
|
||||
Note that ``message.chat`` can be either an instance of ``User`` or
|
||||
``GroupChat`` (see `How can I distinguish a User and a GroupChat in
|
||||
message.chat? <#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat>`__).
|
||||
|
||||
The Message object also has a ``content_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
|
||||
-------
|
||||
|
||||
All `API
|
||||
methods <https://core.telegram.org/bots/api#available-methods>`__ are
|
||||
located in the TeleBot class. They are renamed to follow common Python
|
||||
naming conventions. E.g. ``getMe`` is renamed to ``get_me`` and
|
||||
``sendMessage`` to ``send_message``.
|
||||
|
||||
General use of the API
|
||||
----------------------
|
||||
|
||||
Outlined below are some general use cases of the API.
|
||||
|
||||
Message handlers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
A message handler is a function that is decorated with the
|
||||
``message_handler`` decorator of a TeleBot instance. Message handlers
|
||||
consist of one or multiple filters. Each filter much return True for a
|
||||
certain message in order for a message handler to become eligible to
|
||||
handle that message. A message handler is declared in the following way
|
||||
(provided ``bot`` is an instance of TeleBot):
|
||||
|
||||
.. code:: python
|
||||
|
||||
@bot.message_handler(filters)
|
||||
def function_name(message):
|
||||
bot.reply_to(message, "This is a message handler")
|
||||
|
||||
``function_name`` is not bound to any restrictions. Any function name is
|
||||
permitted with message handlers. The function must accept at most one
|
||||
argument, which will be the message that the function must handle.
|
||||
``filters`` is a list of keyword arguments. A filter is declared in the
|
||||
following manner: ``name=argument``. One handler may have multiple
|
||||
filters. TeleBot supports the following filters:
|
||||
|
||||
+------------------+---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| name | 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
|
||||
|
||||
import telebot
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
|
||||
# Handles all text messages that contains the commands '/start' or '/help'.
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def handle_start_help(message):
|
||||
pass
|
||||
|
||||
# Handles all sent documents and audio files
|
||||
@bot.message_handler(content_types=['document', 'audio'])
|
||||
def handle_docs_audio(message):
|
||||
pass
|
||||
|
||||
# Handles all text messages that match the regular expression
|
||||
@bot.message_handler(regexp="SOME_REGEXP")
|
||||
def handle_message(message):
|
||||
pass
|
||||
|
||||
#Handles all messages for which the lambda returns True
|
||||
@bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document'])
|
||||
def handle_text_doc(message):
|
||||
pass
|
||||
|
||||
#Which could also be defined as:
|
||||
def test_message(message):
|
||||
return message.document.mime_type == 'text/plan'
|
||||
|
||||
@bot.message_handler(func=test_message, content_types=['document'])
|
||||
def handle_text_doc(message)
|
||||
pass
|
||||
|
||||
# Handlers can be stacked to create a function which will be called if either message_handler is eligible
|
||||
# This handler will be called if the message starts with '/hello' OR is some emoji
|
||||
@bot.message_handler(commands=['hello'])
|
||||
@bot.message_handler(func=lambda msg: msg.text.encode("utf-8") == SOME_FANCY_EMOJI)
|
||||
def send_something(message):
|
||||
pass
|
||||
|
||||
**Important: all handlers are tested in the order in which they were
|
||||
declared** #### TeleBot
|
||||
|
||||
.. code:: python
|
||||
|
||||
import telebot
|
||||
|
||||
TOKEN = '<token_string>'
|
||||
tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object
|
||||
|
||||
# Upon calling this function, TeleBot starts polling the Telegram servers for new messages.
|
||||
# - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers
|
||||
# - interval: True/False (default False) - The interval between polling requests
|
||||
# Note: Editing this parameter harms the bot's response time
|
||||
# - block: True/False (default True) - Blocks upon calling this function
|
||||
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):
|
||||
|
||||
# sendMessage
|
||||
tb.send_message(chatid, text)
|
||||
|
||||
# forwardMessage
|
||||
tb.forward_message(to_chat_id, from_chat_id, message_id)
|
||||
|
||||
# All send_xyz functions which can take a file as an argument, can also take a file_id instead of a file.
|
||||
# sendPhoto
|
||||
photo = open('/tmp/photo.png', 'rb')
|
||||
tb.send_photo(chat_id, photo)
|
||||
tb.send_photo(chat_id, "FILEID")
|
||||
|
||||
# sendAudio
|
||||
audio = open('/tmp/audio.mp3', 'rb')
|
||||
tb.send_audio(chat_id, audio)
|
||||
tb.send_audio(chat_id, "FILEID")
|
||||
|
||||
## sendAudio with duration, performer and title.
|
||||
tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram')
|
||||
|
||||
# sendVoice
|
||||
voice = open('/tmp/voice.ogg', 'rb')
|
||||
tb.send_voice(chat_id, voice)
|
||||
tb.send_voice(chat_id, "FILEID")
|
||||
|
||||
# sendDocument
|
||||
doc = open('/tmp/file.txt', 'rb')
|
||||
tb.send_document(chat_id, doc)
|
||||
tb.send_document(chat_id, "FILEID")
|
||||
|
||||
# sendSticker
|
||||
sti = open('/tmp/sti.webp', 'rb')
|
||||
tb.send_sticker(chat_id, sti)
|
||||
tb.send_sticker(chat_id, "FILEID")
|
||||
|
||||
# sendVideo
|
||||
video = open('/tmp/video.mp4', 'rb')
|
||||
tb.send_video(chat_id, video)
|
||||
tb.send_video(chat_id, "FILEID")
|
||||
|
||||
# sendLocation
|
||||
tb.send_location(chat_id, lat, lon)
|
||||
|
||||
# sendChatAction
|
||||
# action_string can be one of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video',
|
||||
# 'record_audio', 'upload_audio', 'upload_document' or 'find_location'.
|
||||
tb.send_chat_action(chat_id, action_string)
|
||||
|
||||
# getFile
|
||||
# Downloading a file is straightforward
|
||||
# Returns a File object
|
||||
import requests
|
||||
file_info = tb.get_file(file_id)
|
||||
|
||||
file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN, file_info.file_path))
|
||||
|
||||
Reply markup
|
||||
~~~~~~~~~~~~
|
||||
|
||||
All ``send_xyz`` functions of TeleBot take an optional ``reply_markup``
|
||||
argument. This argument must be an instance of ``ReplyKeyboardMarkup``,
|
||||
``ReplyKeyboardHide`` or ``ForceReply``, which are defined in types.py.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from telebot import types
|
||||
|
||||
# Using the ReplyKeyboardMarkup class
|
||||
# It's constructor can take the following optional arguments:
|
||||
# - resize_keyboard: True/False (default False)
|
||||
# - one_time_keyboard: True/False (default False)
|
||||
# - selective: True/False (default False)
|
||||
# - row_width: integer (default 3)
|
||||
# row_width is used in combination with the add() function.
|
||||
# It defines how many buttons are fit on each row before continuing on the next row.
|
||||
markup = types.ReplyKeyboardMarkup(row_width=2)
|
||||
markup.add('a', 'v', 'd')
|
||||
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
|
||||
|
||||
# or add strings one row at a time:
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
markup.row('a', 'v')
|
||||
markup.row('c', 'd', 'e')
|
||||
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
|
||||
|
||||
The last example yields this result:
|
||||
|
||||
.. figure:: https://pp.vk.me/c624430/v624430512/473e5/_mxxW7FPe4U.jpg
|
||||
:alt: ReplyKeyboardMarkup
|
||||
|
||||
ReplyKeyboardMarkup
|
||||
|
||||
.. code:: python
|
||||
|
||||
# ReplyKeyboardHide: hides a previously sent ReplyKeyboardMarkup
|
||||
# Takes an optional selective argument (True/False, default False)
|
||||
markup = types.ReplyKeyboardHide(selective=False)
|
||||
tb.send_message(chat_id, message, reply_markup=markup)
|
||||
|
||||
.. code:: python
|
||||
|
||||
# ForceReply: forces a user to reply to a message
|
||||
# Takes an optional selective argument (True/False, default False)
|
||||
markup = types.ForceReply(selective=False)
|
||||
tb.send_message(chat_id, "Send me another word:", reply_markup=markup)
|
||||
|
||||
ForceReply:
|
||||
|
||||
.. figure:: https://pp.vk.me/c624430/v624430512/473ec/602byyWUHcs.jpg
|
||||
:alt: ForceReply
|
||||
|
||||
ForceReply
|
||||
|
||||
Advanced use of the API
|
||||
=======================
|
||||
|
||||
Asynchronous delivery of messages
|
||||
---------------------------------
|
||||
|
||||
There exists an implementation of TeleBot which executes all
|
||||
``send_xyz`` and the ``get_me`` functions asynchronously. This can speed
|
||||
up you bot **significantly**, but it has unwanted side effects if used
|
||||
without caution. To enable this behaviour, create an instance of
|
||||
AsyncTeleBot instead of TeleBot.
|
||||
|
||||
.. code:: python
|
||||
|
||||
tb = telebot.AsyncTeleBot("TOKEN")
|
||||
|
||||
Now, every function that calls the Telegram API is executed in a
|
||||
separate Thread. The functions are modified to return an AsyncTask
|
||||
instance (defined in util.py). Using AsyncTeleBot allows you to do the
|
||||
following:
|
||||
|
||||
.. code:: python
|
||||
|
||||
import telebot
|
||||
|
||||
tb = telebot.AsyncTeleBot("TOKEN")
|
||||
task = tb.get_me() # Execute an API call
|
||||
# Do some other operations...
|
||||
a = 0
|
||||
for a in range(100):
|
||||
a += 10
|
||||
|
||||
result = task.wait() # Get the result of the execution
|
||||
|
||||
*Note: if you execute send\_xyz functions after eachother without
|
||||
calling wait(), the order in which messages are delivered might be
|
||||
wrong.*
|
||||
|
||||
Sending large text messages
|
||||
---------------------------
|
||||
|
||||
Sometimes you must send messages that exceed 5000 characters. The
|
||||
Telegram API can not handle that many characters in one request, so we
|
||||
need to split the message in multiples. Here is how to do that using the
|
||||
API:
|
||||
|
||||
.. code:: python
|
||||
|
||||
from telebot import util
|
||||
large_text = open("large_text.txt", "rb").read()
|
||||
|
||||
# Split the text each 3000 characters.
|
||||
# split_string returns a list with the splitted text.
|
||||
splitted_text = util.split_string(large_text, 3000)
|
||||
for text in splitted_text:
|
||||
tb.send_message(chat_id, text)
|
||||
|
||||
Controlling the amount of Threads used by TeleBot
|
||||
-------------------------------------------------
|
||||
|
||||
The TeleBot constructor takes the following optional arguments:
|
||||
|
||||
- create\_threads: True/False (default True). A flag to indicate
|
||||
whether TeleBot should execute message handlers on it's polling
|
||||
Thread.
|
||||
- num\_threads: integer (default 4). Controls the amount of
|
||||
WorkerThreads created for the internal thread pool that TeleBot uses
|
||||
to execute message handlers. Is not used when create\_threads is
|
||||
False.
|
||||
|
||||
The listener mechanism
|
||||
----------------------
|
||||
|
||||
As an alternative to the message handlers, one can also register a
|
||||
function as a listener to TeleBot. Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
def handle_messages(messages):
|
||||
for message in messsages:
|
||||
# Do something with the message
|
||||
bot.reply_to(message, 'Hi')
|
||||
|
||||
bot.set_update_listener(handle_messages)
|
||||
bot.polling()
|
||||
|
||||
Using webhooks
|
||||
--------------
|
||||
|
||||
When using webhooks telegram sends one Update per call, for processing
|
||||
it you should call process\_new\_messages([update.message]) when you
|
||||
recieve it.
|
||||
|
||||
There are some examples using webhooks in the
|
||||
*examples/webhook\_examples* directory.
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
You can use the Telebot module logger to log debug info about Telebot.
|
||||
Use ``telebot.logger`` to get the logger of the TeleBot module. It is
|
||||
possible to add custom logging Handlers to the logger. Refer to the
|
||||
`Python logging module
|
||||
page <https://docs.python.org/2/library/logging.html>`__ for more info.
|
||||
|
||||
.. code:: python
|
||||
|
||||
import logging
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
|
||||
|
||||
F.A.Q.
|
||||
======
|
||||
|
||||
How can I distinguish a User and a GroupChat in message.chat?
|
||||
-------------------------------------------------------------
|
||||
|
||||
There are two ways to do this:
|
||||
|
||||
- Checking the instance of message.chat with ``isinstance``:
|
||||
\`\`\`python def is\_user(chat): return isinstance(chat, types.User)
|
||||
|
||||
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
|
||||
|
||||
print is\_user(message.chat) # True or False \`\`\`
|
||||
|
||||
The Telegram Chat Group
|
||||
=======================
|
||||
|
||||
Get help. Discuss. Chat.
|
||||
|
||||
Join the `pyTelegramBotAPI Telegram Chat
|
||||
Group <https://telegram.me/joinchat/067e22c60035523fda8f6025ee87e30b>`__.
|
||||
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.
|
||||
|
||||
.. |Build Status| image:: https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master
|
||||
:target: https://travis-ci.org/eternnoir/pyTelegramBotAPI
|
70
examples/deep_linking.py
Normal file
70
examples/deep_linking.py
Normal file
@ -0,0 +1,70 @@
|
||||
# This example shows how to implement deep linking (https://core.telegram.org/bots#deep-linking)
|
||||
# with the pyTelegramBotAPI.
|
||||
# Note: This is not a working, production-ready sample.
|
||||
#
|
||||
# In this example we are connecting a user account on a website with a Telegram bot.
|
||||
# Implementing this will enable you to push notifications (and other content) to your users' Telegram account.
|
||||
# In this explanation the word 'database' can refer to any form of key-value storage.
|
||||
# The deep linking explained:
|
||||
#
|
||||
# 1. Let the user log in on an actual website with actual username-password authentication.
|
||||
#
|
||||
# 2. Generate a unique hashcode (we will call it unique_code)
|
||||
#
|
||||
# 3. Save unique_code->username to the database.
|
||||
#
|
||||
# 4. Show the user the URL https://telegram.me/YOURBOTNAME?start=unique_code
|
||||
#
|
||||
# 5. Now as soon as the user opens this URL in Telegram and presses 'Start',
|
||||
# your bot will receive a text message containing '/start unique_code',
|
||||
# where unique_code is of course replaced by the actual hashcode.
|
||||
#
|
||||
# 6. Let the bot retrieve the username by querying the database for unique_code.
|
||||
#
|
||||
# 7. Save chat_id->username to the database.
|
||||
#
|
||||
# 8. Now when your bot receives another message, it can query message.chat.id in the database
|
||||
# to check if the message is from this specific user. (And handle accordingly) or
|
||||
# you can push messages to the user using his chat id.
|
||||
#
|
||||
# Steps 1 to 4 will have to be implemented in a web server, using a language such as PHP, Python, C# or Java. These
|
||||
# steps are not shown here. Only steps 5 to 7 are illustrated, some in pseudo-code, with this example.
|
||||
|
||||
import telebot
|
||||
import time
|
||||
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
def extract_unique_code(text):
|
||||
# Extracts the unique_code from the sent /start command.
|
||||
return text.split()[1] if len(text.split()) > 1 else None
|
||||
|
||||
def in_storage(unique_code):
|
||||
# (pseudo-code) Should check if a unique code exists in storage
|
||||
return True
|
||||
|
||||
def get_username_from_storage(unique_code):
|
||||
# (pseudo-code) Does a query to the storage, retrieving the associated username
|
||||
# Should be replaced by a real database-lookup.
|
||||
return "ABC" if in_storage(unique_code) else None
|
||||
|
||||
def save_chat_id(chat_id, username):
|
||||
# (pseudo-code) Save the chat_id->username to storage
|
||||
# Should be replaced by a real database query.
|
||||
pass
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def send_welcome(message):
|
||||
unique_code = extract_unique_code(message.text)
|
||||
if unique_code: # if the '/start' command contains a unique_code
|
||||
username = get_username_from_storage(unique_code)
|
||||
if username: # if the username exists in our database
|
||||
save_chat_id(message.chat.id, username)
|
||||
reply = "Hello {0}, how are you?".format(username)
|
||||
else:
|
||||
reply = "I have no clue who you are..."
|
||||
else:
|
||||
reply = "Please visit me via a provided URL from the website."
|
||||
bot.reply_to(message, reply)
|
||||
|
||||
bot.polling()
|
131
examples/detailed_example/detailed_example.py
Normal file
131
examples/detailed_example/detailed_example.py
Normal file
@ -0,0 +1,131 @@
|
||||
"""
|
||||
This is a detailed example using almost every command of the API
|
||||
"""
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
import time
|
||||
|
||||
TOKEN = '<token_string>'
|
||||
|
||||
knownUsers = [] # todo: save these in a file,
|
||||
userStep = {} # so they won't reset every time the bot restarts
|
||||
|
||||
commands = { # command description used in the "help" command
|
||||
'start': 'Get used to the bot',
|
||||
'help': 'Gives you information about the available commands',
|
||||
'sendLongText': 'A test using the \'send_chat_action\' command',
|
||||
'getImage': 'A test using multi-stage messages, custom keyboard, and media sending'
|
||||
}
|
||||
|
||||
imageSelect = types.ReplyKeyboardMarkup(one_time_keyboard=True) # create the image selection keyboard
|
||||
imageSelect.add('cock', 'pussy')
|
||||
|
||||
hideBoard = types.ReplyKeyboardHide() # if sent as reply_markup, will hide the keyboard
|
||||
|
||||
|
||||
# error handling if user isn't known yet
|
||||
# (obsolete once known users are saved to file, because all users
|
||||
# had to use the /start command and are therefore known to the bot)
|
||||
def get_user_step(uid):
|
||||
if uid in userStep:
|
||||
return userStep[uid]
|
||||
else:
|
||||
knownUsers.append(uid)
|
||||
userStep[uid] = 0
|
||||
print "New user detected, who hasn't used \"/start\" yet"
|
||||
return 0
|
||||
|
||||
|
||||
# only used for console output now
|
||||
def listener(messages):
|
||||
"""
|
||||
When new messages arrive TeleBot will call this function.
|
||||
"""
|
||||
for m in messages:
|
||||
if m.content_type == 'text':
|
||||
# print the sent message to the console
|
||||
print str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text
|
||||
|
||||
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
bot.set_update_listener(listener) # register listener
|
||||
|
||||
|
||||
# handle the "/start" command
|
||||
@bot.message_handler(commands=['start'])
|
||||
def command_start(m):
|
||||
cid = m.chat.id
|
||||
if cid not in knownUsers: # if user hasn't used the "/start" command yet:
|
||||
knownUsers.append(cid) # save user id, so you could brodcast messages to all users of this bot later
|
||||
userStep[cid] = 0 # save user id and his current "command level", so he can use the "/getImage" command
|
||||
bot.send_message(cid, "Hello, stranger, let me scan you...")
|
||||
bot.send_message(cid, "Scanning complete, I know you now")
|
||||
command_help(m) # show the new user the help page
|
||||
else:
|
||||
bot.send_message(cid, "I already know you, no need for me to scan you again!")
|
||||
|
||||
|
||||
# help page
|
||||
@bot.message_handler(commands=['help'])
|
||||
def command_help(m):
|
||||
cid = m.chat.id
|
||||
help_text = "The following commands are available: \n"
|
||||
for key in commands: # generate help text out of the commands dictionary defined at the top
|
||||
help_text += "/" + key + ": "
|
||||
help_text += commands[key] + "\n"
|
||||
bot.send_message(cid, help_text) # send the generated help page
|
||||
|
||||
|
||||
# chat_action example (not a good one...)
|
||||
@bot.message_handler(commands=['sendLongText'])
|
||||
def command_long_text(m):
|
||||
cid = m.chat.id
|
||||
bot.send_message(cid, "If you think so...")
|
||||
bot.send_chat_action(cid, 'typing') # show the bot "typing" (max. 5 secs)
|
||||
time.sleep(3)
|
||||
bot.send_message(cid, ".")
|
||||
|
||||
|
||||
# user can chose an image (multi-stage command example)
|
||||
@bot.message_handler(commands=['getImage'])
|
||||
def command_image(m):
|
||||
cid = m.chat.id
|
||||
bot.send_message(cid, "Please choose your image now", reply_markup=imageSelect) # show the keyboard
|
||||
userStep[cid] = 1 # set the user to the next step (expecting a reply in the listener now)
|
||||
|
||||
|
||||
# if the user has issued the "/getImage" command, process the answer
|
||||
@bot.message_handler(func=lambda message: get_user_step(message.chat.id) == 1)
|
||||
def msg_image_select(m):
|
||||
cid = m.chat.id
|
||||
text = m.text
|
||||
|
||||
# for some reason the 'upload_photo' status isn't quite working (doesn't show at all)
|
||||
bot.send_chat_action(cid, 'typing')
|
||||
|
||||
if text == "cock": # send the appropriate image based on the reply to the "/getImage" command
|
||||
bot.send_photo(cid, open('rooster.jpg', 'rb'),
|
||||
reply_markup=hideBoard) # send file and hide keyboard, after image is sent
|
||||
userStep[cid] = 0 # reset the users step back to 0
|
||||
elif text == "pussy":
|
||||
bot.send_photo(cid, open('kitten.jpg', 'rb'), reply_markup=hideBoard)
|
||||
userStep[cid] = 0
|
||||
else:
|
||||
bot.send_message(cid, "Don't type bullsh*t, if I give you a predefined keyboard!")
|
||||
bot.send_message(cid, "Please try again")
|
||||
|
||||
|
||||
# filter on a specific message
|
||||
@bot.message_handler(func=lambda message: message.text == "hi")
|
||||
def command_text_hi(m):
|
||||
bot.send_message(m.chat.id, "I love you too!")
|
||||
|
||||
|
||||
# default handler for every other text
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def command_default(m):
|
||||
# this is the standard reply to a normal message
|
||||
bot.send_message(m.chat.id, "I don't understand \"" + m.text + "\"\nMaybe try the help page at /help")
|
||||
|
||||
bot.polling()
|
BIN
examples/detailed_example/kitten.jpg
Normal file
BIN
examples/detailed_example/kitten.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
examples/detailed_example/rooster.jpg
Normal file
BIN
examples/detailed_example/rooster.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
15
examples/download_file_example.py
Normal file
15
examples/download_file_example.py
Normal file
@ -0,0 +1,15 @@
|
||||
import telebot
|
||||
|
||||
TOKEN = 'YOUR BOT TOKEN'
|
||||
CHAT_ID = 'YOUR CHAT ID'
|
||||
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
|
||||
ret_msg = bot.send_voice(CHAT_ID, open('tests/test_data/record.ogg'))
|
||||
|
||||
file_info = bot.get_file(ret_msg.voice.file_id)
|
||||
|
||||
downloaded_file = bot.download_file(file_info.file_path)
|
||||
|
||||
with open('new_file.ogg', 'wb') as new_file:
|
||||
new_file.write(downloaded_file)
|
@ -7,9 +7,8 @@ API_TOKEN = '<api_token>'
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help, start'])
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message, """\
|
||||
Hi there, I am EchoBot.
|
||||
@ -23,6 +22,3 @@ def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
bot.polling()
|
||||
|
||||
while True:
|
||||
pass
|
||||
|
72
examples/inline_example.py
Normal file
72
examples/inline_example.py
Normal file
@ -0,0 +1,72 @@
|
||||
# This example show how to write an inline mode telegramt bot use pyTelegramBotAPI.
|
||||
import telebot
|
||||
import time
|
||||
import sys
|
||||
import logging
|
||||
from telebot import types
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
telebot.logger.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'text')
|
||||
def query_text(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultArticle('1', 'Result', inline_query.query)
|
||||
r2 = types.InlineQueryResultArticle('2', 'Result2', inline_query.query)
|
||||
bot.answer_inline_query(inline_query.id, [r, r2])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'photo')
|
||||
def query_photo(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultPhoto('1',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/kitten.jpg',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/kitten.jpg')
|
||||
r2 = types.InlineQueryResultPhoto('2',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg')
|
||||
bot.answer_inline_query(inline_query.id, [r, r2])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'video')
|
||||
def query_video(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultVideo('1',
|
||||
'https://github.com/eternnoir/pyTelegramBotAPI/blob/master/tests/test_data/test_video.mp4?raw=true',
|
||||
'video/mp4', 'Video',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg',
|
||||
'Title'
|
||||
)
|
||||
bot.answer_inline_query(inline_query.id, [r])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: len(query.query) is 0)
|
||||
def default_query(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultArticle('1', 'default', 'default')
|
||||
bot.answer_inline_query(inline_query.id, [r])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
def main_loop():
|
||||
bot.polling(True)
|
||||
while 1:
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main_loop()
|
||||
except KeyboardInterrupt:
|
||||
print >> sys.stderr, '\nExiting by user request.\n'
|
||||
sys.exit(0)
|
78
examples/step_example.py
Normal file
78
examples/step_example.py
Normal file
@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This Example will show you how to use register_next_step handler.
|
||||
"""
|
||||
import time
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
user_dict = {}
|
||||
|
||||
|
||||
class User:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.age = None
|
||||
self.sex = None
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
msg = bot.reply_to(message, """\
|
||||
Hi there, I am Example bot.
|
||||
What's your name?
|
||||
""")
|
||||
bot.register_next_step_handler(msg, process_name_step)
|
||||
|
||||
|
||||
def process_name_step(message):
|
||||
try:
|
||||
chat_id = message.chat.id
|
||||
name = message.text
|
||||
user = User(name)
|
||||
user_dict[chat_id] = user
|
||||
msg = bot.reply_to(message, 'How old are you?')
|
||||
bot.register_next_step_handler(msg, process_age_step)
|
||||
except Exception as e:
|
||||
bot.reply_to(message, 'oooops')
|
||||
|
||||
|
||||
def process_age_step(message):
|
||||
try:
|
||||
chat_id = message.chat.id
|
||||
age = message.text
|
||||
if not age.isdigit():
|
||||
msg = bot.reply_to(message, 'Age should be a number. How old are you?')
|
||||
bot.register_next_step_handler(msg, process_age_step)
|
||||
return
|
||||
user = user_dict[chat_id]
|
||||
user.age = age
|
||||
markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
|
||||
markup.add('Male', 'Female')
|
||||
msg = bot.reply_to(message, 'What is your gender', reply_markup=markup)
|
||||
bot.register_next_step_handler(msg, process_sex_step)
|
||||
except Exception as e:
|
||||
bot.reply_to(message, 'oooops')
|
||||
|
||||
|
||||
def process_sex_step(message):
|
||||
try:
|
||||
chat_id = message.chat.id
|
||||
sex = message.text
|
||||
user = user_dict[chat_id]
|
||||
if (sex == u'Male') or (sex == u'Female'):
|
||||
user.sex = sex
|
||||
else:
|
||||
raise Exception()
|
||||
bot.send_message(chat_id, 'Nice to meet you ' + user.name + '\n Age:' + str(user.age) + '\n Sex:' + user.sex)
|
||||
except Exception as e:
|
||||
bot.reply_to(message, 'oooops')
|
||||
|
||||
|
||||
bot.polling()
|
78
examples/telebot_bot/telebot_bot.py
Normal file
78
examples/telebot_bot/telebot_bot.py
Normal file
@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This bot was made specifically for the pyTelegramAPI Telegram chat,
|
||||
# and goes by the name 'TeleBot (@pyTeleBot)'. Join our group to talk to him!
|
||||
# WARNING: Tested with Python 2.7
|
||||
|
||||
import telebot
|
||||
import os
|
||||
|
||||
text_messages = {
|
||||
'welcome':
|
||||
u'Please welcome {name}!\n\n'
|
||||
u'This chat is intended for questions about and discussion of the pyTelegramBotAPI.\n'
|
||||
u'To enable group members to answer your questions fast and accurately, please make sure to study the '
|
||||
u'project\'s documentation (https://github.com/eternnoir/pyTelegramBotAPI/blob/master/README.md) and the '
|
||||
u'examples (https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples) first.\n\n'
|
||||
u'I hope you enjoy your stay here!',
|
||||
|
||||
'info':
|
||||
u'My name is TeleBot,\n'
|
||||
u'I am a bot that assists these wonderful bot-creating people of this bot library group chat.\n'
|
||||
u'Also, I am still under development. Please improve my functionality by making a pull request! '
|
||||
u'Suggestions are also welcome, just drop them in this group chat!',
|
||||
|
||||
'wrong_chat':
|
||||
u'Hi there!\nThanks for trying me out. However, this bot can only be used in the pyTelegramAPI group chat.\n'
|
||||
u'Join us!\n\n'
|
||||
u'https://telegram.me/joinchat/067e22c60035523fda8f6025ee87e30b'
|
||||
}
|
||||
|
||||
if "TELEBOT_BOT_TOKEN" not in os.environ or "GROUP_CHAT_ID" not in os.environ:
|
||||
raise AssertionError("Please configure TELEBOT_BOT_TOKEN and GROUP_CHAT_ID as environment variables")
|
||||
|
||||
bot = telebot.AsyncTeleBot(os.environ["TELEBOT_BOT_TOKEN"])
|
||||
GROUP_CHAT_ID = int(os.environ["GROUP_CHAT_ID"])
|
||||
|
||||
def is_api_group(chat_id):
|
||||
return chat_id== GROUP_CHAT_ID
|
||||
|
||||
@bot.message_handler(func=lambda m: True, content_types=['new_chat_participant'])
|
||||
def on_user_joins(message):
|
||||
if not is_api_group(message.chat.id):
|
||||
return
|
||||
|
||||
name = message.new_chat_participant.first_name
|
||||
if hasattr(message.new_chat_participant, 'last_name') and message.new_chat_participant.last_name is not None:
|
||||
name += u" {}".format(message.new_chat_participant.last_name)
|
||||
|
||||
if hasattr(message.new_chat_participant, 'username') and message.new_chat_participant.username is not None:
|
||||
name += u" (@{})".format(message.new_chat_participant.username)
|
||||
|
||||
bot.reply_to(message, text_messages['welcome'].format(name=name))
|
||||
|
||||
@bot.message_handler(commands=['info', 'help'])
|
||||
def on_info(message):
|
||||
if not is_api_group(message.chat.id):
|
||||
bot.reply_to(message, text_messages['wrong_chat'])
|
||||
return
|
||||
|
||||
bot.reply_to(message, text_messages['info'])
|
||||
|
||||
@bot.message_handler(commands=["ping"])
|
||||
def on_ping(message):
|
||||
bot.reply_to(message, "Still alive and kicking!")
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def on_start(message):
|
||||
if not is_api_group(message.chat.id):
|
||||
bot.reply_to(message, text_messages['wrong_chat'])
|
||||
return
|
||||
|
||||
def listener(messages):
|
||||
for m in messages:
|
||||
print str(m)
|
||||
|
||||
bot.set_update_listener(listener)
|
||||
bot.polling()
|
||||
|
||||
|
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)
|
@ -1,4 +1,5 @@
|
||||
py==1.4.29
|
||||
pytest==2.7.2
|
||||
requests==2.7.0
|
||||
six==1.9.0
|
||||
wheel==0.24.0
|
||||
|
13
setup.py
13
setup.py
@ -1,18 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
from setuptools import setup
|
||||
|
||||
def readme():
|
||||
with open('README.rst') as f:
|
||||
return f.read()
|
||||
|
||||
setup(name='pyTelegramBotAPI',
|
||||
version='0.2.0',
|
||||
version='1.4.2',
|
||||
description='Python Telegram bot api. ',
|
||||
long_description=readme(),
|
||||
author='eternnoir',
|
||||
author_email='eternnoir@gmail.com',
|
||||
url='https://github.com/eternnoir/pyTelegramBotAPI',
|
||||
packages=['telebot'],
|
||||
license='GPL2',
|
||||
keywords='tools',
|
||||
install_requires=['pytest', 'requests'],
|
||||
keywords='telegram bot api tools',
|
||||
install_requires=['pytest', 'requests', 'six'],
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
|
@ -2,16 +2,30 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import threading
|
||||
|
||||
import time
|
||||
import re
|
||||
from telebot import apihelper, types
|
||||
import sys
|
||||
import six
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('TeleBot')
|
||||
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)
|
||||
logger.addHandler(console_output_handler)
|
||||
|
||||
logger.setLevel(logging.ERROR)
|
||||
|
||||
from telebot import apihelper, types, util
|
||||
|
||||
"""
|
||||
Module : telebot
|
||||
"""
|
||||
|
||||
API_URL = r"https://api.telegram.org/"
|
||||
|
||||
|
||||
class TeleBot:
|
||||
""" This is TeleBot Class
|
||||
@ -30,69 +44,212 @@ class TeleBot:
|
||||
getUpdates
|
||||
"""
|
||||
|
||||
def __init__(self, token):
|
||||
def __init__(self, token, threaded=True, skip_pending=False):
|
||||
"""
|
||||
:param token: bot API token
|
||||
:return: Telebot object.
|
||||
"""
|
||||
self.token = token
|
||||
self.update_listener = []
|
||||
self.polling_thread = None
|
||||
self.__stop_polling = False
|
||||
self.interval = 3
|
||||
self.skip_pending = skip_pending
|
||||
|
||||
self.__stop_polling = threading.Event()
|
||||
self.last_update_id = 0
|
||||
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 = []
|
||||
self.inline_handlers = []
|
||||
self.chosen_inline_handlers = []
|
||||
|
||||
def get_update(self):
|
||||
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):
|
||||
"""
|
||||
Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned.
|
||||
:param offset: Integer. Identifier of the first update to be returned.
|
||||
:param limit: Integer. Limits the number of updates to be retrieved.
|
||||
:param timeout: Integer. Timeout in seconds for long polling.
|
||||
:return: array of Updates
|
||||
"""
|
||||
json_updates = apihelper.get_updates(self.token, offset, limit, timeout)
|
||||
ret = []
|
||||
for ju in json_updates:
|
||||
ret.append(types.Update.de_json(ju))
|
||||
return ret
|
||||
|
||||
def __skip_updates(self):
|
||||
"""
|
||||
Get and discard all pending updates before first poll of the bot
|
||||
:return: total updates skipped
|
||||
"""
|
||||
total = 0
|
||||
updates = self.get_updates(offset=self.last_update_id, timeout=1)
|
||||
while updates:
|
||||
total += len(updates)
|
||||
for update in updates:
|
||||
if update.update_id > self.last_update_id:
|
||||
self.last_update_id = update.update_id
|
||||
updates = self.get_updates(offset=self.last_update_id + 1, timeout=1)
|
||||
return total
|
||||
|
||||
def __retrieve_updates(self, timeout=20):
|
||||
"""
|
||||
Retrieves any updates from the Telegram API.
|
||||
Registered listeners and applicable message handlers will be notified when a new message arrives.
|
||||
:raises ApiException when a call has failed.
|
||||
"""
|
||||
updates = apihelper.get_updates(self.token, offset=(self.last_update_id + 1), timeout=20)
|
||||
new_messages = []
|
||||
for update in updates:
|
||||
if update['update_id'] > self.last_update_id:
|
||||
self.last_update_id = update['update_id']
|
||||
msg = types.Message.de_json(update['message'])
|
||||
new_messages.append(msg)
|
||||
if self.skip_pending:
|
||||
logger.debug('Skipped {0} pending messages'.format(self.__skip_updates()))
|
||||
self.skip_pending = False
|
||||
updates = self.get_updates(offset=(self.last_update_id + 1), timeout=timeout)
|
||||
self.process_new_updates(updates)
|
||||
|
||||
def process_new_updates(self, updates):
|
||||
new_messages = []
|
||||
new_inline_querys = []
|
||||
new_chosen_inline_results = []
|
||||
for update in updates:
|
||||
if update.update_id > self.last_update_id:
|
||||
self.last_update_id = update.update_id
|
||||
if update.message:
|
||||
new_messages.append(update.message)
|
||||
if update.inline_query:
|
||||
new_inline_querys.append(update.inline_query)
|
||||
if update.chosen_inline_result:
|
||||
new_chosen_inline_results.append(update.chosen_inline_result)
|
||||
logger.debug('Received {0} new updates'.format(len(updates)))
|
||||
if len(new_messages) > 0:
|
||||
self.__notify_update(new_messages)
|
||||
self._notify_command_handlers(new_messages)
|
||||
self.process_new_messages(new_messages)
|
||||
if len(new_inline_querys) > 0:
|
||||
self.process_new_inline_query(new_inline_querys)
|
||||
if len(new_chosen_inline_results) > 0:
|
||||
self.process_new_chosen_inline_query(new_chosen_inline_results)
|
||||
|
||||
def process_new_messages(self, new_messages):
|
||||
self._append_pre_next_step_handler()
|
||||
self.__notify_update(new_messages)
|
||||
self._notify_command_handlers(self.message_handlers, new_messages)
|
||||
self._notify_message_subscribers(new_messages)
|
||||
self._notify_message_next_handler(new_messages)
|
||||
|
||||
def process_new_inline_query(self, new_inline_querys):
|
||||
self._notify_command_handlers(self.inline_handlers, new_inline_querys)
|
||||
|
||||
def process_new_chosen_inline_query(self, new_chosen_inline_querys):
|
||||
self._notify_command_handlers(self.chosen_inline_handlers, new_chosen_inline_querys)
|
||||
|
||||
def __notify_update(self, new_messages):
|
||||
for listener in self.update_listener:
|
||||
t = threading.Thread(target=listener, args=new_messages)
|
||||
t.start()
|
||||
self.__exec_task(listener, new_messages)
|
||||
|
||||
def polling(self):
|
||||
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 an ApiException occurs.
|
||||
:param timeout: Timeout in seconds for long polling.
|
||||
:return:
|
||||
"""
|
||||
self.__stop_polling = False
|
||||
self.polling_thread = threading.Thread(target=self.__polling, args=())
|
||||
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)
|
||||
|
||||
def __polling(self):
|
||||
print('TeleBot: Started polling.')
|
||||
while not self.__stop_polling:
|
||||
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()
|
||||
except Exception as e:
|
||||
print("TeleBot: Exception occurred. Stopping.")
|
||||
self.__stop_polling = True
|
||||
print(e)
|
||||
polling_thread.put(self.__retrieve_updates, timeout)
|
||||
|
||||
print('TeleBot: Stopped polling.')
|
||||
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
|
||||
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 = True
|
||||
self.__stop_polling.set()
|
||||
|
||||
def set_update_listener(self, listener):
|
||||
self.update_listener.append(listener)
|
||||
@ -101,6 +258,12 @@ class TeleBot:
|
||||
result = apihelper.get_me(self.token)
|
||||
return types.User.de_json(result)
|
||||
|
||||
def get_file(self, file_id):
|
||||
return types.File.de_json(apihelper.get_file(self.token, file_id))
|
||||
|
||||
def download_file(self, file_path):
|
||||
return apihelper.download_file(self.token, file_path)
|
||||
|
||||
def get_user_profile_photos(self, user_id, offset=None, limit=None):
|
||||
"""
|
||||
Retrieves the user profile photos of the person with 'user_id'
|
||||
@ -113,31 +276,41 @@ class TeleBot:
|
||||
result = apihelper.get_user_profile_photos(self.token, user_id, offset, limit)
|
||||
return types.UserProfilePhotos.de_json(result)
|
||||
|
||||
def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None):
|
||||
def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None,
|
||||
parse_mode=None, disable_notification=None):
|
||||
"""
|
||||
Use this method to send text messages.
|
||||
|
||||
Warning: Do not send more than about 5000 characters each message, otherwise you'll risk an HTTP 414 error.
|
||||
If you must send more than 5000 characters, use the split_string function in apihelper.py.
|
||||
|
||||
:param chat_id:
|
||||
:param text:
|
||||
:param disable_web_page_preview:
|
||||
:param reply_to_message_id:
|
||||
:param reply_markup:
|
||||
:param parse_mode:
|
||||
:param disable_notification: Boolean, Optional. Sends the message silently.
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id,
|
||||
reply_markup))
|
||||
reply_markup, parse_mode, disable_notification))
|
||||
|
||||
def forward_message(self, chat_id, from_chat_id, message_id):
|
||||
def forward_message(self, chat_id, from_chat_id, message_id, disable_notification=None):
|
||||
"""
|
||||
Use this method to forward messages of any kind.
|
||||
:param disable_notification:
|
||||
:param chat_id: which chat to forward
|
||||
:param from_chat_id: which chat message from
|
||||
:param message_id: message id
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(apihelper.forward_message(self.token, chat_id, from_chat_id, message_id))
|
||||
return types.Message.de_json(
|
||||
apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification))
|
||||
|
||||
def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None):
|
||||
def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
"""
|
||||
Use this method to send photos.
|
||||
:param chat_id:
|
||||
@ -148,22 +321,42 @@ class TeleBot:
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup))
|
||||
apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup,
|
||||
disable_notification))
|
||||
|
||||
def send_audio(self, chat_id, data, reply_to_message_id=None, reply_markup=None):
|
||||
def send_audio(self, chat_id, audio, duration=None, performer=None, title=None, reply_to_message_id=None,
|
||||
reply_markup=None, disable_notification=None):
|
||||
"""
|
||||
Use this method to send audio files, if you want Telegram clients to display the file as a playable
|
||||
voice message. For this to work, your audio must be in an .ogg file encoded with OPUS
|
||||
:param chat_id:
|
||||
:param data:
|
||||
:param reply_to_message_id:
|
||||
Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format.
|
||||
:param chat_id:Unique identifier for the message recipient
|
||||
:param audio:Audio file to send.
|
||||
:param duration:Duration of the audio in seconds
|
||||
:param performer:Performer
|
||||
:param title:Track name
|
||||
:param reply_to_message_id:If the message is a reply, ID of the original message
|
||||
:param reply_markup:
|
||||
:return: API reply.
|
||||
:return: Message
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_data(self.token, chat_id, data, 'audio', reply_to_message_id, reply_markup))
|
||||
apihelper.send_audio(self.token, chat_id, audio, duration, performer, title, reply_to_message_id,
|
||||
reply_markup, disable_notification))
|
||||
|
||||
def send_document(self, chat_id, data, reply_to_message_id=None, reply_markup=None):
|
||||
def send_voice(self, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
"""
|
||||
Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message.
|
||||
:param chat_id:Unique identifier for the message recipient.
|
||||
:param voice:
|
||||
:param duration:Duration of sent audio in seconds
|
||||
:param reply_to_message_id:
|
||||
:param reply_markup:
|
||||
:return: Message
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_voice(self.token, chat_id, voice, duration, reply_to_message_id, reply_markup,
|
||||
disable_notification))
|
||||
|
||||
def send_document(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None):
|
||||
"""
|
||||
Use this method to send general files.
|
||||
:param chat_id:
|
||||
@ -173,9 +366,10 @@ class TeleBot:
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup))
|
||||
apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup,
|
||||
disable_notification))
|
||||
|
||||
def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None):
|
||||
def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None):
|
||||
"""
|
||||
Use this method to send .webp stickers.
|
||||
:param chat_id:
|
||||
@ -185,21 +379,27 @@ class TeleBot:
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_data(self.token, chat_id, data, 'sticker', reply_to_message_id, reply_markup))
|
||||
apihelper.send_data(self.token, chat_id, data, 'sticker', reply_to_message_id, reply_markup,
|
||||
disable_notification))
|
||||
|
||||
def send_video(self, chat_id, data, reply_to_message_id=None, reply_markup=None):
|
||||
def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
"""
|
||||
Use this method to send video files, Telegram clients support mp4 videos.
|
||||
:param chat_id:
|
||||
:param data:
|
||||
:param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id
|
||||
:param data: InputFile or String : Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram server
|
||||
:param duration: Integer : Duration of sent video in seconds
|
||||
:param caption: String : Video caption (may also be used when resending videos by file_id).
|
||||
:param reply_to_message_id:
|
||||
:param reply_markup:
|
||||
:return: API reply.
|
||||
:return:
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_data(self.token, chat_id, data, 'video', reply_to_message_id, reply_markup))
|
||||
apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup,
|
||||
disable_notification))
|
||||
|
||||
def send_location(self, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None):
|
||||
def send_location(self, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
"""
|
||||
Use this method to send point on the map.
|
||||
:param chat_id:
|
||||
@ -210,7 +410,8 @@ class TeleBot:
|
||||
:return: API reply.
|
||||
"""
|
||||
return types.Message.de_json(
|
||||
apihelper.send_location(self.token, chat_id, latitude, longitude, reply_to_message_id, reply_markup))
|
||||
apihelper.send_location(self.token, chat_id, latitude, longitude, reply_to_message_id, reply_markup,
|
||||
disable_notification))
|
||||
|
||||
def send_chat_action(self, chat_id, action):
|
||||
"""
|
||||
@ -225,8 +426,87 @@ class TeleBot:
|
||||
return apihelper.send_chat_action(self.token, chat_id, action)
|
||||
|
||||
def reply_to(self, message, text, **kwargs):
|
||||
"""
|
||||
Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)`
|
||||
"""
|
||||
return self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)
|
||||
|
||||
def answer_inline_query(self, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None):
|
||||
"""
|
||||
Use this method to send answers to an inline query. On success, True is returned.
|
||||
No more than 50 results per query are allowed.
|
||||
:param inline_query_id: Unique identifier for the answered query
|
||||
:param results: Array of results for the inline query
|
||||
:param cache_time: The maximum amount of time in seconds that the result of the inline query may be cached on the server.
|
||||
:param is_personal: Pass True, if results may be cached on the server side only for the user that sent the query.
|
||||
:param next_offset: Pass the offset that a client should send in the next query with the same text to receive more results.
|
||||
:return: True means success.
|
||||
"""
|
||||
return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset)
|
||||
|
||||
def register_for_reply(self, message, callback):
|
||||
"""
|
||||
Registers a callback function to be notified when a reply to `message` arrives.
|
||||
|
||||
Warning: `message` must be sent with reply_markup=types.ForceReply(), otherwise TeleBot will not be able to see
|
||||
the difference between a reply to `message` and an ordinary message.
|
||||
|
||||
:param message: The message for which we are awaiting a reply.
|
||||
:param callback: The callback function to be called when a reply arrives. Must accept one `message`
|
||||
parameter, which will contain the replied message.
|
||||
"""
|
||||
with self.message_subscribers_lock:
|
||||
self.message_subscribers_messages.insert(0, message.message_id)
|
||||
self.message_subscribers_callbacks.insert(0, callback)
|
||||
if len(self.message_subscribers_messages) > 10000:
|
||||
self.message_subscribers_messages.pop()
|
||||
self.message_subscribers_callbacks.pop()
|
||||
|
||||
def _notify_message_subscribers(self, new_messages):
|
||||
for message in new_messages:
|
||||
if not message.reply_to_message:
|
||||
continue
|
||||
|
||||
reply_msg_id = message.reply_to_message.message_id
|
||||
if reply_msg_id in self.message_subscribers_messages:
|
||||
index = self.message_subscribers_messages.index(reply_msg_id)
|
||||
self.message_subscribers_callbacks[index](message)
|
||||
|
||||
with self.message_subscribers_lock:
|
||||
index = self.message_subscribers_messages.index(reply_msg_id)
|
||||
del self.message_subscribers_messages[index]
|
||||
del self.message_subscribers_callbacks[index]
|
||||
|
||||
def register_next_step_handler(self, message, callback):
|
||||
"""
|
||||
Registers a callback function to be notified when new message arrives after `message`.
|
||||
|
||||
:param message: The message for which we want to handle new message after that in same chat.
|
||||
:param callback: The callback function which next new message arrives.
|
||||
"""
|
||||
chat_id = message.chat.id
|
||||
if chat_id in self.pre_message_subscribers_next_step:
|
||||
self.pre_message_subscribers_next_step[chat_id].append(callback)
|
||||
else:
|
||||
self.pre_message_subscribers_next_step[chat_id] = [callback]
|
||||
|
||||
def _notify_message_next_handler(self, new_messages):
|
||||
for message in new_messages:
|
||||
chat_id = message.chat.id
|
||||
if chat_id in self.message_subscribers_next_step:
|
||||
handlers = self.message_subscribers_next_step[chat_id]
|
||||
for handler in handlers:
|
||||
self.__exec_task(handler, message)
|
||||
self.message_subscribers_next_step.pop(chat_id, None)
|
||||
|
||||
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.
|
||||
@ -257,146 +537,136 @@ class TeleBot:
|
||||
:param content_types: This commands' supported content types. Must be a list. Defaults to ['text'].
|
||||
"""
|
||||
|
||||
def decorator(fn):
|
||||
func_dict = {'function': fn, 'content_types': content_types}
|
||||
if regexp:
|
||||
func_dict['regexp'] = regexp if 'text' in content_types else None
|
||||
if func:
|
||||
func_dict['lambda'] = func
|
||||
if commands:
|
||||
func_dict['commands'] = commands if 'text' in content_types else None
|
||||
self.message_handlers.append(func_dict)
|
||||
return fn
|
||||
def decorator(handler):
|
||||
self.add_message_handler(handler, commands, regexp, func, content_types)
|
||||
return handler
|
||||
|
||||
return decorator
|
||||
|
||||
@staticmethod
|
||||
def is_command(text):
|
||||
"""
|
||||
Checks if `text` is a command. Telegram chat commands start with the '/' character.
|
||||
:param text: Text to check.
|
||||
:return: True if `text` is a command, else False.
|
||||
"""
|
||||
return text.startswith('/')
|
||||
def add_message_handler(self, handler, commands=None, regexp=None, func=None, content_types=None):
|
||||
if content_types is None:
|
||||
content_types = ['text']
|
||||
|
||||
@staticmethod
|
||||
def extract_command(text):
|
||||
"""
|
||||
Extracts the command from `text` (minus the '/') if `text` is a command (see is_command).
|
||||
If `text` is not a command, this function returns None.
|
||||
filters = {'content_types': content_types}
|
||||
if regexp:
|
||||
filters['regexp'] = regexp
|
||||
if func:
|
||||
filters['lambda'] = func
|
||||
if commands:
|
||||
filters['commands'] = commands
|
||||
|
||||
Examples:
|
||||
extract_command('/help'): 'help'
|
||||
extract_command('/search black eyed peas'): 'search'
|
||||
extract_command('Good day to you'): None
|
||||
handler_dict = {
|
||||
'function': handler,
|
||||
'filters': filters
|
||||
}
|
||||
|
||||
:param text: String to extract the command from
|
||||
:return: the command if `text` is a command, else None.
|
||||
"""
|
||||
return text.split()[0][1:] if TeleBot.is_command(text) else None
|
||||
self.message_handlers.append(handler_dict)
|
||||
|
||||
def inline_handler(self, func):
|
||||
def decorator(handler):
|
||||
self.add_inline_handler(handler, func)
|
||||
return handler
|
||||
|
||||
return decorator
|
||||
|
||||
def add_inline_handler(self, handler, func):
|
||||
filters = {'lambda': func}
|
||||
|
||||
handler_dict = {
|
||||
'function': handler,
|
||||
'filters': filters
|
||||
}
|
||||
|
||||
self.inline_handlers.append(handler_dict)
|
||||
|
||||
def chosen_inline_handler(self, func):
|
||||
def decorator(handler):
|
||||
self.add_chosen_inline_handler(handler, func)
|
||||
return handler
|
||||
|
||||
return decorator
|
||||
|
||||
def add_chosen_inline_handler(self, handler, func):
|
||||
filters = {'lambda': func}
|
||||
|
||||
handler_dict = {
|
||||
'function': handler,
|
||||
'filters': filters
|
||||
}
|
||||
|
||||
self.chosen_inline_handlers.append(handler_dict)
|
||||
|
||||
@staticmethod
|
||||
def _test_message_handler(message_handler, message):
|
||||
if message.content_type not in message_handler['content_types']:
|
||||
return False
|
||||
if 'commands' in message_handler and message.content_type == 'text':
|
||||
return TeleBot.extract_command(message.text) in message_handler['commands']
|
||||
if 'regexp' in message_handler and message.content_type == 'text' and re.search(message_handler['regexp'],
|
||||
message.text):
|
||||
return False
|
||||
if 'lambda' in message_handler:
|
||||
return message_handler['lambda'](message)
|
||||
for filter, filter_value in six.iteritems(message_handler['filters']):
|
||||
if not TeleBot._test_filter(filter, filter_value, message):
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _test_filter(filter, filter_value, message):
|
||||
if filter == 'content_types':
|
||||
return message.content_type in filter_value
|
||||
if filter == 'regexp':
|
||||
return message.content_type == 'text' and re.search(filter_value, message.text)
|
||||
if filter == 'commands':
|
||||
return message.content_type == 'text' and util.extract_command(message.text) in filter_value
|
||||
if filter == 'lambda':
|
||||
return filter_value(message)
|
||||
return False
|
||||
|
||||
def _notify_command_handlers(self, new_messages):
|
||||
def _notify_command_handlers(self, handlers, new_messages):
|
||||
for message in new_messages:
|
||||
for message_handler in self.message_handlers:
|
||||
for message_handler in handlers:
|
||||
if self._test_message_handler(message_handler, message):
|
||||
t = threading.Thread(target=message_handler['function'], args=(message,))
|
||||
t.start()
|
||||
self.__exec_task(message_handler['function'], message)
|
||||
break
|
||||
|
||||
|
||||
class AsyncTask:
|
||||
def __init__(self, target, *args, **kwargs):
|
||||
self.target = target
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
self.done = False
|
||||
self.thread = threading.Thread(target=self._run)
|
||||
self.thread.start()
|
||||
|
||||
def _run(self):
|
||||
try:
|
||||
self.result = self.target(*self.args, **self.kwargs)
|
||||
except Exception as e:
|
||||
self.result = e
|
||||
self.done = True
|
||||
|
||||
def wait(self):
|
||||
if not self.done:
|
||||
self.thread.join()
|
||||
if isinstance(self.result, Exception):
|
||||
raise self.result
|
||||
else:
|
||||
return self.result
|
||||
|
||||
|
||||
def async():
|
||||
def decorator(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
return AsyncTask(fn, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class AsyncTeleBot(TeleBot):
|
||||
def __init__(self, *args, **kwargs):
|
||||
TeleBot.__init__(self, *args, **kwargs)
|
||||
|
||||
@async()
|
||||
@util.async()
|
||||
def get_me(self):
|
||||
return TeleBot.get_me(self)
|
||||
|
||||
@async()
|
||||
@util.async()
|
||||
def get_user_profile_photos(self, *args, **kwargs):
|
||||
return TeleBot.get_user_profile_photos(self, *args, **kwargs)
|
||||
|
||||
@async()
|
||||
@util.async()
|
||||
def send_message(self, *args, **kwargs):
|
||||
return TeleBot.send_message(self, *args, **kwargs)
|
||||
|
||||
@async()
|
||||
@util.async()
|
||||
def forward_message(self, *args, **kwargs):
|
||||
return TeleBot.forward_message(self, *args, **kwargs)
|
||||
|
||||
@async()
|
||||
@util.async()
|
||||
def send_photo(self, *args, **kwargs):
|
||||
return TeleBot.send_photo(self, *args, **kwargs)
|
||||
|
||||
@async()
|
||||
@util.async()
|
||||
def send_audio(self, *args, **kwargs):
|
||||
return TeleBot.send_audio(self, *args, **kwargs)
|
||||
|
||||
@async()
|
||||
@util.async()
|
||||
def send_document(self, *args, **kwargs):
|
||||
return TeleBot.send_document(self, *args, **kwargs)
|
||||
|
||||
@async()
|
||||
@util.async()
|
||||
def send_sticker(self, *args, **kwargs):
|
||||
return TeleBot.send_sticker(self, *args, **kwargs)
|
||||
|
||||
@async()
|
||||
@util.async()
|
||||
def send_video(self, *args, **kwargs):
|
||||
return TeleBot.send_video(self, *args, **kwargs)
|
||||
|
||||
@async()
|
||||
@util.async()
|
||||
def send_location(self, *args, **kwargs):
|
||||
return TeleBot.send_location(self, *args, **kwargs)
|
||||
|
||||
@async()
|
||||
@util.async()
|
||||
def send_chat_action(self, *args, **kwargs):
|
||||
return TeleBot.send_chat_action(self, *args, **kwargs)
|
||||
|
@ -1,12 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import requests
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
from telebot import util
|
||||
|
||||
logger = telebot.logger
|
||||
|
||||
API_URL = "https://api.telegram.org/bot{0}/{1}"
|
||||
FILE_URL = "https://api.telegram.org/file/bot{0}/{1}"
|
||||
|
||||
|
||||
def _make_request(token, method_name, method='get', params=None, files=None):
|
||||
def _make_request(token, method_name, method='get', params=None, files=None, base_url=API_URL):
|
||||
"""
|
||||
Makes a request to the Telegram API.
|
||||
:param token: The bot's API token. (Created with @BotFather)
|
||||
@ -14,27 +19,69 @@ def _make_request(token, method_name, method='get', params=None, files=None):
|
||||
:param method: HTTP method to be used. Defaults to 'get'.
|
||||
:param params: Optional parameters. Should be a dictionary with key-value pairs.
|
||||
:param files: Optional files.
|
||||
:return:
|
||||
:return: The result parsed to a JSON dictionary.
|
||||
"""
|
||||
request_url = telebot.API_URL + 'bot' + token + '/' + method_name
|
||||
request_url = base_url.format(token, method_name)
|
||||
logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files))
|
||||
result = requests.request(method, request_url, params=params, files=files)
|
||||
logger.debug("The server returned: '{0}'".format(result.text.encode('utf8')))
|
||||
return _check_result(method_name, result)['result']
|
||||
|
||||
|
||||
def _check_result(method_name, result):
|
||||
"""
|
||||
Checks whether `result` is a valid API response.
|
||||
A result is considered invalid if:
|
||||
- The server returned an HTTP response code other than 200
|
||||
- The content of the result is invalid JSON.
|
||||
- The method call was unsuccessful (The JSON 'ok' field equals False)
|
||||
|
||||
:raises ApiException: if one of the above listed cases is applicable
|
||||
:param method_name: The name of the method called
|
||||
:param result: The returned result of the method request
|
||||
:return: The result parsed to a JSON dictionary.
|
||||
"""
|
||||
if result.status_code != 200:
|
||||
raise ApiException(method_name, result)
|
||||
msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \
|
||||
.format(result.status_code, result.reason, result.text.encode('utf8'))
|
||||
raise ApiException(msg, method_name, result)
|
||||
|
||||
try:
|
||||
result_json = result.json()
|
||||
if not result_json['ok']:
|
||||
raise Exception()
|
||||
except:
|
||||
raise ApiException(method_name, result)
|
||||
return result_json['result']
|
||||
msg = 'The server returned an invalid JSON response. Response body:\n[{0}]' \
|
||||
.format(result.text.encode('utf8'))
|
||||
raise ApiException(msg, method_name, result)
|
||||
|
||||
if not result_json['ok']:
|
||||
msg = 'Error code: {0} Description: {1}' \
|
||||
.format(result_json['error_code'], result_json['description'])
|
||||
raise ApiException(msg, method_name, result)
|
||||
return result_json
|
||||
|
||||
|
||||
def get_me(token):
|
||||
method_url = 'getMe'
|
||||
method_url = r'getMe'
|
||||
return _make_request(token, method_url)
|
||||
|
||||
|
||||
def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None):
|
||||
def get_file(token, file_id):
|
||||
method_url = r'getFile'
|
||||
return _make_request(token, method_url, params={'file_id': file_id})
|
||||
|
||||
|
||||
def download_file(token, file_path):
|
||||
url = FILE_URL.format(token, file_path)
|
||||
result = requests.get(url)
|
||||
if result.status_code != 200:
|
||||
msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \
|
||||
.format(result.status_code, result.reason, result.text)
|
||||
raise ApiException(msg, 'Download file', result)
|
||||
return result.content
|
||||
|
||||
|
||||
def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None,
|
||||
parse_mode=None, disable_notification=None):
|
||||
"""
|
||||
Use this method to send text messages. On success, the sent Message is returned.
|
||||
:param token:
|
||||
@ -53,7 +100,23 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
return _make_request(token, method_url, params=payload)
|
||||
if parse_mode:
|
||||
payload['parse_mode'] = parse_mode
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def set_webhook(token, url=None, certificate=None):
|
||||
method_url = r'setWebhook'
|
||||
payload = {
|
||||
'url': url if url else "",
|
||||
}
|
||||
files = None
|
||||
if certificate:
|
||||
files = {'certificate': certificate}
|
||||
|
||||
return _make_request(token, method_url, params=payload, files=files)
|
||||
|
||||
|
||||
def get_updates(token, offset=None, limit=None, timeout=None):
|
||||
@ -78,32 +141,44 @@ def get_user_profile_photos(token, user_id, offset=None, limit=None):
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def forward_message(token, chat_id, from_chat_id, message_id):
|
||||
def forward_message(token, chat_id, from_chat_id, message_id, disable_notification=None):
|
||||
method_url = r'forwardMessage'
|
||||
payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id}
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None):
|
||||
def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
method_url = r'sendPhoto'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = {'photo': photo}
|
||||
files = None
|
||||
if not util.is_string(photo):
|
||||
files = {'photo': photo}
|
||||
else:
|
||||
payload['photo'] = photo
|
||||
if caption:
|
||||
payload['caption'] = caption
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_location(token, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None):
|
||||
def send_location(token, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
method_url = r'sendLocation'
|
||||
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude}
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
@ -113,26 +188,116 @@ def send_chat_action(token, chat_id, action):
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None):
|
||||
method_url = get_method_by_type(data_type)
|
||||
def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
method_url = r'sendVideo'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = {data_type: data}
|
||||
files = None
|
||||
if not util.is_string(data):
|
||||
files = {'video': data}
|
||||
else:
|
||||
payload['video'] = data
|
||||
if duration:
|
||||
payload['duration'] = duration
|
||||
if caption:
|
||||
payload['caption'] = caption
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None):
|
||||
method_url = r'sendVoice'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
if not util.is_string(voice):
|
||||
files = {'voice': voice}
|
||||
else:
|
||||
payload['voice'] = voice
|
||||
if duration:
|
||||
payload['duration'] = duration
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_audio(token, chat_id, audio, duration=None, performer=None, title=None, reply_to_message_id=None,
|
||||
reply_markup=None, disable_notification=None):
|
||||
method_url = r'sendAudio'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
if not util.is_string(audio):
|
||||
files = {'audio': audio}
|
||||
else:
|
||||
payload['audio'] = audio
|
||||
if duration:
|
||||
payload['duration'] = duration
|
||||
if performer:
|
||||
payload['performer'] = performer
|
||||
if title:
|
||||
payload['title'] = title
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, disable_notification=None):
|
||||
method_url = get_method_by_type(data_type)
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
if not util.is_string(data):
|
||||
files = {data_type: data}
|
||||
else:
|
||||
payload[data_type] = data
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def get_method_by_type(data_type):
|
||||
if data_type == 'audio':
|
||||
return 'sendAudio'
|
||||
if data_type == 'document':
|
||||
return 'sendDocument'
|
||||
return r'sendDocument'
|
||||
if data_type == 'sticker':
|
||||
return 'sendSticker'
|
||||
if data_type == 'video':
|
||||
return 'sendVideo'
|
||||
return r'sendSticker'
|
||||
|
||||
|
||||
def answer_inline_query(token, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None):
|
||||
method_url = 'answerInlineQuery'
|
||||
payload = {'inline_query_id': inline_query_id, 'results': _convert_inline_results(results)}
|
||||
if cache_time:
|
||||
payload['cache_time'] = cache_time
|
||||
if is_personal:
|
||||
payload['is_personal'] = is_personal
|
||||
if next_offset:
|
||||
payload['next_offset'] = next_offset
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def _convert_inline_results(results):
|
||||
ret = ''
|
||||
for r in results:
|
||||
if isinstance(r, types.JsonSerializable):
|
||||
ret = ret + r.to_json() + ','
|
||||
if len(ret) > 0:
|
||||
ret = ret[:-1]
|
||||
return '[' + ret + ']'
|
||||
|
||||
|
||||
def _convert_markup(markup):
|
||||
@ -144,8 +309,12 @@ def _convert_markup(markup):
|
||||
class ApiException(Exception):
|
||||
"""
|
||||
This class represents an Exception thrown when a call to the Telegram API fails.
|
||||
In addition to an informative message, it has a `function_name` and a `result` attribute, which respectively
|
||||
contain the name of the failed function and the returned result that made the function to be considered as
|
||||
failed.
|
||||
"""
|
||||
def __init__(self, function_name, result):
|
||||
super(ApiException, self).__init__('{0} failed. Returned result: {1}'.format(function_name, result))
|
||||
|
||||
def __init__(self, msg, function_name, result):
|
||||
super(ApiException, self).__init__("A request to the Telegram API was unsuccessful. {0}".format(msg))
|
||||
self.function_name = function_name
|
||||
self.result = result
|
||||
|
543
telebot/types.py
543
telebot/types.py
@ -1,26 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Available types
|
||||
|
||||
User
|
||||
GroupChat
|
||||
Message
|
||||
PhotoSize
|
||||
Audio
|
||||
Document
|
||||
Sticker
|
||||
Video
|
||||
Contact
|
||||
Location
|
||||
Update
|
||||
InputFile
|
||||
UserProfilePhotos
|
||||
ReplyKeyboardMarkup
|
||||
ReplyKeyboardHide
|
||||
ForceReply
|
||||
"""
|
||||
|
||||
import json
|
||||
import six
|
||||
|
||||
|
||||
class JsonSerializable:
|
||||
@ -44,6 +25,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):
|
||||
"""
|
||||
@ -69,6 +51,39 @@ class JsonDeserializable:
|
||||
else:
|
||||
raise ValueError("json_type should be a json dict or string.")
|
||||
|
||||
def __str__(self):
|
||||
d = {}
|
||||
for x, y in six.iteritems(self.__dict__):
|
||||
if hasattr(y, '__dict__'):
|
||||
d[x] = y.__dict__
|
||||
else:
|
||||
d[x] = y
|
||||
|
||||
return six.text_type(d)
|
||||
|
||||
|
||||
class Update(JsonDeserializable):
|
||||
@classmethod
|
||||
def de_json(cls, json_type):
|
||||
obj = cls.check_json(json_type)
|
||||
update_id = obj['update_id']
|
||||
message = None
|
||||
inline_query = None
|
||||
chosen_inline_result = None
|
||||
if 'message' in obj:
|
||||
message = Message.de_json(obj['message'])
|
||||
if 'inline_query' in obj:
|
||||
inline_query = InlineQuery.de_json(obj['inline_query'])
|
||||
if 'chosen_inline_result' in obj:
|
||||
chosen_inline_result = ChosenInlineResult.de_json(obj['chosen_inline_result'])
|
||||
return cls(update_id, message, inline_query, chosen_inline_result)
|
||||
|
||||
def __init__(self, update_id, message, inline_query, chosen_inline_result):
|
||||
self.update_id = update_id
|
||||
self.message = message
|
||||
self.inline_query = inline_query
|
||||
self.chosen_inline_result = chosen_inline_result
|
||||
|
||||
|
||||
class User(JsonDeserializable):
|
||||
@classmethod
|
||||
@ -76,13 +91,9 @@ class User(JsonDeserializable):
|
||||
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
|
||||
@ -97,29 +108,61 @@ 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 = {}
|
||||
if 'forward_from' in obj:
|
||||
opts['forward_from'] = User.de_json(obj['forward_from'])
|
||||
if 'forward_date' in obj:
|
||||
opts['forward_date'] = obj['forward_date']
|
||||
if 'reply_to_message' in obj:
|
||||
opts['reply_to_message'] = Message.de_json(obj['reply_to_message'])
|
||||
if 'text' in obj:
|
||||
opts['text'] = obj['text']
|
||||
content_type = 'text'
|
||||
if 'audio' in obj:
|
||||
opts['audio'] = Audio.de_json(obj['audio'])
|
||||
content_type = 'audio'
|
||||
if 'voice' in obj:
|
||||
opts['voice'] = Audio.de_json(obj['voice'])
|
||||
content_type = 'voice'
|
||||
if 'document' in obj:
|
||||
opts['document'] = Document.de_json(obj['document'])
|
||||
content_type = 'document'
|
||||
@ -156,7 +199,9 @@ class Message(JsonDeserializable):
|
||||
if 'group_chat_created' in obj:
|
||||
opts['group_chat_created'] = obj['group_chat_created']
|
||||
content_type = 'group_chat_created'
|
||||
return Message(message_id, from_user, date, chat, content_type, opts)
|
||||
if 'caption' in obj:
|
||||
opts['caption'] = obj['caption']
|
||||
return cls(message_id, from_user, date, chat, content_type, opts)
|
||||
|
||||
@classmethod
|
||||
def parse_chat(cls, chat):
|
||||
@ -175,9 +220,28 @@ class Message(JsonDeserializable):
|
||||
def __init__(self, message_id, from_user, date, chat, content_type, options):
|
||||
self.chat = chat
|
||||
self.date = date
|
||||
self.fromUser = from_user
|
||||
self.from_user = from_user
|
||||
self.message_id = message_id
|
||||
self.content_type = content_type
|
||||
self.forward_from = None
|
||||
self.forward_date = None
|
||||
self.reply_to_message = None
|
||||
self.text = None
|
||||
self.audio = None
|
||||
self.voice = None
|
||||
self.document = None
|
||||
self.photo = None
|
||||
self.sticker = None
|
||||
self.video = None
|
||||
self.location = None
|
||||
self.contact = None
|
||||
self.new_chat_participant = None
|
||||
self.left_chat_participant = None
|
||||
self.new_chat_title = None
|
||||
self.new_chat_photo = None
|
||||
self.delete_chat_photo = None
|
||||
self.group_chat_created = None
|
||||
self.caption = None
|
||||
for key in options:
|
||||
setattr(self, key, options[key])
|
||||
|
||||
@ -189,10 +253,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
|
||||
@ -207,13 +269,30 @@ class Audio(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 Audio(file_id, duration, 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
|
||||
self.duration = duration
|
||||
self.performer = performer
|
||||
self.title = title
|
||||
self.mime_type = mime_type
|
||||
self.file_size = file_size
|
||||
|
||||
|
||||
class Voice(JsonDeserializable):
|
||||
@classmethod
|
||||
def de_json(cls, json_string):
|
||||
obj = cls.check_json(json_string)
|
||||
file_id = obj['file_id']
|
||||
duration = obj['duration']
|
||||
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
|
||||
@ -228,18 +307,12 @@ class Document(JsonDeserializable):
|
||||
obj = cls.check_json(json_string)
|
||||
file_id = obj['file_id']
|
||||
thumb = None
|
||||
if 'file_id' in obj['thumb']:
|
||||
if 'thumb' in obj and '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 'mine_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)
|
||||
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
|
||||
@ -256,11 +329,11 @@ class Sticker(JsonDeserializable):
|
||||
file_id = obj['file_id']
|
||||
width = obj['width']
|
||||
height = obj['height']
|
||||
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)
|
||||
thumb = None
|
||||
if 'thumb' in obj:
|
||||
thumb = PhotoSize.de_json(obj['thumb'])
|
||||
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
|
||||
@ -278,19 +351,14 @@ class Video(JsonDeserializable):
|
||||
width = obj['width']
|
||||
height = obj['height']
|
||||
duration = obj['duration']
|
||||
thumb = PhotoSize.de_json(obj['thumb'])
|
||||
caption = None
|
||||
mime_type = None
|
||||
file_size = None
|
||||
if 'caption' in obj:
|
||||
caption = obj['caption']
|
||||
if 'mine_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, caption)
|
||||
thumb = None
|
||||
if 'thumb' in obj:
|
||||
thumb = PhotoSize.de_json(obj['thumb'])
|
||||
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, mime_type=None, file_size=None, caption=None):
|
||||
def __init__(self, file_id, width, height, duration, thumb=None, mime_type=None, file_size=None):
|
||||
self.file_id = file_id
|
||||
self.width = width
|
||||
self.height = height
|
||||
@ -298,7 +366,6 @@ class Video(JsonDeserializable):
|
||||
self.thumb = thumb
|
||||
self.mime_type = mime_type
|
||||
self.file_size = file_size
|
||||
self.caption = caption
|
||||
|
||||
|
||||
class Contact(JsonDeserializable):
|
||||
@ -307,13 +374,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
|
||||
@ -328,7 +391,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
|
||||
@ -341,13 +404,28 @@ 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):
|
||||
@classmethod
|
||||
def de_json(cls, json_type):
|
||||
obj = cls.check_json(json_type)
|
||||
file_id = obj['file_id']
|
||||
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
|
||||
self.file_size = file_size
|
||||
self.file_path = file_path
|
||||
|
||||
|
||||
class ForceReply(JsonSerializable):
|
||||
def __init__(self, selective=None):
|
||||
self.selective = selective
|
||||
@ -427,3 +505,306 @@ class ReplyKeyboardMarkup(JsonSerializable):
|
||||
json_dict['selective'] = True
|
||||
|
||||
return json.dumps(json_dict)
|
||||
|
||||
|
||||
class InlineQuery(JsonDeserializable):
|
||||
@classmethod
|
||||
def de_json(cls, json_type):
|
||||
obj = cls.check_json(json_type)
|
||||
id = obj['id']
|
||||
from_user = User.de_json(obj['from'])
|
||||
query = obj['query']
|
||||
offset = obj['offset']
|
||||
return cls(id, from_user, query, offset)
|
||||
|
||||
def __init__(self, id, from_user, query, offset):
|
||||
"""
|
||||
This object represents an incoming inline query.
|
||||
When the user sends an empty query, your bot could
|
||||
return some default or trending results.
|
||||
:param id: string Unique identifier for this query
|
||||
:param from_user: User Sender
|
||||
:param query: String Text of the query
|
||||
:param offset: String Offset of the results to be returned, can be controlled by the bot
|
||||
:return: InlineQuery Object
|
||||
"""
|
||||
self.id = id
|
||||
self.from_user = from_user
|
||||
self.query = query
|
||||
self.offset = offset
|
||||
|
||||
|
||||
class ChosenInlineResult(JsonDeserializable):
|
||||
@classmethod
|
||||
def de_json(cls, json_type):
|
||||
obj = cls.check_json(json_type)
|
||||
result_id = obj['result_id']
|
||||
from_user = User.de_json(obj['from'])
|
||||
query = obj['query']
|
||||
return cls(result_id, from_user, query)
|
||||
|
||||
def __init__(self, result_id, from_user, query):
|
||||
"""
|
||||
This object represents a result of an inline query
|
||||
that was chosen by the user and sent to their chat partner.
|
||||
:param result_id: string The unique identifier for the result that was chosen.
|
||||
:param from_user: User The user that chose the result.
|
||||
:param query: String The query that was used to obtain the result.
|
||||
:return: ChosenInlineResult Object.
|
||||
"""
|
||||
self.result_id = result_id
|
||||
self.from_user = from_user
|
||||
self.query = query
|
||||
|
||||
|
||||
class InlineQueryResultArticle(JsonSerializable):
|
||||
def __init__(self, id, title, message_text, parse_mode=None, disable_web_page_preview=None, url=None,
|
||||
hide_url=None, description=None, thumb_url=None, thumb_width=None, thumb_height=None):
|
||||
"""
|
||||
Represents a link to an article or web page.
|
||||
:param id: Unique identifier for this result, 1-64 Bytes.
|
||||
:param title: Title of the result.
|
||||
:param message_text: Text of the message to be sent.
|
||||
:param parse_mode: Send “Markdown”, if you want Telegram apps to show bold, italic and inline URLs in your bot's message.
|
||||
:param disable_web_page_preview: Disables link previews for links in the sent message.
|
||||
:param url: URL of the result.
|
||||
:param hide_url: Pass True, if you don't want the URL to be shown in the message.
|
||||
:param description: Short description of the result.
|
||||
:param thumb_url: Url of the thumbnail for the result.
|
||||
:param thumb_width: Thumbnail width.
|
||||
:param thumb_height: Thumbnail height
|
||||
:return:
|
||||
"""
|
||||
self.type = 'article'
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.message_text = message_text
|
||||
self.parse_mode = parse_mode
|
||||
self.disable_web_page_preview = disable_web_page_preview
|
||||
self.url = url
|
||||
self.hide_url = hide_url
|
||||
self.description = description
|
||||
self.thumb_url = thumb_url
|
||||
self.thumb_width = thumb_width
|
||||
self.thumb_height = thumb_height
|
||||
|
||||
def to_json(self):
|
||||
json_dict = {'type': self.type, 'id': self.id, 'title': self.title, 'message_text': self.message_text}
|
||||
if self.parse_mode:
|
||||
json_dict['parse_mode'] = self.parse_mode
|
||||
if self.disable_web_page_preview:
|
||||
json_dict['disable_web_page_preview'] = self.disable_web_page_preview
|
||||
if self.url:
|
||||
json_dict['url'] = self.url
|
||||
if self.hide_url:
|
||||
json_dict['hide_url'] = self.hide_url
|
||||
if self.description:
|
||||
json_dict['description'] = self.description
|
||||
if self.thumb_url:
|
||||
json_dict['thumb_url'] = self.thumb_url
|
||||
if self.thumb_width:
|
||||
json_dict['thumb_width'] = self.thumb_width
|
||||
if self.thumb_height:
|
||||
json_dict['thumb_height'] = self.thumb_height
|
||||
return json.dumps(json_dict)
|
||||
|
||||
|
||||
class InlineQueryResultPhoto(JsonSerializable):
|
||||
def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=None,
|
||||
description=None, caption=None, message_text=None, parse_mode=None, disable_web_page_preview=None):
|
||||
"""
|
||||
Represents a link to a photo.
|
||||
:param id: Unique identifier for this result, 1-64 bytes
|
||||
:param photo_url: A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB
|
||||
:param thumb_url: URL of the thumbnail for the photo
|
||||
:param photo_width: Width of the photo.
|
||||
:param photo_height: Height of the photo.
|
||||
:param title: Title for the result.
|
||||
:param description: Short description of the result.
|
||||
:param caption: Caption of the photo to be sent, 0-200 characters.
|
||||
:param message_text: Text of a message to be sent instead of the photo, 1-512 characters.
|
||||
:param parse_mode: Send “Markdown”, if you want Telegram apps to show bold, italic and inline URLs in your bot's message.
|
||||
:param disable_web_page_preview: Disables link previews for links in the sent message.
|
||||
:return:
|
||||
"""
|
||||
self.type = 'photo'
|
||||
self.id = id
|
||||
self.photo_url = photo_url
|
||||
self.photo_width = photo_width
|
||||
self.photo_height = photo_height
|
||||
self.thumb_url = thumb_url
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.caption = caption
|
||||
self.message_text = message_text
|
||||
self.parse_mode = parse_mode
|
||||
self.disable_web_page_preview = disable_web_page_preview
|
||||
|
||||
def to_json(self):
|
||||
json_dict = {'type': self.type, 'id': self.id, 'photo_url': self.photo_url, 'thumb_url': self.thumb_url}
|
||||
if self.photo_width:
|
||||
json_dict['photo_width'] = self.photo_width
|
||||
if self.photo_height:
|
||||
json_dict['photo_height'] = self.photo_height
|
||||
if self.title:
|
||||
json_dict['title'] = self.title
|
||||
if self.description:
|
||||
json_dict['description'] = self.description
|
||||
if self.caption:
|
||||
json_dict['caption'] = self.caption
|
||||
if self.message_text:
|
||||
json_dict['message_text'] = self.message_text
|
||||
if self.parse_mode:
|
||||
json_dict['parse_mode'] = self.parse_mode
|
||||
if self.disable_web_page_preview:
|
||||
json_dict['disable_web_page_preview'] = self.disable_web_page_preview
|
||||
return json.dumps(json_dict)
|
||||
|
||||
|
||||
class InlineQueryResultGif(JsonSerializable):
|
||||
def __init__(self, id, gif_url, thumb_url, gif_width=None, gif_height=None, title=None, caption=None,
|
||||
message_text=None, parse_mode=None, disable_web_page_preview=None):
|
||||
"""
|
||||
Represents a link to an animated GIF file.
|
||||
:param id: Unique identifier for this result, 1-64 bytes.
|
||||
:param gif_url: A valid URL for the GIF file. File size must not exceed 1MB
|
||||
:param thumb_url: URL of the static thumbnail (jpeg or gif) for the result.
|
||||
:param gif_width: Width of the GIF.
|
||||
:param gif_height: Height of the GIF.
|
||||
:param title: Title for the result.
|
||||
:param caption: Caption of the GIF file to be sent, 0-200 characters
|
||||
:param message_text: Text of a message to be sent instead of the animation, 1-512 characters.
|
||||
:param parse_mode: Send “Markdown”, if you want Telegram apps to show bold, italic and inline URLs in your bot's message.
|
||||
:param disable_web_page_preview: Disables link previews for links in the sent message.
|
||||
:return:
|
||||
"""
|
||||
self.type = 'gif'
|
||||
self.id = id
|
||||
self.gif_url = gif_url
|
||||
self.gif_width = gif_width
|
||||
self.gif_height = gif_height
|
||||
self.thumb_url = thumb_url
|
||||
self.title = title
|
||||
self.caption = caption
|
||||
self.message_text = message_text
|
||||
self.parse_mode = parse_mode
|
||||
self.disable_web_page_preview = disable_web_page_preview
|
||||
|
||||
def to_json(self):
|
||||
json_dict = {'type': self.type, 'id': self.id, 'gif_url': self.gif_url, 'thumb_url': self.thumb_url}
|
||||
if self.gif_height:
|
||||
json_dict['gif_height'] = self.gif_height
|
||||
if self.gif_width:
|
||||
json_dict['gif_width'] = self.gif_width
|
||||
if self.title:
|
||||
json_dict['title'] = self.title
|
||||
if self.caption:
|
||||
json_dict['caption'] = self.caption
|
||||
if self.message_text:
|
||||
json_dict['message_text'] = self.message_text
|
||||
if self.parse_mode:
|
||||
json_dict['parse_mode'] = self.parse_mode
|
||||
if self.disable_web_page_preview:
|
||||
json_dict['disable_web_page_preview'] = self.disable_web_page_preview
|
||||
return json.dumps(json_dict)
|
||||
|
||||
|
||||
class InlineQueryResultMpeg4Gif(JsonSerializable):
|
||||
def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None, title=None, caption=None,
|
||||
message_text=None, parse_mode=None, disable_web_page_preview=None):
|
||||
"""
|
||||
Represents a link to a video animation (H.264/MPEG-4 AVC video without sound).
|
||||
:param id: Unique identifier for this result, 1-64 bytes
|
||||
:param mpeg4_url: A valid URL for the MP4 file. File size must not exceed 1MB
|
||||
:param thumb_url: URL of the static thumbnail (jpeg or gif) for the result
|
||||
:param mpeg4_width: Video width
|
||||
:param mpeg4_height: Video height
|
||||
:param title: Title for the result
|
||||
:param caption: Caption of the MPEG-4 file to be sent, 0-200 characters
|
||||
:param message_text: Text of a message to be sent instead of the animation, 1-512 characters
|
||||
:param parse_mode: end “Markdown”, if you want Telegram apps to show bold, italic and inline URLs in your bot's message.
|
||||
:param disable_web_page_preview: Disables link previews for links in the sent message
|
||||
:return:
|
||||
"""
|
||||
self.type = 'mpeg4_gif'
|
||||
self.id = id
|
||||
self.mpeg4_url = mpeg4_url
|
||||
self.mpeg4_width = mpeg4_width
|
||||
self.mpeg4_height = mpeg4_height
|
||||
self.thumb_url = thumb_url
|
||||
self.title = title
|
||||
self.caption = caption
|
||||
self.message_text = message_text
|
||||
self.parse_mode = parse_mode
|
||||
self.disable_web_page_preview = disable_web_page_preview
|
||||
|
||||
def to_json(self):
|
||||
json_dict = {'type': self.type, 'id': self.id, 'mpeg4_url': self.mpeg4_url, 'thumb_url': self.thumb_url}
|
||||
if self.mpeg4_width:
|
||||
json_dict['mpeg4_width'] = self.mpeg4_width
|
||||
if self.mpeg4_height:
|
||||
json_dict['mpeg4_height'] = self.mpeg4_height
|
||||
if self.title:
|
||||
json_dict['title'] = self.title
|
||||
if self.caption:
|
||||
json_dict['caption'] = self.caption
|
||||
if self.message_text:
|
||||
json_dict['message_text'] = self.message_text
|
||||
if self.parse_mode:
|
||||
json_dict['parse_mode'] = self.parse_mode
|
||||
if self.disable_web_page_preview:
|
||||
json_dict['disable_web_page_preview'] = self.disable_web_page_preview
|
||||
return json.dumps(json_dict)
|
||||
|
||||
|
||||
class InlineQueryResultVideo(JsonSerializable):
|
||||
def __init__(self, id, video_url, mime_type, message_text, thumb_url, title, parse_mode=None,
|
||||
disable_web_page_preview=None,
|
||||
video_width=None, video_height=None, video_duration=None,
|
||||
description=None):
|
||||
"""
|
||||
Represents link to a page containing an embedded video player or a video file.
|
||||
:param id: Unique identifier for this result, 1-64 bytes
|
||||
:param video_url: A valid URL for the embedded video player or video file
|
||||
:param mime_type: Mime type of the content of video url, “text/html” or “video/mp4”
|
||||
:param message_text: Text of the message to be sent with the video, 1-512 characters
|
||||
:param thumb_url: URL of the thumbnail (jpeg only) for the video
|
||||
:param title: Title for the result
|
||||
:param parse_mode: Send “Markdown”, if you want Telegram apps to show bold, italic and inline URLs in your bot's message.
|
||||
:param disable_web_page_preview: Disables link previews for links in the sent message
|
||||
:param video_width: Video width
|
||||
:param video_height: Video height
|
||||
:param video_duration: Video duration in seconds
|
||||
:param description: Short description of the result
|
||||
:return:
|
||||
"""
|
||||
self.type = 'video'
|
||||
self.id = id
|
||||
self.video_url = video_url
|
||||
self.mime_type = mime_type
|
||||
self.message_text = message_text
|
||||
self.parse_mode = parse_mode
|
||||
self.disable_web_page_preview = disable_web_page_preview
|
||||
self.video_width = video_width
|
||||
self.video_height = video_height
|
||||
self.video_duration = video_duration
|
||||
self.thumb_url = thumb_url
|
||||
self.title = title
|
||||
self.description = description
|
||||
|
||||
def to_json(self):
|
||||
json_dict = {'type': self.type, 'id': self.id, 'video_url': self.video_url, 'mime_type': self.mime_type,
|
||||
'message_text': self.message_text, 'thumb_url': self.thumb_url, 'title': self.title}
|
||||
if self.parse_mode:
|
||||
json_dict['parse_mode'] = self.parse_mode
|
||||
if self.disable_web_page_preview:
|
||||
json_dict['disable_web_page_preview'] = self.disable_web_page_preview
|
||||
if self.video_width:
|
||||
json_dict['video_width'] = self.video_width
|
||||
if self.video_height:
|
||||
json_dict['video_height'] = self.video_height
|
||||
if self.video_duration:
|
||||
json_dict['video_duration'] = self.video_duration
|
||||
if self.description:
|
||||
json_dict['description'] = self.description
|
||||
return json.dumps(json_dict)
|
||||
|
226
telebot/util.py
Normal file
226
telebot/util.py
Normal file
@ -0,0 +1,226 @@
|
||||
# -*- 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 WorkerThread(threading.Thread):
|
||||
count = 0
|
||||
|
||||
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=.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
|
||||
|
||||
|
||||
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
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
self.done = False
|
||||
self.thread = threading.Thread(target=self._run)
|
||||
self.thread.start()
|
||||
|
||||
def _run(self):
|
||||
try:
|
||||
self.result = self.target(*self.args, **self.kwargs)
|
||||
except:
|
||||
self.result = sys.exc_info()
|
||||
self.done = True
|
||||
|
||||
def wait(self):
|
||||
if not self.done:
|
||||
self.thread.join()
|
||||
if isinstance(self.result, BaseException):
|
||||
six.reraise(self.result[0], self.result[1], self.result[2])
|
||||
else:
|
||||
return self.result
|
||||
|
||||
|
||||
def async():
|
||||
def decorator(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
return AsyncTask(fn, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def is_string(var):
|
||||
return isinstance(var, string_types)
|
||||
|
||||
def is_command(text):
|
||||
"""
|
||||
Checks if `text` is a command. Telegram chat commands start with the '/' character.
|
||||
:param text: Text to check.
|
||||
:return: True if `text` is a command, else False.
|
||||
"""
|
||||
return text.startswith('/')
|
||||
|
||||
|
||||
def extract_command(text):
|
||||
"""
|
||||
Extracts the command from `text` (minus the '/') if `text` is a command (see is_command).
|
||||
If `text` is not a command, this function returns None.
|
||||
|
||||
Examples:
|
||||
extract_command('/help'): 'help'
|
||||
extract_command('/help@BotName'): 'help'
|
||||
extract_command('/search black eyed peas'): 'search'
|
||||
extract_command('Good day to you'): None
|
||||
|
||||
:param text: String to extract the command from
|
||||
:return: the command if `text` is a command (according to is_command), else None.
|
||||
"""
|
||||
return text.split()[0].split('@')[0][1:] if is_command(text) else None
|
||||
|
||||
|
||||
def split_string(text, chars_per_string):
|
||||
"""
|
||||
Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string.
|
||||
This is very useful for splitting one giant message into multiples.
|
||||
|
||||
:param text: The text to split
|
||||
:param chars_per_string: The number of characters per line the text is split into.
|
||||
: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
|
BIN
tests/test_data/record.mp3
Normal file
BIN
tests/test_data/record.mp3
Normal file
Binary file not shown.
BIN
tests/test_data/record.ogg
Normal file
BIN
tests/test_data/record.ogg
Normal file
Binary file not shown.
BIN
tests/test_data/record.wav
Normal file
BIN
tests/test_data/record.wav
Normal file
Binary file not shown.
BIN
tests/test_data/test_video.mp4
Normal file
BIN
tests/test_data/test_video.mp4
Normal file
Binary file not shown.
311
tests/test_telebot.py
Normal file
311
tests/test_telebot.py
Normal file
@ -0,0 +1,311 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
sys.path.append('../')
|
||||
|
||||
import time
|
||||
import pytest
|
||||
import os
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
from telebot import util
|
||||
|
||||
should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ
|
||||
|
||||
if not should_skip:
|
||||
TOKEN = os.environ['TOKEN']
|
||||
CHAT_ID = os.environ['CHAT_ID']
|
||||
|
||||
|
||||
@pytest.mark.skipif(should_skip, reason="No environment variables configured")
|
||||
class TestTeleBot:
|
||||
def test_message_listener(self):
|
||||
msg_list = []
|
||||
for x in range(100):
|
||||
msg_list.append(self.create_text_message('Message ' + str(x)))
|
||||
|
||||
def listener(messages):
|
||||
assert len(messages) == 100
|
||||
|
||||
tb = telebot.TeleBot('')
|
||||
tb.set_update_listener(listener)
|
||||
|
||||
def test_message_handler(self):
|
||||
tb = telebot.TeleBot('')
|
||||
msg = self.create_text_message('/help')
|
||||
|
||||
@tb.message_handler(commands=['help', 'start'])
|
||||
def command_handler(message):
|
||||
message.text = 'got'
|
||||
|
||||
tb.process_new_messages([msg])
|
||||
time.sleep(1)
|
||||
assert msg.text == 'got'
|
||||
|
||||
def test_message_handler_reg(self):
|
||||
bot = telebot.TeleBot('')
|
||||
msg = self.create_text_message(r'https://web.telegram.org/')
|
||||
|
||||
@bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
|
||||
def command_url(message):
|
||||
msg.text = 'got'
|
||||
|
||||
bot.process_new_messages([msg])
|
||||
time.sleep(1)
|
||||
assert msg.text == 'got'
|
||||
|
||||
def test_message_handler_lambda(self):
|
||||
bot = telebot.TeleBot('')
|
||||
msg = self.create_text_message(r'lambda_text')
|
||||
|
||||
@bot.message_handler(func=lambda message: r'lambda' in message.text)
|
||||
def command_url(message):
|
||||
msg.text = 'got'
|
||||
|
||||
bot.process_new_messages([msg])
|
||||
time.sleep(1)
|
||||
assert msg.text == 'got'
|
||||
|
||||
def test_message_handler_lambda_fail(self):
|
||||
bot = telebot.TeleBot('')
|
||||
msg = self.create_text_message(r'text')
|
||||
|
||||
@bot.message_handler(func=lambda message: r'lambda' in message.text)
|
||||
def command_url(message):
|
||||
msg.text = 'got'
|
||||
|
||||
bot.process_new_messages([msg])
|
||||
time.sleep(1)
|
||||
assert not msg.text == 'got'
|
||||
|
||||
def test_message_handler_reg_fail(self):
|
||||
bot = telebot.TeleBot('')
|
||||
msg = self.create_text_message(r'web.telegram.org/')
|
||||
|
||||
@bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
|
||||
def command_url(message):
|
||||
msg.text = 'got'
|
||||
|
||||
bot.process_new_messages([msg])
|
||||
time.sleep(1)
|
||||
assert not msg.text == 'got'
|
||||
|
||||
def test_send_message_with_markdown(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markdown = """
|
||||
*bold text*
|
||||
_italic text_
|
||||
[text](URL)
|
||||
"""
|
||||
ret_msg = tb.send_message(CHAT_ID, markdown, parse_mode="Markdown")
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_message_with_disable_notification(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markdown = """
|
||||
*bold text*
|
||||
_italic text_
|
||||
[text](URL)
|
||||
"""
|
||||
ret_msg = tb.send_message(CHAT_ID, markdown, parse_mode="Markdown", disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_file(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_document(CHAT_ID, file_data)
|
||||
assert ret_msg.message_id
|
||||
|
||||
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_file_dis_noti(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_document(CHAT_ID, file_data, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_video(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_video_dis_noti(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_video_more_params(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data, 1)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_video_more_params_dis_noti(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data, 1, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_file_exception(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
try:
|
||||
tb.send_document(CHAT_ID, None)
|
||||
assert False
|
||||
except Exception as e:
|
||||
print(e)
|
||||
assert True
|
||||
|
||||
def test_send_photo(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_photo(CHAT_ID, file_data)
|
||||
assert ret_msg.message_id
|
||||
|
||||
ret_msg = tb.send_photo(CHAT_ID, ret_msg.photo[0].file_id)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_photo_dis_noti(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_photo(CHAT_ID, file_data)
|
||||
assert ret_msg.message_id
|
||||
|
||||
ret_msg = tb.send_photo(CHAT_ID, ret_msg.photo[0].file_id, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_audio(self):
|
||||
file_data = open('./test_data/record.mp3', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram')
|
||||
assert ret_msg.content_type == 'audio'
|
||||
assert ret_msg.audio.performer == 'eternnoir'
|
||||
assert ret_msg.audio.title == 'pyTelegram'
|
||||
|
||||
def test_send_audio_dis_noti(self):
|
||||
file_data = open('./test_data/record.mp3', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram', disable_notification=True)
|
||||
assert ret_msg.content_type == 'audio'
|
||||
assert ret_msg.audio.performer == 'eternnoir'
|
||||
assert ret_msg.audio.title == 'pyTelegram'
|
||||
|
||||
def test_send_voice(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data)
|
||||
assert ret_msg.voice.mime_type == 'audio/ogg'
|
||||
|
||||
def test_send_voice_dis_noti(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data, disable_notification=True)
|
||||
assert ret_msg.voice.mime_type == 'audio/ogg'
|
||||
|
||||
def test_get_file(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data)
|
||||
file_id = ret_msg.voice.file_id
|
||||
file_info = tb.get_file(file_id)
|
||||
assert file_info.file_id == file_id
|
||||
|
||||
def test_get_file_dis_noti(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data, disable_notification=True)
|
||||
file_id = ret_msg.voice.file_id
|
||||
file_info = tb.get_file(file_id)
|
||||
assert file_info.file_id == file_id
|
||||
|
||||
def test_send_message(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_message(CHAT_ID, text)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_message_dis_noti(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_forward_message(self):
|
||||
text = 'CI forward_message Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_message(CHAT_ID, text)
|
||||
ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id)
|
||||
assert ret_msg.forward_from
|
||||
|
||||
def test_forward_message_dis_noti(self):
|
||||
text = 'CI forward_message Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_message(CHAT_ID, text)
|
||||
ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id, disable_notification=True)
|
||||
assert ret_msg.forward_from
|
||||
|
||||
def test_reply_to(self):
|
||||
text = 'CI reply_to Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_message(CHAT_ID, text)
|
||||
ret_msg = tb.reply_to(msg, text + ' REPLY')
|
||||
assert ret_msg.reply_to_message.message_id == msg.message_id
|
||||
|
||||
def test_register_for_reply(self):
|
||||
text = 'CI reply_to Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_message(CHAT_ID, text, reply_markup=types.ForceReply())
|
||||
reply_msg = tb.reply_to(msg, text + ' REPLY')
|
||||
|
||||
def process_reply(message):
|
||||
assert msg.message_id == message.reply_to_message.message_id
|
||||
|
||||
tb.register_for_reply(msg, process_reply)
|
||||
|
||||
tb.process_new_messages([reply_msg])
|
||||
|
||||
def test_send_location(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
lat = 26.3875591
|
||||
lon = -161.2901042
|
||||
ret_msg = tb.send_location(CHAT_ID, lat, lon)
|
||||
assert int(ret_msg.location.longitude) == int(lon)
|
||||
assert int(ret_msg.location.latitude) == int(lat)
|
||||
|
||||
def test_send_location_dis_noti(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
lat = 26.3875591
|
||||
lon = -161.2901042
|
||||
ret_msg = tb.send_location(CHAT_ID, lat, lon, disable_notification=True)
|
||||
assert int(ret_msg.location.longitude) == int(lon)
|
||||
assert int(ret_msg.location.latitude) == int(lat)
|
||||
|
||||
def test_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')
|
||||
return types.Message(1, None, None, chat, 'text', params)
|
||||
|
||||
def test_is_string_unicode(self):
|
||||
s1 = u'string'
|
||||
assert util.is_string(s1)
|
||||
|
||||
def test_is_string_string(self):
|
||||
s1 = 'string'
|
||||
assert util.is_string(s1)
|
||||
|
||||
def test_not_string(self):
|
||||
i1 = 10
|
||||
assert not util.is_string(i1)
|
@ -12,17 +12,17 @@ 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
|
||||
assert msg.fromUser.username == 'GG'
|
||||
assert msg.from_user.username == 'GG'
|
||||
|
||||
|
||||
def test_json_GroupChat():
|
||||
@ -39,36 +39,46 @@ def test_json_Document():
|
||||
|
||||
|
||||
def test_json_Message_Audio():
|
||||
json_string = r'{"message_id":100,"from":{"id":10734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":10734,"first_name":"dd","last_name":"dd","username":"dd"},"date":1435481343,"audio":{"duration":3,"mime_type":"audio\/ogg","file_id":"ddg","file_size":8249}}'
|
||||
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 == 3
|
||||
assert msg.audio.duration == 1
|
||||
assert msg.content_type == 'audio'
|
||||
assert msg.audio.performer == 'eternnoir'
|
||||
assert msg.audio.title == 'pyTelegram'
|
||||
|
||||
|
||||
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
|
||||
assert msg.content_type == '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","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.sticker.height == 368
|
||||
assert msg.sticker.thumb == None
|
||||
assert msg.content_type == 'sticker'
|
||||
|
||||
|
||||
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
|
||||
@ -77,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'
|
||||
@ -95,3 +105,25 @@ def test_json_contact():
|
||||
contact = types.Contact.de_json(json_string)
|
||||
assert contact.first_name == 'dd'
|
||||
assert contact.last_name == 'ddl'
|
||||
|
||||
|
||||
def test_json_voice():
|
||||
json_string = r'{"duration": 0,"mime_type": "audio/ogg","file_id": "AwcccccccDH1JaB7w_gyFjYQxVAg","file_size": 10481}'
|
||||
voice = types.Voice.de_json(json_string)
|
||||
assert voice.duration == 0
|
||||
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","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