Philipp Hörist pushed to branch master at gajim / python-nbxmpp

Commits:

3 changed files:

Changes:

  • nbxmpp/dispatcher.py
    ... ... @@ -82,6 +82,7 @@ from nbxmpp.modules.vcard4 import VCard4
    82 82
     from nbxmpp.modules.ping import Ping
    
    83 83
     from nbxmpp.modules.delimiter import Delimiter
    
    84 84
     from nbxmpp.modules.roster import Roster
    
    85
    +from nbxmpp.modules.last_activity import LastActivity
    
    85 86
     from nbxmpp.modules.misc import unwrap_carbon
    
    86 87
     from nbxmpp.modules.misc import unwrap_mam
    
    87 88
     from nbxmpp.util import get_properties_struct
    
    ... ... @@ -193,6 +194,7 @@ class StanzaDispatcher(Observable):
    193 194
             self._modules['Ping'] = Ping(self._client)
    
    194 195
             self._modules['Delimiter'] = Delimiter(self._client)
    
    195 196
             self._modules['Roster'] = Roster(self._client)
    
    197
    +        self._modules['LastActivity'] = LastActivity(self._client)
    
    196 198
     
    
    197 199
             for instance in self._modules.values():
    
    198 200
                 for handler in instance.handlers:
    

  • nbxmpp/modules/last_activity.py
    1
    +# Copyright (C) 2021 Philipp Hörist <philipp AT hoerist.com>
    
    2
    +#
    
    3
    +# This file is part of nbxmpp.
    
    4
    +#
    
    5
    +# This program is free software; you can redistribute it and/or
    
    6
    +# modify it under the terms of the GNU General Public License
    
    7
    +# as published by the Free Software Foundation; either version 3
    
    8
    +# of the License, or (at your option) any later version.
    
    9
    +#
    
    10
    +# This program is distributed in the hope that it will be useful,
    
    11
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    12
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    13
    +# GNU General Public License for more details.
    
    14
    +#
    
    15
    +# You should have received a copy of the GNU General Public License
    
    16
    +# along with this program; If not, see <http://www.gnu.org/licenses/>.
    
    17
    +
    
    18
    +from nbxmpp.protocol import Iq
    
    19
    +from nbxmpp.protocol import NodeProcessed
    
    20
    +from nbxmpp.namespaces import Namespace
    
    21
    +from nbxmpp.task import iq_request_task
    
    22
    +from nbxmpp.structs import LastActivityData
    
    23
    +from nbxmpp.structs import StanzaHandler
    
    24
    +from nbxmpp.errors import MalformedStanzaError
    
    25
    +from nbxmpp.errors import StanzaError
    
    26
    +from nbxmpp.modules.base import BaseModule
    
    27
    +
    
    28
    +
    
    29
    +class LastActivity(BaseModule):
    
    30
    +    def __init__(self, client):
    
    31
    +        BaseModule.__init__(self, client)
    
    32
    +
    
    33
    +        self._client = client
    
    34
    +        self.handlers = [
    
    35
    +            StanzaHandler(name='iq',
    
    36
    +                          callback=self._answer_request,
    
    37
    +                          priority=30,
    
    38
    +                          typ='get',
    
    39
    +                          ns=Namespace.LAST),
    
    40
    +        ]
    
    41
    +
    
    42
    +        self._idle_func = None
    
    43
    +
    
    44
    +    def disable(self):
    
    45
    +        self._idle_func = None
    
    46
    +
    
    47
    +    def set_idle_func(self, func):
    
    48
    +        self._idle_func = func
    
    49
    +
    
    50
    +    @iq_request_task
    
    51
    +    def request_last_activity(self, jid):
    
    52
    +        _task = yield
    
    53
    +
    
    54
    +        response = yield _make_request(jid)
    
    55
    +        if response.isError():
    
    56
    +            raise StanzaError(response)
    
    57
    +
    
    58
    +        yield _parse_response(response)
    
    59
    +
    
    60
    +    def _answer_request(self, _client, stanza, _properties):
    
    61
    +        self._log.info('Request received from %s', stanza.getFrom())
    
    62
    +        if self._idle_func is None:
    
    63
    +            return
    
    64
    +
    
    65
    +        seconds = self._idle_func()
    
    66
    +        iq = stanza.buildReply('result')
    
    67
    +        query = iq.getQuery()
    
    68
    +        query.setAttr('seconds', seconds)
    
    69
    +        self._log.info('Send last activity: %s', seconds)
    
    70
    +        self._client.send_stanza(iq)
    
    71
    +        raise NodeProcessed
    
    72
    +
    
    73
    +
    
    74
    +def _make_request(jid):
    
    75
    +    return Iq('get', queryNS=Namespace.LAST, to=jid)
    
    76
    +
    
    77
    +
    
    78
    +def _parse_response(response):
    
    79
    +    query = response.getQuery()
    
    80
    +    seconds = query.getAttr('seconds')
    
    81
    +
    
    82
    +    try:
    
    83
    +        seconds = int(seconds)
    
    84
    +    except Exception:
    
    85
    +        raise MalformedStanzaError('seconds attribute invalid', response)
    
    86
    +
    
    87
    +    return LastActivityData(seconds=seconds, status=query.getData())

  • nbxmpp/structs.py
    ... ... @@ -129,6 +129,8 @@ MAMPreferencesData = namedtuple('MAMPreferencesData', 'default always never')
    129 129
     RosterData = namedtuple('RosterData', 'items version')
    
    130 130
     RosterItem = namedtuple('RosterItem', 'data jid')
    
    131 131
     
    
    132
    +LastActivityData = namedtuple('LastActivityData', 'seconds status')
    
    133
    +
    
    132 134
     
    
    133 135
     class DiscoInfo(namedtuple('DiscoInfo', 'stanza identities features dataforms timestamp')):
    
    134 136