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.
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
):
27 XEP-0012 Last Activity
31 description
= 'XEP-0012: Last Activity'
32 dependencies
= set(['xep_0030'])
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
,
48 self
.api
.register(self
._default
_set
_last
_activity
,
51 self
.api
.register(self
._default
_del
_last
_activity
,
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
={
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
):
84 if self
.xmpp
.is_component
:
85 if jid
.domain
== self
.xmpp
.boundjid
.domain
:
88 if str(jid
) == str(self
.xmpp
.boundjid
):
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)
100 iq
.enable('last_activity')
101 return iq
.send(timeout
=timeout
,
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
)
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)
120 status
= data
.get('status', None)
124 self
._last
_activities
[jid
] = {
125 'seconds': datetime
.now() - timedelta(seconds
=seconds
),
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()
139 if jid
not in self
._last
_activities
:
140 raise XMPPError('service-unavailable')
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