mautrix-python/mautrix/client/api/rooms.py

790 lines
33 KiB
Python

# Copyright (c) 2022 Tulir Asokan
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import annotations
from typing import Any, Awaitable, Callable
import asyncio
from multidict import CIMultiDict
from mautrix.api import Method, Path
from mautrix.errors import (
MatrixRequestError,
MatrixResponseError,
MNotFound,
MNotJoined,
MRoomInUse,
)
from mautrix.types import (
JSON,
DirectoryPaginationToken,
EventID,
EventType,
Membership,
MemberStateEventContent,
PowerLevelStateEventContent,
RoomAlias,
RoomAliasInfo,
RoomCreatePreset,
RoomCreateStateEventContent,
RoomDirectoryResponse,
RoomDirectoryVisibility,
RoomID,
Serializable,
StateEvent,
StrippedStateEvent,
UserID,
)
from .base import BaseClientAPI
from .events import EventMethods
class RoomMethods(EventMethods, BaseClientAPI):
"""
Methods in section 8 Rooms of the spec. These methods are used for creating rooms, interacting
with the room directory and using the easy room metadata editing endpoints. Generic state
setting and sending events are in the :class:`EventMethods` (section 7) module.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#rooms-1>`__
"""
# region 8.1 Creation
# API reference: https://spec.matrix.org/v1.1/client-server-api/#creation
async def create_room(
self,
alias_localpart: str | None = None,
visibility: RoomDirectoryVisibility = RoomDirectoryVisibility.PRIVATE,
preset: RoomCreatePreset = RoomCreatePreset.PRIVATE,
name: str | None = None,
topic: str | None = None,
is_direct: bool = False,
invitees: list[UserID] | None = None,
initial_state: list[StateEvent | StrippedStateEvent | dict[str, JSON]] | None = None,
room_version: str = None,
creation_content: RoomCreateStateEventContent | dict[str, JSON] | None = None,
power_level_override: PowerLevelStateEventContent | dict[str, JSON] | None = None,
beeper_auto_join_invites: bool = False,
custom_request_fields: dict[str, Any] | None = None,
) -> RoomID:
"""
Create a new room with various configuration options.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3createroom>`__
Args:
alias_localpart: The desired room alias **local part**. If this is included, a room
alias will be created and mapped to the newly created room. The alias will belong
on the same homeserver which created the room. For example, if this was set to
"foo" and sent to the homeserver "example.com" the complete room alias would be
``#foo:example.com``.
visibility: A ``public`` visibility indicates that the room will be shown in the
published room list. A ``private`` visibility will hide the room from the published
room list. Defaults to ``private``. **NB:** This should not be confused with
``join_rules`` which also uses the word ``public``.
preset: Convenience parameter for setting various default state events based on a
preset. Defaults to private (invite-only).
name: If this is included, an ``m.room.name`` event will be sent into the room to
indicate the name of the room. See `Room Events`_ for more information on
``m.room.name``.
topic: If this is included, an ``m.room.topic`` event will be sent into the room to
indicate the topic for the room. See `Room Events`_ for more information on
``m.room.topic``.
is_direct: This flag makes the server set the ``is_direct`` flag on the
`m.room.member`_ events sent to the users in ``invite`` and ``invite_3pid``. See
`Direct Messaging`_ for more information.
invitees: A list of user IDs to invite to the room. This will tell the server to invite
everyone in the list to the newly created room.
initial_state: A list of state events to set in the new room. This allows the user to
override the default state events set in the new room. The expected format of the
state events are an object with type, state_key and content keys set.
Takes precedence over events set by ``is_public``, but gets overriden by ``name``
and ``topic keys``.
room_version: The room version to set for the room. If not provided, the homeserver
will use its configured default.
creation_content: Extra keys, such as ``m.federate``, to be added to the
``m.room.create`` event. The server will ignore ``creator`` and ``room_version``.
Future versions of the specification may allow the server to ignore other keys.
power_level_override: The power level content to override in the default power level
event. This object is applied on top of the generated ``m.room.power_levels`` event
content prior to it being sent to the room. Defaults to overriding nothing.
beeper_auto_join_invites: A Beeper-specific extension which auto-joins all members in
the invite array instead of sending invites.
custom_request_fields: Additional fields to put in the top-level /createRoom content.
Non-custom fields take precedence over fields here.
Returns:
The ID of the newly created room.
Raises:
MatrixResponseError: If the response does not contain a ``room_id`` field.
.. _Room Events: https://spec.matrix.org/v1.1/client-server-api/#room-events
.. _Direct Messaging: https://spec.matrix.org/v1.1/client-server-api/#direct-messaging
.. _m.room.create: https://spec.matrix.org/v1.1/client-server-api/#mroomcreate
.. _m.room.member: https://spec.matrix.org/v1.1/client-server-api/#mroommember
"""
content = {
**(custom_request_fields or {}),
"visibility": visibility.value,
"is_direct": is_direct,
"preset": preset.value,
}
if alias_localpart:
content["room_alias_name"] = alias_localpart
if invitees:
content["invite"] = invitees
if beeper_auto_join_invites:
content["com.beeper.auto_join_invites"] = True
if name:
content["name"] = name
if topic:
content["topic"] = topic
if initial_state:
content["initial_state"] = [
event.serialize() if isinstance(event, Serializable) else event
for event in initial_state
]
if room_version:
content["room_version"] = room_version
if creation_content:
content["creation_content"] = (
creation_content.serialize()
if isinstance(creation_content, Serializable)
else creation_content
)
# Remove keys that the server will ignore anyway
content["creation_content"].pop("room_version", None)
if power_level_override:
content["power_level_content_override"] = (
power_level_override.serialize()
if isinstance(power_level_override, Serializable)
else power_level_override
)
resp = await self.api.request(Method.POST, Path.v3.createRoom, content)
try:
return resp["room_id"]
except KeyError:
raise MatrixResponseError("`room_id` not in response.")
# endregion
# region 8.2 Room aliases
# API reference: https://spec.matrix.org/v1.1/client-server-api/#room-aliases
async def add_room_alias(
self, room_id: RoomID, alias_localpart: str, override: bool = False
) -> None:
"""
Create a new mapping from room alias to room ID.
See also: `API reference <https://matrix.org/docs/spec/client_server/r0.4.0.html#put-matrix-client-r0-directory-room-roomalias>`__
Args:
room_id: The room ID to set.
alias_localpart: The localpart of the room alias to set.
override: Whether or not the alias should be removed and the request retried if the
server responds with HTTP 409 Conflict
"""
room_alias = f"#{alias_localpart}:{self.domain}"
content = {"room_id": room_id}
try:
await self.api.request(Method.PUT, Path.v3.directory.room[room_alias], content)
except MatrixRequestError as e:
if e.http_status == 409:
if override:
await self.remove_room_alias(alias_localpart)
await self.api.request(Method.PUT, Path.v3.directory.room[room_alias], content)
else:
raise MRoomInUse(e.http_status, e.message) from e
else:
raise
async def remove_room_alias(self, alias_localpart: str, raise_404: bool = False) -> None:
"""
Remove a mapping of room alias to room ID.
Servers may choose to implement additional access control checks here, for instance that
room aliases can only be deleted by their creator or server administrator.
See also: `API reference <https://matrix.org/docs/spec/client_server/r0.4.0.html#delete-matrix-client-r0-directory-room-roomalias>`__
Args:
alias_localpart: The room alias to remove.
raise_404: Whether 404 errors should be raised as exceptions instead of ignored.
"""
room_alias = f"#{alias_localpart}:{self.domain}"
try:
await self.api.request(Method.DELETE, Path.v3.directory.room[room_alias])
except MNotFound:
if raise_404:
raise
# else: ignore
async def resolve_room_alias(self, room_alias: RoomAlias) -> RoomAliasInfo:
"""
Request the server to resolve a room alias to a room ID.
The server will use the federation API to resolve the alias if the domain part of the alias
does not correspond to the server's own domain.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3directoryroomroomalias>`__
Args:
room_alias: The room alias.
Returns:
The room ID and a list of servers that are aware of the room.
"""
content = await self.api.request(Method.GET, Path.v3.directory.room[room_alias])
return RoomAliasInfo.deserialize(content)
# endregion
# region 8.4 Room membership
# API reference: https://spec.matrix.org/v1.1/client-server-api/#room-membership
async def get_joined_rooms(self) -> list[RoomID]:
"""Get the list of rooms the user is in."""
content = await self.api.request(Method.GET, Path.v3.joined_rooms)
try:
return content["joined_rooms"]
except KeyError:
raise MatrixResponseError("`joined_rooms` not in response.")
# region 8.4.1 Joining rooms
# API reference: https://spec.matrix.org/v1.1/client-server-api/#joining-rooms
async def join_room_by_id(
self,
room_id: RoomID,
third_party_signed: JSON = None,
extra_content: dict[str, Any] | None = None,
) -> RoomID:
"""
Start participating in a room, i.e. join it by its ID.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3roomsroomidjoin>`__
Args:
room_id: The ID of the room to join.
third_party_signed: A signature of an ``m.third_party_invite`` token to prove that this
user owns a third party identity which has been invited to the room.
extra_content: Additional properties for the join event content.
If a non-empty dict is passed, the join event will be created using
the ``PUT /state/m.room.member/...`` endpoint instead of ``POST /join``.
Returns:
The ID of the room the user joined.
"""
if extra_content:
await self.send_member_event(
room_id, self.mxid, Membership.JOIN, extra_content=extra_content
)
return room_id
content = await self.api.request(
Method.POST,
Path.v3.rooms[room_id].join,
{"third_party_signed": third_party_signed} if third_party_signed is not None else None,
)
try:
return content["room_id"]
except KeyError:
raise MatrixResponseError("`room_id` not in response.")
async def join_room(
self,
room_id_or_alias: RoomID | RoomAlias,
servers: list[str] | None = None,
third_party_signed: JSON = None,
max_retries: int = 4,
) -> RoomID:
"""
Start participating in a room, i.e. join it by its ID or alias, with an optional list of
servers to ask about the ID from.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3joinroomidoralias>`__
Args:
room_id_or_alias: The ID of the room to join, or an alias pointing to the room.
servers: A list of servers to ask about the room ID to join. Not applicable for aliases,
as aliases already contain the necessary server information.
third_party_signed: A signature of an ``m.third_party_invite`` token to prove that this
user owns a third party identity which has been invited to the room.
max_retries: The maximum number of retries. Used to circumvent a Synapse bug with
accepting invites over federation. 0 means only one join call will be attempted.
See: `matrix-org/synapse#2807 <https://github.com/matrix-org/synapse/issues/2807>`__
Returns:
The ID of the room the user joined.
"""
max_retries = max(0, max_retries)
tries = 0
content = (
{"third_party_signed": third_party_signed} if third_party_signed is not None else None
)
query_params = CIMultiDict()
for server_name in servers or []:
query_params.add("server_name", server_name)
while tries <= max_retries:
try:
content = await self.api.request(
Method.POST,
Path.v3.join[room_id_or_alias],
content=content,
query_params=query_params,
)
break
except MatrixRequestError:
tries += 1
if tries <= max_retries:
wait = tries * 10
self.log.exception(
f"Failed to join room {room_id_or_alias}, retrying in {wait} seconds..."
)
await asyncio.sleep(wait)
else:
raise
try:
return content["room_id"]
except KeyError:
raise MatrixResponseError("`room_id` not in response.")
fill_member_event_callback: (
Callable[
[RoomID, UserID, MemberStateEventContent], Awaitable[MemberStateEventContent | None]
]
| None
)
async def fill_member_event(
self, room_id: RoomID, user_id: UserID, content: MemberStateEventContent
) -> MemberStateEventContent | None:
"""
Fill a membership event content that is going to be sent in :meth:`send_member_event`.
This is used to set default fields like the displayname and avatar, which are usually set
by the server in the sugar membership endpoints like /join and /invite, but are not set
automatically when sending member events manually.
This default implementation only calls :attr:`fill_member_event_callback`.
Args:
room_id: The room where the member event is going to be sent.
user_id: The user whose membership is changing.
content: The new member event content.
Returns:
The filled member event content.
"""
if self.fill_member_event_callback is not None:
return await self.fill_member_event_callback(room_id, user_id, content)
return None
async def send_member_event(
self,
room_id: RoomID,
user_id: UserID,
membership: Membership,
extra_content: dict[str, JSON] | None = None,
) -> EventID:
"""
Send a membership event manually.
Args:
room_id: The room to send the event to.
user_id: The user whose membership to change.
membership: The membership status.
extra_content: Additional content to put in the member event.
Returns:
The event ID of the new member event.
"""
content = MemberStateEventContent(membership=membership)
for key, value in extra_content.items():
content[key] = value
content = await self.fill_member_event(room_id, user_id, content) or content
return await self.send_state_event(
room_id, EventType.ROOM_MEMBER, content=content, state_key=user_id, ensure_joined=False
)
async def invite_user(
self,
room_id: RoomID,
user_id: UserID,
reason: str | None = None,
extra_content: dict[str, JSON] | None = None,
) -> None:
"""
Invite a user to participate in a particular room. They do not start participating in the
room until they actually join the room.
Only users currently in the room can invite other users to join that room.
If the user was invited to the room, the homeserver will add a `m.room.member`_ event to
the room.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3roomsroomidinvite>`__
.. _m.room.member: https://spec.matrix.org/v1.1/client-server-api/#mroommember
Args:
room_id: The ID of the room to which to invite the user.
user_id: The fully qualified user ID of the invitee.
reason: The reason the user was invited. This will be supplied as the ``reason`` on
the `m.room.member`_ event.
extra_content: Additional properties for the invite event content.
If a non-empty dict is passed, the invite event will be created using
the ``PUT /state/m.room.member/...`` endpoint instead of ``POST /invite``.
"""
if extra_content:
await self.send_member_event(
room_id, user_id, Membership.INVITE, extra_content=extra_content
)
else:
data = {"user_id": user_id}
if reason:
data["reason"] = reason
await self.api.request(Method.POST, Path.v3.rooms[room_id].invite, content=data)
# endregion
# region 8.4.2 Leaving rooms
# API reference: https://spec.matrix.org/v1.1/client-server-api/#leaving-rooms
async def leave_room(
self,
room_id: RoomID,
reason: str | None = None,
extra_content: dict[str, JSON] | None = None,
raise_not_in_room: bool = False,
) -> None:
"""
Stop participating in a particular room, i.e. leave the room.
If the user was already in the room, they will no longer be able to see new events in the
room. If the room requires an invite to join, they will need to be re-invited before they
can re-join.
If the user was invited to the room, but had not joined, this call serves to reject the
invite.
The user will still be allowed to retrieve history from the room which they were previously
allowed to see.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3roomsroomidleave>`__
Args:
room_id: The ID of the room to leave.
reason: The reason for leaving the room. This will be supplied as the ``reason`` on
the updated `m.room.member`_ event.
extra_content: Additional properties for the leave event content.
If a non-empty dict is passed, the leave event will be created using
the ``PUT /state/m.room.member/...`` endpoint instead of ``POST /leave``.
raise_not_in_room: Should errors about the user not being in the room be raised?
"""
try:
if extra_content:
await self.send_member_event(
room_id, self.mxid, Membership.LEAVE, extra_content=extra_content
)
else:
data = {}
if reason:
data["reason"] = reason
await self.api.request(Method.POST, Path.v3.rooms[room_id].leave, content=data)
except MNotJoined:
if raise_not_in_room:
raise
except MatrixRequestError as e:
# TODO remove this once MSC3848 is released and minimum spec version is bumped
if "not in room" not in e.message or raise_not_in_room:
raise
async def knock_room(
self,
room_id_or_alias: RoomID | RoomAlias,
reason: str | None = None,
servers: list[str] | None = None,
) -> RoomID:
"""
Knock on a room, i.e. request to join it by its ID or alias, with an optional list of
servers to ask about the ID from.
See also: `API reference <https://spec.matrix.org/v1.3/client-server-api/#post_matrixclientv3knockroomidoralias>`__
Args:
room_id_or_alias: The ID of the room to knock on, or an alias pointing to the room.
reason: The reason for knocking on the room. This will be supplied as the ``reason`` on
the updated `m.room.member`_ event.
servers: A list of servers to ask about the room ID to knock. Not applicable for aliases,
as aliases already contain the necessary server information.
Returns:
The ID of the room the user knocked on.
"""
data = {}
if reason:
data["reason"] = reason
query_params = CIMultiDict()
for server_name in servers or []:
query_params.add("server_name", server_name)
content = await self.api.request(
Method.POST,
Path.v3.knock[room_id_or_alias],
content=data,
query_params=query_params,
)
try:
return content["room_id"]
except KeyError:
raise MatrixResponseError("`room_id` not in response.")
async def forget_room(self, room_id: RoomID) -> None:
"""
Stop remembering a particular room, i.e. forget it.
In general, history is a first class citizen in Matrix. After this API is called, however,
a user will no longer be able to retrieve history for this room. If all users on a
homeserver forget a room, the room is eligible for deletion from that homeserver.
If the user is currently joined to the room, they must leave the room before calling this
API.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3roomsroomidforget>`__
Args:
room_id: The ID of the room to forget.
"""
await self.api.request(Method.POST, Path.v3.rooms[room_id].forget)
async def kick_user(
self,
room_id: RoomID,
user_id: UserID,
reason: str = "",
extra_content: dict[str, JSON] | None = None,
) -> None:
"""
Kick a user from the room.
The caller must have the required power level in order to perform this operation.
Kicking a user adjusts the target member's membership state to be ``leave`` with an optional
``reason``. Like with other membership changes, a user can directly adjust the target
member's state by calling :meth:`EventMethods.send_state_event` with
:attr:`EventType.ROOM_MEMBER` as the event type and the ``user_id`` as the state key.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3roomsroomidkick>`__
Args:
room_id: The ID of the room from which the user should be kicked.
user_id: The fully qualified user ID of the user being kicked.
reason: The reason the user has been kicked. This will be supplied as the ``reason`` on
the target's updated `m.room.member`_ event.
extra_content: Additional properties for the kick event content.
If a non-empty dict is passed, the kick event will be created using
the ``PUT /state/m.room.member/...`` endpoint instead of ``POST /kick``.
.. _m.room.member: https://spec.matrix.org/v1.1/client-server-api/#mroommember
"""
if extra_content:
if reason and "reason" not in extra_content:
extra_content["reason"] = reason
await self.send_member_event(
room_id, user_id, Membership.LEAVE, extra_content=extra_content
)
return
await self.api.request(
Method.POST, Path.v3.rooms[room_id].kick, {"user_id": user_id, "reason": reason}
)
# endregion
# region 8.4.2.1 Banning users in a room
# API reference: https://spec.matrix.org/v1.1/client-server-api/#banning-users-in-a-room
async def ban_user(
self,
room_id: RoomID,
user_id: UserID,
reason: str = "",
extra_content: dict[str, JSON] | None = None,
) -> None:
"""
Ban a user in the room. If the user is currently in the room, also kick them. When a user is
banned from a room, they may not join it or be invited to it until they are unbanned. The
caller must have the required power level in order to perform this operation.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3roomsroomidban>`__
Args:
room_id: The ID of the room from which the user should be banned.
user_id: The fully qualified user ID of the user being banned.
reason: The reason the user has been kicked. This will be supplied as the ``reason`` on
the target's updated `m.room.member`_ event.
extra_content: Additional properties for the ban event content.
If a non-empty dict is passed, the ban will be created using
the ``PUT /state/m.room.member/...`` endpoint instead of ``POST /ban``.
.. _m.room.member: https://spec.matrix.org/v1.1/client-server-api/#mroommember
"""
if extra_content:
if reason and "reason" not in extra_content:
extra_content["reason"] = reason
await self.send_member_event(
room_id, user_id, Membership.BAN, extra_content=extra_content
)
return
await self.api.request(
Method.POST, Path.v3.rooms[room_id].ban, {"user_id": user_id, "reason": reason}
)
async def unban_user(
self,
room_id: RoomID,
user_id: UserID,
reason: str = "",
extra_content: dict[str, JSON] | None = None,
) -> None:
"""
Unban a user from the room. This allows them to be invited to the room, and join if they
would otherwise be allowed to join according to its join rules. The caller must have the
required power level in order to perform this operation.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3roomsroomidunban>`__
Args:
room_id: The ID of the room from which the user should be unbanned.
user_id: The fully qualified user ID of the user being banned.
reason: The reason the user has been unbanned. This will be supplied as the ``reason`` on
the target's updated `m.room.member`_ event.
extra_content: Additional properties for the unban (leave) event content.
If a non-empty dict is passed, the unban will be created using
the ``PUT /state/m.room.member/...`` endpoint instead of ``POST /unban``.
"""
if extra_content:
if reason and "reason" not in extra_content:
extra_content["reason"] = reason
await self.send_member_event(
room_id, user_id, Membership.LEAVE, extra_content=extra_content
)
return
await self.api.request(
Method.POST, Path.v3.rooms[room_id].unban, {"user_id": user_id, "reason": reason}
)
# endregion
# endregion
# region 8.5 Listing rooms
# API reference: https://spec.matrix.org/v1.1/client-server-api/#listing-rooms
async def get_room_directory_visibility(self, room_id: RoomID) -> RoomDirectoryVisibility:
"""
Get the visibility of the room on the server's public room directory.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3directorylistroomroomid>`__
Args:
room_id: The ID of the room.
Returns:
The visibility of the room in the directory.
"""
resp = await self.api.request(Method.GET, Path.v3.directory.list.room[room_id])
try:
return RoomDirectoryVisibility(resp["visibility"])
except KeyError:
raise MatrixResponseError("`visibility` not in response.")
except ValueError:
raise MatrixResponseError(
f"Invalid value for `visibility` in response: {resp['visibility']}"
)
async def set_room_directory_visibility(
self, room_id: RoomID, visibility: RoomDirectoryVisibility
) -> None:
"""
Set the visibility of the room in the server's public room directory.
Servers may choose to implement additional access control checks here, for instance that
room visibility can only be changed by the room creator or a server administrator.
Args:
room_id: The ID of the room.
visibility: The new visibility setting for the room.
.. _API reference: https://spec.matrix.org/v1.1/client-server-api/#put_matrixclientv3directorylistroomroomid
"""
await self.api.request(
Method.PUT,
Path.v3.directory.list.room[room_id],
{
"visibility": visibility.value,
},
)
async def get_room_directory(
self,
limit: int | None = None,
server: str | None = None,
since: DirectoryPaginationToken | None = None,
search_query: str | None = None,
include_all_networks: bool | None = None,
third_party_instance_id: str | None = None,
) -> RoomDirectoryResponse:
"""
Get a list of public rooms from the server's room directory.
See also: `API reference <https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3publicrooms>`__
Args:
limit: The maximum number of results to return.
server: The server to fetch the room directory from. Defaults to the user's server.
since: A pagination token from a previous request, allowing clients to get the next (or
previous) batch of rooms. The direction of pagination is specified solely by which
token is supplied, rather than via an explicit flag.
search_query: A string to search for in the room metadata, e.g. name, topic, canonical
alias etc.
include_all_networks: Whether or not to include rooms from all known networks/protocols
from application services on the homeserver. Defaults to false.
third_party_instance_id: The specific third party network/protocol to request from the
homeserver. Can only be used if ``include_all_networks`` is false.
Returns:
The relevant pagination tokens, an estimate of the total number of public rooms and the
paginated chunk of public rooms.
"""
method = (
Method.GET
if (
search_query is None
and include_all_networks is None
and third_party_instance_id is None
)
else Method.POST
)
content = {}
if limit is not None:
content["limit"] = limit
if since is not None:
content["since"] = since
if search_query is not None:
content["filter"] = {"generic_search_term": search_query}
if include_all_networks is not None:
content["include_all_networks"] = include_all_networks
if third_party_instance_id is not None:
content["third_party_instance_id"] = third_party_instance_id
query_params = {"server": server} if server is not None else None
content = await self.api.request(
method, Path.v3.publicRooms, content, query_params=query_params
)
return RoomDirectoryResponse.deserialize(content)
# endregion