@@ -228,6 +228,7 @@ def __init__(self, hs: "HomeServer"):
228228
229229 # Called when there are new things to stream over replication
230230 self .replication_callbacks : List [Callable [[], None ]] = []
231+ self ._new_join_in_room_callbacks : List [Callable [[str , str ], None ]] = []
231232
232233 self ._federation_client = hs .get_federation_http_client ()
233234
@@ -280,6 +281,19 @@ def add_replication_callback(self, cb: Callable[[], None]) -> None:
280281 """
281282 self .replication_callbacks .append (cb )
282283
284+ def add_new_join_in_room_callback (self , cb : Callable [[str , str ], None ]) -> None :
285+ """Add a callback that will be called when a user joins a room.
286+
287+ This only fires on genuine membership changes, e.g. "invite" -> "join".
288+ Membership transitions like "join" -> "join" (for e.g. displayname changes) do
289+ not trigger the callback.
290+
291+ When called, the callback receives two arguments: the event ID and the room ID.
292+ It should *not* return a Deferred - if it needs to do any asynchronous work, a
293+ background thread should be started and wrapped with run_as_background_process.
294+ """
295+ self ._new_join_in_room_callbacks .append (cb )
296+
283297 async def on_new_room_event (
284298 self ,
285299 event : EventBase ,
@@ -723,6 +737,10 @@ def notify_replication(self) -> None:
723737 for cb in self .replication_callbacks :
724738 cb ()
725739
740+ def notify_user_joined_room (self , event_id : str , room_id : str ) -> None :
741+ for cb in self ._new_join_in_room_callbacks :
742+ cb (event_id , room_id )
743+
726744 def notify_remote_server_up (self , server : str ) -> None :
727745 """Notify any replication that a remote server has come back up"""
728746 # We call federation_sender directly rather than registering as a
0 commit comments