Add support for formatted captions

This commit is contained in:
Tulir Asokan
2019-10-27 13:55:34 +02:00
parent 73a6ad2cf2
commit 01b317484f
4 changed files with 43 additions and 42 deletions
+12 -13
View File
@@ -196,20 +196,19 @@ bridge:
# Text msgtypes (m.text, m.notice and m.emote) support HTML, media msgtypes don't.
#
# Available variables:
# $sender_displayname - The display name of the sender (e.g. Example User)
# $sender_username - The username (Matrix ID localpart) of the sender (e.g. exampleuser)
# $sender_mxid - The Matrix ID of the sender (e.g. @exampleuser:example.com)
# $body - The plaintext body (file name for media msgtypes)
# $formatted_body - The message content as HTML (for text msgtypes)
# $sender_displayname - The display name of the sender (e.g. Example User)
# $sender_username - The username (Matrix ID localpart) of the sender (e.g. exampleuser)
# $sender_mxid - The Matrix ID of the sender (e.g. @exampleuser:example.com)
# $message - The message content
message_formats:
m.text: "<b>$sender_displayname</b>: $formatted_body"
m.notice: "<b>$sender_displayname</b>: $formatted_body"
m.emote: "* <b>$sender_displayname</b> $formatted_body"
m.file: "$sender_displayname sent a file: $body"
m.image: "$sender_displayname sent an image: $body"
m.audio: "$sender_displayname sent an audio file: $body"
m.video: "$sender_displayname sent a video: $body"
m.location: "$sender_displayname sent a location: $body"
m.text: "<b>$sender_displayname</b>: $message"
m.notice: "<b>$sender_displayname</b>: $message"
m.emote: "* <b>$sender_displayname</b> $message"
m.file: "<b>$sender_displayname</b> sent a file: $message"
m.image: "<b>$sender_displayname</b> sent an image: $message"
m.audio: "<b>$sender_displayname</b> sent an audio file: $message"
m.video: "<b>$sender_displayname</b> sent a video: $message"
m.location: "<b>$sender_displayname</b> sent a location: $message"
# Telegram doesn't have built-in emotes, this field specifies how m.emote's from authenticated
# users are sent to telegram. All fields in message_formats are supported. Additionally, the
# Telegram user info is available in the following variables:
+6 -6
View File
@@ -18,7 +18,7 @@ from typing import Awaitable, Callable, List, Optional, NamedTuple, Any
from telethon.errors import FloodWaitError
from mautrix.types import RoomID, EventID
from mautrix.types import RoomID, EventID, MessageEventContent
from mautrix.bridge.commands import (HelpSection, CommandEvent as BaseCommandEvent,
CommandHandler as BaseCommandHandler,
CommandProcessor as BaseCommandProcessor,
@@ -42,10 +42,10 @@ class CommandEvent(BaseCommandEvent):
sender: u.User
def __init__(self, processor: 'CommandProcessor', room_id: RoomID, event_id: EventID,
sender: u.User, command: str, args: List[str], is_management: bool,
is_portal: bool) -> None:
super().__init__(processor, room_id, event_id, sender, command, args, is_management,
is_portal)
sender: u.User, command: str, args: List[str], content: MessageEventContent,
is_management: bool, is_portal: bool) -> None:
super().__init__(processor, room_id, event_id, sender, command, args, content,
is_management, is_portal)
self.bridge = processor.bridge
self.tgbot = processor.tgbot
self.config = processor.config
@@ -69,7 +69,7 @@ class CommandHandler(BaseCommandHandler):
def __init__(self, handler: Callable[[CommandEvent], Awaitable[EventID]],
management_only: bool, name: str, help_text: str, help_args: str,
help_section: HelpSection, needs_auth: bool, needs_puppeting: bool,
needs_matrix_puppeting: bool, needs_admin: bool,) -> None:
needs_matrix_puppeting: bool, needs_admin: bool) -> None:
super().__init__(handler, management_only, name, help_text, help_args, help_section,
needs_auth=needs_auth, needs_puppeting=needs_puppeting,
needs_matrix_puppeting=needs_matrix_puppeting, needs_admin=needs_admin)
+7 -6
View File
@@ -29,7 +29,7 @@ from telethon.tl.functions.messages import (ImportChatInviteRequest, CheckChatIn
GetBotCallbackAnswerRequest, SendVoteRequest)
from telethon.tl.functions.channels import JoinChannelRequest
from mautrix.types import EventID
from mautrix.types import EventID, Format
from ... import puppet as pu, portal as po
from ...abstract_user import AbstractUser
@@ -45,11 +45,12 @@ async def caption(evt: CommandEvent) -> EventID:
if len(evt.args) == 0:
return await evt.reply("**Usage:** `$cmdprefix+sp caption <caption>`")
text = " ".join(evt.args)
evt.sender.command_status = {"caption": text}
quoted_text = "\n".join(f"> {row}" for row in text.split("\n"))
return await evt.reply("Your next image will be captioned with\n\n"
f"{quoted_text}\n\n"
prefix = f"{evt.command_prefix} caption "
if evt.content.format == Format.HTML:
evt.content.formatted_body = evt.content.formatted_body.replace(prefix, "", 1)
evt.content.body = evt.content.body.replace(prefix, "", 1)
evt.sender.command_status = {"caption": evt.content}
return await evt.reply("Your next image or file will be sent with that caption. "
"Use `$cmdprefix+sp cancel` to cancel the caption.")
+18 -17
View File
@@ -169,7 +169,7 @@ class PortalMatrix(BasePortal, MautrixBasePortal, ABC):
async def _apply_msg_format(self, sender: 'u.User', content: MessageEventContent
) -> None:
if isinstance(content, TextMessageEventContent) and content.format != Format.HTML:
if not isinstance(content, TextMessageEventContent) or content.format != Format.HTML:
content.format = Format.HTML
content.formatted_body = escape_html(content.body).replace("\n", "<br/>")
@@ -179,14 +179,9 @@ class PortalMatrix(BasePortal, MautrixBasePortal, ABC):
tpl_args = dict(sender_mxid=sender.mxid,
sender_username=sender.mxid_localpart,
sender_displayname=escape_html(displayname),
body=content.body)
if isinstance(content, TextMessageEventContent):
tpl_args["formatted_body"] = content.formatted_body
tpl_args["message"] = content.formatted_body
content.formatted_body = Template(tpl).safe_substitute(tpl_args)
else:
tpl_args["message"] = content.body
content.body = Template(tpl).safe_substitute(tpl_args)
message=content.formatted_body,
body=content.body, formatted_body=content.formatted_body)
content.formatted_body = Template(tpl).safe_substitute(tpl_args)
async def _apply_emote_format(self, sender: 'u.User',
content: TextMessageEventContent) -> None:
@@ -248,7 +243,8 @@ class PortalMatrix(BasePortal, MautrixBasePortal, ABC):
async def _handle_matrix_file(self, sender_id: TelegramID, event_id: EventID,
space: TelegramID, client: 'MautrixTelegramClient',
content: MediaMessageEventContent, reply_to: TelegramID) -> None:
content: MediaMessageEventContent, reply_to: TelegramID,
caption: TextMessageEventContent = None) -> None:
mime = content.info.mimetype
w, h = content.info.width, content.info.height
file_name = content["net.maunium.telegram.internal.filename"]
@@ -256,7 +252,7 @@ class PortalMatrix(BasePortal, MautrixBasePortal, ABC):
if config["bridge.parallel_file_transfer"]:
file_handle, file_size = await parallel_transfer_to_telegram(client, self.main_intent,
content.url, 0)
content.url, sender_id)
else:
file = await self.main_intent.download_media(content.url)
@@ -283,19 +279,19 @@ class PortalMatrix(BasePortal, MautrixBasePortal, ABC):
media = InputMediaUploadedDocument(file=file_handle, attributes=attributes,
mime_type=mime or "application/octet-stream")
caption = content.body if content.body != file_name else None
caption, entities = self._matrix_event_to_entities(caption) if caption else (None, None)
async with self.send_lock(sender_id):
if await self._matrix_document_edit(client, content, space, caption, media, event_id):
return
try:
response = await client.send_media(self.peer, media, reply_to=reply_to,
caption=caption)
caption=caption, entities=entities)
except (PhotoInvalidDimensionsError, PhotoSaveFileInvalidError, PhotoExtInvalidError):
media = InputMediaUploadedDocument(file=media.file, mime_type=mime,
attributes=attributes)
response = await client.send_media(self.peer, media, reply_to=reply_to,
caption=caption)
caption=caption, entities=entities)
self._add_telegram_message_to_db(event_id, space, 0, response)
async def _matrix_document_edit(self, client: 'MautrixTelegramClient',
@@ -364,14 +360,18 @@ class PortalMatrix(BasePortal, MautrixBasePortal, ABC):
media = (MessageType.STICKER, MessageType.IMAGE, MessageType.FILE, MessageType.AUDIO,
MessageType.VIDEO)
caption_content = None
if content.msgtype in media:
content["net.maunium.telegram.internal.filename"] = content.body
try:
content.body = sender.command_status["caption"]
caption_content: MessageEventContent = sender.command_status["caption"]
caption_content.msgtype = content.msgtype
reply_to = reply_to or formatter.matrix_reply_to_telegram(caption_content, space,
room_id=self.mxid)
sender.command_status = None
except (KeyError, TypeError):
pass
await self._pre_process_matrix_message(sender, not logged_in, content)
await self._pre_process_matrix_message(sender, not logged_in, caption_content or content)
if content.msgtype == MessageType.NOTICE:
bridge_notices = self.get_config("bridge_notices.default")
@@ -385,7 +385,8 @@ class PortalMatrix(BasePortal, MautrixBasePortal, ABC):
await self._handle_matrix_location(sender_id, event_id, space, client, content,
reply_to)
elif content.msgtype in media:
await self._handle_matrix_file(sender_id, event_id, space, client, content, reply_to)
await self._handle_matrix_file(sender_id, event_id, space, client, content, reply_to,
caption_content)
else:
self.log.debug(f"Unhandled Matrix event: {content}")