|
@@ -119,6 +119,16 @@ class GroupsSyncResult(collections.namedtuple("GroupsSyncResult", [
|
|
|
return bool(self.join or self.invite or self.leave)
|
|
|
|
|
|
|
|
|
+class DeviceLists(collections.namedtuple("DeviceLists", [
|
|
|
+ "changed", # list of user_ids whose devices may have changed
|
|
|
+ "left", # list of user_ids whose devices we no longer track
|
|
|
+])):
|
|
|
+ __slots__ = []
|
|
|
+
|
|
|
+ def __nonzero__(self):
|
|
|
+ return bool(self.changed or self.left)
|
|
|
+
|
|
|
+
|
|
|
class SyncResult(collections.namedtuple("SyncResult", [
|
|
|
"next_batch", # Token for the next sync
|
|
|
"presence", # List of presence events for the user.
|
|
@@ -296,6 +306,11 @@ class SyncHandler(object):
|
|
|
timeline_limit = sync_config.filter_collection.timeline_limit()
|
|
|
block_all_timeline = sync_config.filter_collection.blocks_all_room_timeline()
|
|
|
|
|
|
+ # Pull out the current state, as we always want to include those events
|
|
|
+ # in the timeline if they're there.
|
|
|
+ current_state_ids = yield self.state.get_current_state_ids(room_id)
|
|
|
+ current_state_ids = frozenset(current_state_ids.itervalues())
|
|
|
+
|
|
|
if recents is None or newly_joined_room or timeline_limit < len(recents):
|
|
|
limited = True
|
|
|
else:
|
|
@@ -307,6 +322,7 @@ class SyncHandler(object):
|
|
|
self.store,
|
|
|
sync_config.user.to_string(),
|
|
|
recents,
|
|
|
+ always_include_ids=current_state_ids,
|
|
|
)
|
|
|
else:
|
|
|
recents = []
|
|
@@ -342,6 +358,7 @@ class SyncHandler(object):
|
|
|
self.store,
|
|
|
sync_config.user.to_string(),
|
|
|
loaded_recents,
|
|
|
+ always_include_ids=current_state_ids,
|
|
|
)
|
|
|
loaded_recents.extend(recents)
|
|
|
recents = loaded_recents
|
|
@@ -548,7 +565,8 @@ class SyncHandler(object):
|
|
|
res = yield self._generate_sync_entry_for_rooms(
|
|
|
sync_result_builder, account_data_by_room
|
|
|
)
|
|
|
- newly_joined_rooms, newly_joined_users = res
|
|
|
+ newly_joined_rooms, newly_joined_users, _, _ = res
|
|
|
+ _, _, newly_left_rooms, newly_left_users = res
|
|
|
|
|
|
block_all_presence_data = (
|
|
|
since_token is None and
|
|
@@ -562,7 +580,11 @@ class SyncHandler(object):
|
|
|
yield self._generate_sync_entry_for_to_device(sync_result_builder)
|
|
|
|
|
|
device_lists = yield self._generate_sync_entry_for_device_list(
|
|
|
- sync_result_builder
|
|
|
+ sync_result_builder,
|
|
|
+ newly_joined_rooms=newly_joined_rooms,
|
|
|
+ newly_joined_users=newly_joined_users,
|
|
|
+ newly_left_rooms=newly_left_rooms,
|
|
|
+ newly_left_users=newly_left_users,
|
|
|
)
|
|
|
|
|
|
device_id = sync_config.device_id
|
|
@@ -635,25 +657,50 @@ class SyncHandler(object):
|
|
|
|
|
|
@measure_func("_generate_sync_entry_for_device_list")
|
|
|
@defer.inlineCallbacks
|
|
|
- def _generate_sync_entry_for_device_list(self, sync_result_builder):
|
|
|
+ def _generate_sync_entry_for_device_list(self, sync_result_builder,
|
|
|
+ newly_joined_rooms, newly_joined_users,
|
|
|
+ newly_left_rooms, newly_left_users):
|
|
|
user_id = sync_result_builder.sync_config.user.to_string()
|
|
|
since_token = sync_result_builder.since_token
|
|
|
|
|
|
if since_token and since_token.device_list_key:
|
|
|
- room_ids = yield self.store.get_rooms_for_user(user_id)
|
|
|
-
|
|
|
- user_ids_changed = set()
|
|
|
changed = yield self.store.get_user_whose_devices_changed(
|
|
|
since_token.device_list_key
|
|
|
)
|
|
|
- for other_user_id in changed:
|
|
|
- other_room_ids = yield self.store.get_rooms_for_user(other_user_id)
|
|
|
- if room_ids.intersection(other_room_ids):
|
|
|
- user_ids_changed.add(other_user_id)
|
|
|
|
|
|
- defer.returnValue(user_ids_changed)
|
|
|
+ # TODO: Be more clever than this, i.e. remove users who we already
|
|
|
+ # share a room with?
|
|
|
+ for room_id in newly_joined_rooms:
|
|
|
+ joined_users = yield self.state.get_current_user_in_room(room_id)
|
|
|
+ newly_joined_users.update(joined_users)
|
|
|
+
|
|
|
+ for room_id in newly_left_rooms:
|
|
|
+ left_users = yield self.state.get_current_user_in_room(room_id)
|
|
|
+ newly_left_users.update(left_users)
|
|
|
+
|
|
|
+ # TODO: Check that these users are actually new, i.e. either they
|
|
|
+ # weren't in the previous sync *or* they left and rejoined.
|
|
|
+ changed.update(newly_joined_users)
|
|
|
+
|
|
|
+ if not changed and not newly_left_users:
|
|
|
+ defer.returnValue(DeviceLists(
|
|
|
+ changed=[],
|
|
|
+ left=newly_left_users,
|
|
|
+ ))
|
|
|
+
|
|
|
+ users_who_share_room = yield self.store.get_users_who_share_room_with_user(
|
|
|
+ user_id
|
|
|
+ )
|
|
|
+
|
|
|
+ defer.returnValue(DeviceLists(
|
|
|
+ changed=users_who_share_room & changed,
|
|
|
+ left=set(newly_left_users) - users_who_share_room,
|
|
|
+ ))
|
|
|
else:
|
|
|
- defer.returnValue([])
|
|
|
+ defer.returnValue(DeviceLists(
|
|
|
+ changed=[],
|
|
|
+ left=[],
|
|
|
+ ))
|
|
|
|
|
|
@defer.inlineCallbacks
|
|
|
def _generate_sync_entry_for_to_device(self, sync_result_builder):
|
|
@@ -817,8 +864,8 @@ class SyncHandler(object):
|
|
|
account_data_by_room(dict): Dictionary of per room account data
|
|
|
|
|
|
Returns:
|
|
|
- Deferred(tuple): Returns a 2-tuple of
|
|
|
- `(newly_joined_rooms, newly_joined_users)`
|
|
|
+ Deferred(tuple): Returns a 4-tuple of
|
|
|
+ `(newly_joined_rooms, newly_joined_users, newly_left_rooms, newly_left_users)`
|
|
|
"""
|
|
|
user_id = sync_result_builder.sync_config.user.to_string()
|
|
|
block_all_room_ephemeral = (
|
|
@@ -849,7 +896,7 @@ class SyncHandler(object):
|
|
|
)
|
|
|
if not tags_by_room:
|
|
|
logger.debug("no-oping sync")
|
|
|
- defer.returnValue(([], []))
|
|
|
+ defer.returnValue(([], [], [], []))
|
|
|
|
|
|
ignored_account_data = yield self.store.get_global_account_data_by_type_for_user(
|
|
|
"m.ignored_user_list", user_id=user_id,
|
|
@@ -862,7 +909,7 @@ class SyncHandler(object):
|
|
|
|
|
|
if since_token:
|
|
|
res = yield self._get_rooms_changed(sync_result_builder, ignored_users)
|
|
|
- room_entries, invited, newly_joined_rooms = res
|
|
|
+ room_entries, invited, newly_joined_rooms, newly_left_rooms = res
|
|
|
|
|
|
tags_by_room = yield self.store.get_updated_tags(
|
|
|
user_id, since_token.account_data_key,
|
|
@@ -870,6 +917,7 @@ class SyncHandler(object):
|
|
|
else:
|
|
|
res = yield self._get_all_rooms(sync_result_builder, ignored_users)
|
|
|
room_entries, invited, newly_joined_rooms = res
|
|
|
+ newly_left_rooms = []
|
|
|
|
|
|
tags_by_room = yield self.store.get_tags_for_user(user_id)
|
|
|
|
|
@@ -890,17 +938,30 @@ class SyncHandler(object):
|
|
|
|
|
|
# Now we want to get any newly joined users
|
|
|
newly_joined_users = set()
|
|
|
+ newly_left_users = set()
|
|
|
if since_token:
|
|
|
for joined_sync in sync_result_builder.joined:
|
|
|
it = itertools.chain(
|
|
|
- joined_sync.timeline.events, joined_sync.state.values()
|
|
|
+ joined_sync.timeline.events, joined_sync.state.itervalues()
|
|
|
)
|
|
|
for event in it:
|
|
|
if event.type == EventTypes.Member:
|
|
|
if event.membership == Membership.JOIN:
|
|
|
newly_joined_users.add(event.state_key)
|
|
|
-
|
|
|
- defer.returnValue((newly_joined_rooms, newly_joined_users))
|
|
|
+ else:
|
|
|
+ prev_content = event.unsigned.get("prev_content", {})
|
|
|
+ prev_membership = prev_content.get("membership", None)
|
|
|
+ if prev_membership == Membership.JOIN:
|
|
|
+ newly_left_users.add(event.state_key)
|
|
|
+
|
|
|
+ newly_left_users -= newly_joined_users
|
|
|
+
|
|
|
+ defer.returnValue((
|
|
|
+ newly_joined_rooms,
|
|
|
+ newly_joined_users,
|
|
|
+ newly_left_rooms,
|
|
|
+ newly_left_users,
|
|
|
+ ))
|
|
|
|
|
|
@defer.inlineCallbacks
|
|
|
def _have_rooms_changed(self, sync_result_builder):
|
|
@@ -970,15 +1031,17 @@ class SyncHandler(object):
|
|
|
mem_change_events_by_room_id.setdefault(event.room_id, []).append(event)
|
|
|
|
|
|
newly_joined_rooms = []
|
|
|
+ newly_left_rooms = []
|
|
|
room_entries = []
|
|
|
invited = []
|
|
|
- for room_id, events in mem_change_events_by_room_id.items():
|
|
|
+ for room_id, events in mem_change_events_by_room_id.iteritems():
|
|
|
non_joins = [e for e in events if e.membership != Membership.JOIN]
|
|
|
has_join = len(non_joins) != len(events)
|
|
|
|
|
|
# We want to figure out if we joined the room at some point since
|
|
|
# the last sync (even if we have since left). This is to make sure
|
|
|
# we do send down the room, and with full state, where necessary
|
|
|
+ old_state_ids = None
|
|
|
if room_id in joined_room_ids or has_join:
|
|
|
old_state_ids = yield self.get_state_at(room_id, since_token)
|
|
|
old_mem_ev_id = old_state_ids.get((EventTypes.Member, user_id), None)
|
|
@@ -996,6 +1059,26 @@ class SyncHandler(object):
|
|
|
if not non_joins:
|
|
|
continue
|
|
|
|
|
|
+ # Check if we have left the room. This can either be because we were
|
|
|
+ # joined before *or* that we since joined and then left.
|
|
|
+ if events[-1].membership != Membership.JOIN:
|
|
|
+ if has_join:
|
|
|
+ newly_left_rooms.append(room_id)
|
|
|
+ else:
|
|
|
+ if not old_state_ids:
|
|
|
+ old_state_ids = yield self.get_state_at(room_id, since_token)
|
|
|
+ old_mem_ev_id = old_state_ids.get(
|
|
|
+ (EventTypes.Member, user_id),
|
|
|
+ None,
|
|
|
+ )
|
|
|
+ old_mem_ev = None
|
|
|
+ if old_mem_ev_id:
|
|
|
+ old_mem_ev = yield self.store.get_event(
|
|
|
+ old_mem_ev_id, allow_none=True
|
|
|
+ )
|
|
|
+ if old_mem_ev and old_mem_ev.membership == Membership.JOIN:
|
|
|
+ newly_left_rooms.append(room_id)
|
|
|
+
|
|
|
# Only bother if we're still currently invited
|
|
|
should_invite = non_joins[-1].membership == Membership.INVITE
|
|
|
if should_invite:
|
|
@@ -1073,7 +1156,7 @@ class SyncHandler(object):
|
|
|
upto_token=since_token,
|
|
|
))
|
|
|
|
|
|
- defer.returnValue((room_entries, invited, newly_joined_rooms))
|
|
|
+ defer.returnValue((room_entries, invited, newly_joined_rooms, newly_left_rooms))
|
|
|
|
|
|
@defer.inlineCallbacks
|
|
|
def _get_all_rooms(self, sync_result_builder, ignored_users):
|
|
@@ -1322,6 +1405,7 @@ class SyncResultBuilder(object):
|
|
|
self.archived = []
|
|
|
self.device = []
|
|
|
self.groups = None
|
|
|
+ self.to_device = []
|
|
|
|
|
|
|
|
|
class RoomSyncResultBuilder(object):
|