From 554ff650bff79488d259f32622dbc6b49842c243 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 7 Oct 2017 18:08:39 +0100 Subject: Display contact avatar in the roster. Implements XEP-0084 and XEP-0153. --- data/default_config.cfg | 3 +++ doc/source/dev/xep.rst | 6 +++++- poezio/config.py | 1 + poezio/connection.py | 2 ++ poezio/contact.py | 1 + poezio/core/core.py | 5 +++++ poezio/core/handlers.py | 37 +++++++++++++++++++++++++++++++++++++ poezio/tabs/rostertab.py | 20 ++++++++++++++------ 8 files changed, 68 insertions(+), 7 deletions(-) diff --git a/data/default_config.cfg b/data/default_config.cfg index 96472f49..7efded0b 100644 --- a/data/default_config.cfg +++ b/data/default_config.cfg @@ -425,6 +425,9 @@ use_bookmarks_method = # Ask for message delivery receipts (XEP-0184) #request_message_receipts = true +# Display your contacts’ avatar in the roster if true. +#enable_avatars = true + # Extract base64 images received in XHTML-IM messages # if true. #extract_inline_images = true diff --git a/doc/source/dev/xep.rst b/doc/source/dev/xep.rst index a0a2c558..558602ad 100644 --- a/doc/source/dev/xep.rst +++ b/doc/source/dev/xep.rst @@ -23,7 +23,7 @@ Table of all XEPs implemented in poezio. +----------+-------------------------+---------------------+ |0050 |Ad-Hoc Commands |70% | +----------+-------------------------+---------------------+ -|0054 |vcard-temp |30% | +|0054 |vcard-temp |70% | +----------+-------------------------+---------------------+ |0060 |Publish-Subscribe |10% | +----------+-------------------------+---------------------+ @@ -33,6 +33,8 @@ Table of all XEPs implemented in poezio. +----------+-------------------------+---------------------+ |0077 |In-Band Registration |50% | +----------+-------------------------+---------------------+ +|0084 |User Avatar |50% | ++----------+-------------------------+---------------------+ |0085 |Chat State Notifications |100% | +----------+-------------------------+---------------------+ |0092 |Software Version |100% | @@ -45,6 +47,8 @@ Table of all XEPs implemented in poezio. +----------+-------------------------+---------------------+ |0118 |User Tune |90% | +----------+-------------------------+---------------------+ +|0153 |vCard-Based Avatars |50% | ++----------+-------------------------+---------------------+ |0163 |Personal Eventing |100% | | |Protocol | | +----------+-------------------------+---------------------+ diff --git a/poezio/config.py b/poezio/config.py index ade9f740..b1d09810 100644 --- a/poezio/config.py +++ b/poezio/config.py @@ -50,6 +50,7 @@ DEFAULT_CONFIG = { 'display_mood_notifications': False, 'display_tune_notifications': False, 'display_user_color_in_join_part': True, + 'enable_avatars': True, 'enable_carbons': True, 'enable_user_activity': True, 'enable_user_gaming': True, diff --git a/poezio/connection.py b/poezio/connection.py index 45667ab7..d00f714f 100644 --- a/poezio/connection.py +++ b/poezio/connection.py @@ -106,8 +106,10 @@ class Connection(slixmpp.ClientXMPP): self.register_plugin('xep_0071') self.register_plugin('xep_0077') self.plugin['xep_0077'].create_account = False + self.register_plugin('xep_0084') self.register_plugin('xep_0085') self.register_plugin('xep_0115') + self.register_plugin('xep_0153') # monkey-patch xep_0184 to avoid requesting receipts for messages # without a body diff --git a/poezio/contact.py b/poezio/contact.py index 6e01e41e..090fed92 100644 --- a/poezio/contact.py +++ b/poezio/contact.py @@ -65,6 +65,7 @@ class Contact(object): self.__item = item self.folded_states = defaultdict(lambda: True) self._name = '' + self.avatar = None self.error = None self.tune = {} self.gaming = {} diff --git a/poezio/core/core.py b/poezio/core/core.py index 410ac83b..7d4e46e5 100644 --- a/poezio/core/core.py +++ b/poezio/core/core.py @@ -261,6 +261,11 @@ class Core(object): connection.MatchAll(None), self.handler.incoming_stanza) self.xmpp.register_handler(all_stanzas) + if config.get('enable_avatars'): + self.xmpp.add_event_handler("vcard_avatar_update", + self.handler.on_vcard_avatar) + self.xmpp.add_event_handler("avatar_metadata_publish", + self.handler.on_0084_avatar) if config.get('enable_user_tune'): self.xmpp.add_event_handler("user_tune_publish", self.handler.on_tune_event) diff --git a/poezio/core/handlers.py b/poezio/core/handlers.py index a3d447bd..d8983041 100644 --- a/poezio/core/handlers.py +++ b/poezio/core/handlers.py @@ -355,6 +355,43 @@ class HandlerCore: else: self.core.refresh_window() + @asyncio.coroutine + def on_0084_avatar(self, msg): + jid = msg['from'].bare + contact = roster[jid] + if not contact: + return + log.debug('Received 0084 avatar update from %s', jid) + try: + metadata = msg['pubsub_event']['items']['item']['avatar_metadata']['items'] + except Exception: + return + for info in metadata: + if not info['url']: + try: + result = yield from self.core.xmpp['xep_0084'].retrieve_avatar(jid, + info['id'], + timeout=10) + contact.avatar = result['pubsub']['items']['item']['avatar_data']['value'] + except Exception: + log.exception('Failed retrieving 0084 data from %s:', jid) + return + log.debug('Received %s avatar: %s', jid, info['type']) + + @asyncio.coroutine + def on_vcard_avatar(self, pres): + jid = pres['from'].bare + log.debug('Received vCard avatar update from %s', jid) + try: + result = yield from self.core.xmpp['xep_0054'].get_vcard(jid, + cached=True, + timeout=10) + contact.avatar = result['vcard_temp']['PHOTO'] + except Exception: + log.exception('Failed retrieving vCard from %s:', jid) + return + log.debug('Received %s avatar: %s', jid, avatar['TYPE']) + def on_nick_received(self, message): """ Called when a pep notification for an user nickname diff --git a/poezio/tabs/rostertab.py b/poezio/tabs/rostertab.py index 00992e3c..2aa2fa5a 100644 --- a/poezio/tabs/rostertab.py +++ b/poezio/tabs/rostertab.py @@ -44,6 +44,7 @@ class RosterInfoTab(Tab): self.core.information_buffer.add_window(self.information_win) self.roster_win = windows.RosterWin() self.contact_info_win = windows.ContactInfoWin() + self.avatar_win = windows.ImageWin() self.default_help_message = windows.HelpText("Enter commands with “/”. “o”: toggle offline show") self.input = self.default_help_message self.state = 'normal' @@ -513,11 +514,14 @@ class RosterInfoTab(Tab): info_width, 0, roster_width + 1, self.core.information_buffer) if display_contact_win: + y = self.height - tab_win_height - contact_win_h - 1 + avatar_width = contact_win_h * 2 self.contact_info_win.resize(contact_win_h, - info_width, - self.height - tab_win_height - - contact_win_h - 1, - roster_width + 1) + info_width - avatar_width, + y, roster_width + 1) + self.avatar_win.resize(contact_win_h, + avatar_width, + y, self.width - avatar_width) self.roster_win.resize(self.height - 1 - Tab.tab_win_height(), roster_width, 0, 0) self.input.resize(1, self.width, self.height-1, 0) @@ -989,8 +993,12 @@ class RosterInfoTab(Tab): self.v_separator.refresh() self.information_win.refresh() if display_contact_win: - self.contact_info_win.refresh( - self.roster_win.get_selected_row()) + row = self.roster_win.get_selected_row() + self.contact_info_win.refresh(row) + if isinstance(row, Contact): + self.avatar_win.refresh(row.avatar) + else: + self.avatar_win.refresh(None) self.refresh_tab_win() self.input.refresh() -- cgit v1.2.3