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

Commit

Permalink
presence: draft interface for Directed Presence management
Browse files Browse the repository at this point in the history
  • Loading branch information
horazont committed Nov 11, 2018
1 parent 86e789a commit 20d416a
Showing 1 changed file with 216 additions and 0 deletions.
216 changes: 216 additions & 0 deletions aioxmpp/presence/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,159 @@ def handle_presence(self, st):
self.on_changed(st.from_, st)


class DirectedPresenceHandle:
"""
Represent a directed presence relationship with a peer.
Directed Presence is specified in :rfc:`6121` section 4.6. Since the users
server is not responsible for distributing presence updates to peers to
which the client has sent directed presence, special handling is needed.
(The only presence automatically sent by a client’s server to a peer which
has received directed presence is the
:attr:`~aioxmpp.PresenceType.UNAVAILABLE` presence which is created when
the client disconnects.)
.. note::
Directed presence relationships get
:meth:`unsubscribed <unsubscribe>` immediately when the stream is
destroyed. This is because the peer has received
:attr:`~aioxmpp.PresenceType.UNAVAILABLE` presence from the client’s
server.
.. autoattribute:: address
.. autoattribute:: muted
.. autoattribute:: presence_filter
.. automethod:: set_muted
.. automethod:: unsubscribe
.. automethod:: send_presence
"""

@property
def address(self) -> aioxmpp.JID:
"""
The address of the peer. This attribute is read-only.
To change the address of a peer,
:meth:`aioxmpp.PresenceServer.rebind_directed_presence` can be used.
"""

@property
def muted(self) -> bool:
"""
Flag to indicate whether the directed presence relationship is *muted*.
If the relationship is **not** muted, presence updates made through the
:class:`PresenceServer` will be unicast to the peer entity of the
relationship.
For a muted relationships, presence updates will *not* be automatically
sent to the peer.
The *muted* behaviour is useful if presence updates need to be managed
by a service for some reason.
This attribute is read-only. It must be modified through
:meth:`set_muted`.
"""

@property
def presence_filter(self):
"""
Optional callback which is invoked on the presence stanza before it is
sent.
This is called whenever a presence stanza is sent for this relationship
by the :class:`PresenceServer`. This is not invoked for presence stanzas
sent to the peer by other means (e.g. :meth:`aioxmpp.Client.send`).
If the :attr:`presence_filter` is not :data:`None`, it is called with
the presence stanza as its only argument. It must either return the
presence stanza, or :data:`None`. If it returns :data:`None`, the
presence stanza is not sent.
The callback operates on a copy of the presence stanza to prevent
modifications from leaking into other presence relationships; making a
copy inside the callback is not required or recommended.
"""

@presence_filter.setter
def presence_filter(self, new_callback):
pass

def set_muted(self, muted, *, send_update_now=True):
"""
Change the :attr:`muted` state of the relationship.
(This is not a setter to the property due to the additional options
which are available when changing the :attr:`muted` state.)
:param muted: The new muted state.
:type muted: :class:`bool`
:param send_update_now: Whether to send a presence update to the peer
immediately.
:type send_update_now: :class:`bool`
:raises RuntimeError: if the presence relationship has been destroyed
with :meth:`unsubscribe`
If `muted` is equal to :attr:`muted`, this method does nothing.
If `muted` is :data:`True`, the presence relationship will be muted.
`send_update_now` is ignored.
If `muted` is :data:`False`, the presence relationship will be unmuted.
If `send_update_now` is :data:`True`, the current presence is sent to
the peer immediately.
"""

def unsubscribe(self):
"""
Destroy the directed presence relationship.
The presence relationship becomes useless afterwards. Any additional
calls to the methods will result in :class:`RuntimeError`. No additional
updates wil be sent to the peer automatically (except, of course, if
a new relationship is created).
If the presence relationship is still active when the method is called,
:attr:`~aioxmpp.PresenceType.UNAVAILABLE` presence is sent to the peer
immediately. Otherwise, no stanza is sent. The stanza is passed through
the :attr:`presence_filter`.
.. note::
Directed presence relationships get unsubscribed immediately when
the stream is destroyed. This is because the peer has received
:attr:`~aioxmpp.PresenceType.UNAVAILABLE` presence from the client’s
server.
This operation is idempotent.
"""

def send_presence(self, stanza: aioxmpp.Presence):
"""
Send a presence stanza to the peer.
:param stanza: The stanza to send.
:type stanza: :class:`aioxmpp.Presence`
:raises RuntimeError: if the presence relationship has been destroyed
with :meth:`unsubscribe`
The type of the presence `stanza` must be either
:attr:`aioxmpp.PresenceType.AVAILABLE` or
:attr:`aioxmpp.PresenceType.UNAVAILABLE`.
The :meth:`presence_filter` is not invoked on this `stanza`; it is
assumed that the owner of the relationship takes care of setting the
`stanza` up in the way it is needed.
"""


class PresenceServer(aioxmpp.service.Service):
"""
Manage the presence broadcast by the client.
Expand Down Expand Up @@ -438,3 +591,66 @@ def resend_presence(self):

if self.client.established:
return self.client.enqueue(self.make_stanza())

def subscribe_peer_directed(self,
peer: aioxmpp.JID,
muted: bool = False):
"""
Create a directed presence relationship with a peer.
:param peer: The address of the peer. This can be a full or bare JID.
:type peer: :class:`aioxmpp.JID`
:param muted: Flag to create the relationship in muted state.
:type muted: :class:`bool`
:rtype: :class:`DirectedPresenceHandle`
:return: The new directed presence handle.
`peer` is the address of the peer which is going to receive directed
presence. For each bare JID, there can only exist either a single bare
JID directed presence relationship, or zero or more full JID directed
presence relationships. It is not possible to have a bare JID directed
presence relationship and a full JID directed presence relationship for
the same bare JID.
If `muted` is :data:`False` (the default), the current presence is
unicast to `peer` when the relationship is created and the relationship
is created with :attr:`~.DirectedPresenceHandle.muted` set to
:data:`False`.
If `muted` is :data:`True`, no presence is sent when the relationship is
created, and it is created with :attr:`~.DirectedPresenceHandle.muted`
set to :data:`True`.
If the user of this method needs to set a
:attr:`~.DirectedPresenceHandle.presence_filter` on the relationship,
creating it with `muted` set to true is the only way to achieve this
before the initial directed presence to the peer is sent.
The newly created handle is returned.
"""

def rebind_directed_presence(self,
relationship: DirectedPresenceHandle,
new_peer: aioxmpp.JID):
"""
Modify the peer of an existing directed presence relationship.
:param relationship: The relationship to operate on.
:type relationship: :class:`DirectedPresenceHandle`
:param new_peer: The new destination address of the relationship.
:type new_peer: :class:`aioxmpp.JID`
:raises RuntimeError: if the `relationship` has been destroyed with
:meth:`~DirectedPresenceHandle.unsubscribe` already.
:raises ValueError: if another relationship for `new_peer` exists and
is active
This changes the peer :attr:`~.DirectedPresenceHandle.address` of the
`relationship` to `new_peer`. The conditions for peer addresses of
directed presence relationships as described in
:meth:`subscribe_peer_directed` are enforced in this operation. If they
are violated, :class:`ValueError` is raised.
If the `relationship` has already been closed/destroyed using
:meth:`~DirectedPresenceHandle.unsubscribe`, :class:`RuntimeError` is
raised.
"""

0 comments on commit 20d416a

Please sign in to comment.