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.
This commit is contained in:
mathieui 2012-08-02 16:24:10 +02:00
parent 1d908702b1
commit 3897d131c1
4 changed files with 94 additions and 2 deletions

View file

@ -233,6 +233,8 @@ def parse_secs_to_str(duration=0):
result += '%sh' % hours if hours else '' result += '%sh' % hours if hours else ''
result += '%sm' % mins if mins else '' result += '%sm' % mins if mins else ''
result += '%ss' % secs if secs else '' result += '%ss' % secs if secs else ''
if not result:
result = '0s'
return result return result
def parse_command_args_to_alias(arg, strto): def parse_command_args_to_alias(arg, strto):

View file

@ -53,6 +53,7 @@ class Connection(sleekxmpp.ClientXMPP):
self.whitespace_keepalive_interval = int(interval) self.whitespace_keepalive_interval = int(interval)
else: else:
self.whitespace_keepalive_interval = 300 self.whitespace_keepalive_interval = 300
self.register_plugin('xep_0012')
self.register_plugin('xep_0030') self.register_plugin('xep_0030')
self.register_plugin('xep_0004') self.register_plugin('xep_0004')
self.register_plugin('xep_0045') self.register_plugin('xep_0045')

View file

@ -24,7 +24,7 @@ import logging
import singleton import singleton
import collections import collections
from sleekxmpp.xmlstream.stanzabase import JID from sleekxmpp import JID, InvalidJID
from sleekxmpp.xmlstream.stanzabase import StanzaBase from sleekxmpp.xmlstream.stanzabase import StanzaBase
from sleekxmpp.xmlstream.handler import Callback 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), 'xml_tab': (self.command_xml_tab, _("Usage: /xml_tab\nXML Tab: Open an XML tab."), None),
'runkey': (self.command_runkey, _("Usage: /runkey <key>\nRunkey: Execute the action defined for <key>."), self.completion_runkey), 'runkey': (self.command_runkey, _("Usage: /runkey <key>\nRunkey: Execute the action defined for <key>."), self.completion_runkey),
'self': (self.command_self, _("Usage: /self\nSelf: Remind you of who you are."), None), 'self': (self.command_self, _("Usage: /self\nSelf: Remind you of who you are."), None),
'activity': (self.command_activity, _("Usage: /activity <jid>\nActivity: Informs you of the last activity of a JID."), self.completion_activity),
} }
# We are invisible # We are invisible
@ -1866,6 +1867,40 @@ class Core(object):
serv_list.append(serv) serv_list.append(serv)
return the_input.auto_completion(serv_list, ' ') return the_input.auto_completion(serv_list, ' ')
def command_activity(self, arg):
"""
/activity <jid>
"""
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): def command_invite(self, arg):
"""/invite <to> <room> [reason]""" """/invite <to> <room> [reason]"""
args = common.shell_split(arg) 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. 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: if self.xml_tabs:
self.add_message_to_text_buffer(self.xml_buffer, '\x191}<--\x19o %s' % stanza) self.add_message_to_text_buffer(self.xml_buffer, '\x191}<--\x19o %s' % stanza)
if isinstance(self.current_tab(), tabs.XMLTab): if isinstance(self.current_tab(), tabs.XMLTab):

View file

@ -1842,6 +1842,7 @@ class RosterInfoTab(Tab):
self.key_func["M-Y"] = self.move_cursor_to_prev_group 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;5B"] = self.move_cursor_to_next_group
self.key_func["M-[1;5A"] = self.move_cursor_to_prev_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["o"] = self.toggle_offline_show
self.key_func["v"] = self.get_contact_version self.key_func["v"] = self.get_contact_version
self.key_func["i"] = self.show_contact_info 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['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['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['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 <jid>\nActivity: Informs you of the last activity of a JID."), self.core.completion_activity)
self.core.xmpp.add_event_handler('session_start', self.core.xmpp.add_event_handler('session_start',
lambda event: self.core.xmpp.plugin['xep_0030'].get_info( lambda event: self.core.xmpp.plugin['xep_0030'].get_info(
jid=self.core.xmpp.boundjid.domain, jid=self.core.xmpp.boundjid.domain,
@ -1968,7 +1970,6 @@ class RosterInfoTab(Tab):
if iq['type'] == 'error': if iq['type'] == 'error':
return self.core.information('Could not retrieve the blocklist.', 'Error') return self.core.information('Could not retrieve the blocklist.', 'Error')
s = 'List of blocked JIDs:\n' 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']) items = (str(item) for item in iq['blocklist']['items'])
jids = '\n'.join(items) jids = '\n'.join(items)
if jids: if jids:
@ -1979,6 +1980,23 @@ class RosterInfoTab(Tab):
self.core.xmpp.plugin['xep_0191'].get_blocked(block=False, callback=callback) 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): def resize(self):
if not self.visible: if not self.visible:
return return
@ -2632,6 +2650,7 @@ class ConversationTab(ChatTab):
self.commands['close'] = (self.command_unquery, _("Usage: /close\Close: Close the tab."), None) 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['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['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.resize()
self.update_commands() self.update_commands()
self.update_keys() self.update_keys()
@ -2680,6 +2699,40 @@ class ConversationTab(ChatTab):
self.text_win.refresh() self.text_win.refresh()
self.input.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): def command_info(self, arg):
contact = roster[self.get_name()] contact = roster[self.get_name()]
jid = JID(self.get_name()) jid = JID(self.get_name())