diff --git a/synapse/handlers/account_data.py b/synapse/handlers/account_data.py
index 177b4f8991..4af9fbc5d1 100644
--- a/synapse/handlers/account_data.py
+++ b/synapse/handlers/account_data.py
@@ -12,8 +12,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+import logging
import random
-from typing import TYPE_CHECKING, Collection, List, Optional, Tuple
+from typing import TYPE_CHECKING, Awaitable, Callable, Collection, List, Optional, Tuple
from synapse.replication.http.account_data import (
ReplicationAddTagRestServlet,
@@ -27,6 +28,12 @@ from synapse.types import JsonDict, UserID
if TYPE_CHECKING:
from synapse.server import HomeServer
+logger = logging.getLogger(__name__)
+
+ON_ACCOUNT_DATA_UPDATED_CALLBACK = Callable[
+ [str, Optional[str], str, JsonDict], Awaitable
+]
+
class AccountDataHandler:
def __init__(self, hs: "HomeServer"):
@@ -40,6 +47,44 @@ class AccountDataHandler:
self._remove_tag_client = ReplicationRemoveTagRestServlet.make_client(hs)
self._account_data_writers = hs.config.worker.writers.account_data
+ self._on_account_data_updated_callbacks: List[
+ ON_ACCOUNT_DATA_UPDATED_CALLBACK
+ ] = []
+
+ def register_module_callbacks(
+ self, on_account_data_updated: Optional[ON_ACCOUNT_DATA_UPDATED_CALLBACK] = None
+ ) -> None:
+ """Register callbacks from modules."""
+ if on_account_data_updated is not None:
+ self._on_account_data_updated_callbacks.append(on_account_data_updated)
+
+ async def _notify_modules(
+ self,
+ user_id: str,
+ room_id: Optional[str],
+ account_data_type: str,
+ content: JsonDict,
+ ) -> None:
+ """Notifies modules about new account data changes.
+
+ A change can be either a new account data type being added, or the content
+ associated with a type being changed. Account data for a given type is removed by
+ changing the associated content to an empty dictionary.
+
+ Note that this is not called when the tags associated with a room change.
+
+ Args:
+ user_id: The user whose account data is changing.
+ room_id: The ID of the room the account data change concerns, if any.
+ account_data_type: The type of the account data.
+ content: The content that is now associated with this type.
+ """
+ for callback in self._on_account_data_updated_callbacks:
+ try:
+ await callback(user_id, room_id, account_data_type, content)
+ except Exception as e:
+ logger.exception("Failed to run module callback %s: %s", callback, e)
+
async def add_account_data_to_room(
self, user_id: str, room_id: str, account_data_type: str, content: JsonDict
) -> int:
@@ -63,6 +108,8 @@ class AccountDataHandler:
"account_data_key", max_stream_id, users=[user_id]
)
+ await self._notify_modules(user_id, room_id, account_data_type, content)
+
return max_stream_id
else:
response = await self._room_data_client(
@@ -96,6 +143,9 @@ class AccountDataHandler:
self._notifier.on_new_event(
"account_data_key", max_stream_id, users=[user_id]
)
+
+ await self._notify_modules(user_id, None, account_data_type, content)
+
return max_stream_id
else:
response = await self._user_data_client(
|