Skip to content

Commit

Permalink
Merge commit '338a4d9761be565337e19739a953f4be25cccb63' into element-…
Browse files Browse the repository at this point in the history
…master
  • Loading branch information
Half-Shot committed Nov 8, 2021
2 parents 91978fe + 338a4d9 commit 2a60fdb
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 56 deletions.
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ RUN apk add --virtual .build-deps \
libffi-dev \
build-base \
&& sed -Ei 's/psycopg2-binary.+//' optional-requirements.txt \
&& pip3 install -r requirements.txt -r optional-requirements.txt \
# TODO: unpin Pillow here after it's updated in Alpine
&& pip3 install -r requirements.txt -r optional-requirements.txt 'pillow==8.2' \
&& apk del .build-deps

COPY . /opt/mautrix-telegram
Expand Down
5 changes: 4 additions & 1 deletion mautrix_telegram/abstract_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from .context import Context
from .config import Config
from .bot import Bot
from .__main__ import TelegramBridge

config: Optional['Config'] = None
# Value updated from config in init()
Expand All @@ -71,6 +72,7 @@ class AbstractUser(ABC):
loop: asyncio.AbstractEventLoop = None
log: TraceLogger
az: AppService
bridge: 'TelegramBridge'
relaybot: Optional['Bot']
ignore_incoming_bot_events: bool = True

Expand Down Expand Up @@ -196,7 +198,7 @@ async def _update_catch(self, update: TypeUpdate) -> None:
if not await self.update(update):
await self._update(update)
except Exception:
self.log.exception(f"Failed to handle Telegram update {update}")
self.log.exception("Failed to handle Telegram update")
UPDATE_ERRORS.labels(update_type=update_type).inc()
UPDATE_TIME.labels(update_type=update_type).observe(time.time() - start_time)

Expand Down Expand Up @@ -515,6 +517,7 @@ async def update_message(self, original_update: UpdateMessage) -> None:
def init(context: 'Context') -> None:
global config, MAX_DELETIONS
AbstractUser.az, config, AbstractUser.loop, AbstractUser.relaybot = context.core
AbstractUser.bridge = context.bridge
AbstractUser.ignore_incoming_bot_events = config["bridge.relaybot.ignore_own_incoming_events"]
AbstractUser.session_container = context.session_container
MAX_DELETIONS = config.get("bridge.max_telegram_delete", 10)
15 changes: 10 additions & 5 deletions mautrix_telegram/commands/telegram/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,13 @@
help_section=SECTION_AUTH,
help_text="Check if you're logged into Telegram.")
async def ping(evt: CommandEvent) -> EventID:
me = await evt.sender.client.get_me() if await evt.sender.is_logged_in() else None
if me:
human_tg_id = f"@{me.username}" if me.username else f"+{me.phone}"
return await evt.reply(f"You're logged in as {human_tg_id}")
if await evt.sender.is_logged_in():
me = await evt.sender.get_me()
if me:
human_tg_id = f"@{me.username}" if me.username else f"+{me.phone}"
return await evt.reply(f"You're logged in as {human_tg_id}")
else:
return await evt.reply("You were logged in, but there appears to have been an error.")
else:
return await evt.reply("You're not logged in.")

