Compare commits

...

25 Commits

Author SHA1 Message Date
Tulir Asokan 0068341185 Bump version to 0.15.2 2024-07-16 11:53:19 +03:00
Tulir Asokan efcf1535ff Update mautrix-python 2024-07-12 20:25:57 +03:00
Tulir Asokan 99f633e98d Update telethon and changelog 2024-07-09 12:15:41 +03:00
Tulir Asokan 0137bfcbf6 Update mautrix-python 2024-07-09 12:15:41 +03:00
Javier Cuevas f6cb26f7f5 Merge pull request #964 from mautrix/feature/periodic-refresh
Add periodic connection refresh
2024-05-24 10:19:43 +02:00
Javier Cuevas 6418202118 Update mautrix_telegram/abstract_user.py
Co-authored-by: Tulir Asokan <tulir@maunium.net>
2024-05-24 09:39:20 +02:00
Javier Cuevas 4b25e855e0 Add force_refresh_interval_seconds to config.py 2024-05-24 09:36:09 +02:00
Javier Cuevas a35f6abfd1 Change default for force_refresh_interval_seconds (disabled by default) 2024-05-24 09:36:03 +02:00
Javier Cuevas 716222a671 Format to pass linting 2024-05-23 17:18:06 +02:00
Javier Cuevas 31801a436c Add periodic connection refresh 2024-05-23 17:06:04 +02:00
Tulir Asokan 8bd5a4e367 Update changelog 2024-05-03 11:47:48 +02:00
Tulir Asokan 43d17a335b Fix call end message 2024-04-08 17:47:44 +03:00
Nick Mills-Barrett 84a3fde1ca Implement bot/channel file size limit 2024-03-25 14:36:29 +00:00
Nick Mills-Barrett 05d05e671b Add config to limit size of documents from bots/channels copied to Matrix 2024-03-25 14:36:29 +00:00
Nick Mills-Barrett ab6a6654f7 Pass through is channel to msg conversion 2024-03-25 14:36:29 +00:00
Tulir Asokan dbfbf12862 Fix error handling replies in some cases 2024-03-19 12:02:58 +02:00
Tulir Asokan 6166173376 Fix message in MSS events 2024-03-14 13:08:53 +02:00
Tulir Asokan 2232d9898e Avoid logging RPCErrors twice 2024-03-14 13:07:22 +02:00
Tulir Asokan 3cf279718f Don't send notices for some errors 2024-03-14 13:05:55 +02:00
Tulir Asokan 65ec4491e2 Merge branch 'tulir/bot-reactions' 2024-03-13 15:21:33 +02:00
Tulir Asokan ce43607c56 Update dependencies 2024-03-13 15:20:41 +02:00
Nick Mills-Barrett 150bf5e338 Return if no document contained in media document event 2024-02-14 09:58:24 +00:00
Tulir Asokan 77cbbebfb2 Update Black to 2024 style and Python 3.10 target 2024-01-29 18:52:10 +02:00
Tulir Asokan 511043a720 Add support for bot-specific reaction update 2024-01-13 22:42:42 +02:00
Tulir Asokan 19a4b4374d Update dependencies and drop Python 3.9 support 2024-01-08 17:35:37 +02:00
19 changed files with 297 additions and 56 deletions
+4 -4
View File
@@ -6,17 +6,17 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
python-version: "3.12"
- uses: isort/isort-action@master
with:
sortPaths: "./mautrix_telegram"
- uses: psf/black@stable
with:
src: "./mautrix_telegram"
version: "23.1.0"
version: "24.1.1"
- name: pre-commit
run: |
pip install pre-commit
+3 -3
View File
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: trailing-whitespace
exclude_types: [markdown]
@@ -8,13 +8,13 @@ repos:
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 23.1.0
rev: 24.1.1
hooks:
- id: black
language_version: python3
files: ^mautrix_telegram/.*\.pyi?$
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
rev: 5.13.2
hooks:
- id: isort
files: ^mautrix_telegram/.*\.pyi?$
+9
View File
@@ -1,3 +1,12 @@
# v0.15.2 (2024-07-16)
* Dropped support for Python 3.9.
* Updated Telegram API to layer 183.
* Added support for authenticated media downloads.
* Added support for receiving reactions when using a bot account.
* Added option to limit file size by chat type.
* Fixed reply bridging breaking in some cases.
# v0.15.1 (2023-12-26)
* Updated Telegram API to layer 169.
+1 -1
View File
@@ -1,3 +1,3 @@
pre-commit>=2.10.1,<3
isort>=5.10.1,<6
black>=23,<24
black>=24,<25
+1 -1
View File
@@ -1,2 +1,2 @@
__version__ = "0.15.1"
__version__ = "0.15.2"
__author__ = "Tulir Asokan <tulir@maunium.net>"
+27
View File
@@ -40,6 +40,7 @@ from telethon.tl.types import (
PeerUser,
PhoneCallRequested,
TypeUpdate,
UpdateBotMessageReaction,
UpdateChannel,
UpdateChannelUserTyping,
UpdateChatDefaultBannedRights,
@@ -240,6 +241,24 @@ class AbstractUser(ABC):
use_ipv6=self.config["telegram.connection.use_ipv6"],
)
self.client.add_event_handler(self._update_catch)
self._schedule_reconnect()
def _schedule_reconnect(self) -> None:
reconnect_interval = self.config["telegram.force_refresh_interval_seconds"]
if not reconnect_interval or reconnect_interval == 0:
return
refresh_time = time.time() + reconnect_interval
self.log.info(
"Scheduling forced reconnect in %d seconds. Connection will be refreshed at %s",
reconnect_interval,
time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(refresh_time)),
)
self.loop.call_later(reconnect_interval, lambda: background_task.create(self._reconnect()))
async def _reconnect(self) -> None:
self.log.info("Reconnecting to Telegram...")
await self.stop()
await self.start()
@abstractmethod
async def on_signed_out(self, err: UnauthorizedError | AuthKeyError) -> None:
@@ -363,6 +382,8 @@ class AbstractUser(ABC):
await self.update_phone_call(update)
elif isinstance(update, UpdateMessageReactions):
await self.update_reactions(update)
elif isinstance(update, UpdateBotMessageReaction):
await self.update_bot_reactions(update)
elif isinstance(update, (UpdateChatUserTyping, UpdateChannelUserTyping, UpdateUserTyping)):
await self.update_typing(update)
elif isinstance(update, UpdateUserStatus):
@@ -636,6 +657,12 @@ class AbstractUser(ABC):
return
await portal.handle_telegram_reactions(self, TelegramID(update.msg_id), update.reactions)
async def update_bot_reactions(self, update: UpdateBotMessageReaction) -> None:
portal = await po.Portal.get_by_entity(update.peer, tg_receiver=self.tgid)
if not portal or not portal.mxid or not portal.allow_bridging:
return
await portal.handle_telegram_bot_reactions(self, update)
async def update_phone_call(self, update: UpdatePhoneCall) -> None:
self.log.debug("Phone call update %s", update)
if not isinstance(update.phone_call, PhoneCallRequested):
+3
View File
@@ -136,6 +136,8 @@ class Config(BaseBridgeConfig):
copy("bridge.caption_in_message")
copy("bridge.image_as_file_size")
copy("bridge.image_as_file_pixels")
copy("bridge.document_as_link_size.bot")
copy("bridge.document_as_link_size.channel")
copy("bridge.parallel_file_transfer")
copy("bridge.federate_rooms")
copy("bridge.always_custom_emoji_reaction")
@@ -262,6 +264,7 @@ class Config(BaseBridgeConfig):
copy("telegram.catch_up")
copy("telegram.sequential_updates")
copy("telegram.exit_on_update_error")
copy("telegram.force_refresh_interval_seconds")
copy("telegram.connection.timeout")
copy("telegram.connection.retries")
+3 -3
View File
@@ -208,9 +208,9 @@ class PgSession(MemorySession):
await self._locked_process_entities(tlo)
async def _locked_process_entities(self, tlo) -> None:
rows: list[
tuple[str, int, int, str | None, str | None, str | None]
] = self._entities_to_rows(tlo)
rows: list[tuple[str, int, int, str | None, str | None, str | None]] = (
self._entities_to_rows(tlo)
)
if not rows:
return
if self.db.scheme == Scheme.POSTGRES:
+7
View File
@@ -222,6 +222,11 @@ bridge:
image_as_file_size: 10
# Maximum number of pixels in an image before sending to Telegram as a document. Defaults to 4096x4096 = 16777216.
image_as_file_pixels: 16777216
# Maximum size of Telegram documents before linking to Telegrm instead of bridge
# to Matrix media.
document_as_link_size:
channel:
bot:
# Enable experimental parallel file transfer, which makes uploads/downloads much faster by
# streaming from/to Matrix and using many connections for Telegram.
# Note that generating HQ thumbnails for videos is not possible with streamed transfers.
@@ -576,6 +581,8 @@ telegram:
# Should incoming updates be handled sequentially to make sure order is preserved on Matrix?
sequential_updates: true
exit_on_update_error: false
# Interval to force refresh the connection (full reconnect). 0 disables it.
force_refresh_interval_seconds: 0
# Telethon connection options.
connection:
+96 -22
View File
@@ -23,6 +23,7 @@ from typing import (
Callable,
List,
Literal,
NamedTuple,
Union,
cast,
)
@@ -49,6 +50,7 @@ from telethon.errors import (
InputUserDeactivatedError,
MessageEmptyError,
MessageIdInvalidError,
MessageNotModifiedError,
MessageTooLongError,
PhotoExtInvalidError,
PhotoInvalidDimensionsError,
@@ -113,6 +115,7 @@ from telethon.tl.types import (
InputPeerUser,
InputStickerSetEmpty,
InputUser,
MessageActionBoostApply,
MessageActionChannelCreate,
MessageActionChatAddUser,
MessageActionChatCreate,
@@ -159,6 +162,7 @@ from telethon.tl.types import (
TypeUser,
TypeUserFull,
TypeUserProfilePhoto,
UpdateBotMessageReaction,
UpdateChannelUserTyping,
UpdateChatUserTyping,
UpdateMessageReactions,
@@ -269,6 +273,11 @@ class IgnoredMessageError(Exception):
pass
class WrappedReaction(NamedTuple):
reaction: ReactionEmoji | ReactionCustomEmoji
date: datetime | None
class Portal(DBPortal, BasePortal):
bot: "Bot"
config: Config
@@ -435,6 +444,10 @@ class Portal(DBPortal, BasePortal):
def is_direct(self) -> bool:
return self.peer_type == "user"
@property
def is_channel(self) -> bool:
return self.peer_type == "channel"
@property
def has_bot(self) -> bool:
return bool(self.bot) and (
@@ -2133,7 +2146,7 @@ class Portal(DBPortal, BasePortal):
status.status = MessageStatus.FAIL
elif err:
status.reason = MessageStatusReason.GENERIC_ERROR
status.error = f"{type(err)}: {err}"
status.error = f"{type(err).__name__}: {err}"
status.status = MessageStatus.RETRIABLE
status.message = self._error_to_human_message(err)
else:
@@ -2164,9 +2177,10 @@ class Portal(DBPortal, BasePortal):
)
if msg and self.config["bridge.delivery_error_reports"]:
await self._send_message(
self.main_intent, TextMessageEventContent(msgtype=MessageType.NOTICE, body=msg)
)
if not isinstance(err, MessageNotModifiedError):
await self._send_message(
self.main_intent, TextMessageEventContent(msgtype=MessageType.NOTICE, body=msg)
)
await self._send_message_status(event_id, err)
async def handle_matrix_message(
@@ -2184,7 +2198,6 @@ class Portal(DBPortal, BasePortal):
message_type=content.msgtype,
msg=f"\u26a0 Your message may not have been bridged: {e}",
)
raise
except Exception as e:
if isinstance(e, IgnoredMessageError):
self.log.debug(f"Ignored {event_id}: {e}")
@@ -2323,20 +2336,17 @@ class Portal(DBPortal, BasePortal):
sender.command_status = None
except (KeyError, TypeError):
if not logged_in or (
"filename" in content and content["filename"] != content.body
content.filename is not None and content.filename != content.body
):
if "filename" in content:
file_name = content["filename"]
if content.filename:
file_name = content.filename
caption_content = TextMessageEventContent(
msgtype=MessageType.TEXT,
body=content.body,
)
if (
"formatted_body" in content
and str(content.get("format")) == Format.HTML.value
):
caption_content["formatted_body"] = content["formatted_body"]
caption_content["format"] = Format.HTML
if content.formatted_body and content.format == Format.HTML:
caption_content.formatted_body = content.formatted_body
caption_content.format = Format.HTML
else:
caption_content = None
if caption_content:
@@ -2800,7 +2810,7 @@ class Portal(DBPortal, BasePortal):
intent = sender.intent_for(self) if sender else self.main_intent
is_bot = sender.is_bot if sender else False
converted = await self._msg_conv.convert(
source, intent, is_bot, evt, no_reply_fallback=True
source, intent, is_bot, self.is_channel, evt, no_reply_fallback=True
)
converted.content.set_edit(editing_msg.mxid)
await intent.set_typing(self.mxid, timeout=0)
@@ -3016,6 +3026,7 @@ class Portal(DBPortal, BasePortal):
source,
intent,
is_bot,
self.is_channel,
msg,
client=client,
deterministic_reply_id=self.bridge.homeserver_software.is_hungry,
@@ -3250,12 +3261,40 @@ class Portal(DBPortal, BasePortal):
recent_reactions = resp.reactions
async with self.reaction_lock(dbm.mxid):
await self._handle_telegram_reactions_locked(
await self._handle_telegram_user_reactions_locked(
source, dbm, recent_reactions, total_count, timestamp=timestamp
)
async def handle_telegram_bot_reactions(
self, source: au.AbstractUser, update: UpdateBotMessageReaction
) -> None:
tg_space = self.tgid if self.peer_type == "channel" else source.tgid
dbm = await DBMessage.get_one_by_tgid(TelegramID(update.msg_id), tg_space)
if dbm is None:
return
reactions: dict[TelegramID, list[WrappedReaction]] = {}
custom_emoji_ids: list[int] = []
if isinstance(update.actor, PeerUser):
user_id = TelegramID(update.actor.user_id)
elif isinstance(update.actor, PeerChannel):
user_id = TelegramID(update.actor.channel_id)
else:
return
for reaction in update.new_reactions:
reactions.setdefault(user_id, []).append(WrappedReaction(reaction=reaction, date=None))
async with self.reaction_lock(dbm.mxid):
await self._handle_telegram_parsed_reactions_locked(
source,
dbm,
reactions,
custom_emoji_ids,
is_full=True,
only_user_id=user_id,
timestamp=update.date,
)
@staticmethod
def _reactions_filter(lst: list[MessagePeerReaction], existing: DBReaction) -> bool:
def _reactions_filter(lst: list[WrappedReaction], existing: DBReaction) -> bool:
if not lst:
return False
for wrapped_reaction in lst:
@@ -3278,7 +3317,7 @@ class Portal(DBPortal, BasePortal):
return await source.get_max_reactions(is_premium)
return 3 if is_premium else 1
async def _handle_telegram_reactions_locked(
async def _handle_telegram_user_reactions_locked(
self,
source: au.AbstractUser,
msg: DBMessage,
@@ -3286,17 +3325,38 @@ class Portal(DBPortal, BasePortal):
total_count: int,
timestamp: datetime | None = None,
) -> None:
reactions: dict[TelegramID, list[MessagePeerReaction]] = {}
reactions: dict[TelegramID, list[WrappedReaction]] = {}
custom_emoji_ids: list[int] = []
for reaction in reaction_list:
if isinstance(reaction.peer_id, (PeerUser, PeerChannel)) and isinstance(
reaction.reaction, (ReactionEmoji, ReactionCustomEmoji)
):
sender_user_id = p.Puppet.get_id_from_peer(reaction.peer_id)
reactions.setdefault(sender_user_id, []).append(reaction)
reactions.setdefault(sender_user_id, []).append(
WrappedReaction(reaction.reaction, reaction.date)
)
if isinstance(reaction.reaction, ReactionCustomEmoji):
custom_emoji_ids.append(reaction.reaction.document_id)
is_full = len(reaction_list) == total_count
await self._handle_telegram_parsed_reactions_locked(
source,
msg,
reactions,
custom_emoji_ids,
is_full=is_full,
timestamp=timestamp,
)
async def _handle_telegram_parsed_reactions_locked(
self,
source: au.AbstractUser,
msg: DBMessage,
reactions: dict[TelegramID, list[WrappedReaction]],
custom_emoji_ids: list[int],
is_full: bool,
only_user_id: TelegramID | None = None,
timestamp: datetime | None = None,
) -> None:
custom_emojis = await util.transfer_custom_emojis_to_matrix(source, custom_emoji_ids)
existing_reactions = await DBReaction.get_all_by_message(msg.mxid, msg.mx_room)
@@ -3304,6 +3364,8 @@ class Portal(DBPortal, BasePortal):
removed: list[DBReaction] = []
for existing_reaction in existing_reactions:
sender_id = existing_reaction.tg_sender
if only_user_id is not None and sender_id != only_user_id:
continue
new_reactions = reactions.get(sender_id)
if self._reactions_filter(new_reactions, existing_reaction):
if new_reactions is not None and len(new_reactions) == 0:
@@ -3469,7 +3531,7 @@ class Portal(DBPortal, BasePortal):
else:
intent = self.main_intent
is_bot = sender.is_bot if sender else False
converted = await self._msg_conv.convert(source, intent, is_bot, evt)
converted = await self._msg_conv.convert(source, intent, is_bot, self.is_channel, evt)
if not converted:
return
await intent.set_typing(self.mxid, timeout=0)
@@ -3623,7 +3685,7 @@ class Portal(DBPortal, BasePortal):
end_reason = "disconnected"
body = f"{call_type} {end_reason}"
if action.duration:
body += f" ({format_duration(action.duration)}"
body += f" ({format_duration(action.duration)})"
await self._send_message(
sender.intent_for(self),
TextMessageEventContent(msgtype=MessageType.NOTICE, body=body),
@@ -3651,6 +3713,18 @@ class Portal(DBPortal, BasePortal):
),
),
)
elif isinstance(action, MessageActionBoostApply):
await self._send_message(
sender.intent_for(self),
TextMessageEventContent(
msgtype=MessageType.EMOTE,
body=(
"boosted the group"
if action.boosts == 1
else f"boosted the group {action.boosts} times"
),
),
)
elif isinstance(action, MessageActionGameScore):
# TODO handle game score
pass
+118 -3
View File
@@ -53,6 +53,7 @@ from telethon.tl.types import (
MessageMediaWebPage,
MessageReplyStoryHeader,
PeerChannel,
PeerChat,
PeerUser,
Photo,
PhotoCachedSize,
@@ -63,6 +64,8 @@ from telethon.tl.types import (
Poll,
TypeDocumentAttribute,
TypePhotoSize,
UpdateShortChatMessage,
UpdateShortMessage,
WebPage,
)
from telethon.utils import decode_waveform
@@ -158,6 +161,7 @@ class TelegramMessageConverter:
source: au.AbstractUser,
intent: IntentAPI,
is_bot: bool,
is_channel: bool,
evt: Message,
no_reply_fallback: bool = False,
deterministic_reply_id: bool = False,
@@ -166,8 +170,13 @@ class TelegramMessageConverter:
if not client:
client = source.client
if hasattr(evt, "media") and isinstance(evt.media, self._allowed_media):
convert_media = self._media_converters[type(evt.media)]
converted = await convert_media(source=source, intent=intent, evt=evt, client=client)
if self._should_convert_full_document(evt.media, is_bot, is_channel):
convert_media = self._media_converters[type(evt.media)]
converted = await convert_media(
source=source, intent=intent, evt=evt, client=client
)
else:
converted = await self._convert_document_thumb_only(source, intent, evt, client)
elif evt.message:
converted = await self._convert_text(source, intent, is_bot, evt, client)
else:
@@ -200,6 +209,16 @@ class TelegramMessageConverter:
)
return converted
def _should_convert_full_document(self, media, is_bot: bool, is_channel: bool) -> bool:
if not isinstance(media, MessageMediaDocument):
return True
size = media.document.size
if is_bot and self.config["bridge.document_as_link_size.bot"]:
return size < self.config["bridge.document_as_link_size.bot"] * 1000**2
if is_channel and self.config["bridge.document_as_link_size.channel"]:
return size < self.config["bridge.document_as_link_size.channel"] * 1000**2
return True
@staticmethod
def _caption_to_message(converted: ConvertedMessage) -> None:
content, caption = converted.content, converted.caption
@@ -278,7 +297,15 @@ class TelegramMessageConverter:
if isinstance(evt, Message) and isinstance(evt.peer_id, PeerChannel)
else source.tgid
)
if evt.reply_to.reply_to_peer_id and evt.reply_to.reply_to_peer_id != evt.peer_id:
if isinstance(evt, Message):
evt_peer_id = evt.peer_id
elif isinstance(evt, UpdateShortMessage):
evt_peer_id = PeerUser(evt.user_id)
elif isinstance(evt, UpdateShortChatMessage):
evt_peer_id = PeerChat(evt.chat_id)
else:
evt_peer_id = None
if evt.reply_to.reply_to_peer_id and evt.reply_to.reply_to_peer_id != evt_peer_id:
if not self.config["bridge.cross_room_replies"]:
return
space = (
@@ -481,6 +508,91 @@ class TelegramMessageConverter:
# but we can only count it from read receipt.
return ttl * 5
async def _convert_document_thumb_only(
self,
source: au.AbstractUser,
intent: IntentAPI,
evt: Message,
client: MautrixTelegramClient,
) -> ConvertedMessage | None:
document = evt.media.document
if not document:
return None
external_link_content = "Unsupported file, please access directly on Telegram"
external_url = self._get_external_url(evt)
# We don't generate external URLs for bot users so only set if known
if external_url is not None:
external_link_content = (
f"Unsupported file, please access directly on Telegram here: {external_url}"
)
attrs = _parse_document_attributes(document.attributes)
file = None
thumb_loc, thumb_size = self.get_largest_photo_size(document)
if thumb_size and not isinstance(thumb_size, (PhotoSize, PhotoCachedSize)):
self.log.debug(f"Unsupported thumbnail type {type(thumb_size)}")
thumb_loc = None
thumb_size = None
if thumb_loc:
try:
file = await util.transfer_thumbnail_to_matrix(
client,
intent,
thumb_loc,
video=None,
mime_type=document.mime_type,
encrypt=self.portal.encrypted,
async_upload=self.config["homeserver.async_media"],
)
except Exception:
self.log.exception("Failed to transfer thumbnail")
if not file:
name = attrs.name or ""
caption = f"\n{evt.message}" if evt.message else ""
return ConvertedMessage(
content=TextMessageEventContent(
msgtype=MessageType.NOTICE,
body=f"{name}{caption}\n{external_link_content}",
)
)
info, name = _parse_document_meta(evt, file, attrs, thumb_size)
event_type = EventType.ROOM_MESSAGE
if not name:
ext = sane_mimetypes.guess_extension(file.mime_type) or ""
name = "unnamed_file" + ext
content = MediaMessageEventContent(
body=name,
info=info,
msgtype={
"video/": MessageType.VIDEO,
"audio/": MessageType.AUDIO,
"image/": MessageType.IMAGE,
}.get(info.mimetype[:6], MessageType.FILE),
)
if file.decryption_info:
content.file = file.decryption_info
else:
content.url = file.mxc
caption_content = (
await formatter.telegram_to_matrix(evt, source, client) if evt.message else None
)
caption_content = f"{caption_content}\n{external_link_content}"
return ConvertedMessage(
type=event_type,
content=content,
caption=caption_content,
disappear_seconds=self._adjust_ttl(evt.media.ttl_seconds),
)
async def _convert_document(
self,
source: au.AbstractUser,
@@ -490,6 +602,9 @@ class TelegramMessageConverter:
) -> ConvertedMessage | None:
document = evt.media.document
if not document:
return None
attrs = _parse_document_attributes(document.attributes)
if document.size > self.matrix.media_config.upload_size:
+5 -3
View File
@@ -83,9 +83,11 @@ def get_base_power_levels(
levels.users_default = overrides.get("users_default", 0)
levels.events_default = overrides.get(
"events_default",
50
if portal.peer_type == "channel" and not portal.megagroup or dbr.send_messages
else 0,
(
50
if portal.peer_type == "channel" and not portal.megagroup or dbr.send_messages
else 0
),
)
for evt_type, value in overrides.get("events", {}).items():
levels.events[EventType.find(evt_type)] = value
+5 -3
View File
@@ -298,9 +298,11 @@ class User(DBUser, AbstractUser, BaseUser):
self._track_metric(METRIC_CONNECTED, connected)
if connected:
await self.push_bridge_state(
BridgeStateEvent.BACKFILLING
if self._is_backfilling
else BridgeStateEvent.CONNECTED,
(
BridgeStateEvent.BACKFILLING
if self._is_backfilling
else BridgeStateEvent.CONNECTED
),
info=self._bridge_state_info,
)
else:
+1
View File
@@ -4,6 +4,7 @@ from .file_transfer import (
convert_image,
transfer_custom_emojis_to_matrix,
transfer_file_to_matrix,
transfer_thumbnail_to_matrix,
unicode_custom_emoji_map,
)
from .parallel_file_transfer import parallel_transfer_to_telegram
@@ -130,9 +130,9 @@ class ProvisioningAPI(AuthAPI):
"about": portal.about,
"username": portal.username,
"megagroup": portal.megagroup,
"can_unbridge": (await portal.can_user_perform(user, "unbridge"))
if user
else False,
"can_unbridge": (
(await portal.can_user_perform(user, "unbridge")) if user else False
),
}
)
@@ -188,9 +188,11 @@ class ProvisioningAPI(AuthAPI):
if force in ("delete", "unbridge"):
delete = force == "delete"
await portal.cleanup_portal(
"Portal deleted (moving to another room)"
if delete
else "Room unbridged (portal moving to another room)",
(
"Portal deleted (moving to another room)"
if delete
else "Room unbridged (portal moving to another room)"
),
puppets_only=not delete,
)
else:
+2 -2
View File
@@ -14,7 +14,7 @@ qrcode>=6,<8
phonenumbers>=8,<9
#/metrics
prometheus_client>=0.6,<0.20
prometheus_client>=0.6,<0.21
#/e2be
python-olm>=3,<4
@@ -22,7 +22,7 @@ pycryptodome>=3,<4
unpaddedbase64>=1,<3
#/sqlite
aiosqlite>=0.16,<0.20
aiosqlite>=0.16,<0.21
#/proxy
python-socks[asyncio]
+1 -1
View File
@@ -9,4 +9,4 @@ line_length = 99
[tool.black]
line-length = 99
target-version = ["py38"]
target-version = ["py310"]
+2 -2
View File
@@ -3,8 +3,8 @@ python-magic>=0.4,<0.5
commonmark>=0.8,<0.10
aiohttp>=3,<4
yarl>=1,<2
mautrix>=0.20.3,<0.21
tulir-telethon==1.34.0a2
mautrix>=0.20.6,<0.21
tulir-telethon==1.37.0a1
asyncpg>=0.20,<0.30
mako>=1,<2
setuptools
+1 -2
View File
@@ -51,7 +51,7 @@ setuptools.setup(
install_requires=install_requires,
extras_require=extras_require,
python_requires="~=3.9",
python_requires="~=3.10",
classifiers=[
"Development Status :: 4 - Beta",
@@ -60,7 +60,6 @@ setuptools.setup(
"Framework :: AsyncIO",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",