Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Format presence events on the edges instead of reformatting them multiple times #2013

Merged
merged 5 commits into from
Mar 15, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions synapse/api/filtering.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from synapse.api.errors import SynapseError
from synapse.storage.presence import UserPresenceState
from synapse.types import UserID, RoomID

from twisted.internet import defer
Expand Down Expand Up @@ -253,19 +254,30 @@ def check(self, event):
Returns:
bool: True if the event matches
"""
sender = event.get("sender", None)
if not sender:
# Presence events have their 'sender' in content.user_id
content = event.get("content")
# account_data has been allowed to have non-dict content, so check type first
if isinstance(content, dict):
sender = content.get("user_id")
if isinstance(event, UserPresenceState):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you comment what's going on here? Just a quick comment to explain why both branches are necessary would be enough.

sender = event.user_id
room_id = None
ev_type = "m.presence"
is_url = False
else:
sender = event.get("sender", None)
if not sender:
# Presence events have their 'sender' in content.user_id
content = event.get("content")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still needed? It looks like it is presence specific and presence should be handled as UserPresenceState tuples/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does account data have a sender?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember tbh. I think only presence has a content.user_id though.

# account_data has been allowed to have non-dict content, so
# check type first
if isinstance(content, dict):
sender = content.get("user_id")

room_id = event.get("room_id", None)
ev_type = event.get("type", None)
is_url = "url" in event.get("content", {})

return self.check_fields(
event.get("room_id", None),
room_id,
sender,
event.get("type", None),
"url" in event.get("content", {})
ev_type,
is_url,
)

def check_fields(self, room_id, sender, event_type, contains_url):
Expand Down
11 changes: 10 additions & 1 deletion synapse/handlers/initial_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from synapse.api.errors import AuthError, Codes
from synapse.events.utils import serialize_event
from synapse.events.validator import EventValidator
from synapse.handlers.presence import format_user_presence_state
from synapse.streams.config import PaginationConfig
from synapse.types import (
UserID, StreamToken,
Expand Down Expand Up @@ -225,9 +226,17 @@ def handle_room(event):
"content": content,
})

now = self.clock.time_msec()

ret = {
"rooms": rooms_ret,
"presence": presence,
"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(),
Expand Down
30 changes: 15 additions & 15 deletions synapse/handlers/presence.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,9 +719,7 @@ def get_states(self, target_user_ids, as_event=False):
for state in updates
])
else:
defer.returnValue([
format_user_presence_state(state, now) for state in updates
])
defer.returnValue(updates)

@defer.inlineCallbacks
def set_state(self, target_user, state, ignore_status_msg=False):
Expand Down Expand Up @@ -795,6 +793,9 @@ def get_presence_list(self, observer_user, accepted=None):
as_event=False,
)

now = self.clock.time_msec()
results[:] = [format_user_presence_state(r, now) for r in results]

is_accepted = {
row["observed_user_id"]: row["accepted"] for row in presence_list
}
Expand Down Expand Up @@ -847,6 +848,7 @@ def invite_presence(self, observed_user, observer_user):
)

state_dict = yield self.get_state(observed_user, as_event=False)
state_dict = format_user_presence_state(state_dict, self.clock.time_msec())

self.federation.send_edu(
destination=observer_user.domain,
Expand Down Expand Up @@ -979,14 +981,15 @@ def should_notify(old_state, new_state):
return False


def format_user_presence_state(state, now):
def format_user_presence_state(state, now, include_user_id=True):
"""Convert UserPresenceState to a format that can be sent down to clients
and to other servers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment to explain why the "user_id" is optional? something like

The "user_id" is optional so that this function can be used to format presence 
updates for client /sync responses and for federation /send requests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(done)

