def on_GET(self, request): auth_user, _, is_guest = yield self.auth.get_user_by_req( request, allow_guest=True ) room_id = None if is_guest: if "room_id" not in request.args: raise SynapseError(400, "Guest users must specify room_id param") room_id = request.args["room_id"][0] try: handler = self.handlers.event_stream_handler pagin_config = PaginationConfig.from_request(request) timeout = EventStreamRestServlet.DEFAULT_LONGPOLL_TIME_MS if "timeout" in request.args: try: timeout = int(request.args["timeout"][0]) except ValueError: raise SynapseError(400, "timeout must be in milliseconds.") as_client_event = "raw" not in request.args chunk = yield handler.get_stream( auth_user.to_string(), pagin_config, timeout=timeout, as_client_event=as_client_event, affect_presence=(not is_guest), room_id=room_id, is_guest=is_guest ) except: logger.exception("Event stream failed") raise defer.returnValue((200, chunk))
def on_GET(self, request): requester = yield self.auth.get_user_by_req( request, allow_guest=True, ) is_guest = requester.is_guest room_id = None if is_guest: if b"room_id" not in request.args: raise SynapseError(400, "Guest users must specify room_id param") if b"room_id" in request.args: room_id = request.args[b"room_id"][0].decode('ascii') pagin_config = PaginationConfig.from_request(request) timeout = EventStreamRestServlet.DEFAULT_LONGPOLL_TIME_MS if b"timeout" in request.args: try: timeout = int(request.args[b"timeout"][0]) except ValueError: raise SynapseError(400, "timeout must be in milliseconds.") as_client_event = b"raw" not in request.args chunk = yield self.event_stream_handler.get_stream( requester.user.to_string(), pagin_config, timeout=timeout, as_client_event=as_client_event, affect_presence=(not is_guest), room_id=room_id, is_guest=is_guest, ) defer.returnValue((200, chunk))
def on_GET(self, request, room_id): user = yield self.auth.get_user_by_req(request) pagination_config = PaginationConfig.from_request(request) content = yield self.handlers.message_handler.room_initial_sync( room_id=urllib.unquote(room_id), user_id=user.to_string(), pagin_config=pagination_config, ) defer.returnValue((200, content))
def on_GET(self, request, room_id): requester = yield self.auth.get_user_by_req(request, allow_guest=True) pagination_config = PaginationConfig.from_request(request) content = yield self.handlers.message_handler.room_initial_sync( room_id=room_id, requester=requester, pagin_config=pagination_config, ) defer.returnValue((200, content))
def initial_sync(self, sync_config): """Get a sync for a client which is starting without any state Returns: A Deferred SyncResult. """ if sync_config.sort == "timeline,desc": # TODO(mjark): Handle going through events in reverse order?. # What does "most recent events" mean when applying the limits mean # in this case? raise NotImplementedError() now_token = yield self.event_sources.get_current_token() presence_stream = self.event_sources.sources["presence"] # TODO (mjark): This looks wrong, shouldn't we be getting the presence # UP to the present rather than after the present? pagination_config = PaginationConfig(from_token=now_token) presence, _ = yield presence_stream.get_pagination_rows( user=sync_config.user, pagination_config=pagination_config.get_source_config("presence"), key=None ) room_list = yield self.store.get_rooms_for_user_where_membership_is( user_id=sync_config.user.to_string(), membership_list=[Membership.INVITE, Membership.JOIN] ) # TODO (mjark): Does public mean "published"? published_rooms = yield self.store.get_rooms(is_public=True) published_room_ids = set(r["room_id"] for r in published_rooms) rooms = [] for event in room_list: room_sync = yield self.initial_sync_for_room( event.room_id, sync_config, now_token, published_room_ids ) rooms.append(room_sync) defer.returnValue(SyncResult( public_user_data=presence, private_user_data=[], rooms=rooms, next_batch=now_token, ))
def on_GET(self, request): user = yield self.auth.get_user_by_req(request) with_feedback = "feedback" in request.args pagination_config = PaginationConfig.from_request(request) handler = self.handlers.message_handler content = yield handler.snapshot_all_rooms( user_id=user.to_string(), pagin_config=pagination_config, feedback=with_feedback) defer.returnValue((200, content))
def on_GET(self, request, room_id): user = yield self.auth.get_user_by_req(request) pagination_config = PaginationConfig.from_request(request) with_feedback = "feedback" in request.args handler = self.handlers.message_handler msgs = yield handler.get_messages( room_id=urllib.unquote(room_id), user_id=user.to_string(), pagin_config=pagination_config, feedback=with_feedback) defer.returnValue((200, msgs))
def on_GET(self, request): requester = yield self.auth.get_user_by_req(request) as_client_event = "raw" not in request.args pagination_config = PaginationConfig.from_request(request) include_archived = request.args.get("archived", None) == ["true"] content = yield self.initial_sync_handler.snapshot_all_rooms( user_id=requester.user.to_string(), pagin_config=pagination_config, as_client_event=as_client_event, include_archived=include_archived, ) defer.returnValue((200, content))
def on_GET(self, request, room_id): requester = yield self.auth.get_user_by_req(request, allow_guest=True) pagination_config = PaginationConfig.from_request( request, default_limit=10, ) as_client_event = "raw" not in request.args handler = self.handlers.message_handler msgs = yield handler.get_messages(room_id=room_id, requester=requester, pagin_config=pagination_config, as_client_event=as_client_event) defer.returnValue((200, msgs))
def on_GET(self, request, room_id): requester = yield self.auth.get_user_by_req(request, allow_guest=True) pagination_config = PaginationConfig.from_request( request, default_limit=10, ) as_client_event = "raw" not in request.args handler = self.handlers.message_handler msgs = yield handler.get_messages( room_id=room_id, requester=requester, pagin_config=pagination_config, as_client_event=as_client_event ) defer.returnValue((200, msgs))
def on_GET(self, request): auth_user = yield self.auth.get_user_by_req(request) handler = self.handlers.event_stream_handler pagin_config = PaginationConfig.from_request(request) timeout = EventStreamRestServlet.DEFAULT_LONGPOLL_TIME_MS if "timeout" in request.args: try: timeout = int(request.args["timeout"][0]) except ValueError: raise SynapseError(400, "timeout must be in milliseconds.") chunk = yield handler.get_stream(auth_user.to_string(), pagin_config, timeout=timeout) defer.returnValue((200, chunk))
def on_GET(self, request, room_id): user, client = yield self.auth.get_user_by_req(request) pagination_config = PaginationConfig.from_request( request, default_limit=10, ) with_feedback = "feedback" in request.args as_client_event = "raw" not in request.args handler = self.handlers.message_handler msgs = yield handler.get_messages( room_id=room_id, user_id=user.to_string(), pagin_config=pagination_config, feedback=with_feedback, as_client_event=as_client_event ) defer.returnValue((200, msgs))
def on_GET(self, request, room_id): requester = yield self.auth.get_user_by_req(request, allow_guest=True) pagination_config = PaginationConfig.from_request( request, default_limit=10, ) as_client_event = "raw" not in request.args filter_bytes = parse_string(request, "filter") if filter_bytes: filter_json = urlparse.unquote(filter_bytes).decode("UTF-8") event_filter = Filter(json.loads(filter_json)) else: event_filter = None msgs = yield self.pagination_handler.get_messages( room_id=room_id, requester=requester, pagin_config=pagination_config, as_client_event=as_client_event, event_filter=event_filter, ) defer.returnValue((200, msgs))
def on_GET(self, request, room_id): requester = yield self.auth.get_user_by_req(request, allow_guest=True) pagination_config = PaginationConfig.from_request( request, default_limit=10, ) as_client_event = b"raw" not in request.args filter_bytes = parse_string(request, b"filter", encoding=None) if filter_bytes: filter_json = urlparse.unquote(filter_bytes.decode("UTF-8")) event_filter = Filter(json.loads(filter_json)) else: event_filter = None msgs = yield self.pagination_handler.get_messages( room_id=room_id, requester=requester, pagin_config=pagination_config, as_client_event=as_client_event, event_filter=event_filter, ) defer.returnValue((200, msgs))
def on_GET(self, request, room_id): requester = yield self.auth.get_user_by_req(request, allow_guest=True) pagination_config = PaginationConfig.from_request( request, default_limit=10, ) as_client_event = "raw" not in request.args filter_bytes = request.args.get("filter", None) if filter_bytes: filter_json = urllib.unquote(filter_bytes[-1]).decode("UTF-8") event_filter = Filter(json.loads(filter_json)) else: event_filter = None handler = self.handlers.message_handler msgs = yield handler.get_messages( room_id=room_id, requester=requester, pagin_config=pagination_config, as_client_event=as_client_event, event_filter=event_filter, ) defer.returnValue((200, msgs))
def _snapshot_all_rooms(self, user_id=None, pagin_config=None, as_client_event=True, include_archived=False): memberships = [Membership.INVITE, Membership.JOIN] if include_archived: memberships.append(Membership.LEAVE) room_list = yield self.store.get_rooms_for_user_where_membership_is( user_id=user_id, membership_list=memberships ) user = UserID.from_string(user_id) rooms_ret = [] now_token = yield self.hs.get_event_sources().get_current_token() presence_stream = self.hs.get_event_sources().sources["presence"] pagination_config = PaginationConfig(from_token=now_token) presence, _ = yield presence_stream.get_pagination_rows( user, pagination_config.get_source_config("presence"), None ) receipt_stream = self.hs.get_event_sources().sources["receipt"] receipt, _ = yield receipt_stream.get_pagination_rows( user, pagination_config.get_source_config("receipt"), None ) tags_by_room = yield self.store.get_tags_for_user(user_id) account_data, account_data_by_room = ( yield self.store.get_account_data_for_user(user_id) ) public_room_ids = yield self.store.get_public_room_ids() limit = pagin_config.limit if limit is None: limit = 10 @defer.inlineCallbacks def handle_room(event): d = { "room_id": event.room_id, "membership": event.membership, "visibility": ( "public" if event.room_id in public_room_ids else "private" ), } if event.membership == Membership.INVITE: time_now = self.clock.time_msec() d["inviter"] = event.sender invite_event = yield self.store.get_event(event.event_id) d["invite"] = yield self._event_serializer.serialize_event( invite_event, time_now, as_client_event, ) rooms_ret.append(d) if event.membership not in (Membership.JOIN, Membership.LEAVE): return try: if event.membership == Membership.JOIN: room_end_token = now_token.room_key deferred_room_state = run_in_background( self.state_handler.get_current_state, event.room_id, ) elif event.membership == Membership.LEAVE: room_end_token = "s%d" % (event.stream_ordering,) deferred_room_state = run_in_background( self.store.get_state_for_events, [event.event_id], ) deferred_room_state.addCallback( lambda states: states[event.event_id] ) (messages, token), current_state = yield make_deferred_yieldable( defer.gatherResults( [ run_in_background( self.store.get_recent_events_for_room, event.room_id, limit=limit, end_token=room_end_token, ), deferred_room_state, ] ) ).addErrback(unwrapFirstError) messages = yield filter_events_for_client( self.store, user_id, messages ) start_token = now_token.copy_and_replace("room_key", token) end_token = now_token.copy_and_replace("room_key", room_end_token) time_now = self.clock.time_msec() d["messages"] = { "chunk": ( yield self._event_serializer.serialize_events( messages, time_now=time_now, as_client_event=as_client_event, ) ), "start": start_token.to_string(), "end": end_token.to_string(), } d["state"] = yield self._event_serializer.serialize_events( current_state.values(), time_now=time_now, as_client_event=as_client_event ) account_data_events = [] tags = tags_by_room.get(event.room_id) if tags: account_data_events.append({ "type": "m.tag", "content": {"tags": tags}, }) account_data = account_data_by_room.get(event.room_id, {}) for account_data_type, content in account_data.items(): account_data_events.append({ "type": account_data_type, "content": content, }) d["account_data"] = account_data_events except Exception: logger.exception("Failed to get snapshot") yield concurrently_execute(handle_room, room_list, 10) account_data_events = [] for account_data_type, content in account_data.items(): account_data_events.append({ "type": account_data_type, "content": content, }) now = self.clock.time_msec() ret = { "rooms": rooms_ret, "presence": [ { "type": "m.presence", "content": format_user_presence_state(event, now), } for event in presence ], "account_data": account_data_events, "receipts": receipt, "end": now_token.to_string(), } defer.returnValue(ret)
def _snapshot_all_rooms(self, user_id=None, pagin_config=None, as_client_event=True, include_archived=False): memberships = [Membership.INVITE, Membership.JOIN] if include_archived: memberships.append(Membership.LEAVE) room_list = yield self.store.get_rooms_for_user_where_membership_is( user_id=user_id, membership_list=memberships ) user = UserID.from_string(user_id) rooms_ret = [] now_token = yield self.hs.get_event_sources().get_current_token() presence_stream = self.hs.get_event_sources().sources["presence"] pagination_config = PaginationConfig(from_token=now_token) presence, _ = yield presence_stream.get_pagination_rows( user, pagination_config.get_source_config("presence"), None ) receipt_stream = self.hs.get_event_sources().sources["receipt"] receipt, _ = yield receipt_stream.get_pagination_rows( user, pagination_config.get_source_config("receipt"), None ) tags_by_room = yield self.store.get_tags_for_user(user_id) account_data, account_data_by_room = ( yield self.store.get_account_data_for_user(user_id) ) public_room_ids = yield self.store.get_public_room_ids() limit = pagin_config.limit if limit is None: limit = 10 @defer.inlineCallbacks def handle_room(event): d = { "room_id": event.room_id, "membership": event.membership, "visibility": ( "public" if event.room_id in public_room_ids else "private" ), } if event.membership == Membership.INVITE: time_now = self.clock.time_msec() d["inviter"] = event.sender invite_event = yield self.store.get_event(event.event_id) d["invite"] = serialize_event(invite_event, time_now, as_client_event) rooms_ret.append(d) if event.membership not in (Membership.JOIN, Membership.LEAVE): return try: if event.membership == Membership.JOIN: room_end_token = now_token.room_key deferred_room_state = self.state_handler.get_current_state( event.room_id ) elif event.membership == Membership.LEAVE: room_end_token = "s%d" % (event.stream_ordering,) deferred_room_state = self.store.get_state_for_events( [event.event_id], None ) deferred_room_state.addCallback( lambda states: states[event.event_id] ) (messages, token), current_state = yield defer.gatherResults( [ self.store.get_recent_events_for_room( event.room_id, limit=limit, end_token=room_end_token, ), deferred_room_state, ] ).addErrback(unwrapFirstError) messages = yield self._filter_events_for_client( user_id, messages ) start_token = now_token.copy_and_replace("room_key", token[0]) end_token = now_token.copy_and_replace("room_key", token[1]) time_now = self.clock.time_msec() d["messages"] = { "chunk": [ serialize_event(m, time_now, as_client_event) for m in messages ], "start": start_token.to_string(), "end": end_token.to_string(), } d["state"] = [ serialize_event(c, time_now, as_client_event) for c in current_state.values() ] account_data_events = [] tags = tags_by_room.get(event.room_id) if tags: account_data_events.append({ "type": "m.tag", "content": {"tags": tags}, }) account_data = account_data_by_room.get(event.room_id, {}) for account_data_type, content in account_data.items(): account_data_events.append({ "type": account_data_type, "content": content, }) d["account_data"] = account_data_events except: logger.exception("Failed to get snapshot") # Only do N rooms at once n = 5 d_list = [handle_room(e) for e in room_list] for i in range(0, len(d_list), n): yield defer.gatherResults( d_list[i:i + n], consumeErrors=True ).addErrback(unwrapFirstError) account_data_events = [] for account_data_type, content in account_data.items(): account_data_events.append({ "type": account_data_type, "content": content, }) ret = { "rooms": rooms_ret, "presence": presence, "account_data": account_data_events, "receipts": receipt, "end": now_token.to_string(), } defer.returnValue(ret)
def full_state_sync(self, sync_config, timeline_since_token): """Get a sync for a client which is starting without any state. If a 'message_since_token' is given, only timeline events which have happened since that token will be returned. Returns: A Deferred SyncResult. """ now_token = yield self.event_sources.get_current_token() now_token, ephemeral_by_room = yield self.ephemeral_by_room( sync_config, now_token ) presence_stream = self.event_sources.sources["presence"] # TODO (mjark): This looks wrong, shouldn't we be getting the presence # UP to the present rather than after the present? pagination_config = PaginationConfig(from_token=now_token) presence, _ = yield presence_stream.get_pagination_rows( user=sync_config.user, pagination_config=pagination_config.get_source_config("presence"), key=None ) room_list = yield self.store.get_rooms_for_user_where_membership_is( user_id=sync_config.user.to_string(), membership_list=( Membership.INVITE, Membership.JOIN, Membership.LEAVE, Membership.BAN ) ) tags_by_room = yield self.store.get_tags_for_user( sync_config.user.to_string() ) joined = [] invited = [] archived = [] for event in room_list: if event.membership == Membership.JOIN: room_sync = yield self.full_state_sync_for_joined_room( room_id=event.room_id, sync_config=sync_config, now_token=now_token, timeline_since_token=timeline_since_token, ephemeral_by_room=ephemeral_by_room, tags_by_room=tags_by_room, ) joined.append(room_sync) elif event.membership == Membership.INVITE: invite = yield self.store.get_event(event.event_id) invited.append(InvitedSyncResult( room_id=event.room_id, invite=invite, )) elif event.membership in (Membership.LEAVE, Membership.BAN): leave_token = now_token.copy_and_replace( "room_key", "s%d" % (event.stream_ordering,) ) room_sync = yield self.full_state_sync_for_archived_room( sync_config=sync_config, room_id=event.room_id, leave_event_id=event.event_id, leave_token=leave_token, timeline_since_token=timeline_since_token, tags_by_room=tags_by_room, ) archived.append(room_sync) defer.returnValue(SyncResult( presence=presence, joined=joined, invited=invited, archived=archived, next_batch=now_token, ))
def snapshot_all_rooms(self, user_id=None, pagin_config=None, feedback=False): """Retrieve a snapshot of all rooms the user is invited or has joined. This snapshot may include messages for all rooms where the user is joined, depending on the pagination config. Args: user_id (str): The ID of the user making the request. pagin_config (synapse.api.streams.PaginationConfig): The pagination config used to determine how many messages *PER ROOM* to return. feedback (bool): True to get feedback along with these messages. Returns: A list of dicts with "room_id" and "membership" keys for all rooms the user is currently invited or joined in on. Rooms where the user is joined on, may return a "messages" key with messages, depending on the specified PaginationConfig. """ room_list = yield self.store.get_rooms_for_user_where_membership_is( user_id=user_id, membership_list=[Membership.INVITE, Membership.JOIN]) user = self.hs.parse_userid(user_id) rooms_ret = [] now_token = yield self.hs.get_event_sources().get_current_token() presence_stream = self.hs.get_event_sources().sources["presence"] pagination_config = PaginationConfig(from_token=now_token) presence, _ = yield presence_stream.get_pagination_rows( user, pagination_config, None) public_rooms = yield self.store.get_rooms(is_public=True) public_room_ids = [r["room_id"] for r in public_rooms] limit = pagin_config.limit if not limit: limit = 10 for event in room_list: d = { "room_id": event.room_id, "membership": event.membership, "visibility": ("public" if event.room_id in public_room_ids else "private"), } if event.membership == Membership.INVITE: d["inviter"] = event.user_id rooms_ret.append(d) if event.membership != Membership.JOIN: continue try: messages, token = yield self.store.get_recent_events_for_room( event.room_id, limit=limit, end_token=now_token.room_key, ) start_token = now_token.copy_and_replace("room_key", token[0]) end_token = now_token.copy_and_replace("room_key", token[1]) d["messages"] = { "chunk": [self.hs.serialize_event(m) for m in messages], "start": start_token.to_string(), "end": end_token.to_string(), } current_state = yield self.store.get_current_state( event.room_id) d["state"] = [ self.hs.serialize_event(c) for c in current_state ] except: logger.exception("Failed to get snapshot") ret = { "rooms": rooms_ret, "presence": presence, "end": now_token.to_string() } defer.returnValue(ret)
def snapshot_all_rooms(self, user_id=None, pagin_config=None, feedback=False): """Retrieve a snapshot of all rooms the user is invited or has joined. This snapshot may include messages for all rooms where the user is joined, depending on the pagination config. Args: user_id (str): The ID of the user making the request. pagin_config (synapse.api.streams.PaginationConfig): The pagination config used to determine how many messages *PER ROOM* to return. feedback (bool): True to get feedback along with these messages. Returns: A list of dicts with "room_id" and "membership" keys for all rooms the user is currently invited or joined in on. Rooms where the user is joined on, may return a "messages" key with messages, depending on the specified PaginationConfig. """ room_list = yield self.store.get_rooms_for_user_where_membership_is( user_id=user_id, membership_list=[Membership.INVITE, Membership.JOIN] ) user = self.hs.parse_userid(user_id) rooms_ret = [] now_token = yield self.hs.get_event_sources().get_current_token() presence_stream = self.hs.get_event_sources().sources["presence"] pagination_config = PaginationConfig(from_token=now_token) presence, _ = yield presence_stream.get_pagination_rows( user, pagination_config.get_source_config("presence"), None ) public_rooms = yield self.store.get_rooms(is_public=True) public_room_ids = [r["room_id"] for r in public_rooms] limit = pagin_config.limit if limit is None: limit = 10 for event in room_list: d = { "room_id": event.room_id, "membership": event.membership, "visibility": ( "public" if event.room_id in public_room_ids else "private" ), } if event.membership == Membership.INVITE: d["inviter"] = event.user_id rooms_ret.append(d) if event.membership != Membership.JOIN: continue try: messages, token = yield self.store.get_recent_events_for_room( event.room_id, limit=limit, end_token=now_token.room_key, ) start_token = now_token.copy_and_replace("room_key", token[0]) end_token = now_token.copy_and_replace("room_key", token[1]) d["messages"] = { "chunk": [self.hs.serialize_event(m) for m in messages], "start": start_token.to_string(), "end": end_token.to_string(), } current_state = yield self.state_handler.get_current_state( event.room_id ) d["state"] = [ self.hs.serialize_event(c) for c in current_state ] except: logger.exception("Failed to get snapshot") ret = { "rooms": rooms_ret, "presence": presence, "end": now_token.to_string() } defer.returnValue(ret)
def full_state_sync(self, sync_config, timeline_since_token): """Get a sync for a client which is starting without any state. If a 'message_since_token' is given, only timeline events which have happened since that token will be returned. Returns: A Deferred SyncResult. """ now_token = yield self.event_sources.get_current_token() now_token, ephemeral_by_room = yield self.ephemeral_by_room( sync_config, now_token) presence_stream = self.event_sources.sources["presence"] # TODO (mjark): This looks wrong, shouldn't we be getting the presence # UP to the present rather than after the present? pagination_config = PaginationConfig(from_token=now_token) presence, _ = yield presence_stream.get_pagination_rows( user=sync_config.user, pagination_config=pagination_config.get_source_config("presence"), key=None) membership_list = (Membership.INVITE, Membership.JOIN, Membership.LEAVE, Membership.BAN) room_list = yield self.store.get_rooms_for_user_where_membership_is( user_id=sync_config.user.to_string(), membership_list=membership_list) account_data, account_data_by_room = ( yield self.store.get_account_data_for_user(sync_config.user.to_string())) account_data['m.push_rules'] = yield self.push_rules_for_user( sync_config.user) tags_by_room = yield self.store.get_tags_for_user( sync_config.user.to_string()) joined = [] invited = [] archived = [] deferreds = [] room_list_chunks = [ room_list[i:i + 10] for i in xrange(0, len(room_list), 10) ] for room_list_chunk in room_list_chunks: for event in room_list_chunk: if event.membership == Membership.JOIN: room_sync_deferred = preserve_fn( self.full_state_sync_for_joined_room)( room_id=event.room_id, sync_config=sync_config, now_token=now_token, timeline_since_token=timeline_since_token, ephemeral_by_room=ephemeral_by_room, tags_by_room=tags_by_room, account_data_by_room=account_data_by_room, ) room_sync_deferred.addCallback(joined.append) deferreds.append(room_sync_deferred) elif event.membership == Membership.INVITE: invite = yield self.store.get_event(event.event_id) invited.append( InvitedSyncResult( room_id=event.room_id, invite=invite, )) elif event.membership in (Membership.LEAVE, Membership.BAN): # Always send down rooms we were banned or kicked from. if not sync_config.filter_collection.include_leave: if event.membership == Membership.LEAVE: if sync_config.user.to_string() == event.sender: continue leave_token = now_token.copy_and_replace( "room_key", "s%d" % (event.stream_ordering, )) room_sync_deferred = preserve_fn( self.full_state_sync_for_archived_room)( sync_config=sync_config, room_id=event.room_id, leave_event_id=event.event_id, leave_token=leave_token, timeline_since_token=timeline_since_token, tags_by_room=tags_by_room, account_data_by_room=account_data_by_room, ) room_sync_deferred.addCallback(archived.append) deferreds.append(room_sync_deferred) yield defer.gatherResults( deferreds, consumeErrors=True).addErrback(unwrapFirstError) account_data_for_user = sync_config.filter_collection.filter_account_data( self.account_data_for_user(account_data)) presence = sync_config.filter_collection.filter_presence(presence) defer.returnValue( SyncResult( presence=presence, account_data=account_data_for_user, joined=joined, invited=invited, archived=archived, next_batch=now_token, ))
def get_and_dispatch(self): from_tok = StreamToken.from_string(self.last_token) config = PaginationConfig(from_token=from_tok, limit='1') timeout = (300 + random.randint(-60, 60)) * 1000 chunk = yield self.evStreamHandler.get_stream( self.user_id, config, timeout=timeout, affect_presence=False, only_keys=( "room", "receipt", ), ) # limiting to 1 may get 1 event plus 1 presence event, so # pick out the actual event single_event = None read_receipt = None for c in chunk['chunk']: if 'event_id' in c: # Hmmm... single_event = c elif c['type'] == 'm.receipt': read_receipt = c have_updated_badge = False if read_receipt: for receipt_part in read_receipt['content'].values(): if 'm.read' in receipt_part: if self.user_id in receipt_part['m.read'].keys(): have_updated_badge = True if not single_event: if have_updated_badge: yield self.update_badge() self.last_token = chunk['end'] yield self.store.update_pusher_last_token(self.app_id, self.pushkey, self.user_id, self.last_token) return if not self.alive: return processed = False rule_evaluator = yield \ evaluator_for_user_id( self.user_id, single_event['room_id'], self.store ) actions = yield rule_evaluator.actions_for_event(single_event) tweaks = rule_evaluator.tweaks_for_actions(actions) if 'notify' in actions: self.badge = yield self._get_badge_count() rejected = yield self.dispatch_push(single_event, tweaks, self.badge) self.has_unread = True if isinstance(rejected, list) or isinstance(rejected, tuple): processed = True for pk in rejected: if pk != self.pushkey: # for sanity, we only remove the pushkey if it # was the one we actually sent... logger.warn(("Ignoring rejected pushkey %s because we" " didn't send it"), pk) else: logger.info("Pushkey %s was rejected: removing", pk) yield self.hs.get_pusherpool().remove_pusher( self.app_id, pk, self.user_id) else: if have_updated_badge: yield self.update_badge() processed = True if not self.alive: return if processed: self.backoff_delay = Pusher.INITIAL_BACKOFF self.last_token = chunk['end'] yield self.store.update_pusher_last_token_and_success( self.app_id, self.pushkey, self.user_id, self.last_token, self.clock.time_msec()) if self.failing_since: self.failing_since = None yield self.store.update_pusher_failing_since( self.app_id, self.pushkey, self.user_id, self.failing_since) else: if not self.failing_since: self.failing_since = self.clock.time_msec() yield self.store.update_pusher_failing_since( self.app_id, self.pushkey, self.user_id, self.failing_since) if (self.failing_since and self.failing_since < self.clock.time_msec() - Pusher.GIVE_UP_AFTER): # we really only give up so that if the URL gets # fixed, we don't suddenly deliver a load # of old notifications. logger.warn( "Giving up on a notification to user %s, " "pushkey %s", self.user_id, self.pushkey) self.backoff_delay = Pusher.INITIAL_BACKOFF self.last_token = chunk['end'] yield self.store.update_pusher_last_token( self.app_id, self.pushkey, self.user_id, self.last_token) self.failing_since = None yield self.store.update_pusher_failing_since( self.app_id, self.pushkey, self.user_id, self.failing_since) else: logger.warn( "Failed to dispatch push for user %s " "(failing for %dms)." "Trying again in %dms", self.user_id, self.clock.time_msec() - self.failing_since, self.backoff_delay) yield synapse.util. async .sleep(self.backoff_delay / 1000.0) self.backoff_delay *= 2 if self.backoff_delay > Pusher.MAX_BACKOFF: self.backoff_delay = Pusher.MAX_BACKOFF
def full_state_sync(self, sync_config, timeline_since_token): """Get a sync for a client which is starting without any state. If a 'message_since_token' is given, only timeline events which have happened since that token will be returned. Returns: A Deferred SyncResult. """ now_token = yield self.event_sources.get_current_token() now_token, ephemeral_by_room = yield self.ephemeral_by_room( sync_config, now_token ) presence_stream = self.event_sources.sources["presence"] # TODO (mjark): This looks wrong, shouldn't we be getting the presence # UP to the present rather than after the present? pagination_config = PaginationConfig(from_token=now_token) presence, _ = yield presence_stream.get_pagination_rows( user=sync_config.user, pagination_config=pagination_config.get_source_config("presence"), key=None ) membership_list = (Membership.INVITE, Membership.JOIN) if sync_config.filter_collection.include_leave: membership_list += (Membership.LEAVE, Membership.BAN) room_list = yield self.store.get_rooms_for_user_where_membership_is( user_id=sync_config.user.to_string(), membership_list=membership_list ) account_data, account_data_by_room = ( yield self.store.get_account_data_for_user( sync_config.user.to_string() ) ) tags_by_room = yield self.store.get_tags_for_user( sync_config.user.to_string() ) joined = [] invited = [] archived = [] deferreds = [] room_list_chunks = [room_list[i:i + 10] for i in xrange(0, len(room_list), 10)] for room_list_chunk in room_list_chunks: for event in room_list_chunk: if event.membership == Membership.JOIN: room_sync_deferred = preserve_fn( self.full_state_sync_for_joined_room )( room_id=event.room_id, sync_config=sync_config, now_token=now_token, timeline_since_token=timeline_since_token, ephemeral_by_room=ephemeral_by_room, tags_by_room=tags_by_room, account_data_by_room=account_data_by_room, ) room_sync_deferred.addCallback(joined.append) deferreds.append(room_sync_deferred) elif event.membership == Membership.INVITE: invite = yield self.store.get_event(event.event_id) invited.append(InvitedSyncResult( room_id=event.room_id, invite=invite, )) elif event.membership in (Membership.LEAVE, Membership.BAN): leave_token = now_token.copy_and_replace( "room_key", "s%d" % (event.stream_ordering,) ) room_sync_deferred = preserve_fn( self.full_state_sync_for_archived_room )( sync_config=sync_config, room_id=event.room_id, leave_event_id=event.event_id, leave_token=leave_token, timeline_since_token=timeline_since_token, tags_by_room=tags_by_room, account_data_by_room=account_data_by_room, ) room_sync_deferred.addCallback(archived.append) deferreds.append(room_sync_deferred) yield defer.gatherResults( deferreds, consumeErrors=True ).addErrback(unwrapFirstError) account_data_for_user = sync_config.filter_collection.filter_account_data( self.account_data_for_user(account_data) ) presence = sync_config.filter_collection.filter_presence( presence ) defer.returnValue(SyncResult( presence=presence, account_data=account_data_for_user, joined=joined, invited=invited, archived=archived, next_batch=now_token, ))
async def get_messages( self, requester: Requester, room_id: str, pagin_config: PaginationConfig, as_client_event: bool = True, event_filter: Optional[Filter] = None, ) -> Dict[str, Any]: """Get messages in a room. Args: requester: The user requesting messages. room_id: The room they want messages from. pagin_config: The pagination config rules to apply, if any. as_client_event: True to get events in client-server format. event_filter: Filter to apply to results or None Returns: Pagination API results """ user_id = requester.user.to_string() if pagin_config.from_token: room_token = pagin_config.from_token.room_key else: pagin_config.from_token = ( self.hs.get_event_sources().get_current_token_for_pagination()) room_token = pagin_config.from_token.room_key room_token = RoomStreamToken.parse(room_token) pagin_config.from_token = pagin_config.from_token.copy_and_replace( "room_key", str(room_token)) source_config = pagin_config.get_source_config("room") with await self.pagination_lock.read(room_id): ( membership, member_event_id, ) = await self.auth.check_user_in_room_or_world_readable( room_id, user_id, allow_departed_users=True) if source_config.direction == "b": # if we're going backwards, we might need to backfill. This # requires that we have a topo token. if room_token.topological: curr_topo = room_token.topological else: curr_topo = await self.store.get_current_topological_token( room_id, room_token.stream) if membership == Membership.LEAVE: # If they have left the room then clamp the token to be before # they left the room, to save the effort of loading from the # database. # This is only None if the room is world_readable, in which # case "JOIN" would have been returned. assert member_event_id leave_token = await self.store.get_topological_token_for_event( member_event_id) if RoomStreamToken.parse( leave_token).topological < curr_topo: source_config.from_key = str(leave_token) await self.hs.get_handlers().federation_handler.maybe_backfill( room_id, curr_topo, limit=source_config.limit, ) events, next_key = await self.store.paginate_room_events( room_id=room_id, from_key=source_config.from_key, to_key=source_config.to_key, direction=source_config.direction, limit=source_config.limit, event_filter=event_filter, ) next_token = pagin_config.from_token.copy_and_replace( "room_key", next_key) if events: if event_filter: events = event_filter.filter(events) events = await filter_events_for_client( self.storage, user_id, events, is_peeking=(member_event_id is None)) if not events: return { "chunk": [], "start": pagin_config.from_token.to_string(), "end": next_token.to_string(), } state = None if event_filter and event_filter.lazy_load_members( ) and len(events) > 0: # TODO: remove redundant members # FIXME: we also care about invite targets etc. state_filter = StateFilter.from_types( (EventTypes.Member, event.sender) for event in events) state_ids = await self.state_store.get_state_ids_for_event( events[0].event_id, state_filter=state_filter) if state_ids: state_dict = await self.store.get_events( list(state_ids.values())) state = state_dict.values() time_now = self.clock.time_msec() chunk = { "chunk": (await self._event_serializer.serialize_events( events, time_now, as_client_event=as_client_event)), "start": pagin_config.from_token.to_string(), "end": next_token.to_string(), } if state: chunk["state"] = await self._event_serializer.serialize_events( state, time_now, as_client_event=as_client_event) return chunk
def snapshot_all_rooms(self, user_id=None, pagin_config=None, feedback=False, as_client_event=True): """Retrieve a snapshot of all rooms the user is invited or has joined. This snapshot may include messages for all rooms where the user is joined, depending on the pagination config. Args: user_id (str): The ID of the user making the request. pagin_config (synapse.api.streams.PaginationConfig): The pagination config used to determine how many messages *PER ROOM* to return. feedback (bool): True to get feedback along with these messages. as_client_event (bool): True to get events in client-server format. Returns: A list of dicts with "room_id" and "membership" keys for all rooms the user is currently invited or joined in on. Rooms where the user is joined on, may return a "messages" key with messages, depending on the specified PaginationConfig. """ room_list = yield self.store.get_rooms_for_user_where_membership_is( user_id=user_id, membership_list=[Membership.INVITE, Membership.JOIN] ) user = UserID.from_string(user_id) rooms_ret = [] now_token = yield self.hs.get_event_sources().get_current_token() presence_stream = self.hs.get_event_sources().sources["presence"] pagination_config = PaginationConfig(from_token=now_token) presence, _ = yield presence_stream.get_pagination_rows( user, pagination_config.get_source_config("presence"), None ) public_room_ids = yield self.store.get_public_room_ids() limit = pagin_config.limit if limit is None: limit = 10 @defer.inlineCallbacks def handle_room(event): d = { "room_id": event.room_id, "membership": event.membership, "visibility": ( "public" if event.room_id in public_room_ids else "private" ), } if event.membership == Membership.INVITE: d["inviter"] = event.sender rooms_ret.append(d) if event.membership != Membership.JOIN: return try: (messages, token), current_state = yield defer.gatherResults( [ self.store.get_recent_events_for_room( event.room_id, limit=limit, end_token=now_token.room_key, ), self.state_handler.get_current_state( event.room_id ), ] ).addErrback(unwrapFirstError) start_token = now_token.copy_and_replace("room_key", token[0]) end_token = now_token.copy_and_replace("room_key", token[1]) time_now = self.clock.time_msec() d["messages"] = { "chunk": [ serialize_event(m, time_now, as_client_event) for m in messages ], "start": start_token.to_string(), "end": end_token.to_string(), } d["state"] = [ serialize_event(c, time_now, as_client_event) for c in current_state.values() ] except: logger.exception("Failed to get snapshot") yield defer.gatherResults( [handle_room(e) for e in room_list], consumeErrors=True ).addErrback(unwrapFirstError) ret = { "rooms": rooms_ret, "presence": presence, "end": now_token.to_string() } defer.returnValue(ret)
def snapshot_all_rooms(self, user_id=None, pagin_config=None, as_client_event=True, include_archived=False): """Retrieve a snapshot of all rooms the user is invited or has joined. This snapshot may include messages for all rooms where the user is joined, depending on the pagination config. Args: user_id (str): The ID of the user making the request. pagin_config (synapse.api.streams.PaginationConfig): The pagination config used to determine how many messages *PER ROOM* to return. as_client_event (bool): True to get events in client-server format. include_archived (bool): True to get rooms that the user has left Returns: A list of dicts with "room_id" and "membership" keys for all rooms the user is currently invited or joined in on. Rooms where the user is joined on, may return a "messages" key with messages, depending on the specified PaginationConfig. """ memberships = [Membership.INVITE, Membership.JOIN] if include_archived: memberships.append(Membership.LEAVE) room_list = yield self.store.get_rooms_for_user_where_membership_is( user_id=user_id, membership_list=memberships ) user = UserID.from_string(user_id) rooms_ret = [] now_token = yield self.hs.get_event_sources().get_current_token() presence_stream = self.hs.get_event_sources().sources["presence"] pagination_config = PaginationConfig(from_token=now_token) presence, _ = yield presence_stream.get_pagination_rows( user, pagination_config.get_source_config("presence"), None ) receipt_stream = self.hs.get_event_sources().sources["receipt"] receipt, _ = yield receipt_stream.get_pagination_rows( user, pagination_config.get_source_config("receipt"), None ) tags_by_room = yield self.store.get_tags_for_user(user_id) public_room_ids = yield self.store.get_public_room_ids() limit = pagin_config.limit if limit is None: limit = 10 @defer.inlineCallbacks def handle_room(event): d = { "room_id": event.room_id, "membership": event.membership, "visibility": ( "public" if event.room_id in public_room_ids else "private" ), } if event.membership == Membership.INVITE: time_now = self.clock.time_msec() d["inviter"] = event.sender invite_event = yield self.store.get_event(event.event_id) d["invite"] = serialize_event(invite_event, time_now, as_client_event) rooms_ret.append(d) if event.membership not in (Membership.JOIN, Membership.LEAVE): return try: if event.membership == Membership.JOIN: room_end_token = now_token.room_key deferred_room_state = self.state_handler.get_current_state( event.room_id ) elif event.membership == Membership.LEAVE: room_end_token = "s%d" % (event.stream_ordering,) deferred_room_state = self.store.get_state_for_events( [event.event_id], None ) deferred_room_state.addCallback( lambda states: states[event.event_id] ) (messages, token), current_state = yield defer.gatherResults( [ self.store.get_recent_events_for_room( event.room_id, limit=limit, end_token=room_end_token, ), deferred_room_state, ] ).addErrback(unwrapFirstError) messages = yield self._filter_events_for_client( user_id, messages ) start_token = now_token.copy_and_replace("room_key", token[0]) end_token = now_token.copy_and_replace("room_key", token[1]) time_now = self.clock.time_msec() d["messages"] = { "chunk": [ serialize_event(m, time_now, as_client_event) for m in messages ], "start": start_token.to_string(), "end": end_token.to_string(), } d["state"] = [ serialize_event(c, time_now, as_client_event) for c in current_state.values() ] account_data = [] tags = tags_by_room.get(event.room_id) if tags: account_data.append({ "type": "m.tag", "content": {"tags": tags}, }) d["account_data"] = account_data except: logger.exception("Failed to get snapshot") # Only do N rooms at once n = 5 d_list = [handle_room(e) for e in room_list] for i in range(0, len(d_list), n): yield defer.gatherResults( d_list[i:i + n], consumeErrors=True ).addErrback(unwrapFirstError) ret = { "rooms": rooms_ret, "presence": presence, "receipts": receipt, "end": now_token.to_string(), } defer.returnValue(ret)