From 9b75c78b4df276fe40d3f1b92a57481c3e970886 Mon Sep 17 00:00:00 2001 From: Neil Johnson Date: Tue, 14 Aug 2018 11:20:41 +0100 Subject: support server notice state events for resource limits --- synapse/server_notices/server_notices_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'synapse/server_notices/server_notices_manager.py') diff --git a/synapse/server_notices/server_notices_manager.py b/synapse/server_notices/server_notices_manager.py index a26deace53..4806977a89 100644 --- a/synapse/server_notices/server_notices_manager.py +++ b/synapse/server_notices/server_notices_manager.py @@ -46,7 +46,7 @@ class ServerNoticesManager(object): return self._config.server_notices_mxid is not None @defer.inlineCallbacks - def send_notice(self, user_id, event_content): + def send_notice(self, user_id, event_content, type): """Send a notice to the given user Creates the server notices room, if none exists. @@ -67,7 +67,7 @@ class ServerNoticesManager(object): yield self._event_creation_handler.create_and_send_nonmember_event( requester, { - "type": EventTypes.Message, + "type": type, "room_id": room_id, "sender": system_mxid, "content": event_content, -- cgit 1.5.1 From e2c9fe0a6a6dbe4f81402b79b6605e17fb05e5d4 Mon Sep 17 00:00:00 2001 From: Neil Johnson Date: Tue, 14 Aug 2018 13:32:56 +0100 Subject: backout ability to pass in event type to server notices --- synapse/server_notices/consent_server_notices.py | 2 +- synapse/server_notices/resource_limits_server_notices.py | 3 ++- synapse/server_notices/server_notices_manager.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'synapse/server_notices/server_notices_manager.py') diff --git a/synapse/server_notices/consent_server_notices.py b/synapse/server_notices/consent_server_notices.py index 6d5caedb08..783ceae1ad 100644 --- a/synapse/server_notices/consent_server_notices.py +++ b/synapse/server_notices/consent_server_notices.py @@ -104,7 +104,7 @@ class ConsentServerNotices(object): }, ) yield self._server_notices_manager.send_notice( - user_id, content, EventTypes.Message + user_id, content ) yield self._store.user_set_consent_server_notice_sent( user_id, self._current_consent_version, diff --git a/synapse/server_notices/resource_limits_server_notices.py b/synapse/server_notices/resource_limits_server_notices.py index 2f49dae168..0470f66205 100644 --- a/synapse/server_notices/resource_limits_server_notices.py +++ b/synapse/server_notices/resource_limits_server_notices.py @@ -91,6 +91,7 @@ class ResourceLimitsServerNotices(object): self._send_server_notice(user_id, content) self._notified_of_blocking.add(user_id) + @defer.inlineCallbacks def _send_server_notice(self, user_id, content): """Sends Server notice @@ -104,7 +105,7 @@ class ResourceLimitsServerNotices(object): """ try: yield self._server_notices_manager.send_notice( - user_id, content, EventTypes.ServerNoticeLimitReached + user_id, content ) except SynapseError as e: logger.error("Error sending server notice about resource limits: %s", e) diff --git a/synapse/server_notices/server_notices_manager.py b/synapse/server_notices/server_notices_manager.py index 4806977a89..a26deace53 100644 --- a/synapse/server_notices/server_notices_manager.py +++ b/synapse/server_notices/server_notices_manager.py @@ -46,7 +46,7 @@ class ServerNoticesManager(object): return self._config.server_notices_mxid is not None @defer.inlineCallbacks - def send_notice(self, user_id, event_content, type): + def send_notice(self, user_id, event_content): """Send a notice to the given user Creates the server notices room, if none exists. @@ -67,7 +67,7 @@ class ServerNoticesManager(object): yield self._event_creation_handler.create_and_send_nonmember_event( requester, { - "type": type, + "type": EventTypes.Message, "room_id": room_id, "sender": system_mxid, "content": event_content, -- cgit 1.5.1 From eabc5f827107a24717655e0eec376adf88b574c4 Mon Sep 17 00:00:00 2001 From: Neil Johnson Date: Wed, 15 Aug 2018 15:04:52 +0100 Subject: wip cut at sending resource server notices --- .../resource_limits_server_notices.py | 69 +++++++++++----------- synapse/server_notices/server_notices_manager.py | 30 ++++++---- 2 files changed, 55 insertions(+), 44 deletions(-) (limited to 'synapse/server_notices/server_notices_manager.py') diff --git a/synapse/server_notices/resource_limits_server_notices.py b/synapse/server_notices/resource_limits_server_notices.py index 0470f66205..94d0f98189 100644 --- a/synapse/server_notices/resource_limits_server_notices.py +++ b/synapse/server_notices/resource_limits_server_notices.py @@ -40,6 +40,7 @@ class ResourceLimitsServerNotices(object): self._notified_of_blocking = set() self._resouce_limited = False + # Config checks? @defer.inlineCallbacks @@ -69,43 +70,43 @@ class ResourceLimitsServerNotices(object): self._resouce_limited = False # Need to start removing notices if user_id in self._notified_of_blocking: - # Send message to remove warning - needs updating + # Send message to remove warning + # send state event here + # How do I do this? if drop the id, how to refer to it? content = { - 'body': '', - 'admin_email': '', + "pinned":[] } - self._send_server_notice(user_id, content) + yield self._server_notices_manager.send_notice( + user_id, content, EventTypes.Pinned, '', + ) + self._notified_of_blocking.remove(user_id) except AuthError as e: # Need to start notifying of blocking - - self._resouce_limited = True - if user_id not in self._notified_of_blocking: - # TODO use admin email contained in error once PR lands - content = { - 'body': e.msg, - 'admin_email': 'stunt@adminemail.com', - 'msgtype': 'm.text' - } - self._send_server_notice(user_id, content) - self._notified_of_blocking.add(user_id) - - - @defer.inlineCallbacks - def _send_server_notice(self, user_id, content): - """Sends Server notice - - Args: - user_id(str): The user to send to - content(str): The content of the message - - Returns: - Deferred[] - """ - try: - yield self._server_notices_manager.send_notice( - user_id, content - ) - except SynapseError as e: - logger.error("Error sending server notice about resource limits: %s", e) + try: + self._resouce_limited = True + if user_id not in self._notified_of_blocking: + # TODO use admin email contained in error once PR lands + content = { + 'body': e.msg, + 'admin_email': 'stunt@adminemail.com', + } + event = yield self._server_notices_manager.send_notice( + user_id, content, EventTypes.ServerNoticeLimitReached + ) + + # send server notices state event here + # TODO Over writing pinned events + content = { + "pinned":[ + event.event_id, + ] + } + yield self._server_notices_manager.send_notice( + user_id, content, EventTypes.Pinned, '', + ) + + self._notified_of_blocking.add(user_id) + except SynapseError as e: + logger.error("Error sending server notice about resource limits: %s", e) diff --git a/synapse/server_notices/server_notices_manager.py b/synapse/server_notices/server_notices_manager.py index a26deace53..3e57f8211a 100644 --- a/synapse/server_notices/server_notices_manager.py +++ b/synapse/server_notices/server_notices_manager.py @@ -46,7 +46,10 @@ class ServerNoticesManager(object): return self._config.server_notices_mxid is not None @defer.inlineCallbacks - def send_notice(self, user_id, event_content): + def send_notice( + self, user_id, event_content, + type=EventTypes.Message, state_key=None + ): """Send a notice to the given user Creates the server notices room, if none exists. @@ -54,9 +57,11 @@ class ServerNoticesManager(object): Args: user_id (str): mxid of user to send event to. event_content (dict): content of event to send + type(EventTypes): type of event + is_state_event(bool): Is the event a state event Returns: - Deferred[None] + Deferred[FrozenEvent] """ room_id = yield self.get_notice_room_for_user(user_id) @@ -65,15 +70,19 @@ class ServerNoticesManager(object): logger.info("Sending server notice to %s", user_id) - yield self._event_creation_handler.create_and_send_nonmember_event( - requester, { - "type": EventTypes.Message, - "room_id": room_id, - "sender": system_mxid, - "content": event_content, - }, - ratelimit=False, + event_dict = { + "type": type, + "room_id": room_id, + "sender": system_mxid, + "content": event_content, + } + if state_key: + event_dict['state_key'] = state_key + + res = yield self._event_creation_handler.create_and_send_nonmember_event( + requester, event_dict, ratelimit=False, ) + defer.returnValue(res) @cachedInlineCallbacks() def get_notice_room_for_user(self, user_id): @@ -141,6 +150,7 @@ class ServerNoticesManager(object): creator_join_profile=join_profile, ) room_id = info['room_id'] + yield self._store.add_tag_to_room(user_id, room_id, 'm.server_notice', None) logger.info("Created server notices room %s for %s", room_id, user_id) defer.returnValue(room_id) -- cgit 1.5.1 From c055c91655cbe5d4e6b36d86a00dafb02c585b6d Mon Sep 17 00:00:00 2001 From: Neil Johnson Date: Thu, 16 Aug 2018 11:10:19 +0100 Subject: fix case where empty string state check is evaulated as False --- synapse/server_notices/server_notices_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'synapse/server_notices/server_notices_manager.py') diff --git a/synapse/server_notices/server_notices_manager.py b/synapse/server_notices/server_notices_manager.py index 3e57f8211a..22c819cc5b 100644 --- a/synapse/server_notices/server_notices_manager.py +++ b/synapse/server_notices/server_notices_manager.py @@ -76,7 +76,8 @@ class ServerNoticesManager(object): "sender": system_mxid, "content": event_content, } - if state_key: + + if state_key is not None: event_dict['state_key'] = state_key res = yield self._event_creation_handler.create_and_send_nonmember_event( -- cgit 1.5.1 From 3c1080b6e4a4d7c80d67bd9bee45d845aa145088 Mon Sep 17 00:00:00 2001 From: Neil Johnson Date: Thu, 16 Aug 2018 17:02:04 +0100 Subject: refactor for readability, and reuse caching for setting tags --- .../resource_limits_server_notices.py | 176 +++++++++++++-------- synapse/server_notices/server_notices_manager.py | 6 +- .../test_resource_limits_server_notices.py | 1 + 3 files changed, 115 insertions(+), 68 deletions(-) (limited to 'synapse/server_notices/server_notices_manager.py') diff --git a/synapse/server_notices/resource_limits_server_notices.py b/synapse/server_notices/resource_limits_server_notices.py index 570e8307cc..61becca758 100644 --- a/synapse/server_notices/resource_limits_server_notices.py +++ b/synapse/server_notices/resource_limits_server_notices.py @@ -18,6 +18,7 @@ from twisted.internet import defer from synapse.api.constants import EventTypes from synapse.api.errors import AuthError, SynapseError +from synapse.server_notices.server_notices_manager import SERVER_NOTICE_ROOM_TAG logger = logging.getLogger(__name__) @@ -57,80 +58,121 @@ class ResourceLimitsServerNotices(object): if self._hs_disabled is True: return - if self._limit_usage_by_mau is True: - timestamp = yield self._store.user_last_seen_monthly_active(user_id) - if timestamp is None: - # This user will be blocked from receiving the notice anyway. - # In practice, not sure we can ever get here - return - - room_id = yield self._server_notices_manager.get_notice_room_for_user(user_id) + if self._limit_usage_by_mau is False: + return - # Need to set tag here because room may have been created prior to - # tagging being set on creation. Ideally would set something to read - # room tags first, and cache that aggressively/ - yield self._store.add_tag_to_room(user_id, room_id, 'm.server_notice', None) + timestamp = yield self._store.user_last_seen_monthly_active(user_id) + if timestamp is None: + # This user will be blocked from receiving the notice anyway. + # In practice, not sure we can ever get here + return - currently_blocked = False - pinned_state_event = None - try: - pinned_state_event = yield self._state.get_current_state( - room_id, event_type=EventTypes.Pinned + # Determine current state of room + + room_id = yield self._server_notices_manager.get_notice_room_for_user(user_id) + + yield self._check_and_set_tags(user_id, room_id) + currently_blocked, ref_events = yield self._is_room_currently_blocked(room_id) + + try: + # Normally should always pass in user_id if you have it, but in + # this case are checking what would happen to other users if they + # were to arrive. + yield self.auth.check_auth_blocking() + if currently_blocked: + # Room is notifying of a block, when it ought not to be. + # Remove block notification + content = { + "pinned": ref_events + } + yield self._server_notices_manager.send_notice( + user_id, content, EventTypes.Pinned, '', ) - except AuthError as e: - # The user has yet to join the server notices room - pass - - referenced_events = [] - if pinned_state_event is not None: - referenced_events = pinned_state_event.content.get('pinned') - - events = yield self._store.get_events(referenced_events) - for event_id, event in events.items(): - if event.type == EventTypes.ServerNoticeLimitReached: - currently_blocked = True + + except AuthError as e: + try: - # Normally should always pass in user_id if you have it, but in - # this case are checking what would happen to other users if they - # were to arrive. - yield self.auth.check_auth_blocking() - - # Need to start removing notices - # if user_id in self._notified_of_blocking: - if currently_blocked: - # Send message to remove warning - # send state event here - # How do I do this? if drop the id, how to refer to it? + if not currently_blocked: + # Room is not notifying of a block, when it ought to be. + # Add block notification content = { - "pinned": [] + 'body': e.msg, + 'admin_uri': self._admin_uri, + } + event = yield self._server_notices_manager.send_notice( + user_id, content, EventTypes.ServerNoticeLimitReached + ) + + content = { + "pinned": [ + event.event_id, + ] } yield self._server_notices_manager.send_notice( user_id, content, EventTypes.Pinned, '', ) - except AuthError as e: - # Need to start notifying of blocking - try: - if not currently_blocked: - # TODO use admin email contained in error once PR lands - content = { - 'body': e.msg, - 'admin_uri': self._admin_uri, - } - event = yield self._server_notices_manager.send_notice( - user_id, content, EventTypes.ServerNoticeLimitReached - ) - - # send server notices state event here - # TODO Over writing pinned events - content = { - "pinned": [ - event.event_id, - ] - } - yield self._server_notices_manager.send_notice( - user_id, content, EventTypes.Pinned, '', - ) - - except SynapseError as e: - logger.error("Error sending resource limits server notice: %s", e) + except SynapseError as e: + logger.error("Error sending resource limits server notice: %s", e) + + @defer.inlineCallbacks + def _check_and_set_tags(self, user_id, room_id): + """ + Since server notices rooms were originally not with tags, + important to check that tags have been set correctly + Args: + user_id(str): the user in question + room_id(str): the server notices room for that user + """ + tags = yield self._store.get_tags_for_user(user_id) + server_notices_tags = tags.get(room_id) + need_to_set_tag = True + if server_notices_tags: + if server_notice_tags.get(SERVER_NOTICE_ROOM_TAG): + # tag already present, nothing to do here + need_to_set_tag = False + if need_to_set_tag: + yield self._store.add_tag_to_room( + user_id, room_id, SERVER_NOTICE_ROOM_TAG, None + ) + + @defer.inlineCallbacks + def _is_room_currently_blocked(self, room_id): + """ + Determines if the room is currently blocked + + Args: + room_id(str): The room id of the server notices room + + Returns: + + bool: Is the room currently blocked + list: The list of pinned events that are unrelated to limit blocking + This list can be used as a convenience in the case where the block + is to be lifted and the remaining pinned event references need to be + preserved + """ + currently_blocked = False + pinned_state_event = None + try: + pinned_state_event = yield self._state.get_current_state( + room_id, event_type=EventTypes.Pinned + ) + except AuthError as e: + # The user has yet to join the server notices room + pass + + referenced_events = [] + if pinned_state_event is not None: + referenced_events = pinned_state_event.content.get('pinned') + + events = yield self._store.get_events(referenced_events) + event_to_remove = None + for event_id, event in events.items(): + if event.type == EventTypes.ServerNoticeLimitReached: + currently_blocked = True + # remove event in case we need to disable blocking later on. + if event_id in referenced_events: + referenced_events.remove(event.event_id) + + defer.returnValue((currently_blocked, referenced_events)) diff --git a/synapse/server_notices/server_notices_manager.py b/synapse/server_notices/server_notices_manager.py index 22c819cc5b..5968104a99 100644 --- a/synapse/server_notices/server_notices_manager.py +++ b/synapse/server_notices/server_notices_manager.py @@ -22,6 +22,8 @@ from synapse.util.caches.descriptors import cachedInlineCallbacks logger = logging.getLogger(__name__) +SERVER_NOTICE_ROOM_TAG = "m.server_notice" + class ServerNoticesManager(object): def __init__(self, hs): @@ -151,7 +153,9 @@ class ServerNoticesManager(object): creator_join_profile=join_profile, ) room_id = info['room_id'] - yield self._store.add_tag_to_room(user_id, room_id, 'm.server_notice', None) + yield self._store.add_tag_to_room( + user_id, room_id, SERVER_NOTICE_ROOM_TAG, None + ) logger.info("Created server notices room %s for %s", room_id, user_id) defer.returnValue(room_id) diff --git a/tests/server_notices/test_resource_limits_server_notices.py b/tests/server_notices/test_resource_limits_server_notices.py index fb8c593a75..ccb69097b1 100644 --- a/tests/server_notices/test_resource_limits_server_notices.py +++ b/tests/server_notices/test_resource_limits_server_notices.py @@ -54,6 +54,7 @@ class TestResourceLimitsServerNotices(unittest.TestCase): self._rlsn._server_notices_manager.get_notice_room_for_user = Mock( returnValue="" ) + self._rlsn._store.add_tag_to_room = Mock() self.hs.config.admin_uri = "mailto:user@test.com" @defer.inlineCallbacks -- cgit 1.5.1 From 14d49c51dbbf1222477e076eb4d8d79c0181f901 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 24 Aug 2018 14:44:16 +0100 Subject: Make content of tag an empty object rather than null --- synapse/server_notices/resource_limits_server_notices.py | 2 +- synapse/server_notices/server_notices_manager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'synapse/server_notices/server_notices_manager.py') diff --git a/synapse/server_notices/resource_limits_server_notices.py b/synapse/server_notices/resource_limits_server_notices.py index 98495ddbf9..7144dfcbc2 100644 --- a/synapse/server_notices/resource_limits_server_notices.py +++ b/synapse/server_notices/resource_limits_server_notices.py @@ -153,7 +153,7 @@ class ResourceLimitsServerNotices(object): need_to_set_tag = False if need_to_set_tag: yield self._store.add_tag_to_room( - user_id, room_id, SERVER_NOTICE_ROOM_TAG, None + user_id, room_id, SERVER_NOTICE_ROOM_TAG, {} ) @defer.inlineCallbacks diff --git a/synapse/server_notices/server_notices_manager.py b/synapse/server_notices/server_notices_manager.py index 5968104a99..a5e4728859 100644 --- a/synapse/server_notices/server_notices_manager.py +++ b/synapse/server_notices/server_notices_manager.py @@ -154,7 +154,7 @@ class ServerNoticesManager(object): ) room_id = info['room_id'] yield self._store.add_tag_to_room( - user_id, room_id, SERVER_NOTICE_ROOM_TAG, None + user_id, room_id, SERVER_NOTICE_ROOM_TAG, {}, ) logger.info("Created server notices room %s for %s", room_id, user_id) -- cgit 1.5.1 From cdd24449eeec12f869ceca233085f957d8dc7d19 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 24 Aug 2018 14:50:03 +0100 Subject: Ensure we wake up /sync when we add tag to notice room --- synapse/server_notices/resource_limits_server_notices.py | 7 ++++++- synapse/server_notices/server_notices_manager.py | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'synapse/server_notices/server_notices_manager.py') diff --git a/synapse/server_notices/resource_limits_server_notices.py b/synapse/server_notices/resource_limits_server_notices.py index 7144dfcbc2..893b9001cd 100644 --- a/synapse/server_notices/resource_limits_server_notices.py +++ b/synapse/server_notices/resource_limits_server_notices.py @@ -46,6 +46,8 @@ class ResourceLimitsServerNotices(object): self._message_handler = hs.get_message_handler() self._state = hs.get_state_handler() + self._notifier = hs.get_notifier() + @defer.inlineCallbacks def maybe_send_server_notice_to_user(self, user_id): """Check if we need to send a notice to this user, this will be true in @@ -152,9 +154,12 @@ class ResourceLimitsServerNotices(object): # tag already present, nothing to do here need_to_set_tag = False if need_to_set_tag: - yield self._store.add_tag_to_room( + max_id = yield self._store.add_tag_to_room( user_id, room_id, SERVER_NOTICE_ROOM_TAG, {} ) + self._notifier.on_new_event( + "account_data_key", max_id, users=[user_id] + ) @defer.inlineCallbacks def _is_room_currently_blocked(self, room_id): diff --git a/synapse/server_notices/server_notices_manager.py b/synapse/server_notices/server_notices_manager.py index a5e4728859..c5cc6d728e 100644 --- a/synapse/server_notices/server_notices_manager.py +++ b/synapse/server_notices/server_notices_manager.py @@ -39,6 +39,8 @@ class ServerNoticesManager(object): self._event_creation_handler = hs.get_event_creation_handler() self._is_mine_id = hs.is_mine_id + self._notifier = hs.get_notifier() + def is_enabled(self): """Checks if server notices are enabled on this server. @@ -153,9 +155,13 @@ class ServerNoticesManager(object): creator_join_profile=join_profile, ) room_id = info['room_id'] - yield self._store.add_tag_to_room( + + max_id = yield self._store.add_tag_to_room( user_id, room_id, SERVER_NOTICE_ROOM_TAG, {}, ) + self._notifier.on_new_event( + "account_data_key", max_id, users=[user_id] + ) logger.info("Created server notices room %s for %s", room_id, user_id) defer.returnValue(room_id) -- cgit 1.5.1