From de429a5b7dbcb83f5f3da7a6ad25891c3d9661d8 Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Wed, 1 Jan 2020 20:59:22 +0100 Subject: [PATCH] Extended Telegram integration --- platypush/backend/chat/telegram.py | 55 +++++++++++--------- platypush/message/event/chat/telegram.py | 30 ++++++++++- platypush/message/response/chat/telegram.py | 10 +++- platypush/plugins/chat/telegram.py | 56 ++++++++++++++++++++- platypush/plugins/redis.py | 11 ++-- 5 files changed, 130 insertions(+), 32 deletions(-) diff --git a/platypush/backend/chat/telegram.py b/platypush/backend/chat/telegram.py index dfbccc21d..0c247cb37 100644 --- a/platypush/backend/chat/telegram.py +++ b/platypush/backend/chat/telegram.py @@ -1,8 +1,11 @@ -from typing import List, Optional +import re + +from typing import Type from platypush.backend import Backend from platypush.context import get_plugin -from platypush.message.event.chat.telegram import MessageEvent, CommandMessageEvent +from platypush.message.event.chat.telegram import MessageEvent, CommandMessageEvent, TextMessageEvent, \ + PhotoMessageEvent, VideoMessageEvent, ContactMessageEvent, DocumentMessageEvent from platypush.plugins.chat.telegram import ChatTelegramPlugin @@ -12,7 +15,11 @@ class ChatTelegramBackend(Backend): Triggers: - * :class:`platypush.message.event.chat.telegram.MessageEvent` when a message is received. + * :class:`platypush.message.event.chat.telegram.TextMessageEvent` when a text message is received. + * :class:`platypush.message.event.chat.telegram.PhotoMessageEvent` when a photo is received. + * :class:`platypush.message.event.chat.telegram.VideoMessageEvent` when a video is received. + * :class:`platypush.message.event.chat.telegram.ContactMessageEvent` when a contact is received. + * :class:`platypush.message.event.chat.telegram.DocumentMessageEvent` when a document is received. * :class:`platypush.message.event.chat.telegram.CommandMessageEvent` when a command message is received. Requires: @@ -21,46 +28,48 @@ class ChatTelegramBackend(Backend): """ - def __init__(self, commands: Optional[List[str]] = None, **kwargs): - """ - :param commands: Optional list of commands to be registered on the bot (e.g. 'start', 'stop', 'help' etc.). - When you send e.g. '/start' to the bot conversation then a - :class:`platypush.message.event.chat.telegram.NewCommandMessageEvent` will be triggered instead of a - :class:`platypush.message.event.chat.telegram.NewMessageEvent` event. - """ + def __init__(self, **kwargs): super().__init__(**kwargs) - self.commands = commands or [] self._plugin: ChatTelegramPlugin = get_plugin('chat.telegram') - def _msg_hook(self): + def _msg_hook(self, cls: Type[MessageEvent]): # noinspection PyUnusedLocal def hook(update, context): - self.bus.post(MessageEvent(chat_id=update.effective_chat.id, - message=self._plugin.parse_msg(update.effective_message).output, - user=self._plugin.parse_user(update.effective_user).output)) + self.bus.post(cls(chat_id=update.effective_chat.id, + message=self._plugin.parse_msg(update.effective_message).output, + user=self._plugin.parse_user(update.effective_user).output)) + return hook - def _command_hook(self, cmd): + def _command_hook(self): # noinspection PyUnusedLocal def hook(update, context): - self.bus.post(CommandMessageEvent(command=cmd, - chat_id=update.effective_chat.id, - message=self._plugin.parse_msg(update.effective_message).output, + msg = update.effective_message + m = re.match('\s*/([0-9a-zA-Z]+)\s*(.*)', msg.text) + cmd = m.group(1).lower() + args = [arg for arg in re.split('\s+', m.group(2)) if len(arg)] + self.bus.post(CommandMessageEvent(chat_id=update.effective_chat.id, + command=cmd, + cmdargs=args, + message=self._plugin.parse_msg(msg).output, user=self._plugin.parse_user(update.effective_user).output)) return hook def run(self): # noinspection PyPackageRequirements - from telegram.ext import CommandHandler, MessageHandler, Filters + from telegram.ext import MessageHandler, Filters super().run() telegram = self._plugin.get_telegram() dispatcher = telegram.dispatcher - dispatcher.add_handler(MessageHandler(Filters.all, self._msg_hook())) - for cmd in self.commands: - dispatcher.add_handler(CommandHandler(cmd, self._command_hook(cmd))) + dispatcher.add_handler(MessageHandler(Filters.text, self._msg_hook(TextMessageEvent))) + dispatcher.add_handler(MessageHandler(Filters.photo, self._msg_hook(PhotoMessageEvent))) + dispatcher.add_handler(MessageHandler(Filters.video, self._msg_hook(VideoMessageEvent))) + dispatcher.add_handler(MessageHandler(Filters.location, self._msg_hook(ContactMessageEvent))) + dispatcher.add_handler(MessageHandler(Filters.document, self._msg_hook(DocumentMessageEvent))) + dispatcher.add_handler(MessageHandler(Filters.command, self._command_hook())) self.logger.info('Initialized Telegram backend') telegram.start_polling() diff --git a/platypush/message/event/chat/telegram.py b/platypush/message/event/chat/telegram.py index bc7ef97b1..d0a062626 100644 --- a/platypush/message/event/chat/telegram.py +++ b/platypush/message/event/chat/telegram.py @@ -1,3 +1,5 @@ +from typing import List, Optional + from platypush.message.event import Event @@ -18,8 +20,32 @@ class CommandMessageEvent(MessageEvent): """ Event triggered when a new message is received by the Telegram bot. """ - def __init__(self, command: str, *args, **kwargs): - super().__init__(*args, command=command, **kwargs) + def __init__(self, command: str, cmdargs: Optional[List[str]] = None, *args, **kwargs): + super().__init__(*args, command=command, cmdargs=(cmdargs or []), **kwargs) + + +class TextMessageEvent(MessageEvent): + pass + + +class PhotoMessageEvent(MessageEvent): + pass + + +class VideoMessageEvent(MessageEvent): + pass + + +class ContactMessageEvent(MessageEvent): + pass + + +class LocationMessageEvent(MessageEvent): + pass + + +class DocumentMessageEvent(MessageEvent): + pass # vim:sw=4:ts=4:et: diff --git a/platypush/message/response/chat/telegram.py b/platypush/message/response/chat/telegram.py index dc3f8330c..5bacb3df5 100644 --- a/platypush/message/response/chat/telegram.py +++ b/platypush/message/response/chat/telegram.py @@ -1,6 +1,6 @@ import datetime -from typing import Optional +from typing import Optional, List from platypush.message.response import Response @@ -161,4 +161,12 @@ class TelegramUserResponse(Response): }, **kwargs) +class TelegramUsersResponse(Response): + # noinspection PyShadowingBuiltins + def __init__(self, + users: List[TelegramUserResponse], + *args, **kwargs): + super().__init__(*args, output=[user.output for user in users], **kwargs) + + # vim:sw=4:ts=4:et: diff --git a/platypush/plugins/chat/telegram.py b/platypush/plugins/chat/telegram.py index bb1ee5316..3f238b9ea 100644 --- a/platypush/plugins/chat/telegram.py +++ b/platypush/plugins/chat/telegram.py @@ -12,7 +12,7 @@ from telegram.message import Message as TelegramMessage from telegram.user import User as TelegramUser from platypush.message.response.chat.telegram import TelegramMessageResponse, TelegramFileResponse, \ - TelegramChatResponse, TelegramUserResponse + TelegramChatResponse, TelegramUserResponse, TelegramUsersResponse from platypush.plugins import Plugin, action @@ -667,6 +667,60 @@ class ChatTelegramPlugin(Plugin): first_name=chat.first_name, last_name=chat.last_name) + @action + def get_chat_user(self, chat_id: Union[int, str], user_id: int, timeout: int = 20) -> TelegramUserResponse: + """ + Get the info about a user connected to a chat. + + :param chat_id: Chat ID. + :param user_id: User ID. + :param timeout: Upload timeout (default: 20 seconds). + """ + + telegram = self.get_telegram() + user = telegram.bot.get_chat_member(chat_id, user_id, timeout=timeout) + return TelegramUserResponse(user_id=user.user.id, + link=user.user.link, + username=user.user.username, + first_name=user.user.first_name, + last_name=user.user.last_name, + is_bot=user.user.is_bot, + language_code=user.user.language_code) + + @action + def get_chat_administrators(self, chat_id: Union[int, str], timeout: int = 20) -> TelegramUsersResponse: + """ + Get the list of the administrators of a chat. + + :param chat_id: Chat ID. + :param timeout: Upload timeout (default: 20 seconds). + """ + + telegram = self.get_telegram() + admins = telegram.bot.get_chat_administrators(chat_id, timeout=timeout) + return TelegramUsersResponse([ + TelegramUserResponse( + user_id=user.user.id, + link=user.user.link, + username=user.user.username, + first_name=user.user.first_name, + last_name=user.user.last_name, + is_bot=user.user.is_bot, + language_code=user.user.language_code, + ) for user in admins + ]) + + @action + def get_chat_members_count(self, chat_id: Union[int, str], timeout: int = 20) -> int: + """ + Get the number of users in a chat. + + :param chat_id: Chat ID. + :param timeout: Upload timeout (default: 20 seconds). + """ + telegram = self.get_telegram() + return telegram.bot.get_chat_members_count(chat_id, timeout=timeout) + @action def kick_chat_member(self, chat_id: Union[str, int], user_id: int, diff --git a/platypush/plugins/redis.py b/platypush/plugins/redis.py index b4bf82076..dd5324585 100644 --- a/platypush/plugins/redis.py +++ b/platypush/plugins/redis.py @@ -19,6 +19,7 @@ class RedisPlugin(Plugin): self.kwargs = kwargs if not kwargs: + # noinspection PyBroadException try: redis_backend = get_backend('redis') if redis_backend and redis_backend.redis_args: @@ -32,7 +33,7 @@ class RedisPlugin(Plugin): @action def send_message(self, queue, msg, *args, **kwargs): """ - Send a message to a Redis queu. + Send a message to a Redis queue. :param queue: Queue name :type queue: str @@ -43,7 +44,8 @@ class RedisPlugin(Plugin): :param args: Args passed to the Redis constructor (see https://redis-py.readthedocs.io/en/latest/#redis.Redis) :type args: list - :param kwargs: Kwargs passed to the Redis constructor (see https://redis-py.readthedocs.io/en/latest/#redis.Redis) + :param kwargs: Kwargs passed to the Redis constructor (see + https://redis-py.readthedocs.io/en/latest/#redis.Redis) :type kwargs: dict """ @@ -66,13 +68,13 @@ class RedisPlugin(Plugin): } @action - def mset(self, *args, **kwargs): + def mset(self, **kwargs): """ Set key/values based on mapping (wraps MSET) """ try: - return self._get_redis().mset(*args, **kwargs) + return self._get_redis().mset(**kwargs) except TypeError: # XXX commit https://github.com/andymccurdy/redis-py/commit/90a52dd5de111f0053bb3ebaa7c78f73a82a1e3e # broke back-compatibility with the previous way of passing @@ -106,4 +108,3 @@ class RedisPlugin(Plugin): # vim:sw=4:ts=4:et: -