Commit 14e676a6 authored by Philipp Hörist's avatar Philipp Hörist
Browse files

Add Last Activity module (XEP-0012)

parent ce28d895
Pipeline #7317 passed with stages
in 4 minutes and 57 seconds
......@@ -82,6 +82,7 @@ from nbxmpp.modules.vcard4 import VCard4
from import Ping
from nbxmpp.modules.delimiter import Delimiter
from nbxmpp.modules.roster import Roster
from nbxmpp.modules.last_activity import LastActivity
from nbxmpp.modules.misc import unwrap_carbon
from nbxmpp.modules.misc import unwrap_mam
from nbxmpp.util import get_properties_struct
......@@ -193,6 +194,7 @@ class StanzaDispatcher(Observable):
self._modules['Ping'] = Ping(self._client)
self._modules['Delimiter'] = Delimiter(self._client)
self._modules['Roster'] = Roster(self._client)
self._modules['LastActivity'] = LastActivity(self._client)
for instance in self._modules.values():
for handler in instance.handlers:
# Copyright (C) 2021 Philipp Hörist <philipp AT>
# This file is part of nbxmpp.
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; If not, see <>.
from nbxmpp.protocol import Iq
from nbxmpp.protocol import NodeProcessed
from nbxmpp.namespaces import Namespace
from nbxmpp.task import iq_request_task
from nbxmpp.structs import LastActivityData
from nbxmpp.structs import StanzaHandler
from nbxmpp.errors import MalformedStanzaError
from nbxmpp.errors import StanzaError
from nbxmpp.modules.base import BaseModule
class LastActivity(BaseModule):
def __init__(self, client):
BaseModule.__init__(self, client)
self._client = client
self.handlers = [
self._idle_func = None
def disable(self):
self._idle_func = None
def set_idle_func(self, func):
self._idle_func = func
def request_last_activity(self, jid):
_task = yield
response = yield _make_request(jid)
if response.isError():
raise StanzaError(response)
yield _parse_response(response)
def _answer_request(self, _client, stanza, _properties):'Request received from %s', stanza.getFrom())
if self._idle_func is None:
seconds = self._idle_func()
iq = stanza.buildReply('result')
query = iq.getQuery()
query.setAttr('seconds', seconds)'Send last activity: %s', seconds)
raise NodeProcessed
def _make_request(jid):
return Iq('get', queryNS=Namespace.LAST, to=jid)
def _parse_response(response):
query = response.getQuery()
seconds = query.getAttr('seconds')
seconds = int(seconds)
except Exception:
raise MalformedStanzaError('seconds attribute invalid', response)
return LastActivityData(seconds=seconds, status=query.getData())
......@@ -129,6 +129,8 @@ MAMPreferencesData = namedtuple('MAMPreferencesData', 'default always never')
RosterData = namedtuple('RosterData', 'items version')
RosterItem = namedtuple('RosterItem', 'data jid')
LastActivityData = namedtuple('LastActivityData', 'seconds status')
class DiscoInfo(namedtuple('DiscoInfo', 'stanza identities features dataforms timestamp')):
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment