mirror of
https://github.com/eternnoir/pyTelegramBotAPI.git
synced 2023-08-10 21:12:57 +03:00
Compare commits
252 Commits
Author | SHA1 | Date | |
---|---|---|---|
4dce9340b0 | |||
bf79e8341e | |||
dadfdc2f13 | |||
585f627e1f | |||
eead303d47 | |||
14cc15c711 | |||
bf8736e17e | |||
5014ca2459 | |||
f337abe06e | |||
ff35f25211 | |||
2e4280a947 | |||
4a6b5b3d28 | |||
a28af3903d | |||
d1d5b9effb | |||
062fababf2 | |||
946afcc3c1 | |||
6e502cd1c6 | |||
0a7f897f05 | |||
b35f17124f | |||
44b44ac2c5 | |||
39e875c1ea | |||
be7317cc86 | |||
e1c33a1de6 | |||
8149551a15 | |||
ab648ef3db | |||
e721910c0c | |||
9287eced49 | |||
967b94b14f | |||
2df6f00ba5 | |||
beb4f8df44 | |||
92ac5a4166 | |||
716323e56a | |||
cd92d95f91 | |||
9c86ed623d | |||
c6ff9b07df | |||
38cc96d0f3 | |||
82518d8664 | |||
aba2a9e179 | |||
c5c4d081ea | |||
f854163626 | |||
fc31a2d466 | |||
86a0a8cd68 | |||
31c3b3b28e | |||
b95ab104e3 | |||
a54e4c22a8 | |||
7913e25be2 | |||
63cbda8890 | |||
38851bce22 | |||
74835c40ea | |||
97e99b4910 | |||
4ced4d29f5 | |||
239a90de14 | |||
c86af0496b | |||
4071ab9124 | |||
5c715dabc3 | |||
43d2d8583e | |||
cf78234e3a | |||
5f4cd09490 | |||
8534804c0c | |||
cf75e76e5c | |||
7d5e9e5111 | |||
1ceec3cb54 | |||
5f8c75816e | |||
4e37662ab3 | |||
a97a917522 | |||
88f91518c7 | |||
e89acc8ba6 | |||
5d611ea7f3 | |||
5c80f11261 | |||
f2202b44fe | |||
2da48c0adc | |||
389407e3ee | |||
14be2b8c18 | |||
df7808264f | |||
9d37503442 | |||
dd6f39c3cd | |||
8e4d70b9c6 | |||
87fb30d57b | |||
8f3371dcd5 | |||
ec8975c9e3 | |||
16edfbb9dc | |||
f70b135359 | |||
78fb69ded1 | |||
0f7eb1571e | |||
ac54b7abd4 | |||
0f3a6393fc | |||
de6f339cdf | |||
d0969bd5f3 | |||
4035a38507 | |||
944b077c65 | |||
644c6b9082 | |||
dc9f8db556 | |||
07ebdeab25 | |||
e8738cce7d | |||
d9e638a7df | |||
08b1dd31c8 | |||
b4f0a6d546 | |||
4eb28df1ab | |||
50e5e96bb1 | |||
bd3a9bc350 | |||
06923c8274 | |||
3efc2cf869 | |||
f5de0eeacf | |||
6e871b8eb1 | |||
f6359bc32c | |||
2bc052ad5a | |||
022ef6a64c | |||
3232811543 | |||
fabcd93dd7 | |||
8053183cb5 | |||
b2b7d90888 | |||
3e9d73c25d | |||
d6501ddc0e | |||
e818e3875d | |||
56cd3971dc | |||
958ca34e9c | |||
f4ef2366b6 | |||
f553960096 | |||
24ef64456b | |||
3e7da0fd18 | |||
2c0f42b363 | |||
1e4a6e2125 | |||
beeb60aab8 | |||
5b942a5b31 | |||
3e4a6cd702 | |||
0e369953cb | |||
911e356930 | |||
554b39a49a | |||
ea16f35432 | |||
81d94687be | |||
4ba4bc18cf | |||
c117ff2d50 | |||
735c224444 | |||
81adfd335e | |||
7ebe589b46 | |||
9c1b19a9e4 | |||
02b886465e | |||
2d89ceb745 | |||
ae8c3252df | |||
7914f71938 | |||
097ba9fec2 | |||
d09d9f0c09 | |||
29c98b0230 | |||
2b1db1f1b3 | |||
fa80b1dba0 | |||
b45db584df | |||
f52ea635e5 | |||
9b56afd569 | |||
6fb10e92e4 | |||
fcf4d91564 | |||
38319871e6 | |||
2d0b092ea4 | |||
060b8c61bb | |||
db2accc2f8 | |||
798fda4c8a | |||
2578e48134 | |||
ac20216a7a | |||
beb5a456eb | |||
41faadd572 | |||
a15016d7d9 | |||
47dd84c441 | |||
c7b360e982 | |||
09041b018f | |||
3a4cf47def | |||
56e4f68a83 | |||
484e7fccbd | |||
791d65e95a | |||
073d7fb6a7 | |||
a6668397e1 | |||
983d626d87 | |||
a4e73a05c6 | |||
30e304ffb5 | |||
430b34c7a2 | |||
b222416fd8 | |||
f8110cd046 | |||
6bc60f4aa9 | |||
b48a445e9f | |||
0b383498eb | |||
2e3b4223a5 | |||
60bb63ab2b | |||
0aa7a8a8f6 | |||
72ed7c1dde | |||
a29c4af2ee | |||
8d8f234138 | |||
491cc05a95 | |||
b2c6077f4d | |||
fb290dc12d | |||
c088fabe6c | |||
a791ff4e46 | |||
e56f134a7c | |||
38c4c21030 | |||
3e33b7f1cb | |||
e381671645 | |||
ce991e9ac3 | |||
3d5415433e | |||
0bfefdf15d | |||
506464e637 | |||
4554cb969f | |||
65cf841015 | |||
0f0ce934dc | |||
bffbe764e5 | |||
c00595e212 | |||
b20f5b359b | |||
558eef78b4 | |||
3f46ce3b7b | |||
69e8edef19 | |||
d3369245c4 | |||
55e9f2095e | |||
7118613ef7 | |||
105d65d5ce | |||
f11bf08ba1 | |||
66598e39fe | |||
4146b50384 | |||
f62d642572 | |||
18f1fd42b0 | |||
07d198aebe | |||
0370a9f277 | |||
22d3ac027a | |||
795f7fff7f | |||
ab6d40a072 | |||
d26923e167 | |||
05aff236c1 | |||
a9ae070256 | |||
63fe6e01d1 | |||
bbafdd1c1d | |||
fe9df2df8c | |||
b0b8623dce | |||
a01e59951a | |||
d5c202abbd | |||
81299ff613 | |||
25bac68309 | |||
a05324bdad | |||
74c4ab2f04 | |||
ab05cb0045 | |||
709eb8cf45 | |||
643cdeceee | |||
2add34c702 | |||
877397a46b | |||
afbc67795a | |||
da5dc20b3a | |||
ed5e5e5077 | |||
9a6ddce8df | |||
db8478d0a4 | |||
20030f47af | |||
f7cf1965cb | |||
aea067f789 | |||
e2c20c1e55 | |||
1209281787 | |||
742f67c85b | |||
e22fcbe3c0 | |||
d3998dfadb | |||
ff54f194ad |
35
.github/workflows/setup_python.yml
vendored
Normal file
35
.github/workflows/setup_python.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Setup
|
||||
|
||||
# Controls when the action will run.
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the master branch
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
#workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
all-setups:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [ '3.6','3.7','3.8','3.9', 'pypy-3.6', 'pypy-3.7' ] #'pypy-3.8', 'pypy-3.9' NOT SUPPORTED NOW
|
||||
name: ${{ matrix.python-version }} and tests
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: x64
|
||||
- run: |
|
||||
pip3 install -r requirements.txt
|
||||
python setup.py install
|
||||
cd tests && py.test
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -62,3 +62,4 @@ testMain.py
|
||||
|
||||
#VS Code
|
||||
.vscode/
|
||||
.DS_Store
|
||||
|
340
README.md
340
README.md
@ -1,12 +1,17 @@
|
||||
# <p align="center">pyTelegramBotAPI
|
||||
|
||||
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.
|
||||
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
[](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
|
||||
[](https://pypi.org/project/pyTelegramBotAPI/)
|
||||
|
||||
# <p align="center">pyTelegramBotAPI
|
||||
|
||||
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.
|
||||
|
||||
## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#june-25-2021">5.3</a>!
|
||||
|
||||
## Contents
|
||||
|
||||
* [Getting started.](#getting-started)
|
||||
* [Writing your first bot](#writing-your-first-bot)
|
||||
* [Prerequisites](#prerequisites)
|
||||
@ -16,12 +21,27 @@
|
||||
* [Methods](#methods)
|
||||
* [General use of the API](#general-use-of-the-api)
|
||||
* [Message handlers](#message-handlers)
|
||||
* [Edited Message handler](#edited-message-handler)
|
||||
* [Channel Post handler](#channel-post-handler)
|
||||
* [Edited Channel Post handler](#edited-channel-post-handler)
|
||||
* [Callback Query handlers](#callback-query-handler)
|
||||
* [Middleware handlers](#middleware-handler)
|
||||
* [Shipping Query Handler](#shipping-query-handler)
|
||||
* [Pre Checkout Query Handler](#pre-checkout-query-handler)
|
||||
* [Poll Handler](#poll-handler)
|
||||
* [Poll Answer Handler](#poll-answer-handler)
|
||||
* [My Chat Member Handler](#my-chat-member-handler)
|
||||
* [Chat Member Handler](#chat-member-handler)
|
||||
* [Inline Mode](#inline-mode)
|
||||
* [Inline handler](#inline-handler)
|
||||
* [Chosen Inline handler](#chosen-inline-handler)
|
||||
* [Answer Inline Query](#answer-inline-query)
|
||||
* [Additional API features](#additional-api-features)
|
||||
* [Middleware handlers](#middleware-handlers)
|
||||
* [Custom filters](#custom-filters)
|
||||
* [TeleBot](#telebot)
|
||||
* [Reply markup](#reply-markup)
|
||||
* [Inline Mode](#inline-mode)
|
||||
* [Advanced use of the API](#advanced-use-of-the-api)
|
||||
* [Using local Bot API Server](#using-local-bot-api-sever)
|
||||
* [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)
|
||||
@ -30,9 +50,7 @@
|
||||
* [Logging](#logging)
|
||||
* [Proxy](#proxy)
|
||||
* [API conformance](#api-conformance)
|
||||
* [Change log](#change-log)
|
||||
* [F.A.Q.](#faq)
|
||||
* [Bot 2.0](#bot-20)
|
||||
* [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)
|
||||
* [How can I handle reocurring ConnectionResetErrors?](#how-can-i-handle-reocurring-connectionreseterrors)
|
||||
* [The Telegram Chat Group](#the-telegram-chat-group)
|
||||
@ -41,7 +59,7 @@
|
||||
|
||||
## Getting started.
|
||||
|
||||
This API is tested with Python Python 3.6-3.9 and Pypy 3.
|
||||
This API is tested with Python 3.6-3.9 and Pypy 3.
|
||||
There are two ways to install the library:
|
||||
|
||||
* Installation using pip (a Python package manager)*:
|
||||
@ -103,13 +121,13 @@ This one echoes all incoming text messages back to the sender. It uses a lambda
|
||||
|
||||
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()
|
||||
bot.infinity_polling()
|
||||
```
|
||||
Alright, that's it! Our source file now looks like this:
|
||||
```python
|
||||
import telebot
|
||||
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
bot = telebot.TeleBot("YOUR_BOT_TOKEN")
|
||||
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def send_welcome(message):
|
||||
@ -119,7 +137,7 @@ def send_welcome(message):
|
||||
def echo_all(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_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.
|
||||
|
||||
@ -146,7 +164,7 @@ 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):
|
||||
Each filter must 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):
|
||||
@ -162,8 +180,9 @@ TeleBot supports the following filters:
|
||||
|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.|
|
||||
|chat_types|list of chat types|`True` if `message.chat.type` in your filter
|
||||
|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
|
||||
@ -207,28 +226,96 @@ def send_something(message):
|
||||
```
|
||||
**Important: all handlers are tested in the order in which they were declared**
|
||||
|
||||
#### Edited Message handlers
|
||||
#### Edited Message handler
|
||||
Handle edited messages
|
||||
`@bot.edited_message_handler(filters) # <- passes a Message type object to your function`
|
||||
|
||||
@bot.edited_message_handler(filters)
|
||||
#### Channel Post handler
|
||||
Handle channel post messages
|
||||
`@bot.channel_post_handler(filters) # <- passes a Message type object to your function`
|
||||
|
||||
#### channel_post_handler
|
||||
|
||||
@bot.channel_post_handler(filters)
|
||||
|
||||
#### edited_channel_post_handler
|
||||
|
||||
@bot.edited_channel_post_handler(filters)
|
||||
#### Edited Channel Post handler
|
||||
Handle edited channel post messages
|
||||
`@bot.edited_channel_post_handler(filters) # <- passes a Message type object to your function`
|
||||
|
||||
#### Callback Query Handler
|
||||
|
||||
In bot2.0 update. You can get `callback_query` in update object. In telebot use `callback_query_handler` to process callback queries.
|
||||
|
||||
Handle callback queries
|
||||
```python
|
||||
@bot.callback_query_handler(func=lambda call: True)
|
||||
def test_callback(call):
|
||||
def test_callback(call): # <- passes a CallbackQuery type object to your function
|
||||
logger.info(call)
|
||||
```
|
||||
#### Middleware Handler
|
||||
|
||||
#### Shipping Query Handler
|
||||
Handle shipping queries
|
||||
`@bot.shipping_query_handeler() # <- passes a ShippingQuery type object to your function`
|
||||
|
||||
#### Pre Checkout Query Handler
|
||||
Handle pre checkoupt queries
|
||||
`@bot.pre_checkout_query_handler() # <- passes a PreCheckoutQuery type object to your function`
|
||||
|
||||
#### Poll Handler
|
||||
Handle poll updates
|
||||
`@bot.poll_handler() # <- passes a Poll type object to your function`
|
||||
|
||||
#### Poll Answer Handler
|
||||
Handle poll answers
|
||||
`@bot.poll_answer_handler() # <- passes a PollAnswer type object to your function`
|
||||
|
||||
#### My Chat Member Handler
|
||||
Handle updates of a the bot's member status in a chat
|
||||
`@bot.my_chat_member_handler() # <- passes a ChatMemberUpdated type object to your function`
|
||||
|
||||
#### Chat Member Handler
|
||||
Handle updates of a chat member's status in a chat
|
||||
`@bot.chat_member_handler() # <- passes a ChatMemberUpdated type object to your function`
|
||||
*Note: "chat_member" updates are not requested by default. If you want to allow all update types, set `allowed_updates` in `bot.polling()` / `bot.infinity_polling()` to `util.update_types`*
|
||||
|
||||
### Inline Mode
|
||||
|
||||
More information about [Inline mode](https://core.telegram.org/bots/inline).
|
||||
|
||||
#### Inline handler
|
||||
|
||||
Now, you can use inline_handler to get inline queries 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', types.InputTextMessageContent('Result message.'))
|
||||
r2 = types.InlineQueryResultArticle('2', 'Result2', types.InputTextMessageContent('Result message2.'))
|
||||
bot.answer_inline_query(inline_query.id, [r, r2])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
```
|
||||
|
||||
### Additional API features
|
||||
|
||||
#### Middleware Handlers
|
||||
|
||||
A middleware handler is a function that allows you to modify requests or the bot context as they pass through the
|
||||
Telegram to the bot. You can imagine middleware as a chain of logic connection handled before any other handlers are
|
||||
@ -248,6 +335,43 @@ def start(message):
|
||||
assert message.another_text == message.text + ':changed'
|
||||
```
|
||||
There are other examples using middleware handler in the [examples/middleware](examples/middleware) directory.
|
||||
|
||||
|
||||
#### Custom filters
|
||||
Also, you can use built-in custom filters. Or, you can create your own filter.
|
||||
|
||||
[Example of custom filter](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters/general_custom_filters.py)
|
||||
|
||||
Also, we have examples on them. Check this links:
|
||||
|
||||
You can check some built-in filters in source [code](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/telebot/custom_filters.py)
|
||||
|
||||
Example of [filtering by id](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters/id_filter_example.py)
|
||||
|
||||
Example of [filtering by text](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters/text_filter_example.py)
|
||||
|
||||
If you want to add some built-in filter, you are welcome to add it in custom_filters.py file.
|
||||
|
||||
Here is example of creating filter-class:
|
||||
|
||||
```python
|
||||
class IsAdmin(telebot.custom_filters.SimpleCustomFilter):
|
||||
# Class will check whether the user is admin or creator in group or not
|
||||
key='is_admin'
|
||||
@staticmethod
|
||||
def check(message: telebot.types.Message):
|
||||
return bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator']
|
||||
|
||||
# To register filter, you need to use method add_custom_filter.
|
||||
bot.add_custom_filter(IsAdmin())
|
||||
|
||||
# Now, you can use it in handler.
|
||||
@bot.message_handler(is_admin=True)
|
||||
def admin_of_group(message):
|
||||
bot.send_message(message.chat.id, 'You are admin of this group'!)
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### TeleBot
|
||||
```python
|
||||
@ -257,11 +381,10 @@ 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
|
||||
# - interval: int (default 0) - The interval between polling requests
|
||||
# - timeout: integer (default 20) - Timeout in seconds for long polling.
|
||||
tb.polling(none_stop=False, interval=0, timeout=20)
|
||||
# - allowed_updates: List of Strings (default None) - List of update types to request
|
||||
tb.infinity_polling(interval=0, timeout=20)
|
||||
|
||||
# getMe
|
||||
user = tb.get_me()
|
||||
@ -273,6 +396,7 @@ tb.remove_webhook()
|
||||
|
||||
# getUpdates
|
||||
updates = tb.get_updates()
|
||||
# or
|
||||
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
|
||||
|
||||
# sendMessage
|
||||
@ -394,49 +518,8 @@ 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 queries 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', types.InputTextMessageContent('Result message.'))
|
||||
r2 = types.InlineQueryResultArticle('2', 'Result2', types.InputTextMessageContent('Result message2.'))
|
||||
bot.answer_inline_query(inline_query.id, [r, r2])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
```
|
||||
### Working with entities:
|
||||
### Working with entities
|
||||
This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.
|
||||
Attributes:
|
||||
* `type`
|
||||
@ -454,8 +537,20 @@ Refer [Bot Api](https://core.telegram.org/bots/api#messageentity) for extra deta
|
||||
|
||||
## Advanced use of the API
|
||||
|
||||
### Using local Bot API Sever
|
||||
Since version 5.0 of the Bot API, you have the possibility to run your own [Local Bot API Server](https://core.telegram.org/bots/api#using-a-local-bot-api-server).
|
||||
pyTelegramBotAPI also supports this feature.
|
||||
```python
|
||||
from telebot import apihelper
|
||||
|
||||
apihelper.API_URL = "http://localhost:4200/bot{0}/{1}"
|
||||
```
|
||||
**Important: Like described [here](https://core.telegram.org/bots/api#logout), you have to log out your bot from the Telegram server before switching to your local API server. in pyTelegramBotAPI use `bot.log_out()`**
|
||||
|
||||
*Note: 4200 is an example port*
|
||||
|
||||
### 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.
|
||||
There exists an implementation of TeleBot which executes all `send_xyz` and the `get_me` functions asynchronously. This can speed up your 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")
|
||||
@ -484,6 +579,19 @@ 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)
|
||||
```
|
||||
|
||||
Or you can use the new `smart_split` function to get more meaningful substrings:
|
||||
```python
|
||||
from telebot import util
|
||||
large_text = open("large_text.txt", "rb").read()
|
||||
# Splits one string into multiple strings, with a maximum amount of `chars_per_string` (max. 4096)
|
||||
# Splits by last '\n', '. ' or ' ' in exactly this priority.
|
||||
# smart_split returns a list with the splitted text.
|
||||
splitted_text = util.smart_split(large_text, chars_per_string=3000)
|
||||
for text in splitted_text:
|
||||
tb.send_message(chat_id, text)
|
||||
```
|
||||
@ -505,7 +613,7 @@ def handle_messages(messages):
|
||||
bot.reply_to(message, 'Hi')
|
||||
|
||||
bot.set_update_listener(handle_messages)
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
```
|
||||
|
||||
### Using web hooks
|
||||
@ -532,7 +640,7 @@ You can use proxy for request. `apihelper.proxy` object will use by call `reques
|
||||
```python
|
||||
from telebot import apihelper
|
||||
|
||||
apihelper.proxy = {'http':'http://10.10.1.10:3128'}
|
||||
apihelper.proxy = {'http':'http://127.0.0.1:3128'}
|
||||
```
|
||||
|
||||
If you want to use socket5 proxy you need install dependency `pip install requests[socks]` and make sure, that you have the latest version of `gunicorn`, `PySocks`, `pyTelegramBotAPI`, `requests` and `urllib3`.
|
||||
@ -544,15 +652,20 @@ apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
|
||||
|
||||
## API conformance
|
||||
|
||||
_Checking is in progress..._
|
||||
|
||||
✅ [Bot API 4.5](https://core.telegram.org/bots/api-changelog#december-31-2019) _- To be checked..._
|
||||
|
||||
* ➕ [Bot API 5.3](https://core.telegram.org/bots/api#june-25-2021) - ChatMemberXXX classes are full copies of ChatMember
|
||||
* ✔ [Bot API 5.2](https://core.telegram.org/bots/api#april-26-2021)
|
||||
* ✔ [Bot API 5.1](https://core.telegram.org/bots/api#march-9-2021)
|
||||
* ✔ [Bot API 5.0](https://core.telegram.org/bots/api-changelog#november-4-2020)
|
||||
* ✔ [Bot API 4.9](https://core.telegram.org/bots/api-changelog#june-4-2020)
|
||||
* ✔ [Bot API 4.8](https://core.telegram.org/bots/api-changelog#april-24-2020)
|
||||
* ✔ [Bot API 4.7](https://core.telegram.org/bots/api-changelog#march-30-2020)
|
||||
* ✔ [Bot API 4.6](https://core.telegram.org/bots/api-changelog#january-23-2020)
|
||||
* ➕ [Bot API 4.5](https://core.telegram.org/bots/api-changelog#december-31-2019) - No nested MessageEntities and Markdown2 support
|
||||
* ✔ [Bot API 4.4](https://core.telegram.org/bots/api-changelog#july-29-2019)
|
||||
* ✔ [Bot API 4.3](https://core.telegram.org/bots/api-changelog#may-31-2019)
|
||||
* ✔ [Bot API 4.2](https://core.telegram.org/bots/api-changelog#april-14-2019)
|
||||
* ➕ [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support.
|
||||
* ➕ [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support.
|
||||
* ➕ [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support
|
||||
* ➕ [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support
|
||||
* ✔ [Bot API 3.6](https://core.telegram.org/bots/api-changelog#february-13-2018)
|
||||
* ✔ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017)
|
||||
* ✔ [Bot API 3.4](https://core.telegram.org/bots/api-changelog#october-11-2017)
|
||||
@ -567,23 +680,8 @@ _Checking is in progress..._
|
||||
* ✔ [Bot API 2.0](https://core.telegram.org/bots/api-changelog#april-9-2016)
|
||||
|
||||
|
||||
## Change log
|
||||
|
||||
27.04.2020 - Poll and Dice are up to date.
|
||||
Python2 conformance is not checked any more due to EOL.
|
||||
|
||||
11.04.2020 - Refactoring. new_chat_member is out of support. Bugfix in html_text. Started Bot API conformance checking.
|
||||
|
||||
06.06.2019 - Added polls support (Poll). Added functions send_poll, stop_poll
|
||||
|
||||
## F.A.Q.
|
||||
|
||||
### Bot 2.0
|
||||
|
||||
April 9,2016 Telegram release new bot 2.0 API, which has a drastic revision especially for the change of method's interface.If you want to update to the latest version, please make sure you've switched bot's code to bot 2.0 method interface.
|
||||
|
||||
[More information about pyTelegramBotAPI support bot2.0](https://github.com/eternnoir/pyTelegramBotAPI/issues/130)
|
||||
|
||||
### How can I distinguish a User and a GroupChat in message.chat?
|
||||
Telegram Bot API support new type Chat for message.chat.
|
||||
|
||||
@ -613,7 +711,6 @@ Bot instances that were idle for a long time might be rejected by the server whe
|
||||
Get help. Discuss. Chat.
|
||||
|
||||
* Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/joinchat/Bn4ixj84FIZVkwhk2jag6A)
|
||||
* 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
|
||||
|
||||
@ -624,66 +721,47 @@ Get help. Discuss. Chat.
|
||||
## 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*
|
||||
* [Send to Kindle Bot](https://telegram.me/Send2KindleBot) by *GabrielRF* - Send to Kindle files or links to files.
|
||||
* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) ([source](https://github.com/GabrielRF/telegram-lmgtfy_bot)) by *GabrielRF* - Let me Google that for you.
|
||||
* [Telegram UrlProBot](https://github.com/GabrielRF/telegram-urlprobot) ([source](https://github.com/GabrielRF/telegram-urlprobot)) by *GabrielRF* - URL shortener and URL expander.
|
||||
* [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte* - `Credits for the original version of this bot goes to` **Groosha** `, simply added certain features which I thought were needed`.
|
||||
* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you.
|
||||
* [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte*
|
||||
* [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](https://github.com/neoranger)
|
||||
* [TagAlertBot](https://github.com/pitasi/TagAlertBot) by *pitasi*
|
||||
* [ColorCodeBot](https://t.me/colorcodebot) ([source](https://github.com/andydecleyre/colorcodebot)) - Share code snippets as beautifully syntax-highlighted HTML and/or images.
|
||||
* [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall.
|
||||
* [picpingbot](https://web.telegram.org/#/im?p=%40picpingbot) - Fun anonymous photo exchange by Boogie Muffin.
|
||||
* [TheZigZagProject](https://github.com/WebShark025/TheZigZagProject) - The 'All In One' bot for Telegram! by WebShark025
|
||||
* [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash
|
||||
* [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch
|
||||
* [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot) - DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch
|
||||
* [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev.
|
||||
* [dailypepebot](https://telegram.me/dailypepebot) by [*Jaime*](https://github.com/jiwidi/Dailypepe) - Get's you random pepe images and gives you their id, then you can call this image with the number.
|
||||
* [DailyQwertee](https://t.me/DailyQwertee) by [*Jaime*](https://github.com/jiwidi/DailyQwertee) - Bot that manages a channel that sends qwertee daily tshirts every day at 00:00
|
||||
* [wat-bridge](https://github.com/rmed/wat-bridge) by [*rmed*](https://github.com/rmed) - Send and receive messages to/from WhatsApp through Telegram
|
||||
* [flibusta_bot](https://github.com/Kurbezz/flibusta_bot) by [*Kurbezz*](https://github.com/Kurbezz)
|
||||
* [EmaProject](https://github.com/halkliff/emaproject) by [*halkliff*](https://github.com/halkliff) - Ema - Eastern Media Assistant was made thinking on the ease-to-use feature. Coding here is simple, as much as is fast and powerful.
|
||||
* [filmratingbot](http://t.me/filmratingbot)([source](https://github.com/jcolladosp/film-rating-bot)) by [*jcolladosp*](https://github.com/jcolladosp) - Telegram bot using the Python API that gets films rating from IMDb and metacritic
|
||||
* [you2mp3bot](http://t.me/you2mp3bot)([link](https://storebot.me/bot/you2mp3bot)) - This bot can convert a Youtube video to Mp3. All you need is send the URL video.
|
||||
* [Send2Kindlebot](http://t.me/Send2KindleBot) ([source](https://github.com/GabrielRF/Send2KindleBot)) by *GabrielRF* - Send to Kindle service.
|
||||
* [RastreioBot](http://t.me/RastreioBot) ([source](https://github.com/GabrielRF/RastreioBot)) by *GabrielRF* - Bot used to track packages on the Brazilian Mail Service.
|
||||
* [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram))
|
||||
* [Spbu4UBot](http://t.me/Spbu4UBot)([link](https://github.com/EeOneDown/spbu4u)) by *EeOneDown* - Bot with timetables for SPbU students.
|
||||
* [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students.
|
||||
* [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free.
|
||||
* [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages’ vocabulary.
|
||||
* [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audio samples and try to name the performer of the song.
|
||||
* [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon)
|
||||
* [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm).
|
||||
* [ManjaroBot](https://t.me/ManjaroBot) by [@NeoRanger](https://github.com/neoranger) - Bot for Manjaro Linux Spanish group with a lot of info for Manjaro Newbies.
|
||||
* [VigoBusTelegramBot](https://t.me/vigobusbot) ([GitHub](https://github.com/Pythoneiro/VigoBus-TelegramBot)) - Bot that provides buses coming to a certain stop and their remaining time for the city of Vigo (Galicia - Spain)
|
||||
* [kaishnik-bot](https://t.me/kaishnik_bot) ([source](https://github.com/airatk/kaishnik-bot)) by *airatk* - bot which shows all the necessary information to KNTRU-KAI students.
|
||||
* [Creation Date](https://t.me/creationdatebot) by @karipov - interpolates account creation dates based on telegram given ID’s
|
||||
* [m0xbot](https://t.me/m0xbot) by [kor0p](https://github.com/kor0p) - tic-tac-toe.
|
||||
* [kboardbot](https://t.me/kboardbot) by [kor0p](https://github.com/kor0p) - inline switches keyboard layout (English, Hebrew, Ukrainian, Russian).
|
||||
* [Robbie](https://t.me/romdeliverybot) ([source](https://github.com/FacuM/romdeliverybot_support)) by @FacuM - Support Telegram bot for developers and maintainers.
|
||||
* [AsadovBot](https://t.me/asadov_bot) ([source](https://github.com/desexcile/BotApi)) by @DesExcile - Сatalog of poems by Eduard Asadov.
|
||||
* [thesaurus_com_bot](https://t.me/thesaurus_com_bot) ([source](https://github.com/LeoSvalov/words-i-learn-bot)) by @LeoSvalov - words and synonyms from [dictionary.com](https://www.dictionary.com) and [thesaurus.com](https://www.thesaurus.com) in the telegram.
|
||||
* [InfoBot](https://t.me/info2019_bot) ([source](https://github.com/irevenko/info-bot)) by @irevenko - An all-round bot that displays some statistics (weather, time, crypto etc...)
|
||||
* [FoodBot](https://t.me/ChensonUz_bot) ([source](https://github.com/Fliego/old_restaurant_telegram_chatbot)) by @Fliego - a simple bot for food ordering
|
||||
* [Sporty](https://t.me/SportydBot) ([source](https://github.com/0xnu/sporty)) by @0xnu - Telegram bot for displaying the latest news, sports schedules and injury updates.
|
||||
* [Neural style transfer](https://t.me/ebanyivolshebnikBot) ([source](https://github.com/timbyxty/StyleTransfer-tgbot)) by @timbyxty - bot for transferring style from one picture to another based on neural network.
|
||||
* [JoinGroup Silencer Bot](https://t.me/joingroup_silencer_bot) ([source](https://github.com/zeph1997/Telegram-Group-Silencer-Bot)) by [@zeph1997](https://github.com/zeph1997) - A Telegram Bot to remove "join group" and "removed from group" notifications.
|
||||
* [AdviceBook](https://t.me/adviceokbot) by [@barbax7](https://github.com/barbax7) - A Telegram Bot that allows you to receive random reading tips when you don't know which book to read.
|
||||
* [Blue_CC_Bot](https://t.me/Blue_CC_Bot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Checks Your Given Credit Cards And Says Which Is A Real,Card And Which Is Fake.
|
||||
* [RandomInfoBot](https://t.me/RandomInfoBot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Generates Random Information Of Humans Scraped From Over 13 Websites.
|
||||
* [TasksListsBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/TasksListsBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - A (tasks) lists manager bot for Telegram.
|
||||
* [MyElizaPsychologistBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/MyElizaPsychologistBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - An implementation of the famous Eliza psychologist chatbot.
|
||||
* [Evdembot](https://t.me/Evdembot) by Adem Kavak. A bot that informs you about everything you want.
|
||||
* [Frcstbot](https://t.me/frcstbot) ([source](https://github.com/Mrsqd/frcstbot_public)) by [Mrsqd](https://github.com/Mrsqd). A Telegram bot that will always be happy to show you the weather forecast.
|
||||
* [Bot Hour](https://t.me/roadtocode_bot) a little bot that say the time in different countries by [@diegop384](https://github.com/diegop384) [repo](https://github.com/diegop384/telegrambothour)
|
||||
* [moodforfood_bot](https://t.me/moodforfood_bot) This bot will provide you with a list of food place(s) near your current Telegram location, which you are prompted to share. The API for all this info is from https://foursquare.com/. by [@sophiamarani](https://github.com/sophiamarani)
|
||||
* [Donation with Amazon](https://t.me/donamazonbot) by [@barbax7](https://github.com/barbax7) This bot donates amazon advertising commissions to the non-profit organization chosen by the user.
|
||||
* [COVID-19 Galicia Bot](https://t.me/covid19_galicia_bot) by [@dgarcoe](https://github.com/dgarcoe) This bot provides daily data related to the COVID19 crisis in Galicia (Spain) obtained from official government sources.
|
||||
* [MineGramBot](https://github.com/ModischFabrications/MineGramBot) by [ModischFabrications](https://github.com/ModischFabrications). This bot can start, stop and monitor a minecraft server.
|
||||
* [Tabletop DiceBot](https://github.com/dexpiper/tabletopdicebot) by [dexpiper](https://github.com/dexpiper). This bot can roll multiple dices for RPG-like games, add positive and negative modifiers and show short descriptions to the rolls.
|
||||
* [BarnameKon](https://t.me/BarnameKonBot) by [Anvaari](https://github.com/anvaari). This Bot make "Add to google calendar" link for your events. It give information about event and return link. It work for Jalali calendar and in Tehran Time. [Source code](https://github.com/anvaari/BarnameKon)
|
||||
* [Price Tracker](https://t.me/trackokbot) by [@barbax7](https://github.com/barbax7). This bot tracks amazon.it product's prices the user is interested to and notify him when one price go down.
|
||||
* [Torrent Hunt](https://t.me/torrenthuntbot) by [@Hemantapkh](https://github.com/hemantapkh/torrenthunt). Torrent Hunt bot is a multilingual bot with inline mode support to search and explore torrents from 1337x.to.
|
||||
* [Translator bot](https://github.com/AREEG94FAHAD/translate_text_bot) by Areeg Fahad. This bot can be used to translate texts.
|
||||
* [Digital Cryptocurrency bot](https://github.com/AREEG94FAHAD/currencies_bot) by Areeg Fahad. With this bot, you can now monitor the prices of more than 12 digital Cryptocurrency.
|
||||
* [Anti-Tracking Bot](https://t.me/AntiTrackingBot) by Leon Heess [(source)](https://github.com/leonheess/AntiTrackingBot). Send any link, and the bot tries its best to remove all tracking from the link you sent.
|
||||
* [Developer Bot](https://t.me/IndDeveloper_bot) by [Vishal Singh](https://github.com/vishal2376) [(source code)](https://github.com/vishal2376/telegram-bot) This telegram bot can do tasks like GitHub search & clone,provide c++ learning resources ,Stackoverflow search, Codeforces(profile visualizer,random problems)
|
||||
* [oneIPO bot](https://github.com/aaditya2200/IPO-proj) by [Aadithya](https://github.com/aaditya2200) & [Amol Soans](https://github.com/AmolDerickSoans) This Telegram bot provides live updates , data and documents on current and upcoming IPOs(Initial Public Offerings)
|
||||
* [CoronaGraphsBot](https://t.me/CovidGraph_bot) ([source](https://github.com/TrevorWinstral/CoronaGraphsBot)) by *TrevorWinstral* - Gets live COVID Country data, plots it, and briefs the user
|
||||
* [ETHLectureBot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded
|
||||
|
||||
**Want to have your bot listed here? Just make a pull request.**
|
||||
|
||||
|
||||
**Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.**
|
||||
|
117
examples/anonymous_bot.py
Normal file
117
examples/anonymous_bot.py
Normal file
@ -0,0 +1,117 @@
|
||||
# This bot is needed to connect two people and their subsequent anonymous communication
|
||||
#
|
||||
# Avaiable commands:
|
||||
# `/start` - Just send you a messsage how to start
|
||||
# `/find` - Find a person you can contact
|
||||
# `/stop` - Stop active conversation
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
|
||||
# Initialize bot with your token
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
|
||||
# The `users` variable is needed to contain chat ids that are either in the search or in the active dialog, like {chat_id, chat_id}
|
||||
users = {}
|
||||
# The `freeid` variable is needed to contain chat id, that want to start conversation
|
||||
# Or, in other words: chat id of user in the search
|
||||
freeid = None
|
||||
|
||||
# `/start` command handler
|
||||
#
|
||||
# That command only sends you 'Just use /find command!'
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start(message: types.Message):
|
||||
bot.send_message(message.chat.id, 'Just use /find command!')
|
||||
|
||||
# `/find` command handler
|
||||
#
|
||||
# That command finds opponent for you
|
||||
#
|
||||
# That command according to the following principle:
|
||||
# 1. You have written `/find` command
|
||||
# 2. If you are already in the search or have an active dialog, bot sends you 'Shut up!'
|
||||
# 3. If not:
|
||||
# 3.1. Bot sends you 'Finding...'
|
||||
# 3.2. If there is no user in the search:
|
||||
# 3.2.2. `freeid` updated with `your_chat_id`
|
||||
# 3.3. If there is user in the search:
|
||||
# 3.3.1. Both you and the user in the search recieve the message 'Founded!'
|
||||
# 3.3.2. `users` updated with a {user_in_the_search_chat_id, your_chat_id}
|
||||
# 3.3.3. `users` updated with a {your_chat_id, user_in_the_search_id}
|
||||
# 3.3.4. `freeid` updated with `None`
|
||||
@bot.message_handler(commands=['find'])
|
||||
def find(message: types.Message):
|
||||
global freeid
|
||||
|
||||
if message.chat.id not in users:
|
||||
bot.send_message(message.chat.id, 'Finding...')
|
||||
|
||||
if freeid == None:
|
||||
freeid = message.chat.id
|
||||
else:
|
||||
# Question:
|
||||
# Is there any way to simplify this like `bot.send_message([message.chat.id, freeid], 'Founded!')`?
|
||||
bot.send_message(message.chat.id, 'Founded!')
|
||||
bot.send_message(freeid, 'Founded!')
|
||||
|
||||
users[freeid] = message.chat.id
|
||||
users[message.chat.id] = freeid
|
||||
freeid = None
|
||||
|
||||
print(users, freeid) # Debug purpose, you can remove that line
|
||||
else:
|
||||
bot.send_message(message.chat.id, 'Shut up!')
|
||||
|
||||
# `/stop` command handler
|
||||
#
|
||||
# That command stops your current conversation (if it exist)
|
||||
#
|
||||
# That command according to the following principle:
|
||||
# 1. You have written `/stop` command
|
||||
# 2. If you are not have active dialog or you are not in search, bot sends you 'You are not in search!'
|
||||
# 3. If you are in active dialog:
|
||||
# 3.1. Bot sends you 'Stopping...'
|
||||
# 3.2. Bot sends 'Your opponent is leavin`...' to your opponent
|
||||
# 3.3. {your_opponent_chat_id, your_chat_id} removes from `users`
|
||||
# 3.4. {your_chat_id, your_opponent_chat_id} removes from `users`
|
||||
# 4. If you are only in search:
|
||||
# 4.1. Bot sends you 'Stopping...'
|
||||
# 4.2. `freeid` updated with `None`
|
||||
@bot.message_handler(commands=['stop'])
|
||||
def stop(message: types.Message):
|
||||
global freeid
|
||||
|
||||
if message.chat.id in users:
|
||||
bot.send_message(message.chat.id, 'Stopping...')
|
||||
bot.send_message(users[message.chat.id], 'Your opponent is leavin`...')
|
||||
|
||||
del users[users[message.chat.id]]
|
||||
del users[message.chat.id]
|
||||
|
||||
print(users, freeid) # Debug purpose, you can remove that line
|
||||
elif message.chat.id == freeid:
|
||||
bot.send_message(message.chat.id, 'Stopping...')
|
||||
freeid = None
|
||||
|
||||
print(users, freeid) # Debug purpose, you can remove that line
|
||||
else:
|
||||
bot.send_message(message.chat.id, 'You are not in search!')
|
||||
|
||||
# message handler for conversation
|
||||
#
|
||||
# That handler needed to send message from one opponent to another
|
||||
# If you are not in `users`, you will recieve a message 'No one can hear you...'
|
||||
# Otherwise all your messages are sent to your opponent
|
||||
#
|
||||
# Questions:
|
||||
# 1. Is there any way to improve readability like `content_types=['all']`?
|
||||
# 2. Is there any way to register this message handler only when i found the opponent?
|
||||
@bot.message_handler(content_types=['animation', 'audio', 'contact', 'dice', 'document', 'location', 'photo', 'poll', 'sticker', 'text', 'venue', 'video', 'video_note', 'voice'])
|
||||
def chatting(message: types.Message):
|
||||
if message.chat.id in users:
|
||||
bot.copy_message(users[message.chat.id], users[users[message.chat.id]], message.id)
|
||||
else:
|
||||
bot.send_message(message.chat.id, 'No one can hear you...')
|
||||
|
||||
bot.infinity_polling(skip_pending=True)
|
33
examples/chat_member_example.py
Normal file
33
examples/chat_member_example.py
Normal file
@ -0,0 +1,33 @@
|
||||
import telebot
|
||||
from telebot import types,util
|
||||
|
||||
bot = telebot.TeleBot("token")
|
||||
|
||||
#chat_member_handler. When status changes, telegram gives update. check status from old_chat_member and new_chat_member.
|
||||
@bot.chat_member_handler()
|
||||
def chat_m(message: types.ChatMemberUpdated):
|
||||
old = message.old_chat_member
|
||||
new = message.new_chat_member
|
||||
if new.status == "member":
|
||||
bot.send_message(message.chat.id,"Hello {name}!".format(name=new.user.first_name)) # Welcome message
|
||||
|
||||
#if bot is added to group, this handler will work
|
||||
@bot.my_chat_member_handler()
|
||||
def my_chat_m(message: types.ChatMemberUpdated):
|
||||
old = message.old_chat_member
|
||||
new = message.new_chat_member
|
||||
if new.status == "member":
|
||||
bot.send_message(message.chat.id,"Somebody added me to group") # Welcome message, if bot was added to group
|
||||
bot.leave_chat(message.chat.id)
|
||||
|
||||
#content_Type_service is:
|
||||
#'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created',
|
||||
#'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message',
|
||||
#'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended',
|
||||
#'voice_chat_participants_invited', 'message_auto_delete_timer_changed'
|
||||
# this handler deletes service messages
|
||||
|
||||
@bot.message_handler(content_types=util.content_type_service)
|
||||
def delall(message: types.Message):
|
||||
bot.delete_message(message.chat.id,message.message_id)
|
||||
bot.infinity_polling(allowed_updates=util.update_types)
|
12
examples/custom_filters/admin_filter_example.py
Normal file
12
examples/custom_filters/admin_filter_example.py
Normal file
@ -0,0 +1,12 @@
|
||||
import telebot
|
||||
from telebot import custom_filters
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
# Handler
|
||||
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
|
||||
def answer_for_admin(message):
|
||||
bot.send_message(message.chat.id,"hello my admin")
|
||||
|
||||
# Register filter
|
||||
bot.add_custom_filter(custom_filters.IsAdminFilter(bot))
|
||||
bot.infinity_polling()
|
42
examples/custom_filters/general_custom_filters.py
Normal file
42
examples/custom_filters/general_custom_filters.py
Normal file
@ -0,0 +1,42 @@
|
||||
import telebot
|
||||
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
|
||||
# AdvancedCustomFilter is for list, string filter values
|
||||
class MainFilter(telebot.custom_filters.AdvancedCustomFilter):
|
||||
key='text'
|
||||
@staticmethod
|
||||
def check(message, text):
|
||||
return message.text in text
|
||||
|
||||
# SimpleCustomFilter is for boolean values, such as is_admin=True
|
||||
class IsAdmin(telebot.custom_filters.SimpleCustomFilter):
|
||||
key='is_admin'
|
||||
@staticmethod
|
||||
def check(message: telebot.types.Message):
|
||||
return bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator']
|
||||
|
||||
|
||||
@bot.message_handler(is_admin=True, commands=['admin']) # Check if user is admin
|
||||
def admin_rep(message):
|
||||
bot.send_message(message.chat.id, "Hi admin")
|
||||
|
||||
@bot.message_handler(is_admin=False, commands=['admin']) # If user is not admin
|
||||
def not_admin(message):
|
||||
bot.send_message(message.chat.id, "You are not admin")
|
||||
|
||||
@bot.message_handler(text=['hi']) # Response to hi message
|
||||
def welcome_hi(message):
|
||||
bot.send_message(message.chat.id, 'You said hi')
|
||||
|
||||
@bot.message_handler(text=['bye']) # Response to bye message
|
||||
def bye_user(message):
|
||||
bot.send_message(message.chat.id, 'You said bye')
|
||||
|
||||
|
||||
# Do not forget to register filters
|
||||
bot.add_custom_filter(MainFilter())
|
||||
bot.add_custom_filter(IsAdmin())
|
||||
|
||||
bot.infinity_polling(skip_pending=True) # Skip old updates
|
19
examples/custom_filters/id_filter_example.py
Normal file
19
examples/custom_filters/id_filter_example.py
Normal file
@ -0,0 +1,19 @@
|
||||
import telebot
|
||||
from telebot import custom_filters
|
||||
|
||||
bot = telebot.TeleBot('token')
|
||||
|
||||
|
||||
# Chat id can be private or supergroups.
|
||||
@bot.message_handler(chat_id=[12345678], commands=['admin']) # chat_id checks id corresponds to your list or not.
|
||||
def admin_rep(message):
|
||||
bot.send_message(message.chat.id, "You are allowed to use this command.")
|
||||
|
||||
@bot.message_handler(commands=['admin'])
|
||||
def not_admin(message):
|
||||
bot.send_message(message.chat.id, "You are not allowed to use this command")
|
||||
|
||||
# Do not forget to register
|
||||
bot.add_custom_filter(custom_filters.ChatFilter())
|
||||
|
||||
bot.infinity_polling()
|
21
examples/custom_filters/is_filter_example.py
Normal file
21
examples/custom_filters/is_filter_example.py
Normal file
@ -0,0 +1,21 @@
|
||||
import telebot
|
||||
from telebot import custom_filters
|
||||
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
|
||||
# Check if message is a reply
|
||||
@bot.message_handler(is_reply=True)
|
||||
def start_filter(message):
|
||||
bot.send_message(message.chat.id, "Looks like you replied to my message.")
|
||||
|
||||
# Check if message was forwarded
|
||||
@bot.message_handler(is_forwarded=True)
|
||||
def text_filter(message):
|
||||
bot.send_message(message.chat.id, "I do not accept forwarded messages!")
|
||||
|
||||
# Do not forget to register filters
|
||||
bot.add_custom_filter(custom_filters.IsReplyFilter())
|
||||
bot.add_custom_filter(custom_filters.ForwardFilter())
|
||||
|
||||
bot.infinity_polling()
|
21
examples/custom_filters/text_filter_example.py
Normal file
21
examples/custom_filters/text_filter_example.py
Normal file
@ -0,0 +1,21 @@
|
||||
import telebot
|
||||
from telebot import custom_filters
|
||||
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
|
||||
# Check if message starts with @admin tag
|
||||
@bot.message_handler(text_startswith="@admin")
|
||||
def start_filter(message):
|
||||
bot.send_message(message.chat.id, "Looks like you are calling admin, wait...")
|
||||
|
||||
# Check if text is hi or hello
|
||||
@bot.message_handler(text=['hi','hello'])
|
||||
def text_filter(message):
|
||||
bot.send_message(message.chat.id, "Hi, {name}!".format(name=message.from_user.first_name))
|
||||
|
||||
# Do not forget to register filters
|
||||
bot.add_custom_filter(custom_filters.TextMatchFilter())
|
||||
bot.add_custom_filter(custom_filters.TextStartsFilter())
|
||||
|
||||
bot.infinity_polling()
|
64
examples/custom_states.py
Normal file
64
examples/custom_states.py
Normal file
@ -0,0 +1,64 @@
|
||||
import telebot
|
||||
|
||||
from telebot import custom_filters
|
||||
|
||||
bot = telebot.TeleBot("")
|
||||
|
||||
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start_ex(message):
|
||||
"""
|
||||
Start command. Here we are starting state
|
||||
"""
|
||||
bot.set_state(message.chat.id, 1)
|
||||
bot.send_message(message.chat.id, 'Hi, write me a name')
|
||||
|
||||
|
||||
|
||||
@bot.message_handler(state="*", commands='cancel')
|
||||
def any_state(message):
|
||||
"""
|
||||
Cancel state
|
||||
"""
|
||||
bot.send_message(message.chat.id, "Your state was cancelled.")
|
||||
bot.delete_state(message.chat.id)
|
||||
|
||||
@bot.message_handler(state=1)
|
||||
def name_get(message):
|
||||
"""
|
||||
State 1. Will process when user's state is 1.
|
||||
"""
|
||||
bot.send_message(message.chat.id, f'Now write me a surname')
|
||||
bot.set_state(message.chat.id, 2)
|
||||
with bot.retrieve_data(message.chat.id) as data:
|
||||
data['name'] = message.text
|
||||
|
||||
|
||||
@bot.message_handler(state=2)
|
||||
def ask_age(message):
|
||||
"""
|
||||
State 2. Will process when user's state is 2.
|
||||
"""
|
||||
bot.send_message(message.chat.id, "What is your age?")
|
||||
bot.set_state(message.chat.id, 3)
|
||||
with bot.retrieve_data(message.chat.id) as data:
|
||||
data['surname'] = message.text
|
||||
|
||||
# result
|
||||
@bot.message_handler(state=3, is_digit=True)
|
||||
def ready_for_answer(message):
|
||||
with bot.retrieve_data(message.chat.id) as data:
|
||||
bot.send_message(message.chat.id, "Ready, take a look:\n<b>Name: {name}\nSurname: {surname}\nAge: {age}</b>".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html")
|
||||
bot.delete_state(message.chat.id)
|
||||
|
||||
#incorrect number
|
||||
@bot.message_handler(state=3, is_digit=False)
|
||||
def age_incorrect(message):
|
||||
bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number')
|
||||
|
||||
# register filters
|
||||
|
||||
bot.add_custom_filter(custom_filters.StateFilter(bot))
|
||||
bot.add_custom_filter(custom_filters.IsDigitFilter())
|
||||
bot.infinity_polling(skip_pending=True)
|
@ -74,4 +74,4 @@ def send_welcome(message):
|
||||
bot.reply_to(message, reply)
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -130,4 +130,4 @@ def command_default(m):
|
||||
bot.send_message(m.chat.id, "I don't understand \"" + m.text + "\"\nMaybe try the help page at /help")
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -25,4 +25,4 @@ def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -61,7 +61,7 @@ def default_query(inline_query):
|
||||
|
||||
|
||||
def main_loop():
|
||||
bot.polling(True)
|
||||
bot.infinity_polling()
|
||||
while 1:
|
||||
time.sleep(3)
|
||||
|
||||
|
@ -24,4 +24,4 @@ def callback_query(call):
|
||||
def message_handler(message):
|
||||
bot.send_message(message.chat.id, "Yes/no?", reply_markup=gen_markup())
|
||||
|
||||
bot.polling(none_stop=True)
|
||||
bot.infinity_polling()
|
||||
|
@ -50,4 +50,4 @@ def start(message):
|
||||
bot.send_message(message.chat.id, _('hello'))
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -58,4 +58,4 @@ def start(message):
|
||||
bot.send_message(message.chat.id, bot.session['state'])
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -78,5 +78,4 @@ def got_payment(message):
|
||||
parse_mode='Markdown')
|
||||
|
||||
|
||||
bot.skip_pending = True
|
||||
bot.polling(none_stop=True, interval=0)
|
||||
bot.infinity_polling(skip_pending = True)
|
||||
|
21
examples/register_handler.py
Normal file
21
examples/register_handler.py
Normal file
@ -0,0 +1,21 @@
|
||||
import telebot
|
||||
|
||||
api_token = 'token'
|
||||
|
||||
bot = telebot.TeleBot(api_token)
|
||||
|
||||
def start_executor(message):
|
||||
bot.send_message(message.chat.id, 'Hello!')
|
||||
|
||||
bot.register_message_handler(start_executor, commands=['start']) # Start command executor
|
||||
|
||||
# See also
|
||||
# bot.register_callback_query_handler(*args, **kwargs)
|
||||
# bot.register_channel_post_handler(*args, **kwargs)
|
||||
# bot.register_chat_member_handler(*args, **kwargs)
|
||||
# bot.register_inline_handler(*args, **kwargs)
|
||||
# bot.register_my_chat_member_handler(*args, **kwargs)
|
||||
# bot.register_edited_message_handler(*args, **kwargs)
|
||||
# And other functions..
|
||||
|
||||
bot.infinity_polling()
|
13
examples/skip_updates_example.py
Normal file
13
examples/skip_updates_example.py
Normal file
@ -0,0 +1,13 @@
|
||||
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.infinity_polling(skip_pending=True)# Skip pending skips old updates
|
@ -83,4 +83,4 @@ bot.enable_save_next_step_handlers(delay=2)
|
||||
# WARNING It will work only if enable_save_next_step_handlers was called!
|
||||
bot.load_next_step_handlers()
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -81,4 +81,4 @@ def listener(messages):
|
||||
|
||||
|
||||
bot.set_update_listener(listener)
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -1,4 +1,3 @@
|
||||
py==1.10.0
|
||||
pytest==3.0.2
|
||||
requests==2.20.0
|
||||
wheel==0.24.0
|
||||
|
1
setup.py
1
setup.py
@ -25,6 +25,7 @@ setup(name='pyTelegramBotAPI',
|
||||
install_requires=['requests'],
|
||||
extras_require={
|
||||
'json': 'ujson',
|
||||
'PIL': 'Pillow',
|
||||
'redis': 'redis>=3.4.1'
|
||||
},
|
||||
classifiers=[
|
||||
|
1634
telebot/__init__.py
1634
telebot/__init__.py
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,7 @@ import requests
|
||||
from requests.exceptions import HTTPError, ConnectionError, Timeout
|
||||
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
from requests.packages.urllib3 import fields
|
||||
format_header_param = fields.format_header_param
|
||||
except ImportError:
|
||||
@ -27,9 +28,12 @@ session = None
|
||||
API_URL = None
|
||||
FILE_URL = None
|
||||
|
||||
CONNECT_TIMEOUT = 3.5
|
||||
READ_TIMEOUT = 9999
|
||||
SESSION_TIME_TO_LIVE = None # In seconds. None - live forever, 0 - one-time
|
||||
CONNECT_TIMEOUT = 15
|
||||
READ_TIMEOUT = 30
|
||||
|
||||
LONG_POLLING_TIMEOUT = 10 # Should be positive, short polling should be used for testing purposes only (https://core.telegram.org/bots/api#getupdates)
|
||||
|
||||
SESSION_TIME_TO_LIVE = 600 # In seconds. None - live forever, 0 - one-time
|
||||
|
||||
RETRY_ON_ERROR = False
|
||||
RETRY_TIMEOUT = 2
|
||||
@ -44,6 +48,7 @@ def _get_req_session(reset=False):
|
||||
if SESSION_TIME_TO_LIVE:
|
||||
# If session TTL is set - check time passed
|
||||
creation_date = util.per_thread('req_session_time', lambda: datetime.now(), reset)
|
||||
# noinspection PyTypeChecker
|
||||
if (datetime.now() - creation_date).total_seconds() > SESSION_TIME_TO_LIVE:
|
||||
# Force session reset
|
||||
reset = True
|
||||
@ -68,7 +73,10 @@ def _make_request(token, method_name, method='get', params=None, files=None):
|
||||
:param files: Optional files.
|
||||
:return: The result parsed to a JSON dictionary.
|
||||
"""
|
||||
if not token:
|
||||
raise Exception('Bot token is not defined')
|
||||
if API_URL:
|
||||
# noinspection PyUnresolvedReferences
|
||||
request_url = API_URL.format(token, method_name)
|
||||
else:
|
||||
request_url = "https://api.telegram.org/bot{0}/{1}".format(token, method_name)
|
||||
@ -80,16 +88,21 @@ def _make_request(token, method_name, method='get', params=None, files=None):
|
||||
fields.format_header_param = _no_encode(format_header_param)
|
||||
if params:
|
||||
if 'timeout' in params:
|
||||
read_timeout = params.pop('timeout') + 10
|
||||
if 'connect-timeout' in params:
|
||||
connect_timeout = params.pop('connect-timeout') + 10
|
||||
read_timeout = params.pop('timeout')
|
||||
connect_timeout = read_timeout
|
||||
# if 'connect-timeout' in params:
|
||||
# connect_timeout = params.pop('connect-timeout') + 10
|
||||
if 'long_polling_timeout' in params:
|
||||
# For getUpdates
|
||||
# The only function with timeout on the BOT API side
|
||||
params['timeout'] = params.pop('long_polling_timeout')
|
||||
# Long polling hangs for given time. Read timeout should be greater that long_polling_timeout
|
||||
read_timeout = max(params['timeout'] + 10, read_timeout)
|
||||
# For getUpdates: it's the only function with timeout parameter on the BOT API side
|
||||
long_polling_timeout = params.pop('long_polling_timeout')
|
||||
params['timeout'] = long_polling_timeout
|
||||
# Long polling hangs for a given time. Read timeout should be greater that long_polling_timeout
|
||||
read_timeout = max(long_polling_timeout + 5, read_timeout)
|
||||
# Lets stop suppose that user is stupid and assume that he knows what he do...
|
||||
# read_timeout = read_timeout + 10
|
||||
# connect_timeout = connect_timeout + 10
|
||||
|
||||
params = params or None # Set params to None if empty
|
||||
|
||||
result = None
|
||||
if RETRY_ON_ERROR:
|
||||
@ -166,6 +179,16 @@ def get_me(token):
|
||||
return _make_request(token, method_url)
|
||||
|
||||
|
||||
def log_out(token):
|
||||
method_url = r'logOut'
|
||||
return _make_request(token, method_url)
|
||||
|
||||
|
||||
def close(token):
|
||||
method_url = r'close'
|
||||
return _make_request(token, method_url)
|
||||
|
||||
|
||||
def get_file(token, file_id):
|
||||
method_url = r'getFile'
|
||||
return _make_request(token, method_url, params={'file_id': file_id})
|
||||
@ -175,13 +198,15 @@ def get_file_url(token, file_id):
|
||||
if FILE_URL is None:
|
||||
return "https://api.telegram.org/file/bot{0}/{1}".format(token, get_file(token, file_id)['file_path'])
|
||||
else:
|
||||
# noinspection PyUnresolvedReferences
|
||||
return FILE_URL.format(token, get_file(token, file_id)['file_path'])
|
||||
|
||||
|
||||
|
||||
def download_file(token, file_path):
|
||||
if FILE_URL is None:
|
||||
url = "https://api.telegram.org/file/bot{0}/{1}".format(token, file_path)
|
||||
else:
|
||||
# noinspection PyUnresolvedReferences
|
||||
url = FILE_URL.format(token, file_path)
|
||||
|
||||
result = _get_req_session().get(url, proxies=proxy)
|
||||
@ -194,7 +219,8 @@ def download_file(token, file_path):
|
||||
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, timeout=None):
|
||||
parse_mode=None, disable_notification=None, timeout=None,
|
||||
entities=None, allow_sending_without_reply=None):
|
||||
"""
|
||||
Use this method to send text messages. On success, the sent Message is returned.
|
||||
:param token:
|
||||
@ -206,6 +232,8 @@ def send_message(
|
||||
:param parse_mode:
|
||||
:param disable_notification:
|
||||
:param timeout:
|
||||
:param entities:
|
||||
:param allow_sending_without_reply:
|
||||
:return:
|
||||
"""
|
||||
method_url = r'sendMessage'
|
||||
@ -221,7 +249,11 @@ def send_message(
|
||||
if disable_notification is not None:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if entities:
|
||||
payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities))
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
@ -243,7 +275,7 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed
|
||||
if drop_pending_updates is not None: # Any bool value should pass
|
||||
payload['drop_pending_updates'] = drop_pending_updates
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload, files=files)
|
||||
|
||||
|
||||
@ -253,7 +285,7 @@ def delete_webhook(token, drop_pending_updates=None, timeout=None):
|
||||
if drop_pending_updates is not None: # Any bool value should pass
|
||||
payload['drop_pending_updates'] = drop_pending_updates
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
@ -261,7 +293,7 @@ def get_webhook_info(token, timeout=None):
|
||||
method_url = r'getWebhookInfo'
|
||||
payload = {}
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
@ -273,9 +305,8 @@ def get_updates(token, offset=None, limit=None, timeout=None, allowed_updates=No
|
||||
if limit:
|
||||
payload['limit'] = limit
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
if long_polling_timeout:
|
||||
payload['long_polling_timeout'] = long_polling_timeout
|
||||
payload['timeout'] = timeout
|
||||
payload['long_polling_timeout'] = long_polling_timeout if long_polling_timeout else LONG_POLLING_TIMEOUT
|
||||
if allowed_updates is not None: # Empty lists should pass
|
||||
payload['allowed_updates'] = json.dumps(allowed_updates)
|
||||
return _make_request(token, method_url, params=payload)
|
||||
@ -309,12 +340,24 @@ def get_chat_administrators(token, chat_id):
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def get_chat_members_count(token, chat_id):
|
||||
method_url = r'getChatMembersCount'
|
||||
def get_chat_member_count(token, chat_id):
|
||||
method_url = r'getChatMemberCount'
|
||||
payload = {'chat_id': chat_id}
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def set_sticker_set_thumb(token, name, user_id, thumb):
|
||||
method_url = r'setStickerSetThumb'
|
||||
payload = {'name': name, 'user_id': user_id}
|
||||
files = {}
|
||||
if thumb:
|
||||
if not isinstance(thumb, str):
|
||||
files['thumb'] = thumb
|
||||
else:
|
||||
payload['thumb'] = thumb
|
||||
return _make_request(token, method_url, params=payload, files=files or None)
|
||||
|
||||
|
||||
def set_chat_sticker_set(token, chat_id, sticker_set_name):
|
||||
method_url = r'setChatStickerSet'
|
||||
payload = {'chat_id': chat_id, 'sticker_set_name': sticker_set_name}
|
||||
@ -341,7 +384,7 @@ def forward_message(
|
||||
if disable_notification is not None:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
@ -365,14 +408,14 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def send_dice(
|
||||
token, chat_id,
|
||||
emoji=None, disable_notification=None, reply_to_message_id=None,
|
||||
reply_markup=None, timeout=None):
|
||||
reply_markup=None, timeout=None, allow_sending_without_reply=None):
|
||||
method_url = r'sendDice'
|
||||
payload = {'chat_id': chat_id}
|
||||
if emoji:
|
||||
@ -384,14 +427,17 @@ def send_dice(
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
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,
|
||||
parse_mode=None, disable_notification=None, timeout=None):
|
||||
parse_mode=None, disable_notification=None, timeout=None,
|
||||
caption_entities=None, allow_sending_without_reply=None):
|
||||
method_url = r'sendPhoto'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -412,14 +458,18 @@ def send_photo(
|
||||
if disable_notification is not None:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if caption_entities:
|
||||
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_media_group(
|
||||
token, chat_id, media,
|
||||
disable_notification=None, reply_to_message_id=None,
|
||||
timeout=None):
|
||||
timeout=None, allow_sending_without_reply=None):
|
||||
method_url = r'sendMediaGroup'
|
||||
media_json, files = convert_input_media_array(media)
|
||||
payload = {'chat_id': chat_id, 'media': media_json}
|
||||
@ -428,7 +478,9 @@ def send_media_group(
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
return _make_request(
|
||||
token, method_url, params=payload,
|
||||
method='post' if files else 'get',
|
||||
@ -437,37 +489,55 @@ def send_media_group(
|
||||
|
||||
def send_location(
|
||||
token, chat_id, latitude, longitude,
|
||||
live_period=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None):
|
||||
live_period=None, reply_to_message_id=None,
|
||||
reply_markup=None, disable_notification=None,
|
||||
timeout=None, horizontal_accuracy=None, heading=None,
|
||||
proximity_alert_radius=None, allow_sending_without_reply=None):
|
||||
method_url = r'sendLocation'
|
||||
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude}
|
||||
if live_period:
|
||||
payload['live_period'] = live_period
|
||||
if horizontal_accuracy:
|
||||
payload['horizontal_accuracy'] = horizontal_accuracy
|
||||
if heading:
|
||||
payload['heading'] = heading
|
||||
if proximity_alert_radius:
|
||||
payload['proximity_alert_radius'] = proximity_alert_radius
|
||||
if reply_to_message_id:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if disable_notification is not None:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def edit_message_live_location(token, latitude, longitude, chat_id=None, message_id=None,
|
||||
inline_message_id=None, reply_markup=None, timeout=None):
|
||||
def edit_message_live_location(
|
||||
token, latitude, longitude, chat_id=None, message_id=None,
|
||||
inline_message_id=None, reply_markup=None, timeout=None,
|
||||
horizontal_accuracy=None, heading=None, proximity_alert_radius=None):
|
||||
method_url = r'editMessageLiveLocation'
|
||||
payload = {'latitude': latitude, 'longitude': longitude}
|
||||
if chat_id:
|
||||
payload['chat_id'] = chat_id
|
||||
if message_id:
|
||||
payload['message_id'] = message_id
|
||||
if horizontal_accuracy:
|
||||
payload['horizontal_accuracy'] = horizontal_accuracy
|
||||
if heading:
|
||||
payload['heading'] = heading
|
||||
if proximity_alert_radius:
|
||||
payload['proximity_alert_radius'] = proximity_alert_radius
|
||||
if inline_message_id:
|
||||
payload['inline_message_id'] = inline_message_id
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
@ -485,14 +555,16 @@ def stop_message_live_location(
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def send_venue(
|
||||
token, chat_id, latitude, longitude, title, address,
|
||||
foursquare_id=None, foursquare_type=None, disable_notification=None,
|
||||
reply_to_message_id=None, reply_markup=None, timeout=None):
|
||||
reply_to_message_id=None, reply_markup=None, timeout=None,
|
||||
allow_sending_without_reply=None, google_place_id=None,
|
||||
google_place_type=None):
|
||||
method_url = r'sendVenue'
|
||||
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address}
|
||||
if foursquare_id:
|
||||
@ -506,13 +578,20 @@ def send_venue(
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
if google_place_id:
|
||||
payload['google_place_id'] = google_place_id
|
||||
if google_place_type:
|
||||
payload['google_place_type'] = google_place_type
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def send_contact(
|
||||
token, chat_id, phone_number, first_name, last_name=None, vcard=None,
|
||||
disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None):
|
||||
disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None,
|
||||
allow_sending_without_reply=None):
|
||||
method_url = r'sendContact'
|
||||
payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name}
|
||||
if last_name:
|
||||
@ -526,7 +605,9 @@ def send_contact(
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
@ -534,12 +615,13 @@ def send_chat_action(token, chat_id, action, timeout=None):
|
||||
method_url = r'sendChatAction'
|
||||
payload = {'chat_id': chat_id, 'action': action}
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None, width=None, height=None):
|
||||
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None,
|
||||
thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None):
|
||||
method_url = r'sendVideo'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -562,7 +644,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
|
||||
if disable_notification is not None:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if thumb:
|
||||
if not util.is_string(thumb):
|
||||
if files:
|
||||
@ -575,11 +657,17 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
|
||||
payload['width'] = width
|
||||
if height:
|
||||
payload['height'] = height
|
||||
if caption_entities:
|
||||
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
parse_mode=None, disable_notification=None, timeout=None, thumb=None):
|
||||
def send_animation(
|
||||
token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
|
||||
parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None,
|
||||
allow_sending_without_reply=None):
|
||||
method_url = r'sendAnimation'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -600,7 +688,7 @@ def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_m
|
||||
if disable_notification is not None:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if thumb:
|
||||
if not util.is_string(thumb):
|
||||
if files:
|
||||
@ -609,11 +697,16 @@ def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_m
|
||||
files = {'thumb': thumb}
|
||||
else:
|
||||
payload['thumb'] = thumb
|
||||
if caption_entities:
|
||||
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None,
|
||||
parse_mode=None, disable_notification=None, timeout=None):
|
||||
parse_mode=None, disable_notification=None, timeout=None, caption_entities=None,
|
||||
allow_sending_without_reply=None):
|
||||
method_url = r'sendVoice'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -634,12 +727,16 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess
|
||||
if disable_notification is not None:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if caption_entities:
|
||||
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None,
|
||||
disable_notification=None, timeout=None, thumb=None):
|
||||
disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None):
|
||||
method_url = r'sendVideoNote'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -660,7 +757,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m
|
||||
if disable_notification is not None:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if thumb:
|
||||
if not util.is_string(thumb):
|
||||
if files:
|
||||
@ -669,11 +766,14 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m
|
||||
files = {'thumb': thumb}
|
||||
else:
|
||||
payload['thumb'] = thumb
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None,
|
||||
reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None):
|
||||
reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None,
|
||||
caption_entities=None, allow_sending_without_reply=None):
|
||||
method_url = r'sendAudio'
|
||||
payload = {'chat_id': chat_id}
|
||||
files = None
|
||||
@ -698,7 +798,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non
|
||||
if disable_notification is not None:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if thumb:
|
||||
if not util.is_string(thumb):
|
||||
if files:
|
||||
@ -707,16 +807,24 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non
|
||||
files = {'thumb': thumb}
|
||||
else:
|
||||
payload['thumb'] = thumb
|
||||
if caption_entities:
|
||||
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
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, parse_mode=None,
|
||||
disable_notification=None, timeout=None, caption=None, thumb=None):
|
||||
disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None,
|
||||
allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=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}
|
||||
file_data = data
|
||||
if visible_file_name:
|
||||
file_data = (visible_file_name, data)
|
||||
files = {data_type: file_data}
|
||||
else:
|
||||
payload[data_type] = data
|
||||
if reply_to_message_id:
|
||||
@ -728,7 +836,7 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m
|
||||
if disable_notification is not None:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if caption:
|
||||
payload['caption'] = caption
|
||||
if thumb:
|
||||
@ -739,6 +847,12 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m
|
||||
files = {'thumb': thumb}
|
||||
else:
|
||||
payload['thumb'] = thumb
|
||||
if caption_entities:
|
||||
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
if method_url == 'sendDocument' and disable_content_type_detection is not None:
|
||||
payload['disable_content_type_detection'] = disable_content_type_detection
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
@ -749,13 +863,15 @@ def get_method_by_type(data_type):
|
||||
return r'sendSticker'
|
||||
|
||||
|
||||
def kick_chat_member(token, chat_id, user_id, until_date=None):
|
||||
method_url = 'kickChatMember'
|
||||
def ban_chat_member(token, chat_id, user_id, until_date=None, revoke_messages=None):
|
||||
method_url = 'banChatMember'
|
||||
payload = {'chat_id': chat_id, 'user_id': user_id}
|
||||
if isinstance(until_date, datetime):
|
||||
payload['until_date'] = until_date.timestamp()
|
||||
else:
|
||||
payload['until_date'] = until_date
|
||||
if revoke_messages is not None:
|
||||
payload['revoke_messages'] = revoke_messages
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
@ -804,7 +920,8 @@ def restrict_chat_member(
|
||||
def promote_chat_member(
|
||||
token, chat_id, user_id, can_change_info=None, can_post_messages=None,
|
||||
can_edit_messages=None, can_delete_messages=None, can_invite_users=None,
|
||||
can_restrict_members=None, can_pin_messages=None, can_promote_members=None):
|
||||
can_restrict_members=None, can_pin_messages=None, can_promote_members=None,
|
||||
is_anonymous=None, can_manage_chat=None, can_manage_voice_chats=None):
|
||||
method_url = 'promoteChatMember'
|
||||
payload = {'chat_id': chat_id, 'user_id': user_id}
|
||||
if can_change_info is not None:
|
||||
@ -823,6 +940,12 @@ def promote_chat_member(
|
||||
payload['can_pin_messages'] = can_pin_messages
|
||||
if can_promote_members is not None:
|
||||
payload['can_promote_members'] = can_promote_members
|
||||
if is_anonymous is not None:
|
||||
payload['is_anonymous'] = is_anonymous
|
||||
if can_manage_chat is not None:
|
||||
payload['can_manage_chat'] = can_manage_chat
|
||||
if can_manage_voice_chats is not None:
|
||||
payload['can_manage_voice_chats'] = can_manage_voice_chats
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
@ -843,6 +966,51 @@ def set_chat_permissions(token, chat_id, permissions):
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def create_chat_invite_link(token, chat_id, expire_date, member_limit):
|
||||
method_url = 'createChatInviteLink'
|
||||
payload = {
|
||||
'chat_id': chat_id
|
||||
}
|
||||
|
||||
if expire_date is not None:
|
||||
if isinstance(expire_date, datetime):
|
||||
payload['expire_date'] = expire_date.timestamp()
|
||||
else:
|
||||
payload['expire_date'] = expire_date
|
||||
if member_limit:
|
||||
payload['member_limit'] = member_limit
|
||||
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def edit_chat_invite_link(token, chat_id, invite_link, expire_date, member_limit):
|
||||
method_url = 'editChatInviteLink'
|
||||
payload = {
|
||||
'chat_id': chat_id,
|
||||
'invite_link': invite_link
|
||||
}
|
||||
|
||||
if expire_date is not None:
|
||||
if isinstance(expire_date, datetime):
|
||||
payload['expire_date'] = expire_date.timestamp()
|
||||
else:
|
||||
payload['expire_date'] = expire_date
|
||||
|
||||
if member_limit is not None:
|
||||
payload['member_limit'] = member_limit
|
||||
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def revoke_chat_invite_link(token, chat_id, invite_link):
|
||||
method_url = 'revokeChatInviteLink'
|
||||
payload = {
|
||||
'chat_id': chat_id,
|
||||
'invite_link': invite_link
|
||||
}
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def export_chat_invite_link(token, chat_id):
|
||||
method_url = 'exportChatInviteLink'
|
||||
payload = {'chat_id': chat_id}
|
||||
@ -874,9 +1042,33 @@ def set_chat_title(token, chat_id, title):
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def set_my_commands(token, commands):
|
||||
def get_my_commands(token, scope=None, language_code=None):
|
||||
method_url = r'getMyCommands'
|
||||
payload = {}
|
||||
if scope:
|
||||
payload['scope'] = scope.to_json()
|
||||
if language_code:
|
||||
payload['language_code'] = language_code
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
def set_my_commands(token, commands, scope=None, language_code=None):
|
||||
method_url = r'setMyCommands'
|
||||
payload = {'commands': _convert_list_json_serializable(commands)}
|
||||
if scope:
|
||||
payload['scope'] = scope.to_json()
|
||||
if language_code:
|
||||
payload['language_code'] = language_code
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
def delete_my_commands(token, scope=None, language_code=None):
|
||||
method_url = r'deleteMyCommands'
|
||||
payload = {}
|
||||
if scope:
|
||||
payload['scope'] = scope.to_json()
|
||||
if language_code:
|
||||
payload['language_code'] = language_code
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
@ -913,7 +1105,7 @@ def unpin_all_chat_messages(token, chat_id):
|
||||
# Updating messages
|
||||
|
||||
def edit_message_text(token, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None,
|
||||
disable_web_page_preview=None, reply_markup=None):
|
||||
entities = None, disable_web_page_preview=None, reply_markup=None):
|
||||
method_url = r'editMessageText'
|
||||
payload = {'text': text}
|
||||
if chat_id:
|
||||
@ -924,6 +1116,8 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message
|
||||
payload['inline_message_id'] = inline_message_id
|
||||
if parse_mode:
|
||||
payload['parse_mode'] = parse_mode
|
||||
if entities:
|
||||
payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities))
|
||||
if disable_web_page_preview is not None:
|
||||
payload['disable_web_page_preview'] = disable_web_page_preview
|
||||
if reply_markup:
|
||||
@ -932,7 +1126,7 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message
|
||||
|
||||
|
||||
def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None,
|
||||
parse_mode=None, reply_markup=None):
|
||||
parse_mode=None, caption_entities=None,reply_markup=None):
|
||||
method_url = r'editMessageCaption'
|
||||
payload = {'caption': caption}
|
||||
if chat_id:
|
||||
@ -943,6 +1137,8 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m
|
||||
payload['inline_message_id'] = inline_message_id
|
||||
if parse_mode:
|
||||
payload['parse_mode'] = parse_mode
|
||||
if caption_entities:
|
||||
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
@ -981,7 +1177,7 @@ def delete_message(token, chat_id, message_id, timeout=None):
|
||||
method_url = r'deleteMessage'
|
||||
payload = {'chat_id': chat_id, 'message_id': message_id}
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
@ -989,7 +1185,8 @@ def delete_message(token, chat_id, message_id, timeout=None):
|
||||
|
||||
def send_game(
|
||||
token, chat_id, game_short_name,
|
||||
disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None):
|
||||
disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None,
|
||||
allow_sending_without_reply=None):
|
||||
method_url = r'sendGame'
|
||||
payload = {'chat_id': chat_id, 'game_short_name': game_short_name}
|
||||
if disable_notification is not None:
|
||||
@ -999,7 +1196,9 @@ def send_game(
|
||||
if reply_markup:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
@ -1060,11 +1259,11 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m
|
||||
|
||||
def send_invoice(
|
||||
token, chat_id, title, description, invoice_payload, provider_token, currency, prices,
|
||||
start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None,
|
||||
start_parameter = None, photo_url=None, photo_size=None, photo_width=None, photo_height=None,
|
||||
need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None,
|
||||
send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None,
|
||||
disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None,
|
||||
timeout=None):
|
||||
timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None):
|
||||
"""
|
||||
Use this method to send invoices. On success, the sent Message is returned.
|
||||
:param token: Bot's token (you don't need to fill this)
|
||||
@ -1092,12 +1291,18 @@ def send_invoice(
|
||||
:param reply_markup: A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button
|
||||
:param provider_data: A JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider.
|
||||
:param timeout:
|
||||
:param allow_sending_without_reply:
|
||||
:param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency
|
||||
:param suggested_tip_amounts: A JSON-serialized array of suggested amounts of tips in the smallest units of the currency.
|
||||
At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed max_tip_amount.
|
||||
:return:
|
||||
"""
|
||||
method_url = r'sendInvoice'
|
||||
payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload,
|
||||
'provider_token': provider_token, 'start_parameter': start_parameter, 'currency': currency,
|
||||
'provider_token': provider_token, 'currency': currency,
|
||||
'prices': _convert_list_json_serializable(prices)}
|
||||
if start_parameter:
|
||||
payload['start_parameter'] = start_parameter
|
||||
if photo_url:
|
||||
payload['photo_url'] = photo_url
|
||||
if photo_size:
|
||||
@ -1129,7 +1334,13 @@ def send_invoice(
|
||||
if provider_data:
|
||||
payload['provider_data'] = provider_data
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
if max_tip_amount is not None:
|
||||
payload['max_tip_amount'] = max_tip_amount
|
||||
if suggested_tip_amounts is not None:
|
||||
payload['suggested_tip_amounts'] = json.dumps(suggested_tip_amounts)
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
@ -1226,15 +1437,17 @@ def upload_sticker_file(token, user_id, png_sticker):
|
||||
|
||||
|
||||
def create_new_sticker_set(
|
||||
token, user_id, name, title, png_sticker, emojis,
|
||||
token, user_id, name, title, emojis, png_sticker, tgs_sticker,
|
||||
contains_masks=None, mask_position=None):
|
||||
method_url = 'createNewStickerSet'
|
||||
payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis}
|
||||
stype = 'png_sticker' if png_sticker else 'tgs_sticker'
|
||||
sticker = png_sticker or tgs_sticker
|
||||
files = None
|
||||
if not util.is_string(png_sticker):
|
||||
files = {'png_sticker': png_sticker}
|
||||
if not util.is_string(sticker):
|
||||
files = {stype: sticker}
|
||||
else:
|
||||
payload['png_sticker'] = png_sticker
|
||||
payload[stype] = sticker
|
||||
if contains_masks is not None:
|
||||
payload['contains_masks'] = contains_masks
|
||||
if mask_position:
|
||||
@ -1242,14 +1455,16 @@ def create_new_sticker_set(
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
|
||||
|
||||
def add_sticker_to_set(token, user_id, name, png_sticker, emojis, mask_position):
|
||||
def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position):
|
||||
method_url = 'addStickerToSet'
|
||||
payload = {'user_id': user_id, 'name': name, 'emojis': emojis}
|
||||
stype = 'png_sticker' if png_sticker else 'tgs_sticker'
|
||||
sticker = png_sticker or tgs_sticker
|
||||
files = None
|
||||
if not util.is_string(png_sticker):
|
||||
files = {'png_sticker': png_sticker}
|
||||
if not util.is_string(sticker):
|
||||
files = {stype: sticker}
|
||||
else:
|
||||
payload['png_sticker'] = png_sticker
|
||||
payload[stype] = sticker
|
||||
if mask_position:
|
||||
payload['mask_position'] = mask_position.to_json()
|
||||
return _make_request(token, method_url, params=payload, files=files, method='post')
|
||||
@ -1267,12 +1482,14 @@ def delete_sticker_from_set(token, sticker):
|
||||
return _make_request(token, method_url, params=payload, method='post')
|
||||
|
||||
|
||||
# noinspection PyShadowingBuiltins
|
||||
def send_poll(
|
||||
token, chat_id,
|
||||
question, options,
|
||||
is_anonymous = None, type = None, allows_multiple_answers = None, correct_option_id = None,
|
||||
explanation = None, explanation_parse_mode=None, open_period = None, close_date = None, is_closed = None,
|
||||
disable_notifications=False, reply_to_message_id=None, reply_markup=None, timeout=None):
|
||||
disable_notification=False, reply_to_message_id=None, allow_sending_without_reply=None,
|
||||
reply_markup=None, timeout=None, explanation_entities=None):
|
||||
method_url = r'sendPoll'
|
||||
payload = {
|
||||
'chat_id': str(chat_id),
|
||||
@ -1301,14 +1518,19 @@ def send_poll(
|
||||
if is_closed is not None:
|
||||
payload['is_closed'] = is_closed
|
||||
|
||||
if disable_notifications:
|
||||
payload['disable_notification'] = disable_notifications
|
||||
if disable_notification:
|
||||
payload['disable_notification'] = disable_notification
|
||||
if reply_to_message_id is not None:
|
||||
payload['reply_to_message_id'] = reply_to_message_id
|
||||
if allow_sending_without_reply is not None:
|
||||
payload['allow_sending_without_reply'] = allow_sending_without_reply
|
||||
if reply_markup is not None:
|
||||
payload['reply_markup'] = _convert_markup(reply_markup)
|
||||
if timeout:
|
||||
payload['connect-timeout'] = timeout
|
||||
payload['timeout'] = timeout
|
||||
if explanation_entities:
|
||||
payload['explanation_entities'] = json.dumps(
|
||||
types.MessageEntity.to_list_of_dicts(explanation_entities))
|
||||
return _make_request(token, method_url, params=payload)
|
||||
|
||||
|
||||
|
176
telebot/custom_filters.py
Normal file
176
telebot/custom_filters.py
Normal file
@ -0,0 +1,176 @@
|
||||
from abc import ABC
|
||||
|
||||
class SimpleCustomFilter(ABC):
|
||||
"""
|
||||
Simple Custom Filter base class.
|
||||
Create child class with check() method.
|
||||
Accepts only message, returns bool value, that is compared with given in handler.
|
||||
"""
|
||||
|
||||
def check(self, message):
|
||||
"""
|
||||
Perform a check.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class AdvancedCustomFilter(ABC):
|
||||
"""
|
||||
Simple Custom Filter base class.
|
||||
Create child class with check() method.
|
||||
Accepts two parameters, returns bool: True - filter passed, False - filter failed.
|
||||
message: Message class
|
||||
text: Filter value given in handler
|
||||
"""
|
||||
|
||||
def check(self, message, text):
|
||||
"""
|
||||
Perform a check.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class TextMatchFilter(AdvancedCustomFilter):
|
||||
"""
|
||||
Filter to check Text message.
|
||||
key: text
|
||||
|
||||
Example:
|
||||
@bot.message_handler(text=['account'])
|
||||
"""
|
||||
|
||||
key = 'text'
|
||||
|
||||
def check(self, message, text):
|
||||
if type(text) is list:return message.text in text
|
||||
else: return text == message.text
|
||||
|
||||
class TextContainsFilter(AdvancedCustomFilter):
|
||||
"""
|
||||
Filter to check Text message.
|
||||
key: text
|
||||
|
||||
Example:
|
||||
# Will respond if any message.text contains word 'account'
|
||||
@bot.message_handler(text_contains=['account'])
|
||||
"""
|
||||
|
||||
key = 'text_contains'
|
||||
|
||||
def check(self, message, text):
|
||||
return text in message.text
|
||||
|
||||
class TextStartsFilter(AdvancedCustomFilter):
|
||||
"""
|
||||
Filter to check whether message starts with some text.
|
||||
|
||||
Example:
|
||||
# Will work if message.text starts with 'Sir'.
|
||||
@bot.message_handler(text_startswith='Sir')
|
||||
"""
|
||||
|
||||
key = 'text_startswith'
|
||||
def check(self, message, text):
|
||||
return message.text.startswith(text)
|
||||
|
||||
class ChatFilter(AdvancedCustomFilter):
|
||||
"""
|
||||
Check whether chat_id corresponds to given chat_id.
|
||||
|
||||
Example:
|
||||
@bot.message_handler(chat_id=[99999])
|
||||
"""
|
||||
|
||||
key = 'chat_id'
|
||||
def check(self, message, text):
|
||||
return message.chat.id in text
|
||||
|
||||
class ForwardFilter(SimpleCustomFilter):
|
||||
"""
|
||||
Check whether message was forwarded from channel or group.
|
||||
|
||||
Example:
|
||||
|
||||
@bot.message_handler(is_forwarded=True)
|
||||
"""
|
||||
|
||||
key = 'is_forwarded'
|
||||
|
||||
def check(self, message):
|
||||
return message.forward_from_chat is not None
|
||||
|
||||
class IsReplyFilter(SimpleCustomFilter):
|
||||
"""
|
||||
Check whether message is a reply.
|
||||
|
||||
Example:
|
||||
|
||||
@bot.message_handler(is_reply=True)
|
||||
"""
|
||||
|
||||
key = 'is_reply'
|
||||
|
||||
def check(self, message):
|
||||
return message.reply_to_message is not None
|
||||
|
||||
|
||||
|
||||
class LanguageFilter(AdvancedCustomFilter):
|
||||
"""
|
||||
Check users language_code.
|
||||
|
||||
Example:
|
||||
|
||||
@bot.message_handler(language_code=['ru'])
|
||||
"""
|
||||
|
||||
key = 'language_code'
|
||||
|
||||
def check(self, message, text):
|
||||
if type(text) is list:return message.from_user.language_code in text
|
||||
else: return message.from_user.language_code == text
|
||||
|
||||
class IsAdminFilter(SimpleCustomFilter):
|
||||
"""
|
||||
Check whether the user is administrator / owner of the chat.
|
||||
|
||||
Example:
|
||||
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
|
||||
"""
|
||||
|
||||
key = 'is_chat_admin'
|
||||
|
||||
def __init__(self, bot):
|
||||
self._bot = bot
|
||||
|
||||
def check(self, message):
|
||||
return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator']
|
||||
|
||||
class StateFilter(AdvancedCustomFilter):
|
||||
"""
|
||||
Filter to check state.
|
||||
|
||||
Example:
|
||||
@bot.message_handler(state=1)
|
||||
"""
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
key = 'state'
|
||||
|
||||
def check(self, message, text):
|
||||
if self.bot.current_states.current_state(message.from_user.id) is False:return False
|
||||
elif text == '*':return True
|
||||
elif type(text) is list:return self.bot.current_states.current_state(message.from_user.id) in text
|
||||
return self.bot.current_states.current_state(message.from_user.id) == text
|
||||
|
||||
class IsDigitFilter(SimpleCustomFilter):
|
||||
"""
|
||||
Filter to check whether the string is made up of only digits.
|
||||
|
||||
Example:
|
||||
@bot.message_handler(is_digit=True)
|
||||
"""
|
||||
key = 'is_digit'
|
||||
|
||||
def check(self, message):
|
||||
return message.text.isdigit()
|
@ -141,3 +141,79 @@ class RedisHandlerBackend(HandlerBackend):
|
||||
handlers = pickle.loads(value)
|
||||
self.clear_handlers(handler_group_id)
|
||||
return handlers
|
||||
|
||||
|
||||
class State:
|
||||
def __init__(self):
|
||||
self._states = {}
|
||||
|
||||
def add_state(self, chat_id, state):
|
||||
"""
|
||||
Add a state.
|
||||
:param chat_id:
|
||||
:param state: new state
|
||||
"""
|
||||
if chat_id in self._states:
|
||||
|
||||
self._states[chat_id]['state'] = state
|
||||
else:
|
||||
self._states[chat_id] = {'state': state,'data': {}}
|
||||
|
||||
def current_state(self, chat_id):
|
||||
"""Current state"""
|
||||
if chat_id in self._states: return self._states[chat_id]['state']
|
||||
else: return False
|
||||
|
||||
def delete_state(self, chat_id):
|
||||
"""Delete a state"""
|
||||
return self._states.pop(chat_id)
|
||||
|
||||
def _get_data(self, chat_id):
|
||||
return self._states[chat_id]['data']
|
||||
|
||||
def set(self, chat_id, new_state):
|
||||
"""
|
||||
Set a new state for a user.
|
||||
:param chat_id:
|
||||
:param new_state: new_state of a user
|
||||
"""
|
||||
self.add_state(chat_id,new_state)
|
||||
|
||||
def _add_data(self, chat_id, key, value):
|
||||
result = self._states[chat_id]['data'][key] = value
|
||||
return result
|
||||
|
||||
def finish(self, chat_id):
|
||||
"""
|
||||
Finish(delete) state of a user.
|
||||
:param chat_id:
|
||||
"""
|
||||
self.delete_state(chat_id)
|
||||
|
||||
def retrieve_data(self, chat_id):
|
||||
"""
|
||||
Save input text.
|
||||
|
||||
Usage:
|
||||
with state.retrieve_data(message.chat.id) as data:
|
||||
data['name'] = message.text
|
||||
|
||||
Also, at the end of your 'Form' you can get the name:
|
||||
data['name']
|
||||
"""
|
||||
return StateContext(self, chat_id)
|
||||
|
||||
class StateContext:
|
||||
"""
|
||||
Class for data.
|
||||
"""
|
||||
def __init__(self , obj: State, chat_id) -> None:
|
||||
self.obj = obj
|
||||
self.chat_id = chat_id
|
||||
self.data = obj._get_data(chat_id)
|
||||
|
||||
def __enter__(self):
|
||||
return self.data
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
return
|
||||
|
2426
telebot/types.py
2426
telebot/types.py
File diff suppressed because it is too large
Load Diff
223
telebot/util.py
223
telebot/util.py
@ -4,31 +4,44 @@ import re
|
||||
import string
|
||||
import threading
|
||||
import traceback
|
||||
import warnings
|
||||
import functools
|
||||
from typing import Any, Callable, List, Dict, Optional, Union
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
import queue as Queue
|
||||
import logging
|
||||
|
||||
from telebot import types
|
||||
|
||||
try:
|
||||
# noinspection PyPackageRequirements
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
pil_imported = True
|
||||
except:
|
||||
pil_imported = False
|
||||
|
||||
MAX_MESSAGE_LENGTH = 4096
|
||||
|
||||
logger = logging.getLogger('TeleBot')
|
||||
|
||||
thread_local = threading.local()
|
||||
|
||||
content_type_media = [
|
||||
'text', 'audio', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', 'poll',
|
||||
'text', 'audio', 'animation', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', 'poll',
|
||||
'venue', 'location'
|
||||
]
|
||||
|
||||
content_type_service = [
|
||||
'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created',
|
||||
'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message'
|
||||
'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message',
|
||||
'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended',
|
||||
'voice_chat_participants_invited', 'message_auto_delete_timer_changed'
|
||||
]
|
||||
|
||||
update_types = [
|
||||
"update_id", "message", "edited_message", "channel_post", "edited_channel_post", "inline_query",
|
||||
"chosen_inline_result", "callback_query", "shipping_query", "pre_checkout_query", "poll", "poll_answer",
|
||||
"my_chat_member", "chat_member"
|
||||
]
|
||||
|
||||
class WorkerThread(threading.Thread):
|
||||
@ -165,15 +178,19 @@ def async_dec():
|
||||
def is_string(var):
|
||||
return isinstance(var, str)
|
||||
|
||||
|
||||
def is_dict(var):
|
||||
return isinstance(var, dict)
|
||||
|
||||
|
||||
def is_bytes(var):
|
||||
return isinstance(var, bytes)
|
||||
|
||||
|
||||
def is_pil_image(var):
|
||||
return pil_imported and isinstance(var, Image.Image)
|
||||
|
||||
|
||||
def pil_image_to_file(image, extension='JPEG', quality='web_low'):
|
||||
if pil_imported:
|
||||
photoBuffer = BytesIO()
|
||||
@ -184,17 +201,18 @@ def pil_image_to_file(image, extension='JPEG', quality='web_low'):
|
||||
else:
|
||||
raise RuntimeError('PIL module is not imported')
|
||||
|
||||
def is_command(text):
|
||||
|
||||
def is_command(text: str) -> bool:
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
if (text is None): return None
|
||||
if text is None: return False
|
||||
return text.startswith('/')
|
||||
|
||||
|
||||
def extract_command(text):
|
||||
def extract_command(text: str) -> Union[str, None]:
|
||||
"""
|
||||
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.
|
||||
@ -208,11 +226,28 @@ def extract_command(text):
|
||||
:param text: String to extract the command from
|
||||
:return: the command if `text` is a command (according to is_command), else None.
|
||||
"""
|
||||
if (text is None): return None
|
||||
if text is None: return None
|
||||
return text.split()[0].split('@')[0][1:] if is_command(text) else None
|
||||
|
||||
|
||||
def split_string(text, chars_per_string):
|
||||
def extract_arguments(text: str) -> str:
|
||||
"""
|
||||
Returns the argument after the command.
|
||||
|
||||
Examples:
|
||||
extract_arguments("/get name"): 'name'
|
||||
extract_arguments("/get"): ''
|
||||
extract_arguments("/get@botName name"): 'name'
|
||||
|
||||
:param text: String to extract the arguments from a command
|
||||
:return: the arguments if `text` is a command (according to is_command), else None.
|
||||
"""
|
||||
regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)", re.IGNORECASE)
|
||||
result = regexp.match(text)
|
||||
return result.group(2) if is_command(text) else None
|
||||
|
||||
|
||||
def split_string(text: str, chars_per_string: int) -> List[str]:
|
||||
"""
|
||||
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.
|
||||
@ -223,6 +258,107 @@ def split_string(text, chars_per_string):
|
||||
"""
|
||||
return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)]
|
||||
|
||||
|
||||
def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str]:
|
||||
"""
|
||||
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.
|
||||
If `chars_per_string` > 4096: `chars_per_string` = 4096.
|
||||
Splits by '\n', '. ' or ' ' in exactly this priority.
|
||||
|
||||
:param text: The text to split
|
||||
:param chars_per_string: The number of maximum characters per part the text is split to.
|
||||
:return: The splitted text as a list of strings.
|
||||
"""
|
||||
|
||||
def _text_before_last(substr: str) -> str:
|
||||
return substr.join(part.split(substr)[:-1]) + substr
|
||||
|
||||
if chars_per_string > MAX_MESSAGE_LENGTH: chars_per_string = MAX_MESSAGE_LENGTH
|
||||
|
||||
parts = []
|
||||
while True:
|
||||
if len(text) < chars_per_string:
|
||||
parts.append(text)
|
||||
return parts
|
||||
|
||||
part = text[:chars_per_string]
|
||||
|
||||
if "\n" in part: part = _text_before_last("\n")
|
||||
elif ". " in part: part = _text_before_last(". ")
|
||||
elif " " in part: part = _text_before_last(" ")
|
||||
|
||||
parts.append(part)
|
||||
text = text[len(part):]
|
||||
|
||||
|
||||
def escape(text: str) -> str:
|
||||
"""
|
||||
Replaces the following chars in `text` ('&' with '&', '<' with '<' and '>' with '>').
|
||||
|
||||
:param text: the text to escape
|
||||
:return: the escaped text
|
||||
"""
|
||||
chars = {"&": "&", "<": "<", ">": ">"}
|
||||
for old, new in chars.items(): text = text.replace(old, new)
|
||||
return text
|
||||
|
||||
|
||||
def user_link(user: types.User, include_id: bool=False) -> str:
|
||||
"""
|
||||
Returns an HTML user link. This is useful for reports.
|
||||
Attention: Don't forget to set parse_mode to 'HTML'!
|
||||
|
||||
Example:
|
||||
bot.send_message(your_user_id, user_link(message.from_user) + ' started the bot!', parse_mode='HTML')
|
||||
|
||||
:param user: the user (not the user_id)
|
||||
:param include_id: include the user_id
|
||||
:return: HTML user link
|
||||
"""
|
||||
name = escape(user.first_name)
|
||||
return (f"<a href='tg://user?id={user.id}'>{name}</a>"
|
||||
+ (f" (<pre>{user.id}</pre>)" if include_id else ""))
|
||||
|
||||
|
||||
def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.InlineKeyboardMarkup:
|
||||
"""
|
||||
Returns a reply markup from a dict in this format: {'text': kwargs}
|
||||
This is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)'
|
||||
|
||||
Example:
|
||||
quick_markup({
|
||||
'Twitter': {'url': 'https://twitter.com'},
|
||||
'Facebook': {'url': 'https://facebook.com'},
|
||||
'Back': {'callback_data': 'whatever'}
|
||||
}, row_width=2):
|
||||
returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook
|
||||
and a back button below
|
||||
|
||||
kwargs can be:
|
||||
{
|
||||
'url': None,
|
||||
'callback_data': None,
|
||||
'switch_inline_query': None,
|
||||
'switch_inline_query_current_chat': None,
|
||||
'callback_game': None,
|
||||
'pay': None,
|
||||
'login_url': None
|
||||
}
|
||||
|
||||
:param values: a dict containing all buttons to create in this format: {text: kwargs} {str:}
|
||||
:param row_width: int row width
|
||||
:return: InlineKeyboardMarkup
|
||||
"""
|
||||
markup = types.InlineKeyboardMarkup(row_width=row_width)
|
||||
buttons = [
|
||||
types.InlineKeyboardButton(text=text, **kwargs)
|
||||
for text, kwargs in values.items()
|
||||
]
|
||||
markup.add(*buttons)
|
||||
return markup
|
||||
|
||||
|
||||
# CREDITS TO http://stackoverflow.com/questions/12317940#answer-12320352
|
||||
def or_set(self):
|
||||
self._set()
|
||||
@ -243,8 +379,10 @@ def orify(e, changed_callback):
|
||||
e.set = lambda: or_set(e)
|
||||
e.clear = lambda: or_clear(e)
|
||||
|
||||
|
||||
def OrEvent(*events):
|
||||
or_event = threading.Event()
|
||||
|
||||
def changed():
|
||||
bools = [ev.is_set() for ev in events]
|
||||
if any(bools):
|
||||
@ -263,22 +401,6 @@ def OrEvent(*events):
|
||||
changed()
|
||||
return or_event
|
||||
|
||||
def extract_arguments(text):
|
||||
"""
|
||||
Returns the argument after the command.
|
||||
|
||||
Examples:
|
||||
extract_arguments("/get name"): 'name'
|
||||
extract_arguments("/get"): ''
|
||||
extract_arguments("/get@botName name"): 'name'
|
||||
|
||||
:param text: String to extract the arguments from a command
|
||||
:return: the arguments if `text` is a command (according to is_command), else None.
|
||||
"""
|
||||
regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE)
|
||||
result = regexp.match(text)
|
||||
return result.group(2) if is_command(text) else None
|
||||
|
||||
|
||||
def per_thread(key, construct_value, reset=False):
|
||||
if reset or not hasattr(thread_local, key):
|
||||
@ -287,26 +409,49 @@ def per_thread(key, construct_value, reset=False):
|
||||
|
||||
return getattr(thread_local, key)
|
||||
|
||||
|
||||
def chunks(lst, n):
|
||||
"""Yield successive n-sized chunks from lst."""
|
||||
# https://stackoverflow.com/a/312464/9935473
|
||||
for i in range(0, len(lst), n):
|
||||
yield lst[i:i + n]
|
||||
|
||||
|
||||
def generate_random_token():
|
||||
return ''.join(random.sample(string.ascii_letters, 16))
|
||||
|
||||
def deprecated(func):
|
||||
"""This is a decorator which can be used to mark functions
|
||||
as deprecated. It will result in a warning being emitted
|
||||
when the function is used."""
|
||||
# https://stackoverflow.com/a/30253848/441814
|
||||
@functools.wraps(func)
|
||||
def new_func(*args, **kwargs):
|
||||
warnings.simplefilter('always', DeprecationWarning) # turn off filter
|
||||
warnings.warn("Call to deprecated function {}.".format(func.__name__),
|
||||
category=DeprecationWarning,
|
||||
stacklevel=2)
|
||||
warnings.simplefilter('default', DeprecationWarning) # reset filter
|
||||
return func(*args, **kwargs)
|
||||
return new_func
|
||||
|
||||
def deprecated(warn: bool=False, alternative: Optional[Callable]=None):
|
||||
"""
|
||||
Use this decorator to mark functions as deprecated.
|
||||
When the function is used, an info (or warning if `warn` is True) is logged.
|
||||
:param warn: If True a warning is logged else an info
|
||||
:param alternative: The new function to use instead
|
||||
"""
|
||||
def decorator(function):
|
||||
def wrapper(*args, **kwargs):
|
||||
if not warn:
|
||||
logger.info(f"`{function.__name__}` is deprecated."
|
||||
+ (f" Use `{alternative.__name__}` instead" if alternative else ""))
|
||||
else:
|
||||
logger.warn(f"`{function.__name__}` is deprecated."
|
||||
+ (f" Use `{alternative.__name__}` instead" if alternative else ""))
|
||||
return function(*args, **kwargs)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
# Cloud helpers
|
||||
def webhook_google_functions(bot, request):
|
||||
"""A webhook endpoint for Google Cloud Functions FaaS."""
|
||||
if request.is_json:
|
||||
try:
|
||||
request_json = request.get_json()
|
||||
update = types.Update.de_json(request_json)
|
||||
bot.process_new_updates([update])
|
||||
return ''
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return 'Bot FAIL', 400
|
||||
else:
|
||||
return 'Bot ON'
|
||||
|
@ -1,3 +1,3 @@
|
||||
# Versions should comply with PEP440.
|
||||
# This line is parsed in setup.py:
|
||||
__version__ = '3.7.9'
|
||||
__version__ = '4.1.0'
|
||||
|
@ -62,8 +62,11 @@ def update_type(message):
|
||||
pre_checkout_query = None
|
||||
poll = None
|
||||
poll_answer = None
|
||||
my_chat_member = None
|
||||
chat_member = None
|
||||
return types.Update(1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query,
|
||||
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer)
|
||||
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer,
|
||||
my_chat_member, chat_member)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@ -78,9 +81,11 @@ def reply_to_message_update_type(reply_to_message):
|
||||
pre_checkout_query = None
|
||||
poll = None
|
||||
poll_answer = None
|
||||
my_chat_member = None
|
||||
chat_member = None
|
||||
return types.Update(1001234038284, reply_to_message, edited_message, channel_post, edited_channel_post,
|
||||
inline_query,
|
||||
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer)
|
||||
inline_query, chosen_inline_result, callback_query, shipping_query, pre_checkout_query,
|
||||
poll, poll_answer, my_chat_member, chat_member)
|
||||
|
||||
|
||||
def next_handler(message):
|
||||
|
@ -6,6 +6,7 @@ sys.path.append('../')
|
||||
import time
|
||||
import pytest
|
||||
import os
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
@ -18,6 +19,14 @@ if not should_skip:
|
||||
CHAT_ID = os.environ['CHAT_ID']
|
||||
GROUP_ID = os.environ['GROUP_ID']
|
||||
|
||||
def _new_test():
|
||||
pass
|
||||
|
||||
@util.deprecated(alternative=_new_test)
|
||||
def _test():
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@pytest.mark.skipif(should_skip, reason="No environment variables configured")
|
||||
class TestTeleBot:
|
||||
@ -125,6 +134,16 @@ class TestTeleBot:
|
||||
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_file_with_filename(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, file_data, visible_file_name="test.jpg")
|
||||
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)
|
||||
@ -407,6 +426,23 @@ class TestTeleBot:
|
||||
cn = tb.get_chat_members_count(GROUP_ID)
|
||||
assert cn > 1
|
||||
|
||||
def test_export_chat_invite_link(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
il = tb.export_chat_invite_link(GROUP_ID)
|
||||
assert isinstance(il, str)
|
||||
|
||||
def test_create_revoke_detailed_chat_invite_link(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
cil = tb.create_chat_invite_link(GROUP_ID,
|
||||
(datetime.now() + timedelta(minutes=1)).timestamp(), member_limit=5)
|
||||
assert isinstance(cil.invite_link, str)
|
||||
assert cil.creator.id == tb.get_me().id
|
||||
assert isinstance(cil.expire_date, (float, int))
|
||||
assert cil.member_limit == 5
|
||||
assert not cil.is_revoked
|
||||
rcil = tb.revoke_chat_invite_link(GROUP_ID, cil.invite_link)
|
||||
assert rcil.is_revoked
|
||||
|
||||
def test_edit_markup(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
@ -440,8 +476,11 @@ class TestTeleBot:
|
||||
pre_checkout_query = None
|
||||
poll = None
|
||||
poll_answer = None
|
||||
my_chat_member = None
|
||||
chat_member = None
|
||||
return types.Update(-1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query,
|
||||
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer)
|
||||
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer,
|
||||
my_chat_member, chat_member)
|
||||
|
||||
def test_is_string_unicode(self):
|
||||
s1 = u'string'
|
||||
@ -525,6 +564,24 @@ class TestTeleBot:
|
||||
ret_msg = tb.send_document(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
|
||||
assert ret_msg.caption_entities[0].type == 'italic'
|
||||
|
||||
def test_chat_commands(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
command, description, lang = 'command_1', 'description of command 1', 'en'
|
||||
scope = telebot.types.BotCommandScopeChat(CHAT_ID)
|
||||
ret_msg = tb.set_my_commands([telebot.types.BotCommand(command, description)], scope, lang)
|
||||
assert ret_msg is True
|
||||
|
||||
ret_msg = tb.get_my_commands(scope, lang)
|
||||
assert ret_msg[0].command == command
|
||||
assert ret_msg[0].description == description
|
||||
|
||||
ret_msg = tb.delete_my_commands(scope, lang)
|
||||
assert ret_msg is True
|
||||
|
||||
ret_msg = tb.get_my_commands(scope, lang)
|
||||
assert ret_msg == []
|
||||
|
||||
|
||||
def test_typed_middleware_handler(self):
|
||||
from telebot import apihelper
|
||||
|
||||
@ -566,6 +623,9 @@ class TestTeleBot:
|
||||
tb.process_new_updates([update])
|
||||
time.sleep(1)
|
||||
assert update.message.text == 'got' * 2
|
||||
|
||||
def test_deprecated_dec(self):
|
||||
_test()
|
||||
|
||||
def test_chat_permissions(self):
|
||||
return # CHAT_ID is private chat, no permissions can be set
|
||||
|
@ -210,7 +210,7 @@ def test_json_poll_answer():
|
||||
poll_answer = types.PollAnswer.de_json(jsonstring)
|
||||
assert poll_answer.poll_id == '5895675970559410186'
|
||||
assert isinstance(poll_answer.user, types.User)
|
||||
assert poll_answer.options_ids == [1]
|
||||
assert poll_answer.option_ids == [1]
|
||||
|
||||
|
||||
def test_KeyboardButtonPollType():
|
||||
@ -219,3 +219,24 @@ def test_KeyboardButtonPollType():
|
||||
json_str = markup.to_json()
|
||||
assert 'request_poll' in json_str
|
||||
assert 'quiz' in json_str
|
||||
|
||||
|
||||
def test_json_chat_invite_link():
|
||||
json_string = r'{"invite_link": "https://t.me/joinchat/z-abCdEFghijKlMn", "creator": {"id": 329343347, "is_bot": false, "first_name": "Test", "username": "test_user", "last_name": "User", "language_code": "en"}, "is_primary": false, "is_revoked": false, "expire_date": 1624119999, "member_limit": 10}'
|
||||
invite_link = types.ChatInviteLink.de_json(json_string)
|
||||
assert invite_link.invite_link == 'https://t.me/joinchat/z-abCdEFghijKlMn'
|
||||
assert isinstance(invite_link.creator, types.User)
|
||||
assert not invite_link.is_primary
|
||||
assert not invite_link.is_revoked
|
||||
assert invite_link.expire_date == 1624119999
|
||||
assert invite_link.member_limit == 10
|
||||
|
||||
def test_chat_member_updated():
|
||||
json_string = r'{"chat": {"id": -1234567890123, "type": "supergroup", "title": "No Real Group", "username": "NoRealGroup"}, "from": {"id": 133869498, "is_bot": false, "first_name": "Vincent"}, "date": 1624119999, "old_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "member"}, "new_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "administrator"}}'
|
||||
cm_updated = types.ChatMemberUpdated.de_json(json_string)
|
||||
assert cm_updated.chat.id == -1234567890123
|
||||
assert cm_updated.from_user.id == 133869498
|
||||
assert cm_updated.date == 1624119999
|
||||
assert cm_updated.old_chat_member.status == "member"
|
||||
assert cm_updated.new_chat_member.status == "administrator"
|
||||
|
||||
|
Reference in New Issue
Block a user