wip: call the public room callback
This commit is contained in:
parent
2436153e8f
commit
5c1e9f24da
|
@ -27,6 +27,7 @@ from synapse.api.constants import (
|
||||||
JoinRules,
|
JoinRules,
|
||||||
PublicRoomsFilterFields,
|
PublicRoomsFilterFields,
|
||||||
)
|
)
|
||||||
|
from synapse.types import Requester
|
||||||
from synapse.api.errors import (
|
from synapse.api.errors import (
|
||||||
Codes,
|
Codes,
|
||||||
HttpResponseException,
|
HttpResponseException,
|
||||||
|
@ -60,6 +61,7 @@ class RoomListHandler:
|
||||||
self.remote_response_cache: ResponseCache[
|
self.remote_response_cache: ResponseCache[
|
||||||
Tuple[str, Optional[int], Optional[str], bool, Optional[str]]
|
Tuple[str, Optional[int], Optional[str], bool, Optional[str]]
|
||||||
] = ResponseCache(hs.get_clock(), "remote_room_list", timeout_ms=30 * 1000)
|
] = ResponseCache(hs.get_clock(), "remote_room_list", timeout_ms=30 * 1000)
|
||||||
|
self._module_api_callbacks = hs.get_module_api_callbacks().public_rooms
|
||||||
|
|
||||||
async def get_local_public_room_list(
|
async def get_local_public_room_list(
|
||||||
self,
|
self,
|
||||||
|
@ -67,7 +69,8 @@ class RoomListHandler:
|
||||||
since_token: Optional[str] = None,
|
since_token: Optional[str] = None,
|
||||||
search_filter: Optional[dict] = None,
|
search_filter: Optional[dict] = None,
|
||||||
network_tuple: Optional[ThirdPartyInstanceID] = EMPTY_THIRD_PARTY_ID,
|
network_tuple: Optional[ThirdPartyInstanceID] = EMPTY_THIRD_PARTY_ID,
|
||||||
from_federation: bool = False,
|
from_client_mxid: Optional[str] = None,
|
||||||
|
from_remote_server_name: Optional[str] = None,
|
||||||
) -> JsonDict:
|
) -> JsonDict:
|
||||||
"""Generate a local public room list.
|
"""Generate a local public room list.
|
||||||
|
|
||||||
|
@ -75,14 +78,20 @@ class RoomListHandler:
|
||||||
party network. A client can ask for a specific list or to return all.
|
party network. A client can ask for a specific list or to return all.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
limit
|
limit: The maximum number of rooms to return, or None to return all rooms.
|
||||||
since_token
|
since_token: A pagination token, or None to return the head of the public
|
||||||
search_filter
|
rooms list.
|
||||||
|
search_filter: An optional dictionary with the following keys:
|
||||||
|
* generic_search_term: A string to search for in room ...
|
||||||
|
* room_types: A list to filter returned rooms by their type. If None or
|
||||||
|
an empty list is passed, rooms will not be filtered by type.
|
||||||
network_tuple: Which public list to use.
|
network_tuple: Which public list to use.
|
||||||
This can be (None, None) to indicate the main list, or a particular
|
This can be (None, None) to indicate the main list, or a particular
|
||||||
appservice and network id to use an appservice specific one.
|
appservice and network id to use an appservice specific one.
|
||||||
Setting to None returns all public rooms across all lists.
|
Setting to None returns all public rooms across all lists.
|
||||||
from_federation: true iff the request comes from the federation API
|
from_client_mxid: A user's MXID if this request came from a registered user.
|
||||||
|
from_remote_server_name: A remote homeserver's server name, if this
|
||||||
|
request came from the federation API.
|
||||||
"""
|
"""
|
||||||
if not self.enable_room_list_search:
|
if not self.enable_room_list_search:
|
||||||
return {"chunk": [], "total_room_count_estimate": 0}
|
return {"chunk": [], "total_room_count_estimate": 0}
|
||||||
|
@ -105,7 +114,8 @@ class RoomListHandler:
|
||||||
since_token,
|
since_token,
|
||||||
search_filter,
|
search_filter,
|
||||||
network_tuple=network_tuple,
|
network_tuple=network_tuple,
|
||||||
from_federation=from_federation,
|
from_client_mxid=from_client_mxid,
|
||||||
|
from_remote_server_name=from_remote_server_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
key = (limit, since_token, network_tuple)
|
key = (limit, since_token, network_tuple)
|
||||||
|
@ -115,7 +125,8 @@ class RoomListHandler:
|
||||||
limit,
|
limit,
|
||||||
since_token,
|
since_token,
|
||||||
network_tuple=network_tuple,
|
network_tuple=network_tuple,
|
||||||
from_federation=from_federation,
|
from_client_mxid=from_client_mxid,
|
||||||
|
from_remote_server_name=from_remote_server_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _get_public_room_list(
|
async def _get_public_room_list(
|
||||||
|
@ -124,7 +135,8 @@ class RoomListHandler:
|
||||||
since_token: Optional[str] = None,
|
since_token: Optional[str] = None,
|
||||||
search_filter: Optional[dict] = None,
|
search_filter: Optional[dict] = None,
|
||||||
network_tuple: Optional[ThirdPartyInstanceID] = EMPTY_THIRD_PARTY_ID,
|
network_tuple: Optional[ThirdPartyInstanceID] = EMPTY_THIRD_PARTY_ID,
|
||||||
from_federation: bool = False,
|
from_client_mxid: Optional[str] = None,
|
||||||
|
from_remote_server_name: Optional[str] = None,
|
||||||
) -> JsonDict:
|
) -> JsonDict:
|
||||||
"""Generate a public room list.
|
"""Generate a public room list.
|
||||||
Args:
|
Args:
|
||||||
|
@ -135,8 +147,9 @@ class RoomListHandler:
|
||||||
This can be (None, None) to indicate the main list, or a particular
|
This can be (None, None) to indicate the main list, or a particular
|
||||||
appservice and network id to use an appservice specific one.
|
appservice and network id to use an appservice specific one.
|
||||||
Setting to None returns all public rooms across all lists.
|
Setting to None returns all public rooms across all lists.
|
||||||
from_federation: Whether this request originated from a
|
from_client_mxid: A user's MXID if this request came from a registered user.
|
||||||
federating server or a client. Used for room filtering.
|
from_remote_server_name: A remote homeserver's server name, if this
|
||||||
|
request came from the federation API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Pagination tokens work by storing the room ID sent in the last batch,
|
# Pagination tokens work by storing the room ID sent in the last batch,
|
||||||
|
@ -145,50 +158,38 @@ class RoomListHandler:
|
||||||
|
|
||||||
if since_token:
|
if since_token:
|
||||||
batch_token = RoomListNextBatch.from_token(since_token)
|
batch_token = RoomListNextBatch.from_token(since_token)
|
||||||
|
|
||||||
bounds: Optional[Tuple[int, str]] = (
|
|
||||||
batch_token.last_joined_members,
|
|
||||||
batch_token.last_room_id,
|
|
||||||
)
|
|
||||||
forwards = batch_token.direction_is_forward
|
forwards = batch_token.direction_is_forward
|
||||||
has_batch_token = True
|
|
||||||
else:
|
else:
|
||||||
bounds = None
|
batch_token = None
|
||||||
|
|
||||||
forwards = True
|
forwards = True
|
||||||
has_batch_token = False
|
|
||||||
|
|
||||||
# we request one more than wanted to see if there are more pages to come
|
# we request one more than wanted to see if there are more pages to come
|
||||||
probing_limit = limit + 1 if limit is not None else None
|
probing_limit = limit + 1 if limit is not None else None
|
||||||
|
|
||||||
results = await self.store.get_largest_public_rooms(
|
public_rooms = await self.store.get_largest_public_rooms(
|
||||||
network_tuple,
|
network_tuple,
|
||||||
search_filter,
|
search_filter,
|
||||||
probing_limit,
|
probing_limit,
|
||||||
bounds=bounds,
|
bounds=(
|
||||||
|
[batch_token.last_joined_members, batch_token.last_room_id]
|
||||||
|
if batch_token else None
|
||||||
|
),
|
||||||
forwards=forwards,
|
forwards=forwards,
|
||||||
ignore_non_federatable=from_federation,
|
ignore_non_federatable=bool(from_remote_server_name),
|
||||||
)
|
)
|
||||||
|
|
||||||
def build_room_entry(room: JsonDict) -> JsonDict:
|
for fetch_public_rooms in self._module_api_callbacks.fetch_public_rooms_callbacks:
|
||||||
entry = {
|
# Ask each module for a list of public rooms given the last_joined_members
|
||||||
"room_id": room["room_id"],
|
# value from the since token and the probing limit.
|
||||||
"name": room["name"],
|
module_public_rooms = await fetch_public_rooms(
|
||||||
"topic": room["topic"],
|
limit=probing_limit,
|
||||||
"canonical_alias": room["canonical_alias"],
|
max_member_count=(
|
||||||
"num_joined_members": room["joined_members"],
|
batch_token.last_joined_members
|
||||||
"avatar_url": room["avatar"],
|
if batch_token else None
|
||||||
"world_readable": room["history_visibility"]
|
),
|
||||||
== HistoryVisibility.WORLD_READABLE,
|
)
|
||||||
"guest_can_join": room["guest_access"] == "can_join",
|
|
||||||
"join_rule": room["join_rules"],
|
|
||||||
"room_type": room["room_type"],
|
|
||||||
}
|
|
||||||
|
|
||||||
# Filter out Nones – rather omit the field altogether
|
# Insert the module's reported public rooms into the list
|
||||||
return {k: v for k, v in entry.items() if v is not None}
|
|
||||||
|
|
||||||
results = [build_room_entry(r) for r in results]
|
|
||||||
|
|
||||||
response: JsonDict = {}
|
response: JsonDict = {}
|
||||||
num_results = len(results)
|
num_results = len(results)
|
||||||
|
@ -208,7 +209,7 @@ class RoomListHandler:
|
||||||
initial_entry = results[0]
|
initial_entry = results[0]
|
||||||
|
|
||||||
if forwards:
|
if forwards:
|
||||||
if has_batch_token:
|
if batch_token is not None:
|
||||||
# If there was a token given then we assume that there
|
# If there was a token given then we assume that there
|
||||||
# must be previous results.
|
# must be previous results.
|
||||||
response["prev_batch"] = RoomListNextBatch(
|
response["prev_batch"] = RoomListNextBatch(
|
||||||
|
@ -224,7 +225,7 @@ class RoomListHandler:
|
||||||
direction_is_forward=True,
|
direction_is_forward=True,
|
||||||
).to_token()
|
).to_token()
|
||||||
else:
|
else:
|
||||||
if has_batch_token:
|
if batch_token is not None:
|
||||||
response["next_batch"] = RoomListNextBatch(
|
response["next_batch"] = RoomListNextBatch(
|
||||||
last_joined_members=final_entry["num_joined_members"],
|
last_joined_members=final_entry["num_joined_members"],
|
||||||
last_room_id=final_entry["room_id"],
|
last_room_id=final_entry["room_id"],
|
||||||
|
@ -242,7 +243,7 @@ class RoomListHandler:
|
||||||
|
|
||||||
response["total_room_count_estimate"] = await self.store.count_public_rooms(
|
response["total_room_count_estimate"] = await self.store.count_public_rooms(
|
||||||
network_tuple,
|
network_tuple,
|
||||||
ignore_non_federatable=from_federation,
|
ignore_non_federatable=bool(from_remote_server_name),
|
||||||
search_filter=search_filter,
|
search_filter=search_filter,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(auto_attribs=True)
|
@attr.s(auto_attribs=True)
|
||||||
class PublicRoomChunk:
|
class PublicRoom:
|
||||||
room_id: str
|
room_id: str
|
||||||
name: str
|
name: str
|
||||||
topic: str
|
topic: str
|
||||||
|
@ -36,8 +36,8 @@ class PublicRoomChunk:
|
||||||
|
|
||||||
# Types for callbacks to be registered via the module api
|
# Types for callbacks to be registered via the module api
|
||||||
FETCH_PUBLIC_ROOMS_CALLBACK = Callable[
|
FETCH_PUBLIC_ROOMS_CALLBACK = Callable[
|
||||||
[int, Optional[int], Optional[dict], Optional[str], Optional[str]],
|
[int, Optional[Tuple[int, bool]], Optional[dict], Optional[str], Optional[str]],
|
||||||
Awaitable[Tuple[Iterable[PublicRoomChunk], bool]],
|
Awaitable[Tuple[Iterable[PublicRoom], bool]],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -476,8 +476,9 @@ class PublicRoomListRestServlet(RestServlet):
|
||||||
async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
|
async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
|
||||||
server = parse_string(request, "server")
|
server = parse_string(request, "server")
|
||||||
|
|
||||||
|
requester: Optional[Requester] = None
|
||||||
try:
|
try:
|
||||||
await self.auth.get_user_by_req(request, allow_guest=True)
|
requester = await self.auth.get_user_by_req(request, allow_guest=True)
|
||||||
except InvalidClientCredentialsError as e:
|
except InvalidClientCredentialsError as e:
|
||||||
# Option to allow servers to require auth when accessing
|
# Option to allow servers to require auth when accessing
|
||||||
# /publicRooms via CS API. This is especially helpful in private
|
# /publicRooms via CS API. This is especially helpful in private
|
||||||
|
@ -516,8 +517,15 @@ class PublicRoomListRestServlet(RestServlet):
|
||||||
server, limit=limit, since_token=since_token
|
server, limit=limit, since_token=since_token
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
# If a user we know made this request, pass that information to the
|
||||||
|
# public rooms list handler.
|
||||||
|
if requester is None:
|
||||||
|
from_client_mxid = None
|
||||||
|
else:
|
||||||
|
from_client_mxid = requester.user.to_string()
|
||||||
|
|
||||||
data = await handler.get_local_public_room_list(
|
data = await handler.get_local_public_room_list(
|
||||||
limit=limit, since_token=since_token
|
limit=limit, since_token=since_token, from_client_mxid=from_client_mxid
|
||||||
)
|
)
|
||||||
|
|
||||||
return 200, data
|
return 200, data
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
import logging
|
import logging
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from synapse.api.constants import HistoryVisibility
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
AbstractSet,
|
AbstractSet,
|
||||||
|
@ -518,7 +519,26 @@ class RoomWorkerStore(CacheInvalidationWorkerStore):
|
||||||
ret_val = await self.db_pool.runInteraction(
|
ret_val = await self.db_pool.runInteraction(
|
||||||
"get_largest_public_rooms", _get_largest_public_rooms_txn
|
"get_largest_public_rooms", _get_largest_public_rooms_txn
|
||||||
)
|
)
|
||||||
return ret_val
|
|
||||||
|
def build_room_entry(room: JsonDict) -> JsonDict:
|
||||||
|
entry = {
|
||||||
|
"room_id": room["room_id"],
|
||||||
|
"name": room["name"],
|
||||||
|
"topic": room["topic"],
|
||||||
|
"canonical_alias": room["canonical_alias"],
|
||||||
|
"num_joined_members": room["joined_members"],
|
||||||
|
"avatar_url": room["avatar"],
|
||||||
|
"world_readable": room["history_visibility"]
|
||||||
|
== HistoryVisibility.WORLD_READABLE,
|
||||||
|
"guest_can_join": room["guest_access"] == "can_join",
|
||||||
|
"join_rule": room["join_rules"],
|
||||||
|
"room_type": room["room_type"],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Filter out Nones – rather omit the field altogether
|
||||||
|
return {k: v for k, v in entry.items() if v is not None}
|
||||||
|
|
||||||
|
return [build_room_entry(r) for r in ret_val]
|
||||||
|
|
||||||
@cached(max_entries=10000)
|
@cached(max_entries=10000)
|
||||||
async def is_room_blocked(self, room_id: str) -> Optional[bool]:
|
async def is_room_blocked(self, room_id: str) -> Optional[bool]:
|
||||||
|
|
|
@ -936,6 +936,19 @@ class UserInfo:
|
||||||
is_shadow_banned: bool
|
is_shadow_banned: bool
|
||||||
|
|
||||||
|
|
||||||
|
class PublicRoomsChunk:
|
||||||
|
room_id: str
|
||||||
|
name: str
|
||||||
|
topic: str
|
||||||
|
num_joined_members: int
|
||||||
|
canonical_alias: str
|
||||||
|
avatar_url: str
|
||||||
|
world_readable: bool
|
||||||
|
guest_can_join: bool
|
||||||
|
join_rule: str
|
||||||
|
room_type: str
|
||||||
|
|
||||||
|
|
||||||
class UserProfile(TypedDict):
|
class UserProfile(TypedDict):
|
||||||
user_id: str
|
user_id: str
|
||||||
display_name: Optional[str]
|
display_name: Optional[str]
|
||||||
|
|
Loading…
Reference in New Issue