diff --git a/latest/modules/spam_checker_callbacks.html b/latest/modules/spam_checker_callbacks.html
index 9a5b390ce9..9c44bf6ca7 100644
--- a/latest/modules/spam_checker_callbacks.html
+++ b/latest/modules/spam_checker_callbacks.html
@@ -413,8 +413,8 @@ this callback.</p>
<p><em>First introduced in Synapse v1.37.0</em></p>
<p><em>Changed in Synapse v1.62.0: <code>synapse.module_api.NOT_SPAM</code> and <code>synapse.module_api.errors.Codes</code> can be returned by this callback. Returning a boolean is now deprecated.</em> </p>
<pre><code class="language-python">async def check_media_file_for_spam(
- file_wrapper: "synapse.rest.media.v1.media_storage.ReadableFileWrapper",
- file_info: "synapse.rest.media.v1._base.FileInfo",
+ file_wrapper: "synapse.media.media_storage.ReadableFileWrapper",
+ file_info: "synapse.media._base.FileInfo",
) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
</code></pre>
<p>Called when storing a local or remote file.</p>
diff --git a/latest/modules/third_party_rules_callbacks.html b/latest/modules/third_party_rules_callbacks.html
index db3980be83..43f97fa055 100644
--- a/latest/modules/third_party_rules_callbacks.html
+++ b/latest/modules/third_party_rules_callbacks.html
@@ -253,6 +253,8 @@ it will be included in this state.</p>
<p>Note that this callback is called when the event has already been processed and stored
into the room, which means this callback cannot be used to deny persisting the event. To
deny an incoming event, see <a href="spam_checker_callbacks.html#check_event_for_spam"><code>check_event_for_spam</code></a> instead.</p>
+<p>For any given event, this callback will be called on every worker process, even if that worker will not end up
+acting on that event. This callback will not be called for events that are marked as rejected.</p>
<p>If multiple modules implement this callback, Synapse runs them all in order.</p>
<h3 id="check_can_shutdown_room"><a class="header" href="#check_can_shutdown_room"><code>check_can_shutdown_room</code></a></h3>
<p><em>First introduced in Synapse v1.55.0</em></p>
@@ -327,6 +329,10 @@ admin API.</p>
<p>If multiple modules implement this callback, Synapse runs them all in order.</p>
<h3 id="on_threepid_bind"><a class="header" href="#on_threepid_bind"><code>on_threepid_bind</code></a></h3>
<p><em>First introduced in Synapse v1.56.0</em></p>
+<p><strong><span style="color:red">
+This callback is deprecated in favour of the <code>on_add_user_third_party_identifier</code> callback, which
+features the same functionality. The only difference is in name.
+</span></strong></p>
<pre><code class="language-python">async def on_threepid_bind(user_id: str, medium: str, address: str) -> None:
</code></pre>
<p>Called after creating an association between a local user and a third-party identifier
@@ -336,6 +342,28 @@ third-party identifier.</p>
<p>Note that this callback is <em>not</em> called after a successful association on an <em>identity
server</em>.</p>
<p>If multiple modules implement this callback, Synapse runs them all in order.</p>
+<h3 id="on_add_user_third_party_identifier"><a class="header" href="#on_add_user_third_party_identifier"><code>on_add_user_third_party_identifier</code></a></h3>
+<p><em>First introduced in Synapse v1.79.0</em></p>
+<pre><code class="language-python">async def on_add_user_third_party_identifier(user_id: str, medium: str, address: str) -> None:
+</code></pre>
+<p>Called after successfully creating an association between a user and a third-party identifier
+(email address, phone number). The module is given the Matrix ID of the user the
+association is for, as well as the medium (<code>email</code> or <code>msisdn</code>) and address of the
+third-party identifier (i.e. an email address).</p>
+<p>Note that this callback is <em>not</em> called if a user attempts to bind their third-party identifier
+to an identity server (via a call to <a href="https://spec.matrix.org/v1.5/client-server-api/#post_matrixclientv3account3pidbind"><code>POST /_matrix/client/v3/account/3pid/bind</code></a>).</p>
+<p>If multiple modules implement this callback, Synapse runs them all in order.</p>
+<h3 id="on_remove_user_third_party_identifier"><a class="header" href="#on_remove_user_third_party_identifier"><code>on_remove_user_third_party_identifier</code></a></h3>
+<p><em>First introduced in Synapse v1.79.0</em></p>
+<pre><code class="language-python">async def on_remove_user_third_party_identifier(user_id: str, medium: str, address: str) -> None:
+</code></pre>
+<p>Called after successfully removing an association between a user and a third-party identifier
+(email address, phone number). The module is given the Matrix ID of the user the
+association is for, as well as the medium (<code>email</code> or <code>msisdn</code>) and address of the
+third-party identifier (i.e. an email address).</p>
+<p>Note that this callback is <em>not</em> called if a user attempts to unbind their third-party
+identifier from an identity server (via a call to <a href="https://spec.matrix.org/v1.5/client-server-api/#post_matrixclientv3account3pidunbind"><code>POST /_matrix/client/v3/account/3pid/unbind</code></a>).</p>
+<p>If multiple modules implement this callback, Synapse runs them all in order.</p>
<h2 id="example"><a class="header" href="#example">Example</a></h2>
<p>The example below is a module that implements the third-party rules callback
<code>check_event_allowed</code> to censor incoming messages as dictated by a third-party service.</p>
diff --git a/latest/modules/writing_a_module.html b/latest/modules/writing_a_module.html
index 1e2119a40c..bc8627c62e 100644
--- a/latest/modules/writing_a_module.html
+++ b/latest/modules/writing_a_module.html
@@ -207,6 +207,54 @@ the callback name as the argument name and the function as its value. A
<code>register_[...]_callbacks</code> method exists for each category.</p>
<p>Callbacks for each category can be found on their respective page of the
<a href="https://matrix-org.github.io/synapse">Synapse documentation website</a>.</p>
+<h2 id="caching"><a class="header" href="#caching">Caching</a></h2>
+<p><em>Added in Synapse 1.74.0.</em></p>
+<p>Modules can leverage Synapse's caching tools to manage their own cached functions. This
+can be helpful for modules that need to repeatedly request the same data from the database
+or a remote service.</p>
+<p>Functions that need to be wrapped with a cache need to be decorated with a <code>@cached()</code>
+decorator (which can be imported from <code>synapse.module_api</code>) and registered with the
+<a href="https://github.com/matrix-org/synapse/blob/release-v1.77/synapse/module_api/__init__.py#L888"><code>ModuleApi.register_cached_function</code></a>
+API when initialising the module. If the module needs to invalidate an entry in a cache,
+it needs to use the <a href="https://github.com/matrix-org/synapse/blob/release-v1.77/synapse/module_api/__init__.py#L904"><code>ModuleApi.invalidate_cache</code></a>
+API, with the function to invalidate the cache of and the key(s) of the entry to
+invalidate.</p>
+<p>Below is an example of a simple module using a cached function:</p>
+<pre><code class="language-python">from typing import Any
+from synapse.module_api import cached, ModuleApi
+
+class MyModule:
+ def __init__(self, config: Any, api: ModuleApi):
+ self.api = api
+
+ # Register the cached function so Synapse knows how to correctly invalidate
+ # entries for it.
+ self.api.register_cached_function(self.get_user_from_id)
+
+ @cached()
+ async def get_department_for_user(self, user_id: str) -> str:
+ """A function with a cache."""
+ # Request a department from an external service.
+ return await self.http_client.get_json(
+ "https://int.example.com/users", {"user_id": user_id)
+ )["department"]
+
+ async def do_something_with_users(self) -> None:
+ """Calls the cached function and then invalidates an entry in its cache."""
+
+ user_id = "@alice:example.com"
+
+ # Get the user. Since get_department_for_user is wrapped with a cache,
+ # the return value for this user_id will be cached.
+ department = await self.get_department_for_user(user_id)
+
+ # Do something with `department`...
+
+ # Let's say something has changed with our user, and the entry we have for
+ # them in the cache is out of date, so we want to invalidate it.
+ await self.api.invalidate_cache(self.get_department_for_user, (user_id,))
+</code></pre>
+<p>See the <a href="https://github.com/matrix-org/synapse/blob/release-v1.77/synapse/module_api/__init__.py#L190"><code>cached</code> docstring</a> for more details.</p>
</main>
|