Files
mautrix-telegram/mautrix_telegram/commands/handler.py
T
Tulir Asokan 524f60ab48 Update to mautrix-python 0.8.0.beta3
* Cross-server double puppeting is now possible
* End-to-bridge encryption no longer requires login_shared_secret,
  but the homeserver must support MSC2778 (Synapse 1.21+)
2020-10-14 18:56:26 +03:00

125 lines
5.8 KiB
Python

# mautrix-telegram - A Matrix-Telegram puppeting bridge
# Copyright (C) 2019 Tulir Asokan
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""This module contains classes handling commands issued by Matrix users."""
from typing import Awaitable, Callable, List, Optional, NamedTuple, Any
from telethon.errors import FloodWaitError
from mautrix.types import RoomID, EventID, MessageEventContent
from mautrix.bridge.commands import (HelpSection, CommandEvent as BaseCommandEvent,
CommandHandler as BaseCommandHandler,
CommandProcessor as BaseCommandProcessor,
CommandHandlerFunc, command_handler as base_command_handler,
HelpCacheKey as BaseHelpCacheKey)
from ..util import format_duration
from .. import user as u, context as c, portal as po
class HelpCacheKey(BaseHelpCacheKey, NamedTuple):
is_management: bool
is_portal: bool
puppet_whitelisted: bool
matrix_puppet_whitelisted: bool
is_admin: bool
is_logged_in: bool
SECTION_AUTH = HelpSection("Authentication", 10, "")
SECTION_CREATING_PORTALS = HelpSection("Creating portals", 20, "")
SECTION_PORTAL_MANAGEMENT = HelpSection("Portal management", 30, "")
SECTION_MISC = HelpSection("Miscellaneous", 40, "")
SECTION_ADMIN = HelpSection("Administration", 50, "")
class CommandEvent(BaseCommandEvent):
sender: u.User
portal: po.Portal
def __init__(self, processor: 'CommandProcessor', room_id: RoomID, event_id: EventID,
sender: u.User, command: str, args: List[str], content: MessageEventContent,
portal: Optional['po.Portal'], is_management: bool, has_bridge_bot: bool) -> None:
super().__init__(processor, room_id, event_id, sender, command, args, content,
portal, is_management, has_bridge_bot)
self.bridge = processor.bridge
self.tgbot = processor.tgbot
self.config = processor.config
self.public_website = processor.public_website
@property
def print_error_traceback(self) -> bool:
return self.sender.is_admin
async def get_help_key(self) -> HelpCacheKey:
return HelpCacheKey(self.is_management, self.portal is not None,
self.sender.puppet_whitelisted, self.sender.matrix_puppet_whitelisted,
self.sender.is_admin, await self.sender.is_logged_in())
class CommandHandler(BaseCommandHandler):
name: str
needs_puppeting: bool
needs_matrix_puppeting: bool
def __init__(self, handler: Callable[[CommandEvent], Awaitable[EventID]],
management_only: bool, name: str, help_text: str, help_args: str,
help_section: HelpSection, needs_auth: bool, needs_puppeting: bool,
needs_matrix_puppeting: bool, needs_admin: bool) -> None:
super().__init__(handler, management_only, name, help_text, help_args, help_section,
needs_auth=needs_auth, needs_puppeting=needs_puppeting,
needs_matrix_puppeting=needs_matrix_puppeting, needs_admin=needs_admin)
async def get_permission_error(self, evt: CommandEvent) -> Optional[str]:
if self.needs_puppeting and not evt.sender.puppet_whitelisted:
return "This command requires puppeting privileges."
elif self.needs_matrix_puppeting and not evt.sender.matrix_puppet_whitelisted:
return "This command requires Matrix puppeting privileges."
return await super().get_permission_error(evt)
def has_permission(self, key: HelpCacheKey) -> bool:
return (super().has_permission(key) and
(not self.needs_puppeting or key.puppet_whitelisted) and
(not self.needs_matrix_puppeting or key.matrix_puppet_whitelisted))
def command_handler(_func: Optional[CommandHandlerFunc] = None, *, needs_auth: bool = True,
needs_puppeting: bool = True, needs_matrix_puppeting: bool = False,
needs_admin: bool = False, management_only: bool = False,
name: Optional[str] = None, help_text: str = "", help_args: str = "",
help_section: HelpSection = None) -> Callable[[CommandHandlerFunc],
CommandHandler]:
return base_command_handler(
_func, _handler_class=CommandHandler, name=name, help_text=help_text, help_args=help_args,
help_section=help_section, management_only=management_only, needs_auth=needs_auth,
needs_admin=needs_admin, needs_puppeting=needs_puppeting,
needs_matrix_puppeting=needs_matrix_puppeting)
class CommandProcessor(BaseCommandProcessor):
def __init__(self, context: c.Context) -> None:
super().__init__(event_class=CommandEvent, bridge=context.bridge)
self.tgbot = context.bot
self.public_website = context.public_website
@staticmethod
async def _run_handler(handler: Callable[[CommandEvent], Awaitable[Any]], evt: CommandEvent
) -> Any:
try:
return await handler(evt)
except FloodWaitError as e:
return await evt.reply(f"Flood error: Please wait {format_duration(e.seconds)}")