From f5cb8baf9934ffa7b6b747e2b16b2b42a3c47b52 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 1 Oct 2022 17:27:56 +0300 Subject: [PATCH] Get reaction limit from server app config --- mautrix_telegram/portal.py | 15 ++++++------ mautrix_telegram/user.py | 26 +++++++++++++++++++-- mautrix_telegram/util/__init__.py | 1 + mautrix_telegram/util/tl_json.py | 39 +++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 mautrix_telegram/util/tl_json.py diff --git a/mautrix_telegram/portal.py b/mautrix_telegram/portal.py index dd2d697b..96ce8369 100644 --- a/mautrix_telegram/portal.py +++ b/mautrix_telegram/portal.py @@ -2319,8 +2319,7 @@ class Portal(DBPortal, BasePortal): existing_reacts = await DBReaction.get_by_sender(msg.mxid, msg.mx_room, user.tgid) new_tg_reactions: list[TypeReaction] = [] reactions_to_remove: list[DBReaction] = [] - # TODO use config https://corefork.telegram.org/api/config#reactions-user-max-default - max_reactions = 3 if user.is_premium else 1 + max_reactions = await user.get_max_reactions() max_reactions -= 1 # Leave one reaction of space for the new reaction for db_reaction in existing_reacts: if db_reaction.reaction == emoji_id: @@ -2862,12 +2861,12 @@ class Portal(DBPortal, BasePortal): return False @staticmethod - async def _get_reaction_limit(sender: TelegramID) -> int: + async def _get_reaction_limit(source: au.AbstractUser, sender: TelegramID) -> int: puppet = await p.Puppet.get_by_tgid(sender, create=False) - # TODO use config https://corefork.telegram.org/api/config#reactions-user-max-default - if puppet and puppet.is_premium: - return 3 - return 1 + is_premium = puppet and puppet.is_premium + if isinstance(source, u.User) and not source.is_bot: + return await source.get_max_reactions(is_premium) + return 3 if is_premium else 1 async def _handle_telegram_reactions_locked( self, @@ -2903,7 +2902,7 @@ class Portal(DBPortal, BasePortal): else: if is_full or ( new_reactions is not None - and len(new_reactions) == await self._get_reaction_limit(sender_id) + and len(new_reactions) == await self._get_reaction_limit(source, sender_id) ): removed.append(existing_reaction) # else: assume the reaction is still there, too much effort to fetch it diff --git a/mautrix_telegram/user.py b/mautrix_telegram/user.py index d0f9f689..c03d8cfd 100644 --- a/mautrix_telegram/user.py +++ b/mautrix_telegram/user.py @@ -15,7 +15,7 @@ # along with this program. If not, see . from __future__ import annotations -from typing import TYPE_CHECKING, AsyncGenerator, AsyncIterable, Awaitable, NamedTuple, cast +from typing import TYPE_CHECKING, Any, AsyncGenerator, AsyncIterable, Awaitable, NamedTuple, cast from datetime import datetime, timezone import asyncio import time @@ -24,6 +24,7 @@ from telethon.errors import AuthKeyDuplicatedError, RPCError, UnauthorizedError from telethon.tl.custom import Dialog from telethon.tl.functions.account import UpdateStatusRequest from telethon.tl.functions.contacts import GetContactsRequest, SearchRequest +from telethon.tl.functions.help import GetAppConfigRequest from telethon.tl.functions.messages import GetAvailableReactionsRequest from telethon.tl.functions.updates import GetStateRequest from telethon.tl.functions.users import GetUsersRequest @@ -54,7 +55,7 @@ from mautrix.types import PushActionType, PushRuleKind, PushRuleScope, RoomID, R from mautrix.util.bridge_state import BridgeState, BridgeStateEvent from mautrix.util.opt_prometheus import Gauge -from . import portal as po, puppet as pu +from . import portal as po, puppet as pu, util from .abstract_user import AbstractUser from .db import Message as DBMessage, PgSession, User as DBUser from .types import TelegramID @@ -91,6 +92,7 @@ class User(DBUser, AbstractUser, BaseUser): _available_emoji_reactions_hash: int | None _available_emoji_reactions_fetched: float _available_emoji_reactions_lock: asyncio.Lock + _app_config: dict[str, Any] | None def __init__( self, @@ -121,6 +123,7 @@ class User(DBUser, AbstractUser, BaseUser): self._available_emoji_reactions_hash = None self._available_emoji_reactions_fetched = 0 self._available_emoji_reactions_lock = asyncio.Lock() + self._app_config = None ( self.relaybot_whitelisted, @@ -734,6 +737,25 @@ class User(DBUser, AbstractUser, BaseUser): ) return self._available_emoji_reactions + def tl_to_json(self) -> Any: + pass + + async def get_app_config(self) -> dict[str, Any]: + if not self._app_config: + cfg = await self.client(GetAppConfigRequest()) + self._app_config = util.parse_tl_json(cfg) + return self._app_config + + async def get_max_reactions(self, is_premium: bool | None = None) -> int: + if is_premium is None: + is_premium = self.is_premium + cfg = await self.get_app_config() + return ( + cfg.get("reactions_user_max_premium", 3) + if is_premium + else cfg.get("reactions_user_max_default", 1) + ) + # endregion # region Class instance lookup diff --git a/mautrix_telegram/util/__init__.py b/mautrix_telegram/util/__init__.py index 08c390d9..eb5a7e21 100644 --- a/mautrix_telegram/util/__init__.py +++ b/mautrix_telegram/util/__init__.py @@ -8,3 +8,4 @@ from .file_transfer import ( ) from .parallel_file_transfer import parallel_transfer_to_telegram from .recursive_dict import recursive_del, recursive_get, recursive_set +from .tl_json import parse_tl_json diff --git a/mautrix_telegram/util/tl_json.py b/mautrix_telegram/util/tl_json.py new file mode 100644 index 00000000..b99b26d8 --- /dev/null +++ b/mautrix_telegram/util/tl_json.py @@ -0,0 +1,39 @@ +# mautrix-telegram - A Matrix-Telegram puppeting bridge +# Copyright (C) 2022 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +from telethon.tl.types import ( + JsonArray, + JsonBool, + JsonNull, + JsonNumber, + JsonObject, + JsonObjectValue, + JsonString, + TypeJSONValue, +) + +from mautrix.types import JSON + + +def parse_tl_json(val: TypeJSONValue) -> JSON: + if isinstance(val, JsonObject): + return {entry.key: parse_tl_json(entry.value) for entry in val.value} + elif isinstance(val, JsonArray): + return [parse_tl_json(item) for item in val.value] + elif isinstance(val, (JsonBool, JsonNumber, JsonString)): + return val.value + elif isinstance(val, JsonNull): + return None + raise ValueError(f"Unsupported type {type(val)} in TL JSON object")