-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #122 from sosanzma/documentation
Documentation
- Loading branch information
Showing
1 changed file
with
89 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,29 +12,35 @@ Presence Manager | |
Every SPADE agent has a property to manage its presence. This manager is called ``presence`` and implements all the | ||
methods and attributes to manage an agent's presence notification. | ||
|
||
A presence object has three attributes: the **state**, the **status** and the **priority**. Let's see every one of them: | ||
A presence object encapsulates its information in a ``PresenceInfo`` class that contains four main attributes: | ||
**type**, **show**, **status** and **priority**. These attributes work together to provide a complete representation | ||
of an agent's presence state. Let's see every one of them: | ||
|
||
State | ||
^^^^^ | ||
|
||
The state of a presence message shows if the agent is **Available** or **Unavailable**. This means that the agent is | ||
connected to an XMPP server or not. This is very useful to know, before contacting an agent, if it is available to | ||
receive a message in real time or not. The availability state is a boolean attribute. | ||
receive a message in real time or not. | ||
|
||
The state is now managed through the ``PresenceType`` enumeration which can have the following values: | ||
|
||
- ``PresenceType.AVAILABLE``: The agent is connected and can receive messages | ||
- ``PresenceType.UNAVAILABLE``: The agent is disconnected or not able to receive messages | ||
|
||
Besides, the *State* has also an attribute to give additional information about *how available* the contact is. This is | ||
the **Show** attribute. The *Show* attribute belongs to the | ||
class ``aioxmpp.PresenceShow`` and can take the following values: | ||
the **Show** attribute. The *Show* attribute is managed through the ``PresenceShow`` enumeration and can take the following values: | ||
|
||
- ``PresenceShow.CHAT``: The entity or resource is actively interested in chatting (i.e. receiving messages). | ||
- ``PresenceShow.AWAY``: The entity or resource is temporarily away, however it can receive messages (they will probably be attended later) | ||
- ``PresenceShow.XA``: The entity or resource is away for an extended period (xa = "*eXtended Away*"). | ||
- ``PresenceShow.EXTENDED_AWAY``: The entity or resource is away for an extended period (xa = "*eXtended Away*"). | ||
- ``PresenceShow.DND``: The entity or resource is busy (dnd = "*Do Not Disturb*"). | ||
- ``PresenceShow.NONE``: Signifies absence of the *Show* element. Used for unavailable states. | ||
|
||
|
||
An agent can set its availability and show property:: | ||
|
||
agent.presence.set_available(availability=True, show=PresenceShow.CHAT) | ||
agent.presence.set_presence(presence_type=PresenceType.AVAILABLE, show=PresenceShow.CHAT) | ||
|
||
|
||
.. warning:: If you set your presence to *unavailable* the only possible show state is ``PresenceShow.NONE``. | ||
|
@@ -45,18 +51,16 @@ A short method to set *unavailability* is:: | |
agent.presence.set_unavailable() | ||
|
||
|
||
|
||
To get your presence state:: | ||
|
||
my_state = agent.presence.state # Gets your current PresenceState instance. | ||
|
||
agent.presence.is_available() # Returns a boolean to report wether the agent is available or not | ||
presence_info = agent.presence.get_presence() # Gets your current presence information | ||
|
||
my_show = agent.presence.state.show # Gets your current PresenceShow info. | ||
agent.presence.is_available() # Returns a boolean to report whether the agent is available or not | ||
|
||
current_show = agent.presence.get_show() # Gets your current PresenceShow info | ||
|
||
|
||
.. tip:: If no *Show* element is provided, the entity is assumed to be online and available. | ||
.. tip:: If no *Show* element is provided, the entity is assumed to be online and available with ``PresenceShow.NONE``. | ||
|
||
Status | ||
^^^^^^ | ||
|
@@ -66,23 +70,20 @@ your current status which is broadcasted when the client connects and when the p | |
|
||
An agent can get its status as follows:: | ||
|
||
>> agent.presence.status | ||
{None: "Working..."} | ||
>> agent.presence.get_status() | ||
"Working..." | ||
|
||
The status can be set when defining a new presence:: | ||
|
||
.. warning:: | ||
It should be noted that the status is returned as a dict with a ``None`` key. This is because the status supports | ||
different languages. If you set the status as a string it is set as the default status (and stored with the key | ||
``None``. If you want to set the status in different languages you can specify it using the keys:: | ||
|
||
>> agent.presence.status | ||
{ | ||
None: "Working...", | ||
"es": "Trabajando...", | ||
"fr": "Travailler..." | ||
} | ||
|
||
agent.presence.set_presence( | ||
presence_type=PresenceType.AVAILABLE, | ||
show=PresenceShow.CHAT, | ||
status="Working on SPADE agents" | ||
) | ||
|
||
.. note:: | ||
In this new version the status is managed as a simple string value, making it more straightforward | ||
to set and retrieve status information. | ||
|
||
Priority | ||
^^^^^^^^ | ||
|
@@ -91,70 +92,74 @@ Since an agent (and indeed any XMPP user) can have multiple connections to an XM | |
each of those connections to establish the level of each one. The value must be an integer between -128 and +127. | ||
|
||
|
||
|
||
Setting the Presence | ||
^^^^^^^^^^^^^^^^^^^^ | ||
|
||
There is a method that can be used to set the three presence attributes. Since they are all optional, you can change any | ||
There is a method that can be used to set all presence attributes. Since they are all optional, you can change any | ||
of the attribute values with every call:: | ||
|
||
agent.presence.set_presence( | ||
state=PresenceState(True, PresenceShow.CHAT), # available and interested in chatting | ||
status="Lunch", | ||
priority=2 | ||
presence_type=PresenceType.AVAILABLE, # set availability | ||
show=PresenceShow.CHAT, # show status | ||
status="Lunch", # status message | ||
priority=2 # connection priority | ||
) | ||
|
||
|
||
|
||
Availability handlers | ||
--------------------- | ||
To get notified when a contact gets available or unavailable you can override the ``on_available`` and ``on_unavailable`` | ||
handlers. As you can see in the next example, these handlers receive the peer jid of the contact and the *stanza* of | ||
the XMPP Presence message (class ``aioxmpp.Presence``) which contains all its presence information (availability, show, | ||
state, priority, ...):: | ||
handlers. These handlers now receive the peer jid of the contact, the current presence information, and the last known | ||
presence state:: | ||
|
||
def my_on_available_handler(peer_jid, stanza): | ||
print(f"My friend {peer_jid} is now available with show {stanza.show}") | ||
|
||
agent.presence.on_available = my_on_available_handler | ||
def my_on_available_handler(peer_jid, presence_info, last_presence): | ||
print(f"My friend {peer_jid} is now {presence_info.show.value}") | ||
if last_presence: | ||
print(f"Previous state was: {last_presence.show.value}") | ||
|
||
agent.presence.on_available = my_on_available_handler | ||
|
||
Contact List | ||
------------ | ||
|
||
Every contact to whom you are subscribed to appears in your *contact list*. You can use the ``get_contacts()`` method to | ||
get the full list of your contacts. This method returns a ``dict`` where the keys are the ``JID`` of your contacts and the | ||
values are an dict that show the information you have about each of your contacts (presence, name, approved, | ||
groups, ask, subscription, ...). Note that the "presence" value is an ``aioxmpp.Presence`` object with the latest updated | ||
information about the contact's presence. | ||
values are ``Contact`` objects that contain all the information about each contact (presence info, name, subscription status, | ||
groups, etc.). The contact's current presence is managed through the ``PresenceInfo`` class. | ||
|
||
Example:: | ||
|
||
>>> contacts = agent.presence.get_contacts() | ||
>>> contacts[myfriend_jid] | ||
{ | ||
'presence': Presence(type_=PresenceType.AVAILABLE), | ||
'subscription': 'both', | ||
'name': 'My Friend', | ||
'approved': True | ||
} | ||
Contact( | ||
JID: [email protected], | ||
Name: My Friend, | ||
Presence: PresenceInfo(Type: PresenceType.AVAILABLE, Show: PresenceShow.CHAT, Status: "Working", Priority: 10) | ||
) | ||
|
||
You can also get a specific contact using:: | ||
|
||
contact = agent.presence.get_contact("[email protected]") | ||
|
||
.. warning:: An empty contact list will return an empty dictionary. | ||
|
||
.. note:: The Contact class provides helper methods like ``is_available()`` and ``is_subscribed()`` to easily check contact status. | ||
|
||
|
||
Subscribing and unsubscribing to contacts | ||
----------------------------------------- | ||
|
||
To subscribe and unsubscribe to/from a contact you have to send a special presence message asking for that subscription. | ||
SPADE helps you by providing some methods that send these special messages:: | ||
|
||
|
||
# Send a subscription request to a peer_jid | ||
agent.presence.subscribe(peer_jid) | ||
|
||
# Send an unsubscribe request to a peer_jid | ||
agent.presence.unsubscribe(peer_jid) | ||
|
||
# Approve a subscription request | ||
agent.presence.approve_subscription(peer_jid) | ||
|
||
Subscription handlers | ||
^^^^^^^^^^^^^^^^^^^^^ | ||
|
@@ -167,15 +172,15 @@ There are four handlers that you can override to manage these kind of messages: | |
|
||
def my_on_subscribe_callback(peer_jid): | ||
if i_want_to_approve_request: | ||
self.approve(peer_jid) | ||
self.presence.approve_subscription(peer_jid) | ||
|
||
agent.presence.on_subscribe = my_on_subscribe_callback | ||
|
||
|
||
.. note:: In the previous example you can see also how to approve a subscription request by using the ``approve`` method. | ||
|
||
.. tip:: If you want to automatically approve all subscription requests you can set the ``PresenceManager.approve_all`` flag to ``True``. | ||
.. note:: In the previous example you can see also how to approve a subscription request by using the ``approve_subscription`` method. | ||
|
||
.. tip:: If you want to automatically approve all subscription requests you can set the ``presence.approve_all`` flag to ``True``. | ||
|
||
Example | ||
------- | ||
|
@@ -188,54 +193,65 @@ This is an example that shows in a practical way the presence module:: | |
import spade | ||
from spade.agent import Agent | ||
from spade.behaviour import OneShotBehaviour | ||
from spade.presence import PresenceType, PresenceShow, PresenceInfo | ||
|
||
|
||
class Agent1(Agent): | ||
async def setup(self): | ||
print("Agent {} running".format(self.name)) | ||
print(f"Agent {self.name} running") | ||
self.add_behaviour(self.Behav1()) | ||
|
||
class Behav1(OneShotBehaviour): | ||
def on_available(self, jid, stanza): | ||
print("[{}] Agent {} is available.".format(self.agent.name, jid.split("@")[0])) | ||
def on_available(self, peer_jid, presence_info, last_presence): | ||
print(f"[{self.agent.name}] Agent {peer_jid.split('@')[0]} is {presence_info.show.value}") | ||
|
||
def on_subscribed(self, jid): | ||
print("[{}] Agent {} has accepted the subscription.".format(self.agent.name, jid.split("@")[0])) | ||
print("[{}] Contacts List: {}".format(self.agent.name, self.agent.presence.get_contacts())) | ||
def on_subscribed(self, peer_jid): | ||
print(f"[{self.agent.name}] Agent {peer_jid.split('@')[0]} has accepted the subscription") | ||
contacts = self.agent.presence.get_contacts() | ||
print(f"[{self.agent.name}] Contacts List: {contacts}") | ||
|
||
def on_subscribe(self, jid): | ||
print("[{}] Agent {} asked for subscription. Let's aprove it.".format(self.agent.name, jid.split("@")[0])) | ||
self.presence.approve(jid) | ||
def on_subscribe(self, peer_jid): | ||
print(f"[{self.agent.name}] Agent {peer_jid.split('@')[0]} asked for subscription. Let's approve it") | ||
self.presence.approve_subscription(peer_jid) | ||
|
||
async def run(self): | ||
self.presence.on_subscribe = self.on_subscribe | ||
self.presence.on_subscribed = self.on_subscribed | ||
self.presence.on_available = self.on_available | ||
|
||
self.presence.set_available() | ||
self.presence.set_presence( | ||
presence_type=PresenceType.AVAILABLE, | ||
show=PresenceShow.CHAT, | ||
status="Ready to chat" | ||
) | ||
self.presence.subscribe(self.agent.jid2) | ||
|
||
|
||
class Agent2(Agent): | ||
async def setup(self): | ||
print("Agent {} running".format(self.name)) | ||
print(f"Agent {self.name} running") | ||
self.add_behaviour(self.Behav2()) | ||
|
||
class Behav2(OneShotBehaviour): | ||
def on_available(self, jid, stanza): | ||
print("[{}] Agent {} is available.".format(self.agent.name, jid.split("@")[0])) | ||
def on_available(self, peer_jid, presence_info, last_presence): | ||
print(f"[{self.agent.name}] Agent {peer_jid.split('@')[0]} is {presence_info.show.value}") | ||
|
||
def on_subscribed(self, jid): | ||
print("[{}] Agent {} has accepted the subscription.".format(self.agent.name, jid.split("@")[0])) | ||
print("[{}] Contacts List: {}".format(self.agent.name, self.agent.presence.get_contacts())) | ||
def on_subscribed(self, peer_jid): | ||
print(f"[{self.agent.name}] Agent {peer_jid.split('@')[0]} has accepted the subscription") | ||
contacts = self.agent.presence.get_contacts() | ||
print(f"[{self.agent.name}] Contacts List: {contacts}") | ||
|
||
def on_subscribe(self, jid): | ||
print("[{}] Agent {} asked for subscription. Let's aprove it.".format(self.agent.name, jid.split("@")[0])) | ||
self.presence.approve(jid) | ||
self.presence.subscribe(jid) | ||
def on_subscribe(self, peer_jid): | ||
print(f"[{self.agent.name}] Agent {peer_jid.split('@')[0]} asked for subscription. Let's approve it") | ||
self.presence.approve_subscription(peer_jid) | ||
self.presence.subscribe(peer_jid) | ||
|
||
async def run(self): | ||
self.presence.set_available() | ||
self.presence.set_presence( | ||
presence_type=PresenceType.AVAILABLE, | ||
show=PresenceShow.CHAT, | ||
status="Ready to chat" | ||
) | ||
self.presence.on_subscribe = self.on_subscribe | ||
self.presence.on_subscribed = self.on_subscribed | ||
self.presence.on_available = self.on_available | ||
|
@@ -266,5 +282,3 @@ This is an example that shows in a practical way the presence module:: | |
|
||
if __name__ == "__main__": | ||
spade.run(main()) | ||
|
||
|