From ae4bcffa068306ecada3e4e0c3b7e9f12b55701f Mon Sep 17 00:00:00 2001 From: miruka Date: Sun, 10 Jan 2021 15:26:19 -0400 Subject: [PATCH] settings.py: add Chat.Composer.TypingNotifications --- src/backend/matrix_client.py | 7 +++++++ src/backend/pcn/section.py | 5 +++++ src/backend/utils.py | 16 ++++++++++++++++ src/config/settings.py | 17 ++++++++++++++++- 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index 56c0c918..9b1d55c5 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -1312,6 +1312,13 @@ class MatrixClient(nio.AsyncClient): ): """Set typing notice to the server.""" + if not utils.config_get_account_room_rule( + rules = self.backend.settings.Chat.Composer.TypingNotifications, + user_id = self.user_id, + room_id = room_id, + ): + return + presence = self.models["accounts"][self.user_id].presence if presence not in [Presence.State.invisible, Presence.State.offline]: diff --git a/src/backend/pcn/section.py b/src/backend/pcn/section.py index 7ed46015..900c28bd 100644 --- a/src/backend/pcn/section.py +++ b/src/backend/pcn/section.py @@ -179,6 +179,11 @@ class Section(MutableMapping): return f"{name}({content})" + def children(self) -> Tuple[Tuple[str, Union["Section", Any]], ...]: + """Return pairs of (name, value) for child sections and properties.""" + return tuple((name, getattr(self, name)) for name in self) + + @classmethod def _register_set_attr(cls, name: str, add_to_set_name: str) -> None: cls.methods.discard(name) diff --git a/src/backend/utils.py b/src/backend/utils.py index 899676d1..3ca0175e 100644 --- a/src/backend/utils.py +++ b/src/backend/utils.py @@ -9,6 +9,7 @@ import html import inspect import io import json +import re import sys import xml.etree.cElementTree as xml_etree from concurrent.futures import ProcessPoolExecutor @@ -33,6 +34,7 @@ from nio.crypto import async_generator_from_data from PIL import Image as PILImage from .color import Color +from .pcn.section import Section if sys.version_info >= (3, 7): from contextlib import asynccontextmanager @@ -105,6 +107,20 @@ def flatten_dict_keys( return flat +def config_get_account_room_rule( + rules: Section, user_id: str, room_id: str, +) -> Any: + """Return best matching rule value for an account/room PCN free Section.""" + + for name, value in reversed(rules.children()): + name = re.sub(r"\s+", " ", name.strip()) + + if name in (user_id, room_id, f"{user_id} {room_id}"): + return value + + return rules.default + + async def is_svg(file: File) -> bool: """Return whether the file is a SVG (`lxml` is used for detection).""" diff --git a/src/config/settings.py b/src/config/settings.py index 44488d62..b6698b7d 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -98,7 +98,7 @@ class Chat: always_center_header: bool = False # When the chat timeline is larger than this pixel number, - # Align your own messages to the left of the timeline instead of right. + # align your own messages to the left of the timeline instead of right. # Can be 0 to always show your messages on the left. own_messages_on_left_above: int = 895 @@ -122,6 +122,21 @@ class Chat: mark_read_delay: float = 0.2 class Composer: + class TypingNotifications: + # Rules controlling whether " is typing..." notifications + # should be sent to other users in rooms. + # The `default` property is required. Other properties can be + # added: user IDs, room IDs, or space-separated user + room IDs. + + # Send typing notifications everywhere by default: + default: bool = True + # But don't send them for rooms under this account: + "@account_1:example.org": bool = False + # Neither send them in this room, regardless of the account used: + "!room:example.org": bool = False + # Except if it's this account and this room, then send them: + "@account_2:example.org !room:example.org": bool = True + class Aliases: # Each property is the user ID of an account, value is the alias. # From any chat, start a message with an alias followed by a space