2016-08-28 17:47:52 +00:00
|
|
|
"""
|
|
|
|
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 <jid>``
|
|
|
|
|
2016-10-03 22:26:54 +00:00
|
|
|
**Usage (in a chatroom tab):** ``/vcard <jid or nick>``
|
2016-08-28 17:47:52 +00:00
|
|
|
|
2016-10-03 22:40:56 +00:00
|
|
|
**Usage (in a conversation or contact list tab):** ``/vcard [jid]``
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
Globally, you can do ``/vcard user@server.example`` to get a vcard.
|
|
|
|
|
2016-10-03 22:26:54 +00:00
|
|
|
In a chatroom , you can either do it on a JID or a nick (``/vcard nick``,
|
2016-08-28 17:47:52 +00:00
|
|
|
``/vcard room@muc.server.example/nick`` or ``/vcard
|
|
|
|
user@server.example``).
|
|
|
|
|
|
|
|
In a private or a direct conversation, you can do ``/vcard`` to request
|
2016-10-03 22:40:56 +00:00
|
|
|
vcard from the current interlocutor, and in the contact list to do it
|
|
|
|
on the currently selected contact.
|
2016-08-28 17:47:52 +00:00
|
|
|
"""
|
2021-01-30 12:59:42 +00:00
|
|
|
import asyncio
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
from poezio.decorators import command_args_parser
|
|
|
|
from poezio.plugin import BasePlugin
|
|
|
|
from poezio.roster import roster
|
|
|
|
from poezio.contact import Contact, Resource
|
|
|
|
from poezio.core.structs import Completion
|
|
|
|
from poezio import tabs
|
|
|
|
from slixmpp.jid import JID, InvalidJID
|
2021-01-30 12:59:42 +00:00
|
|
|
from slixmpp.exceptions import IqTimeout
|
2016-08-28 17:47:52 +00:00
|
|
|
|
2018-08-15 11:13:17 +00:00
|
|
|
|
2016-08-28 17:47:52 +00:00
|
|
|
class Plugin(BasePlugin):
|
|
|
|
def init(self):
|
2018-08-15 11:13:17 +00:00
|
|
|
self.api.add_command(
|
|
|
|
'vcard',
|
|
|
|
self.command_vcard,
|
|
|
|
usage='<jid>',
|
|
|
|
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='<jid|nick>',
|
|
|
|
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='<jid>',
|
|
|
|
help='Send an XMPP vcard request to jid (see XEP-0054).',
|
|
|
|
short='Send a vcard request.',
|
|
|
|
completion=self.completion_vcard)
|
2019-07-19 16:55:16 +00:00
|
|
|
for _class in (tabs.PrivateTab, tabs.DynamicConversationTab, tabs.StaticConversationTab):
|
2018-08-15 11:13:17 +00:00
|
|
|
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.',
|
2016-08-28 17:47:52 +00:00
|
|
|
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':
|
2018-08-15 11:13:17 +00:00
|
|
|
self.api.information(
|
|
|
|
'Error retrieving vCard for %s: %s: %s' %
|
|
|
|
(jid, iq['error']['type'], iq['error']['condition']), 'Error')
|
2016-08-28 17:47:52 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
vcard = iq['vcard_temp']
|
|
|
|
|
2018-08-15 11:13:17 +00:00
|
|
|
form = self.core.xmpp['xep_0004'].make_form(
|
|
|
|
ftype='result', title='vCard of %s' % jid)
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
# TODO: implement the other fields.
|
|
|
|
|
2018-08-15 11:13:17 +00:00
|
|
|
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'])
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
# TODO: find a way to detect whether this is present or not.
|
|
|
|
form.add_field(ftype='fixed', value='Full Name')
|
2018-08-15 11:13:17 +00:00
|
|
|
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'])
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
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_]]
|
2018-08-15 11:13:17 +00:00
|
|
|
addr_type = form.add_field(
|
|
|
|
var='ADR %d/TYPE' % i,
|
|
|
|
ftype='list-multi',
|
|
|
|
label='Type',
|
|
|
|
value=values)
|
2016-08-28 17:47:52 +00:00
|
|
|
addr_type.add_option(label='Home', value='HOME')
|
|
|
|
for type_ in addr.bool_interfaces:
|
|
|
|
addr_type.add_option(label=type_, value=type_)
|
2018-08-15 11:13:17 +00:00
|
|
|
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'])
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
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_]]
|
2018-08-15 11:13:17 +00:00
|
|
|
tel_type = form.add_field(
|
|
|
|
var='TEL %d/TYPE' % i,
|
|
|
|
ftype='list-multi',
|
|
|
|
label='Type',
|
|
|
|
value=values)
|
2016-08-28 17:47:52 +00:00
|
|
|
for type_ in tel.bool_interfaces:
|
|
|
|
tel_type.add_option(label=type_, value=type_)
|
2018-08-15 11:13:17 +00:00
|
|
|
form.add_field(
|
|
|
|
var='TEL %d/NUMBER' % i,
|
|
|
|
ftype='text-single',
|
|
|
|
label='Number',
|
|
|
|
value=tel['NUMBER'])
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
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_]]
|
2018-08-15 11:13:17 +00:00
|
|
|
email_type = form.add_field(
|
|
|
|
var='EMAIL %d/TYPE' % i,
|
|
|
|
ftype='list-multi',
|
|
|
|
label='Type',
|
|
|
|
value=values)
|
2016-08-28 17:47:52 +00:00
|
|
|
for type_ in email.bool_interfaces:
|
|
|
|
email_type.add_option(label=type_, value=type_)
|
2018-08-15 11:13:17 +00:00
|
|
|
form.add_field(
|
|
|
|
var='EMAIL %d/USERID' % i,
|
|
|
|
ftype='text-single',
|
|
|
|
label='Email Address',
|
|
|
|
value=email['USERID'])
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
form.add_field(ftype='fixed', value='Misc')
|
2018-08-15 11:13:17 +00:00
|
|
|
form.add_field(
|
|
|
|
var='BDAY',
|
|
|
|
ftype='text-single',
|
|
|
|
label='Birthday',
|
|
|
|
value=str(vcard['BDAY']))
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
for i, jabberid in enumerate(vcard['jids']):
|
|
|
|
form.add_field(ftype='fixed', value='URL')
|
2018-08-15 11:13:17 +00:00
|
|
|
form.add_field(
|
|
|
|
var='JABBERID %d' % i,
|
|
|
|
ftype='jid-single',
|
|
|
|
label='URL',
|
|
|
|
value=jabberid['JABBERID'])
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
for i, url in enumerate(vcard['urls']):
|
|
|
|
form.add_field(ftype='fixed', value='URL')
|
2018-08-15 11:13:17 +00:00
|
|
|
form.add_field(
|
|
|
|
var='URL %d' % i,
|
|
|
|
ftype='text-single',
|
|
|
|
label='URL',
|
|
|
|
value=url['URL'])
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
for i, desc in enumerate(vcard['descriptions']):
|
|
|
|
form.add_field(ftype='fixed', value='Description')
|
2018-08-15 11:13:17 +00:00
|
|
|
form.add_field(
|
|
|
|
var='DESC %d' % i,
|
|
|
|
ftype='text-multi',
|
|
|
|
label='Description',
|
|
|
|
value=desc['DESC'])
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2021-01-30 12:59:42 +00:00
|
|
|
async def _get_vcard(self, jid):
|
2016-08-28 17:47:52 +00:00
|
|
|
'''Send an iq to ask the vCard.'''
|
2021-01-30 12:59:42 +00:00
|
|
|
try:
|
|
|
|
vcard = await self.core.xmpp.plugin['xep_0054'].get_vcard(
|
|
|
|
jid=jid,
|
|
|
|
timeout=30,
|
|
|
|
)
|
|
|
|
self._handle_vcard(vcard)
|
|
|
|
except IqTimeout:
|
2018-08-15 11:13:17 +00:00
|
|
|
self.api.information('Timeout while retrieving vCard for %s' % jid,
|
|
|
|
'Error')
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
@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
|
|
|
|
|
2021-11-18 14:23:46 +00:00
|
|
|
asyncio.create_task(
|
2021-01-30 12:59:42 +00:00
|
|
|
self._get_vcard(jid)
|
|
|
|
)
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
@command_args_parser.raw
|
|
|
|
def command_private_vcard(self, arg):
|
|
|
|
if arg:
|
|
|
|
self.command_vcard(arg)
|
|
|
|
return
|
2019-04-28 00:02:49 +00:00
|
|
|
self.command_vcard(self.api.current_tab().jid.full)
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
@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:
|
2019-04-28 00:02:49 +00:00
|
|
|
jid = self.api.current_tab().jid.bare + '/' + user.nick
|
2016-08-28 17:47:52 +00:00
|
|
|
else:
|
2019-04-28 00:02:49 +00:00
|
|
|
try:
|
2021-01-30 12:59:42 +00:00
|
|
|
jid = JID(arg)
|
2019-04-28 00:02:49 +00:00
|
|
|
except InvalidJID:
|
|
|
|
return self.api.information('Invalid JID: %s' % arg, 'Error')
|
2021-11-18 14:23:46 +00:00
|
|
|
asyncio.create_task(
|
2021-01-30 12:59:42 +00:00
|
|
|
self._get_vcard(jid)
|
|
|
|
)
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
@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):
|
2021-11-18 14:23:46 +00:00
|
|
|
asyncio.create_task(
|
2021-01-30 12:59:42 +00:00
|
|
|
self._get_vcard(JID(current.jid).bare)
|
|
|
|
)
|
2016-08-28 17:47:52 +00:00
|
|
|
elif isinstance(current, Contact):
|
2021-11-18 14:23:46 +00:00
|
|
|
asyncio.create_task(
|
2021-01-30 12:59:42 +00:00
|
|
|
self._get_vcard(current.bare_jid)
|
|
|
|
)
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
def completion_vcard(self, the_input):
|
|
|
|
contacts = [contact.bare_jid for contact in roster.get_contacts()]
|
2018-08-15 11:13:17 +00:00
|
|
|
return Completion(
|
|
|
|
the_input.auto_completion, contacts, '', quotify=False)
|
2016-08-28 17:47:52 +00:00
|
|
|
|
|
|
|
def completion_muc_vcard(self, the_input):
|
|
|
|
users = [user.nick for user in self.api.current_tab().users]
|
2018-08-15 11:13:17 +00:00
|
|
|
users.extend([
|
|
|
|
resource.jid for contact in roster.get_contacts()
|
|
|
|
for resource in contact.resources
|
|
|
|
])
|
2016-08-28 17:47:52 +00:00
|
|
|
return Completion(the_input.auto_completion, users, '', quotify=False)
|