From dbbc24b7c6c85bdbcf3726f7c557b8738eb84052 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 28 Aug 2016 18:47:52 +0100 Subject: =?UTF-8?q?Add=20a=20vcard=20plugin,=20to=20view=20other=20people?= =?UTF-8?q?=E2=80=99s=20vcard-temp=20as=20a=20dataform.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #1683 and #1843. --- doc/source/plugins/index.rst | 6 ++ doc/source/plugins/vcard.rst | 6 ++ plugins/vcard.py | 200 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 doc/source/plugins/vcard.rst create mode 100644 plugins/vcard.py diff --git a/doc/source/plugins/index.rst b/doc/source/plugins/index.rst index 578e832c..397ffefe 100644 --- a/doc/source/plugins/index.rst +++ b/doc/source/plugins/index.rst @@ -281,6 +281,11 @@ Plugin index Roll one or several dice using message corrections. + vCard + :ref:`Documentation ` + + Add a ``/vcard`` command to retrieve and display a vCard. + .. toctree:: :hidden: @@ -326,3 +331,4 @@ Plugin index dice marquee server_part + vcard diff --git a/doc/source/plugins/vcard.rst b/doc/source/plugins/vcard.rst new file mode 100644 index 00000000..602da5a2 --- /dev/null +++ b/doc/source/plugins/vcard.rst @@ -0,0 +1,6 @@ +.. _vcard-plugin: + +vCard +===== + +.. automodule:: vcard diff --git a/plugins/vcard.py b/plugins/vcard.py new file mode 100644 index 00000000..5acc8d3a --- /dev/null +++ b/plugins/vcard.py @@ -0,0 +1,200 @@ +""" +This plugin adds a :term:`/vcard` command to all tabs, allowing you to request +and display the vcard-temp of any given entity. + +Command +------- + +.. glossary:: + + /vcard + + **Usage (globally):** ``/vcard `` + + **Usage (in a MUC tab):** ``/vcard `` + + **Usage (in a conversation or roster tab):** ``/vcard [jid]`` + + Globally, you can do ``/vcard user@server.example`` to get a vcard. + + In a MUC, you can either do it on a JID or a nick (``/vcard nick``, + ``/vcard room@muc.server.example/nick`` or ``/vcard + user@server.example``). + + In a private or a direct conversation, you can do ``/vcard`` to request + vcard from the current interlocutor, and in the roster to do it on the + currently selected contact. +""" + +from poezio.decorators import command_args_parser +from poezio.plugin import BasePlugin +from poezio.roster import roster +from poezio.common import safeJID +from poezio.contact import Contact, Resource +from poezio.core.structs import Completion +from poezio import tabs +from slixmpp.jid import JID, InvalidJID + +class Plugin(BasePlugin): + def init(self): + self.api.add_command('vcard', self.command_vcard, + usage='', + help='Send an XMPP vcard request to jid (see XEP-0054).', + short='Send a vcard request', + completion=self.completion_vcard) + self.api.add_tab_command(tabs.MucTab, 'vcard', self.command_muc_vcard, + usage='', + help='Send an XMPP vcard request to jid or nick (see XEP-0054).', + short='Send a vcard request.', + completion=self.completion_muc_vcard) + self.api.add_tab_command(tabs.RosterInfoTab, 'vcard', self.command_roster_vcard, + usage='', + help='Send an XMPP vcard request to jid (see XEP-0054).', + short='Send a vcard request.', + completion=self.completion_vcard) + for _class in (tabs.PrivateTab, tabs.ConversationTab): + self.api.add_tab_command(_class, 'vcard', self.command_private_vcard, + usage='[jid]', + help='Send an XMPP vcard request to the current interlocutor or the given JID.', + short='Send a vcard request', + completion=self.completion_vcard) + + def _handle_vcard(self, iq): + '''Retrieves a vCard from vcard-temp and present it as a DataFormsTab. + ''' + jid = iq['from'] + + if iq['type'] == 'error': + self.api.information('Error retrieving vCard for %s: %s: %s' % (jid, iq['error']['type'], iq['error']['condition']), 'Error') + return + + vcard = iq['vcard_temp'] + + form = self.core.xmpp['xep_0004'].make_form(ftype='result', title='vCard of %s' % jid) + + # TODO: implement the other fields. + + form.add_field(var='FN', ftype='text-single', label='Name', value=vcard['FN']) + form.add_field(var='NICKNAME', ftype='text-multi', label='Nicknames', value=vcard['NICKNAME']) + + # TODO: find a way to detect whether this is present or not. + form.add_field(ftype='fixed', value='Full Name') + form.add_field(var='N/GIVEN', ftype='text-single', label='Given', value=vcard['N']['GIVEN']) + form.add_field(var='N/MIDDLE', ftype='text-single', label='Middle', value=vcard['N']['MIDDLE']) + form.add_field(var='N/FAMILY', ftype='text-single', label='Family', value=vcard['N']['FAMILY']) + form.add_field(var='N/PREFIX', ftype='text-single', label='Prefix', value=vcard['N']['PREFIX']) + form.add_field(var='N/SUFFIX', ftype='text-single', label='Suffix', value=vcard['N']['SUFFIX']) + + for i, addr in enumerate(vcard['addresses']): + form.add_field(ftype='fixed', value='Address') + values = [type_ for type_ in addr.bool_interfaces if addr[type_]] + addr_type = form.add_field(var='ADR %d/TYPE' % i, ftype='list-multi', label='Type', value=values) + addr_type.add_option(label='Home', value='HOME') + for type_ in addr.bool_interfaces: + addr_type.add_option(label=type_, value=type_) + form.add_field(var='ADR %d/POBOX' % i, ftype='text-single', label='Pobox', value=addr['POBOX']) + form.add_field(var='ADR %d/EXTADD' % i, ftype='text-single', label='Extended Address', value=addr['EXTADD']) + form.add_field(var='ADR %d/STREET' % i, ftype='text-single', label='Street', value=addr['STREET']) + form.add_field(var='ADR %d/LOCALITY' % i, ftype='text-single', label='Locality', value=addr['LOCALITY']) + form.add_field(var='ADR %d/REGION' % i, ftype='text-single', label='Region', value=addr['REGION']) + form.add_field(var='ADR %d/PCODE' % i, ftype='text-single', label='Post Code', value=addr['PCODE']) + form.add_field(var='ADR %d/CTRY' % i, ftype='text-single', label='Country', value=addr['CTRY']) + + for i, tel in enumerate(vcard['telephone_numbers']): + form.add_field(ftype='fixed', value='Telephone') + values = [type_ for type_ in tel.bool_interfaces if tel[type_]] + tel_type = form.add_field(var='TEL %d/TYPE' % i, ftype='list-multi', label='Type', value=values) + for type_ in tel.bool_interfaces: + tel_type.add_option(label=type_, value=type_) + form.add_field(var='TEL %d/NUMBER' % i, ftype='text-single', label='Number', value=tel['NUMBER']) + + for i, email in enumerate(vcard['emails']): + form.add_field(ftype='fixed', value='Email address') + values = [type_ for type_ in email.bool_interfaces if email[type_]] + email_type = form.add_field(var='EMAIL %d/TYPE' % i, ftype='list-multi', label='Type', value=values) + for type_ in email.bool_interfaces: + email_type.add_option(label=type_, value=type_) + form.add_field(var='EMAIL %d/USERID' % i, ftype='text-single', label='Email Address', value=email['USERID']) + + form.add_field(ftype='fixed', value='Misc') + form.add_field(var='BDAY', ftype='text-single', label='Birthday', value=str(vcard['BDAY'])) + + for i, jabberid in enumerate(vcard['jids']): + form.add_field(ftype='fixed', value='URL') + form.add_field(var='JABBERID %d' % i, ftype='jid-single', label='URL', value=jabberid['JABBERID']) + + for i, url in enumerate(vcard['urls']): + form.add_field(ftype='fixed', value='URL') + form.add_field(var='URL %d' % i, ftype='text-single', label='URL', value=url['URL']) + + for i, desc in enumerate(vcard['descriptions']): + form.add_field(ftype='fixed', value='Description') + form.add_field(var='DESC %d' % i, ftype='text-multi', label='Description', value=desc['DESC']) + + on_validate = lambda form: self.core.close_tab() + on_cancel = lambda form: self.core.close_tab() + self.core.open_new_form(form, on_cancel, on_validate) + + def _get_vcard(self, jid): + '''Send an iq to ask the vCard.''' + def timeout_cb(iq): + self.api.information('Timeout while retrieving vCard for %s' % jid, 'Error') + return + + self.core.xmpp.plugin['xep_0054'].get_vcard(jid=jid, timeout=30, + callback=self._handle_vcard, + timeout_callback=timeout_cb) + + @command_args_parser.raw + def command_vcard(self, arg): + if not arg: + self.core.command.help('vcard') + return + + try: + jid = JID(arg) + except InvalidJID: + self.api.information('Invalid JID: %s' % arg, 'Error') + return + + self._get_vcard(jid) + + @command_args_parser.raw + def command_private_vcard(self, arg): + if arg: + self.command_vcard(arg) + return + self.command_vcard(self.api.current_tab().name) + + @command_args_parser.raw + def command_muc_vcard(self, arg): + if not arg: + self.core.command.help('vcard') + return + user = self.api.current_tab().get_user_by_name(arg) + if user: + # No need to use safeJID here, we already know the JID is valid. + jid = JID(self.api.current_tab().name + '/' + user.nick) + else: + jid = safeJID(arg) + self._get_vcard(jid) + + @command_args_parser.raw + def command_roster_vcard(self, arg): + if arg: + self.command_vcard(arg) + return + current = self.api.current_tab().selected_row + if isinstance(current, Resource): + self._get_vcard(JID(current.jid).bare) + elif isinstance(current, Contact): + self._get_vcard(current.bare_jid) + + def completion_vcard(self, the_input): + contacts = [contact.bare_jid for contact in roster.get_contacts()] + return Completion(the_input.auto_completion, contacts, '', quotify=False) + + def completion_muc_vcard(self, the_input): + users = [user.nick for user in self.api.current_tab().users] + users.extend([resource.jid for contact in roster.get_contacts() for resource in contact.resources]) + return Completion(the_input.auto_completion, users, '', quotify=False) -- cgit v1.2.3