Add Gajim to the list of recommended clients;
Improve PEP and PubSub publishing mechanism.
This commit is contained in:
parent
640677437c
commit
263382ba8d
8 changed files with 123 additions and 79 deletions
|
@ -123,10 +123,6 @@ def main():
|
||||||
# Setup logging.
|
# Setup logging.
|
||||||
logging.basicConfig(level=args.loglevel,
|
logging.basicConfig(level=args.loglevel,
|
||||||
format='%(levelname)-8s %(message)s')
|
format='%(levelname)-8s %(message)s')
|
||||||
|
|
||||||
# # Setup logging.
|
|
||||||
# logging.basicConfig(level=args.loglevel,
|
|
||||||
# format='%(levelname)-8s %(message)s')
|
|
||||||
# # logging.basicConfig(format='[%(levelname)s] %(message)s')
|
# # logging.basicConfig(format='[%(levelname)s] %(message)s')
|
||||||
# logger = logging.getLogger()
|
# logger = logging.getLogger()
|
||||||
# logdbg = logger.debug
|
# logdbg = logger.debug
|
||||||
|
|
|
@ -653,10 +653,15 @@ or on your desktop.
|
||||||
url = "https://conversejs.org"
|
url = "https://conversejs.org"
|
||||||
platform = "HTML (Web)"
|
platform = "HTML (Web)"
|
||||||
|
|
||||||
# [[clients]]
|
[[clients]]
|
||||||
# name = "Gajim"
|
name = "Gajim"
|
||||||
# info = "XMPP client for desktop"
|
info = "XMPP client for desktop"
|
||||||
# url = "https://gajim.org"
|
info = ["""
|
||||||
|
Gajim aims to be an easy to use and fully-featured XMPP client. \
|
||||||
|
It is open source and released under the GNU General Public License (GPL).
|
||||||
|
"""]
|
||||||
|
url = "https://gajim.org"
|
||||||
|
platform = "Any"
|
||||||
|
|
||||||
# [[clients]]
|
# [[clients]]
|
||||||
# name = "Monal IM"
|
# name = "Monal IM"
|
||||||
|
|
|
@ -19,6 +19,8 @@ import logging
|
||||||
|
|
||||||
class Logger:
|
class Logger:
|
||||||
|
|
||||||
|
def set_logging_level(level):
|
||||||
|
logging.basicConfig(level)
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.logger = logging.getLogger(name)
|
self.logger = logging.getLogger(name)
|
||||||
|
|
|
@ -1278,7 +1278,7 @@ class FeedTask:
|
||||||
db_file = config.get_pathname_to_database(jid_bare)
|
db_file = config.get_pathname_to_database(jid_bare)
|
||||||
urls = sqlite.get_active_feeds_url_sorted_by_last_scanned(db_file)
|
urls = sqlite.get_active_feeds_url_sorted_by_last_scanned(db_file)
|
||||||
for url in urls:
|
for url in urls:
|
||||||
Message.printer('Scanning updates for URL {} ...'.format(url))
|
#Message.printer('Scanning updates for URL {} ...'.format(url))
|
||||||
url = url[0]
|
url = url[0]
|
||||||
# print('STA',url)
|
# print('STA',url)
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ class XmppChat:
|
||||||
# Adding one to the length because of
|
# Adding one to the length because of
|
||||||
# assumption that a comma or a dot is added
|
# assumption that a comma or a dot is added
|
||||||
alias_of_slixfeed_length = len(alias_of_slixfeed) + 1
|
alias_of_slixfeed_length = len(alias_of_slixfeed) + 1
|
||||||
command = (command[alias_of_slixfeed_length:]).lstrip()
|
command = command[alias_of_slixfeed_length:].lstrip()
|
||||||
if isinstance(command, Message): command = command['body']
|
if isinstance(command, Message): command = command['body']
|
||||||
|
|
||||||
command_lowercase = command.lower()
|
command_lowercase = command.lower()
|
||||||
|
|
|
@ -26,6 +26,9 @@ NOTE
|
||||||
message = xmltodict.parse(str(message))
|
message = xmltodict.parse(str(message))
|
||||||
jid = message["message"]["x"]["@jid"]
|
jid = message["message"]["x"]["@jid"]
|
||||||
|
|
||||||
|
2) It seems that XmppRoster.get_contacts(self) is being used excessively.
|
||||||
|
Use self.client_roster instead.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -383,9 +386,20 @@ class XmppClient(slixmpp.ClientXMPP):
|
||||||
print('POSIX sockets: Initiating IPC server...')
|
print('POSIX sockets: Initiating IPC server...')
|
||||||
self.ipc = asyncio.create_task(XmppIpcServer.ipc(self))
|
self.ipc = asyncio.create_task(XmppIpcServer.ipc(self))
|
||||||
|
|
||||||
results = await XmppPubsub.get_pubsub_services(self)
|
jids_of_pubsub = await XmppPubsub.get_pubsub_services(self)
|
||||||
for result in results + [{'jid' : self.boundjid.bare,
|
for jid_bare in jids_of_pubsub:
|
||||||
'name' : self.alias}]:
|
jid_bare['type'] = 'pubsub'
|
||||||
|
# NOTE Do you need 'name' too, or only 'jid'?
|
||||||
|
#jids_of_roster = []
|
||||||
|
#for jid_bare in self.client_roster:
|
||||||
|
# jids_of_roster.append({'jid' : jid_bare,
|
||||||
|
# 'name' : '',
|
||||||
|
# 'type' : 'pep'})
|
||||||
|
jid_of_slixfeed = [{'jid' : self.boundjid.bare,
|
||||||
|
'name' : self.alias,
|
||||||
|
'type' : 'pep'}]
|
||||||
|
#for result in jids_of_pubsub + jids_of_roster + jid_of_slixfeed:
|
||||||
|
for result in jids_of_pubsub + jid_of_slixfeed:
|
||||||
jid_bare = result['jid']
|
jid_bare = result['jid']
|
||||||
if jid_bare not in self.settings:
|
if jid_bare not in self.settings:
|
||||||
db_file = config.get_pathname_to_database(jid_bare)
|
db_file = config.get_pathname_to_database(jid_bare)
|
||||||
|
@ -399,8 +413,12 @@ class XmppClient(slixmpp.ClientXMPP):
|
||||||
# asyncio.create_task(FeedTask.loop_task(self, jid_bare))
|
# asyncio.create_task(FeedTask.loop_task(self, jid_bare))
|
||||||
#]
|
#]
|
||||||
#await asyncio.gather(*tasks)
|
#await asyncio.gather(*tasks)
|
||||||
|
print('feed task for {}'.format(jid_bare))
|
||||||
asyncio.create_task(FeedTask.loop_task(self, jid_bare))
|
asyncio.create_task(FeedTask.loop_task(self, jid_bare))
|
||||||
asyncio.create_task(XmppPubsubTask.loop_task(self, jid_bare)),
|
#await asyncio.sleep(10)
|
||||||
|
print('publish task for {}'.format(jid_bare))
|
||||||
|
publish_type = result['type']
|
||||||
|
asyncio.create_task(XmppPubsubTask.loop_task(self, jid_bare, publish_type)),
|
||||||
print('End')
|
print('End')
|
||||||
|
|
||||||
time_end = time.time()
|
time_end = time.time()
|
||||||
|
@ -1802,7 +1820,7 @@ class XmppClient(slixmpp.ClientXMPP):
|
||||||
identifier = values['identifier'] if 'identifier' in values else None
|
identifier = values['identifier'] if 'identifier' in values else None
|
||||||
url = values['subscription']
|
url = values['subscription']
|
||||||
jid_bare = session['from'].bare
|
jid_bare = session['from'].bare
|
||||||
if 'jid' in values: custom_jid = values['jid']
|
custom_jid = values['jid'] if 'jid' in values else None
|
||||||
if XmppUtilities.is_operator(self, jid_bare) and custom_jid:
|
if XmppUtilities.is_operator(self, jid_bare) and custom_jid:
|
||||||
if isinstance(custom_jid, list): custom_jid = custom_jid[0]
|
if isinstance(custom_jid, list): custom_jid = custom_jid[0]
|
||||||
jid_bare = custom_jid or jid_bare
|
jid_bare = custom_jid or jid_bare
|
||||||
|
@ -1929,14 +1947,16 @@ class XmppClient(slixmpp.ClientXMPP):
|
||||||
entries = sqlite.get_entries_of_feed(db_file, feed_id)
|
entries = sqlite.get_entries_of_feed(db_file, feed_id)
|
||||||
renewed, scanned = sqlite.get_last_update_time_of_feed(db_file,
|
renewed, scanned = sqlite.get_last_update_time_of_feed(db_file,
|
||||||
feed_id)
|
feed_id)
|
||||||
|
last_updated_string = renewed or scanned
|
||||||
last_updated = DateAndTime.convert_seconds_to_yyyy_mm_dd(
|
last_updated = DateAndTime.convert_seconds_to_yyyy_mm_dd(
|
||||||
float(renewed or scanned))
|
float(last_updated_string)) if last_updated_string else 'N/A'
|
||||||
form.add_field(desc='Recent titles from subscription',
|
form.add_field(desc='Recent titles from subscription',
|
||||||
ftype='fixed',
|
ftype='fixed',
|
||||||
value='Recent updates')
|
value='Recent updates')
|
||||||
recent_updates = ''
|
recent_updates = ''
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
recent_updates += '* ' + entry[1] + '\n\n'
|
recent_updates += '* ' + entry[1] + '\n\n'
|
||||||
|
if not recent_updates: recent_updates = 'N/A'
|
||||||
form.add_field(ftype='text-multi',
|
form.add_field(ftype='text-multi',
|
||||||
value=recent_updates)
|
value=recent_updates)
|
||||||
form.add_field(ftype='fixed',
|
form.add_field(ftype='fixed',
|
||||||
|
|
|
@ -10,10 +10,13 @@ class XmppIQ:
|
||||||
|
|
||||||
async def send(self, iq):
|
async def send(self, iq):
|
||||||
try:
|
try:
|
||||||
await iq.send(timeout=15)
|
result = await iq.send(timeout=15)
|
||||||
except IqTimeout as e:
|
except IqTimeout as e:
|
||||||
logger.error('Error Timeout')
|
logger.error('Error Timeout')
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
|
result = e
|
||||||
except IqError as e:
|
except IqError as e:
|
||||||
logger.error('Error XmppIQ')
|
logger.error('Error XmppIQ')
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
|
result = e
|
||||||
|
return result
|
||||||
|
|
|
@ -44,10 +44,10 @@ class XmppPubsub:
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
async def get_node_properties(self, jid, node):
|
async def get_node_properties(self, jid_bare, node):
|
||||||
config = await self.plugin['xep_0060'].get_node_config(jid, node)
|
config = await self.plugin['xep_0060'].get_node_config(jid_bare, node)
|
||||||
subscriptions = await self.plugin['xep_0060'].get_node_subscriptions(jid, node)
|
subscriptions = await self.plugin['xep_0060'].get_node_subscriptions(jid_bare, node)
|
||||||
affiliations = await self.plugin['xep_0060'].get_node_affiliations(jid, node)
|
affiliations = await self.plugin['xep_0060'].get_node_affiliations(jid_bare, node)
|
||||||
properties = {'config': config,
|
properties = {'config': config,
|
||||||
'subscriptions': subscriptions,
|
'subscriptions': subscriptions,
|
||||||
'affiliations': affiliations}
|
'affiliations': affiliations}
|
||||||
|
@ -55,49 +55,48 @@ class XmppPubsub:
|
||||||
return properties
|
return properties
|
||||||
|
|
||||||
|
|
||||||
async def get_node_configuration(self, jid, node_id):
|
|
||||||
node = await self.plugin['xep_0060'].get_node_config(jid, node_id)
|
async def get_node_configuration(self, jid_bare, node_id):
|
||||||
if not node:
|
node = await self.plugin['xep_0060'].get_node_config(jid_bare, node_id)
|
||||||
print('NODE CONFIG', node_id, str(node))
|
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
async def get_nodes(self, jid):
|
async def get_nodes(self, jid_bare):
|
||||||
nodes = await self.plugin['xep_0060'].get_nodes(jid)
|
nodes = await self.plugin['xep_0060'].get_nodes(jid_bare)
|
||||||
# 'self' would lead to slixmpp.jid.InvalidJID: idna validation failed:
|
# 'self' would lead to slixmpp.jid.InvalidJID: idna validation failed:
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
async def get_item(self, jid, node, item_id):
|
async def get_item(self, jid_bare, node, item_id):
|
||||||
item = await self.plugin['xep_0060'].get_item(jid, node, item_id)
|
item = await self.plugin['xep_0060'].get_item(jid_bare, node, item_id)
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
|
||||||
async def get_items(self, jid, node):
|
async def get_items(self, jid_bare, node):
|
||||||
items = await self.plugin['xep_0060'].get_items(jid, node)
|
items = await self.plugin['xep_0060'].get_items(jid_bare, node)
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
def delete_node(self, jid, node):
|
def delete_node(self, jid_bare, node):
|
||||||
jid_from = str(self.boundjid) if self.is_component else None
|
jid_from = self.boundjid.bare if self.is_component else None
|
||||||
self.plugin['xep_0060'].delete_node(jid, node, ifrom=jid_from)
|
self.plugin['xep_0060'].delete_node(jid_bare, node, ifrom=jid_from)
|
||||||
|
|
||||||
|
|
||||||
def purge_node(self, jid, node):
|
def purge_node(self, jid_bare, node):
|
||||||
jid_from = str(self.boundjid) if self.is_component else None
|
jid_from = self.boundjid.bare if self.is_component else None
|
||||||
self.plugin['xep_0060'].purge(jid, node, ifrom=jid_from)
|
self.plugin['xep_0060'].purge(jid_bare, node, ifrom=jid_from)
|
||||||
# iq = self.Iq(stype='set',
|
# iq = self.Iq(stype='set',
|
||||||
# sto=jid,
|
# sto=jid_bare,
|
||||||
# sfrom=jid_from)
|
# sfrom=jid_from)
|
||||||
# iq['pubsub']['purge']['node'] = node
|
# iq['pubsub']['purge']['node'] = node
|
||||||
# return iq
|
# return iq
|
||||||
|
|
||||||
|
|
||||||
# TODO Make use of var "xep" with match/case (XEP-0060, XEP-0277, XEP-0472)
|
# TODO Make use of var "xep" with match/case (XEP-0060, XEP-0277, XEP-0472)
|
||||||
def create_node(self, jid, node, xep ,title=None, subtitle=None):
|
def create_node(self, jid_bare, node, xep=None ,title=None, subtitle=None):
|
||||||
jid_from = str(self.boundjid) if self.is_component else None
|
jid_from = self.boundjid.bare if self.is_component else None
|
||||||
iq = self.Iq(stype='set',
|
iq = self.Iq(stype='set',
|
||||||
sto=jid,
|
sto=jid_bare,
|
||||||
sfrom=jid_from)
|
sfrom=jid_from)
|
||||||
iq['pubsub']['create']['node'] = node
|
iq['pubsub']['create']['node'] = node
|
||||||
form = iq['pubsub']['configure']['form']
|
form = iq['pubsub']['configure']['form']
|
||||||
|
@ -131,8 +130,8 @@ class XmppPubsub:
|
||||||
|
|
||||||
# TODO Consider to create a separate function called "create_atom_entry"
|
# TODO Consider to create a separate function called "create_atom_entry"
|
||||||
# or "create_rfc4287_entry" for anything related to variable "node_entry".
|
# or "create_rfc4287_entry" for anything related to variable "node_entry".
|
||||||
def create_entry(self, jid, node_id, item_id, node_item):
|
def create_entry(self, jid_bare, node_id, item_id, node_item):
|
||||||
iq = self.Iq(stype="set", sto=jid)
|
iq = self.Iq(stype="set", sto=jid_bare)
|
||||||
iq['pubsub']['publish']['node'] = node_id
|
iq['pubsub']['publish']['node'] = node_id
|
||||||
|
|
||||||
item = pubsub.Item()
|
item = pubsub.Item()
|
||||||
|
@ -153,8 +152,8 @@ class XmppPubsub:
|
||||||
return iq
|
return iq
|
||||||
|
|
||||||
|
|
||||||
def _create_entry(self, jid, node, entry, version):
|
def _create_entry(self, jid_bare, node, entry, version):
|
||||||
iq = self.Iq(stype="set", sto=jid)
|
iq = self.Iq(stype="set", sto=jid_bare)
|
||||||
iq['pubsub']['publish']['node'] = node
|
iq['pubsub']['publish']['node'] = node
|
||||||
|
|
||||||
item = pubsub.Item()
|
item = pubsub.Item()
|
||||||
|
@ -294,13 +293,15 @@ class XmppPubsubAction:
|
||||||
return report
|
return report
|
||||||
|
|
||||||
|
|
||||||
async def send_unread_items(self, jid_bare):
|
async def send_unread_items(self, jid_bare, publish_type):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
jid_bare : TYPE
|
jid_bare : str
|
||||||
Bare Jabber ID.
|
Bare Jabber ID.
|
||||||
|
publish_type : str
|
||||||
|
To which type of PubSub ('pep' or 'pubsub').
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
|
@ -324,11 +325,15 @@ class XmppPubsubAction:
|
||||||
# Publish to node 'urn:xmpp:microblog:0' for own JID
|
# Publish to node 'urn:xmpp:microblog:0' for own JID
|
||||||
# Publish to node based on feed identifier for PubSub service.
|
# Publish to node based on feed identifier for PubSub service.
|
||||||
|
|
||||||
if jid_bare == self.boundjid.bare:
|
match publish_type:
|
||||||
|
# XEP-0163: Personal Eventing Protocol
|
||||||
|
# 2.2 One Publisher Per Node¶
|
||||||
|
# The owner-publisher for every node is the bare JID of the account owner.
|
||||||
|
case 'pep':
|
||||||
node_id = 'urn:xmpp:microblog:0'
|
node_id = 'urn:xmpp:microblog:0'
|
||||||
node_subtitle = None
|
node_subtitle = None
|
||||||
node_title = None
|
node_title = None
|
||||||
else:
|
case 'pubsub':
|
||||||
# node_id = feed_properties[2]
|
# node_id = feed_properties[2]
|
||||||
# node_title = feed_properties[3]
|
# node_title = feed_properties[3]
|
||||||
# node_subtitle = feed_properties[5]
|
# node_subtitle = feed_properties[5]
|
||||||
|
@ -349,12 +354,26 @@ class XmppPubsubAction:
|
||||||
node_title = node_title[0]
|
node_title = node_title[0]
|
||||||
node_subtitle = sqlite.get_feed_subtitle(db_file, feed_id)
|
node_subtitle = sqlite.get_feed_subtitle(db_file, feed_id)
|
||||||
node_subtitle = node_subtitle[0]
|
node_subtitle = node_subtitle[0]
|
||||||
|
print ([jid_bare, publish_type, node_id])
|
||||||
xep = None
|
xep = None
|
||||||
node_exist = await XmppPubsub.get_node_configuration(self, jid_bare, node_id)
|
#node_exist = await XmppPubsub.get_node_configuration(self, jid_bare, node_id)
|
||||||
|
nodes = await XmppPubsub.get_nodes(self, jid_bare)
|
||||||
|
node_items = nodes['disco_items']['items']
|
||||||
|
node_exist = False
|
||||||
|
for node_item in node_items:
|
||||||
|
if node_item[1] == node_id:
|
||||||
|
node_exist = True
|
||||||
|
break
|
||||||
|
print(['node_exist', node_exist])
|
||||||
if not node_exist:
|
if not node_exist:
|
||||||
iq_create_node = XmppPubsub.create_node(
|
iq_create_node = XmppPubsub.create_node(
|
||||||
self, jid_bare, node_id, xep, node_title, node_subtitle)
|
self, jid_bare, node_id, xep, node_title, node_subtitle)
|
||||||
await XmppIQ.send(self, iq_create_node)
|
result = await XmppIQ.send(self, iq_create_node)
|
||||||
|
result_condition = result.iq['error']['condition']
|
||||||
|
if result_condition in ('forbidden', 'service-unavailable'):
|
||||||
|
reason = result.iq['error']['text']
|
||||||
|
print('Creation of node {} for JID {} has failed'.format(node_id, jid_bare, reason))
|
||||||
|
return
|
||||||
entries = sqlite.get_unread_entries_of_feed(db_file, feed_id)
|
entries = sqlite.get_unread_entries_of_feed(db_file, feed_id)
|
||||||
report[url] = len(entries)
|
report[url] = len(entries)
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
|
@ -362,12 +381,11 @@ class XmppPubsubAction:
|
||||||
node_entry = Feed.create_rfc4287_entry(feed_entry)
|
node_entry = Feed.create_rfc4287_entry(feed_entry)
|
||||||
entry_url = feed_entry['link']
|
entry_url = feed_entry['link']
|
||||||
item_id = Utilities.hash_url_to_md5(entry_url)
|
item_id = Utilities.hash_url_to_md5(entry_url)
|
||||||
print('PubSub node item was sent to', jid_bare, node_id)
|
print(['PubSub node item was sent to', jid_bare, node_id])
|
||||||
print(entry_url)
|
print([entry_url, item_id])
|
||||||
print(item_id)
|
|
||||||
iq_create_entry = XmppPubsub.create_entry(
|
iq_create_entry = XmppPubsub.create_entry(
|
||||||
self, jid_bare, node_id, item_id, node_entry)
|
self, jid_bare, node_id, item_id, node_entry)
|
||||||
await XmppIQ.send(self, iq_create_entry)
|
result = await XmppIQ.send(self, iq_create_entry)
|
||||||
ix = entry[0]
|
ix = entry[0]
|
||||||
await sqlite.mark_as_read(db_file, ix)
|
await sqlite.mark_as_read(db_file, ix)
|
||||||
print(report)
|
print(report)
|
||||||
|
@ -377,7 +395,7 @@ class XmppPubsubAction:
|
||||||
class XmppPubsubTask:
|
class XmppPubsubTask:
|
||||||
|
|
||||||
|
|
||||||
async def loop_task(self, jid_bare):
|
async def loop_task(self, jid_bare, publish_type):
|
||||||
db_file = config.get_pathname_to_database(jid_bare)
|
db_file = config.get_pathname_to_database(jid_bare)
|
||||||
if jid_bare not in self.settings:
|
if jid_bare not in self.settings:
|
||||||
Config.add_settings_jid(self, jid_bare, db_file)
|
Config.add_settings_jid(self, jid_bare, db_file)
|
||||||
|
@ -394,7 +412,7 @@ class XmppPubsubTask:
|
||||||
.format(jid_bare))
|
.format(jid_bare))
|
||||||
logger.info('Starting tasks "publish" for JID {}'.format(jid_bare))
|
logger.info('Starting tasks "publish" for JID {}'.format(jid_bare))
|
||||||
self.task_manager[jid_bare]['publish'] = asyncio.create_task(
|
self.task_manager[jid_bare]['publish'] = asyncio.create_task(
|
||||||
XmppPubsubAction.send_unread_items(self, jid_bare))
|
XmppPubsubAction.send_unread_items(self, jid_bare, publish_type))
|
||||||
await asyncio.sleep(60 * 180)
|
await asyncio.sleep(60 * 180)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue