Compare commits

...

31 Commits

Author SHA1 Message Date
Tulir Asokan cb9665f9ab Bump version to 0.7.2 2020-04-04 22:05:01 +03:00
Tulir Asokan 69ffdcfed6 Bump version to 0.7.2rc1 2020-02-08 13:32:25 +02:00
Tulir Asokan da72c51644 Only leave group chat portals with default puppet. Fixes #418 2020-02-08 13:28:07 +02:00
Tulir Asokan 62efc39eed Fix ignore_incoming_bot_events check in channels
Fixes #417
2020-02-08 13:28:07 +02:00
Tulir Asokan 07edcc4867 Bump version to 0.7.1 2020-02-04 22:31:09 +02:00
Tulir Asokan 65d7934c21 Add missing response to logout provisioning API endpoint 2020-01-28 22:49:48 +02:00
Tulir Asokan 842d98dc1c Bump version to 0.7.1rc2 2020-01-25 23:37:18 +02:00
Tulir Asokan b7e69ddc61 Fix relaybot messages being allowed through with ignore_own_incoming_events set 2020-01-25 23:36:17 +02:00
Tulir Asokan 82f7905367 Add note to Matrix->Telegram EDU bridging 2020-01-13 20:46:00 +02:00
Tulir Asokan 1d8699054c Merge pull request #409 from cubesky/master
Fix mautrix-python import error.
2020-01-12 23:21:18 +02:00
天空/立音 32c521cb79 Fix mautrix-python import error.
Because of mautrix-python library [API Changes](https://github.com/tulir/mautrix-python/commit/04d2ae4c3d4db5f8798f4f844caafb5d00606507). Database migration script is broken.
2020-01-13 02:46:26 +08:00
Tulir Asokan b4cf8cd451 Bump version to 0.7.1rc1 2020-01-11 20:08:47 +02:00
Tulir Asokan 80ff9d0f66 Precalculate list of channel IDs to get info for to fix #393 2020-01-11 20:07:21 +02:00
Tulir Asokan b0e60e60e4 Fix parameter name error in has_power_level call 2020-01-11 19:58:08 +02:00
Tulir Asokan c4b9a76931 Merge pull request #406 from Ma27/fix-tests
Fix several broken tests that were missing some required positional arguments
2019-12-28 14:56:21 +02:00
Maximilian Bosch fe52f0ad10 Fix several broken tests that were missing some required positional arguments 2019-12-28 13:00:39 +01:00
Tulir Asokan a9abf9a1af Bump version to 0.7.0 2019-12-28 01:29:38 +02:00
Tulir Asokan 815f9605f9 Bump version to 0.7.0rc4 2019-12-25 16:31:00 +02:00
Tulir Asokan 9a9d6fc0bb Fix handling m.emotes 2019-12-25 16:29:22 +02:00
Tulir Asokan 2f691bf1b8 Bump version to 0.7.0rc3 2019-12-25 16:01:39 +02:00
Tulir Asokan 50984dab14 Trust displaynames from non-contacts when syncing puppets 2019-12-25 15:49:35 +02:00
Tulir Asokan 6f6ce4bcc7 Try deleting sources in docker image 2019-12-23 19:44:44 +02:00
Tulir Asokan 119729393c Restore git for version info in CI builds 2019-12-23 19:30:55 +02:00
Tulir Asokan 9f3869e878 Try to fix version info in CI builds again 2019-12-23 19:15:36 +02:00
Tulir Asokan 9fb2a73ec5 Update mautrix-python to handle invites separately from leaves. Fixes #402 2019-12-21 21:02:41 +02:00
Tulir Asokan 64b3699b3c Only print stack traces for admins. Fixes #392 2019-12-21 20:46:49 +02:00
Tulir Asokan 76ad31a3bc Update to Alpine 3.11 and fix version info in CI builds 2019-12-21 20:45:02 +02:00
Tulir Asokan 71cdee5a4d Fix crash when login shared secret is not enabled 2019-12-15 19:04:43 +02:00
Tulir Asokan 2ae4b23528 Add option to log in to custom puppet with shared secret 2019-12-15 18:50:07 +02:00
Tulir Asokan 39927ac6c0 Try to fix cleaning up rooms
Not tested at all
2019-12-11 10:03:05 +02:00
Tulir Asokan 3e6e59db29 Add postgres password field to example helm chart values 2019-12-06 15:57:53 +02:00
23 changed files with 106 additions and 50 deletions
+4 -2
View File
@@ -18,7 +18,7 @@ RUN apk add --no-cache libpng libpng-dev zlib zlib-dev \
&& git checkout 543c1d23ac9322f4f03c7fb6612ea7d026d44ac0 \ && git checkout 543c1d23ac9322f4f03c7fb6612ea7d026d44ac0 \
&& make && make
FROM docker.io/alpine:3.10 FROM docker.io/alpine:3.11
ENV UID=1337 \ ENV UID=1337 \
GID=1337 \ GID=1337 \
@@ -52,7 +52,7 @@ RUN apk add --no-cache --virtual .build-deps \
py3-markupsafe \ py3-markupsafe \
#moviepy #moviepy
py3-decorator \ py3-decorator \
#py3-tqdm \ py3-tqdm \
py3-requests \ py3-requests \
#imageio #imageio
py3-numpy \ py3-numpy \
@@ -68,6 +68,8 @@ RUN apk add --no-cache --virtual .build-deps \
# lottieconverter # lottieconverter
zlib libpng \ zlib libpng \
&& pip3 install .[speedups,hq_thumbnails,metrics] \ && pip3 install .[speedups,hq_thumbnails,metrics] \
# pip installs the sources to /usr/lib/python3.8/site-packages, so we don't need them here
&& rm -rf /opt/mautrix-telegram/mautrix_telegram \
&& apk del .build-deps && apk del .build-deps
VOLUME /data VOLUME /data
+4 -3
View File
@@ -6,9 +6,9 @@
* [x] Message edits * [x] Message edits
* [ ] ‡ Message history * [ ] ‡ Message history
* [x] Presence * [x] Presence
* [x] Typing notifications * [x] Typing notifications*
* [x] Read receipts * [x] Read receipts*
* [x] Pinning messages * [x] Pinning messages*
* [x] Power level * [x] Power level
* [x] Normal chats * [x] Normal chats
* [ ] Non-hardcoded PL requirements * [ ] Non-hardcoded PL requirements
@@ -56,5 +56,6 @@
* [ ] ‡ Secret chats (not yet supported by Telethon) * [ ] ‡ Secret chats (not yet supported by Telethon)
* [ ] ‡ E2EE in Matrix rooms (not yet supported * [ ] ‡ E2EE in Matrix rooms (not yet supported
\* Requires [double puppeting](https://github.com/tulir/mautrix-telegram/wiki/Authentication#replacing-telegram-accounts-matrix-puppet-with-matrix-account) to be enabled
† Information not automatically sent from source, i.e. implementation may not be possible † Information not automatically sent from source, i.e. implementation may not be possible
‡ Maybe, i.e. this feature may or may not be implemented at some point ‡ Maybe, i.e. this feature may or may not be implemented at some point
+6
View File
@@ -154,6 +154,12 @@ bridge:
# Whether or not to use /sync to get presence, read receipts and typing notifications when using # Whether or not to use /sync to get presence, read receipts and typing notifications when using
# your own Matrix account as the Matrix puppet for your Telegram account. # your own Matrix account as the Matrix puppet for your Telegram account.
sync_with_custom_puppets: true sync_with_custom_puppets: true
# Shared secret for https://github.com/devture/matrix-synapse-shared-secret-auth
#
# If set, custom puppets will be enabled automatically for local users
# instead of users having to find an access token and run `login-matrix`
# manually.
login_shared_secret: null
# Set to false to disable link previews in messages sent to Telegram. # Set to false to disable link previews in messages sent to Telegram.
telegram_link_preview: true telegram_link_preview: true
# Use inline images instead of a separate message for the caption. # Use inline images instead of a separate message for the caption.
+1
View File
@@ -35,6 +35,7 @@ affinity: {}
postgresql: postgresql:
enabled: true enabled: true
postgresqlDatabase: mxtg postgresqlDatabase: mxtg
postgresqlPassword: SET TO RANDOM STRING
persistence: persistence:
size: 2Gi size: 2Gi
resources: resources:
+1 -1
View File
@@ -1,2 +1,2 @@
__version__ = "0.7.0rc2" __version__ = "0.7.2"
__author__ = "Tulir Asokan <tulir@maunium.net>" __author__ = "Tulir Asokan <tulir@maunium.net>"
+3 -3
View File
@@ -97,7 +97,6 @@ class AbstractUser(ABC):
self.client = None self.client = None
self.is_relaybot = False self.is_relaybot = False
self.is_bot = False self.is_bot = False
self.relaybot = None
@property @property
def connected(self) -> bool: def connected(self) -> bool:
@@ -422,8 +421,9 @@ class AbstractUser(ABC):
f" in unbridged chat {portal.tgid_log}") f" in unbridged chat {portal.tgid_log}")
return return
if self.ignore_incoming_bot_events and self.relaybot and sender.id == self.relaybot.tgid: if ((self.ignore_incoming_bot_events and self.relaybot
self.log.debug(f"Ignoring relaybot-sent message %s to %s", update, portal.tgid_log) and sender and sender.id == self.relaybot.tgid)):
self.log.debug(f"Ignoring relaybot-sent message %s to %s", update.id, portal.tgid_log)
return return
if isinstance(update, MessageService): if isinstance(update, MessageService):
+2 -2
View File
@@ -108,9 +108,9 @@ class Bot(AbstractUser):
if isinstance(chat, ChatForbidden) or chat.left or chat.deactivated: if isinstance(chat, ChatForbidden) or chat.left or chat.deactivated:
self.remove_chat(TelegramID(chat.id)) self.remove_chat(TelegramID(chat.id))
channel_ids = (InputChannel(chat_id, 0) channel_ids = [InputChannel(chat_id, 0)
for chat_id, chat_type in self.chats.items() for chat_id, chat_type in self.chats.items()
if chat_type == "channel") if chat_type == "channel"]
for channel_id in channel_ids: for channel_id in channel_ids:
try: try:
await self.client(GetChannelsRequest([channel_id])) await self.client(GetChannelsRequest([channel_id]))
+1 -1
View File
@@ -169,7 +169,7 @@ async def execute_room_cleanup(evt, rooms_to_clean: List[Union[po.Portal, RoomID
await room.cleanup_and_delete() await room.cleanup_and_delete()
cleaned += 1 cleaned += 1
else: else:
await po.Portal.cleanup_room(evt.az.intent, room, message="Room deleted") await po.Portal.cleanup_room(evt.az.intent, room, "Room deleted")
cleaned += 1 cleaned += 1
evt.sender.command_status = None evt.sender.command_status = None
await evt.reply(f"{cleaned} rooms cleaned up successfully.") await evt.reply(f"{cleaned} rooms cleaned up successfully.")
+4
View File
@@ -51,6 +51,10 @@ class CommandEvent(BaseCommandEvent):
self.config = processor.config self.config = processor.config
self.public_website = processor.public_website 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: async def get_help_key(self) -> HelpCacheKey:
return HelpCacheKey(self.is_management, self.is_portal, self.sender.puppet_whitelisted, return HelpCacheKey(self.is_management, self.is_portal, self.sender.puppet_whitelisted,
self.sender.matrix_puppet_whitelisted, self.sender.is_admin, self.sender.matrix_puppet_whitelisted, self.sender.is_admin,
+3 -5
View File
@@ -113,12 +113,10 @@ async def cleanup_old_portal_while_bridging(evt: CommandEvent, portal: "po.Porta
"Continuing without touching previous Matrix room...") "Continuing without touching previous Matrix room...")
return True, None return True, None
elif evt.args[0] == "delete-and-continue": elif evt.args[0] == "delete-and-continue":
return True, portal.cleanup_room(portal.main_intent, portal.mxid, return True, portal.cleanup_portal("Portal deleted (moving to another room)")
message="Portal deleted (moving to another room)")
elif evt.args[0] == "unbridge-and-continue": elif evt.args[0] == "unbridge-and-continue":
return True, portal.cleanup_room(portal.main_intent, portal.mxid, return True, portal.cleanup_portal("Room unbridged (portal moving to another room)",
message="Room unbridged (portal moving to another room)", puppets_only=True)
puppets_only=True)
else: else:
await evt.reply( await evt.reply(
"The chat you were trying to bridge already has a Matrix portal room.\n\n" "The chat you were trying to bridge already has a Matrix portal room.\n\n"
+6 -3
View File
@@ -20,7 +20,8 @@ from telethon.errors import ( # isort: skip
AccessTokenExpiredError, AccessTokenInvalidError, FirstNameInvalidError, FloodWaitError, AccessTokenExpiredError, AccessTokenInvalidError, FirstNameInvalidError, FloodWaitError,
PasswordHashInvalidError, PhoneCodeExpiredError, PhoneCodeInvalidError, PasswordHashInvalidError, PhoneCodeExpiredError, PhoneCodeInvalidError,
PhoneNumberAppSignupForbiddenError, PhoneNumberBannedError, PhoneNumberFloodError, PhoneNumberAppSignupForbiddenError, PhoneNumberBannedError, PhoneNumberFloodError,
PhoneNumberOccupiedError, PhoneNumberUnoccupiedError, SessionPasswordNeededError) PhoneNumberOccupiedError, PhoneNumberUnoccupiedError, SessionPasswordNeededError,
PhoneNumberInvalidError)
from mautrix.types import EventID from mautrix.types import EventID
@@ -84,7 +85,7 @@ async def enter_code_register(evt: CommandEvent) -> EventID:
await evt.sender.ensure_started(even_if_no_session=True) await evt.sender.ensure_started(even_if_no_session=True)
first_name, last_name = evt.sender.command_status["full_name"] first_name, last_name = evt.sender.command_status["full_name"]
user = await evt.sender.client.sign_up(evt.args[0], first_name, last_name) user = await evt.sender.client.sign_up(evt.args[0], first_name, last_name)
asyncio.ensure_future(evt.sender.post_login(user), loop=evt.loop) asyncio.ensure_future(evt.sender.post_login(user, first_login=True), loop=evt.loop)
evt.sender.command_status = None evt.sender.command_status = None
return await evt.reply(f"Successfully registered to Telegram.") return await evt.reply(f"Successfully registered to Telegram.")
except PhoneNumberOccupiedError: except PhoneNumberOccupiedError:
@@ -166,6 +167,8 @@ async def _request_code(evt: CommandEvent, phone_number: str, next_status: Dict[
except PhoneNumberUnoccupiedError: except PhoneNumberUnoccupiedError:
return await evt.reply("That phone number has not been registered. " return await evt.reply("That phone number has not been registered. "
"Please register with `$cmdprefix+sp register <phone>`.") "Please register with `$cmdprefix+sp register <phone>`.")
except PhoneNumberInvalidError:
return await evt.reply("That phone number is not valid.")
except Exception: except Exception:
evt.log.exception("Error requesting phone code") evt.log.exception("Error requesting phone code")
return await evt.reply("Unhandled exception while requesting code. " return await evt.reply("Unhandled exception while requesting code. "
@@ -244,7 +247,7 @@ async def _sign_in(evt: CommandEvent, **sign_in_info) -> EventID:
await evt.reply(f"[{existing_user.displayname}]" await evt.reply(f"[{existing_user.displayname}]"
f"(https://matrix.to/#/{existing_user.mxid})" f"(https://matrix.to/#/{existing_user.mxid})"
" was logged out from the account.") " was logged out from the account.")
asyncio.ensure_future(evt.sender.post_login(user), loop=evt.loop) asyncio.ensure_future(evt.sender.post_login(user, first_login=True), loop=evt.loop)
evt.sender.command_status = None evt.sender.command_status = None
name = f"@{user.username}" if user.username else f"+{user.phone}" name = f"@{user.username}" if user.username else f"+{user.phone}"
return await evt.reply(f"Successfully logged in as {name}") return await evt.reply(f"Successfully logged in as {name}")
+1
View File
@@ -109,6 +109,7 @@ class Config(BaseBridgeConfig):
copy("bridge.plaintext_highlights") copy("bridge.plaintext_highlights")
copy("bridge.public_portals") copy("bridge.public_portals")
copy("bridge.sync_with_custom_puppets") copy("bridge.sync_with_custom_puppets")
copy("bridge.login_shared_secret")
copy("bridge.telegram_link_preview") copy("bridge.telegram_link_preview")
copy("bridge.inline_images") copy("bridge.inline_images")
copy("bridge.image_as_file_size") copy("bridge.image_as_file_size")
+5 -5
View File
@@ -1,4 +1,5 @@
import subprocess import subprocess
import shutil
import os import os
from . import __version__ from . import __version__
@@ -14,8 +15,7 @@ cmd_env = {
def run(cmd): def run(cmd):
return subprocess.check_output(cmd, stderr=subprocess.DEVNULL, env=cmd_env) return subprocess.check_output(cmd, stderr=subprocess.DEVNULL, env=cmd_env)
if os.path.exists(".git") and shutil.which("git"):
if os.path.exists(".git"):
try: try:
git_revision = run(["git", "rev-parse", "HEAD"]).strip().decode("ascii") git_revision = run(["git", "rev-parse", "HEAD"]).strip().decode("ascii")
git_revision_url = f"https://github.com/tulir/mautrix-telegram/commit/{git_revision}" git_revision_url = f"https://github.com/tulir/mautrix-telegram/commit/{git_revision}"
@@ -26,15 +26,15 @@ if os.path.exists(".git"):
try: try:
git_tag = run(["git", "describe", "--exact-match", "--tags"]).strip().decode("ascii") git_tag = run(["git", "describe", "--exact-match", "--tags"]).strip().decode("ascii")
git_tag_url = f"https://github.com/tulir/mautrix-telegram/releases/tag/{git_tag}"
except (subprocess.SubprocessError, OSError): except (subprocess.SubprocessError, OSError):
git_tag = None git_tag = None
git_tag_url = None
else: else:
git_revision = "unknown" git_revision = "unknown"
git_revision_url = None git_revision_url = None
git_tag = None git_tag = None
git_tag_url = None
git_tag_url = (f"https://github.com/tulir/mautrix-telegram/releases/tag/{git_tag}"
if git_tag else None)
if git_tag and __version__ == git_tag[1:].replace("-", ""): if git_tag and __version__ == git_tag[1:].replace("-", ""):
version = __version__ version = __version__
+5
View File
@@ -215,6 +215,11 @@ class MatrixHandler(BaseMatrixHandler):
event_id: EventID) -> None: event_id: EventID) -> None:
await self.handle_kick_ban(False, room_id, user_id, kicked_by, reason, event_id) await self.handle_kick_ban(False, room_id, user_id, kicked_by, reason, event_id)
async def handle_unban(self, room_id: RoomID, user_id: UserID, unbanned_by: UserID,
reason: str, event_id: EventID) -> None:
# TODO handle unbans properly instead of handling it as a kick
await self.handle_kick_ban(False, room_id, user_id, unbanned_by, reason, event_id)
async def handle_ban(self, room_id: RoomID, user_id: UserID, banned_by: UserID, reason: str, async def handle_ban(self, room_id: RoomID, user_id: UserID, banned_by: UserID, reason: str,
event_id: EventID) -> None: event_id: EventID) -> None:
await self.handle_kick_ban(True, room_id, user_id, banned_by, reason, event_id) await self.handle_kick_ban(True, room_id, user_id, banned_by, reason, event_id)
+14 -10
View File
@@ -222,7 +222,7 @@ class BasePortal(ABC):
return False return False
evt_type = EventType.find(f"net.maunium.telegram.{event}") evt_type = EventType.find(f"net.maunium.telegram.{event}")
evt_type.t_class = EventType.Class.STATE evt_type.t_class = EventType.Class.STATE
return self.main_intent.state_store.has_power_level(self.mxid, user.mxid, event=evt_type) return self.main_intent.state_store.has_power_level(self.mxid, user.mxid, evt_type)
def get_input_entity(self, user: 'AbstractUser' def get_input_entity(self, user: 'AbstractUser'
) -> Awaitable[Union[TypeInputPeer, TypeInputChannel]]: ) -> Awaitable[Union[TypeInputPeer, TypeInputChannel]]:
@@ -273,17 +273,13 @@ class BasePortal(ABC):
authenticated.append(user) authenticated.append(user)
return authenticated return authenticated
async def cleanup_room(self, intent: IntentAPI, room_id: RoomID, @staticmethod
message: str = "Portal deleted", puppets_only: bool = False) -> None: async def cleanup_room(intent: IntentAPI, room_id: RoomID, message: str,
puppets_only: bool = False) -> None:
try: try:
members = await intent.get_room_members(room_id) members = await intent.get_room_members(room_id)
except MatrixRequestError: except MatrixRequestError:
members = [] members = []
if self.username:
try:
await intent.remove_room_alias(self.alias_localpart)
except (MatrixRequestError, IntentError):
self.log.warning("Failed to remove alias when cleaning up room", exc_info=True)
for user in members: for user in members:
puppet = p.Puppet.get_by_mxid(UserID(user), create=False) puppet = p.Puppet.get_by_mxid(UserID(user), create=False)
if user != intent.mxid and (not puppets_only or puppet): if user != intent.mxid and (not puppets_only or puppet):
@@ -299,12 +295,20 @@ class BasePortal(ABC):
except (MatrixRequestError, IntentError): except (MatrixRequestError, IntentError):
self.log.warning("Failed to leave room when cleaning up room", exc_info=True) self.log.warning("Failed to leave room when cleaning up room", exc_info=True)
async def cleanup_portal(self, message: str, puppets_only: bool = False) -> None:
if self.username:
try:
await self.main_intent.remove_room_alias(self.alias_localpart)
except (MatrixRequestError, IntentError):
self.log.warning("Failed to remove alias when cleaning up room", exc_info=True)
await self.cleanup_room(self.main_intent, self.mxid, message, puppets_only)
async def unbridge(self) -> None: async def unbridge(self) -> None:
await self.cleanup_room(self.main_intent, self.mxid, "Room unbridged", puppets_only=True) await self.cleanup_portal("Room unbridged", puppets_only=True)
self.delete() self.delete()
async def cleanup_and_delete(self) -> None: async def cleanup_and_delete(self) -> None:
await self.cleanup_room(self.main_intent, self.mxid) await self.cleanup_portal("Portal deleted")
self.delete() self.delete()
# endregion # endregion
+1 -1
View File
@@ -367,7 +367,7 @@ class PortalMatrix(BasePortal, MautrixBasePortal, ABC):
if not bridge_notices and not excepted: if not bridge_notices and not excepted:
return return
if content.msgtype in (MessageType.TEXT, MessageType.NOTICE): if content.msgtype in (MessageType.TEXT, MessageType.EMOTE, MessageType.NOTICE):
await self._pre_process_matrix_message(sender, not logged_in, content) await self._pre_process_matrix_message(sender, not logged_in, content)
await self._handle_matrix_text(sender_id, event_id, space, client, content, reply_to) await self._handle_matrix_text(sender_id, event_id, space, client, content, reply_to)
elif content.msgtype == MessageType.LOCATION: elif content.msgtype == MessageType.LOCATION:
+11 -1
View File
@@ -25,7 +25,7 @@ from telethon.tl.types import (UserProfilePhoto, User, UpdateUserName, PeerUser,
from mautrix.appservice import AppService, IntentAPI from mautrix.appservice import AppService, IntentAPI
from mautrix.errors import MatrixRequestError from mautrix.errors import MatrixRequestError
from mautrix.bridge import CustomPuppetMixin from mautrix.bridge import CustomPuppetMixin
from mautrix.types import UserID, SyncToken from mautrix.types import UserID, SyncToken, RoomID
from mautrix.util.simple_template import SimpleTemplate from mautrix.util.simple_template import SimpleTemplate
from .types import TelegramID from .types import TelegramID
@@ -258,6 +258,8 @@ class Puppet(CustomPuppetMixin):
return False return False
allow_source = (source.is_relaybot allow_source = (source.is_relaybot
or self.displayname_source == source.tgid or self.displayname_source == source.tgid
# User is not a contact, so there's no custom name
or not info.contact
# No displayname source, so just trust anything # No displayname source, so just trust anything
or self.displayname_source is None) or self.displayname_source is None)
if not allow_source: if not allow_source:
@@ -318,6 +320,10 @@ class Puppet(CustomPuppetMixin):
return True return True
return False return False
def default_puppet_should_leave_room(self, room_id: RoomID) -> bool:
portal: p.Portal = p.Portal.get_by_mxid(room_id)
return portal and not portal.backfilling and portal.peer_type != "user"
# endregion # endregion
# region Getters # region Getters
@@ -424,4 +430,8 @@ def init(context: 'Context') -> Iterable[Awaitable[Any]]:
Puppet.displayname_template = SimpleTemplate(config["bridge.displayname_template"], Puppet.displayname_template = SimpleTemplate(config["bridge.displayname_template"],
"displayname") "displayname")
secret = config["bridge.login_shared_secret"]
Puppet.login_shared_secret = secret.encode("utf-8") if secret else None
Puppet.login_device_name = "Telegram Bridge"
return (puppet.try_start() for puppet in Puppet.all_with_custom_mxid()) return (puppet.try_start() for puppet in Puppet.all_with_custom_mxid())
@@ -24,7 +24,8 @@ def log(message, end="\n"):
def connect(to): def connect(to):
from mautrix.bridge.db import Base, RoomState, UserProfile from mautrix.util.db import Base
from mautrix.bridge.db import RoomState, UserProfile
from mautrix_telegram.db import (Portal, Message, UserPortal, User, Contact, Puppet, BotChat, from mautrix_telegram.db import (Portal, Message, UserPortal, User, Contact, Puppet, BotChat,
TelegramFile) TelegramFile)
+17 -4
View File
@@ -199,14 +199,27 @@ class User(AbstractUser, BaseUser):
self.client.session.delete() self.client.session.delete()
return self return self
async def post_login(self, info: TLUser = None) -> None: async def post_login(self, info: TLUser = None, first_login: bool = False) -> None:
try: try:
await self.update_info(info) await self.update_info(info)
if not self.is_bot and config["bridge.startup_sync"]: except Exception:
self.log.exception("Failed to update telegram account info")
return
try:
puppet = pu.Puppet.get(self.tgid)
if puppet.custom_mxid != self.mxid and puppet.can_auto_login(self.mxid):
self.log.info(f"Automatically enabling custom puppet")
await puppet.switch_mxid(access_token="auto", mxid=self.mxid)
except Exception:
self.log.exception("Failed to automatically enable custom puppet")
if not self.is_bot and config["bridge.startup_sync"]:
try:
await self.sync_dialogs() await self.sync_dialogs()
await self.sync_contacts() await self.sync_contacts()
except Exception: except Exception:
self.log.exception("Failed to run post-login functions for %s", self.mxid) self.log.exception("Failed to run post-login sync")
async def update(self, update: TypeUpdate) -> bool: async def update(self, update: TypeUpdate) -> bool:
if not self.is_bot: if not self.is_bot:
+1 -1
View File
@@ -119,7 +119,7 @@ class AuthAPI(abc.ABC):
existing_user = User.get_by_tgid(user_info.id) existing_user = User.get_by_tgid(user_info.id)
if existing_user and existing_user != user: if existing_user and existing_user != user:
await existing_user.log_out() await existing_user.log_out()
asyncio.ensure_future(user.post_login(user_info), loop=self.loop) asyncio.ensure_future(user.post_login(user_info, first_login=True), loop=self.loop)
if user.command_status and user.command_status["action"] == "Login": if user.command_status and user.command_status["action"] == "Login":
user.command_status = None user.command_status = None
@@ -149,11 +149,9 @@ class ProvisioningAPI(AuthAPI):
force = request.query.get("force", None) force = request.query.get("force", None)
if force in ("delete", "unbridge"): if force in ("delete", "unbridge"):
delete = force == "delete" delete = force == "delete"
await portal.cleanup_room(portal.main_intent, portal.mxid, puppets_only=not delete, await portal.cleanup_portal("Portal deleted (moving to another room)" if delete
message=("Portal deleted (moving to another room)" else "Room unbridged (portal moving to another room)",
if delete puppets_only=not delete)
else "Room unbridged (portal moving to another "
"room)"))
else: else:
return self.get_error_response(409, "chat_already_bridged", return self.get_error_response(409, "chat_already_bridged",
"Telegram chat is already bridged to another " "Telegram chat is already bridged to another "
@@ -357,6 +355,7 @@ class ProvisioningAPI(AuthAPI):
if err is not None: if err is not None:
return err return err
await user.log_out() await user.log_out()
return web.json_response({}, status=200)
async def bridge_info(self, request: web.Request) -> web.Response: async def bridge_info(self, request: web.Request) -> web.Response:
return web.json_response({ return web.json_response({
+1 -1
View File
@@ -42,7 +42,7 @@ setuptools.setup(
install_requires=[ install_requires=[
"aiohttp>=3.0.1,<4", "aiohttp>=3.0.1,<4",
"mautrix>=0.4.0rc2,<0.5", "mautrix>=0.4.0,<0.5",
"SQLAlchemy>=1.2.3,<2", "SQLAlchemy>=1.2.3,<2",
"alembic>=1.0.0,<2", "alembic>=1.0.0,<2",
"commonmark>=0.8.1,<0.10", "commonmark>=0.8.1,<0.10",
+9 -1
View File
@@ -26,7 +26,7 @@ def context(request: FixtureRequest) -> Context:
""" """
# Config(path, registration_path, base_path) # Config(path, registration_path, base_path)
config = getattr(request.cls, 'config', Config("", "", "")) config = getattr(request.cls, 'config', Config("", "", ""))
return Context(az=Mock(), config=config, loop=Mock(), session_container=Mock(), bot=Mock()) return Context(az=Mock(), config=config, loop=Mock(), session_container=Mock(), bridge=Mock(), bot=Mock())
@pytest.fixture @pytest.fixture
@@ -52,6 +52,7 @@ class TestCommandEvent:
sender=u.User(UserID("@sender:example.org")), sender=u.User(UserID("@sender:example.org")),
command="help", command="help",
args=[], args=[],
content=Mock(),
is_management=True, is_management=True,
is_portal=False, is_portal=False,
) )
@@ -107,6 +108,7 @@ class TestCommandEvent:
sender=u.User(UserID("@sender:example.org")), sender=u.User(UserID("@sender:example.org")),
command="help", command="help",
args=[], args=[],
content=Mock(),
is_management=False, is_management=False,
is_portal=False, is_portal=False,
) )
@@ -133,6 +135,7 @@ class TestCommandEvent:
sender=u.User(UserID("@sender:example.org")), sender=u.User(UserID("@sender:example.org")),
command="help", command="help",
args=[], args=[],
content=Mock(),
is_management=True, is_management=True,
is_portal=False, is_portal=False,
) )
@@ -209,6 +212,7 @@ class TestCommandHandler:
sender=sender, sender=sender,
command=command, command=command,
args=[], args=[],
content=Mock(),
is_management=False, is_management=False,
is_portal=boolean, is_portal=boolean,
) )
@@ -271,6 +275,7 @@ class TestCommandHandler:
sender=sender, sender=sender,
command=command, command=command,
args=[], args=[],
content=Mock(),
is_management=is_management, is_management=is_management,
is_portal=boolean, is_portal=boolean,
) )
@@ -307,6 +312,7 @@ class TestCommandProcessor:
sender=sender, sender=sender,
command="hElp", command="hElp",
args=[], args=[],
content=Mock(),
is_management=boolean2[0], is_management=boolean2[0],
is_portal=boolean2[1]) is_portal=boolean2[1])
@@ -333,6 +339,7 @@ class TestCommandProcessor:
sender=sender, sender=sender,
command="foo", command="foo",
args=[], args=[],
content=Mock(),
is_management=boolean2[0], is_management=boolean2[0],
is_portal=boolean2[1], is_portal=boolean2[1],
) )
@@ -361,6 +368,7 @@ class TestCommandProcessor:
sender=sender, # u.User sender=sender, # u.User
command="foo", command="foo",
args=[], args=[],
content=Mock(),
is_management=boolean2[0], is_management=boolean2[0],
is_portal=boolean2[1] is_portal=boolean2[1]
) )