Extended Telegram integration

This commit is contained in:
Fabio Manganiello 2020-01-01 20:59:22 +01:00
parent fd5abc748f
commit de429a5b7d
5 changed files with 130 additions and 32 deletions

View file

@ -1,8 +1,11 @@
from typing import List, Optional import re
from typing import Type
from platypush.backend import Backend from platypush.backend import Backend
from platypush.context import get_plugin 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 from platypush.plugins.chat.telegram import ChatTelegramPlugin
@ -12,7 +15,11 @@ class ChatTelegramBackend(Backend):
Triggers: 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. * :class:`platypush.message.event.chat.telegram.CommandMessageEvent` when a command message is received.
Requires: Requires:
@ -21,46 +28,48 @@ class ChatTelegramBackend(Backend):
""" """
def __init__(self, commands: Optional[List[str]] = None, **kwargs): def __init__(self, **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.
"""
super().__init__(**kwargs) super().__init__(**kwargs)
self.commands = commands or []
self._plugin: ChatTelegramPlugin = get_plugin('chat.telegram') self._plugin: ChatTelegramPlugin = get_plugin('chat.telegram')
def _msg_hook(self): def _msg_hook(self, cls: Type[MessageEvent]):
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
def hook(update, context): def hook(update, context):
self.bus.post(MessageEvent(chat_id=update.effective_chat.id, self.bus.post(cls(chat_id=update.effective_chat.id,
message=self._plugin.parse_msg(update.effective_message).output, message=self._plugin.parse_msg(update.effective_message).output,
user=self._plugin.parse_user(update.effective_user).output)) user=self._plugin.parse_user(update.effective_user).output))
return hook return hook
def _command_hook(self, cmd): def _command_hook(self):
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
def hook(update, context): def hook(update, context):
self.bus.post(CommandMessageEvent(command=cmd, msg = update.effective_message
chat_id=update.effective_chat.id, m = re.match('\s*/([0-9a-zA-Z]+)\s*(.*)', msg.text)
message=self._plugin.parse_msg(update.effective_message).output, 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)) user=self._plugin.parse_user(update.effective_user).output))
return hook return hook
def run(self): def run(self):
# noinspection PyPackageRequirements # noinspection PyPackageRequirements
from telegram.ext import CommandHandler, MessageHandler, Filters from telegram.ext import MessageHandler, Filters
super().run() super().run()
telegram = self._plugin.get_telegram() telegram = self._plugin.get_telegram()
dispatcher = telegram.dispatcher dispatcher = telegram.dispatcher
dispatcher.add_handler(MessageHandler(Filters.all, self._msg_hook()))
for cmd in self.commands: dispatcher.add_handler(MessageHandler(Filters.text, self._msg_hook(TextMessageEvent)))
dispatcher.add_handler(CommandHandler(cmd, self._command_hook(cmd))) 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') self.logger.info('Initialized Telegram backend')
telegram.start_polling() telegram.start_polling()

View file

@ -1,3 +1,5 @@
from typing import List, Optional
from platypush.message.event import Event 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. Event triggered when a new message is received by the Telegram bot.
""" """
def __init__(self, command: str, *args, **kwargs): def __init__(self, command: str, cmdargs: Optional[List[str]] = None, *args, **kwargs):
super().__init__(*args, command=command, **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: # vim:sw=4:ts=4:et:

View file

@ -1,6 +1,6 @@
import datetime import datetime
from typing import Optional from typing import Optional, List
from platypush.message.response import Response from platypush.message.response import Response
@ -161,4 +161,12 @@ class TelegramUserResponse(Response):
}, **kwargs) }, **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: # vim:sw=4:ts=4:et:

View file

@ -12,7 +12,7 @@ from telegram.message import Message as TelegramMessage
from telegram.user import User as TelegramUser from telegram.user import User as TelegramUser
from platypush.message.response.chat.telegram import TelegramMessageResponse, TelegramFileResponse, \ from platypush.message.response.chat.telegram import TelegramMessageResponse, TelegramFileResponse, \
TelegramChatResponse, TelegramUserResponse TelegramChatResponse, TelegramUserResponse, TelegramUsersResponse
from platypush.plugins import Plugin, action from platypush.plugins import Plugin, action
@ -667,6 +667,60 @@ class ChatTelegramPlugin(Plugin):
first_name=chat.first_name, first_name=chat.first_name,
last_name=chat.last_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 @action
def kick_chat_member(self, chat_id: Union[str, int], def kick_chat_member(self, chat_id: Union[str, int],
user_id: int, user_id: int,

View file

@ -19,6 +19,7 @@ class RedisPlugin(Plugin):
self.kwargs = kwargs self.kwargs = kwargs
if not kwargs: if not kwargs:
# noinspection PyBroadException
try: try:
redis_backend = get_backend('redis') redis_backend = get_backend('redis')
if redis_backend and redis_backend.redis_args: if redis_backend and redis_backend.redis_args:
@ -32,7 +33,7 @@ class RedisPlugin(Plugin):
@action @action
def send_message(self, queue, msg, *args, **kwargs): 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 :param queue: Queue name
:type queue: str :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) :param args: Args passed to the Redis constructor (see https://redis-py.readthedocs.io/en/latest/#redis.Redis)
:type args: list :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 :type kwargs: dict
""" """
@ -66,13 +68,13 @@ class RedisPlugin(Plugin):
} }
@action @action
def mset(self, *args, **kwargs): def mset(self, **kwargs):
""" """
Set key/values based on mapping (wraps MSET) Set key/values based on mapping (wraps MSET)
""" """
try: try:
return self._get_redis().mset(*args, **kwargs) return self._get_redis().mset(**kwargs)
except TypeError: except TypeError:
# XXX commit https://github.com/andymccurdy/redis-py/commit/90a52dd5de111f0053bb3ebaa7c78f73a82a1e3e # XXX commit https://github.com/andymccurdy/redis-py/commit/90a52dd5de111f0053bb3ebaa7c78f73a82a1e3e
# broke back-compatibility with the previous way of passing # broke back-compatibility with the previous way of passing
@ -106,4 +108,3 @@ class RedisPlugin(Plugin):
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et: