Make help message dynamic based on permissions

This commit is contained in:
Tulir Asokan
2018-07-10 14:17:03 +03:00
parent ecf0e262df
commit 7a4d29e1e4
8 changed files with 236 additions and 168 deletions
+4 -1
View File
@@ -1,2 +1,5 @@
from .handler import command_handler, CommandHandler, CommandEvent
from .handler import (command_handler, command_handlers as _command_handlers,
CommandHandler, CommandProcessor, CommandEvent,
SECTION_GENERAL, SECTION_AUTH, SECTION_CREATING_PORTALS,
SECTION_PORTAL_MANAGEMENT, SECTION_MISC, SECTION_ADMIN)
from . import clean_rooms, auth, meta, telegram, portal
+28 -23
View File
@@ -14,17 +14,20 @@
#
# 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/>.
from typing import Dict
import asyncio
from telethon.errors import *
from . import command_handler
from . import command_handler, CommandEvent, SECTION_AUTH
from .. import puppet as pu
from ..util import format_duration
@command_handler(needs_auth=False)
async def ping(evt):
@command_handler(needs_auth=False,
help_section=SECTION_AUTH,
help_text="Check if you're logged into Telegram.")
async def ping(evt: CommandEvent):
me = await evt.sender.client.get_me() if await evt.sender.is_logged_in() else None
if me:
return await evt.reply(f"You're logged in as @{me.username}")
@@ -32,8 +35,10 @@ async def ping(evt):
return await evt.reply("You're not logged in.")
@command_handler(needs_auth=False, needs_puppeting=False)
async def ping_bot(evt):
@command_handler(needs_auth=False, needs_puppeting=False,
help_section=SECTION_AUTH,
help_text="Get the info of the message relay Telegram bot.")
async def ping_bot(evt: CommandEvent):
if not evt.tgbot:
return await evt.reply("Telegram message relay bot not configured.")
bot_info = await evt.tgbot.client.get_me()
@@ -44,13 +49,11 @@ async def ping_bot(evt):
"To use the bot, simply invite it to a portal room.")
@command_handler(needs_auth=False, management_only=True)
def register(evt):
return evt.reply("Not yet implemented.")
@command_handler(needs_auth=False, management_only=True)
async def register(evt):
@command_handler(needs_auth=False, management_only=True,
help_section=SECTION_AUTH,
help_args="<_phone_> <_full name_>",
help_text="Register to Telegram")
async def register(evt: CommandEvent):
if await evt.sender.is_logged_in():
return await evt.reply("You are already logged in.")
elif len(evt.args) < 1:
@@ -69,7 +72,7 @@ async def register(evt):
})
async def enter_code_register(evt):
async def enter_code_register(evt: CommandEvent):
if len(evt.args) == 0:
return await evt.reply("**Usage:** `$cmdprefix+sp <code>`")
try:
@@ -95,8 +98,10 @@ async def enter_code_register(evt):
"Check console for more details.")
@command_handler(needs_auth=False, management_only=True)
async def login(evt):
@command_handler(needs_auth=False, management_only=True,
help_section=SECTION_AUTH,
help_text="Get instructions on how to log in.")
async def login(evt: CommandEvent):
if await evt.sender.is_logged_in():
return await evt.reply("You are already logged in.")
@@ -126,7 +131,7 @@ async def login(evt):
return await evt.reply("This bridge instance has been configured to not allow logging in.")
async def request_code(evt, phone_number, next_status):
async def request_code(evt: CommandEvent, phone_number: str, next_status: Dict[str, str]):
ok = False
try:
await evt.sender.ensure_started(even_if_no_session=True)
@@ -158,7 +163,7 @@ async def request_code(evt, phone_number, next_status):
@command_handler(needs_auth=False)
async def enter_phone(evt):
async def enter_phone(evt: CommandEvent):
if len(evt.args) == 0:
return await evt.reply("**Usage:** `$cmdprefix+sp enter-phone <phone>`")
elif not evt.config.get("bridge.allow_matrix_login", True):
@@ -173,7 +178,7 @@ async def enter_phone(evt):
@command_handler(needs_auth=False)
async def enter_code(evt):
async def enter_code(evt: CommandEvent):
if len(evt.args) == 0:
return await evt.reply("**Usage:** `$cmdprefix+sp enter-code <code>`")
elif not evt.config.get("bridge.allow_matrix_login", True):
@@ -203,7 +208,7 @@ async def enter_code(evt):
@command_handler(needs_auth=False)
async def enter_password(evt):
async def enter_password(evt: CommandEvent):
if len(evt.args) == 0:
return await evt.reply("**Usage:** `$cmdprefix+sp enter-password <password>`")
elif not evt.config.get("bridge.allow_matrix_login", True):
@@ -223,10 +228,10 @@ async def enter_password(evt):
"Check console for more details.")
@command_handler(needs_auth=False)
async def logout(evt):
if not await evt.sender.is_logged_in():
return await evt.reply("You're not logged in.")
@command_handler(needs_auth=True,
help_section=SECTION_AUTH,
help_text="Log out from Telegram.")
async def logout(evt: CommandEvent):
if await evt.sender.log_out():
return await evt.reply("Logged out successfully.")
return await evt.reply("Failed to log out.")
+5 -7
View File
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from mautrix_appservice import MatrixRequestError
from . import command_handler
from . import command_handler, CommandEvent, SECTION_ADMIN
from .. import puppet as pu, portal as po
@@ -52,12 +52,10 @@ async def _find_rooms(intent):
return management_rooms, unidentified_rooms, portals, empty_portals
@command_handler(needs_admin=True, needs_auth=False, name="clean-rooms")
async def clean_rooms(evt):
if not evt.is_management:
return await evt.reply("`clean-rooms` is a particularly spammy command. Please don't "
"run it in non-management rooms.")
@command_handler(needs_admin=True, needs_auth=False, management_only=True, name="clean-rooms",
help_section=SECTION_ADMIN,
help_text="Clean up unused portal/management rooms.")
async def clean_rooms(evt: CommandEvent):
management_rooms, unidentified_rooms, portals, empty_portals = await _find_rooms(evt.az.intent)
reply = ["#### Management rooms (M)"]
+90 -36
View File
@@ -14,45 +14,38 @@
#
# 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/>.
from typing import List, Dict, Callable, Optional
from collections import namedtuple
import markdown
import logging
from telethon.errors import FloodWaitError
from ..util import format_duration
from ..context import Context
from .. import user as u
command_handlers = {}
command_handlers = {} # type: Dict[str, CommandHandler]
HelpSection = namedtuple("HelpSection", "name order description")
def command_handler(needs_auth=True, management_only=False, needs_puppeting=True,
needs_admin=False, name=None):
def decorator(func):
async def wrapper(evt):
if management_only and not evt.is_management:
return await evt.reply(f"`{evt.command}` is a restricted command:"
"you may only run it in management rooms.")
elif needs_auth and not await evt.sender.is_logged_in():
return await evt.reply("This command requires you to be logged in.")
elif needs_puppeting and not evt.sender.puppet_whitelisted:
return await evt.reply("This command requires puppeting privileges.")
elif needs_admin and not evt.sender.is_admin:
return await evt.reply("This command requires administrator privileges.")
return await func(evt)
command_handlers[name or func.__name__.replace("_", "-")] = wrapper
return wrapper
return decorator
SECTION_GENERAL = HelpSection("General", 0, "")
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:
def __init__(self, handler, room, sender, command, args, is_management, is_portal):
self.az = handler.az
self.log = handler.log
self.loop = handler.loop
self.tgbot = handler.tgbot
self.config = handler.config
self.command_prefix = handler.command_prefix
def __init__(self, processor: "CommandProcessor", room: str, sender: u.User, command: str,
args: List[str], is_management: bool, is_portal: bool):
self.az = processor.az
self.log = processor.log
self.loop = processor.loop
self.tgbot = processor.tgbot
self.config = processor.config
self.command_prefix = processor.command_prefix
self.room_id = room
self.sender = sender
self.command = command
@@ -60,7 +53,7 @@ class CommandEvent:
self.is_management = is_management
self.is_portal = is_portal
def reply(self, message, allow_html=False, render_markdown=True):
def reply(self, message: str, allow_html: bool = False, render_markdown: bool = True):
message = message.replace("$cmdprefix+sp ",
"" if self.is_management else f"{self.command_prefix} ")
message = message.replace("$cmdprefix", self.command_prefix)
@@ -73,17 +66,78 @@ class CommandEvent:
class CommandHandler:
def __init__(self, handler: Callable[[CommandEvent], None],
needs_auth: bool, needs_puppeting: bool, needs_admin: bool, management_only: bool,
name: str, help_text: str, help_args: str, help_section: HelpSection):
self._handler = handler
self.needs_auth = needs_auth
self.needs_puppeting = needs_puppeting
self.needs_admin = needs_admin
self.management_only = management_only
self.name = name
self._help_text = help_text
self._help_args = help_args
self.help_section = help_section
async def get_permission_error(self, evt: CommandEvent) -> Optional[str]:
if self.management_only and not evt.is_management:
return (f"`{evt.command}` is a restricted command: "
"you may only run it in management rooms.")
elif self.needs_puppeting and not evt.sender.puppet_whitelisted:
return "This command requires puppeting privileges."
elif self.needs_admin and not evt.sender.is_admin:
return "This command requires administrator privileges."
elif self.needs_auth and not await evt.sender.is_logged_in():
return "This command requires you to be logged in."
return None
def has_permission(self, is_management: bool, puppet_whitelisted: bool, is_admin: bool,
is_logged_in: bool) -> bool:
return ((not self.management_only or is_management) and
(not self.needs_puppeting or puppet_whitelisted) and
(not self.needs_admin or is_admin) and
(not self.needs_auth or is_logged_in))
async def __call__(self, evt: CommandEvent):
error = await self.get_permission_error(evt)
if error is not None:
return await evt.reply(error)
return await self._handler(evt)
@property
def has_help(self) -> bool:
return bool(self.help_section) and bool(self._help_text)
@property
def help(self) -> str:
return f"**{self.name}** {self._help_args} - {self._help_text}"
def command_handler(_func: Optional[Callable[[CommandEvent], None]] = None, *, needs_auth=True,
needs_puppeting=True, needs_admin=False, management_only=False,
name=None, help_text="", help_args="", help_section=None):
input_name = name
def decorator(func: Callable[[CommandEvent], None]):
name = input_name or func.__name__.replace("_", "-")
handler = CommandHandler(func, needs_auth, needs_puppeting, needs_admin, management_only,
name, help_text, help_args, help_section)
command_handlers[handler.name] = handler
return handler
return decorator if _func is None else decorator(_func)
class CommandProcessor:
log = logging.getLogger("mau.commands")
def __init__(self, context):
def __init__(self, context: Context):
self.az, self.db, self.config, self.loop, self.tgbot = context
self.command_prefix = self.config["bridge.command_prefix"]
# region Utility functions for handling commands
async def handle(self, room, sender, command, args, is_management, is_portal):
evt = CommandEvent(self, room, sender, command, args,
is_management, is_portal)
async def handle(self, room: str, sender: u.User, command: str, args: List[str],
is_management: bool, is_portal: bool):
evt = CommandEvent(self, room, sender, command, args, is_management, is_portal)
orig_command = command
command = command.lower()
try:
@@ -100,7 +154,7 @@ class CommandHandler:
except FloodWaitError as e:
return await evt.reply(f"Flood error: Please wait {format_duration(e.seconds)}")
except Exception:
self.log.exception("Fatal error handling command "
self.log.exception("Unhandled error while handling command "
f"{evt.command} {' '.join(args)} from {sender.mxid}")
return await evt.reply("Fatal error while handling command. "
return await evt.reply("Unhandled error while handling command. "
"Check logs for more details.")
+34 -71
View File
@@ -14,11 +14,13 @@
#
# 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/>.
from . import command_handler
from . import command_handler, CommandEvent, _command_handlers, SECTION_GENERAL
@command_handler(needs_auth=False, needs_puppeting=False)
def cancel(evt):
@command_handler(needs_auth=False, needs_puppeting=False,
help_section=SECTION_GENERAL,
help_text="Cancel an ongoing action (such as login)")
def cancel(evt: CommandEvent):
if evt.sender.command_status:
action = evt.sender.command_status["action"]
evt.sender.command_status = None
@@ -28,78 +30,39 @@ def cancel(evt):
@command_handler(needs_auth=False, needs_puppeting=False)
def unknown_command(evt):
def unknown_command(evt: CommandEvent):
return evt.reply("Unknown command. Try `$cmdprefix+sp help` for help.")
@command_handler(needs_auth=False, needs_puppeting=False)
def help(evt):
help_cache = {}
async def _get_help_text(evt: CommandEvent):
cache_key = (evt.is_management, evt.sender.puppet_whitelisted, evt.sender.is_admin,
await evt.sender.is_logged_in())
if cache_key not in help_cache:
help = {}
for handler in _command_handlers.values():
if handler.has_help and handler.has_permission(*cache_key):
help.setdefault(handler.help_section, [])
help[handler.help_section].append(handler.help + " ")
help = sorted(help.items(), key=lambda item: item[0].order)
help = ["#### {}\n{}\n".format(key.name, "\n".join(value)) for key, value in help]
help_cache[cache_key] = "\n".join(help)
return help_cache[cache_key]
def _get_management_status(evt: CommandEvent):
if evt.is_management:
management_status = ("This is a management room: prefixing commands "
"with `$cmdprefix` is not required.\n")
return "This is a management room: prefixing commands with `$cmdprefix` is not required."
elif evt.is_portal:
management_status = ("**This is a portal room**: you must always "
"prefix commands with `$cmdprefix`.\n"
"Management commands will not be sent to Telegram.")
else:
management_status = ("**This is not a management room**: you must "
"prefix commands with `$cmdprefix`.\n")
help = None
if not evt.sender.puppet_whitelisted:
help = """\n
#### Generic bridge commands
**help** - Show this help message.
**cancel** - Cancel an ongoing action (such as login).
**ping-bot** - Get info of the message relay Telegram bot.
**invite-link** - Get a Telegram invite link to the current chat.
**delete-portal** - Remove all users from the current portal room and forget the portal.
Only works for group chats; to delete a private chat portal, simply
leave the room.
**unbridge** - Remove puppets from the current portal room and forget the portal.
**bridge** [_id_] - Bridge the current Matrix room to the Telegram chat with the given
ID. The ID must be the prefixed version that you get with the `/id`
command of the Telegram-side bot.
return ("**This is a portal room**: you must always prefix commands with `$cmdprefix`.\n"
"Management commands will not be sent to Telegram.")
return "**This is not a management room**: you must prefix commands with `$cmdprefix`."
"""
help = help or """\n
#### Generic bridge commands
**help** - Show this help message.
**cancel** - Cancel an ongoing action (such as login).
#### Authentication
**login** - Request an authentication code.
**logout** - Log out from Telegram.
**ping** - Check if you're logged into Telegram.
#### Miscellaneous things
**search** [_-r|--remote_] <_query_> - Search your contacts or the Telegram servers for users.
**sync** [`chats`|`contacts`|`me`] - Synchronize your chat portals, contacts and/or own info.
**ping-bot** - Get info of the message relay Telegram bot.
**set-pl** <_level_> [_mxid_] - Set a temporary power level without affecting Telegram.
#### Initiating chats
**pm** <_identifier_> - Open a private chat with the given Telegram user. The identifier is either
the internal user ID, the username or the phone number.
**join** <_link_> - Join a chat with an invite link.
**create** [_type_] - Create a Telegram chat of the given type for the current Matrix room. The
type is either `group`, `supergroup` or `channel` (defaults to `group`).
#### Portal management
**upgrade** - Upgrade a normal Telegram group to a supergroup.
**invite-link** - Get a Telegram invite link to the current chat.
**delete-portal** - Remove all users from the current portal room and forget the portal.
Only works for group chats; to delete a private chat portal, simply
leave the room.
**unbridge** - Remove puppets from the current portal room and forget the portal.
**bridge** [_id_] - Bridge the current Matrix room to the Telegram chat with the given
ID. The ID must be the prefixed version that you get with the `/id`
command of the Telegram-side bot.
**group-name** <_name_|`-`> - Change the username of a supergroup/channel. To disable, use a dash
(`-`) as the name.
**clean-rooms** - Clean up unused portal/management rooms.
**filter** <`whitelist`|`blacklist`> <_chat ID_> - Allow or disallow bridging a specific chat.
**filter-mode** <`whitelist`|`blacklist`> - Change whether the bridge will allow or disallow
bridging rooms by default.
"""
return evt.reply(management_status + help)
@command_handler(needs_auth=False, needs_puppeting=False,
help_section=SECTION_GENERAL,
help_text="Show this help message.")
async def help(evt: CommandEvent):
return await evt.reply(_get_management_status(evt) + "\n" + await _get_help_text(evt))
+51 -18
View File
@@ -14,17 +14,21 @@
#
# 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/>.
from typing import Optional, Callable
import asyncio
from telethon.errors import *
from telethon.tl.types import ChatForbidden, ChannelForbidden
from mautrix_appservice import MatrixRequestError
from .. import portal as po
from . import command_handler, CommandEvent
from .. import portal as po, user as u
from . import command_handler, CommandEvent, SECTION_ADMIN, SECTION_CREATING_PORTALS, SECTION_PORTAL_MANAGEMENT
@command_handler(needs_admin=True, needs_auth=False, name="set-pl")
@command_handler(needs_admin=True, needs_auth=False, name="set-pl",
help_section=SECTION_ADMIN,
help_args="<_level_> [_mxid_]",
help_text="Set a temporary power level without affecting Telegram.")
async def set_power_level(evt: CommandEvent):
try:
level = int(evt.args[0])
@@ -42,7 +46,8 @@ async def set_power_level(evt: CommandEvent):
return await evt.reply("Failed to set power level.")
@command_handler()
@command_handler(help_section=SECTION_PORTAL_MANAGEMENT,
help_text="Get a Telegram invite link to the current chat.")
async def invite_link(evt: CommandEvent):
portal = po.Portal.get_by_mxid(evt.room_id)
if not portal:
@@ -60,7 +65,7 @@ async def invite_link(evt: CommandEvent):
return await evt.reply("You don't have the permission to create an invite link.")
async def _has_access_to(room, intent, sender, event, default=50):
async def _has_access_to(room: str, intent, sender: u.User, event: str, default: int = 50):
if sender.is_admin:
return True
# Make sure the state store contains the power levels.
@@ -73,7 +78,8 @@ async def _has_access_to(room, intent, sender, event, default=50):
default=default)
async def _get_portal_and_check_permission(evt, permission, action=None):
async def _get_portal_and_check_permission(evt: CommandEvent, permission: str,
action: Optional[str] = None):
room_id = evt.args[0] if len(evt.args) > 0 else evt.room_id
portal = po.Portal.get_by_mxid(room_id)
@@ -87,7 +93,8 @@ async def _get_portal_and_check_permission(evt, permission, action=None):
return portal, True
def _get_portal_murder_function(action, room_id, function, command, completed_message):
def _get_portal_murder_function(action: str, room_id: str, function: Callable, command: str,
completed_message: str):
async def post_confirm(confirm):
confirm.sender.command_status = None
if len(confirm.args) > 0 and confirm.args[0] == f"confirm-{command}":
@@ -103,7 +110,11 @@ def _get_portal_murder_function(action, room_id, function, command, completed_me
}
@command_handler(needs_auth=False, needs_puppeting=False)
@command_handler(needs_auth=False, needs_puppeting=False,
help_section=SECTION_PORTAL_MANAGEMENT,
help_text="Remove all users from the current portal room and forget the portal. "
"Only works for group chats; to delete a private chat portal, simply "
"leave the room.")
async def delete_portal(evt: CommandEvent):
portal, ok = await _get_portal_and_check_permission(evt, "delete_portal")
if not ok:
@@ -122,7 +133,9 @@ async def delete_portal(evt: CommandEvent):
"bridge, use `$cmdprefix+sp unbridge` instead.")
@command_handler(needs_auth=False)
@command_handler(needs_auth=False,
help_section=SECTION_PORTAL_MANAGEMENT,
help_text="Remove puppets from the current portal room and forget the portal.")
async def unbridge(evt: CommandEvent):
portal, ok = await _get_portal_and_check_permission(evt, "unbridge_room")
if not ok:
@@ -136,7 +149,12 @@ async def unbridge(evt: CommandEvent):
"by typing `$cmdprefix+sp confirm-unbridge`")
@command_handler(needs_auth=False)
@command_handler(needs_auth=False,
help_section=SECTION_PORTAL_MANAGEMENT,
help_args="[_id_]",
help_text="Bridge the current Matrix room to the Telegram chat with the given "
"ID. The ID must be the prefixed version that you get with the `/id` "
"command of the Telegram-side bot.")
async def bridge(evt: CommandEvent):
if len(evt.args) == 0:
return await evt.reply("**Usage:** "
@@ -168,7 +186,7 @@ async def bridge(evt: CommandEvent):
portal = po.Portal.get_by_tgid(tgid, peer_type=peer_type)
if not portal.allow_bridging():
return await evt.reply("This bridge doesn't allow bridging that Telegram chat.\n"
"If you're the bridge admin, try"
"If you're the bridge admin, try "
"`$cmdprefix+sp whitelist <Telegram chat ID>` first.")
if portal.mxid:
has_portal_message = (
@@ -203,7 +221,7 @@ async def bridge(evt: CommandEvent):
"chat to this room, use `$cmdprefix+sp continue`")
async def cleanup_old_portal_while_bridging(evt, portal):
async def cleanup_old_portal_while_bridging(evt: CommandEvent, portal: po.Portal):
if not portal.mxid:
await evt.reply("The portal seems to have lost its Matrix room between you"
"calling `$cmdprefix+sp bridge` and this command.\n\n"
@@ -253,7 +271,7 @@ async def confirm_bridge(evt: CommandEvent):
"`$cmdprefix+sp cancel` to cancel.")
is_logged_in = await evt.sender.is_logged_in()
user = evt.sender if is_logged_in else evt.tgbot
user = evt.sender if is_logged_in else evt.tgbot
try:
entity = await user.client.get_entity(portal.peer)
except Exception:
@@ -300,7 +318,11 @@ async def _get_initial_state(evt: CommandEvent):
return title, about, levels
@command_handler()
@command_handler(help_section=SECTION_CREATING_PORTALS,
help_args="[_type_]",
help_text="Create a Telegram chat of the given type for the current Matrix room. "
"The type is either `group`, `supergroup` or `channel` (defaults to "
"`group`).")
async def create(evt: CommandEvent):
type = evt.args[0] if len(evt.args) > 0 else "group"
if type not in {"chat", "group", "supergroup", "channel"}:
@@ -331,7 +353,8 @@ async def create(evt: CommandEvent):
return await evt.reply(f"Telegram chat created. ID: {portal.tgid}")
@command_handler()
@command_handler(help_section=SECTION_PORTAL_MANAGEMENT,
help_text="Upgrade a normal Telegram group to a supergroup.")
async def upgrade(evt: CommandEvent):
portal = po.Portal.get_by_mxid(evt.room_id)
if not portal:
@@ -350,7 +373,10 @@ async def upgrade(evt: CommandEvent):
return await evt.reply(e.args[0])
@command_handler()
@command_handler(help_section=SECTION_PORTAL_MANAGEMENT,
help_args="<_name_|`-`>",
help_text="Change the username of a supergroup/channel. "
"To disable, use a dash (`-`) as the name.")
async def group_name(evt: CommandEvent):
if len(evt.args) == 0:
return await evt.reply("**Usage:** `$cmdprefix+sp group-name <name/->`")
@@ -382,7 +408,11 @@ async def group_name(evt: CommandEvent):
return await evt.reply("Invalid username")
@command_handler(needs_admin=True)
@command_handler(needs_admin=True,
help_section=SECTION_ADMIN,
help_args="<`whitelist`|`blacklist`>",
help_text="Change whether the bridge will allow or disallow bridging rooms by "
"default.")
async def filter_mode(evt: CommandEvent):
try:
mode = evt.args[0]
@@ -404,7 +434,10 @@ async def filter_mode(evt: CommandEvent):
"`!filter blacklist <chat ID>`.")
@command_handler(needs_admin=True)
@command_handler(needs_admin=True,
help_section=SECTION_ADMIN,
help_args="<`whitelist`|`blacklist`> <_chat ID_>",
help_text="Allow or disallow bridging a specific chat.")
async def filter(evt: CommandEvent):
try:
action = evt.args[0]
+22 -10
View File
@@ -20,11 +20,13 @@ from telethon.tl.functions.messages import ImportChatInviteRequest, CheckChatInv
from telethon.tl.functions.channels import JoinChannelRequest
from .. import puppet as pu, portal as po
from . import command_handler
from . import command_handler, CommandEvent, SECTION_MISC, SECTION_CREATING_PORTALS
@command_handler()
async def search(evt):
@command_handler(help_section=SECTION_MISC,
help_args="[_-r|--remote_] <_query_>",
help_text="Search your contacts or the Telegram servers for users.")
async def search(evt: CommandEvent):
if len(evt.args) == 0:
return await evt.reply("**Usage:** `$cmdprefix+sp search [-r|--remote] <query>`")
@@ -59,8 +61,14 @@ async def search(evt):
return await evt.reply("\n".join(reply))
@command_handler(name="pm")
async def private_message(evt):
@command_handler(name="pm",
help_section=SECTION_CREATING_PORTALS,
help_args="<_identifier_>",
help_text="Open a private chat with the given Telegram user. The identifier is "
"either the internal user ID, the username or the phone number. "
"**N.B.** The phone numbers you start chats with must already be in "
"your contacts.")
async def private_message(evt: CommandEvent):
if len(evt.args) == 0:
return await evt.reply("**Usage:** `$cmdprefix+sp pm <user identifier>`")
@@ -79,7 +87,7 @@ async def private_message(evt):
f"{pu.Puppet.get_displayname(user, False)}")
async def _join(evt, arg):
async def _join(evt: CommandEvent, arg: str):
if arg.startswith("joinchat/"):
invite_hash = arg[len("joinchat/"):]
try:
@@ -99,8 +107,10 @@ async def _join(evt, arg):
return await evt.sender.client(JoinChannelRequest(channel)), None
@command_handler()
async def join(evt):
@command_handler(help_section=SECTION_CREATING_PORTALS,
help_args="<_link_>",
help_text="Join a chat with an invite link.")
async def join(evt: CommandEvent):
if len(evt.args) == 0:
return await evt.reply("**Usage:** `$cmdprefix+sp join <invite link>`")
@@ -124,8 +134,10 @@ async def join(evt):
return await evt.reply(f"Created room for {portal.title}")
@command_handler()
async def sync(evt):
@command_handler(help_section=SECTION_MISC,
help_args="[`chats`|`contacts`|`me`]",
help_text="Synchronize your chat portals, contacts and/or own info.")
async def sync(evt: CommandEvent):
if len(evt.args) > 0:
sync_only = evt.args[0]
if sync_only not in ("chats", "contacts", "me"):
+2 -2
View File
@@ -23,7 +23,7 @@ from mautrix_appservice import MatrixRequestError, IntentError
from .user import User
from .portal import Portal
from .puppet import Puppet
from .commands import CommandHandler
from .commands import CommandProcessor
class MatrixHandler:
@@ -31,7 +31,7 @@ class MatrixHandler:
def __init__(self, context):
self.az, self.db, self.config, _, self.tgbot = context
self.commands = CommandHandler(context)
self.commands = CommandProcessor(context)
self.az.matrix_event_handler(self.handle_event)