Expand Down Expand Up @@ -346,10 +349,12 @@ async def _finish_sign_in(evt: CommandEvent, user: User, login_as: 'u.User' = No
return await evt.reply(msg)


@command_handler(needs_auth=True,
@command_handler(needs_auth=False,
help_section=SECTION_AUTH,
help_text="Log out from Telegram.")
async def logout(evt: CommandEvent) -> EventID:
if not evt.sender.tgid:
return await evt.reply("You're not logged in")
if await evt.sender.log_out():
return await evt.reply("Logged out successfully.")
return await evt.reply("Failed to log out.")
15 changes: 15 additions & 0 deletions mautrix_telegram/example-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,21 @@ bridge:
# The prefix for commands. Only required in non-management rooms.
command_prefix: "!tg"

# Messages sent upon joining a management room.
# Markdown is supported. The defaults are listed below.
management_room_text:
# Sent when joining a room.
welcome: "Hello, I'm a Telegram bridge bot."
# Sent when joining a management room and the user is already logged in.
welcome_connected: "Use `help` for help."
# Sent when joining a management room and the user is not logged in.
welcome_unconnected: "Use `help` for help or `login` to log in."
# Optional extra text sent when joining a management room.
additional_help: ""

# Send each message separately (for readability in some clients)
management_room_multiple_messages: false

# Permissions for using the bridge.
# Permitted values:
# relaybot - Only use the bridge via the relaybot, no access to commands.
Expand Down
8 changes: 5 additions & 3 deletions mautrix_telegram/formatter/from_telegram.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from mautrix.errors import MatrixRequestError
from mautrix.appservice import IntentAPI
from mautrix.types import (TextMessageEventContent, RelatesTo, RelationType, Format, MessageType,
MessageEvent)
MessageEvent, EventType)

from .. import user as u, puppet as pu, portal as po
from ..types import TelegramID
Expand Down Expand Up @@ -129,12 +129,14 @@ async def _add_reply_header(source: 'AbstractUser', content: TextMessageEventCon
content.relates_to = RelatesTo(rel_type=RelationType.REPLY, event_id=msg.mxid)

try:
event: MessageEvent = await main_intent.get_event(msg.mx_room, msg.mxid)
event = await main_intent.get_event(msg.mx_room, msg.mxid)
if event.type == EventType.ROOM_ENCRYPTED and source.bridge.matrix.e2ee:
event = await source.bridge.matrix.e2ee.decrypt(event)
if isinstance(event.content, TextMessageEventContent):
event.content.trim_reply_fallback()
puppet = await pu.Puppet.get_by_mxid(event.sender, create=False)
content.set_reply(event, displayname=puppet.displayname if puppet else event.sender)
except MatrixRequestError:
except Exception:
log.exception("Failed to get event to add reply fallback")


Expand Down
17 changes: 0 additions & 17 deletions mautrix_telegram/matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,23 +115,6 @@ async def handle_puppet_invite(self, room_id: RoomID, puppet: pu.Puppet, inviter
await intent.send_notice(room_id, "This puppet will remain inactive until a "
"Telegram chat is created for this room.")

async def send_welcome_message(self, room_id: RoomID, inviter: 'u.User') -> None:
try:
is_management = len(await self.az.intent.get_room_members(room_id)) == 2
except MatrixError:
# The AS bot is not in the room.
return
cmd_prefix = self.commands.command_prefix
text = html = "Hello, I'm a Telegram bridge bot. "
if is_management and inviter.puppet_whitelisted and not await inviter.is_logged_in():
text += f"Use `{cmd_prefix} help` for help or `{cmd_prefix} login` to log in."
html += (f"Use <code>{cmd_prefix} help</code> for help"
f" or <code>{cmd_prefix} login</code> to log in.")
else:
text += f"Use `{cmd_prefix} help` for help."
html += f"Use <code>{cmd_prefix} help</code> for help."
await self.az.intent.send_notice(room_id, text=text, html=html)

async def handle_invite(self, room_id: RoomID, user_id: UserID, inviter: 'u.User',
event_id: EventID) -> None:
user = u.User.get_by_mxid(user_id, create=False)
Expand Down
3 changes: 2 additions & 1 deletion mautrix_telegram/portal/matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,8 @@ async def _handle_matrix_message(self, sender: 'u.User', content: MessageEventCo
await self._handle_matrix_file(sender_id, event_id, space, client, content, reply_to,
caption_content)
else:
self.log.trace("Unhandled Matrix event: %s", content)
self.log.debug("Didn't handle Matrix event {event_id} due to unknown msgtype {content.msgtype}")
self.log.trace("Unhandled Matrix event content: %s", content)

async def handle_matrix_unpin_all(self, sender: 'u.User', pin_event_id: EventID) -> None:
await sender.client(UnpinAllMessagesRequest(peer=self.peer))
Expand Down
18 changes: 10 additions & 8 deletions mautrix_telegram/portal/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
PhotoEmpty, InputChannel, InputUser, ChatPhotoEmpty, PeerUser, Photo, TypeChat, TypeInputPeer,
TypeUser, User, InputPeerPhotoFileLocation, ChatParticipantAdmin, ChannelParticipantAdmin,
ChatParticipantCreator, ChannelParticipantCreator, UserProfilePhoto, UserProfilePhotoEmpty,
InputPeerUser)
InputPeerUser, ChannelParticipantBanned)

from mautrix.errors import MForbidden
from mautrix.types import (RoomID, UserID, RoomCreatePreset, EventType, Membership,
Expand Down Expand Up @@ -509,14 +509,15 @@ def _get_base_power_levels(self, levels: PowerLevelStateEventContent = None,
levels.users[self.main_intent.mxid] = 100
return levels

@staticmethod
def _get_level_from_participant(participant: TypeParticipant) -> int:
@classmethod
def _get_level_from_participant(cls, participant: TypeParticipant,
levels: PowerLevelStateEventContent) -> int:
# TODO use the power level requirements to get better precision in channels
if isinstance(participant, (ChatParticipantAdmin, ChannelParticipantAdmin)):
return 50
return levels.state_default or 50
elif isinstance(participant, (ChatParticipantCreator, ChannelParticipantCreator)):
return 95
return 0
return levels.get_user_level(cls.az.bot_mxid) - 5
return levels.users_default or 0

@staticmethod
def _participant_to_power_levels(levels: PowerLevelStateEventContent,
Expand Down Expand Up @@ -547,7 +548,7 @@ async def _participants_to_power_levels(self, users: List[Union[TypeUser, TypePa

puppet = p.Puppet.get(TelegramID(participant.user_id))
user = u.User.get_by_tgid(TelegramID(participant.user_id))
new_level = self._get_level_from_participant(participant)
new_level = self._get_level_from_participant(participant, levels)

if user:
await user.register_portal(self)
Expand Down Expand Up @@ -802,7 +803,8 @@ async def _update_avatar(self, user: 'AbstractUser', photo: TypeChatPhoto,
@staticmethod
def _filter_participants(users: List[TypeUser], participants: List[TypeParticipant]
) -> Iterable[TypeUser]:
participant_map = {part.user_id: part for part in participants}
participant_map = {part.user_id: part for part in participants
if not isinstance(part, ChannelParticipantBanned)}
for user in users:
try:
user.participant = participant_map[user.id]
Expand Down
31 changes: 25 additions & 6 deletions mautrix_telegram/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
from telethon.tl.types import (TypeUpdate, UpdateNewMessage, UpdateNewChannelMessage,
UpdateShortChatMessage, UpdateShortMessage, User as TLUser, Chat,
ChatForbidden, UpdateFolderPeers, UpdatePinnedDialogs,
UpdateNotifySettings, NotifyPeer)
UpdateNotifySettings, NotifyPeer, InputUserSelf)
from telethon.tl.custom import Dialog
from telethon.tl.types.contacts import ContactsNotModified
from telethon.tl.functions.contacts import GetContactsRequest, SearchRequest
from telethon.tl.functions.account import UpdateStatusRequest
from telethon.errors import AuthKeyDuplicatedError
from telethon.tl.functions.users import GetUsersRequest
from telethon.errors import (AuthKeyDuplicatedError, UserDeactivatedError, UserDeactivatedBanError,
SessionRevokedError, UnauthorizedError)

from mautrix.client import Client
from mautrix.errors import MatrixRequestError, MNotFound
Expand Down Expand Up @@ -56,6 +58,7 @@
BridgeState.human_readable_errors.update({
"tg-not-connected": "Your Telegram connection failed",
"tg-auth-key-duplicated": "The bridge accidentally logged you out",
"tg-not-authenticated": "The stored auth token did not work",
})


Expand Down Expand Up @@ -226,6 +229,9 @@ async def start(self, delete_unless_authenticated: bool = False) -> 'User':
elif delete_unless_authenticated:
self.log.debug(f"Unauthenticated user {self.name} start()ed, deleting session...")
await self.client.disconnect()
if self.tgid:
await self.push_bridge_state(BridgeStateEvent.BAD_CREDENTIALS,
error="tg-not-authenticated")
self.client.session.delete()
return self

Expand Down Expand Up @@ -333,8 +339,22 @@ async def set_presence(self, online: bool = True) -> None:
if not self.is_bot:
await self.client(UpdateStatusRequest(offline=not online))

async def get_me(self) -> Optional[TLUser]:
try:
return (await self.client(GetUsersRequest([InputUserSelf()])))[0]
except UnauthorizedError as e:
self.log.error(f"Authorization error in get_me(): {e}")
await self.push_bridge_state(BridgeStateEvent.BAD_CREDENTIALS, error="tg-auth-error",
message=str(e), ttl=3600)
await self.stop()
return None

async def update_info(self, info: TLUser = None) -> None:
info = info or await self.client.get_me()
if not info:
info = await self.get_me()
if not info:
self.log.warning("get_me() returned None, aborting update_info()")
return
changed = False
if self.is_bot != info.bot:
self.is_bot = info.bot
Expand Down Expand Up @@ -378,12 +398,11 @@ async def log_out(self) -> bool:
self.tgid = None
await self.save()
ok = await self.client.log_out()
if not ok:
return False
self.client.session.delete()
self.delete()
await self.stop()
self._track_metric(METRIC_LOGGED_IN, False)
return True
return ok

def _search_local(self, query: str, max_results: int = 5, min_similarity: int = 45
) -> List[SearchResult]:
Expand Down
30 changes: 17 additions & 13 deletions mautrix_telegram/web/provisioning/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
from aiohttp import web

from telethon.utils import get_peer_id, resolve_id
from telethon.tl.types import ChatForbidden, ChannelForbidden, TypeChat
from telethon.tl.types import ChatForbidden, ChannelForbidden, TypeChat, InputUserSelf
from telethon.tl.functions.users import GetUsersRequest
from telethon.errors import (UserDeactivatedError, UserDeactivatedBanError, SessionRevokedError,
UnauthorizedError)

from mautrix.appservice import AppService
from mautrix.errors import MatrixRequestError, IntentError
Expand Down Expand Up @@ -294,16 +297,17 @@ async def get_user_info(self, request: web.Request) -> web.Response:

user_data = None
if await user.is_logged_in():
me = await user.client.get_me()
await user.update_info(me)
user_data = {
"id": user.tgid,
"username": user.username,
"first_name": me.first_name,
"last_name": me.last_name,
"phone": me.phone,
"is_bot": user.is_bot,
}
me = await user.get_me()
if me:
await user.update_info(me)
user_data = {
"id": user.tgid,
"username": user.username,
"first_name": me.first_name,
"last_name": me.last_name,
"phone": me.phone,
"is_bot": user.is_bot,
}
return web.json_response({
"telegram": user_data,
"mxid": user.mxid,
Expand Down Expand Up @@ -351,7 +355,7 @@ async def send_password(self, request: web.Request) -> web.Response:
return await self.post_login_password(user, data.get("password", ""))

async def logout(self, request: web.Request) -> web.Response:
_, user, err = await self.get_user_request_info(request, expect_logged_in=True,
_, user, err = await self.get_user_request_info(request, expect_logged_in=None,
require_puppeting=False,
want_data=False)
if err is not None:
Expand Down Expand Up @@ -461,7 +465,7 @@ async def get_user_request_info(self, request: web.Request,
Optional[web.Response]]):
err = self.check_authorization(request)
if err is not None:
return err
return None, None, err

data = None
if want_data and (request.method == "POST" or request.method == "PUT"):
Expand Down
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ commonmark>=0.8,<0.10
aiohttp>=3,<4
yarl>=1,<2
mautrix>=0.10.5,<0.11
telethon>=1.22,<1.24
#telethon>=1.22,<1.24
# Temporary patch for 64-bit IDs until upstream telethon 2.0 is ready
tulir-telethon==1.24.0a2
telethon-session-sqlalchemy>=0.2.14,<0.3
# as per t2bot reccomendations https://github.com/t2bot/mautrix-telegram/commit/77f6d5ca9d35e36b5cf1ba977497182364998090#diff-4d7c51b1efe9043e44439a949dfd92e5827321b34082903477fd04876edb7552R11-R12
psycopg2
Expand Down

0 comments on commit 2a60fdb

Please sign in to comment.