"""
content = {
"presence": state.state,
"user_id": state.user_id,
}
if include_user_id:
content["user_id"] = state.user_id
if state.last_active_ts:
content["last_active_ago"] = now - state.last_active_ts
if state.status_msg and state.state != PresenceState.OFFLINE:
Expand Down Expand Up @@ -1073,16 +1076,13 @@ def get_new_events(self, user, from_key, room_ids=None, include_offline=True,

updates = yield presence.current_state_for_users(user_ids_changed)

now = self.clock.time_msec()

defer.returnValue(([
{
"type": "m.presence",
"content": format_user_presence_state(s, now),
}
for s in updates.values()
if include_offline or s.state != PresenceState.OFFLINE
], max_token))
if include_offline:
defer.returnValue((updates.values(), max_token))
else:
defer.returnValue(([
s for s in updates.itervalues()
if s.state != PresenceState.OFFLINE
], max_token))

def get_current_key(self):
return self.store.get_current_presence_token()
Expand Down
14 changes: 7 additions & 7 deletions synapse/handlers/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,14 +721,14 @@ def _generate_sync_entry_for_presence(self, sync_result_builder, newly_joined_ro
extra_users_ids.update(users)
extra_users_ids.discard(user.to_string())

states = yield self.presence_handler.get_states(
extra_users_ids,
as_event=True,
)
presence.extend(states)
if extra_users_ids:
states = yield self.presence_handler.get_states(
extra_users_ids,
)
presence.extend(states)

# Deduplicate the presence entries so that there's at most one per user
presence = {p["content"]["user_id"]: p for p in presence}.values()
# Deduplicate the presence entries so that there's at most one per user
presence = {p.user_id: p for p in presence}.values()

presence = sync_config.filter_collection.filter_presence(
presence
Expand Down
10 changes: 10 additions & 0 deletions synapse/notifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from twisted.internet import defer
from synapse.api.constants import EventTypes, Membership
from synapse.api.errors import AuthError
from synapse.handlers.presence import format_user_presence_state

from synapse.util import DeferredTimedOutError
from synapse.util.logutils import log_function
Expand Down Expand Up @@ -412,6 +413,15 @@ def check_for_updates(before_token, after_token):
new_events,
is_peeking=is_peeking,
)
elif name == "presence":
now = self.clock.time_msec()
new_events[:] = [
{
"type": "m.presence",
"content": format_user_presence_state(event, now),
}
for event in new_events
]

events.extend(new_events)
end_token = end_token.copy_and_replace(keyname, new_key)
Expand Down
3 changes: 3 additions & 0 deletions synapse/rest/client/v1/presence.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from synapse.api.errors import SynapseError, AuthError
from synapse.types import UserID
from synapse.handlers.presence import format_user_presence_state
from synapse.http.servlet import parse_json_object_from_request
from .base import ClientV1RestServlet, client_path_patterns

Expand All @@ -33,6 +34,7 @@ class PresenceStatusRestServlet(ClientV1RestServlet):
def __init__(self, hs):
super(PresenceStatusRestServlet, self).__init__(hs)
self.presence_handler = hs.get_presence_handler()
self.clock = hs.get_clock()

@defer.inlineCallbacks
def on_GET(self, request, user_id):
Expand All @@ -48,6 +50,7 @@ def on_GET(self, request, user_id):
raise AuthError(403, "You are not allowed to see their presence.")

state = yield self.presence_handler.get_state(target_user=user)
state = format_user_presence_state(state, self.clock.time_msec())

defer.returnValue((200, state))

Expand Down
19 changes: 13 additions & 6 deletions synapse/rest/client/v2_alpha/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from synapse.http.servlet import (
RestServlet, parse_string, parse_integer, parse_boolean
)
from synapse.handlers.presence import format_user_presence_state
from synapse.handlers.sync import SyncConfig
from synapse.types import StreamToken
from synapse.events.utils import (
Expand Down Expand Up @@ -194,12 +195,18 @@ def on_GET(self, request):
defer.returnValue((200, response_content))

def encode_presence(self, events, time_now):
formatted = []
for event in events:
event = copy.deepcopy(event)
event['sender'] = event['content'].pop('user_id')
formatted.append(event)
return {"events": formatted}
return {
"events": [
{
"type": "m.presence",
"sender": event.user_id,
"content": format_user_presence_state(
event, time_now, include_user_id=False
),
}
for event in events
]
}

def encode_joined(self, rooms, time_now, token_id, event_fields):
"""
Expand Down