Switch from SQLAlchemy to asyncpg/aiosqlite
This commit is contained in:
@@ -1,7 +1,2 @@
|
||||
from .from_matrix import matrix_reply_to_telegram, matrix_to_telegram, init_mx
|
||||
from .from_matrix import matrix_reply_to_telegram, matrix_to_telegram
|
||||
from .from_telegram import telegram_reply_to_matrix, telegram_to_matrix
|
||||
from .. import context as c
|
||||
|
||||
|
||||
def init(context: c.Context) -> None:
|
||||
init_mx(context)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# mautrix-telegram - A Matrix-Telegram puppeting bridge
|
||||
# Copyright (C) 2019 Tulir Asokan
|
||||
# Copyright (C) 2021 Tulir Asokan
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -13,39 +13,77 @@
|
||||
#
|
||||
# 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, List, Tuple, Callable, Pattern, Match, TYPE_CHECKING
|
||||
import re
|
||||
import logging
|
||||
from __future__ import annotations
|
||||
|
||||
from telethon.tl.types import (MessageEntityMention, MessageEntityMentionName, MessageEntityItalic,
|
||||
TypeMessageEntity, InputMessageEntityMentionName)
|
||||
import re
|
||||
|
||||
from telethon.tl.types import MessageEntityItalic, TypeMessageEntity
|
||||
from telethon.helpers import add_surrogate, del_surrogate
|
||||
from telethon import TelegramClient
|
||||
|
||||
from mautrix.types import RoomID, MessageEventContent
|
||||
from mautrix.util.logging import TraceLogger
|
||||
|
||||
from ... import puppet as pu
|
||||
from ...types import TelegramID
|
||||
from ...db import Message as DBMessage
|
||||
from .parser import ParsedMessage, parse_html
|
||||
from .parser import MatrixParser
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ...context import Context
|
||||
|
||||
log: TraceLogger = logging.getLogger("mau.fmt.mx")
|
||||
should_bridge_plaintext_highlights: bool = False
|
||||
|
||||
command_regex: Pattern = re.compile(r"^!([A-Za-z0-9@]+)")
|
||||
not_command_regex: Pattern = re.compile(r"^\\(![A-Za-z0-9@]+)")
|
||||
plain_mention_regex: Optional[Pattern] = None
|
||||
command_regex = re.compile(r"^!([A-Za-z0-9@]+)")
|
||||
not_command_regex = re.compile(r"^\\(![A-Za-z0-9@]+)")
|
||||
|
||||
MAX_LENGTH = 4096
|
||||
CUTOFF_TEXT = " [message cut]"
|
||||
CUT_MAX_LENGTH = MAX_LENGTH - len(CUTOFF_TEXT)
|
||||
|
||||
|
||||
def _cut_long_message(message: str, entities: List[TypeMessageEntity]) -> ParsedMessage:
|
||||
class FormatError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
async def matrix_reply_to_telegram(
|
||||
content: MessageEventContent, tg_space: TelegramID, room_id: RoomID | None = None
|
||||
) -> TelegramID | None:
|
||||
event_id = content.get_reply_to()
|
||||
if not event_id:
|
||||
return
|
||||
content.trim_reply_fallback()
|
||||
|
||||
message = await DBMessage.get_by_mxid(event_id, room_id, tg_space)
|
||||
if message:
|
||||
return message.tgid
|
||||
return None
|
||||
|
||||
|
||||
async def matrix_to_telegram(
|
||||
client: TelegramClient, *, text: str | None = None, html: str | None = None
|
||||
) -> tuple[str, list[TypeMessageEntity]]:
|
||||
if html is not None:
|
||||
return await _matrix_html_to_telegram(client, html)
|
||||
elif text is not None:
|
||||
return _matrix_text_to_telegram(text), []
|
||||
else:
|
||||
raise ValueError("text or html must be provided to convert formatting")
|
||||
|
||||
|
||||
async def _matrix_html_to_telegram(
|
||||
client: TelegramClient, html: str
|
||||
) -> tuple[str, list[TypeMessageEntity]]:
|
||||
try:
|
||||
html = command_regex.sub(r"<command>\1</command>", html)
|
||||
html = html.replace("\t", " " * 4)
|
||||
html = not_command_regex.sub(r"\1", html)
|
||||
|
||||
parsed = await MatrixParser(client).parse(add_surrogate(html))
|
||||
text = del_surrogate(parsed.text.strip())
|
||||
text, entities = _cut_long_message(text, parsed.telegram_entities)
|
||||
|
||||
return text, entities
|
||||
except Exception as e:
|
||||
raise FormatError(f"Failed to convert Matrix format: {html}") from e
|
||||
|
||||
|
||||
def _cut_long_message(
|
||||
message: str, entities: list[TypeMessageEntity]
|
||||
) -> tuple[str, list[TypeMessageEntity]]:
|
||||
if len(message) > MAX_LENGTH:
|
||||
message = message[0:CUT_MAX_LENGTH] + CUTOFF_TEXT
|
||||
new_entities = []
|
||||
@@ -60,112 +98,8 @@ def _cut_long_message(message: str, entities: List[TypeMessageEntity]) -> Parsed
|
||||
return message, entities
|
||||
|
||||
|
||||
class FormatError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def matrix_reply_to_telegram(content: MessageEventContent, tg_space: TelegramID,
|
||||
room_id: Optional[RoomID] = None) -> Optional[TelegramID]:
|
||||
event_id = content.get_reply_to()
|
||||
if not event_id:
|
||||
return
|
||||
content.trim_reply_fallback()
|
||||
|
||||
message = DBMessage.get_by_mxid(event_id, room_id, tg_space)
|
||||
if message:
|
||||
return message.tgid
|
||||
return None
|
||||
|
||||
|
||||
async def matrix_to_telegram(client: TelegramClient, *, text: Optional[str] = None,
|
||||
html: Optional[str] = None) -> ParsedMessage:
|
||||
if html is not None:
|
||||
text, entities = _matrix_html_to_telegram(html)
|
||||
elif text is not None:
|
||||
text, entities = _matrix_text_to_telegram(text)
|
||||
else:
|
||||
raise ValueError("text or html must be provided to convert formatting")
|
||||
await _fix_name_mentions(client, entities)
|
||||
return text, entities
|
||||
|
||||
|
||||
def _matrix_html_to_telegram(html: str) -> ParsedMessage:
|
||||
try:
|
||||
html = command_regex.sub(r"<command>\1</command>", html)
|
||||
html = html.replace("\t", " " * 4)
|
||||
html = not_command_regex.sub(r"\1", html)
|
||||
if should_bridge_plaintext_highlights:
|
||||
html = plain_mention_regex.sub(_plain_mention_to_html, html)
|
||||
|
||||
text, entities = parse_html(add_surrogate(html))
|
||||
text = del_surrogate(text.strip())
|
||||
text, entities = _cut_long_message(text, entities)
|
||||
|
||||
return text, entities
|
||||
except Exception as e:
|
||||
raise FormatError(f"Failed to convert Matrix format: {html}") from e
|
||||
|
||||
|
||||
def _matrix_text_to_telegram(text: str) -> ParsedMessage:
|
||||
def _matrix_text_to_telegram(text: str) -> str:
|
||||
text = command_regex.sub(r"/\1", text)
|
||||
text = text.replace("\t", " " * 4)
|
||||
text = not_command_regex.sub(r"\1", text)
|
||||
if should_bridge_plaintext_highlights:
|
||||
entities, pmr_replacer = _plain_mention_to_text()
|
||||
text = plain_mention_regex.sub(pmr_replacer, text)
|
||||
else:
|
||||
entities = []
|
||||
return text, entities
|
||||
|
||||
|
||||
async def _fix_name_mentions(client: TelegramClient, entities: List[TypeMessageEntity]) -> None:
|
||||
for index in reversed(range(len(entities))):
|
||||
entity = entities[index]
|
||||
if isinstance(entity, (MessageEntityMentionName, InputMessageEntityMentionName)):
|
||||
try:
|
||||
user = await client.get_input_entity(entity.user_id)
|
||||
except (ValueError, TypeError) as e:
|
||||
log.trace(f"Dropping mention of {entity.user_id}: {e}")
|
||||
del entities[index]
|
||||
else:
|
||||
entities[index] = InputMessageEntityMentionName(entity.offset, entity.length, user)
|
||||
|
||||
|
||||
def _plain_mention_to_text() -> Tuple[List[TypeMessageEntity], Callable[[Match], str]]:
|
||||
entities = []
|
||||
|
||||
def replacer(match: Match) -> str:
|
||||
puppet = pu.Puppet.find_by_displayname(match.group(2))
|
||||
if puppet:
|
||||
offset = match.start()
|
||||
length = match.end() - offset
|
||||
if puppet.username:
|
||||
entity = MessageEntityMention(offset, length)
|
||||
text = f"@{puppet.username}"
|
||||
else:
|
||||
entity = MessageEntityMentionName(offset, length, user_id=puppet.tgid)
|
||||
text = puppet.displayname
|
||||
entities.append(entity)
|
||||
return text
|
||||
return "".join(match.groups())
|
||||
|
||||
return entities, replacer
|
||||
|
||||
|
||||
def _plain_mention_to_html(match: Match) -> str:
|
||||
puppet = pu.Puppet.find_by_displayname(match.group(2))
|
||||
if puppet:
|
||||
return (f"{match.group(1)}"
|
||||
f"<a href='https://matrix.to/#/{puppet.mxid}'>"
|
||||
f"{puppet.displayname}"
|
||||
"</a>")
|
||||
return "".join(match.groups())
|
||||
|
||||
|
||||
def init_mx(context: "Context") -> None:
|
||||
global plain_mention_regex, should_bridge_plaintext_highlights
|
||||
config = context.config
|
||||
dn_template = config["bridge.displayname_template"]
|
||||
dn_template = re.escape(dn_template).replace(re.escape("{displayname}"), "[^>]+")
|
||||
plain_mention_regex = re.compile(f"^({dn_template})")
|
||||
should_bridge_plaintext_highlights = config["bridge.plaintext_highlights"]
|
||||
return text
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# mautrix-telegram - A Matrix-Telegram puppeting bridge
|
||||
# Copyright (C) 2019 Tulir Asokan
|
||||
# Copyright (C) 2021 Tulir Asokan
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -13,77 +13,80 @@
|
||||
#
|
||||
# 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, Tuple, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from telethon.tl.types import TypeMessageEntity
|
||||
import logging
|
||||
|
||||
from telethon import TelegramClient
|
||||
|
||||
from mautrix.types import UserID, RoomID
|
||||
from mautrix.util.formatter import MatrixParser as BaseMatrixParser, RecursionContext
|
||||
from mautrix.util.formatter.html_reader_htmlparser import read_html, HTMLNode
|
||||
from mautrix.util.logging import TraceLogger
|
||||
|
||||
from ... import user as u, puppet as pu, portal as po
|
||||
from .telegram_message import TelegramMessage, TelegramEntityType
|
||||
|
||||
|
||||
ParsedMessage = Tuple[str, List[TypeMessageEntity]]
|
||||
|
||||
|
||||
def parse_html(input_html: str) -> ParsedMessage:
|
||||
msg = MatrixParser.parse(input_html)
|
||||
return msg.text, msg.telegram_entities
|
||||
log: TraceLogger = logging.getLogger("mau.fmt.mx")
|
||||
|
||||
|
||||
class MatrixParser(BaseMatrixParser[TelegramMessage]):
|
||||
e = TelegramEntityType
|
||||
fs = TelegramMessage
|
||||
read_html = read_html
|
||||
client: TelegramClient
|
||||
|
||||
@classmethod
|
||||
def custom_node_to_fstring(cls, node: HTMLNode, ctx: RecursionContext
|
||||
) -> Optional[TelegramMessage]:
|
||||
msg = cls.tag_aware_parse_node(node, ctx)
|
||||
def __init__(self, client: TelegramClient) -> None:
|
||||
self.client = client
|
||||
self.read_html = read_html
|
||||
|
||||
async def custom_node_to_fstring(
|
||||
self, node: HTMLNode, ctx: RecursionContext
|
||||
) -> TelegramMessage | None:
|
||||
msg = await self.tag_aware_parse_node(node, ctx)
|
||||
if node.tag == "command":
|
||||
msg.format(TelegramEntityType.COMMAND)
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def user_pill_to_fstring(cls, msg: TelegramMessage, user_id: UserID) -> TelegramMessage:
|
||||
user = (pu.Puppet.deprecated_sync_get_by_mxid(user_id)
|
||||
or u.User.get_by_mxid(user_id, create=False))
|
||||
async def user_pill_to_fstring(self, msg: TelegramMessage, user_id: UserID) -> TelegramMessage:
|
||||
user = (await pu.Puppet.get_by_mxid(user_id)
|
||||
or await u.User.get_by_mxid(user_id, create=False))
|
||||
if not user:
|
||||
return msg
|
||||
if user.username:
|
||||
return TelegramMessage(f"@{user.username}").format(TelegramEntityType.MENTION)
|
||||
if user.tg_username:
|
||||
return TelegramMessage(f"@{user.tg_username}").format(TelegramEntityType.MENTION)
|
||||
elif user.tgid:
|
||||
displayname = user.plain_displayname or msg.text
|
||||
return TelegramMessage(displayname).format(TelegramEntityType.MENTION_NAME,
|
||||
user_id=user.tgid)
|
||||
msg = TelegramMessage(displayname)
|
||||
try:
|
||||
input_entity = self.client.get_input_entity(user.tgid)
|
||||
except (ValueError, TypeError) as e:
|
||||
log.trace(f"Dropping mention of {user.tgid}: {e}")
|
||||
else:
|
||||
msg = msg.format(TelegramEntityType.MENTION_NAME, user_id=input_entity)
|
||||
return msg
|
||||
|
||||
@classmethod
|
||||
def url_to_fstring(cls, msg: TelegramMessage, url: str) -> TelegramMessage:
|
||||
async def url_to_fstring(self, msg: TelegramMessage, url: str) -> TelegramMessage:
|
||||
if url == msg.text:
|
||||
return msg.format(cls.e.URL)
|
||||
return msg.format(self.e.URL)
|
||||
else:
|
||||
return msg.format(cls.e.INLINE_URL, url=url)
|
||||
return msg.format(self.e.INLINE_URL, url=url)
|
||||
|
||||
@classmethod
|
||||
def room_pill_to_fstring(cls, msg: TelegramMessage, room_id: RoomID) -> TelegramMessage:
|
||||
async def room_pill_to_fstring(self, msg: TelegramMessage, room_id: RoomID) -> TelegramMessage:
|
||||
username = po.Portal.get_username_from_mx_alias(room_id)
|
||||
portal = po.Portal.find_by_username(username)
|
||||
portal = await po.Portal.find_by_username(username)
|
||||
if portal and portal.username:
|
||||
return TelegramMessage(f"@{portal.username}").format(TelegramEntityType.MENTION)
|
||||
|
||||
@classmethod
|
||||
def header_to_fstring(cls, node: HTMLNode, ctx: RecursionContext) -> TelegramMessage:
|
||||
children = cls.node_to_fstrings(node, ctx)
|
||||
async def header_to_fstring(self, node: HTMLNode, ctx: RecursionContext) -> TelegramMessage:
|
||||
children = await self.node_to_fstrings(node, ctx)
|
||||
length = int(node.tag[1])
|
||||
prefix = "#" * length + " "
|
||||
return TelegramMessage.join(children, "").prepend(prefix).format(TelegramEntityType.BOLD)
|
||||
|
||||
@classmethod
|
||||
def blockquote_to_fstring(cls, node: HTMLNode, ctx: RecursionContext) -> TelegramMessage:
|
||||
msg = cls.tag_aware_parse_node(node, ctx)
|
||||
async def blockquote_to_fstring(
|
||||
self, node: HTMLNode, ctx: RecursionContext
|
||||
) -> TelegramMessage:
|
||||
msg = await self.tag_aware_parse_node(node, ctx)
|
||||
children = msg.trim().split("\n")
|
||||
children = [child.prepend("> ") for child in children]
|
||||
return TelegramMessage.join(children, "\n")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# mautrix-telegram - A Matrix-Telegram puppeting bridge
|
||||
# Copyright (C) 2019 Tulir Asokan
|
||||
# Copyright (C) 2021 Tulir Asokan
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -13,7 +13,9 @@
|
||||
#
|
||||
# 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, Union, Any, List, Type, Dict
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Type
|
||||
from enum import Enum
|
||||
|
||||
from telethon.tl.types import (MessageEntityMention as Mention, MessageEntityBotCommand as Command,
|
||||
@@ -41,7 +43,7 @@ class TelegramEntityType(Enum):
|
||||
INLINE_CODE = Code
|
||||
BLOCKQUOTE = Blockquote
|
||||
MENTION = Mention
|
||||
MENTION_NAME = MentionName
|
||||
MENTION_NAME = InputMentionName
|
||||
COMMAND = Command
|
||||
|
||||
USER_MENTION = 1
|
||||
@@ -52,15 +54,15 @@ class TelegramEntityType(Enum):
|
||||
class TelegramEntity(SemiAbstractEntity):
|
||||
internal: TypeMessageEntity
|
||||
|
||||
def __init__(self, type: Union[TelegramEntityType, Type[TypeMessageEntity]],
|
||||
offset: int, length: int, extra_info: Dict[str, Any]) -> None:
|
||||
def __init__(self, type: TelegramEntityType | Type[TypeMessageEntity],
|
||||
offset: int, length: int, extra_info: dict[str, Any]) -> None:
|
||||
if isinstance(type, TelegramEntityType):
|
||||
if isinstance(type.value, int):
|
||||
raise ValueError(f"Can't create Entity with non-Telegram EntityType {type}")
|
||||
type = type.value
|
||||
self.internal = type(offset=offset, length=length, **extra_info)
|
||||
|
||||
def copy(self) -> Optional['TelegramEntity']:
|
||||
def copy(self) -> TelegramEntity:
|
||||
extra_info = {}
|
||||
if isinstance(self.internal, Pre):
|
||||
extra_info["language"] = self.internal.language
|
||||
@@ -95,5 +97,5 @@ class TelegramMessage(EntityString[TelegramEntity, TelegramEntityType]):
|
||||
entity_class = TelegramEntity
|
||||
|
||||
@property
|
||||
def telegram_entities(self) -> List[TypeMessageEntity]:
|
||||
def telegram_entities(self) -> list[TypeMessageEntity]:
|
||||
return [entity.internal for entity in self.entities]
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
#
|
||||
# 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, Optional, TYPE_CHECKING
|
||||
from __future__ import annotations
|
||||
|
||||
from html import escape
|
||||
import logging
|
||||
import re
|
||||
@@ -29,47 +30,45 @@ from telethon.tl.custom import Message
|
||||
from telethon.errors import RPCError
|
||||
from telethon.helpers import add_surrogate, del_surrogate
|
||||
|
||||
from mautrix.errors import MatrixRequestError
|
||||
from mautrix.appservice import IntentAPI
|
||||
from mautrix.types import (TextMessageEventContent, RelatesTo, RelationType, Format, MessageType,
|
||||
MessageEvent, EventType)
|
||||
EventType)
|
||||
|
||||
from .. import user as u, puppet as pu, portal as po
|
||||
from .. import user as u, puppet as pu, portal as po, abstract_user as au
|
||||
from ..types import TelegramID
|
||||
from ..db import Message as DBMessage
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..abstract_user import AbstractUser
|
||||
|
||||
log: logging.Logger = logging.getLogger("mau.fmt.tg")
|
||||
|
||||
|
||||
def telegram_reply_to_matrix(evt: Message, source: 'AbstractUser') -> Optional[RelatesTo]:
|
||||
async def telegram_reply_to_matrix(evt: Message, source: au.AbstractUser) -> RelatesTo | None:
|
||||
if evt.reply_to:
|
||||
space = (evt.peer_id.channel_id
|
||||
if isinstance(evt, Message) and isinstance(evt.peer_id, PeerChannel)
|
||||
else source.tgid)
|
||||
msg = DBMessage.get_one_by_tgid(TelegramID(evt.reply_to.reply_to_msg_id), space)
|
||||
msg = await DBMessage.get_one_by_tgid(TelegramID(evt.reply_to.reply_to_msg_id), space)
|
||||
if msg:
|
||||
return RelatesTo(rel_type=RelationType.REPLY, event_id=msg.mxid)
|
||||
return None
|
||||
|
||||
|
||||
async def _add_forward_header(source: 'AbstractUser', content: TextMessageEventContent,
|
||||
async def _add_forward_header(source: au.AbstractUser, content: TextMessageEventContent,
|
||||
fwd_from: MessageFwdHeader) -> None:
|
||||
if not content.formatted_body or content.format != Format.HTML:
|
||||
content.format = Format.HTML
|
||||
content.formatted_body = escape(content.body)
|
||||
fwd_from_html, fwd_from_text = None, None
|
||||
if isinstance(fwd_from.from_id, PeerUser):
|
||||
user = u.User.get_by_tgid(TelegramID(fwd_from.from_id.user_id))
|
||||
user = await u.User.get_by_tgid(TelegramID(fwd_from.from_id.user_id))
|
||||
if user:
|
||||
fwd_from_text = user.displayname or user.mxid
|
||||
fwd_from_html = (f"<a href='https://matrix.to/#/{user.mxid}'>"
|
||||
f"{escape(fwd_from_text)}</a>")
|
||||
|
||||
if not fwd_from_text:
|
||||
puppet = pu.Puppet.get(TelegramID(fwd_from.from_id.user_id), create=False)
|
||||
puppet = await pu.Puppet.get_by_tgid(
|
||||
TelegramID(fwd_from.from_id.user_id), create=False
|
||||
)
|
||||
if puppet and puppet.displayname:
|
||||
fwd_from_text = puppet.displayname or puppet.mxid
|
||||
fwd_from_html = (f"<a href='https://matrix.to/#/{puppet.mxid}'>"
|
||||
@@ -86,7 +85,7 @@ async def _add_forward_header(source: 'AbstractUser', content: TextMessageEventC
|
||||
elif isinstance(fwd_from.from_id, (PeerChannel, PeerChat)):
|
||||
from_id = (fwd_from.from_id.chat_id if isinstance(fwd_from.from_id, PeerChat)
|
||||
else fwd_from.from_id.channel_id)
|
||||
portal = po.Portal.get_by_tgid(TelegramID(from_id))
|
||||
portal = await po.Portal.get_by_tgid(TelegramID(from_id))
|
||||
if portal and portal.title:
|
||||
fwd_from_text = portal.title
|
||||
if portal.alias:
|
||||
@@ -116,13 +115,13 @@ async def _add_forward_header(source: 'AbstractUser', content: TextMessageEventC
|
||||
f"<tg-forward><blockquote>{content.formatted_body}</blockquote></tg-forward>")
|
||||
|
||||
|
||||
async def _add_reply_header(source: 'AbstractUser', content: TextMessageEventContent, evt: Message,
|
||||
main_intent: IntentAPI):
|
||||
async def _add_reply_header(source: au.AbstractUser, content: TextMessageEventContent,
|
||||
evt: Message, main_intent: IntentAPI) -> None:
|
||||
space = (evt.peer_id.channel_id
|
||||
if isinstance(evt, Message) and isinstance(evt.peer_id, PeerChannel)
|
||||
else source.tgid)
|
||||
|
||||
msg = DBMessage.get_one_by_tgid(TelegramID(evt.reply_to.reply_to_msg_id), space)
|
||||
msg = await DBMessage.get_one_by_tgid(TelegramID(evt.reply_to.reply_to_msg_id), space)
|
||||
if not msg:
|
||||
return
|
||||
|
||||
@@ -140,11 +139,11 @@ async def _add_reply_header(source: 'AbstractUser', content: TextMessageEventCon
|
||||
log.exception("Failed to get event to add reply fallback")
|
||||
|
||||
|
||||
async def telegram_to_matrix(evt: Message, source: "AbstractUser",
|
||||
main_intent: Optional[IntentAPI] = None,
|
||||
prefix_text: Optional[str] = None, prefix_html: Optional[str] = None,
|
||||
async def telegram_to_matrix(evt: Message, source: au.AbstractUser,
|
||||
main_intent: IntentAPI | None = None,
|
||||
prefix_text: str | None = None, prefix_html: str | None = None,
|
||||
override_text: str = None,
|
||||
override_entities: List[TypeMessageEntity] = None,
|
||||
override_entities: list[TypeMessageEntity] = None,
|
||||
no_reply_fallback: bool = False) -> TextMessageEventContent:
|
||||
content = TextMessageEventContent(
|
||||
msgtype=MessageType.TEXT,
|
||||
@@ -153,7 +152,7 @@ async def telegram_to_matrix(evt: Message, source: "AbstractUser",
|
||||
entities = override_entities or evt.entities
|
||||
if entities:
|
||||
content.format = Format.HTML
|
||||
content.formatted_body = _telegram_entities_to_matrix_catch(content.body, entities)
|
||||
content.formatted_body = await _telegram_entities_to_matrix_catch(content.body, entities)
|
||||
|
||||
if prefix_html:
|
||||
if not content.formatted_body:
|
||||
@@ -183,9 +182,9 @@ async def telegram_to_matrix(evt: Message, source: "AbstractUser",
|
||||
return content
|
||||
|
||||
|
||||
def _telegram_entities_to_matrix_catch(text: str, entities: List[TypeMessageEntity]) -> str:
|
||||
async def _telegram_entities_to_matrix_catch(text: str, entities: list[TypeMessageEntity]) -> str:
|
||||
try:
|
||||
return _telegram_entities_to_matrix(text, entities)
|
||||
return await _telegram_entities_to_matrix(text, entities)
|
||||
except Exception:
|
||||
log.exception("Failed to convert Telegram format:\n"
|
||||
"message=%s\n"
|
||||
@@ -194,8 +193,8 @@ def _telegram_entities_to_matrix_catch(text: str, entities: List[TypeMessageEnti
|
||||
return "[failed conversion in _telegram_entities_to_matrix]"
|
||||
|
||||
|
||||
def _telegram_entities_to_matrix(text: str, entities: List[TypeMessageEntity],
|
||||
offset: int = 0, length: int = None) -> str:
|
||||
async def _telegram_entities_to_matrix(text: str, entities: list[TypeMessageEntity],
|
||||
offset: int = 0, length: int = None) -> str:
|
||||
if not entities:
|
||||
return escape(text)
|
||||
if length is None:
|
||||
@@ -212,7 +211,7 @@ def _telegram_entities_to_matrix(text: str, entities: List[TypeMessageEntity],
|
||||
continue
|
||||
|
||||
skip_entity = False
|
||||
entity_text = _telegram_entities_to_matrix(
|
||||
entity_text = await _telegram_entities_to_matrix(
|
||||
text=text[relative_offset:relative_offset + entity.length],
|
||||
entities=entities[i + 1:], offset=entity.offset, length=entity.length)
|
||||
entity_type = type(entity)
|
||||
@@ -234,16 +233,17 @@ def _telegram_entities_to_matrix(text: str, entities: List[TypeMessageEntity],
|
||||
elif entity_type == MessageEntityPre:
|
||||
skip_entity = _parse_pre(html, entity_text, entity.language)
|
||||
elif entity_type == MessageEntityMention:
|
||||
skip_entity = _parse_mention(html, entity_text)
|
||||
skip_entity = await _parse_mention(html, entity_text)
|
||||
elif entity_type == MessageEntityMentionName:
|
||||
skip_entity = _parse_name_mention(html, entity_text, TelegramID(entity.user_id))
|
||||
skip_entity = await _parse_name_mention(html, entity_text, TelegramID(entity.user_id))
|
||||
elif entity_type == MessageEntityEmail:
|
||||
html.append(f"<a href='mailto:{entity_text}'>{entity_text}</a>")
|
||||
elif entity_type in (MessageEntityTextUrl, MessageEntityUrl):
|
||||
skip_entity = _parse_url(html, entity_text,
|
||||
entity.url if entity_type == MessageEntityTextUrl else None)
|
||||
skip_entity = await _parse_url(
|
||||
html, entity_text, entity.url if entity_type == MessageEntityTextUrl else None
|
||||
)
|
||||
elif entity_type == MessageEntityBotCommand:
|
||||
html.append(f"<font color='blue'>!{entity_text[1:]}</font>")
|
||||
html.append(f"<font color='blue'>{entity_text}</font>")
|
||||
elif entity_type in (MessageEntityHashtag, MessageEntityCashtag, MessageEntityPhone):
|
||||
html.append(f"<font color='blue'>{entity_text}</font>")
|
||||
else:
|
||||
@@ -254,24 +254,22 @@ def _telegram_entities_to_matrix(text: str, entities: List[TypeMessageEntity],
|
||||
return "".join(html)
|
||||
|
||||
|
||||
def _parse_pre(html: List[str], entity_text: str, language: str) -> bool:
|
||||
def _parse_pre(html: list[str], entity_text: str, language: str) -> bool:
|
||||
if language:
|
||||
html.append("<pre>"
|
||||
f"<code class='language-{language}'>{entity_text}</code>"
|
||||
"</pre>")
|
||||
html.append(f"<pre><code class='language-{language}'>{entity_text}</code></pre>")
|
||||
else:
|
||||
html.append(f"<pre><code>{entity_text}</code></pre>")
|
||||
return False
|
||||
|
||||
|
||||
def _parse_mention(html: List[str], entity_text: str) -> bool:
|
||||
async def _parse_mention(html: list[str], entity_text: str) -> bool:
|
||||
username = entity_text[1:]
|
||||
|
||||
user = u.User.find_by_username(username) or pu.Puppet.find_by_username(username)
|
||||
user = await u.User.find_by_username(username) or await pu.Puppet.find_by_username(username)
|
||||
if user:
|
||||
mxid = user.mxid
|
||||
else:
|
||||
portal = po.Portal.find_by_username(username)
|
||||
portal = await po.Portal.find_by_username(username)
|
||||
mxid = portal.alias or portal.mxid if portal else None
|
||||
|
||||
if mxid:
|
||||
@@ -281,12 +279,12 @@ def _parse_mention(html: List[str], entity_text: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _parse_name_mention(html: List[str], entity_text: str, user_id: TelegramID) -> bool:
|
||||
user = u.User.get_by_tgid(user_id)
|
||||
async def _parse_name_mention(html: list[str], entity_text: str, user_id: TelegramID) -> bool:
|
||||
user = await u.User.get_by_tgid(user_id)
|
||||
if user:
|
||||
mxid = user.mxid
|
||||
else:
|
||||
puppet = pu.Puppet.get(user_id, create=False)
|
||||
puppet = await pu.Puppet.get_by_tgid(user_id, create=False)
|
||||
mxid = puppet.mxid if puppet else None
|
||||
if mxid:
|
||||
html.append(f"<a href='https://matrix.to/#/{mxid}'>{entity_text}</a>")
|
||||
@@ -299,7 +297,7 @@ message_link_regex = re.compile(r"https?://t(?:elegram)?\.(?:me|dog)/"
|
||||
r"([A-Za-z][A-Za-z0-9_]{3,}[A-Za-z0-9])/([0-9]{1,50})")
|
||||
|
||||
|
||||
def _parse_url(html: List[str], entity_text: str, url: str) -> bool:
|
||||
async def _parse_url(html: list[str], entity_text: str, url: str) -> bool:
|
||||
url = escape(url) if url else entity_text
|
||||
if not url.startswith(("https://", "http://", "ftp://", "magnet://")):
|
||||
url = "http://" + url
|
||||
@@ -309,9 +307,9 @@ def _parse_url(html: List[str], entity_text: str, url: str) -> bool:
|
||||
group, msgid_str = message_link_match.groups()
|
||||
msgid = int(msgid_str)
|
||||
|
||||
portal = po.Portal.find_by_username(group)
|
||||
portal = await po.Portal.find_by_username(group)
|
||||
if portal:
|
||||
message = DBMessage.get_one_by_tgid(TelegramID(msgid), portal.tgid)
|
||||
message = await DBMessage.get_one_by_tgid(TelegramID(msgid), portal.tgid)
|
||||
if message:
|
||||
url = f"https://matrix.to/#/{portal.mxid}/{message.mxid}"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user