1
0
mirror of https://github.com/eternnoir/pyTelegramBotAPI.git synced 2023-08-10 21:12:57 +03:00

Compare commits

..

110 Commits

Author SHA1 Message Date
41f7c07959 Update version. 2018-08-03 08:35:04 +08:00
35ea2a2b7e Fix #548 2018-08-03 08:34:48 +08:00
522b2b487b Merge pull request #551 from heyyyoyy/origin/Bot_Api_3.6
Added parse mode for objects in Inline mode
2018-08-03 08:30:41 +08:00
5035e0ce80 Added parse mode for objects in Inline mode 2018-08-02 21:15:33 +03:00
7061091c1c Update version.
* Fix python 3.7 async
2018-07-31 08:58:04 +08:00
5c199bd246 Update README.md 2018-07-26 22:17:55 +08:00
44dd89881d Update README.md 2018-07-26 21:41:08 +08:00
76dbb05259 Merge pull request #537 from Andru1999/patch-1
Update __init__.py
2018-07-24 13:05:02 +08:00
578a9383b2 Merge branch 'master' into patch-1 2018-07-24 10:42:04 +08:00
85093bded5 Merge pull request #515 from WaffleWafflerov/master
Saving next step and reply handlers
2018-07-24 10:39:35 +08:00
f251def304 Merge branch 'master' into master 2018-07-22 04:43:53 +03:00
2b822f782d Update __init__.py
I find bug when I use your library without threading. If call bot.register_next_step_handler in function that register next_handler in next_step_handlers but in function _notify_next_handlers this delete and bot don`t have handler, but in threading mode function self.next_step_handlers.pop(chat_id, None) has time to eval self.next_step_handlers.pop(chat_id, None) and bug disappear. Sorry for my English
2018-07-22 00:31:02 +10:00
8bc5b74495 Remove 3.7 stable. Travis ci not support now. 2018-07-03 22:34:28 +08:00
70426ac274 Merge pull request #526 from skar404/master
rename async -> async_dec
2018-07-03 11:19:29 +08:00
a3a2bd5793 Add python 3.7 in travis ci.
#527
2018-07-03 11:17:44 +08:00
c3b6ee9dc0 bug in travis-ci 2018-07-02 23:41:37 +03:00
4079772fd3 Update .travis.yml 2018-07-02 23:29:07 +03:00
9547a8d7b1 test python 3.7 2018-07-02 23:23:48 +03:00
c8b2b14157 rename async -> async_dec 2018-07-02 18:13:11 +03:00
3d5ef5b1d8 Merge pull request #516 from Badiboy/master
_notify_next_handlers drops messages if empty handler list
2018-05-31 14:04:45 +08:00
776a699a8d _notify_next_handlers skips sequential messages
Is there are several sequential messages and next_step_handlers are set, the _notify_next_handlers will process only every even message dew to execute both .pop(i) and i+=1
2018-05-29 18:55:41 +03:00
78afd045d8 _notify_next_handlers drops messages if empty handler list
After calling
clear_step_handler(...)
code:
self.next_step_handlers[chat_id] = []
left the key in next_step_handlers.
When a next message arrives, the old handler executes nothing (no handlers), but still remove message from message queue:
new_messages.pop(i).

Updated to pop message only when there are real handlers in the list.
2018-05-27 23:24:37 +03:00
06faed887c Merge pull request #509 from Badiboy/master
html_text fix and html_caption
2018-05-27 23:14:37 +08:00
bc855f7610 Fix register_for_reply_by_message_id and register_for_reply doc strings. 2018-05-27 17:05:01 +03:00
893d5386c5 Fix register_for_reply_by_message_id doc strings. 2018-05-27 17:02:04 +03:00
909d570dca Add warning about lambda functions in callbacks 2018-05-27 17:01:07 +03:00
424c77fd2c Remove type hint for 2.x and PyPy python compatibility 2018-05-27 16:54:56 +03:00
333949683f Add doc strings to new TeleBot methods
Update telebot/__init__.py
2018-05-27 01:37:06 +03:00
fa038c2e42 Move del_file_after_loading param to right methods :face_palm:
Update: telebot/__init__.py
2018-05-27 01:30:14 +03:00
d61de35a32 Remove rudiment json things, again!
Update: telebot/__init__.py
2018-05-27 01:23:20 +03:00
13df7b5908 Add enable_save_next_step_handlers and load_next_step_handlers methods to step_example/
Update: examples/step_example.py
2018-05-26 17:21:39 +03:00
1de356dcc3 Change default save directory to "./.handler-saves/".
Add del_file_after_loading param to load methods.

Update: telebot/__init__.py
2018-05-26 17:10:00 +03:00
47e6dfd6bc Remove rudiment json things
Update: telebot/__init__.py
2018-05-26 16:52:30 +03:00
3c890a7846 Remove 2 spaces up to TeleBot class.
Update: telebot/__init__.py
2018-05-26 16:37:25 +03:00
17971ff48b Move from json to pickle.
Update: relebot/__init__.py
2018-05-26 12:19:01 +03:00
b989b7601b Add new class: Handler
Change type of (next step|reply) handlers from dict to Handler [WIP]

update: telebot/__init__.py
2018-05-25 20:57:22 +03:00
8c574a786a Merge branch 'master' of https://github.com/WaffleWafflerov/pyTelegramBotAPI-1 2018-05-25 20:37:36 +03:00
7e5f51e4ab Remove old thing.
Update telebot/__init__.py
2018-05-25 09:44:43 +03:00
018e4597a2 Add del_file_after_loading param to Saver.return_load_handlers and Saver.load_handlers methods.
Update telebot/__init__.py
2018-05-25 09:40:29 +03:00
7df6b3d4c9 Fix situation where delay <= 0.
Update telebot/__init__.py
2018-05-25 09:35:39 +03:00
4facc5f7d7 fix unenabled saving handlers.
Updated telebot/__init__.py
2018-05-25 09:30:10 +03:00
4bcfc34a50 Update _notify_next_handlers and _notify_reply_handlers methods:
Now if there wasn't any handler updates, timer willn't start.
2018-05-25 09:07:59 +03:00
b1d5cb2129 Rewrite.
Add class 'Saver' that provides methods for saving (next step|reply) handlers.
Add methods
enable_save_next_step_handlers,
enable_save_reply_handlers,
disable_save_next_step_handlers,
disable_save_reply_handlers,
load_next_step_handlers,
load_reply_handlers
to Telebot and AsyncTelebot.

update telebot/__init__.py
2018-05-25 08:57:48 +03:00
00c8dcc19b Add async methods 2018-05-25 03:10:40 +03:00
ed7e33b4c6 Fix loadings funcs 2018-05-25 02:48:46 +03:00
74a952846c Merge branch 'master' of https://github.com/CoconutWaffle/pyTelegramBotAPI-1 2018-05-24 18:32:25 +03:00
e99fb8f84f Add methods to save (reply|next step) handlers [WIP] 2018-05-20 23:40:25 +03:00
49aee14fca Make _test_filter method static and a bit clear doc strings 2018-05-19 00:42:06 +03:00
9267da205d Merge pull request #1 from eternnoir/master
Updating fork
2018-05-19 00:35:58 +03:00
9c79ba2f87 html_text fix and html_caption
html_text now works with text_link
html_caption now works for caption/caption_entities
2018-05-14 13:29:34 +03:00
3be21ae361 Update version. 2018-05-14 10:15:05 +08:00
42343c3a7f Merge pull request #490 from CoconutWaffle/master
Fixing and upgrading next step and reply handlers. + minor fixes
2018-05-04 07:20:18 +08:00
5a102ed8fa Merge pull request #492 from sviat9440/master
Bugfixes (message.html_text offset)
2018-05-04 07:19:28 +08:00
e1e109bef1 Merge pull request #502 from fojetin/patch-1
Fix #253 #231 #485
2018-05-04 07:18:57 +08:00
b5a217013a Fix #253 #231 #485 2018-04-30 15:41:12 +03:00
3ba9799b98 Renaming back pytelegrambotapi module to telebot 2018-04-28 13:50:59 +03:00
91f213ff34 Fix #501 2018-04-27 15:47:03 +08:00
8f55460924 Fix cache time is zero. 2018-04-26 09:53:55 +08:00
f6b999053d Merge pull request #497 from khabibr/master
Update apihelper.py
2018-04-24 23:52:45 +08:00
99ff104a3f Update apihelper.py
Correct files downloading when proxy used.
2018-04-24 16:48:39 +06:00
662c2c8797 Merge pull request #495 from BennyThink/master
fix issue #403: UnicodeEncodeError when sending a non-ASCII file in Python 2
2018-04-18 20:05:19 +09:00
72a0199a2f Merge pull request #496 from Jay-T/master
Update README.md
2018-04-18 20:04:27 +09:00
989cae597b Update README.md 2018-04-18 13:10:22 +05:00
5dd88f8223 fix issue #403: UnicodeEncodeError when sending a non-ASCII file in Python 2.7 2018-04-18 15:00:05 +08:00
28111bdf4e Merge pull request #494 from LeoNeeDpk1/patch-1
Update README.md SOCKS5 proxy info
2018-04-18 11:51:44 +09:00
10ec897fb5 Update README.md
Updated and 100% working info in SOCKS5 proxy block.
2018-04-18 10:05:26 +12:00
ffe3a0c3d7 Update types.py
-- Fix encoding bug (emoji shifted offset)
2018-04-15 19:19:29 +03:00
f5f48db6ba Merge pull request #1 from eternnoir/master
Update
2018-04-15 19:17:46 +03:00
183230e927 Update setup.py 2018-04-12 17:24:04 +03:00
7957bc45a8 Fixing and upgrading next step and reply handlers. + minor fixes
Rename telebot package to pytelegrambotapi becouse lib named telebot exists and it raising many errors

Add methods:
|     register_for_reply_by_message_id,
|     register_next_step_handler_by_chat_id,
|     clear_reply_handlers,
|     clear_reply_handlers_by_message_id
2018-04-12 13:45:32 +03:00
373d4d37ff Fix test case. 2018-04-10 14:48:39 +08:00
0d0e37dae5 Merge pull request #487 from sviat9440/master
Bugfixes and minor improvements
2018-04-10 15:25:36 +09:00
36d088dfbf Bugfixes and minor improvements 2018-04-04 10:47:37 +03:00
9ae20b4815 Merge pull request #482 from sviat9440/master
Minor improvements
2018-03-25 21:39:27 +08:00
e761e1e1d9 Update apihelper.py 2018-03-25 14:54:28 +03:00
cb0256b37d Update __init__.py 2018-03-25 13:22:35 +03:00
ff3cbaf45b Update apihelper.py 2018-03-25 13:21:55 +03:00
d231b1fbaa Bump version. 2018-03-23 19:59:42 +08:00
7f47f11444 Fix #481 2018-03-23 19:58:43 +08:00
0422e62f65 Update types.py
Fix
2018-03-21 10:45:34 +03:00
82e252ec46 Update types.py
Fix
2018-03-21 10:44:37 +03:00
c11a9f810c Update types.py
Added 'json' property to class 'Message', to quickly save a message to the database
2018-03-21 07:35:42 +03:00
dadcd5a577 Update "Bots using this API" entry. 2018-03-19 15:13:48 +08:00
afc9abc269 Add ci test for python 3.6 2018-03-10 14:48:30 +08:00
e01f17e3a0 Bump version. 2018-03-10 14:46:47 +08:00
48e6757686 Fix import logger problem. 2018-03-10 14:41:34 +08:00
8495229ce1 Update version 3.6.0 2018-03-02 19:28:34 +08:00
3b60f7ca67 Merge pull request #462 from heyyyoyy/Bot_Api_3.6
Bot Api 3.6
2018-02-22 22:11:46 +08:00
a1930e05c2 Merge pull request #463 from dmytrostriletskyi/master
Make the Heroku example actual
2018-02-22 20:52:53 +08:00
bc067662dc Make the Heroku example actual 2018-02-16 17:52:37 +02:00
518c49f23a fixing formatting of caption in the method send_document 2018-02-16 18:29:29 +03:00
903b1dfd50 added parse_mode in edit_message_caption 2018-02-16 14:19:35 +00:00
2e199a5684 Bot Api 3.6 2018-02-14 20:27:55 +00:00
55302cb972 Merge pull request #445 from heyyyoyy/update_send_media_group
Added support for local files in the sendMediaGroup method
2018-02-01 19:24:50 +08:00
f47653d2e4 Update README.md 2018-01-24 19:13:29 +08:00
94b4a25980 Update version. 2018-01-24 19:11:03 +08:00
afac177d7d Fix missing media_group_id in message. 2018-01-24 19:05:38 +08:00
2637e29dbe Updated sendMediaGroup method 2018-01-15 16:08:50 +03:00
6d180e30f0 Merge pull request #443 from reacheight/master
fixed example of usage sendVideoNote method
2018-01-15 10:25:06 +08:00
e2ed4cf065 Merge pull request #387 from fumycat/patch-1
Fix optional parameter
2018-01-15 10:23:26 +08:00
8b2dea1d56 fixed example of usage sendVideoNote method 2018-01-10 00:26:44 +03:00
41e31de034 Merge pull request #429 from JekaFST/master
Fix for SendLocation with live period
2017-12-06 15:50:49 +08:00
ae074fd5c9 Merge pull request #2 from JekaFST/JekaFST-live_location_fixes
Edit and stop live location fixes
2017-12-05 01:32:06 +03:00
60596a95b8 Edit and stop live location fixes
.token was missed in apihelper's methods calls
2017-12-05 01:31:47 +03:00
44531bcedf Merge pull request #1 from JekaFST/JekaFST-FixLiveLocation
Fix for SendLocation with live period
2017-12-05 00:23:32 +03:00
8aa8fa5986 Fix for SendLocation with live period
Fix for payload['live_perion'] typo -> payload['live_period']
2017-12-05 00:21:05 +03:00
f0e64b3653 Merge pull request #428 from 0xVK/patch-1
Added SmartySBot to README
2017-12-04 16:21:41 +08:00
8444ea588a Added SmartySBot to README
Added SmartySBot to README
2017-12-03 20:34:37 +02:00
b2f376a906 Remove debug message. Add content_type 2017-11-30 23:34:07 +08:00
af991ea76e Fix optional parameter 2017-08-20 01:36:08 +07:00
11 changed files with 659 additions and 196 deletions

View File

@ -5,6 +5,7 @@ python:
- "3.3"
- "3.4"
- "3.5"
- "3.6"
- "pypy"
- "pypy3"
install: "pip install -r requirements.txt"

View File

@ -291,8 +291,8 @@ tb.send_video(chat_id, "FILEID")
# sendVideoNote
videonote = open('/tmp/videonote.mp4', 'rb')
tb.send_video(chat_id, videonote)
tb.send_video(chat_id, "FILEID")
tb.send_video_note(chat_id, videonote)
tb.send_video_note(chat_id, "FILEID")
# sendLocation
tb.send_location(chat_id, lat, lon)
@ -500,16 +500,13 @@ 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://10.10.1.10:3128'}
```
If you want to use socket5 proxy you need install dependency `pip install requests[socks]`.
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`.
```python
proxies = {
'http': 'socks5://user:pass@host:port',
'https': 'socks5://user:pass@host:port'
}
apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
```
@ -584,5 +581,9 @@ Get help. Discuss. Chat.
* [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 audiosamles and try to name the performer of the song.
Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh.

View File

@ -75,4 +75,14 @@ def process_sex_step(message):
bot.reply_to(message, 'oooops')
# Enable saving next step handlers to file "./.handlers-saves/step.save".
# Delay=2 means that after any change in next step handlers (e.g. calling register_next_step_handler())
# saving will hapen after delay 2 seconds.
bot.enable_save_next_step_handlers(delay=2)
# Load next_step_handlers from save file (default "./.handlers-saves/step.save")
# WARNING It will work only if enable_save_next_step_handlers was called!
bot.load_next_step_handlers()
bot.polling()

View File

@ -7,6 +7,7 @@
import flask
import telebot
import logging
import time
API_TOKEN = '<api_token>'
@ -73,6 +74,8 @@ def echo_message(message):
# Remove webhook, it fails sometimes the set if there is a previous webhook
bot.remove_webhook()
time.sleep(0.1)
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))

View File

@ -1,29 +1,35 @@
import telebot
import os
import telebot
from flask import Flask, request
bot = telebot.TeleBot('<api_token>')
TOKEN = '<api_token>'
bot = telebot.TeleBot(TOKEN)
server = Flask(__name__)
@bot.message_handler(commands=['start'])
def start(message):
bot.reply_to(message, 'Hello, ' + message.from_user.first_name)
@bot.message_handler(func=lambda message: True, content_types=['text'])
def echo_message(message):
bot.reply_to(message, message.text)
@server.route("/bot", methods=['POST'])
@server.route('/' + TOKEN, methods=['POST'])
def getMessage():
bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))])
return "!", 200
@server.route("/")
def webhook():
bot.remove_webhook()
bot.set_webhook(url="https://herokuProject_url/bot")
bot.set_webhook(url='https://your_heroku_project.com/' + TOKEN)
return "!", 200
server.run(host="0.0.0.0", port=os.environ.get('PORT', 5000))
server = Flask(__name__)
if __name__ == "__main__":
server.run(host="0.0.0.0", port=int(os.environ.get('PORT', 5000)))

View File

@ -7,7 +7,7 @@ def readme():
return f.read()
setup(name='pyTelegramBotAPI',
version='3.5.0',
version='3.6.5',
description='Python Telegram bot api. ',
long_description=readme(),
author='eternnoir',

View File

@ -6,6 +6,10 @@ import time
import re
import sys
import six
import copy
import os
import pickle
import logging
@ -27,6 +31,70 @@ Module : telebot
"""
class Handler:
"""
Class for (next step|reply) handlers
"""
def __init__(self, callback, *args, **kwargs):
self.callback = callback
self.args = args
self.kwargs = kwargs
def __getitem__(self, item):
return getattr(self, item)
class Saver:
"""
Class for saving (next step|reply) handlers
"""
def __init__(self, handlers, filename, delay):
self.handlers = handlers
self.filename = filename
self.delay = delay
self.timer = threading.Timer(delay, self.save_handlers)
def start_save_timer(self):
if not self.timer.is_alive():
if self.delay <= 0:
self.save_handlers()
else:
self.timer = threading.Timer(self.delay, self.save_handlers)
self.timer.start()
def save_handlers(self):
self.dump_handlers(self.handlers, self.filename)
def load_handlers(self, filename, del_file_after_loading=True):
tmp = self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading)
if tmp is not None:
self.handlers.update(tmp)
@staticmethod
def dump_handlers(handlers, filename, file_mode="wb"):
dirs = filename.rsplit('/', maxsplit=1)[0]
os.makedirs(dirs, exist_ok=True)
with open(filename + ".tmp", file_mode) as file:
pickle.dump(handlers, file)
if os.path.isfile(filename):
os.remove(filename)
os.rename(filename + ".tmp", filename)
@staticmethod
def return_load_handlers(filename, del_file_after_loading=True):
if os.path.isfile(filename) and os.path.getsize(filename) > 0:
with open(filename, "rb") as file:
handlers = pickle.load(file)
if del_file_after_loading:
os.remove(filename)
return handlers
class TeleBot:
""" This is TeleBot Class
Methods:
@ -70,6 +138,7 @@ class TeleBot:
:param token: bot API token
:return: Telebot object.
"""
self.token = token
self.update_listener = []
self.skip_pending = skip_pending
@ -78,13 +147,14 @@ class TeleBot:
self.last_update_id = 0
self.exc_info = None
self.message_subscribers_messages = []
self.message_subscribers_callbacks = []
self.message_subscribers_lock = threading.Lock()
# key: message_id, value: handler list
self.reply_handlers = {}
# key: chat_id, value: handler list
self.message_subscribers_next_step = {}
self.pre_message_subscribers_next_step = {}
self.next_step_handlers = {}
self.next_step_saver = None
self.reply_saver = None
self.message_handlers = []
self.edited_message_handlers = []
@ -100,6 +170,54 @@ class TeleBot:
if self.threaded:
self.worker_pool = util.ThreadPool(num_threads=num_threads)
def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"):
"""
Enable saving next step handlers (by default saving disable)
:param delay: Delay between changes in handlers and saving
:param filename: Filename of save file
"""
self.next_step_saver = Saver(self.next_step_handlers, filename, delay)
def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"):
"""
Enable saving reply handlers (by default saving disable)
:param delay: Delay between changes in handlers and saving
:param filename: Filename of save file
"""
self.reply_saver = Saver(self.reply_handlers, filename, delay)
def disable_save_next_step_handlers(self):
"""
Disable saving next step handlers (by default saving disable)
"""
self.next_step_saver = None
def disable_save_reply_handlers(self):
"""
Disable saving next step handlers (by default saving disable)
"""
self.reply_saver = None
def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_file_after_loading=True):
"""
Load next step handlers from save file
:param filename: Filename of the file where handlers was saved
:param del_file_after_loading: Is passed True, after loading save file will be deleted
"""
self.next_step_saver.load_handlers(filename, del_file_after_loading)
def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True):
"""
Load reply handlers from save file
:param filename: Filename of the file where handlers was saved
:param del_file_after_loading: Is passed True, after loading save file will be deleted
"""
self.reply_saver.load_handlers(filename)
def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None):
return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates)
@ -213,11 +331,10 @@ class TeleBot:
self.process_new_shipping_query(new_shipping_querys)
def process_new_messages(self, new_messages):
self._append_pre_next_step_handler()
self._notify_next_handlers(new_messages)
self._notify_reply_handlers(new_messages)
self.__notify_update(new_messages)
self._notify_command_handlers(self.message_handlers, new_messages)
self._notify_message_subscribers(new_messages)
self._notify_message_next_handler(new_messages)
def process_new_edited_messages(self, edited_message):
self._notify_command_handlers(self.edited_message_handlers, edited_message)
@ -247,6 +364,15 @@ class TeleBot:
for listener in self.update_listener:
self._exec_task(listener, new_messages)
def infinity_polling(self, *args, **kwargs):
while not self.__stop_polling.is_set():
try:
self.polling(*args, **kwargs)
except Exception as e:
time.sleep(5)
pass
logger.info("Break infinity polling")
def polling(self, none_stop=False, interval=0, timeout=20):
"""
This function creates a new Thread that calls an internal __retrieve_updates function.
@ -302,9 +428,9 @@ class TeleBot:
except KeyboardInterrupt:
logger.info("KeyboardInterrupt received.")
self.__stop_polling.set()
polling_thread.stop()
break
polling_thread.stop()
logger.info('Stopped polling.')
def __non_threaded_polling(self, none_stop=False, interval=0, timeout=3):
@ -341,6 +467,11 @@ class TeleBot:
def stop_polling(self):
self.__stop_polling.set()
def stop_bot(self):
self.stop_polling()
if self.worker_pool:
self.worker_pool.close()
def set_update_listener(self, listener):
self.update_listener.append(listener)
@ -351,6 +482,9 @@ class TeleBot:
def get_file(self, file_id):
return types.File.de_json(apihelper.get_file(self.token, file_id))
def get_file_url(self, file_id):
return apihelper.get_file_url(self.token, file_id)
def download_file(self, file_path):
return apihelper.download_file(self.token, file_path)
@ -478,7 +612,7 @@ class TeleBot:
def delete_message(self, chat_id, message_id):
"""
Use this method to delete message. Returns True on success.
Use this method to delete message. Returns True on success.
:param chat_id: in which chat to delete
:param message_id: which message to delete
:return: API reply.
@ -486,24 +620,25 @@ class TeleBot:
return apihelper.delete_message(self.token, chat_id, message_id)
def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
parse_mode=None, disable_notification=None):
"""
Use this method to send photos.
:param disable_notification:
:param chat_id:
:param photo:
:param caption:
:param parse_mode
:param reply_to_message_id:
:param reply_markup:
:return: API reply.
"""
return types.Message.de_json(
apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup,
disable_notification))
parse_mode, disable_notification))
def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None,
reply_to_message_id=None,
reply_markup=None, disable_notification=None, timeout=None):
reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None,
timeout=None):
"""
Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format.
:param chat_id:Unique identifier for the message recipient
@ -511,16 +646,17 @@ class TeleBot:
:param duration:Duration of the audio in seconds
:param performer:Performer
:param title:Track name
:param parse_mode
:param reply_to_message_id:If the message is a reply, ID of the original message
:param reply_markup:
:return: Message
"""
return types.Message.de_json(
apihelper.send_audio(self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id,
reply_markup, disable_notification, timeout))
reply_markup, parse_mode, disable_notification, timeout))
def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
parse_mode=None, disable_notification=None, timeout=None):
"""
Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message.
:param chat_id:Unique identifier for the message recipient.
@ -528,25 +664,28 @@ class TeleBot:
:param duration:Duration of sent audio in seconds
:param reply_to_message_id:
:param reply_markup:
:param parse_mode
:return: Message
"""
return types.Message.de_json(
apihelper.send_voice(self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup,
disable_notification, timeout))
parse_mode, disable_notification, timeout))
def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, reply_markup=None,
disable_notification=None, timeout=None):
parse_mode=None, disable_notification=None, timeout=None):
"""
Use this method to send general files.
:param chat_id:
:param data:
:param reply_to_message_id:
:param reply_markup:
:param parse_mode:
:param disable_notification:
:return: API reply.
"""
return types.Message.de_json(
apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup,
disable_notification, timeout, caption=caption))
parse_mode, disable_notification, timeout, caption=caption))
def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None,
timeout=None):
@ -563,20 +702,22 @@ class TeleBot:
disable_notification, timeout))
def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None):
"""
Use this method to send video files, Telegram clients support mp4 videos.
:param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id
:param data: InputFile or String : Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram server
:param duration: Integer : Duration of sent video in seconds
:param caption: String : Video caption (may also be used when resending videos by file_id).
:param parse_mode:
:param supports_streaming:
:param reply_to_message_id:
:param reply_markup:
:return:
"""
return types.Message.de_json(
apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup,
disable_notification, timeout))
parse_mode, supports_streaming, disable_notification, timeout))
def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
@ -639,7 +780,7 @@ class TeleBot:
:return:
"""
return types.Message.de_json(
apihelper.edit_message_live_location(self, latitude, longitude, chat_id, message_id,
apihelper.edit_message_live_location(self.token, latitude, longitude, chat_id, message_id,
inline_message_id, reply_markup))
def stop_message_live_location(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
@ -653,7 +794,7 @@ class TeleBot:
:return:
"""
return types.Message.de_json(
apihelper.stop_message_live_location(self, chat_id, message_id, inline_message_id, reply_markup))
apihelper.stop_message_live_location(self.token, chat_id, message_id, inline_message_id, reply_markup))
def send_venue(self, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None,
reply_to_message_id=None, reply_markup=None):
@ -888,8 +1029,8 @@ class TeleBot:
def send_invoice(self, chat_id, title, description, invoice_payload, provider_token, currency, prices,
start_parameter, 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,
is_flexible=None,
disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None):
is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None,
provider_data=None):
result = apihelper.send_invoice(self.token, chat_id, title, description, invoice_payload, provider_token,
currency, prices, start_parameter, photo_url, photo_size, photo_width,
photo_height,
@ -903,9 +1044,10 @@ class TeleBot:
def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=None):
return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message)
def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None,
parse_mode=None, reply_markup=None):
result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id,
reply_markup)
parse_mode, reply_markup)
if type(result) == bool:
return result
return types.Message.de_json(result)
@ -961,7 +1103,6 @@ class TeleBot:
def get_sticker_set(self, name):
"""
Use this method to get a sticker set. On success, a StickerSet object is returned.
:param token:
:param name:
:return:
"""
@ -996,7 +1137,7 @@ class TeleBot:
return apihelper.create_new_sticker_set(self.token, user_id, name, title, png_sticker, emojis, contains_masks,
mask_position)
def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position):
def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position=None):
"""
Use this method to add a new sticker to a set created by the bot. Returns True on success.
:param user_id:
@ -1025,51 +1166,80 @@ class TeleBot:
"""
return apihelper.delete_sticker_from_set(self.token, sticker)
def register_for_reply(self, message, callback):
def register_for_reply(self, message, callback, *args, **kwargs):
"""
Registers a callback function to be notified when a reply to `message` arrives.
Warning: `message` must be sent with reply_markup=types.ForceReply(), otherwise TeleBot will not be able to see
the difference between a reply to `message` and an ordinary message.
Warning: In case `callback` as lambda function, saving reply handlers will not work.
:param message: The message for which we are awaiting a reply.
:param callback: The callback function to be called when a reply arrives. Must accept one `message`
parameter, which will contain the replied message.
"""
with self.message_subscribers_lock:
self.message_subscribers_messages.insert(0, message.message_id)
self.message_subscribers_callbacks.insert(0, callback)
if len(self.message_subscribers_messages) > 10000:
self.message_subscribers_messages.pop()
self.message_subscribers_callbacks.pop()
message_id = message.message_id
self.register_for_reply_by_message_id(message_id, callback, *args, **kwargs)
def _notify_message_subscribers(self, new_messages):
def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs):
"""
Registers a callback function to be notified when a reply to `message` arrives.
Warning: In case `callback` as lambda function, saving reply handlers will not work.
:param message_id: The id of the message for which we are awaiting a reply.
:param callback: The callback function to be called when a reply arrives. Must accept one `message`
parameter, which will contain the replied message.
"""
if message_id in self.reply_handlers.keys():
self.reply_handlers[message_id].append(Handler(callback, *args, **kwargs))
else:
self.reply_handlers[message_id] = [Handler(callback, *args, **kwargs)]
if self.reply_saver is not None:
self.reply_saver.start_save_timer()
def _notify_reply_handlers(self, new_messages):
for message in new_messages:
if not message.reply_to_message:
continue
if hasattr(message, "reply_to_message") and message.reply_to_message is not None:
reply_msg_id = message.reply_to_message.message_id
if reply_msg_id in self.reply_handlers.keys():
handlers = self.reply_handlers[reply_msg_id]
for handler in handlers:
self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"])
self.reply_handlers.pop(reply_msg_id)
if self.reply_saver is not None:
self.reply_saver.start_save_timer()
reply_msg_id = message.reply_to_message.message_id
if reply_msg_id in self.message_subscribers_messages:
index = self.message_subscribers_messages.index(reply_msg_id)
self.message_subscribers_callbacks[index](message)
with self.message_subscribers_lock:
index = self.message_subscribers_messages.index(reply_msg_id)
del self.message_subscribers_messages[index]
del self.message_subscribers_callbacks[index]
def register_next_step_handler(self, message, callback):
def register_next_step_handler(self, message, callback, *args, **kwargs):
"""
Registers a callback function to be notified when new message arrives after `message`.
:param message: The message for which we want to handle new message after that in same chat.
Warning: In case `callback` as lambda function, saving next step handlers will not work.
:param message: The message for which we want to handle new message in the same chat.
:param callback: The callback function which next new message arrives.
:param args: Args to pass in callback func
:param kwargs: Args to pass in callback func
"""
chat_id = message.chat.id
if chat_id in self.pre_message_subscribers_next_step:
self.pre_message_subscribers_next_step[chat_id].append(callback)
self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs)
def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwargs):
"""
Registers a callback function to be notified when new message arrives after `message`.
Warning: In case `callback` as lambda function, saving next step handlers will not work.
:param chat_id: The chat for which we want to handle new message.
:param callback: The callback function which next new message arrives.
:param args: Args to pass in callback func
:param kwargs: Args to pass in callback func
"""
if chat_id in self.next_step_handlers.keys():
self.next_step_handlers[chat_id].append(Handler(callback, *args, **kwargs))
else:
self.pre_message_subscribers_next_step[chat_id] = [callback]
self.next_step_handlers[chat_id] = [Handler(callback, *args, **kwargs)]
if self.next_step_saver is not None:
self.next_step_saver.start_save_timer()
def clear_step_handler(self, message):
"""
@ -1078,26 +1248,61 @@ class TeleBot:
:param message: The message for which we want to handle new message after that in same chat.
"""
chat_id = message.chat.id
self.pre_message_subscribers_next_step[chat_id] = []
self.clear_step_handler_by_chat_id(chat_id)
def _notify_message_next_handler(self, new_messages):
for message in new_messages:
def clear_step_handler_by_chat_id(self, chat_id):
"""
Clears all callback functions registered by register_next_step_handler().
:param chat_id: The chat for which we want to clear next step handlers
"""
self.next_step_handlers[chat_id] = []
if self.next_step_saver is not None:
self.next_step_saver.start_save_timer()
def clear_reply_handlers(self, message):
"""
Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id().
:param message: The message for which we want to clear reply handlers
"""
message_id = message.message_id
self.clear_reply_handlers_by_message_id(message_id)
def clear_reply_handlers_by_message_id(self, message_id):
"""
Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id().
:param message_id: The message id for which we want to clear reply handlers
"""
self.reply_handlers[message_id] = []
if self.reply_saver is not None:
self.reply_saver.start_save_timer()
def _notify_next_handlers(self, new_messages):
i = 0
while i < len(new_messages):
message = new_messages[i]
chat_id = message.chat.id
if chat_id in self.message_subscribers_next_step:
handlers = self.message_subscribers_next_step[chat_id]
for handler in handlers:
self._exec_task(handler, message)
self.message_subscribers_next_step.pop(chat_id, None)
was_poped = False
if chat_id in self.next_step_handlers.keys():
handlers = copy.deepcopy(self.next_step_handlers[chat_id])
self.next_step_handlers.pop(chat_id, None)
if (handlers):
for handler in handlers:
self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"])
new_messages.pop(i) # removing message that detects with next_step_handler
was_poped = True
if self.next_step_saver is not None:
self.next_step_saver.start_save_timer()
if (not was_poped):
i += 1
def _append_pre_next_step_handler(self):
for k in self.pre_message_subscribers_next_step.keys():
if k in self.message_subscribers_next_step:
self.message_subscribers_next_step[k].extend(self.pre_message_subscribers_next_step[k])
else:
self.message_subscribers_next_step[k] = self.pre_message_subscribers_next_step[k]
self.pre_message_subscribers_next_step = {}
def _build_handler_dict(self, handler, **filters):
@staticmethod
def _build_handler_dict(handler, **filters):
return {
'function': handler,
'filters': filters
@ -1263,7 +1468,8 @@ class TeleBot:
return True
def _test_filter(self, filter, filter_value, message):
@staticmethod
def _test_filter(filter, filter_value, message):
test_cases = {
'content_types': lambda msg: msg.content_type in filter_value,
'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE),
@ -1275,9 +1481,6 @@ class TeleBot:
def _notify_command_handlers(self, handlers, new_messages):
for message in new_messages:
# if message has next step handler, dont exec command handlers
if hasattr(message, 'chat') and message.chat and (message.chat.id in self.message_subscribers_next_step):
continue
for message_handler in handlers:
if self._test_message_handler(message_handler, message):
self._exec_task(message_handler['function'], message)
@ -1288,230 +1491,256 @@ class AsyncTeleBot(TeleBot):
def __init__(self, *args, **kwargs):
TeleBot.__init__(self, *args, **kwargs)
@util.async()
@util.async_dec()
def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save", del_file_after_loading=True):
return TeleBot.enable_save_next_step_handlers(self, delay, filename, del_file_after_loading)
@util.async_dec()
def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save", del_file_after_loading=True):
return TeleBot.enable_save_reply_handlers(self, delay, filename, del_file_after_loading)
@util.async_dec()
def disable_save_next_step_handlers(self):
return TeleBot.disable_save_next_step_handlers(self)
@util.async_dec()
def disable_save_reply_handlers(self):
return TeleBot.enable_save_reply_handlers(self)
@util.async_dec()
def load_next_step_handlers(self, filename="./.handler-saves/step.save"):
return TeleBot.load_next_step_handlers(self, filename)
@util.async_dec()
def load_reply_handlers(self, filename="./.handler-saves/reply.save"):
return TeleBot.load_reply_handlers(self, filename)
@util.async_dec()
@util.async_dec()
def get_me(self):
return TeleBot.get_me(self)
@util.async()
@util.async_dec()
def get_file(self, *args):
return TeleBot.get_file(self, *args)
@util.async()
@util.async_dec()
def download_file(self, *args):
return TeleBot.download_file(self, *args)
@util.async()
@util.async_dec()
def get_user_profile_photos(self, *args, **kwargs):
return TeleBot.get_user_profile_photos(self, *args, **kwargs)
@util.async()
@util.async_dec()
def get_chat(self, *args):
return TeleBot.get_chat(self, *args)
@util.async()
@util.async_dec()
def leave_chat(self, *args):
return TeleBot.leave_chat(self, *args)
@util.async()
@util.async_dec()
def get_chat_administrators(self, *args):
return TeleBot.get_chat_administrators(self, *args)
@util.async()
@util.async_dec()
def get_chat_members_count(self, *args):
return TeleBot.get_chat_members_count(self, *args)
@util.async()
@util.async_dec()
def set_chat_sticker_set(self, *args):
return TeleBot.set_chat_sticker_set(self, *args)
@util.async()
@util.async_dec()
def delete_chat_sticker_set(self, *args):
return TeleBot.delete_chat_sticker_set(self, *args)
@util.async()
@util.async_dec()
def get_chat_member(self, *args):
return TeleBot.get_chat_member(self, *args)
@util.async()
@util.async_dec()
def send_message(self, *args, **kwargs):
return TeleBot.send_message(self, *args, **kwargs)
@util.async()
@util.async_dec()
def forward_message(self, *args, **kwargs):
return TeleBot.forward_message(self, *args, **kwargs)
@util.async()
@util.async_dec()
def delete_message(self, *args):
return TeleBot.delete_message(self, *args)
@util.async()
@util.async_dec()
def send_photo(self, *args, **kwargs):
return TeleBot.send_photo(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_audio(self, *args, **kwargs):
return TeleBot.send_audio(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_voice(self, *args, **kwargs):
return TeleBot.send_voice(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_document(self, *args, **kwargs):
return TeleBot.send_document(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_sticker(self, *args, **kwargs):
return TeleBot.send_sticker(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_video(self, *args, **kwargs):
return TeleBot.send_video(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_video_note(self, *args, **kwargs):
return TeleBot.send_video_note(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_media_group(self, *args, **kwargs):
return TeleBot.send_media_group(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_location(self, *args, **kwargs):
return TeleBot.send_location(self, *args, **kwargs)
@util.async()
@util.async_dec()
def edit_message_live_location(self, *args, **kwargs):
return TeleBot.edit_message_live_location(self, *args, **kwargs)
@util.async()
@util.async_dec()
def stop_message_live_location(self, *args, **kwargs):
return TeleBot.stop_message_live_location(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_venue(self, *args, **kwargs):
return TeleBot.send_venue(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_contact(self, *args, **kwargs):
return TeleBot.send_contact(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_chat_action(self, *args, **kwargs):
return TeleBot.send_chat_action(self, *args, **kwargs)
@util.async()
@util.async_dec()
def kick_chat_member(self, *args, **kwargs):
return TeleBot.kick_chat_member(self, *args, **kwargs)
@util.async()
@util.async_dec()
def unban_chat_member(self, *args):
return TeleBot.unban_chat_member(self, *args)
@util.async()
@util.async_dec()
def restrict_chat_member(self, *args, **kwargs):
return TeleBot.restrict_chat_member(self, *args, **kwargs)
@util.async()
@util.async_dec()
def promote_chat_member(self, *args, **kwargs):
return TeleBot.promote_chat_member(self, *args, **kwargs)
@util.async()
@util.async_dec()
def export_chat_invite_link(self, *args):
return TeleBot.export_chat_invite_link(self, *args)
@util.async()
@util.async_dec()
def set_chat_photo(self, *args):
return TeleBot.set_chat_photo(self, *args)
@util.async()
@util.async_dec()
def delete_chat_photo(self, *args):
return TeleBot.delete_chat_photo(self, *args)
@util.async()
@util.async_dec()
def set_chat_title(self, *args):
return TeleBot.set_chat_title(self, *args)
@util.async()
@util.async_dec()
def set_chat_description(self, *args):
return TeleBot.set_chat_description(self, *args)
@util.async()
@util.async_dec()
def pin_chat_message(self, *args, **kwargs):
return TeleBot.pin_chat_message(self, *args, **kwargs)
@util.async()
@util.async_dec()
def unpin_chat_message(self, *args):
return TeleBot.unpin_chat_message(self, *args)
@util.async()
@util.async_dec()
def edit_message_text(self, *args, **kwargs):
return TeleBot.edit_message_text(self, *args, **kwargs)
@util.async()
@util.async_dec()
def edit_message_reply_markup(self, *args, **kwargs):
return TeleBot.edit_message_reply_markup(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_game(self, *args, **kwargs):
return TeleBot.send_game(self, *args, **kwargs)
@util.async()
@util.async_dec()
def set_game_score(self, *args, **kwargs):
return TeleBot.set_game_score(self, *args, **kwargs)
@util.async()
@util.async_dec()
def get_game_high_scores(self, *args, **kwargs):
return TeleBot.get_game_high_scores(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_invoice(self, *args, **kwargs):
return TeleBot.send_invoice(self, *args, **kwargs)
@util.async()
@util.async_dec()
def answer_shipping_query(self, *args, **kwargs):
return TeleBot.answer_shipping_query(self, *args, **kwargs)
@util.async()
@util.async_dec()
def answer_pre_checkout_query(self, *args, **kwargs):
return TeleBot.answer_pre_checkout_query(self, *args, **kwargs)
@util.async()
@util.async_dec()
def edit_message_caption(self, *args, **kwargs):
return TeleBot.edit_message_caption(self, *args, **kwargs)
@util.async()
@util.async_dec()
def answer_inline_query(self, *args, **kwargs):
return TeleBot.answer_inline_query(self, *args, **kwargs)
@util.async()
@util.async_dec()
def answer_callback_query(self, *args, **kwargs):
return TeleBot.answer_callback_query(self, *args, **kwargs)
@util.async()
@util.async_dec()
def send_sticker(self, *args, **kwargs):
return TeleBot.send_sticker(self, *args, **kwargs)
@util.async()
@util.async_dec()
def get_sticker_set(self, *args, **kwargs):
return TeleBot.get_sticker_set(self, *args, **kwargs)
@util.async()
@util.async_dec()
def upload_sticker_file(self, *args, **kwargs):
return TeleBot.upload_sticker_file(self, *args, **kwargs)
@util.async()
@util.async_dec()
def create_new_sticker_set(self, *args, **kwargs):
return TeleBot.create_new_sticker_set(self, *args, **kwargs)
@util.async()
@util.async_dec()
def add_sticker_to_set(self, *args, **kwargs):
return TeleBot.add_sticker_to_set(self, *args, **kwargs)
@util.async()
@util.async_dec()
def set_sticker_position_in_set(self, *args, **kwargs):
return TeleBot.set_sticker_position_in_set(self, *args, **kwargs)
@util.async()
@util.async_dec()
def delete_sticker_from_set(self, *args, **kwargs):
return TeleBot.delete_sticker_from_set(self, *args, **kwargs)

View File

@ -98,9 +98,14 @@ def get_file(token, file_id):
return _make_request(token, method_url, params={'file_id': file_id})
def get_file_url(token, file_id):
method_url = r'getFile'
return FILE_URL.format(token, get_file(token, file_id).file_path)
def download_file(token, file_path):
url = FILE_URL.format(token, file_path)
result = _get_req_session().get(url)
result = _get_req_session().get(url, proxies=proxy)
if result.status_code != 200:
msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \
.format(result.status_code, result.reason, result.text)
@ -236,7 +241,7 @@ def forward_message(token, chat_id, from_chat_id, message_id, disable_notificati
def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
parse_mode=None, disable_notification=None):
method_url = r'sendPhoto'
payload = {'chat_id': chat_id}
files = None
@ -250,6 +255,8 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
return _make_request(token, method_url, params=payload, files=files, method='post')
@ -257,14 +264,14 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re
def send_media_group(token, chat_id, media, disable_notification=None, reply_to_message_id=None):
method_url = r'sendMediaGroup'
media_json = _convert_list_json_serializable(media)
print(media_json)
media_json, files = _convert_input_media(media)
payload = {'chat_id': chat_id, 'media': media_json}
if disable_notification:
payload['disable_notification'] = disable_notification
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
return _make_request(token, method_url, params=payload)
return _make_request(token, method_url, params=payload, method='post' if files else 'get',
files=files if files else None)
def send_location(token, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, reply_markup=None,
@ -272,7 +279,7 @@ def send_location(token, chat_id, latitude, longitude, live_period=None, reply_t
method_url = r'sendLocation'
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude}
if live_period:
payload['live_perion'] = live_period
payload['live_period'] = live_period
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
@ -349,7 +356,7 @@ def send_chat_action(token, chat_id, action):
def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None):
method_url = r'sendVideo'
payload = {'chat_id': chat_id}
files = None
@ -365,6 +372,10 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if supports_streaming:
payload['supports_streaming'] = supports_streaming
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
@ -373,7 +384,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
parse_mode=None, disable_notification=None, timeout=None):
method_url = r'sendVoice'
payload = {'chat_id': chat_id}
files = None
@ -389,6 +400,8 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
@ -423,7 +436,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m
def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None,
reply_markup=None, disable_notification=None, timeout=None):
reply_markup=None, parse_mode=None, disable_notification=None, timeout=None):
method_url = r'sendAudio'
payload = {'chat_id': chat_id}
files = None
@ -443,6 +456,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
@ -450,8 +465,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, disable_notification=None,
timeout=None, caption=None):
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):
method_url = get_method_by_type(data_type)
payload = {'chat_id': chat_id}
files = None
@ -463,6 +478,8 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode and data_type == 'document':
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
@ -604,7 +621,8 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message
return _make_request(token, method_url, params=payload)
def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None,
parse_mode=None, reply_markup=None):
method_url = r'editMessageCaption'
payload = {'caption': caption}
if chat_id:
@ -613,6 +631,8 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m
payload['message_id'] = message_id
if inline_message_id:
payload['inline_message_id'] = inline_message_id
if parse_mode:
payload['parse_mode'] = parse_mode
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload)
@ -827,7 +847,7 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None,
payload['show_alert'] = show_alert
if url:
payload['url'] = url
if cache_time:
if cache_time is not None:
payload['cache_time'] = cache_time
return _make_request(token, method_url, params=payload, method='post')
@ -836,7 +856,7 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per
switch_pm_text=None, switch_pm_parameter=None):
method_url = 'answerInlineQuery'
payload = {'inline_query_id': inline_query_id, 'results': _convert_list_json_serializable(results)}
if cache_time:
if cache_time is not None:
payload['cache_time'] = cache_time
if is_personal:
payload['is_personal'] = is_personal
@ -917,10 +937,23 @@ def _convert_markup(markup):
return markup
def _convert_input_media(array):
media = []
files = {}
for input_media in array:
if isinstance(input_media, types.JsonSerializable):
media_dict = input_media.to_dic()
if media_dict['media'].startswith('attach://'):
key = media_dict['media'].replace('attach://', '')
files[key] = input_media.media
media.append(media_dict)
return json.dumps(media), files
def _no_encode(func):
def wrapper(key, val):
if key == 'filename':
return '{0}={1}'.format(key, val)
return u'{0}={1}'.format(key, val)
else:
return func(key, val)

View File

@ -127,7 +127,6 @@ class Update(JsonDeserializable):
def __init__(self, update_id, message, edited_message, channel_post, edited_channel_post, inline_query,
chosen_inline_result, callback_query, shipping_query, pre_checkout_query):
self.update_id = update_id
self.edited_message = edited_message
self.message = message
self.edited_message = edited_message
self.channel_post = channel_post
@ -274,6 +273,8 @@ class Message(JsonDeserializable):
opts['reply_to_message'] = Message.de_json(obj['reply_to_message'])
if 'edit_date' in obj:
opts['edit_date'] = obj.get('edit_date')
if 'media_group_id' in obj:
opts['media_group_id'] = obj.get('media_group_id')
if 'author_signature' in obj:
opts['author_signature'] = obj.get('author_signature')
if 'text' in obj:
@ -333,10 +334,13 @@ class Message(JsonDeserializable):
content_type = 'left_chat_member'
if 'new_chat_title' in obj:
opts['new_chat_title'] = obj['new_chat_title']
content_type = 'new_chat_title'
if 'new_chat_photo' in obj:
opts['new_chat_photo'] = Message.parse_photo(obj['new_chat_photo'])
content_type = 'new_chat_photo'
if 'delete_chat_photo' in obj:
opts['delete_chat_photo'] = obj['delete_chat_photo']
content_type = 'delete_chat_photo'
if 'group_chat_created' in obj:
opts['group_chat_created'] = obj['group_chat_created']
content_type = 'group_chat_created'
@ -348,8 +352,10 @@ class Message(JsonDeserializable):
content_type = 'channel_chat_created'
if 'migrate_to_chat_id' in obj:
opts['migrate_to_chat_id'] = obj['migrate_to_chat_id']
content_type = 'migrate_to_chat_id'
if 'migrate_from_chat_id' in obj:
opts['migrate_from_chat_id'] = obj['migrate_from_chat_id']
content_type = 'migrate_from_chat_id'
if 'pinned_message' in obj:
opts['pinned_message'] = Message.de_json(obj['pinned_message'])
content_type = 'pinned_message'
@ -359,7 +365,10 @@ class Message(JsonDeserializable):
if 'successful_payment' in obj:
opts['successful_payment'] = SuccessfulPayment.de_json(obj['successful_payment'])
content_type = 'successful_payment'
return cls(message_id, from_user, date, chat, content_type, opts)
if 'connected_website' in obj:
opts['connected_website'] = obj['connected_website']
content_type = 'connected_website'
return cls(message_id, from_user, date, chat, content_type, opts, json_string)
@classmethod
def parse_chat(cls, chat):
@ -382,7 +391,7 @@ class Message(JsonDeserializable):
ret.append(MessageEntity.de_json(me))
return ret
def __init__(self, message_id, from_user, date, chat, content_type, options):
def __init__(self, message_id, from_user, date, chat, content_type, options, json_string):
self.content_type = content_type
self.message_id = message_id
self.from_user = from_user
@ -393,6 +402,7 @@ class Message(JsonDeserializable):
self.forward_date = None
self.reply_to_message = None
self.edit_date = None
self.media_group_id = None
self.author_signature = None
self.text = None
self.entities = None
@ -422,9 +432,74 @@ class Message(JsonDeserializable):
self.pinned_message = None
self.invoice = None
self.successful_payment = None
self.connected_website = None
for key in options:
setattr(self, key, options[key])
self.json = json_string
def __html_text(self, text, entities):
"""
Author: @sviat9440
Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username"
Example:
message.html_text
>> "<b>Test</b> parse <i>formatting</i>, <a href=\"https://example.com\">url</a>, <a href=\"tg://user?id=123456\">text_mention</a> and mention @username"
Cusom subs:
You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity.
Example:
message.custom_subs = {"bold": "<strong class=\"example\">{text}</strong>", "italic": "<i class=\"example\">{text}</i>", "mention": "<a href={url}>{text}</a>"}
message.html_text
>> "<strong class=\"example\">Test</strong> parse <i class=\"example\">formatting</i>, <a href=\"https://example.com\">url</a> and <a href=\"tg://user?id=123456\">text_mention</a> and mention <a href=\"https://t.me/username\">@username</a>"
"""
if not entities:
return text
_subs = {
"bold": "<b>{text}</b>",
"italic": "<i>{text}</i>",
"pre": "<pre>{text}</pre>",
"code": "<code>{text}</code>",
"url": "<a href=\"{url}\">{text}</a>",
"text_link": "<a href=\"{url}\">{text}</a>"
}
if hasattr(self, "custom_subs"):
for type in self.custom_subs:
_subs[type] = self.custom_subs[type]
utf16_text = text.encode("utf-16-le")
html_text = ""
def func(text, type=None, url=None, user=None):
text = text.decode("utf-16-le")
if type == "text_mention":
type = "url"
url = "tg://user?id={0}".format(user.id)
elif type == "mention":
url = "https://t.me/{0}".format(text[1:])
if not type or not _subs.get(type):
return text
subs = _subs.get(type)
text = text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
return subs.format(text=text, url=url)
offset = 0
for entity in entities:
if entity.offset > offset:
html_text += func(utf16_text[offset * 2 : entity.offset * 2])
offset = entity.offset
html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user)
offset += entity.length
if offset * 2 < len(utf16_text):
html_text += func(utf16_text[offset * 2:])
return html_text
@property
def html_text(self):
return self.__html_text(self.text, self.entities)
@property
def html_caption(self):
return self.__html_text(self.caption, self.caption_entities)
class MessageEntity(JsonDeserializable):
@classmethod
@ -1116,7 +1191,7 @@ class InlineQueryResultArticle(JsonSerializable):
class InlineQueryResultPhoto(JsonSerializable):
def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=None,
description=None, caption=None, reply_markup=None, input_message_content=None):
description=None, caption=None, parse_mode=None, reply_markup=None, input_message_content=None):
"""
Represents a link to a photo.
:param id: Unique identifier for this result, 1-64 bytes
@ -1127,6 +1202,8 @@ class InlineQueryResultPhoto(JsonSerializable):
:param title: Title for the result.
:param description: Short description of the result.
:param caption: Caption of the photo to be sent, 0-200 characters.
:param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or
inline URLs in the media caption.
:param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message
:param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo
:return:
@ -1140,6 +1217,7 @@ class InlineQueryResultPhoto(JsonSerializable):
self.title = title
self.description = description
self.caption = caption
self.parse_mode = parse_mode
self.reply_markup = reply_markup
self.input_message_content = input_message_content
@ -1155,6 +1233,8 @@ class InlineQueryResultPhoto(JsonSerializable):
json_dict['description'] = self.description
if self.caption:
json_dict['caption'] = self.caption
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.reply_markup:
json_dict['reply_markup'] = self.reply_markup.to_dic()
if self.input_message_content:
@ -1211,7 +1291,7 @@ class InlineQueryResultGif(JsonSerializable):
class InlineQueryResultMpeg4Gif(JsonSerializable):
def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None, title=None, caption=None,
reply_markup=None, input_message_content=None, mpeg4_duration=None):
parse_mode=None, reply_markup=None, input_message_content=None, mpeg4_duration=None):
"""
Represents a link to a video animation (H.264/MPEG-4 AVC video without sound).
:param id: Unique identifier for this result, 1-64 bytes
@ -1221,6 +1301,8 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
:param mpeg4_height: Video height
:param title: Title for the result
:param caption: Caption of the MPEG-4 file to be sent, 0-200 characters
:param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text
or inline URLs in the media caption.
:param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message
:param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo
:return:
@ -1233,6 +1315,7 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
self.thumb_url = thumb_url
self.title = title
self.caption = caption
self.parse_mode = parse_mode
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.mpeg4_duration = mpeg4_duration
@ -1247,6 +1330,8 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
json_dict['title'] = self.title
if self.caption:
json_dict['caption'] = self.caption
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.reply_markup:
json_dict['reply_markup'] = self.reply_markup.to_dic()
if self.input_message_content:
@ -1258,8 +1343,8 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
class InlineQueryResultVideo(JsonSerializable):
def __init__(self, id, video_url, mime_type, thumb_url, title,
caption=None, video_width=None, video_height=None, video_duration=None, description=None,
reply_markup=None, input_message_content=None):
caption=None, parse_mode=None, video_width=None, video_height=None, video_duration=None,
description=None, reply_markup=None, input_message_content=None):
"""
Represents link to a page containing an embedded video player or a video file.
:param id: Unique identifier for this result, 1-64 bytes
@ -1267,6 +1352,8 @@ class InlineQueryResultVideo(JsonSerializable):
:param mime_type: Mime type of the content of video url, “text/html” or “video/mp4”
:param thumb_url: URL of the thumbnail (jpeg only) for the video
:param title: Title for the result
:param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or
inline URLs in the media caption.
:param video_width: Video width
:param video_height: Video height
:param video_duration: Video duration in seconds
@ -1283,6 +1370,7 @@ class InlineQueryResultVideo(JsonSerializable):
self.thumb_url = thumb_url
self.title = title
self.caption = caption
self.parse_mode = parse_mode
self.description = description
self.input_message_content = input_message_content
self.reply_markup = reply_markup
@ -1300,6 +1388,8 @@ class InlineQueryResultVideo(JsonSerializable):
json_dict['description'] = self.description
if self.caption:
json_dict['caption'] = self.caption
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.reply_markup:
json_dict['reply_markup'] = self.reply_markup.to_dic()
if self.input_message_content:
@ -1308,13 +1398,14 @@ class InlineQueryResultVideo(JsonSerializable):
class InlineQueryResultAudio(JsonSerializable):
def __init__(self, id, audio_url, title, caption=None, performer=None, audio_duration=None, reply_markup=None,
input_message_content=None):
def __init__(self, id, audio_url, title, caption=None, parse_mode=None, performer=None, audio_duration=None,
reply_markup=None, input_message_content=None):
self.type = 'audio'
self.id = id
self.audio_url = audio_url
self.title = title
self.caption = caption
self.parse_mode = parse_mode
self.performer = performer
self.audio_duration = audio_duration
self.reply_markup = reply_markup
@ -1324,6 +1415,8 @@ class InlineQueryResultAudio(JsonSerializable):
json_dict = {'type': self.type, 'id': self.id, 'audio_url': self.audio_url, 'title': self.title}
if self.caption:
json_dict['caption'] = self.caption
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.performer:
json_dict['performer'] = self.performer
if self.audio_duration:
@ -1336,13 +1429,14 @@ class InlineQueryResultAudio(JsonSerializable):
class InlineQueryResultVoice(JsonSerializable):
def __init__(self, id, voice_url, title, caption=None, performer=None, voice_duration=None, reply_markup=None,
input_message_content=None):
def __init__(self, id, voice_url, title, caption=None, parse_mode=None, performer=None, voice_duration=None,
reply_markup=None, input_message_content=None):
self.type = 'voice'
self.id = id
self.voice_url = voice_url
self.title = title
self.caption = caption
self.parse_mode = parse_mode
self.performer = performer
self.voice_duration = voice_duration
self.reply_markup = reply_markup
@ -1352,6 +1446,8 @@ class InlineQueryResultVoice(JsonSerializable):
json_dict = {'type': self.type, 'id': self.id, 'voice_url': self.voice_url, 'title': self.title}
if self.caption:
json_dict['caption'] = self.caption
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.performer:
json_dict['performer'] = self.performer
if self.voice_duration:
@ -1364,14 +1460,15 @@ class InlineQueryResultVoice(JsonSerializable):
class InlineQueryResultDocument(JsonSerializable):
def __init__(self, id, title, document_url, mime_type, caption=None, description=None, reply_markup=None,
input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None):
def __init__(self, id, title, document_url, mime_type, caption=None, parse_mode=None, description=None,
reply_markup=None, input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None):
self.type = 'document'
self.id = id
self.title = title
self.document_url = document_url
self.mime_type = mime_type
self.caption = caption
self.parse_mode = parse_mode
self.description = description
self.reply_markup = reply_markup
self.input_message_content = input_message_content
@ -1384,6 +1481,8 @@ class InlineQueryResultDocument(JsonSerializable):
'mime_type': self.mime_type}
if self.caption:
json_dict['caption'] = self.caption
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.description:
json_dict['description'] = self.description
if self.thumb_url:
@ -1526,8 +1625,8 @@ class BaseInlineQueryResultCached(JsonSerializable):
class InlineQueryResultCachedPhoto(BaseInlineQueryResultCached):
def __init__(self, id, photo_file_id, title=None, description=None, caption=None, reply_markup=None,
input_message_content=None):
def __init__(self, id, photo_file_id, title=None, description=None, caption=None, parse_mode=None,
reply_markup=None, input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'photo'
self.id = id
@ -1538,10 +1637,11 @@ class InlineQueryResultCachedPhoto(BaseInlineQueryResultCached):
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.payload_dic['photo_file_id'] = photo_file_id
self.payload_dic['parse_mode'] = parse_mode
class InlineQueryResultCachedGif(BaseInlineQueryResultCached):
def __init__(self, id, gif_file_id, title=None, description=None, caption=None, reply_markup=None,
def __init__(self, id, gif_file_id, title=None, description=None, caption=None, parse_mode=None, reply_markup=None,
input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'gif'
@ -1553,11 +1653,12 @@ class InlineQueryResultCachedGif(BaseInlineQueryResultCached):
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.payload_dic['gif_file_id'] = gif_file_id
self.payload_dic['parse_mode'] = parse_mode
class InlineQueryResultCachedMpeg4Gif(BaseInlineQueryResultCached):
def __init__(self, id, mpeg4_file_id, title=None, description=None, caption=None, reply_markup=None,
input_message_content=None):
def __init__(self, id, mpeg4_file_id, title=None, description=None, caption=None, parse_mode=None,
reply_markup=None, input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'mpeg4_gif'
self.id = id
@ -1568,6 +1669,7 @@ class InlineQueryResultCachedMpeg4Gif(BaseInlineQueryResultCached):
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.payload_dic['mpeg4_file_id'] = mpeg4_file_id
self.payload_dic['parse_mode'] = parse_mode
class InlineQueryResultCachedSticker(BaseInlineQueryResultCached):
@ -1582,7 +1684,7 @@ class InlineQueryResultCachedSticker(BaseInlineQueryResultCached):
class InlineQueryResultCachedDocument(BaseInlineQueryResultCached):
def __init__(self, id, document_file_id, title, description=None, caption=None, reply_markup=None,
def __init__(self, id, document_file_id, title, description=None, caption=None, parse_mode=None, reply_markup=None,
input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'document'
@ -1594,10 +1696,11 @@ class InlineQueryResultCachedDocument(BaseInlineQueryResultCached):
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.payload_dic['document_file_id'] = document_file_id
self.payload_dic['parse_mode'] = parse_mode
class InlineQueryResultCachedVideo(BaseInlineQueryResultCached):
def __init__(self, id, video_file_id, title, description=None, caption=None, reply_markup=None,
def __init__(self, id, video_file_id, title, description=None, caption=None, parse_mode=None, reply_markup=None,
input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'video'
@ -1609,10 +1712,12 @@ class InlineQueryResultCachedVideo(BaseInlineQueryResultCached):
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.payload_dic['video_file_id'] = video_file_id
self.payload_dic['parse_mode'] = parse_mode
class InlineQueryResultCachedVoice(BaseInlineQueryResultCached):
def __init__(self, id, voice_file_id, title, caption=None, reply_markup=None, input_message_content=None):
def __init__(self, id, voice_file_id, title, caption=None, parse_mode=None, reply_markup=None,
input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'voice'
self.id = id
@ -1622,10 +1727,11 @@ class InlineQueryResultCachedVoice(BaseInlineQueryResultCached):
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.payload_dic['voice_file_id'] = voice_file_id
self.payload_dic['parse_mode'] = parse_mode
class InlineQueryResultCachedAudio(BaseInlineQueryResultCached):
def __init__(self, id, audio_file_id, caption=None, reply_markup=None, input_message_content=None):
def __init__(self, id, audio_file_id, caption=None, parse_mode=None, reply_markup=None, input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'audio'
self.id = id
@ -1634,6 +1740,7 @@ class InlineQueryResultCachedAudio(BaseInlineQueryResultCached):
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.payload_dic['audio_file_id'] = audio_file_id
self.payload_dic['parse_mode'] = parse_mode
# Games
@ -1809,7 +1916,7 @@ class ShippingOption(JsonSerializable):
def add_price(self, *args):
"""
Add LabeledPrice to ShippingOption
:param args: LabeledPrices
:param args: LabeledPrices
"""
for price in args:
self.prices.append(price)
@ -1967,41 +2074,53 @@ class MaskPosition(JsonDeserializable, JsonSerializable):
# InputMedia
class InputMediaPhoto(JsonSerializable):
def __init__(self, media, caption=None):
def __init__(self, media, caption=None, parse_mode=None):
self.type = "photo"
self.media = media
self.caption = caption
self.parse_mode = parse_mode
def to_json(self):
return json.dumps(self.to_dic())
def to_dic(self):
ret = {'type': self.type, 'media': self.media}
ret = {'type': self.type, 'media': 'attach://' + util.generate_random_token()
if not util.is_string(self.media) else self.media}
if self.caption:
ret['caption'] = self.caption
if self.parse_mode:
ret['parse_mode'] = self.parse_mode
return ret
class InputMediaVideo(JsonSerializable):
def __init__(self, media, caption=None, width=None, height=None, duration=None):
def __init__(self, media, caption=None, parse_mode=None, width=None, height=None, duration=None,
supports_streaming=None):
self.type = "video"
self.media = media
self.caption = caption
self.parse_mode = parse_mode
self.width = width
self.height = height
self.duration = duration
self.supports_streaming = supports_streaming
def to_json(self):
return json.dumps(self.to_dic())
def to_dic(self):
ret = {'type': self.type, 'media': self.media}
ret = {'type': self.type, 'media': 'attach://' + util.generate_random_token()
if not util.is_string(self.media) else self.media}
if self.caption:
ret['caption'] = self.caption
if self.parse_mode:
ret['parse_mode'] = self.parse_mode
if self.width:
ret['width'] = self.width
if self.height:
ret['height'] = self.height
if self.duration:
ret['duration'] = self.duration
if self.supports_streaming:
ret['supports_streaming'] = self.supports_streaming
return ret

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import random
import string
import threading
import traceback
import re
@ -13,9 +14,9 @@ try:
import Queue
except ImportError:
import queue as Queue
import logging
from telebot import logger
logger = logging.getLogger('TeleBot')
thread_local = threading.local()
@ -142,7 +143,7 @@ class AsyncTask:
return self.result
def async():
def async_dec():
def decorator(fn):
def wrapper(*args, **kwargs):
return AsyncTask(fn, *args, **kwargs)
@ -254,3 +255,7 @@ def per_thread(key, construct_value):
value = construct_value()
setattr(thread_local, key, value)
return value
def generate_random_token():
return ''.join(random.sample(string.ascii_letters, 16))

View File

@ -391,7 +391,7 @@ class TestTeleBot:
def create_text_message(self, text):
params = {'text': text}
chat = types.User(11, False, 'test')
return types.Message(1, None, None, chat, 'text', params)
return types.Message(1, None, None, chat, 'text', params, "")
def test_is_string_unicode(self):
s1 = u'string'
@ -418,3 +418,59 @@ class TestTeleBot:
medias = [types.InputMediaPhoto(img1, "View"), types.InputMediaPhoto(img2, "Dog")]
result = tb.send_media_group(CHAT_ID, medias)
assert len(result) == 2
assert result[0].media_group_id is not None
assert result[0].media_group_id == result[1].media_group_id
def test_send_media_group_local_files(self):
photo = open('../examples/detailed_example/kitten.jpg', 'rb')
video = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN)
medias = [types.InputMediaPhoto(photo, "View"),
types.InputMediaVideo(video)]
result = tb.send_media_group(CHAT_ID, medias)
assert len(result) == 2
assert result[0].media_group_id is not None
assert result[1].media_group_id is not None
def test_send_photo_formating_caption(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_photo(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
assert ret_msg.caption_entities[0].type == 'italic'
def test_send_video_formatting_caption(self):
file_data = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_video(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
assert ret_msg.caption_entities[0].type == 'italic'
def test_send_audio_formatting_caption(self):
file_data = open('./test_data/record.mp3', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_audio(CHAT_ID, file_data, caption='<b>bold</b>', parse_mode='HTML')
assert ret_msg.caption_entities[0].type == 'bold'
def test_send_voice_formatting_caprion(self):
file_data = open('./test_data/record.ogg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_voice(CHAT_ID, file_data, caption='<b>bold</b>', parse_mode='HTML')
assert ret_msg.caption_entities[0].type == 'bold'
assert ret_msg.voice.mime_type == 'audio/ogg'
def test_send_media_group_formatting_caption(self):
tb = telebot.TeleBot(TOKEN)
img1 = 'https://i.imgur.com/CjXjcnU.png'
img2 = 'https://i.imgur.com/CjXjcnU.png'
medias = [types.InputMediaPhoto(img1, "*View*", parse_mode='Markdown'),
types.InputMediaPhoto(img2, "_Dog_", parse_mode='Markdown')]
result = tb.send_media_group(CHAT_ID, medias)
assert len(result) == 2
assert result[0].media_group_id is not None
assert result[0].caption_entities[0].type == 'bold'
assert result[1].caption_entities[0].type == 'italic'
def test_send_document_formating_caption(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_document(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
assert ret_msg.caption_entities[0].type == 'italic'