mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-23 06:50:29 +01:00
merge master into inlinebots
This commit is contained in:
commit
a383cee558
9 changed files with 255 additions and 35 deletions
|
@ -16,10 +16,12 @@ The following wonderful people contributed directly or indirectly to this projec
|
|||
- `JASON0916 <https://github.com/JASON0916>`_
|
||||
- `jh0ker <https://github.com/jh0ker>`_
|
||||
- `JRoot3D <https://github.com/JRoot3D>`_
|
||||
- `jlmadurga <https://github.com/jlmadurga>`_
|
||||
- `macrojames <https://github.com/macrojames>`_
|
||||
- `naveenvhegde <https://github.com/naveenvhegde>`_
|
||||
- `njittam <https://github.com/njittam>`_
|
||||
- `Noam Meltzer <https://github.com/tsnoam>`_
|
||||
- `Oleg Shlyazhko <https://github.com/ollmer>`_
|
||||
- `Rahiel Kasim <https://github.com/rahiel>`_
|
||||
- `sooyhwang <https://github.com/sooyhwang>`_
|
||||
- `wjt <https://github.com/wjt>`_
|
||||
|
|
|
@ -51,8 +51,10 @@ from .update import Update
|
|||
from .bot import Bot
|
||||
from .dispatcher import Dispatcher
|
||||
from .jobqueue import JobQueue
|
||||
from .updatequeue import UpdateQueue
|
||||
from .updater import Updater
|
||||
|
||||
|
||||
__author__ = 'devs@python-telegram-bot.org'
|
||||
__version__ = '3.3b1'
|
||||
__all__ = ('Bot', 'Updater', 'Dispatcher', 'Emoji', 'TelegramError',
|
||||
|
@ -63,4 +65,5 @@ __all__ = ('Bot', 'Updater', 'Dispatcher', 'Emoji', 'TelegramError',
|
|||
'User', 'TelegramObject', 'NullHandler', 'Voice', 'JobQueue',
|
||||
'InlineQuery', 'ChosenInlineResult', 'InlineQueryResultArticle',
|
||||
'InlineQueryResultGif', 'InlineQueryResultPhoto',
|
||||
'InlineQueryResultMpeg4Gif', 'InlineQueryResultVideo')
|
||||
'InlineQueryResultMpeg4Gif', 'InlineQueryResultVideo',
|
||||
'UpdateQueue')
|
||||
|
|
|
@ -110,6 +110,13 @@ class Dispatcher:
|
|||
whole query split on spaces.
|
||||
For other updates, args will be None
|
||||
|
||||
In some cases handlers may need some context data to process the update. To
|
||||
procedure just queue in update_queue.put(update, context=context) or
|
||||
processUpdate(update,context=context).
|
||||
|
||||
context:
|
||||
Extra data for handling updates.
|
||||
|
||||
For regex-based handlers, you can also request information about the match.
|
||||
For all other handlers, these will be None
|
||||
|
||||
|
@ -123,7 +130,7 @@ class Dispatcher:
|
|||
Args:
|
||||
bot (telegram.Bot): The bot object that should be passed to the
|
||||
handlers
|
||||
update_queue (queue.Queue): The synchronized queue that will
|
||||
update_queue (UpdateQueue): The synchronized queue that will
|
||||
contain the updates.
|
||||
"""
|
||||
def __init__(self, bot, update_queue, workers=4):
|
||||
|
@ -174,14 +181,15 @@ class Dispatcher:
|
|||
try:
|
||||
# Pop update from update queue.
|
||||
# Blocks if no updates are available.
|
||||
update = self.update_queue.get()
|
||||
update, context = self.update_queue.get(context=True)
|
||||
|
||||
if type(update) is self._Stop:
|
||||
self.running = False
|
||||
break
|
||||
|
||||
self.processUpdate(update)
|
||||
self.logger.debug('Processed Update: %s' % update)
|
||||
self.processUpdate(update, context)
|
||||
self.logger.debug('Processed Update: %s with context %s'
|
||||
% (update, context))
|
||||
|
||||
# Dispatch any errors
|
||||
except TelegramError as te:
|
||||
|
@ -207,7 +215,7 @@ class Dispatcher:
|
|||
while self.running:
|
||||
sleep(0.1)
|
||||
|
||||
def processUpdate(self, update):
|
||||
def processUpdate(self, update, context=None):
|
||||
"""
|
||||
Processes a single update.
|
||||
|
||||
|
@ -220,15 +228,15 @@ class Dispatcher:
|
|||
# Custom type handlers
|
||||
for t in self.type_handlers:
|
||||
if isinstance(update, t):
|
||||
self.dispatchType(update)
|
||||
self.dispatchType(update, context)
|
||||
handled = True
|
||||
|
||||
# string update
|
||||
if type(update) is str and update.startswith('/'):
|
||||
self.dispatchStringCommand(update)
|
||||
self.dispatchStringCommand(update, context)
|
||||
handled = True
|
||||
elif type(update) is str:
|
||||
self.dispatchRegex(update)
|
||||
self.dispatchRegex(update, context)
|
||||
handled = True
|
||||
|
||||
# An error happened while polling
|
||||
|
@ -238,21 +246,21 @@ class Dispatcher:
|
|||
|
||||
# Telegram update (regex)
|
||||
if isinstance(update, Update) and update.message is not None:
|
||||
self.dispatchRegex(update)
|
||||
self.dispatchRegex(update, context)
|
||||
handled = True
|
||||
|
||||
# Telegram update (command)
|
||||
if update.message.text.startswith('/'):
|
||||
self.dispatchTelegramCommand(update)
|
||||
self.dispatchTelegramCommand(update, context)
|
||||
|
||||
# Telegram update (message)
|
||||
else:
|
||||
self.dispatchTelegramMessage(update)
|
||||
self.dispatchTelegramMessage(update, context)
|
||||
handled = True
|
||||
elif isinstance(update, Update) and \
|
||||
(update.inline_query is not None or
|
||||
update.chosen_inline_result is not None):
|
||||
self.dispatchTelegramInline(update)
|
||||
self.dispatchTelegramInline(update, context)
|
||||
handled = True
|
||||
# Update not recognized
|
||||
if not handled:
|
||||
|
@ -519,7 +527,7 @@ class Dispatcher:
|
|||
and handler in self.type_handlers[the_type]:
|
||||
self.type_handlers[the_type].remove(handler)
|
||||
|
||||
def dispatchTelegramCommand(self, update):
|
||||
def dispatchTelegramCommand(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update that contains a command.
|
||||
|
||||
|
@ -532,11 +540,13 @@ class Dispatcher:
|
|||
command = update.message.text.split(' ')[0][1:].split('@')[0]
|
||||
|
||||
if command in self.telegram_command_handlers:
|
||||
self.dispatchTo(self.telegram_command_handlers[command], update)
|
||||
self.dispatchTo(self.telegram_command_handlers[command], update,
|
||||
context=context)
|
||||
else:
|
||||
self.dispatchTo(self.unknown_telegram_command_handlers, update)
|
||||
self.dispatchTo(self.unknown_telegram_command_handlers, update,
|
||||
context=context)
|
||||
|
||||
def dispatchRegex(self, update):
|
||||
def dispatchRegex(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update to all string or telegram regex handlers that
|
||||
match the string/message content.
|
||||
|
@ -559,9 +569,10 @@ class Dispatcher:
|
|||
self.call_handler(handler,
|
||||
update,
|
||||
groups=m.groups(),
|
||||
groupdict=m.groupdict())
|
||||
groupdict=m.groupdict(),
|
||||
context=context)
|
||||
|
||||
def dispatchStringCommand(self, update):
|
||||
def dispatchStringCommand(self, update, context=None):
|
||||
"""
|
||||
Dispatches a string-update that contains a command.
|
||||
|
||||
|
@ -572,11 +583,13 @@ class Dispatcher:
|
|||
command = update.split(' ')[0][1:]
|
||||
|
||||
if command in self.string_command_handlers:
|
||||
self.dispatchTo(self.string_command_handlers[command], update)
|
||||
self.dispatchTo(self.string_command_handlers[command], update,
|
||||
context=context)
|
||||
else:
|
||||
self.dispatchTo(self.unknown_string_command_handlers, update)
|
||||
self.dispatchTo(self.unknown_string_command_handlers, update,
|
||||
context=context)
|
||||
|
||||
def dispatchType(self, update):
|
||||
def dispatchType(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update of any type.
|
||||
|
||||
|
@ -586,9 +599,9 @@ class Dispatcher:
|
|||
|
||||
for t in self.type_handlers:
|
||||
if isinstance(update, t):
|
||||
self.dispatchTo(self.type_handlers[t], update)
|
||||
self.dispatchTo(self.type_handlers[t], update, context=context)
|
||||
|
||||
def dispatchTelegramMessage(self, update):
|
||||
def dispatchTelegramMessage(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update that contains a regular message.
|
||||
|
||||
|
@ -597,9 +610,10 @@ class Dispatcher:
|
|||
message.
|
||||
"""
|
||||
|
||||
self.dispatchTo(self.telegram_message_handlers, update)
|
||||
self.dispatchTo(self.telegram_message_handlers, update,
|
||||
context=context)
|
||||
|
||||
def dispatchTelegramInline(self, update):
|
||||
def dispatchTelegramInline(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update that contains an inline update.
|
||||
|
||||
|
@ -608,7 +622,7 @@ class Dispatcher:
|
|||
message.
|
||||
"""
|
||||
|
||||
self.dispatchTo(self.telegram_inline_handlers, update)
|
||||
self.dispatchTo(self.telegram_inline_handlers, update, context=None)
|
||||
|
||||
def dispatchError(self, update, error):
|
||||
"""
|
||||
|
@ -675,4 +689,7 @@ class Dispatcher:
|
|||
if is_async or 'groupdict' in fargs:
|
||||
target_kwargs['groupdict'] = kwargs.get('groupdict', None)
|
||||
|
||||
if is_async or 'context' in fargs:
|
||||
target_kwargs['context'] = kwargs.get('context', None)
|
||||
|
||||
handler(self.bot, update, **target_kwargs)
|
||||
|
|
59
telegram/updatequeue.py
Normal file
59
telegram/updatequeue.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains the class UpdateQueue to override standard
|
||||
Queue."""
|
||||
|
||||
|
||||
# Adjust for differences in Python versions
|
||||
try:
|
||||
from queue import Queue
|
||||
except ImportError:
|
||||
from Queue import Queue
|
||||
|
||||
|
||||
class UpdateQueue(Queue):
|
||||
"""
|
||||
This class overrides standard Queues. Allows you to de/queue context
|
||||
data apart from the handled `update`
|
||||
"""
|
||||
|
||||
def put(self, item, block=True, timeout=None, context=None):
|
||||
"""
|
||||
Put an item into the queue with context data if provided as a
|
||||
tuple (item, context). Overrides standard Queue.put method.
|
||||
|
||||
Args:
|
||||
update (any): handled by the dispatcher
|
||||
context (any): extra data to use in handlers
|
||||
"""
|
||||
Queue.put(self, (item, context), block, timeout)
|
||||
|
||||
def get(self, block=True, timeout=None, context=False):
|
||||
"""
|
||||
Remove and return an item from the queue. A tuple of
|
||||
(update, context) if requested. Overrides standard Queue.get
|
||||
method.
|
||||
|
||||
Args:
|
||||
context (boolean): set true to get (update, context)
|
||||
"""
|
||||
if not context:
|
||||
return Queue.get(self, block, timeout)[0]
|
||||
return Queue.get(self, block, timeout)
|
|
@ -29,15 +29,9 @@ from time import sleep
|
|||
import subprocess
|
||||
from signal import signal, SIGINT, SIGTERM, SIGABRT
|
||||
from telegram import (Bot, TelegramError, dispatcher, Dispatcher,
|
||||
NullHandler, JobQueue)
|
||||
NullHandler, JobQueue, UpdateQueue)
|
||||
from telegram.utils.webhookhandler import (WebhookServer, WebhookHandler)
|
||||
|
||||
# Adjust for differences in Python versions
|
||||
try:
|
||||
from Queue import Queue
|
||||
except ImportError:
|
||||
from queue import Queue
|
||||
|
||||
try:
|
||||
from urllib2 import URLError
|
||||
except ImportError:
|
||||
|
@ -89,7 +83,7 @@ class Updater:
|
|||
self.bot = bot
|
||||
else:
|
||||
self.bot = Bot(token, base_url)
|
||||
self.update_queue = Queue()
|
||||
self.update_queue = UpdateQueue()
|
||||
self.job_queue = JobQueue(self.bot, job_queue_tick_interval)
|
||||
self.dispatcher = Dispatcher(self.bot, self.update_queue,
|
||||
workers=workers)
|
||||
|
|
55
telegram/utils/botan.py
Normal file
55
telegram/utils/botan.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import json
|
||||
import logging
|
||||
from telegram import NullHandler
|
||||
|
||||
try:
|
||||
from urllib.request import urlopen, Request
|
||||
from urllib.parse import quote
|
||||
from urllib.error import URLError, HTTPError
|
||||
except ImportError:
|
||||
from urllib2 import urlopen, Request
|
||||
from urllib import quote
|
||||
from urllib2 import URLError, HTTPError
|
||||
|
||||
H = NullHandler()
|
||||
logging.getLogger(__name__).addHandler(H)
|
||||
|
||||
|
||||
class Botan(object):
|
||||
"""This class helps to send incoming events in your botan analytics account.
|
||||
See more: https://github.com/botanio/sdk#botan-sdk"""
|
||||
|
||||
token = ''
|
||||
url_template = 'https://api.botan.io/track?token={token}' \
|
||||
'&uid={uid}&name={name}&src=python-telegram-bot'
|
||||
|
||||
def __init__(self, token):
|
||||
self.token = token
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
def track(self, message, event_name='event'):
|
||||
try:
|
||||
uid = message.chat_id
|
||||
except AttributeError:
|
||||
self.logger.warn('No chat_id in message')
|
||||
return False
|
||||
data = json.dumps(message.__dict__)
|
||||
try:
|
||||
url = self.url_template.format(token=str(self.token),
|
||||
uid=str(uid),
|
||||
name=quote(event_name))
|
||||
request = Request(url,
|
||||
data=data.encode(),
|
||||
headers={'Content-Type': 'application/json'})
|
||||
urlopen(request)
|
||||
return True
|
||||
except HTTPError as error:
|
||||
self.logger.warn('Botan track error ' +
|
||||
str(error.code) +
|
||||
':' + error.read().decode('utf-8'))
|
||||
return False
|
||||
except URLError as error:
|
||||
self.logger.warn('Botan track error ' + str(error.reason))
|
||||
return False
|
|
@ -25,6 +25,13 @@ import json
|
|||
import socket
|
||||
from ssl import SSLError
|
||||
|
||||
try:
|
||||
# python2
|
||||
from httplib import HTTPException
|
||||
except ImportError:
|
||||
# python3
|
||||
from http.client import HTTPException
|
||||
|
||||
try:
|
||||
from urllib.request import urlopen, urlretrieve, Request
|
||||
from urllib.error import HTTPError
|
||||
|
@ -82,6 +89,8 @@ def _try_except_req(func):
|
|||
raise TelegramError("Timed out")
|
||||
|
||||
raise TelegramError(str(error))
|
||||
except HTTPException as error:
|
||||
raise TelegramError('HTTPException: {0!r}'.format(error))
|
||||
|
||||
return decorator
|
||||
|
||||
|
|
62
tests/test_botan.py
Normal file
62
tests/test_botan.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""This module contains a object that represents Tests for Botan analytics integration"""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
import sys
|
||||
sys.path.append('.')
|
||||
|
||||
from telegram.utils.botan import Botan
|
||||
from tests.base import BaseTest
|
||||
|
||||
|
||||
class MessageMock(object):
|
||||
chat_id = None
|
||||
|
||||
def __init__(self, chat_id):
|
||||
self.chat_id = chat_id
|
||||
|
||||
|
||||
class BotanTest(BaseTest, unittest.TestCase):
|
||||
"""This object represents Tests for Botan analytics integration."""
|
||||
|
||||
token = os.environ.get('BOTAN_TOKEN')
|
||||
|
||||
def test_track(self):
|
||||
"""Test sending event to botan"""
|
||||
print('Test sending event to botan')
|
||||
botan = Botan(self.token)
|
||||
message = MessageMock(self._chat_id)
|
||||
result = botan.track(message, 'named event')
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_track_fail(self):
|
||||
"""Test fail when sending event to botan"""
|
||||
print('Test fail when sending event to botan')
|
||||
botan = Botan(self.token)
|
||||
botan.url_template = 'https://api.botan.io/traccc?token={token}&uid={uid}&name={name}'
|
||||
message = MessageMock(self._chat_id)
|
||||
result = botan.track(message, 'named event')
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_wrong_message(self):
|
||||
"""Test sending wrong message"""
|
||||
print('Test sending wrong message')
|
||||
botan = Botan(self.token)
|
||||
message = MessageMock(self._chat_id)
|
||||
message = delattr(message, 'chat_id')
|
||||
result = botan.track(message, 'named event')
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_wrong_endpoint(self):
|
||||
"""Test wrong endpoint"""
|
||||
print('Test wrong endpoint')
|
||||
botan = Botan(self.token)
|
||||
botan.url_template = 'https://api.botaaaaan.io/traccc?token={token}&uid={uid}&name={name}'
|
||||
message = MessageMock(self._chat_id)
|
||||
result = botan.track(message, 'named event')
|
||||
self.assertFalse(result)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -110,6 +110,11 @@ class UpdaterTest(BaseTest, unittest.TestCase):
|
|||
elif args[0] == 'noresend':
|
||||
pass
|
||||
|
||||
def contextTest(self, bot, update, context):
|
||||
self.received_message = update
|
||||
self.message_count += 1
|
||||
self.context = context
|
||||
|
||||
@run_async
|
||||
def asyncAdditionalHandlerTest(self, bot, update, update_queue=None,
|
||||
**kwargs):
|
||||
|
@ -349,6 +354,20 @@ class UpdaterTest(BaseTest, unittest.TestCase):
|
|||
self.assertEqual(self.received_message, '/test5 noresend')
|
||||
self.assertEqual(self.message_count, 2)
|
||||
|
||||
def test_context(self):
|
||||
print('Testing context for handlers')
|
||||
context = "context_data"
|
||||
self._setup_updater('', messages=0)
|
||||
self.updater.dispatcher.addStringCommandHandler(
|
||||
'test_context', self.contextTest)
|
||||
|
||||
queue = self.updater.start_polling(0.01)
|
||||
queue.put('/test_context', context=context)
|
||||
sleep(.5)
|
||||
self.assertEqual(self.received_message, '/test_context')
|
||||
self.assertEqual(self.message_count, 1)
|
||||
self.assertEqual(self.context, context)
|
||||
|
||||
def test_regexGroupHandler(self):
|
||||
print('Testing optional groups and groupdict parameters')
|
||||
self._setup_updater('', messages=0)
|
||||
|
|
Loading…
Reference in a new issue