JabberCard/jabbercard/xmpp/utilities.py
Schimon Jehudah, Adv. 5e495579c2 Add file PyProject;
Support display of a single pubsub node item;
Update document README;
Modularize code;
2024-11-17 17:30:38 +02:00

467 lines
21 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
from jabbercard.config import Cache
from jabbercard.utilities.graphics import Graphics
from jabbercard.utilities.toml import Toml
from jabbercard.xmpp.instance import XmppInstance
from jabbercard.xmpp.xep_0030 import XmppXep0030
from jabbercard.xmpp.xep_0045 import XmppXep0045
from jabbercard.xmpp.xep_0054 import XmppXep0054
from jabbercard.xmpp.xep_0060 import XmppXep0060
from jabbercard.xmpp.xep_0369 import XmppXep0369
import os
from dateutil import parser
from slixmpp import stanza
import xml.etree.ElementTree as ET
class XmppUtilities:
async def cache_vcard_data(
jabber_id, password, jid_bare, node_name_vcard4, item_id_vcard4):
# Start an XMPP instance and retrieve information
xmpp_instance = XmppInstance(jabber_id, password, jid_bare)
xmpp_instance.connect()
vcard4_data = await XmppXep0060.get_node_items(
xmpp_instance, jid_bare, node_name_vcard4, item_ids=[item_id_vcard4])
xmpp_instance.disconnect()
if vcard4_data:
directory_cache = Cache.get_directory()
supdirectory = os.path.join(directory_cache, 'xep_0060', jid_bare)
if not os.path.exists(supdirectory): os.mkdir(supdirectory)
directory = os.path.join(directory_cache, 'xep_0060', jid_bare, node_name_vcard4)
if not os.path.exists(directory): os.mkdir(directory)
if isinstance(vcard4_data['iq'], stanza.iq.Iq):
iq = vcard4_data['iq']
for item in iq['pubsub']['items']:
filename = directory + item_id_vcard4 + '.xml'
xml_item_as_string = str(item)
Toml.save_to_file(filename, xml_item_as_string)
#item_payload = item['payload']
#vcard4_info = Syndication.extract_vcard4_items(item_payload)
async def cache_node_data(
jabber_id, password, jid_bare, node_name):
# Start an XMPP instance and retrieve information
xmpp_instance = XmppInstance(jabber_id, password, jid_bare)
xmpp_instance.connect()
node_items = await XmppXep0060.get_node_items(
xmpp_instance, jid_bare, node_name)
xmpp_instance.disconnect()
if node_items:
supdirectory = 'xep_0060/{}/'.format(jid_bare)
if not os.path.exists(supdirectory): os.mkdir(supdirectory)
directory = os.path.join(directory_cache, 'xep_0060', jid_bare, node_name)
if not os.path.exists(directory): os.mkdir(directory)
if isinstance(node_items['iq'], stanza.iq.Iq):
iq = node_items['iq']
namespace = '{http://www.w3.org/2005/Atom}'
for item in iq['pubsub']['items']:
item_payload = item['payload']
date_element = item_payload.find(namespace + 'updated')
if not date_element: date_element = item_payload.find(namespace + 'published')
if isinstance(date_element, ET.Element):
date = date_element.text
modification_time = parser.parse(date).timestamp()
filename = directory + item['id'] + '.xml'
xml_item_as_string = str(item)
Toml.save_to_file(filename, xml_item_as_string)
if isinstance(date_element, ET.Element):
file_statistics = os.stat(filename)
access_time = file_statistics.st_atime
os.utime(filename, (access_time, modification_time))
#item_payload = item['payload']
#entry = Syndication.extract_atom_items(item_payload)
async def cache_jid_data(
jabber_id, password, jid_bare, node_name=None, item_id=None, alias=None):
iq_disco_items_list = iq_disco_items_items_list = node_note = node_title = title = ''
jid_vcard = {
'name' : '',
'note' : '',
'type' : '',
'bin' : ''}
#filename = 'details/{}.toml'.format(jid_bare)
#if os.path.exists(filename): jid_details = Toml.open_file_toml(filename)
# Start an XMPP instance and retrieve information
xmpp_instance = XmppInstance(jabber_id, password, jid_bare)
xmpp_instance.connect()
# JID kind
print('JID kind')
instance = message = node_id = None
jid_info = await XmppXep0030.get_jid_info(xmpp_instance, jid_bare)
jid_info_iq = jid_info['iq']
jid_kind = jid_info['kind']
# Set node name to 'urn:xmpp:microblog:0' if JID is an account
if jid_kind == 'account' and not node_name: node_name = 'urn:xmpp:microblog:0'
# vCard4 data
node_name_vcard4 = 'urn:xmpp:vcard4'
item_id_vcard4 = 'current'
vcard4_data = await XmppXep0060.get_node_items(
xmpp_instance, jid_bare, node_name_vcard4, item_ids=[item_id_vcard4])
if vcard4_data:
directory_cache = Cache.get_directory()
supdirectory = os.path.join(directory_cache, 'xep_0060', jid_bare)
if not os.path.exists(supdirectory): os.mkdir(supdirectory)
directory = os.path.join(directory_cache, 'xep_0060', jid_bare, node_name_vcard4)
if not os.path.exists(directory): os.mkdir(directory)
if isinstance(vcard4_data['iq'], stanza.iq.Iq):
iq = vcard4_data['iq']
for item in iq['pubsub']['items']:
filename = directory + item_id_vcard4 + '.xml'
xml_item_as_string = str(item)
Toml.save_to_file(filename, xml_item_as_string)
#item_payload = item['payload']
#vcard4_info = Syndication.extract_vcard4_items(item_payload)
# JID info
print('JID info')
# NOTE Group chat of Psi+ Project at jabber.ru has a note in its vCard.
vcard_data = await XmppXep0054.get_vcard_data(xmpp_instance, jid_bare)
if not vcard_data['error']:
conference_title = None
if jid_kind in ('mix', 'muc'):
for identity in jid_info_iq['disco_info']['identities']:
if identity[3]:
conference_title = identity[3]
break
vcard_temp = vcard_data['iq']['vcard_temp']
jid_vcard = {
'name' : vcard_temp['FN'] or conference_title or '',
'note' : vcard_temp['notes'] or node_id or '',
'type' : vcard_temp['PHOTO']['TYPE'] or '',
'bin' : vcard_temp['PHOTO']['BINVAL'] or ''}
# TODO /d/pubsub.nicoco.fr/blog/urn-uuid-53e43061-1962-3112-bb8a-1473dca61719
count = ''
jid_items = await XmppXep0030.get_jid_items(xmpp_instance, jid_bare)
if isinstance(jid_items['iq'], stanza.iq.Iq):
iq = jid_items['iq']
iq_disco_items = iq['disco_items']
iq_disco_items_items = iq_disco_items['items']
#iq_disco_items_set = {''}
iq_disco_items_list = []
iq_disco_items_items_list = []
for item in iq_disco_items_items:
if jid_kind == 'muc':
#iq_disco_items_set.update([item[2]])
iq_disco_items_list.append(item[2])
else:
#iq_disco_items_set.update([item[1]])
iq_disco_items_list.append(item[1])
iq_disco_items_items_list.append(
[item[0] or '', item[1] or '', item[2] or ''])
#count = len(iq_disco_items_set)
count = len(iq_disco_items_list)
# Title
print('Title')
if (jid_kind not in ('conference', 'mix', 'muc') and
'@' in jid_bare and
not node_name):
# NOTE Variables node_name and node_title do not appear to be utilized.
node_name = 'urn:xmpp:microblog:0'
node_title = 'Journal'
elif jid_kind == 'pubsub':
category = 'unsorted'
for item in iq_disco_items_items:
if item[2] and item[1] == node_name:
node_title = item[2]
break
else:
jid_items = None
if jid_kind == 'server':
if jid_info_iq:
for identity in jid_info_iq['disco_info']['identities']:
if jid_kind == identity[0] and identity[1] == 'im' and identity[3]:
title = identity[3]
print(jid_bare)
print(identity)
print(jid_info)
# String 'undefined' is sourced from JID discuss@conference.conversejs.org
if not title:
if jid_vcard['name'] and not 'undefined' in jid_vcard['name']:
title = jid_vcard['name']
else:
title = jid_bare.split('@')[0]
# JID item count
#count = await XmppUtilities.count_jid_items(xmpp_instance, jid_bare, node_name, jid_kind)
#if jid_kind in ('mix', 'muc', 'conference', 'server'):
# jid_items = await XmppXep0030.get_jid_items(xmpp_instance, jid_bare)
# if isinstance(jid_items['iq'], stanza.iq.Iq):
# iq = jid_items['iq']
# count = len(iq['disco_items']['items'])
#elif jid_kind in ('account', 'pubsub'):
# node_item_ids = await XmppXep0060.get_node_item_ids(xmpp_instance, jid_bare, node_name)
# if isinstance(node_item_ids, stanza.iq.Iq):
# count = len(node_item_ids['disco_items']['items'])
# Group chat messages
print('Group chat messages')
messages = []
subject = ''
if jid_kind == 'muc':
#action = 'Join'
# TODO Create configurations for group chat preview
room_info_muc = await XmppXep0045.get_room_information(
xmpp_instance, jid_bare, alias, maxstanzas=50)
# NOTE Do not mix error messages with node titles and descriptions etc.
if isinstance(room_info_muc['iq'], tuple):
iq = room_info_muc['iq']
for message in iq[3]:
messages.append({
'id' : message['id'],
'alias' : message['mucnick'],
'body' : message['body'],
'timestamp' : message['delay']['stamp'].__str__()})
messages.reverse()
subject = iq[1]['subject']
#title = title or node_name
if not node_title: node_title = node_name
node_note = jid_bare
else:
message = '{}: {} (XEP-0045)'.format(room_info_muc['condition'], room_info_muc['text'])
elif jid_kind == 'mix':
room_info_muc = await XmppXep0369.get_room_information(
xmpp_instance, jid_bare, alias)
if isinstance(room_info_muc['iq'], tuple):
iq = room_info_muc['iq']
for message in iq[3]:
messages.append({
'id' : message['id'],
'alias' : message['mucnick'],
'body' : message['body'],
'timestamp' : message['delay']['stamp'].__str__()})
messages.reverse()
subject = iq[1]['subject']
#title = title or node_name
if not node_title: node_title = node_name
node_note = jid_bare
else:
message = '{}: {} (XEP-0369)'.format(room_info_muc['condition'], room_info_muc['text'])
# Node items
print('Node items')
nodes = {}
#if node_name and node_name in iq_disco_items_set:
if iq_disco_items_list and node_name and node_name in iq_disco_items_list:
#action = 'Browse'
node_item_ids = await XmppXep0060.get_node_item_ids(xmpp_instance, jid_bare, node_name)
if isinstance(node_item_ids['iq'], stanza.iq.Iq):
iq = node_item_ids['iq']
nodes[node_name] = {}
nodes[node_name]['title'] = node_title
nodes[node_name]['count'] = len(iq['disco_items']['items'])
nodes[node_name]['item_ids'] = []
for item_id in iq['disco_items']['items']:
nodes[node_name]['item_ids'].append(
[item_id[0] or '', item_id[1] or '', item_id[2] or ''])
item_ids = []
for item in nodes[node_name]['item_ids']:
item_ids.append(item[2])
node_items = await XmppXep0060.get_node_items(
xmpp_instance, jid_bare, node_name)
if node_items:
directory_cache = Cache.get_directory()
supdirectory = os.path.join(directory_cache, 'xep_0060', jid_bare)
if not os.path.exists(supdirectory): os.mkdir(supdirectory)
directory = os.path.join(directory_cache, 'xep_0060', jid_bare, node_name)
if not os.path.exists(directory): os.mkdir(directory)
if isinstance(node_items['iq'], stanza.iq.Iq):
iq = node_items['iq']
namespace = '{http://www.w3.org/2005/Atom}'
for item in iq['pubsub']['items']:
item_payload = item['payload']
date_element = item_payload.find(namespace + 'updated')
if not date_element: date_element = item_payload.find(namespace + 'published')
if isinstance(date_element, ET.Element):
date = date_element.text
modification_time = parser.parse(date).timestamp()
filename = directory + item['id'] + '.xml'
xml_item_as_string = str(item)
Toml.save_to_file(filename, xml_item_as_string)
if isinstance(date_element, ET.Element):
file_statistics = os.stat(filename)
access_time = file_statistics.st_atime
os.utime(filename, (access_time, modification_time))
#item_payload = item['payload']
#entry = Syndication.extract_atom_items(item_payload)
xmpp_instance.disconnect()
# Notes
print('Notes')
jid_vcard_note = jid_vcard['note']
if isinstance(jid_vcard_note, list) and len(jid_vcard_note):
note = jid_vcard_note[0]['NOTE']
else:
note = jid_vcard_note
#if not note and jid_vcard['name'] and not 'undefined' in jid_vcard['name'] and title != jid_vcard['name']:
# note = jid_vcard['name']
jid_details = {
'count' : count or '',
'error' : jid_info['error'],
'error_text' : jid_info['text'] or '',
'error_condition' : jid_info['condition'] or '',
'image_type' : jid_vcard['type'],
'items' : iq_disco_items_items_list,
'kind' : jid_kind or '',
'messages' : messages or '',
'name' : title,
'nodes' : nodes,
'note' : note or '',
'subject' : subject or ''}
print(jid_details)
# Query URI href
link_href = XmppUtilities.get_link_href(jid_bare, jid_kind, node_name)
Graphics.handle_photo(jid_bare, jid_vcard, link_href)
directory_cache = Cache.get_directory()
filename = os.path.join(directory_cache, 'details', jid_bare + '.toml')
Toml.save_to_toml(filename, jid_details)
return jid_details
def set_action_instance_type(jid_kind, node_name=None):
if jid_kind in ('conference', 'server'):
action = 'Discover'
if jid_kind == 'conference':
instance = 'conferences'
elif jid_kind == 'server':
instance = 'services'
elif jid_kind in ('mix', 'muc'):
action = 'Join'
instance = 'occupants'
elif jid_kind == 'pubsub':
if node_name:
action = 'Subscribe'
instance = 'articles'
else:
action = 'Browse'
instance = 'nodes'
elif jid_kind == 'account':
action = 'Message'
instance = 'articles'
else: # jid_info['error']
action = 'Contact'
return action, instance
def get_link_href(jid_bare, jid_kind, node_name=None):
if jid_kind in ('conference', 'server'):
link_href = 'xmpp:{}?disco;type=get;request=items'.format(jid_bare)
elif jid_kind in ('mix', 'muc'):
link_href = 'xmpp:{}?join'.format(jid_bare)
elif jid_kind == 'pubsub':
if node_name:
link_href = 'xmpp:{}?pubsub;node={};action=subscribe'.format(jid_bare, node_name)
else:
link_href = 'xmpp:{}?disco;type=get;request=items'.format(jid_bare)
elif jid_kind == 'account':
link_href = 'xmpp:{}?message'.format(jid_bare)
else: # jid_info['error']
link_href = 'xmpp:{}'.format(jid_bare)
return link_href
def get_view_href(jid_bare, jid_kind, node_name=None):
links = []
view_href = None
if jid_kind in ('conference', 'server'):
view_href = '/d/' + jid_bare
elif jid_kind in ('mix', 'muc'):
view_href = '/v/' + jid_bare
elif jid_kind == 'pubsub':
if node_name:
view_href = '/d/{}/{}'.format(jid_bare, node_name)
else:
view_href = '/d/' + jid_bare
elif jid_kind == 'account':
view_href = '/d/{}/{}'.format(jid_bare, node_name)
return view_href
def get_xmpp_uri(jid_bare, jid_kind, node_name=None):
links = []
view_href = None
xmpp_uri = jid_bare
if jid_kind in ('conference', 'server'):
xmpp_uri = jid_bare
elif jid_kind in ('mix', 'muc'):
xmpp_uri = jid_bare
elif jid_kind == 'pubsub':
if node_name:
xmpp_uri = '{}?;node={}'.format(jid_bare, node_name)
else:
xmpp_uri = jid_bare
elif jid_kind == 'account':
xmpp_uri = jid_bare
return xmpp_uri
def get_query_uri_links(jid_bare, jid_kind, node_name=None, item_id=None):
links = []
if jid_kind in ('conference', 'server'):
links.append({'name' : 'Discover',
'href' : 'xmpp:{}?disco;type=get;request=items'.format(jid_bare),
'iden' : 'discover'})
xmpp_uri = jid_bare
elif jid_kind in ('mix', 'muc'):
links.append({'name' : 'Join',
'href' : 'xmpp:{}?join'.format(jid_bare),
'iden' : 'join'})
elif jid_kind == 'pubsub':
links.append({'name' : 'Browse',
'href' : 'xmpp:{}?disco;type=get;request=items'.format(jid_bare),
'iden' : 'browse'})
elif jid_kind == 'account':
links.append({'name' : 'Message',
'href' : 'xmpp:{}?message'.format(jid_bare),
'iden' : 'message'})
links.append({'name' : 'Add',
'href' : 'xmpp:{}?roster'.format(jid_bare),
'iden' : 'add'})
else: # jid_info['error']
links.append({'name' : 'Connect',
'href' : 'xmpp:{}'.format(jid_bare),
'iden' : 'connect'})
links.append({'name' : 'Add',
'href' : 'xmpp:{}?roster'.format(jid_bare),
'iden' : 'add'})
if item_id:
links.append({'name' : 'Subscribe',
'href' : 'xmpp:{}?pubsub;node={};item={};action=subscribe'.format(jid_bare, node_name, item_id),
'iden' : 'subscribe'})
links.append({'name' : 'View',
'href' : 'xmpp:{}?pubsub;node={};item={}'.format(jid_bare, node_name, item_id),
'iden' : 'view'})
elif node_name:
links.append({'name' : 'Subscribe',
'href' : 'xmpp:{}?pubsub;node={};action=subscribe'.format(jid_bare, node_name),
'iden' : 'subscribe'})
links.append({'name' : 'View',
'href' : 'xmpp:{}?pubsub;node={}'.format(jid_bare, node_name),
'iden' : 'view'})
links.append({'name' : 'vCard',
'href' : 'xmpp:{}?vcard'.format(jid_bare),
'iden' : 'vcard'})
return links