mirror of
https://github.com/element-hq/synapse
synced 2024-09-06 01:50:08 +00:00
Implement updating users who share rooms on the fly
This commit is contained in:
parent
72613bc379
commit
4564b05483
2 changed files with 235 additions and 24 deletions
|
@ -390,13 +390,78 @@ class UserDirectoyHandler(object):
|
||||||
room_id
|
room_id
|
||||||
)
|
)
|
||||||
|
|
||||||
if not is_public:
|
if is_public:
|
||||||
return
|
|
||||||
|
|
||||||
row = yield self.store.get_user_in_public_room(user_id)
|
row = yield self.store.get_user_in_public_room(user_id)
|
||||||
if not row:
|
if not row:
|
||||||
yield self.store.add_users_to_public_room(room_id, [user_id])
|
yield self.store.add_users_to_public_room(room_id, [user_id])
|
||||||
|
|
||||||
|
# Now we update users who share rooms with users. We do this by getting
|
||||||
|
# all the current users in the room and seeing which aren't already
|
||||||
|
# marked in the database as sharing with `user_id`
|
||||||
|
|
||||||
|
users_with_profile = yield self.state.get_current_user_in_room(room_id)
|
||||||
|
|
||||||
|
to_insert = set()
|
||||||
|
to_update = set()
|
||||||
|
|
||||||
|
# First, if they're our user then we need to update for every user
|
||||||
|
if self.is_mine_id(user_id):
|
||||||
|
# Returns a map of other_user_id -> shared_private. We only need
|
||||||
|
# to update mappings if for users that either don't share a room
|
||||||
|
# already (aren't in the map) or, if the room is private, those that
|
||||||
|
# only share a public room.
|
||||||
|
user_ids_shared = yield self.store.get_users_who_share_room_from_dir(
|
||||||
|
user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
for other_user_id in users_with_profile:
|
||||||
|
if user_id == other_user_id:
|
||||||
|
continue
|
||||||
|
|
||||||
|
shared_is_private = user_ids_shared.get(other_user_id)
|
||||||
|
if shared_is_private is True:
|
||||||
|
# We've already marked in the database they share a private room
|
||||||
|
continue
|
||||||
|
elif shared_is_private is False:
|
||||||
|
# They already share a public room, so only update if this is
|
||||||
|
# a private room
|
||||||
|
if not is_public:
|
||||||
|
to_update.add((user_id, other_user_id))
|
||||||
|
elif shared_is_private is None:
|
||||||
|
# This is the first time they both share a room
|
||||||
|
to_insert.add((user_id, other_user_id))
|
||||||
|
|
||||||
|
# Next we need to update for every local user in the room
|
||||||
|
for other_user_id in users_with_profile:
|
||||||
|
if user_id == other_user_id:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self.is_mine_id(other_user_id):
|
||||||
|
shared_is_private = yield self.store.get_if_users_share_a_room(
|
||||||
|
other_user_id, user_id,
|
||||||
|
)
|
||||||
|
if shared_is_private is True:
|
||||||
|
# We've already marked in the database they share a private room
|
||||||
|
continue
|
||||||
|
elif shared_is_private is False:
|
||||||
|
# They already share a public room, so only update if this is
|
||||||
|
# a private room
|
||||||
|
if not is_public:
|
||||||
|
to_update.add((other_user_id, user_id))
|
||||||
|
elif shared_is_private is None:
|
||||||
|
# This is the first time they both share a room
|
||||||
|
to_insert.add((other_user_id, user_id))
|
||||||
|
|
||||||
|
if to_insert:
|
||||||
|
yield self.store.add_users_who_share_room(
|
||||||
|
room_id, not is_public, to_insert,
|
||||||
|
)
|
||||||
|
|
||||||
|
if to_update:
|
||||||
|
yield self.store.update_users_who_share_room(
|
||||||
|
room_id, not is_public, to_update,
|
||||||
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _handle_remove_user(self, room_id, user_id):
|
def _handle_remove_user(self, room_id, user_id):
|
||||||
"""Called when we might need to remove user to directory
|
"""Called when we might need to remove user to directory
|
||||||
|
@ -413,13 +478,11 @@ class UserDirectoyHandler(object):
|
||||||
row = yield self.store.get_user_in_public_room(user_id)
|
row = yield self.store.get_user_in_public_room(user_id)
|
||||||
update_user_in_public = row and row["room_id"] == room_id
|
update_user_in_public = row and row["room_id"] == room_id
|
||||||
|
|
||||||
if not update_user_in_public and not update_user_dir:
|
if (update_user_in_public or update_user_dir):
|
||||||
return
|
|
||||||
|
|
||||||
# XXX: Make this faster?
|
# XXX: Make this faster?
|
||||||
rooms = yield self.store.get_rooms_for_user(user_id)
|
rooms = yield self.store.get_rooms_for_user(user_id)
|
||||||
for j_room_id in rooms:
|
for j_room_id in rooms:
|
||||||
if not update_user_in_public and not update_user_dir:
|
if (not update_user_in_public and not update_user_dir):
|
||||||
break
|
break
|
||||||
|
|
||||||
is_in_room = yield self.store.is_host_joined(
|
is_in_room = yield self.store.is_host_joined(
|
||||||
|
@ -433,12 +496,11 @@ class UserDirectoyHandler(object):
|
||||||
update_user_dir = False
|
update_user_dir = False
|
||||||
yield self.store.update_user_in_user_dir(user_id, j_room_id)
|
yield self.store.update_user_in_user_dir(user_id, j_room_id)
|
||||||
|
|
||||||
if update_user_in_public:
|
|
||||||
is_public = yield self.store.is_room_world_readable_or_publicly_joinable(
|
is_public = yield self.store.is_room_world_readable_or_publicly_joinable(
|
||||||
j_room_id
|
j_room_id
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_public:
|
if update_user_in_public and is_public:
|
||||||
yield self.store.update_user_in_public_user_list(user_id, j_room_id)
|
yield self.store.update_user_in_public_user_list(user_id, j_room_id)
|
||||||
update_user_in_public = False
|
update_user_in_public = False
|
||||||
|
|
||||||
|
@ -447,6 +509,46 @@ class UserDirectoyHandler(object):
|
||||||
elif update_user_in_public:
|
elif update_user_in_public:
|
||||||
yield self.store.remove_from_user_in_public_room(user_id)
|
yield self.store.remove_from_user_in_public_room(user_id)
|
||||||
|
|
||||||
|
# Now handle users_who_share_rooms.
|
||||||
|
|
||||||
|
# Get a list of user tuples that were in the DB due to this room and
|
||||||
|
# users (this includes tuples where the other user matches `user_id`)
|
||||||
|
user_tuples = yield self.store.get_users_in_share_dir_with_room_id(
|
||||||
|
user_id, room_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
for user_id, other_user_id in user_tuples:
|
||||||
|
# For each user tuple get a list of rooms that they still share,
|
||||||
|
# trying to find a private room, and update the entry in the DB
|
||||||
|
rooms = yield self.store.get_rooms_in_common_for_users(user_id, other_user_id)
|
||||||
|
|
||||||
|
# If they dont share a room anymore, remove the mapping
|
||||||
|
if not rooms:
|
||||||
|
yield self.store.remove_user_who_share_room(
|
||||||
|
user_id, other_user_id,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
found_public_share = None
|
||||||
|
for j_room_id in rooms:
|
||||||
|
is_public = yield self.store.is_room_world_readable_or_publicly_joinable(
|
||||||
|
j_room_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_public:
|
||||||
|
found_public_share = j_room_id
|
||||||
|
else:
|
||||||
|
found_public_share = None
|
||||||
|
yield self.store.update_users_who_share_room(
|
||||||
|
room_id, not is_public, [(user_id, other_user_id)],
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
if found_public_share:
|
||||||
|
yield self.store.update_users_who_share_room(
|
||||||
|
room_id, not is_public, [(user_id, other_user_id)],
|
||||||
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _handle_profile_change(self, user_id, room_id, prev_event_id, event_id):
|
def _handle_profile_change(self, user_id, room_id, prev_event_id, event_id):
|
||||||
"""Check member event changes for any profile changes and update the
|
"""Check member event changes for any profile changes and update the
|
||||||
|
|
|
@ -273,17 +273,38 @@ class UserDirectoryStore(SQLBaseStore):
|
||||||
desc="get_users_in_public_due_to_room",
|
desc="get_users_in_public_due_to_room",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
def get_users_in_dir_due_to_room(self, room_id):
|
def get_users_in_dir_due_to_room(self, room_id):
|
||||||
"""Get all user_ids that are in the room directory becuase they're
|
"""Get all user_ids that are in the room directory becuase they're
|
||||||
in the given room_id
|
in the given room_id
|
||||||
"""
|
"""
|
||||||
return self._simple_select_onecol(
|
user_ids_dir = yield self._simple_select_onecol(
|
||||||
table="user_directory",
|
table="user_directory",
|
||||||
keyvalues={"room_id": room_id},
|
keyvalues={"room_id": room_id},
|
||||||
retcol="user_id",
|
retcol="user_id",
|
||||||
desc="get_users_in_dir_due_to_room",
|
desc="get_users_in_dir_due_to_room",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
user_ids_pub = yield self._simple_select_onecol(
|
||||||
|
table="users_in_pubic_room",
|
||||||
|
keyvalues={"room_id": room_id},
|
||||||
|
retcol="user_id",
|
||||||
|
desc="get_users_in_dir_due_to_room",
|
||||||
|
)
|
||||||
|
|
||||||
|
user_ids_share = yield self._simple_select_onecol(
|
||||||
|
table="users_who_share_rooms",
|
||||||
|
keyvalues={"room_id": room_id},
|
||||||
|
retcol="user_id",
|
||||||
|
desc="get_users_in_dir_due_to_room",
|
||||||
|
)
|
||||||
|
|
||||||
|
user_ids = set(user_ids_dir)
|
||||||
|
user_ids.update(user_ids_pub)
|
||||||
|
user_ids.update(user_ids_share)
|
||||||
|
|
||||||
|
defer.returnValue(user_ids)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_all_rooms(self):
|
def get_all_rooms(self):
|
||||||
"""Get all room_ids we've ever known about, in ascending order of "size"
|
"""Get all room_ids we've ever known about, in ascending order of "size"
|
||||||
|
@ -398,6 +419,94 @@ class UserDirectoryStore(SQLBaseStore):
|
||||||
"remove_user_who_share_room", _remove_user_who_share_room_txn
|
"remove_user_who_share_room", _remove_user_who_share_room_txn
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@cached(max_entries=500000)
|
||||||
|
def get_if_users_share_a_room(self, user_id, other_user_id):
|
||||||
|
"""Gets if users share a room.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id (str): Must be a local user_id
|
||||||
|
other_user_id (str)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool|None: None if they don't share a room, otherwise whether they
|
||||||
|
share a private room or not.
|
||||||
|
"""
|
||||||
|
return self._simple_select_one_onecol(
|
||||||
|
table="users_who_share_rooms",
|
||||||
|
keyvalues={
|
||||||
|
"user_id": user_id,
|
||||||
|
"other_user_id": other_user_id,
|
||||||
|
},
|
||||||
|
retcol="share_private",
|
||||||
|
allow_none=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@cachedInlineCallbacks(max_entries=500000, iterable=True)
|
||||||
|
def get_users_who_share_room_from_dir(self, user_id):
|
||||||
|
"""Returns the set of users who share a room with `user_id`
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id(str): Must be a local user
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: user_id -> share_private mapping
|
||||||
|
"""
|
||||||
|
rows = yield self._simple_select_list(
|
||||||
|
table="users_who_share_rooms",
|
||||||
|
keyvalues={
|
||||||
|
"user_id": user_id,
|
||||||
|
},
|
||||||
|
retcols=("other_user_id", "share_private",),
|
||||||
|
desc="get_users_who_share_room_with_user",
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue({
|
||||||
|
row["other_user_id"]: row["share_private"]
|
||||||
|
for row in rows
|
||||||
|
})
|
||||||
|
|
||||||
|
def get_users_in_share_dir_with_room_id(self, user_id, room_id):
|
||||||
|
"""Get all user tuples that are in the users_who_share_rooms due to the
|
||||||
|
given room_id.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
[(user_id, other_user_id)]: where one of the two will match the given
|
||||||
|
user_id.
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
SELECT user_id, other_user_id FROM users_who_share_rooms
|
||||||
|
WHERE room_id = ? AND (user_id = ? OR other_user_id = ?)
|
||||||
|
"""
|
||||||
|
return self._execute(
|
||||||
|
"get_users_in_share_dir_with_room_id", None, sql, room_id, user_id, user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_rooms_in_common_for_users(self, user_id, other_user_id):
|
||||||
|
"""Given two user_ids find out the list of rooms they share.
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
SELECT room_id FROM (
|
||||||
|
SELECT c.room_id FROM current_state_events AS c
|
||||||
|
INNER JOIN room_memberships USING (event_id)
|
||||||
|
WHERE type = 'm.room.member'
|
||||||
|
AND membership = 'join'
|
||||||
|
AND state_key = ?
|
||||||
|
) AS f1 INNER JOIN (
|
||||||
|
SELECT c.room_id FROM current_state_events AS c
|
||||||
|
INNER JOIN room_memberships USING (event_id)
|
||||||
|
WHERE type = 'm.room.member'
|
||||||
|
AND membership = 'join'
|
||||||
|
AND state_key = ?
|
||||||
|
) f2 USING (room_id)
|
||||||
|
"""
|
||||||
|
|
||||||
|
rows = yield self._execute(
|
||||||
|
"get_rooms_in_common_for_users", None, sql, user_id, other_user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue([room_id for room_id, in rows])
|
||||||
|
|
||||||
def delete_all_from_user_dir(self):
|
def delete_all_from_user_dir(self):
|
||||||
"""Delete the entire user directory
|
"""Delete the entire user directory
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in a new issue