Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 872dbb0

Browse files
David Robertsonsquahtx
andauthored
Correct check_username_for_spam annotations and docs (#12246)
* Formally type the UserProfile in user searches * export UserProfile in synapse.module_api * Update docs Co-authored-by: Sean Quah <[email protected]>
1 parent 12d1f82 commit 872dbb0

File tree

8 files changed

+46
-16
lines changed

8 files changed

+46
-16
lines changed

changelog.d/12246.doc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Correct `check_username_for_spam` annotations and docs.

docs/modules/spam_checker_callbacks.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ any of the subsequent implementations of this callback.
172172
_First introduced in Synapse v1.37.0_
173173

174174
```python
175-
async def check_username_for_spam(user_profile: Dict[str, str]) -> bool
175+
async def check_username_for_spam(user_profile: synapse.module_api.UserProfile) -> bool
176176
```
177177

178178
Called when computing search results in the user directory. The module must return a
@@ -182,9 +182,11 @@ search results; otherwise return `False`.
182182

183183
The profile is represented as a dictionary with the following keys:
184184

185-
* `user_id`: The Matrix ID for this user.
186-
* `display_name`: The user's display name.
187-
* `avatar_url`: The `mxc://` URL to the user's avatar.
185+
* `user_id: str`. The Matrix ID for this user.
186+
* `display_name: Optional[str]`. The user's display name, or `None` if this user
187+
has not set a display name.
188+
* `avatar_url: Optional[str]`. The `mxc://` URL to the user's avatar, or `None`
189+
if this user has not set an avatar.
188190

189191
The module is given a copy of the original dictionary, so modifying it from within the
190192
module cannot modify a user's profile when included in user directory search results.

synapse/events/spamcheck.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
Awaitable,
2222
Callable,
2323
Collection,
24-
Dict,
2524
List,
2625
Optional,
2726
Tuple,
@@ -31,7 +30,7 @@
3130
from synapse.rest.media.v1._base import FileInfo
3231
from synapse.rest.media.v1.media_storage import ReadableFileWrapper
3332
from synapse.spam_checker_api import RegistrationBehaviour
34-
from synapse.types import RoomAlias
33+
from synapse.types import RoomAlias, UserProfile
3534
from synapse.util.async_helpers import maybe_awaitable
3635

3736
if TYPE_CHECKING:
@@ -50,7 +49,7 @@
5049
USER_MAY_CREATE_ROOM_CALLBACK = Callable[[str], Awaitable[bool]]
5150
USER_MAY_CREATE_ROOM_ALIAS_CALLBACK = Callable[[str, RoomAlias], Awaitable[bool]]
5251
USER_MAY_PUBLISH_ROOM_CALLBACK = Callable[[str, str], Awaitable[bool]]
53-
CHECK_USERNAME_FOR_SPAM_CALLBACK = Callable[[Dict[str, str]], Awaitable[bool]]
52+
CHECK_USERNAME_FOR_SPAM_CALLBACK = Callable[[UserProfile], Awaitable[bool]]
5453
LEGACY_CHECK_REGISTRATION_FOR_SPAM_CALLBACK = Callable[
5554
[
5655
Optional[dict],
@@ -383,7 +382,7 @@ async def user_may_publish_room(self, userid: str, room_id: str) -> bool:
383382

384383
return True
385384

386-
async def check_username_for_spam(self, user_profile: Dict[str, str]) -> bool:
385+
async def check_username_for_spam(self, user_profile: UserProfile) -> bool:
387386
"""Checks if a user ID or display name are considered "spammy" by this server.
388387
389388
If the server considers a username spammy, then it will not be included in

synapse/handlers/user_directory.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
from synapse.api.constants import EventTypes, HistoryVisibility, JoinRules, Membership
2020
from synapse.handlers.state_deltas import MatchChange, StateDeltasHandler
2121
from synapse.metrics.background_process_metrics import run_as_background_process
22+
from synapse.storage.databases.main.user_directory import SearchResult
2223
from synapse.storage.roommember import ProfileInfo
23-
from synapse.types import JsonDict
2424
from synapse.util.metrics import Measure
2525

2626
if TYPE_CHECKING:
@@ -78,7 +78,7 @@ def __init__(self, hs: "HomeServer"):
7878

7979
async def search_users(
8080
self, user_id: str, search_term: str, limit: int
81-
) -> JsonDict:
81+
) -> SearchResult:
8282
"""Searches for users in directory
8383
8484
Returns:

synapse/module_api/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
StateMap,
112112
UserID,
113113
UserInfo,
114+
UserProfile,
114115
create_requester,
115116
)
116117
from synapse.util import Clock
@@ -150,6 +151,7 @@
150151
"EventBase",
151152
"StateMap",
152153
"ProfileInfo",
154+
"UserProfile",
153155
]
154156

155157
logger = logging.getLogger(__name__)

synapse/rest/client/user_directory.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from synapse.http.server import HttpServer
2020
from synapse.http.servlet import RestServlet, parse_json_object_from_request
2121
from synapse.http.site import SynapseRequest
22-
from synapse.types import JsonDict
22+
from synapse.types import JsonMapping
2323

2424
from ._base import client_patterns
2525

@@ -38,7 +38,7 @@ def __init__(self, hs: "HomeServer"):
3838
self.auth = hs.get_auth()
3939
self.user_directory_handler = hs.get_user_directory_handler()
4040

41-
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
41+
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonMapping]:
4242
"""Searches for users in directory
4343
4444
Returns:

synapse/storage/databases/main/user_directory.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
cast,
2727
)
2828

29+
from typing_extensions import TypedDict
30+
2931
from synapse.api.errors import StoreError
3032

3133
if TYPE_CHECKING:
@@ -40,7 +42,12 @@
4042
from synapse.storage.databases.main.state import StateFilter
4143
from synapse.storage.databases.main.state_deltas import StateDeltasStore
4244
from synapse.storage.engines import PostgresEngine, Sqlite3Engine
43-
from synapse.types import JsonDict, get_domain_from_id, get_localpart_from_id
45+
from synapse.types import (
46+
JsonDict,
47+
UserProfile,
48+
get_domain_from_id,
49+
get_localpart_from_id,
50+
)
4451
from synapse.util.caches.descriptors import cached
4552

4653
logger = logging.getLogger(__name__)
@@ -591,6 +598,11 @@ async def update_user_directory_stream_pos(self, stream_id: Optional[int]) -> No
591598
)
592599

593600

601+
class SearchResult(TypedDict):
602+
limited: bool
603+
results: List[UserProfile]
604+
605+
594606
class UserDirectoryStore(UserDirectoryBackgroundUpdateStore):
595607
# How many records do we calculate before sending it to
596608
# add_users_who_share_private_rooms?
@@ -777,7 +789,7 @@ async def get_user_directory_stream_pos(self) -> Optional[int]:
777789

778790
async def search_user_dir(
779791
self, user_id: str, search_term: str, limit: int
780-
) -> JsonDict:
792+
) -> SearchResult:
781793
"""Searches for users in directory
782794
783795
Returns:
@@ -910,8 +922,11 @@ async def search_user_dir(
910922
# This should be unreachable.
911923
raise Exception("Unrecognized database engine")
912924

913-
results = await self.db_pool.execute(
914-
"search_user_dir", self.db_pool.cursor_to_dict, sql, *args
925+
results = cast(
926+
List[UserProfile],
927+
await self.db_pool.execute(
928+
"search_user_dir", self.db_pool.cursor_to_dict, sql, *args
929+
),
915930
)
916931

917932
limited = len(results) > limit

synapse/types.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import attr
3535
from frozendict import frozendict
3636
from signedjson.key import decode_verify_key_bytes
37+
from typing_extensions import TypedDict
3738
from unpaddedbase64 import decode_base64
3839
from zope.interface import Interface
3940

@@ -63,6 +64,10 @@
6364
# JSON types. These could be made stronger, but will do for now.
6465
# A JSON-serialisable dict.
6566
JsonDict = Dict[str, Any]
67+
# A JSON-serialisable mapping; roughly speaking an immutable JSONDict.
68+
# Useful when you have a TypedDict which isn't going to be mutated and you don't want
69+
# to cast to JsonDict everywhere.
70+
JsonMapping = Mapping[str, Any]
6671
# A JSON-serialisable object.
6772
JsonSerializable = object
6873

@@ -791,3 +796,9 @@ class UserInfo:
791796
is_deactivated: bool
792797
is_guest: bool
793798
is_shadow_banned: bool
799+
800+
801+
class UserProfile(TypedDict):
802+
user_id: str
803+
display_name: Optional[str]
804+
avatar_url: Optional[str]

0 commit comments

Comments
 (0)