diff --git a/synapse/handlers/admin.py b/synapse/handlers/admin.py
index a53cd62d3c..be3203ac80 100644
--- a/synapse/handlers/admin.py
+++ b/synapse/handlers/admin.py
@@ -90,6 +90,7 @@ class AdminHandler:
Membership.LEAVE,
Membership.BAN,
Membership.INVITE,
+ Membership.KNOCK,
),
)
@@ -122,6 +123,13 @@ class AdminHandler:
invited_state = invite.unsigned["invite_room_state"]
writer.write_invite(room_id, invite, invited_state)
+ if room.membership == Membership.KNOCK:
+ event_id = room.event_id
+ knock = await self.store.get_event(event_id, allow_none=True)
+ if knock:
+ knock_state = knock.unsigned["knock_room_state"]
+ writer.write_knock(room_id, knock, knock_state)
+
continue
# We only want to bother fetching events up to the last time they
@@ -239,6 +247,20 @@ class ExfiltrationWriter(metaclass=abc.ABCMeta):
raise NotImplementedError()
@abc.abstractmethod
+ def write_knock(
+ self, room_id: str, event: EventBase, state: StateMap[dict]
+ ) -> None:
+ """Write a knock for the room, with associated knock state.
+
+ Args:
+ room_id: The room ID the knock is for.
+ event: The knock event.
+ state: A subset of the state at the knock, with a subset of the
+ event keys (type, state_key content and sender).
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
def finished(self) -> Any:
"""Called when all data has successfully been exported and written.
diff --git a/synapse/handlers/appservice.py b/synapse/handlers/appservice.py
index 163278708c..67f8ffcaff 100644
--- a/synapse/handlers/appservice.py
+++ b/synapse/handlers/appservice.py
@@ -182,22 +182,29 @@ class ApplicationServicesHandler:
def notify_interested_services_ephemeral(
self,
stream_key: str,
- new_token: Optional[int],
+ new_token: Union[int, RoomStreamToken],
users: Optional[Collection[Union[str, UserID]]] = None,
) -> None:
- """This is called by the notifier in the background
- when a ephemeral event handled by the homeserver.
-
- This will determine which appservices
- are interested in the event, and submit them.
+ """
+ This is called by the notifier in the background when an ephemeral event is handled
+ by the homeserver.
- Events will only be pushed to appservices
- that have opted into ephemeral events
+ This will determine which appservices are interested in the event, and submit them.
Args:
stream_key: The stream the event came from.
- new_token: The latest stream token
- users: The user(s) involved with the event.
+
+ `stream_key` can be "typing_key", "receipt_key" or "presence_key". Any other
+ value for `stream_key` will cause this function to return early.
+
+ Ephemeral events will only be pushed to appservices that have opted into
+ them.
+
+ Appservices will only receive ephemeral events that fall within their
+ registered user and room namespaces.
+
+ new_token: The stream token of the event.
+ users: The users that should be informed of the new event, if any.
"""
if not self.notify_appservices:
return
@@ -205,6 +212,19 @@ class ApplicationServicesHandler:
if stream_key not in ("typing_key", "receipt_key", "presence_key"):
return
+ # Assert that new_token is an integer (and not a RoomStreamToken).
+ # All of the supported streams that this function handles use an
+ # integer to track progress (rather than a RoomStreamToken - a
+ # vector clock implementation) as they don't support multiple
+ # stream writers.
+ #
+ # As a result, we simply assert that new_token is an integer.
+ # If we do end up needing to pass a RoomStreamToken down here
+ # in the future, using RoomStreamToken.stream (the minimum stream
+ # position) to convert to an ascending integer value should work.
+ # Additional context: https://github.com/matrix-org/synapse/pull/11137
+ assert isinstance(new_token, int)
+
services = [
service
for service in self.store.get_app_services()
@@ -224,29 +244,39 @@ class ApplicationServicesHandler:
self,
services: List[ApplicationService],
stream_key: str,
- new_token: Optional[int],
+ new_token: int,
users: Collection[Union[str, UserID]],
) -> None:
logger.debug("Checking interested services for %s" % (stream_key))
with Measure(self.clock, "notify_interested_services_ephemeral"):
for service in services:
- # Only handle typing if we have the latest token
- if stream_key == "typing_key" and new_token is not None:
+ if stream_key == "typing_key":
+ # Note that we don't persist the token (via set_type_stream_id_for_appservice)
+ # for typing_key due to performance reasons and due to their highly
+ # ephemeral nature.
+ #
+ # Instead we simply grab the latest typing updates in _handle_typing
+ # and, if they apply to this application service, send it off.
events = await self._handle_typing(service, new_token)
if events:
self.scheduler.submit_ephemeral_events_for_as(service, events)
- # We don't persist the token for typing_key for performance reasons
+
elif stream_key == "receipt_key":
events = await self._handle_receipts(service)
if events:
self.scheduler.submit_ephemeral_events_for_as(service, events)
+
+ # Persist the latest handled stream token for this appservice
await self.store.set_type_stream_id_for_appservice(
service, "read_receipt", new_token
)
+
elif stream_key == "presence_key":
events = await self._handle_presence(service, users)
if events:
self.scheduler.submit_ephemeral_events_for_as(service, events)
+
+ # Persist the latest handled stream token for this appservice
await self.store.set_type_stream_id_for_appservice(
service, "presence", new_token
)
@@ -254,18 +284,54 @@ class ApplicationServicesHandler:
async def _handle_typing(
self, service: ApplicationService, new_token: int
) -> List[JsonDict]:
+ """
+ Return the typing events since the given stream token that the given application
+ service should receive.
+
+ First fetch all typing events between the given typing stream token (non-inclusive)
+ and the latest typing event stream token (inclusive). Then return only those typing
+ events that the given application service may be interested in.
+
+ Args:
+ service: The application service to check for which events it should receive.
+ new_token: A typing event stream token.
+
+ Returns:
+ A list of JSON dictionaries containing data derived from the typing events that
+ should be sent to the given application service.
+ """
typing_source = self.event_sources.sources.typing
# Get the typing events from just before current
typing, _ = await typing_source.get_new_events_as(
service=service,
# For performance reasons, we don't persist the previous
- # token in the DB and instead fetch the latest typing information
+ # token in the DB and instead fetch the latest typing event
# for appservices.
+ # TODO: It'd likely be more efficient to simply fetch the
+ # typing event with the given 'new_token' stream token and
+ # check if the given service was interested, rather than
+ # iterating over all typing events and only grabbing the
+ # latest few.
from_key=new_token - 1,
)
return typing
async def _handle_receipts(self, service: ApplicationService) -> List[JsonDict]:
+ """
+ Return the latest read receipts that the given application service should receive.
+
+ First fetch all read receipts between the last receipt stream token that this
+ application service should have previously received (non-inclusive) and the
+ latest read receipt stream token (inclusive). Then from that set, return only
+ those read receipts that the given application service may be interested in.
+
+ Args:
+ service: The application service to check for which events it should receive.
+
+ Returns:
+ A list of JSON dictionaries containing data derived from the read receipts that
+ should be sent to the given application service.
+ """
from_key = await self.store.get_type_stream_id_for_appservice(
service, "read_receipt"
)
@@ -278,6 +344,22 @@ class ApplicationServicesHandler:
async def _handle_presence(
self, service: ApplicationService, users: Collection[Union[str, UserID]]
) -> List[JsonDict]:
+ """
+ Return the latest presence updates that the given application service should receive.
+
+ First, filter the given users list to those that the application service is
+ interested in. Then retrieve the latest presence updates since the
+ the last-known previously received presence stream token for the given
+ application service. Return those presence updates.
+
+ Args:
+ service: The application service that ephemeral events are being sent to.
+ users: The users that should receive the presence update.
+
+ Returns:
+ A list of json dictionaries containing data derived from the presence events
+ that should be sent to the given application service.
+ """
events: List[JsonDict] = []
presence_source = self.event_sources.sources.presence
from_key = await self.store.get_type_stream_id_for_appservice(
@@ -290,9 +372,9 @@ class ApplicationServicesHandler:
interested = await service.is_interested_in_presence(user, self.store)
if not interested:
continue
+
presence_events, _ = await presence_source.get_new_events(
user=user,
- service=service,
from_key=from_key,
)
time_now = self.clock.time_msec()
diff --git a/synapse/handlers/auth.py b/synapse/handlers/auth.py
index ebe75a9e9b..d508d7d32a 100644
--- a/synapse/handlers/auth.py
+++ b/synapse/handlers/auth.py
@@ -62,7 +62,6 @@ from synapse.http.server import finish_request, respond_with_html
from synapse.http.site import SynapseRequest
from synapse.logging.context import defer_to_thread
from synapse.metrics.background_process_metrics import run_as_background_process
-from synapse.module_api import ModuleApi
from synapse.storage.roommember import ProfileInfo
from synapse.types import JsonDict, Requester, UserID
from synapse.util import stringutils as stringutils
@@ -73,6 +72,7 @@ from synapse.util.stringutils import base62_encode
from synapse.util.threepids import canonicalise_email
if TYPE_CHECKING:
+ from synapse.module_api import ModuleApi
from synapse.rest.client.login import LoginResponse
from synapse.server import HomeServer
@@ -1818,7 +1818,9 @@ def load_legacy_password_auth_providers(hs: "HomeServer") -> None:
def load_single_legacy_password_auth_provider(
- module: Type, config: JsonDict, api: ModuleApi
+ module: Type,
+ config: JsonDict,
+ api: "ModuleApi",
) -> None:
try:
provider = module(config=config, account_handler=api)
diff --git a/synapse/handlers/device.py b/synapse/handlers/device.py
index 6eafbea25d..68b446eb66 100644
--- a/synapse/handlers/device.py
+++ b/synapse/handlers/device.py
@@ -454,6 +454,10 @@ class DeviceHandler(DeviceWorkerHandler):
) -> None:
"""Notify that a user's device(s) has changed. Pokes the notifier, and
remote servers if the user is local.
+
+ Args:
+ user_id: The Matrix ID of the user who's device list has been updated.
+ device_ids: The device IDs that have changed.
"""
if not device_ids:
# No changes to notify about, so this is a no-op.
diff --git a/synapse/handlers/directory.py b/synapse/handlers/directory.py
index 14ed7d9879..8ca5f60b1c 100644
--- a/synapse/handlers/directory.py
+++ b/synapse/handlers/directory.py
@@ -145,7 +145,7 @@ class DirectoryHandler:
if not self.config.roomdirectory.is_alias_creation_allowed(
user_id, room_id, room_alias_str
):
- # Lets just return a generic message, as there may be all sorts of
+ # Let's just return a generic message, as there may be all sorts of
# reasons why we said no. TODO: Allow configurable error messages
# per alias creation rule?
raise SynapseError(403, "Not allowed to create alias")
@@ -245,7 +245,7 @@ class DirectoryHandler:
servers = result.servers
else:
try:
- fed_result = await self.federation.make_query(
+ fed_result: Optional[JsonDict] = await self.federation.make_query(
destination=room_alias.domain,
query_type="directory",
args={"room_alias": room_alias.to_string()},
@@ -461,7 +461,7 @@ class DirectoryHandler:
if not self.config.roomdirectory.is_publishing_room_allowed(
user_id, room_id, room_aliases
):
- # Lets just return a generic message, as there may be all sorts of
+ # Let's just return a generic message, as there may be all sorts of
# reasons why we said no. TODO: Allow configurable error messages
# per alias creation rule?
raise SynapseError(403, "Not allowed to publish room")
diff --git a/synapse/handlers/federation_event.py b/synapse/handlers/federation_event.py
index 2be3ef89a3..afc1de9894 100644
--- a/synapse/handlers/federation_event.py
+++ b/synapse/handlers/federation_event.py
@@ -361,6 +361,7 @@ class FederationEventHandler:
# need to.
await self._event_creation_handler.cache_joined_hosts_for_event(event, context)
+ await self._check_for_soft_fail(event, None, origin=origin)
await self._run_push_actions_and_persist_event(event, context)
return event, context
@@ -402,29 +403,28 @@ class FederationEventHandler:
"""Persists the events returned by a send_join
Checks the auth chain is valid (and passes auth checks) for the
- state and event. Then persists the auth chain and state atomically.
- Persists the event separately. Notifies about the persisted events
- where appropriate.
-
- Will attempt to fetch missing auth events.
+ state and event. Then persists all of the events.
+ Notifies about the persisted events where appropriate.
Args:
origin: Where the events came from
- room_id,
+ room_id:
auth_events
state
event
room_version: The room version we expect this room to have, and
will raise if it doesn't match the version in the create event.
+
+ Returns:
+ The stream ID after which all events have been persisted.
+
+ Raises:
+ SynapseError if the response is in some way invalid.
"""
- events_to_context = {}
for e in itertools.chain(auth_events, state):
e.internal_metadata.outlier = True
- events_to_context[e.event_id] = EventContext.for_outlier()
- event_map = {
- e.event_id: e for e in itertools.chain(auth_events, state, [event])
- }
+ event_map = {e.event_id: e for e in itertools.chain(auth_events, state)}
create_event = None
for e in auth_events:
@@ -444,68 +444,40 @@ class FederationEventHandler:
if room_version.identifier != room_version_id:
raise SynapseError(400, "Room version mismatch")
- missing_auth_events = set()
- for e in itertools.chain(auth_events, state, [event]):
- for e_id in e.auth_event_ids():
- if e_id not in event_map:
- missing_auth_events.add(e_id)
-
- for e_id in missing_auth_events:
- m_ev = await self._federation_client.get_pdu(
- [origin],
- e_id,
- room_version=room_version,
- outlier=True,
- timeout=10000,
- )
- if m_ev and m_ev.event_id == e_id:
- event_map[e_id] = m_ev
- else:
- logger.info("Failed to find auth event %r", e_id)
-
- for e in itertools.chain(auth_events, state, [event]):
- auth_for_e = [
- event_map[e_id] for e_id in e.auth_event_ids() if e_id in event_map
- ]
- if create_event:
- auth_for_e.append(create_event)
-
- try:
- validate_event_for_room_version(room_version, e)
- check_auth_rules_for_event(room_version, e, auth_for_e)
- except SynapseError as err:
- # we may get SynapseErrors here as well as AuthErrors. For
- # instance, there are a couple of (ancient) events in some
- # rooms whose senders do not have the correct sigil; these
- # cause SynapseErrors in auth.check. We don't want to give up
- # the attempt to federate altogether in such cases.
-
- logger.warning("Rejecting %s because %s", e.event_id, err.msg)
-
- if e == event:
- raise
- events_to_context[e.event_id].rejected = RejectedReason.AUTH_ERROR
-
- if auth_events or state:
- await self.persist_events_and_notify(
- room_id,
- [
- (e, events_to_context[e.event_id])
- for e in itertools.chain(auth_events, state)
- ],
+ # filter out any events we have already seen
+ seen_remotes = await self._store.have_seen_events(room_id, event_map.keys())
+ for s in seen_remotes:
+ event_map.pop(s, None)
+
+ # persist the auth chain and state events.
+ #
+ # any invalid events here will be marked as rejected, and we'll carry on.
+ #
+ # any events whose auth events are missing (ie, not in the send_join response,
+ # and not already in our db) will just be ignored. This is correct behaviour,
+ # because the reason that auth_events are missing might be due to us being
+ # unable to validate their signatures. The fact that we can't validate their
+ # signatures right now doesn't mean that we will *never* be able to, so it
+ # is premature to reject them.
+ #
+ await self._auth_and_persist_outliers(room_id, event_map.values())
+
+ # and now persist the join event itself.
+ logger.info("Peristing join-via-remote %s", event)
+ with nested_logging_context(suffix=event.event_id):
+ context = await self._state_handler.compute_event_context(
+ event, old_state=state
)
- new_event_context = await self._state_handler.compute_event_context(
- event, old_state=state
- )
+ context = await self._check_event_auth(origin, event, context)
+ if context.rejected:
+ raise SynapseError(400, "Join event was rejected")
- return await self.persist_events_and_notify(
- room_id, [(event, new_event_context)]
- )
+ return await self.persist_events_and_notify(room_id, [(event, context)])
@log_function
async def backfill(
- self, dest: str, room_id: str, limit: int, extremities: Iterable[str]
+ self, dest: str, room_id: str, limit: int, extremities: Collection[str]
) -> None:
"""Trigger a backfill request to `dest` for the given `room_id`
@@ -991,9 +963,15 @@ class FederationEventHandler:
) -> None:
"""Called when we have a new non-outlier event.
- This is called when we have a new event to add to the room DAG - either directly
- via a /send request, retrieved via get_missing_events after a /send request, or
- backfilled after a client request.
+ This is called when we have a new event to add to the room DAG. This can be
+ due to:
+ * events received directly via a /send request
+ * events retrieved via get_missing_events after a /send request
+ * events backfilled after a client request.
+
+ It's not currently used for events received from incoming send_{join,knock,leave}
+ requests (which go via on_send_membership_event), nor for joins created by a
+ remote join dance (which go via process_remote_join).
We need to do auth checks and put it through the StateHandler.
@@ -1029,13 +1007,21 @@ class FederationEventHandler:
logger.exception("Unexpected AuthError from _check_event_auth")
raise FederationError("ERROR", e.code, e.msg, affected=event.event_id)
+ if not backfilled and not context.rejected:
+ # For new (non-backfilled and non-outlier) events we check if the event
+ # passes auth based on the current state. If it doesn't then we
+ # "soft-fail" the event.
+ await self._check_for_soft_fail(event, state, origin=origin)
+
await self._run_push_actions_and_persist_event(event, context, backfilled)
await self._handle_marker_event(origin, event)
- if backfilled:
+ if backfilled or context.rejected:
return
+ await self._maybe_kick_guest_users(event)
+
# For encrypted messages we check that we know about the sending device,
# if we don't then we mark the device cache for that user as stale.
if event.type == EventTypes.Encrypted:
@@ -1334,14 +1320,14 @@ class FederationEventHandler:
for auth_event_id in event.auth_event_ids():
ae = persisted_events.get(auth_event_id)
if not ae:
+ # the fact we can't find the auth event doesn't mean it doesn't
+ # exist, which means it is premature to reject `event`. Instead we
+ # just ignore it for now.
logger.warning(
- "Event %s relies on auth_event %s, which could not be found.",
+ "Dropping event %s, which relies on auth_event %s, which could not be found",
event,
auth_event_id,
)
- # the fact we can't find the auth event doesn't mean it doesn't
- # exist, which means it is premature to reject `event`. Instead we
- # just ignore it for now.
return None
auth.append(ae)
@@ -1471,10 +1457,6 @@ class FederationEventHandler:
except AuthError as e:
logger.warning("Failed auth resolution for %r because %s", event, e)
context.rejected = RejectedReason.AUTH_ERROR
- return context
-
- await self._check_for_soft_fail(event, state, backfilled, origin=origin)
- await self._maybe_kick_guest_users(event)
return context
@@ -1494,7 +1476,6 @@ class FederationEventHandler:
self,
event: EventBase,
state: Optional[Iterable[EventBase]],
- backfilled: bool,
origin: str,
) -> None:
"""Checks if we should soft fail the event; if so, marks the event as
@@ -1503,15 +1484,8 @@ class FederationEventHandler:
Args:
event
state: The state at the event if we don't have all the event's prev events
- backfilled: Whether the event is from backfill
origin: The host the event originates from.
"""
- # For new (non-backfilled and non-outlier) events we check if the event
- # passes auth based on the current state. If it doesn't then we
- # "soft-fail" the event.
- if backfilled or event.internal_metadata.is_outlier():
- return
-
extrem_ids_list = await self._store.get_latest_event_ids_in_room(event.room_id)
extrem_ids = set(extrem_ids_list)
prev_event_ids = set(event.prev_event_ids())
@@ -1693,7 +1667,7 @@ class FederationEventHandler:
event: the event whose auth_events we want
Returns:
- all of the events in `event.auth_events`, after deduplication
+ all of the events listed in `event.auth_events_ids`, after deduplication
Raises:
AuthError if we were unable to fetch the auth_events for any reason.
@@ -1966,7 +1940,7 @@ class FederationEventHandler:
event_pos = PersistedEventPosition(
self._instance_name, event.internal_metadata.stream_ordering
)
- self._notifier.on_new_room_event(
+ await self._notifier.on_new_room_event(
event, event_pos, max_stream_token, extra_users=extra_users
)
diff --git a/synapse/handlers/identity.py b/synapse/handlers/identity.py
index 7ef8698a5e..6a315117ba 100644
--- a/synapse/handlers/identity.py
+++ b/synapse/handlers/identity.py
@@ -879,6 +879,8 @@ class IdentityHandler:
}
if room_type is not None:
+ invite_config["room_type"] = room_type
+ # TODO The unstable field is deprecated and should be removed in the future.
invite_config["org.matrix.msc3288.room_type"] = room_type
# If a custom web client location is available, include it in the request.
diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py
index 2f4b458d45..916ba0662d 100644
--- a/synapse/handlers/message.py
+++ b/synapse/handlers/message.py
@@ -1328,6 +1328,8 @@ class EventCreationHandler:
# user is actually admin or not).
is_admin_redaction = False
if event.type == EventTypes.Redaction:
+ assert event.redacts is not None
+
original_event = await self.store.get_event(
event.redacts,
redact_behaviour=EventRedactBehaviour.AS_IS,
@@ -1423,6 +1425,8 @@ class EventCreationHandler:
)
if event.type == EventTypes.Redaction:
+ assert event.redacts is not None
+
original_event = await self.store.get_event(
event.redacts,
redact_behaviour=EventRedactBehaviour.AS_IS,
@@ -1510,11 +1514,14 @@ class EventCreationHandler:
next_batch_id = event.content.get(
EventContentFields.MSC2716_NEXT_BATCH_ID
)
- conflicting_insertion_event_id = (
- await self.store.get_insertion_event_id_by_batch_id(
- event.room_id, next_batch_id
+
+ conflicting_insertion_event_id = None
+ if next_batch_id:
+ conflicting_insertion_event_id = (
+ await self.store.get_insertion_event_id_by_batch_id(
+ event.room_id, next_batch_id
+ )
)
- )
if conflicting_insertion_event_id is not None:
# The current insertion event that we're processing is invalid
# because an insertion event already exists in the room with the
@@ -1547,13 +1554,16 @@ class EventCreationHandler:
# If there's an expiry timestamp on the event, schedule its expiry.
self._message_handler.maybe_schedule_expiry(event)
- def _notify() -> None:
+ async def _notify() -> None:
try:
- self.notifier.on_new_room_event(
+ await self.notifier.on_new_room_event(
event, event_pos, max_stream_token, extra_users=extra_users
)
except Exception:
- logger.exception("Error notifying about new room event")
+ logger.exception(
+ "Error notifying about new room event %s",
+ event.event_id,
+ )
run_in_background(_notify)
diff --git a/synapse/handlers/pagination.py b/synapse/handlers/pagination.py
index 60ff896386..abfe7be0e3 100644
--- a/synapse/handlers/pagination.py
+++ b/synapse/handlers/pagination.py
@@ -438,7 +438,7 @@ class PaginationHandler:
}
state = None
- if event_filter and event_filter.lazy_load_members() and len(events) > 0:
+ if event_filter and event_filter.lazy_load_members and len(events) > 0:
# TODO: remove redundant members
# FIXME: we also care about invite targets etc.
diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py
index b5968e047b..3df872c578 100644
--- a/synapse/handlers/presence.py
+++ b/synapse/handlers/presence.py
@@ -1483,11 +1483,37 @@ def should_notify(old_state: UserPresenceState, new_state: UserPresenceState) ->
def format_user_presence_state(
state: UserPresenceState, now: int, include_user_id: bool = True
) -> JsonDict:
- """Convert UserPresenceState to a format that can be sent down to clients
+ """Convert UserPresenceState to a JSON format that can be sent down to clients
and to other servers.
- The "user_id" is optional so that this function can be used to format presence
- updates for client /sync responses and for federation /send requests.
+ Args:
+ state: The user presence state to format.
+ now: The current timestamp since the epoch in ms.
+ include_user_id: Whether to include `user_id` in the returned dictionary.
+ As this function can be used both to format presence updates for client /sync
+ responses and for federation /send requests, only the latter needs the include
+ the `user_id` field.
+
+ Returns:
+ A JSON dictionary with the following keys:
+ * presence: The presence state as a str.
+ * user_id: Optional. Included if `include_user_id` is truthy. The canonical
+ Matrix ID of the user.
+ * last_active_ago: Optional. Included if `last_active_ts` is set on `state`.
+ The timestamp that the user was last active.
+ * status_msg: Optional. Included if `status_msg` is set on `state`. The user's
+ status.
+ * currently_active: Optional. Included only if `state.state` is "online".
+
+ Example:
+
+ {
+ "presence": "online",
+ "user_id": "@alice:example.com",
+ "last_active_ago": 16783813918,
+ "status_msg": "Hello world!",
+ "currently_active": True
+ }
"""
content: JsonDict = {"presence": state.state}
if include_user_id:
diff --git a/synapse/handlers/profile.py b/synapse/handlers/profile.py
index e6c3cf585b..6b5a6ded8b 100644
--- a/synapse/handlers/profile.py
+++ b/synapse/handlers/profile.py
@@ -456,7 +456,11 @@ class ProfileHandler:
continue
new_name = profile.get("displayname")
+ if not isinstance(new_name, str):
+ new_name = None
new_avatar = profile.get("avatar_url")
+ if not isinstance(new_avatar, str):
+ new_avatar = None
# We always hit update to update the last_check timestamp
await self.store.update_remote_profile_cache(user_id, new_name, new_avatar)
diff --git a/synapse/handlers/receipts.py b/synapse/handlers/receipts.py
index 374e961e3b..4911a11535 100644
--- a/synapse/handlers/receipts.py
+++ b/synapse/handlers/receipts.py
@@ -241,12 +241,18 @@ class ReceiptEventSource(EventSource[int, JsonDict]):
async def get_new_events_as(
self, from_key: int, service: ApplicationService
) -> Tuple[List[JsonDict], int]:
- """Returns a set of new receipt events that an appservice
+ """Returns a set of new read receipt events that an appservice
may be interested in.
Args:
from_key: the stream position at which events should be fetched from
service: The appservice which may be interested
+
+ Returns:
+ A two-tuple containing the following:
+ * A list of json dictionaries derived from read receipts that the
+ appservice may be interested in.
+ * The current read receipt stream token.
"""
from_key = int(from_key)
to_key = self.get_current_key()
diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index 6f39e9446f..969eb3b9b0 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -525,7 +525,7 @@ class RoomCreationHandler:
):
await self.room_member_handler.update_membership(
requester,
- UserID.from_string(old_event["state_key"]),
+ UserID.from_string(old_event.state_key),
new_room_id,
"ban",
ratelimit=False,
@@ -773,6 +773,15 @@ class RoomCreationHandler:
if not allowed_by_third_party_rules:
raise SynapseError(403, "Room visibility value not allowed.")
+ if is_public:
+ if not self.config.roomdirectory.is_publishing_room_allowed(
+ user_id, room_id, room_alias
+ ):
+ # Let's just return a generic message, as there may be all sorts of
+ # reasons why we said no. TODO: Allow configurable error messages
+ # per alias creation rule?
+ raise SynapseError(403, "Not allowed to publish room")
+
directory_handler = self.hs.get_directory_handler()
if room_alias:
await directory_handler.create_association(
@@ -783,15 +792,6 @@ class RoomCreationHandler:
check_membership=False,
)
- if is_public:
- if not self.config.roomdirectory.is_publishing_room_allowed(
- user_id, room_id, room_alias
- ):
- # Lets just return a generic message, as there may be all sorts of
- # reasons why we said no. TODO: Allow configurable error messages
- # per alias creation rule?
- raise SynapseError(403, "Not allowed to publish room")
-
preset_config = config.get(
"preset",
RoomCreationPreset.PRIVATE_CHAT
@@ -1173,7 +1173,7 @@ class RoomContextHandler:
else:
last_event_id = event_id
- if event_filter and event_filter.lazy_load_members():
+ if event_filter and event_filter.lazy_load_members:
state_filter = StateFilter.from_lazy_load_member_list(
ev.sender
for ev in itertools.chain(
diff --git a/synapse/handlers/room_batch.py b/synapse/handlers/room_batch.py
index 17ee1d9596..2e31532389 100644
--- a/synapse/handlers/room_batch.py
+++ b/synapse/handlers/room_batch.py
@@ -351,7 +351,7 @@ class RoomBatchHandler:
for (event, context) in reversed(events_to_persist):
await self.event_creation_handler.handle_new_client_event(
await self.create_requester_for_user_id_from_app_service(
- event["sender"], app_service_requester.app_service
+ event.sender, app_service_requester.app_service
),
event=event,
context=context,
diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py
index 3ff82ab229..343e3c6b7b 100644
--- a/synapse/handlers/room_member.py
+++ b/synapse/handlers/room_member.py
@@ -1669,7 +1669,9 @@ class RoomMemberMasterHandler(RoomMemberHandler):
#
# the prev_events consist solely of the previous membership event.
prev_event_ids = [previous_membership_event.event_id]
- auth_event_ids = previous_membership_event.auth_event_ids() + prev_event_ids
+ auth_event_ids = (
+ list(previous_membership_event.auth_event_ids()) + prev_event_ids
+ )
event, context = await self.event_creation_handler.create_event(
requester,
diff --git a/synapse/handlers/search.py b/synapse/handlers/search.py
index a3ffa26be8..6e4dff8056 100644
--- a/synapse/handlers/search.py
+++ b/synapse/handlers/search.py
@@ -249,7 +249,7 @@ class SearchHandler:
)
events.sort(key=lambda e: -rank_map[e.event_id])
- allowed_events = events[: search_filter.limit()]
+ allowed_events = events[: search_filter.limit]
for e in allowed_events:
rm = room_groups.setdefault(
@@ -271,13 +271,13 @@ class SearchHandler:
# We keep looping and we keep filtering until we reach the limit
# or we run out of things.
# But only go around 5 times since otherwise synapse will be sad.
- while len(room_events) < search_filter.limit() and i < 5:
+ while len(room_events) < search_filter.limit and i < 5:
i += 1
search_result = await self.store.search_rooms(
room_ids,
search_term,
keys,
- search_filter.limit() * 2,
+ search_filter.limit * 2,
pagination_token=pagination_token,
)
@@ -299,9 +299,9 @@ class SearchHandler:
)
room_events.extend(events)
- room_events = room_events[: search_filter.limit()]
+ room_events = room_events[: search_filter.limit]
- if len(results) < search_filter.limit() * 2:
+ if len(results) < search_filter.limit * 2:
pagination_token = None
break
else:
@@ -311,7 +311,7 @@ class SearchHandler:
group = room_groups.setdefault(event.room_id, {"results": []})
group["results"].append(event.event_id)
- if room_events and len(room_events) >= search_filter.limit():
+ if room_events and len(room_events) >= search_filter.limit:
last_event_id = room_events[-1].event_id
pagination_token = results_map[last_event_id]["pagination_token"]
diff --git a/synapse/handlers/typing.py b/synapse/handlers/typing.py
index d10e9b8ec4..c411d69924 100644
--- a/synapse/handlers/typing.py
+++ b/synapse/handlers/typing.py
@@ -465,17 +465,23 @@ class TypingNotificationEventSource(EventSource[int, JsonDict]):
may be interested in.
Args:
- from_key: the stream position at which events should be fetched from
- service: The appservice which may be interested
+ from_key: the stream position at which events should be fetched from.
+ service: The appservice which may be interested.
+
+ Returns:
+ A two-tuple containing the following:
+ * A list of json dictionaries derived from typing events that the
+ appservice may be interested in.
+ * The latest known room serial.
"""
with Measure(self.clock, "typing.get_new_events_as"):
- from_key = int(from_key)
handler = self.get_typing_handler()
events = []
for room_id in handler._room_serials.keys():
if handler._room_serials[room_id] <= from_key:
continue
+
if not await service.matches_user_in_member_list(
room_id, handler.store
):
diff --git a/synapse/handlers/user_directory.py b/synapse/handlers/user_directory.py
index 991fee7e58..a0eb45446f 100644
--- a/synapse/handlers/user_directory.py
+++ b/synapse/handlers/user_directory.py
@@ -373,31 +373,29 @@ class UserDirectoryHandler(StateDeltasHandler):
is_public = await self.store.is_room_world_readable_or_publicly_joinable(
room_id
)
- other_users_in_room = await self.store.get_users_in_room(room_id)
-
if is_public:
await self.store.add_users_in_public_rooms(room_id, (user_id,))
else:
+ users_in_room = await self.store.get_users_in_room(room_id)
+ other_users_in_room = [
+ other
+ for other in users_in_room
+ if other != user_id
+ and (
+ not self.is_mine_id(other)
+ or await self.store.should_include_local_user_in_dir(other)
+ )
+ ]
to_insert = set()
# First, if they're our user then we need to update for every user
if self.is_mine_id(user_id):
- if await self.store.should_include_local_user_in_dir(user_id):
- for other_user_id in other_users_in_room:
- if user_id == other_user_id:
- continue
-
- to_insert.add((user_id, other_user_id))
+ for other_user_id in other_users_in_room:
+ to_insert.add((user_id, other_user_id))
# Next we need to update for every local user in the room
for other_user_id in other_users_in_room:
- if user_id == other_user_id:
- continue
-
- include_other_user = self.is_mine_id(
- other_user_id
- ) and await self.store.should_include_local_user_in_dir(other_user_id)
- if include_other_user:
+ if self.is_mine_id(other_user_id):
to_insert.add((other_user_id, user_id))
if to_insert:
|