From 3897d131c1cf971a6ceb6c3d86cf83813bc26dcd Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 2 Aug 2012 16:24:10 +0200 Subject: [PATCH] Implement XEP-0012 (last activity) ; Fixes #1870 - Add a /activity command - Load xep_0012 on start - Add a 'l' shortcut in the roster to get the last activity - Using "/activity" in a direct conversation will add a message in the conversation, and not in the info buffer. --- src/common.py | 2 ++ src/connection.py | 1 + src/core.py | 38 +++++++++++++++++++++++++++++++- src/tabs.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/src/common.py b/src/common.py index 647454cb..aae5928c 100644 --- a/src/common.py +++ b/src/common.py @@ -233,6 +233,8 @@ def parse_secs_to_str(duration=0): result += '%sh' % hours if hours else '' result += '%sm' % mins if mins else '' result += '%ss' % secs if secs else '' + if not result: + result = '0s' return result def parse_command_args_to_alias(arg, strto): diff --git a/src/connection.py b/src/connection.py index dc4b170a..4b786f7c 100644 --- a/src/connection.py +++ b/src/connection.py @@ -53,6 +53,7 @@ class Connection(sleekxmpp.ClientXMPP): self.whitespace_keepalive_interval = int(interval) else: self.whitespace_keepalive_interval = 300 + self.register_plugin('xep_0012') self.register_plugin('xep_0030') self.register_plugin('xep_0004') self.register_plugin('xep_0045') diff --git a/src/core.py b/src/core.py index e7897c59..b2906f10 100644 --- a/src/core.py +++ b/src/core.py @@ -24,7 +24,7 @@ import logging import singleton import collections -from sleekxmpp.xmlstream.stanzabase import JID +from sleekxmpp import JID, InvalidJID from sleekxmpp.xmlstream.stanzabase import StanzaBase from sleekxmpp.xmlstream.handler import Callback @@ -179,6 +179,7 @@ class Core(object): 'xml_tab': (self.command_xml_tab, _("Usage: /xml_tab\nXML Tab: Open an XML tab."), None), 'runkey': (self.command_runkey, _("Usage: /runkey \nRunkey: Execute the action defined for ."), self.completion_runkey), 'self': (self.command_self, _("Usage: /self\nSelf: Remind you of who you are."), None), + 'activity': (self.command_activity, _("Usage: /activity \nActivity: Informs you of the last activity of a JID."), self.completion_activity), } # We are invisible @@ -1866,6 +1867,40 @@ class Core(object): serv_list.append(serv) return the_input.auto_completion(serv_list, ' ') + def command_activity(self, arg): + """ + /activity + """ + def callback(iq): + if iq['type'] != 'result': + if iq['error']['type'] == 'auth': + self.information('You are not allowed to see the activity of this contact.', 'Error') + else: + self.information('Error retrieving the activity', 'Error') + return + seconds = iq['last_activity']['seconds'] + status = iq['last_activity']['status'] + from_ = iq['from'] + if not JID(from_).user: + msg = 'The uptime of %s is %s.' % ( + from_, + common.parse_secs_to_str(seconds)) + else: + msg = 'The last activity of %s was %s ago%s' % ( + from_, + common.parse_secs_to_str(seconds), + (' and his/her last status was %s' % status) if status else '',) + self.information(msg, 'Info') + try: + jid = JID(arg) + except InvalidJID: + self.information('No valid JID given', 'Error') + return + self.xmpp.plugin['xep_0012'].get_last_activity(jid, block=False, callback=callback) + + def completion_activity(self, the_input): + return the_input.auto_completion([jid for jid in roster.jids()], '', quotify=False) + def command_invite(self, arg): """/invite [reason]""" args = common.shell_split(arg) @@ -2607,6 +2642,7 @@ class Core(object): """ We are sending a new stanza, write it in the xml buffer if needed. """ + self.xmpp.plugin['xep_0012'].set_last_activity() if self.xml_tabs: self.add_message_to_text_buffer(self.xml_buffer, '\x191}<--\x19o %s' % stanza) if isinstance(self.current_tab(), tabs.XMLTab): diff --git a/src/tabs.py b/src/tabs.py index af62ea8c..7bfc23d1 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -1842,6 +1842,7 @@ class RosterInfoTab(Tab): self.key_func["M-Y"] = self.move_cursor_to_prev_group self.key_func["M-[1;5B"] = self.move_cursor_to_next_group self.key_func["M-[1;5A"] = self.move_cursor_to_prev_group + self.key_func["l"] = self.command_activity self.key_func["o"] = self.toggle_offline_show self.key_func["v"] = self.get_contact_version self.key_func["i"] = self.show_contact_info @@ -1859,6 +1860,7 @@ class RosterInfoTab(Tab): self.commands['export'] = (self.command_export, _("Usage: /export [/path/to/file]\nExport: Export your contacts into /path/to/file if specified, or $HOME/poezio_contacts if not."), self.completion_file) self.commands['import'] = (self.command_import, _("Usage: /import [/path/to/file]\nImport: Import your contacts from /path/to/file if specified, or $HOME/poezio_contacts if not."), self.completion_file) self.commands['clear_infos'] = (self.command_clear_infos, _("Usage: /clear_infos\nClear Infos: Use this command to clear the info buffer."), None) + self.commands['activity'] = (self.command_activity, _("Usage: /activity \nActivity: Informs you of the last activity of a JID."), self.core.completion_activity) self.core.xmpp.add_event_handler('session_start', lambda event: self.core.xmpp.plugin['xep_0030'].get_info( jid=self.core.xmpp.boundjid.domain, @@ -1968,7 +1970,6 @@ class RosterInfoTab(Tab): if iq['type'] == 'error': return self.core.information('Could not retrieve the blocklist.', 'Error') s = 'List of blocked JIDs:\n' - log.debug('COCUCOCOCOCOCOCOC\n%s\n\n', iq['blocklist']['items']) items = (str(item) for item in iq['blocklist']['items']) jids = '\n'.join(items) if jids: @@ -1979,6 +1980,23 @@ class RosterInfoTab(Tab): self.core.xmpp.plugin['xep_0191'].get_blocked(block=False, callback=callback) + def command_activity(self, arg=None): + """ + /activity [jid] + """ + + item = self.roster_win.selected_row + if arg: + jid = arg + elif isinstance(item, Contact): + jid = item.bare_jid + elif isinstance(item, Resource): + jid = item.jid.bare + else: + self.core.information('No JID selected.', 'Error') + return + self.core.command_activity(jid) + def resize(self): if not self.visible: return @@ -2632,6 +2650,7 @@ class ConversationTab(ChatTab): self.commands['close'] = (self.command_unquery, _("Usage: /close\Close: Close the tab."), None) self.commands['version'] = (self.command_version, _('Usage: /version\nVersion: Get the software version of the current interlocutor (usually its XMPP client and Operating System).'), None) self.commands['info'] = (self.command_info, _('Usage: /info\nInfo: Get the status of the contact.'), None) + self.commands['activity'] = (self.command_activity, _('Usage: /activity [jid]\nActivity: Get the last activity of the given or the current contact.'), self.core.completion_activity) self.resize() self.update_commands() self.update_keys() @@ -2680,6 +2699,40 @@ class ConversationTab(ChatTab): self.text_win.refresh() self.input.refresh() + def command_activity(self, arg): + """ + /activity [jid] + """ + if arg.strip(): + return self.core.command_activity(arg) + + def callback(iq): + if iq['type'] != 'result': + if iq['error']['type'] == 'auth': + self.information('You are not allowed to see the activity of this contact.', 'Error') + else: + self.information('Error retrieving the activity', 'Error') + return + seconds = iq['last_activity']['seconds'] + status = iq['last_activity']['status'] + from_ = iq['from'] + msg = '\x19%s}The last activity of %s was %s ago%s' + if not JID(from_).user: + msg = '\x19%s}The uptime of %s is %s.' % ( + get_theme().COLOR_INFORMATION_TEXT[0], + from_, + common.parse_secs_to_str(seconds)) + else: + msg = '\x19%s}The last activity of %s was %s ago%s' % ( + get_theme().COLOR_INFORMATION_TEXT[0], + from_, + common.parse_secs_to_str(seconds), + (' and his/her last status was %s' % status) if status else '',) + self.add_message(msg) + self.core.refresh_window() + + self.core.xmpp.plugin['xep_0012'].get_last_activity(self.general_jid, block=False, callback=callback) + def command_info(self, arg): contact = roster[self.get_name()] jid = JID(self.get_name())