Add sync command and move commands around

This commit is contained in:
Tulir Asokan
2018-02-25 22:22:11 +02:00
parent da3e37ccc0
commit 56e4f00705
4 changed files with 207 additions and 168 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
from .handler import command_handler, CommandHandler
from . import clean_rooms, auth, meta, telegram
from . import clean_rooms, auth, meta, telegram, portal
+10 -9
View File
@@ -54,18 +54,19 @@ def help(evt):
**logout** - Log out from Telegram.
**ping** - Check if you're logged into Telegram.
#### Initiating chats
#### Miscellaneous things
**search** [_-r|--remote_] <_query_> - Search your contacts or the Telegram servers for users.
**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`).
**sync** [`chats`|`contacts`|`me`] - Synchronize your chat portals, contacts and/or own info.
**ping-bot** - Get info of the message relay Telegram bot.
#### 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
**ping-bot** - Get info of the message relay Telegram bot.
**upgrade** - Upgrade a normal Telegram group to a supergroup.
**invite-link** - Get a Telegram invite link to the current chat.
**delete-portal** - Forget the current portal room. Only works for group chats; to delete
+182
View File
@@ -0,0 +1,182 @@
# -*- coding: future_fstrings -*-
# mautrix-telegram - A Matrix-Telegram puppeting bridge
# Copyright (C) 2018 Tulir Asokan
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from telethon.errors import *
from .. import portal as po
from . import command_handler
@command_handler()
async def invite_link(evt):
portal = po.Portal.get_by_mxid(evt.room_id)
if not portal:
return await evt.reply("This is not a portal room.")
if portal.peer_type == "user":
return await evt.reply("You can't invite users to private chats.")
try:
link = await portal.get_invite_link(evt.sender)
return await evt.reply(f"Invite link to {portal.title}: {link}")
except ValueError as e:
return await evt.reply(e.args[0])
except ChatAdminRequiredError:
return await evt.reply("You don't have the permission to create an invite link.")
@command_handler(needs_admin=True)
async def delete_portal(evt):
room_id = evt.args[0] if len(evt.args) > 0 else evt.room_id
portal = po.Portal.get_by_mxid(room_id)
if not portal:
that_this = "This" if room_id == evt.room_id else "That"
return await evt.reply(f"{that_this} is not a portal room.")
async def post_confirm(confirm):
evt.sender.command_status = None
if len(confirm.args) > 0 and confirm.args[0] == "confirm-delete":
await portal.cleanup_and_delete()
if confirm.room_id != room_id:
return await confirm.reply("Portal successfully deleted.")
else:
return await confirm.reply("Portal deletion cancelled.")
evt.sender.command_status = {
"next": post_confirm,
"action": "Portal deletion",
}
return await evt.reply("Please confirm deletion of portal "
f"[{room_id}](https://matrix.to/#/{room_id}) "
f"to Telegram chat \"{portal.title}\" "
"by typing `$cmdprefix+sp confirm-delete`")
async def _get_initial_state(evt):
state = await evt.az.intent.get_room_state(evt.room_id)
title = None
about = None
levels = None
for event in state:
if event["type"] == "m.room.name":
title = event["content"]["name"]
elif event["type"] == "m.room.topic":
about = event["content"]["topic"]
elif event["type"] == "m.room.power_levels":
levels = event["content"]
return title, about, levels
def _check_power_levels(levels, bot_mxid):
try:
if levels["users"][bot_mxid] < 100:
raise ValueError()
except (TypeError, KeyError, ValueError):
return (f"Please give [the bridge bot](https://matrix.to/#/{bot_mxid}) a power level of "
"100 before creating a Telegram chat.")
for user, level in levels["users"].items():
if level >= 100 and user != bot_mxid:
return (f"Please make sure only the bridge bot has power level above 99 before "
f"creating a Telegram chat.\n\n"
f"Use power level 95 instead of 100 for admins.")
@command_handler()
async def create(evt):
type = evt.args[0] if len(evt.args) > 0 else "group"
if type not in {"chat", "group", "supergroup", "channel"}:
return await evt.reply(
"**Usage:** `$cmdprefix+sp create ['group'/'supergroup'/'channel']`")
if po.Portal.get_by_mxid(evt.room_id):
return await evt.reply("This is already a portal room.")
title, about, levels = await _get_initial_state(evt)
if not title:
return await evt.reply("Please set a title before creating a Telegram chat.")
power_level_error = _check_power_levels(levels, evt.az.bot_mxid)
if power_level_error:
return await evt.reply(power_level_error)
supergroup = type == "supergroup"
type = {
"supergroup": "channel",
"channel": "channel",
"chat": "chat",
"group": "chat",
}[type]
portal = po.Portal(tgid=None, mxid=evt.room_id, title=title, about=about, peer_type=type)
try:
await portal.create_telegram_chat(evt.sender, supergroup=supergroup)
except ValueError as e:
portal.delete()
return await evt.reply(e.args[0])
return await evt.reply(f"Telegram chat created. ID: {portal.tgid}")
@command_handler()
async def upgrade(evt):
portal = po.Portal.get_by_mxid(evt.room_id)
if not portal:
return await evt.reply("This is not a portal room.")
elif portal.peer_type == "channel":
return await evt.reply("This is already a supergroup or a channel.")
elif portal.peer_type == "user":
return await evt.reply("You can't upgrade private chats.")
try:
await portal.upgrade_telegram_chat(evt.sender)
return await evt.reply(f"Group upgraded to supergroup. New ID: {portal.tgid}")
except ChatAdminRequiredError:
return await evt.reply("You don't have the permission to upgrade this group.")
except ValueError as e:
return await evt.reply(e.args[0])
@command_handler()
async def group_name(evt):
if len(evt.args) == 0:
return await evt.reply("**Usage:** `$cmdprefix+sp group-name <name/->`")
portal = po.Portal.get_by_mxid(evt.room_id)
if not portal:
return await evt.reply("This is not a portal room.")
elif portal.peer_type != "channel":
return await evt.reply("Only channels and supergroups have usernames.")
try:
await portal.set_telegram_username(evt.sender,
evt.args[0] if evt.args[0] != "-" else "")
if portal.username:
return await evt.reply(f"Username of channel changed to {portal.username}.")
else:
return await evt.reply(f"Channel is now private.")
except ChatAdminRequiredError:
return await evt.reply(
"You don't have the permission to set the username of this channel.")
except UsernameNotModifiedError:
if portal.username:
return await evt.reply("That is already the username of this channel.")
else:
return await evt.reply("This channel is already private")
except UsernameOccupiedError:
return await evt.reply("That username is already in use.")
except UsernameInvalidError:
return await evt.reply("Invalid username")
+14 -158
View File
@@ -75,52 +75,6 @@ async def private_message(evt):
f"{pu.Puppet.get_displayname(user, False)}")
@command_handler()
async def invite_link(evt):
portal = po.Portal.get_by_mxid(evt.room_id)
if not portal:
return await evt.reply("This is not a portal room.")
if portal.peer_type == "user":
return await evt.reply("You can't invite users to private chats.")
try:
link = await portal.get_invite_link(evt.sender)
return await evt.reply(f"Invite link to {portal.title}: {link}")
except ValueError as e:
return await evt.reply(e.args[0])
except ChatAdminRequiredError:
return await evt.reply("You don't have the permission to create an invite link.")
@command_handler(needs_admin=True)
async def delete_portal(evt):
room_id = evt.args[0] if len(evt.args) > 0 else evt.room_id
portal = po.Portal.get_by_mxid(room_id)
if not portal:
that_this = "This" if room_id == evt.room_id else "That"
return await evt.reply(f"{that_this} is not a portal room.")
async def post_confirm(confirm):
evt.sender.command_status = None
if len(confirm.args) > 0 and confirm.args[0] == "confirm-delete":
await portal.cleanup_and_delete()
if confirm.room_id != room_id:
return await confirm.reply("Portal successfully deleted.")
else:
return await confirm.reply("Portal deletion cancelled.")
evt.sender.command_status = {
"next": post_confirm,
"action": "Portal deletion",
}
return await evt.reply("Please confirm deletion of portal "
f"[{room_id}](https://matrix.to/#/{room_id}) "
f"to Telegram chat \"{portal.title}\" "
"by typing `$cmdprefix+sp confirm-delete`")
async def _join(evt, arg):
if arg.startswith("joinchat/"):
invite_hash = arg[len("joinchat/"):]
@@ -166,117 +120,19 @@ async def join(evt):
return await evt.reply(f"Created room for {portal.title}")
async def _get_initial_state(evt):
state = await evt.az.intent.get_room_state(evt.room_id)
title = None
about = None
levels = None
for event in state:
if event["type"] == "m.room.name":
title = event["content"]["name"]
elif event["type"] == "m.room.topic":
about = event["content"]["topic"]
elif event["type"] == "m.room.power_levels":
levels = event["content"]
return title, about, levels
def _check_power_levels(levels, bot_mxid):
try:
if levels["users"][bot_mxid] < 100:
raise ValueError()
except (TypeError, KeyError, ValueError):
return (f"Please give [the bridge bot](https://matrix.to/#/{bot_mxid}) a power level of "
"100 before creating a Telegram chat.")
for user, level in levels["users"].items():
if level >= 100 and user != bot_mxid:
return (f"Please make sure only the bridge bot has power level above 99 before "
f"creating a Telegram chat.\n\n"
f"Use power level 95 instead of 100 for admins.")
@command_handler()
async def create(evt):
type = evt.args[0] if len(evt.args) > 0 else "group"
if type not in {"chat", "group", "supergroup", "channel"}:
return await evt.reply(
"**Usage:** `$cmdprefix+sp create ['group'/'supergroup'/'channel']`")
async def sync(evt):
if len(evt.args) > 0:
sync_only = evt.args[0]
if sync_only not in ("chats", "contacts", "me"):
return await evt.reply("**Usage:** `$cmdprefix+sp sync [chats|contacts|me]`")
else:
sync_only = None
if po.Portal.get_by_mxid(evt.room_id):
return await evt.reply("This is already a portal room.")
title, about, levels = await _get_initial_state(evt)
if not title:
return await evt.reply("Please set a title before creating a Telegram chat.")
power_level_error = _check_power_levels(levels, evt.az.bot_mxid)
if power_level_error:
return await evt.reply(power_level_error)
supergroup = type == "supergroup"
type = {
"supergroup": "channel",
"channel": "channel",
"chat": "chat",
"group": "chat",
}[type]
portal = po.Portal(tgid=None, mxid=evt.room_id, title=title, about=about, peer_type=type)
try:
await portal.create_telegram_chat(evt.sender, supergroup=supergroup)
except ValueError as e:
portal.delete()
return await evt.reply(e.args[0])
return await evt.reply(f"Telegram chat created. ID: {portal.tgid}")
@command_handler()
async def upgrade(evt):
portal = po.Portal.get_by_mxid(evt.room_id)
if not portal:
return await evt.reply("This is not a portal room.")
elif portal.peer_type == "channel":
return await evt.reply("This is already a supergroup or a channel.")
elif portal.peer_type == "user":
return await evt.reply("You can't upgrade private chats.")
try:
await portal.upgrade_telegram_chat(evt.sender)
return await evt.reply(f"Group upgraded to supergroup. New ID: {portal.tgid}")
except ChatAdminRequiredError:
return await evt.reply("You don't have the permission to upgrade this group.")
except ValueError as e:
return await evt.reply(e.args[0])
@command_handler()
async def group_name(evt):
if len(evt.args) == 0:
return await evt.reply("**Usage:** `$cmdprefix+sp group-name <name/->`")
portal = po.Portal.get_by_mxid(evt.room_id)
if not portal:
return await evt.reply("This is not a portal room.")
elif portal.peer_type != "channel":
return await evt.reply("Only channels and supergroups have usernames.")
try:
await portal.set_telegram_username(evt.sender,
evt.args[0] if evt.args[0] != "-" else "")
if portal.username:
return await evt.reply(f"Username of channel changed to {portal.username}.")
else:
return await evt.reply(f"Channel is now private.")
except ChatAdminRequiredError:
return await evt.reply(
"You don't have the permission to set the username of this channel.")
except UsernameNotModifiedError:
if portal.username:
return await evt.reply("That is already the username of this channel.")
else:
return await evt.reply("This channel is already private")
except UsernameOccupiedError:
return await evt.reply("That username is already in use.")
except UsernameInvalidError:
return await evt.reply("Invalid username")
if not sync_only or sync_only == "chats":
await evt.sender.sync_dialogs()
if not sync_only or sync_only == "contacts":
await evt.sender.sync_contacts()
if not sync_only or sync_only == "me":
await evt.sender.update_info()
return await evt.reply("Synchronization complete.")