Rename to slixmpp
[slixmpp.git] / slixmpp / plugins / xep_0012 / last_activity.py
blob2fef9ca8a46e0a8867c84b7f779516d9153d1330
1 """
2 Slixmpp: The Slick XMPP Library
3 Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
4 This file is part of Slixmpp.
6 See the file LICENSE for copying permission.
7 """
9 import logging
10 from datetime import datetime, timedelta
12 from slixmpp.plugins import BasePlugin, register_plugin
13 from slixmpp import Iq
14 from slixmpp.exceptions import XMPPError
15 from slixmpp.xmlstream import JID, register_stanza_plugin
16 from slixmpp.xmlstream.handler import Callback
17 from slixmpp.xmlstream.matcher import StanzaPath
18 from slixmpp.plugins.xep_0012 import stanza, LastActivity
21 log = logging.getLogger(__name__)
24 class XEP_0012(BasePlugin):
26 """
27 XEP-0012 Last Activity
28 """
30 name = 'xep_0012'
31 description = 'XEP-0012: Last Activity'
32 dependencies = set(['xep_0030'])
33 stanza = stanza
35 def plugin_init(self):
36 register_stanza_plugin(Iq, LastActivity)
38 self._last_activities = {}
40 self.xmpp.register_handler(
41 Callback('Last Activity',
42 StanzaPath('iq@type=get/last_activity'),
43 self._handle_get_last_activity))
45 self.api.register(self._default_get_last_activity,
46 'get_last_activity',
47 default=True)
48 self.api.register(self._default_set_last_activity,
49 'set_last_activity',
50 default=True)
51 self.api.register(self._default_del_last_activity,
52 'del_last_activity',
53 default=True)
55 def plugin_end(self):
56 self.xmpp.remove_handler('Last Activity')
57 self.xmpp['xep_0030'].del_feature(feature='jabber:iq:last')
59 def session_bind(self, jid):
60 self.xmpp['xep_0030'].add_feature('jabber:iq:last')
62 def begin_idle(self, jid=None, status=None):
63 self.set_last_activity(jid, 0, status)
65 def end_idle(self, jid=None):
66 self.del_last_activity(jid)
68 def start_uptime(self, status=None):
69 self.set_last_activity(jid, 0, status)
71 def set_last_activity(self, jid=None, seconds=None, status=None):
72 self.api['set_last_activity'](jid, args={
73 'seconds': seconds,
74 'status': status})
76 def del_last_activity(self, jid):
77 self.api['del_last_activity'](jid)
79 def get_last_activity(self, jid, local=False, ifrom=None, block=True,
80 timeout=None, callback=None):
81 if jid is not None and not isinstance(jid, JID):
82 jid = JID(jid)
84 if self.xmpp.is_component:
85 if jid.domain == self.xmpp.boundjid.domain:
86 local = True
87 else:
88 if str(jid) == str(self.xmpp.boundjid):
89 local = True
90 jid = jid.full
92 if local or jid in (None, ''):
93 log.debug("Looking up local last activity data for %s", jid)
94 return self.api['get_last_activity'](jid, None, ifrom, None)
96 iq = self.xmpp.Iq()
97 iq['from'] = ifrom
98 iq['to'] = jid
99 iq['type'] = 'get'
100 iq.enable('last_activity')
101 return iq.send(timeout=timeout,
102 block=block,
103 callback=callback)
105 def _handle_get_last_activity(self, iq):
106 log.debug("Received last activity query from " + \
107 "<%s> to <%s>.", iq['from'], iq['to'])
108 reply = self.api['get_last_activity'](iq['to'], None, iq['from'], iq)
109 reply.send()
111 # =================================================================
112 # Default in-memory implementations for storing last activity data.
113 # =================================================================
115 def _default_set_last_activity(self, jid, node, ifrom, data):
116 seconds = data.get('seconds', None)
117 if seconds is None:
118 seconds = 0
120 status = data.get('status', None)
121 if status is None:
122 status = ''
124 self._last_activities[jid] = {
125 'seconds': datetime.now() - timedelta(seconds=seconds),
126 'status': status}
128 def _default_del_last_activity(self, jid, node, ifrom, data):
129 if jid in self._last_activities:
130 del self._last_activities[jid]
132 def _default_get_last_activity(self, jid, node, ifrom, iq):
133 if not isinstance(iq, Iq):
134 reply = self.xmpp.Iq()
135 else:
136 iq.reply()
137 reply = iq
139 if jid not in self._last_activities:
140 raise XMPPError('service-unavailable')
142 bare = JID(jid).bare
144 if bare != self.xmpp.boundjid.bare:
145 if bare in self.xmpp.roster[jid]:
146 sub = self.xmpp.roster[jid][bare]['subscription']
147 if sub not in ('from', 'both'):
148 raise XMPPError('forbidden')
150 td = datetime.now() - self._last_activities[jid]['seconds']
151 seconds = td.seconds + td.days * 24 * 3600
152 status = self._last_activities[jid]['status']
154 reply['last_activity']['seconds'] = seconds
155 reply['last_activity']['status'] = status
157 return reply