diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index e61d049889..11bbcf3e37 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -863,13 +863,6 @@ class RoomCreationHandler:
check_membership=False,
)
- preset_identifier = config.get(
- "preset",
- RoomCreationPreset.PRIVATE_CHAT
- if visibility == "private"
- else RoomCreationPreset.PUBLIC_CHAT,
- )
-
raw_initial_state = config.get("initial_state", [])
initial_state = OrderedDict()
@@ -881,10 +874,17 @@ class RoomCreationHandler:
# override any attempt to set room versions via the creation_content
creation_content["room_version"] = room_version.identifier
+ room_preset_identifier = config.get(
+ "preset",
+ RoomCreationPreset.PRIVATE_CHAT
+ if visibility == "private"
+ else RoomCreationPreset.PUBLIC_CHAT,
+ )
+
last_stream_id = await self._send_events_for_new_room(
requester,
room_id,
- preset_identifier=preset_identifier,
+ room_preset_identifier=room_preset_identifier,
invite_list=invite_list,
initial_state=initial_state,
creation_content=creation_content,
@@ -894,6 +894,7 @@ class RoomCreationHandler:
ratelimit=ratelimit,
)
+ # TODO: These could also be moved into `_send_events_for_new_room`
if "name" in config:
name = config["name"]
(
@@ -1057,32 +1058,35 @@ class RoomCreationHandler:
# Determine the options that this preset defines. This will influence which state events will
# be sent below.
- room_preset_config_options = self._default_presets_dict.get(room_preset_identifier)
- if not room_preset_config_options:
+ if room_preset_identifier not in self._default_presets_dict.keys():
if not self._allow_custom_room_presets:
raise SynapseError(
- 400, f"'{room_preset_identifier}' is not a valid preset", errcode=Codes.BAD_JSON
+ 400,
+ f"'{room_preset_identifier}' is not a valid preset",
+ errcode=Codes.BAD_JSON,
)
# TODO: Ask modules about initial_state and base room preset
+ # Should modules be able to say "empty base room preset"? Aka return None here?
+ # This would just result in an empty dictionary I think...
initial_state += {}
- base_room_preset_identifier = RoomCreationPreset.PUBLIC_CHAT
+ room_preset_identifier = RoomCreationPreset.PUBLIC_CHAT
+
# TODO: Should modules also be able to append to room_creation_content? I would think so, just
# that they can't override room version I guess?
- room_preset_config_options = self._default_presets_dict[base_room_preset_identifier]
-
+ # TODO: If no module recognised this preset ID, then raise an exception.
if False:
raise SynapseError(
- 400, f"'{room_preset_identifier}' is not a valid preset", errcode=Codes.BAD_JSON
+ 400,
+ f"'{room_preset_identifier}' is not a valid preset",
+ errcode=Codes.BAD_JSON,
)
- # The code below will take `initial_state` and `preset_config` as input and
- # figure out which events to send in the appropriate order.
-
- # Send the create and creator membership join events first.
+ # Send the create event and creator membership join first. These are required regardless
+ # of room preset.
creation_content.update({"creator": creator_id})
- last_sent_stream_id = await send(etype=EventTypes.Create, content=creation_content)
+ await send(etype=EventTypes.Create, content=creation_content)
logger.debug("Sending %s in new room", EventTypes.Member)
last_sent_stream_id = await self.room_member_handler.update_membership(
@@ -1096,21 +1100,31 @@ class RoomCreationHandler:
)
state_to_send = self._get_state_events_for_new_room(
+ creator.user,
room_preset_identifier,
initial_state,
+ invite_list,
+ power_level_content_override,
+ room_alias,
)
# Send all state events in succession
for (event_type, state_key), event_content in state_to_send:
- last_sent_stream_id = await send(etype=event_type, state_key=state_key, content=event_content)
+ last_sent_stream_id = await send(
+ etype=event_type, state_key=state_key, content=event_content
+ )
return last_sent_stream_id
def _get_state_events_for_new_room(
self,
+ room_creator_user: UserID,
room_preset_identifier: str,
initial_state: MutableStateMap,
- ) -> List:
+ user_invite_list: List[str],
+ power_level_content_override: Optional[JsonDict] = None,
+ room_alias: Optional[RoomAlias] = None,
+ ) -> Dict[StateKey, JsonDict]:
"""
For a given room preset, return a map of state events that should initially be sent into the room.
@@ -1118,20 +1132,21 @@ class RoomCreationHandler:
room_preset_identifier: The identifier of the room preset.
Returns:
- A list of state events to send into the new room.
+ An ordered dict of state events to send into the new room.
"""
- state_to_send = []
+ # We rely on Python 3.7 mandating that `dict`s are ordered by insertion order.
+ state_to_send = {}
+
+ room_preset_config_options = self._default_presets_dict[room_preset_identifier]
# We treat the power levels override specially as this needs to be one
# of the first events that get sent into a room.
pl_content = initial_state.pop((EventTypes.PowerLevels, ""), None)
if pl_content is not None:
- state_to_send.append(
- ((EventTypes.PowerLevels, ""), pl_content)
- )
+ state_to_send[(EventTypes.PowerLevels, "")] = pl_content
else:
power_level_content: JsonDict = {
- "users": {creator_id: 100},
+ "users": {room_creator_user.to_string(): 100},
"users_default": 0,
"events": {
EventTypes.Name: 50,
@@ -1153,17 +1168,21 @@ class RoomCreationHandler:
}
if room_preset_config_options["original_invitees_have_ops"]:
- for invitee in invite_list:
+ for invitee in user_invite_list:
power_level_content["users"][invitee] = 100
# If the user supplied a preset name e.g. "private_chat",
# we apply that preset
- power_level_content.update(room_preset_config_options["power_level_content_override"])
+ power_level_content.update(
+ room_preset_config_options["power_level_content_override"]
+ )
# If the server config contains default_power_level_content_override,
# and that contains information for this room preset, apply it.
if self._default_power_level_content_override:
- override = self._default_power_level_content_override.get(room_preset_identifier)
+ override = self._default_power_level_content_override.get(
+ room_preset_identifier
+ )
if override is not None:
power_level_content.update(override)
@@ -1172,59 +1191,39 @@ class RoomCreationHandler:
if power_level_content_override:
power_level_content.update(power_level_content_override)
- state_to_send.append(
- ((EventTypes.PowerLevels, ""), power_level_content)
- )
+ state_to_send[(EventTypes.PowerLevels, "")] = power_level_content
- if room_alias and (EventTypes.CanonicalAlias, "") not in initial_state:
- state_to_send.append(
- (
- (EventTypes.CanonicalAlias, ""),
- {"alias": room_alias.to_string()},
- )
- )
+ state_to_send[(EventTypes.CanonicalAlias, "")] = {
+ "alias": room_alias.to_string()
+ }
- if (EventTypes.JoinRules, "") not in initial_state:
- state_to_send.append(
- (
- (EventTypes.JoinRules, ""),
- {"join_rule": room_preset_config_options["join_rules"]},
- )
- )
+ state_to_send[(EventTypes.JoinRules, "")] = {
+ "join_rule": room_preset_config_options["join_rules"]
+ }
- if (EventTypes.RoomHistoryVisibility, "") not in initial_state:
- state_to_send.append(
- (
- (EventTypes.RoomHistoryVisibility, ""),
- {"history_visibility": room_preset_config_options["history_visibility"]},
- )
- )
+ state_to_send[(EventTypes.RoomHistoryVisibility, "")] = {
+ "history_visibility": room_preset_config_options["history_visibility"]
+ }
if room_preset_config_options["guest_can_join"]:
- if (EventTypes.GuestAccess, "") not in initial_state:
- state_to_send.append(
- (
- (EventTypes.GuestAccess, ""),
- {EventContentFields.GUEST_ACCESS: GuestAccess.CAN_JOIN},
- )
- )
+ state_to_send[(EventTypes.GuestAccess, "")] = {
+ EventContentFields.GUEST_ACCESS: GuestAccess.CAN_JOIN
+ }
- for (event_type, state_key), content in initial_state.items():
- state_to_send.append(
- (
- (event_type, state_key),
- content,
- )
- )
+ #
+ for (event_type, state_key), content_dict in initial_state.items():
+ # TODO: Support None meaning delete the state from the dict?
+
+ # If this (state event type, state_key) tuple is already in the list, replace it in-place.
+ state_to_send[(event_type, state_key)] = content_dict
+ # TODO: Why do we want to send this last? It means that custom room presets can't override it
if room_preset_config_options["encrypted"]:
- state_to_send.append(
- (
- (EventTypes.RoomEncryption, ""),
- {"algorithm": RoomEncryptionAlgorithms.DEFAULT},
- )
- )
+ state_to_send[(EventTypes.RoomEncryption, "")] = {
+ "algorithm": RoomEncryptionAlgorithms.DEFAULT
+ }
+ return state_to_send
def _generate_room_id(self) -> str:
"""Generates a random room ID.
diff --git a/synapse/module_api/__init__.py b/synapse/module_api/__init__.py
index a302890db9..3c64d55c6c 100644
--- a/synapse/module_api/__init__.py
+++ b/synapse/module_api/__init__.py
@@ -875,17 +875,20 @@ class ModuleApi:
Added in Synapse v1.XX.Y.
"""
- # We need a unique identifier for each calling module, so that when
- # this method is called again, the previous list of room presets can
- # be identified and replaced.
+ # We need a unique identifier for each calling module, so that if this
+ # method is called multiple times, the previous list of room presets can
+ # be replaced.
# Get the calling module's ...
+ # OK, instead of this nonsense, just have the module register a single
+ # custom_room_preset -> initial_state, base_room_preset mapping. The
+ # module could do this multiple times and override its previous ones.
+ # If other modules use the same then there's not much Synapse can do.
+ # We could log if we're overriding to give sysadmins a heads up.
caller_frame = inspect.stack()[1]
module_filepath = inspect.getmodule(caller_frame[0]).__file__
if module_filepath:
- self._room_creation_handler.custom_room_presets.update(
- {module_filepath: room_preset_names}
- )
+ self._room_creation_handler.custom_room_presets[module_filepath] = room_preset_names
async def complete_sso_login_async(
self,
|