Merge pull request #79 from tulir/authless-relaybot-portals
Allow creating relaybot portals without any authenticated users
This commit is contained in:
+20
-10
@@ -87,20 +87,26 @@ bridge:
|
|||||||
# Allow logging in within Matrix. If false, the only way to log in is using the out-of-Matrix
|
# Allow logging in within Matrix. If false, the only way to log in is using the out-of-Matrix
|
||||||
# login website (see appservice.public config section)
|
# login website (see appservice.public config section)
|
||||||
allow_matrix_login: true
|
allow_matrix_login: true
|
||||||
|
# Whether or not to allow creating portals from Telegram.
|
||||||
|
authless_relaybot_portals: true
|
||||||
|
|
||||||
# The prefix for commands. Only required in non-management rooms.
|
# The prefix for commands. Only required in non-management rooms.
|
||||||
command_prefix: "!tg"
|
command_prefix: "!tg"
|
||||||
|
|
||||||
# Whitelist of user IDs that are allowed to use this bridge. Leave empty to disable.
|
# Permissions for using the bridge.
|
||||||
# You can enter a domain without the localpart to allow all users from that homeserver to use the bridge.
|
# Permitted values:
|
||||||
whitelist:
|
# relaybot - Only use the bridge via the relaybot, no access to commands.
|
||||||
- "internal.example.com"
|
# full - Full access to use the bridge via relaybot or logging in with Telegram account.
|
||||||
- "@user:public.example.com"
|
# admin - Full access to use the bridge and some extra administration commands.
|
||||||
|
# Permitted keys:
|
||||||
# Admins can do things like delete portal rooms. Here you must specify the exact MXID, domains
|
# * - All Matrix users
|
||||||
# are not accepted.
|
# domain - All users on that homeserver
|
||||||
admins:
|
# mxid - Specific user
|
||||||
- "@admin:internal.example.com"
|
permissions:
|
||||||
|
"*": "relaybot"
|
||||||
|
"example.com": "full"
|
||||||
|
"public.example.com": "full"
|
||||||
|
"@admin:example.com": "admin"
|
||||||
|
|
||||||
# Telegram config
|
# Telegram config
|
||||||
telegram:
|
telegram:
|
||||||
@@ -109,3 +115,7 @@ telegram:
|
|||||||
api_hash: tjyd5yge35lbodk1xwzw2jstp90k55qz
|
api_hash: tjyd5yge35lbodk1xwzw2jstp90k55qz
|
||||||
# (Optional) Create your own bot at https://t.me/BotFather
|
# (Optional) Create your own bot at https://t.me/BotFather
|
||||||
#bot_token: 123456789:ABCD-QBPd3VrWRhg623xYh07WUWErYA9eMI
|
#bot_token: 123456789:ABCD-QBPd3VrWRhg623xYh07WUWErYA9eMI
|
||||||
|
|
||||||
|
# The version of the config. The bridge will read this and automatically update the config if
|
||||||
|
# the schema has changed. For the latest version, check the example config.
|
||||||
|
version: 1
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ args = parser.parse_args()
|
|||||||
|
|
||||||
config = Config(args.config, args.registration)
|
config = Config(args.config, args.registration)
|
||||||
config.load()
|
config.load()
|
||||||
|
config.check_updates()
|
||||||
|
|
||||||
if args.generate_registration:
|
if args.generate_registration:
|
||||||
config.generate_registration()
|
config.generate_registration()
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ class AbstractUser:
|
|||||||
return self.logged_in and self.whitelisted
|
return self.logged_in and self.whitelisted
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
|
if not self.client:
|
||||||
|
self._init_client()
|
||||||
self.connected = await self.client.connect()
|
self.connected = await self.client.connect()
|
||||||
|
|
||||||
async def ensure_started(self, even_if_no_session=False):
|
async def ensure_started(self, even_if_no_session=False):
|
||||||
|
|||||||
+56
-2
@@ -15,6 +15,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
from telethon.tl.types import *
|
from telethon.tl.types import *
|
||||||
from telethon.errors import ChannelInvalidError, ChannelPrivateError
|
from telethon.errors import ChannelInvalidError, ChannelPrivateError
|
||||||
@@ -23,18 +24,20 @@ from telethon.tl.functions.channels import GetChannelsRequest
|
|||||||
|
|
||||||
from .abstract_user import AbstractUser
|
from .abstract_user import AbstractUser
|
||||||
from .db import BotChat
|
from .db import BotChat
|
||||||
from . import puppet as pu
|
from . import puppet as pu, portal as po, user as u
|
||||||
|
|
||||||
config = None
|
config = None
|
||||||
|
|
||||||
|
|
||||||
class Bot(AbstractUser):
|
class Bot(AbstractUser):
|
||||||
log = logging.getLogger("mau.bot")
|
log = logging.getLogger("mau.bot")
|
||||||
|
mxid_regex = re.compile("@.+:.+")
|
||||||
|
|
||||||
def __init__(self, token):
|
def __init__(self, token):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.token = token
|
self.token = token
|
||||||
self.whitelisted = True
|
self.whitelisted = True
|
||||||
|
self.username = None
|
||||||
self._init_client()
|
self._init_client()
|
||||||
self.chats = {chat.id: chat.type for chat in BotChat.query.all()}
|
self.chats = {chat.id: chat.type for chat in BotChat.query.all()}
|
||||||
|
|
||||||
@@ -48,6 +51,7 @@ class Bot(AbstractUser):
|
|||||||
async def post_login(self):
|
async def post_login(self):
|
||||||
info = await self.client.get_me()
|
info = await self.client.get_me()
|
||||||
self.tgid = info.id
|
self.tgid = info.id
|
||||||
|
self.username = info.username
|
||||||
self.mxid = pu.Puppet.get_mxid_from_id(self.tgid)
|
self.mxid = pu.Puppet.get_mxid_from_id(self.tgid)
|
||||||
|
|
||||||
chat_ids = [id for id, type in self.chats.items() if type == "chat"]
|
chat_ids = [id for id, type in self.chats.items() if type == "chat"]
|
||||||
@@ -85,10 +89,60 @@ class Bot(AbstractUser):
|
|||||||
self.db.delete(BotChat.query.get(id))
|
self.db.delete(BotChat.query.get(id))
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
||||||
|
async def handle_command_portal(self, portal, reply):
|
||||||
|
if not config["bridge.authless_relaybot_portals"]:
|
||||||
|
return await reply("This bridge doesn't allow portal creation/invites from Telegram.")
|
||||||
|
|
||||||
|
await portal.create_matrix_room(self)
|
||||||
|
if portal.mxid:
|
||||||
|
if portal.username:
|
||||||
|
return await reply(
|
||||||
|
f"Portal is public: [{portal.alias}](https://matrix.to/#/{portal.alias})")
|
||||||
|
else:
|
||||||
|
return await reply(
|
||||||
|
"Portal is not public. Use `/invite <mxid>` to get an invite.")
|
||||||
|
|
||||||
|
async def handle_command_invite(self, portal, reply, mxid):
|
||||||
|
if len(mxid) == 0:
|
||||||
|
return await reply("Usage: `/invite <mxid>`")
|
||||||
|
elif not portal.mxid:
|
||||||
|
return await reply("Portal does not have Matrix room. "
|
||||||
|
"Create one with /portal first.")
|
||||||
|
if not self.mxid_regex.match(mxid):
|
||||||
|
return await reply("That doesn't look like a Matrix ID.")
|
||||||
|
user = await u.User.get_by_mxid(mxid).ensure_started()
|
||||||
|
if not user.whitelisted:
|
||||||
|
return await reply("That user is not whitelisted to use the bridge.")
|
||||||
|
elif user.logged_in:
|
||||||
|
displayname = f"@{user.username}" if user.username else user.displayname
|
||||||
|
return await reply("That user seems to be logged in. "
|
||||||
|
f"Just invite [{displayname}](tg://user?id={user.tgid})")
|
||||||
|
else:
|
||||||
|
await portal.main_intent.invite(portal.mxid, user.mxid)
|
||||||
|
return await reply(f"Invited `{user.mxid}` to the portal.")
|
||||||
|
|
||||||
|
async def handle_command(self, message):
|
||||||
|
def reply(reply_text):
|
||||||
|
return self.client.send_message_super(message.to_id, reply_text)
|
||||||
|
|
||||||
|
text = message.message
|
||||||
|
portal = po.Portal.get_by_entity(message.to_id)
|
||||||
|
if text == "/portal":
|
||||||
|
await self.handle_command_portal(portal, reply)
|
||||||
|
elif text.startswith("/invite"):
|
||||||
|
await self.handle_command_invite(portal, reply, mxid=text[len("/invite "):])
|
||||||
|
|
||||||
async def update(self, update):
|
async def update(self, update):
|
||||||
if not isinstance(update, (UpdateNewMessage, UpdateNewChannelMessage)):
|
if not isinstance(update, (UpdateNewMessage, UpdateNewChannelMessage)):
|
||||||
return
|
return
|
||||||
elif not isinstance(update.message, MessageService):
|
|
||||||
|
is_command = (isinstance(update.message, Message)
|
||||||
|
and update.message.entities and len(update.message.entities) > 0
|
||||||
|
and isinstance(update.message.entities[0], MessageEntityBotCommand))
|
||||||
|
if is_command:
|
||||||
|
return await self.handle_command(update.message)
|
||||||
|
|
||||||
|
if not isinstance(update.message, MessageService):
|
||||||
return
|
return
|
||||||
|
|
||||||
to_id = update.message.to_id
|
to_id = update.message.to_id
|
||||||
|
|||||||
+104
-4
@@ -15,20 +15,22 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
from ruamel.yaml import YAML
|
from ruamel.yaml import YAML
|
||||||
|
from ruamel.yaml.comments import CommentedMap
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
|
||||||
yaml = YAML()
|
yaml = YAML()
|
||||||
|
yaml.indent(4)
|
||||||
|
|
||||||
|
|
||||||
class DictWithRecursion:
|
class DictWithRecursion:
|
||||||
def __init__(self, data=None):
|
def __init__(self, data=None):
|
||||||
self._data = data or {}
|
self._data = data or CommentedMap()
|
||||||
|
|
||||||
def _recursive_get(self, data, key, default_value):
|
def _recursive_get(self, data, key, default_value):
|
||||||
if '.' in key:
|
if '.' in key:
|
||||||
key, next_key = key.split('.', 1)
|
key, next_key = key.split('.', 1)
|
||||||
next_data = data.get(key, {})
|
next_data = data.get(key, CommentedMap())
|
||||||
return self._recursive_get(next_data, next_key, default_value)
|
return self._recursive_get(next_data, next_key, default_value)
|
||||||
return data.get(key, default_value)
|
return data.get(key, default_value)
|
||||||
|
|
||||||
@@ -44,8 +46,8 @@ class DictWithRecursion:
|
|||||||
if '.' in key:
|
if '.' in key:
|
||||||
key, next_key = key.split('.', 1)
|
key, next_key = key.split('.', 1)
|
||||||
if key not in data:
|
if key not in data:
|
||||||
data[key] = {}
|
data[key] = CommentedMap()
|
||||||
next_data = data.get(key, {})
|
next_data = data.get(key, CommentedMap())
|
||||||
self._recursive_set(next_data, next_key, value)
|
self._recursive_set(next_data, next_key, value)
|
||||||
return
|
return
|
||||||
data[key] = value
|
data[key] = value
|
||||||
@@ -59,6 +61,42 @@ class DictWithRecursion:
|
|||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
self.set(key, value)
|
self.set(key, value)
|
||||||
|
|
||||||
|
def _recursive_del(self, data, key):
|
||||||
|
if '.' in key:
|
||||||
|
key, next_key = key.split('.', 1)
|
||||||
|
if key not in data:
|
||||||
|
return
|
||||||
|
next_data = data[key]
|
||||||
|
self._recursive_del(next_data, next_key)
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
del data[key]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def delete(self, key, allow_recursion=True):
|
||||||
|
if allow_recursion and '.' in key:
|
||||||
|
self._recursive_del(self._data, key)
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
del self._data[key]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
self.delete(key)
|
||||||
|
|
||||||
|
def comment(self, key, message):
|
||||||
|
indent = key.count(".") * 4
|
||||||
|
try:
|
||||||
|
path, key = key.rsplit(".", 1)
|
||||||
|
except ValueError:
|
||||||
|
path = None
|
||||||
|
entry = self[path] if path else self._data
|
||||||
|
c = self._data.ca.items.setdefault(key, [None, [], None, None])
|
||||||
|
c[1] = []
|
||||||
|
entry.yaml_set_comment_before_after_key(key=key, before=message, indent=indent)
|
||||||
|
|
||||||
|
|
||||||
class Config(DictWithRecursion):
|
class Config(DictWithRecursion):
|
||||||
def __init__(self, path, registration_path):
|
def __init__(self, path, registration_path):
|
||||||
@@ -82,6 +120,68 @@ class Config(DictWithRecursion):
|
|||||||
def _new_token():
|
def _new_token():
|
||||||
return "".join(random.choice(string.ascii_lowercase + string.digits) for _ in range(64))
|
return "".join(random.choice(string.ascii_lowercase + string.digits) for _ in range(64))
|
||||||
|
|
||||||
|
def update_0_1(self):
|
||||||
|
permissions = self["bridge.permissions"] or CommentedMap()
|
||||||
|
for entry in self["bridge.whitelist"] or []:
|
||||||
|
permissions[entry] = "full"
|
||||||
|
for entry in self["bridge.admins"] or []:
|
||||||
|
permissions[entry] = "admin"
|
||||||
|
|
||||||
|
self["bridge.permissions"] = permissions
|
||||||
|
del self["bridge.whitelist"]
|
||||||
|
del self["bridge.admins"]
|
||||||
|
|
||||||
|
self["bridge.authless_relaybot_portals"] = self.get("bridge.authless_relaybot_portals",
|
||||||
|
True)
|
||||||
|
self.comment("bridge.authless_relaybot_portals",
|
||||||
|
"Whether or not to allow creating portals from Telegram.")
|
||||||
|
|
||||||
|
self.comment("bridge.permissions", "\n".join((
|
||||||
|
"",
|
||||||
|
"Permissions for using the bridge.",
|
||||||
|
"Permitted values:",
|
||||||
|
" relaybot - Only use the bridge via the relaybot, no access to commands.",
|
||||||
|
" full - Full access to use the bridge via relaybot or logging in with Telegram account.",
|
||||||
|
" admin - Full access to use the bridge and some extra administration commands.",
|
||||||
|
"Permitted keys:",
|
||||||
|
" * - All Matrix users",
|
||||||
|
" domain - All users on that homeserver",
|
||||||
|
" mxid - Specific user")))
|
||||||
|
# The telegram section comment disappears for some reason 3:
|
||||||
|
self.comment("telegram", "\nTelegram config")
|
||||||
|
|
||||||
|
self["version"] = 1
|
||||||
|
# Add newline before version
|
||||||
|
self.comment("version",
|
||||||
|
"\nThe version of the config. The bridge will read this and automatically "
|
||||||
|
"update the config if\nthe schema has changed. For the latest version, "
|
||||||
|
"check the example config.")
|
||||||
|
|
||||||
|
def check_updates(self):
|
||||||
|
if self.get("version", 0) == 0:
|
||||||
|
self.update_0_1()
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def _get_permissions(self, key):
|
||||||
|
level = self["bridge.permissions"].get(key, "")
|
||||||
|
admin = level == "admin"
|
||||||
|
whitelisted = level == "full" or admin
|
||||||
|
relaybot = level == "relaybot" or whitelisted
|
||||||
|
return relaybot, whitelisted, admin
|
||||||
|
|
||||||
|
def get_permissions(self, mxid):
|
||||||
|
permissions = self["bridge.permissions"] or {}
|
||||||
|
if mxid in permissions:
|
||||||
|
return self._get_permissions(mxid)
|
||||||
|
|
||||||
|
homeserver = mxid[mxid.index(":") + 1:]
|
||||||
|
if homeserver in permissions:
|
||||||
|
return self._get_permissions(homeserver)
|
||||||
|
|
||||||
|
return self._get_permissions("*")
|
||||||
|
|
||||||
def generate_registration(self):
|
def generate_registration(self):
|
||||||
homeserver = self["homeserver.domain"]
|
homeserver = self["homeserver.domain"]
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ class MatrixHandler:
|
|||||||
if not portal:
|
if not portal:
|
||||||
return
|
return
|
||||||
|
|
||||||
if not user.whitelisted:
|
if not user.relaybot_whitelisted:
|
||||||
await portal.main_intent.kick(room, user.mxid,
|
await portal.main_intent.kick(room, user.mxid,
|
||||||
"You are not whitelisted on this Telegram bridge.")
|
"You are not whitelisted on this Telegram bridge.")
|
||||||
return
|
return
|
||||||
@@ -169,7 +169,7 @@ class MatrixHandler:
|
|||||||
|
|
||||||
is_command, text = self.is_command(message)
|
is_command, text = self.is_command(message)
|
||||||
sender = await User.get_by_mxid(sender).ensure_started()
|
sender = await User.get_by_mxid(sender).ensure_started()
|
||||||
if not sender.whitelisted:
|
if not sender.relaybot_whitelisted:
|
||||||
return
|
return
|
||||||
|
|
||||||
portal = Portal.get_by_mxid(room)
|
portal = Portal.get_by_mxid(room)
|
||||||
@@ -177,7 +177,7 @@ class MatrixHandler:
|
|||||||
await portal.handle_matrix_message(sender, message, event_id)
|
await portal.handle_matrix_message(sender, message, event_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
if message["msgtype"] != "m.text":
|
if not sender.whitelisted or message["msgtype"] != "m.text":
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ class Portal:
|
|||||||
az = None
|
az = None
|
||||||
bot = None
|
bot = None
|
||||||
bridge_notices = False
|
bridge_notices = False
|
||||||
|
alias_template = None
|
||||||
mx_alias_regex = None
|
mx_alias_regex = None
|
||||||
hs_domain = None
|
hs_domain = None
|
||||||
mxid_regex = None
|
|
||||||
by_mxid = {}
|
by_mxid = {}
|
||||||
by_tgid = {}
|
by_tgid = {}
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ class Portal:
|
|||||||
|
|
||||||
if self.peer_type == "channel" and entity.username:
|
if self.peer_type == "channel" and entity.username:
|
||||||
public = True
|
public = True
|
||||||
alias = self._get_room_alias(entity.username)
|
alias = self._get_alias_localpart(entity.username)
|
||||||
self.username = entity.username
|
self.username = entity.username
|
||||||
else:
|
else:
|
||||||
public = False
|
public = False
|
||||||
@@ -281,8 +281,17 @@ class Portal:
|
|||||||
}
|
}
|
||||||
return levels
|
return levels
|
||||||
|
|
||||||
def _get_room_alias(self, username=None):
|
@property
|
||||||
return self.alias_template.format(groupname=username or self.username)
|
def alias(self):
|
||||||
|
if not self.username:
|
||||||
|
return None
|
||||||
|
return f"#{self._get_alias_localpart()}:{self.hs_domain}"
|
||||||
|
|
||||||
|
def _get_alias_localpart(self, username=None):
|
||||||
|
username = username or self.username
|
||||||
|
if not username:
|
||||||
|
return None
|
||||||
|
return self.alias_template.format(groupname=username)
|
||||||
|
|
||||||
async def sync_telegram_users(self, source, users):
|
async def sync_telegram_users(self, source, users):
|
||||||
allowed_tgids = set()
|
allowed_tgids = set()
|
||||||
@@ -361,10 +370,10 @@ class Portal:
|
|||||||
async def update_username(self, username):
|
async def update_username(self, username):
|
||||||
if self.username != username:
|
if self.username != username:
|
||||||
if self.username:
|
if self.username:
|
||||||
await self.main_intent.remove_room_alias(self._get_room_alias())
|
await self.main_intent.remove_room_alias(self._get_alias_localpart())
|
||||||
self.username = username or None
|
self.username = username or None
|
||||||
if self.username:
|
if self.username:
|
||||||
await self.main_intent.add_room_alias(self.mxid, self._get_room_alias())
|
await self.main_intent.add_room_alias(self.mxid, self._get_alias_localpart())
|
||||||
await self.main_intent.set_join_rule(self.mxid, "public")
|
await self.main_intent.set_join_rule(self.mxid, "public")
|
||||||
else:
|
else:
|
||||||
await self.main_intent.set_join_rule(self.mxid, "invite")
|
await self.main_intent.set_join_rule(self.mxid, "invite")
|
||||||
@@ -453,7 +462,7 @@ class Portal:
|
|||||||
if p.Puppet.get_id_from_mxid(member) or member == self.main_intent.mxid:
|
if p.Puppet.get_id_from_mxid(member) or member == self.main_intent.mxid:
|
||||||
continue
|
continue
|
||||||
user = await u.User.get_by_mxid(member).ensure_started()
|
user = await u.User.get_by_mxid(member).ensure_started()
|
||||||
if (has_bot and user.whitelisted) or user.has_full_access:
|
if (has_bot and user.relaybot_whitelisted) or user.has_full_access:
|
||||||
authenticated.append(user)
|
authenticated.append(user)
|
||||||
return authenticated
|
return authenticated
|
||||||
|
|
||||||
@@ -1156,7 +1165,7 @@ class Portal:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_entity(cls, entity, receiver_id=None):
|
def get_by_entity(cls, entity, receiver_id=None, create=True):
|
||||||
entity_type = type(entity)
|
entity_type = type(entity)
|
||||||
if entity_type in {Chat, ChatFull}:
|
if entity_type in {Chat, ChatFull}:
|
||||||
type_name = "chat"
|
type_name = "chat"
|
||||||
@@ -1178,7 +1187,9 @@ class Portal:
|
|||||||
id = entity.user_id
|
id = entity.user_id
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown entity type {entity_type.__name__}")
|
raise ValueError(f"Unknown entity type {entity_type.__name__}")
|
||||||
return cls.get_by_tgid(id, receiver_id if type_name == "user" else id, type_name)
|
return cls.get_by_tgid(id,
|
||||||
|
receiver_id if type_name == "user" else id,
|
||||||
|
type_name if create else None)
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ from telethon.tl.types import *
|
|||||||
|
|
||||||
|
|
||||||
class MautrixTelegramClient(TelegramClient):
|
class MautrixTelegramClient(TelegramClient):
|
||||||
|
def send_message_super(self, *args, **kwargs):
|
||||||
|
return super().send_message(*args, **kwargs)
|
||||||
|
|
||||||
async def send_message(self, entity, message, reply_to=None, entities=None, link_preview=True):
|
async def send_message(self, entity, message, reply_to=None, entities=None, link_preview=True):
|
||||||
entity = await self.get_input_entity(entity)
|
entity = await self.get_input_entity(entity)
|
||||||
|
|
||||||
|
|||||||
@@ -50,20 +50,14 @@ class User(AbstractUser):
|
|||||||
|
|
||||||
self.command_status = None
|
self.command_status = None
|
||||||
|
|
||||||
self.is_admin = self.mxid in config.get("bridge.admins", [])
|
(self.relaybot_whitelisted,
|
||||||
|
self.whitelisted,
|
||||||
whitelist = config.get("bridge.whitelist", None) or [self.mxid]
|
self.is_admin) = config.get_permissions(self.mxid)
|
||||||
self.whitelisted = not whitelist or self.mxid in whitelist
|
|
||||||
if not self.whitelisted:
|
|
||||||
homeserver = self.mxid[self.mxid.index(":") + 1:]
|
|
||||||
self.whitelisted = homeserver in whitelist
|
|
||||||
|
|
||||||
self.by_mxid[mxid] = self
|
self.by_mxid[mxid] = self
|
||||||
if tgid:
|
if tgid:
|
||||||
self.by_tgid[tgid] = self
|
self.by_tgid[tgid] = self
|
||||||
|
|
||||||
self._init_client()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return self.mxid
|
return self.mxid
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ setuptools.setup(
|
|||||||
"python-magic>=0.4.15,<0.5",
|
"python-magic>=0.4.15,<0.5",
|
||||||
],
|
],
|
||||||
dependency_links=[
|
dependency_links=[
|
||||||
("https://github.com/LonamiWebs/Telethon/tarball/6e854325a8e0e800a4f337257293d09006946162#egg=Telethon"
|
("https://github.com/LonamiWebs/Telethon/tarball/7998fd59f709ae1cd959c5cc4ab107982307f4a6#egg=Telethon"
|
||||||
if sys.version_info >= (3, 6)
|
if sys.version_info >= (3, 6)
|
||||||
else "https://github.com/tulir/Telethon/tarball/24dc21aea3305ef3bb8c7fcaef2025ae65d5c85e#egg=Telethon")
|
else "https://github.com/tulir/Telethon/tarball/ca08fe28800d74fd6c19fd6f473e12fbf2c258de#egg=Telethon")
|
||||||
],
|
],
|
||||||
|
|
||||||
classifiers=[
|
classifiers=[
|
||||||
|
|||||||
Reference in New Issue
Block a user