Add commands for PubSub.

Improve joining to MUC (WIP).
Improve PubSub (WIP).
This commit is contained in:
Schimon Jehudah 2024-03-27 16:20:32 +00:00
parent 5f946fe0b6
commit d6eec41a86
8 changed files with 199 additions and 70 deletions

View file

@ -49,6 +49,7 @@ from slixfeed.url import (
)
import slixfeed.task as task
from slixfeed.xmpp.bookmark import XmppBookmark
from slixfeed.xmpp.muc import XmppGroupchat
from slixfeed.xmpp.iq import XmppIQ
from slixfeed.xmpp.message import XmppMessage
from slixfeed.xmpp.presence import XmppPresence
@ -116,6 +117,28 @@ def export_feeds(self, jid, jid_file, ext):
return filename
async def xmpp_muc_autojoin(self, bookmarks):
for bookmark in bookmarks:
if bookmark["jid"] and bookmark["autojoin"]:
if not bookmark["nick"]:
bookmark["nick"] = self.alias
logger.error('Alias (i.e. Nicknname) is missing for '
'bookmark {}'.format(bookmark['name']))
alias = bookmark["nick"]
muc_jid = bookmark["jid"]
await XmppGroupchat.join(self, muc_jid, alias)
logger.info('Autojoin groupchat\n'
'Name : {}\n'
'JID : {}\n'
'Alias : {}\n'
.format(bookmark["name"],
bookmark["jid"],
bookmark["nick"]))
elif not bookmark["jid"]:
logger.error('JID is missing for bookmark {}'
.format(bookmark['name']))
"""
TODO
@ -192,23 +215,30 @@ async def xmpp_send_status_message(self, jid):
# )
async def xmpp_send_pubsub(self, jid_bare):
async def xmpp_send_pubsub(self, jid_bare, num=None):
function_name = sys._getframe().f_code.co_name
logger.debug('{}: jid_bare: {}'.format(function_name, jid_bare))
jid_file = jid_bare.replace('/', '_')
db_file = config.get_pathname_to_database(jid_file)
enabled = Config.get_setting_value(self.settings, jid_bare, 'enabled')
if enabled:
if num: counter = 0
report = {}
subscriptions = sqlite.get_active_feeds_url(db_file)
for url in subscriptions:
url = url[0]
feed_id = sqlite.get_feed_id(db_file, url)
feed_id = feed_id[0]
feed_title = sqlite.get_feed_title(db_file, feed_id)
feed_title = feed_title[0]
feed_title = None
feed_summary = None
node = sqlite.get_node_name(db_file, feed_id)
node = node[0]
if jid_bare == self.boundjid.bare:
node = 'urn:xmpp:microblog:0'
else:
feed_title = sqlite.get_feed_title(db_file, feed_id)
feed_title = feed_title[0]
feed_summary = None
node = sqlite.get_node_name(db_file, feed_id)
node = node[0]
xep = None
iq_create_node = XmppPubsub.create_node(
self, jid_bare, node, xep, feed_title, feed_summary)
@ -216,13 +246,17 @@ async def xmpp_send_pubsub(self, jid_bare):
entries = sqlite.get_unread_entries_of_feed(db_file, feed_id)
feed_properties = sqlite.get_feed_properties(db_file, feed_id)
feed_version = feed_properties[2]
print('xmpp_send_pubsub',jid_bare)
print(node)
# if num and counter < num:
report[url] = len(entries)
for entry in entries:
feed_entry = {'author' : None,
'authors' : None,
'category' : None,
'content' : None,
'description' : entry[3],
'href' : entry[2],
'link' : entry[2],
'links' : entry[4],
'tags' : None,
'title' : entry[1],
@ -233,6 +267,9 @@ async def xmpp_send_pubsub(self, jid_bare):
await XmppIQ.send(self, iq_create_entry)
ix = entry[0]
await sqlite.mark_as_read(db_file, ix)
# counter += 1
# if num and counter > num: break
return report
async def xmpp_send_message(self, jid, num=None):

View file

@ -152,10 +152,6 @@ async def start_tasks_xmpp_pubsub(self, jid_bare, tasks=None):
.format(task, jid_bare))
logging.info('Starting tasks {} for JID {}'.format(tasks, jid_bare))
for task in tasks:
# print("task:", task)
# print("tasks:")
# print(tasks)
# breakpoint()
match task:
case 'publish':
self.task_manager[jid_bare]['publish'] = asyncio.create_task(

View file

@ -1,2 +1,2 @@
__version__ = '0.1.50'
__version_info__ = (0, 1, 50)
__version__ = '0.1.51'
__version_info__ = (0, 1, 51)

View file

@ -214,10 +214,9 @@ class Slixfeed(slixmpp.ClientXMPP):
function_name = sys._getframe().f_code.co_name
message_log = '{}: jid_full: {}'
logger.debug(message_log.format(function_name, jid_full))
inviter = message['from'].bare
muc_jid = message['groupchat_invite']['jid']
await XmppBookmark.add(self, muc_jid)
await XmppGroupchat.join(self, inviter, muc_jid)
await XmppGroupchat.join(self, muc_jid)
message_body = ('Greetings! I am {}, the news anchor.\n'
'My job is to bring you the latest '
'news from sources you provide me with.\n'
@ -237,10 +236,9 @@ class Slixfeed(slixmpp.ClientXMPP):
function_name = sys._getframe().f_code.co_name
message_log = '{}: jid_full: {}'
logger.debug(message_log.format(function_name, jid_full))
inviter = message['from'].bare
muc_jid = message['groupchat_invite']['jid']
await XmppBookmark.add(self, muc_jid)
await XmppGroupchat.join(self, inviter, muc_jid)
await XmppGroupchat.join(self, muc_jid)
message_body = ('Greetings! I am {}, the news anchor.\n'
'My job is to bring you the latest '
'news from sources you provide me with.\n'
@ -285,9 +283,10 @@ class Slixfeed(slixmpp.ClientXMPP):
await self['xep_0115'].update_caps()
# self.send_presence()
await self.get_roster()
bookmarks = await self.plugin['xep_0048'].get_bookmarks()
await XmppGroupchat.autojoin(self, bookmarks)
bookmarks = await XmppBookmark.get(self)
await action.xmpp_muc_autojoin(self, bookmarks)
jids = await XmppPubsub.get_pubsub_services(self)
print(jids)
for jid_bare in jids:
if jid_bare not in self.settings:
jid_file = jid_bare
@ -311,8 +310,8 @@ class Slixfeed(slixmpp.ClientXMPP):
# self.send_presence()
profile.set_identity(self, 'client')
self['xep_0115'].update_caps()
bookmarks = await self.plugin['xep_0048'].get_bookmarks()
await XmppGroupchat.autojoin(self, bookmarks)
bookmarks = await XmppBookmark.get(self)
await action.xmpp_muc_autojoin(self, bookmarks)
time_end = time.time()
difference = time_end - time_begin
if difference > 1: logger.warning('{} (time: {})'.format(function_name,
@ -782,7 +781,7 @@ class Slixfeed(slixmpp.ClientXMPP):
form['instructions'] = ('Choose a PubSub Jabber ID and verify '
'that Slixfeed has the necessary '
'permissions to publish into it.')
form.add_field(var='subscription',
form.add_field(var='url',
# TODO Make it possible to add several subscriptions at once;
# Similarly to BitTorrent trackers list
# ftype='text-multi',
@ -833,7 +832,7 @@ class Slixfeed(slixmpp.ClientXMPP):
# options.addOption('XEP-0060: Publish-Subscribe', '0060')
# options.addOption('XEP-0277: Microblogging over XMPP', '0277')
# options.addOption('XEP-0472: Pubsub Social Feed', '0472')
session['next'] = self._handle_subscription_new
session['next'] = self._handle_preview
session['payload'] = form
case 'post':
form = self['xep_0004'].make_form('form', 'Post')
@ -912,7 +911,7 @@ class Slixfeed(slixmpp.ClientXMPP):
return session
node = values['node']
url = values['url']
xep = values['xep']
# xep = values['xep']
if not node:
if jid == self.boundjid.bare:
node = 'urn:xmpp:microblog:0'
@ -958,6 +957,7 @@ class Slixfeed(slixmpp.ClientXMPP):
session['allow_prev'] = True
session['has_next'] = True
session['next'] = self._handle_post_complete
session['notes'] = None
session['prev'] = self._handle_publish
session['payload'] = form
break
@ -981,6 +981,7 @@ class Slixfeed(slixmpp.ClientXMPP):
session['allow_prev'] = True
session['has_next'] = True
session['next'] = self._handle_preview
session['notes'] = None
session['prev'] = self._handle_publish
session['payload'] = form
break
@ -1006,9 +1007,9 @@ class Slixfeed(slixmpp.ClientXMPP):
form.add_field(var='url',
ftype='hidden',
value=url)
form.add_field(var='xep',
ftype='hidden',
value=xep)
# form.add_field(var='xep',
# ftype='hidden',
# value=xep)
return session
async def _handle_post_complete(self, payload, session):
@ -1021,7 +1022,8 @@ class Slixfeed(slixmpp.ClientXMPP):
node = values['node'][0]
jid = values['jid'][0]
url = values['url'][0]
xep = values['xep'][0]
# xep = values['xep'][0]
xep = None
result = await fetch.http(url)
if 'content' in result:
document = result['content']
@ -1054,7 +1056,7 @@ class Slixfeed(slixmpp.ClientXMPP):
# title = "*** No title ***"
# if feed.entries[entry].has_key("summary"):
# summary = feed.entries[entry].summary
iq_create_entry = XmppPubsub.create_entry(
iq_create_entry = XmppPubsub._create_entry(
self, jid, node, feed_entry, feed_version)
await XmppIQ.send(self, iq_create_entry)
text_info = 'Posted {} entries.'.format(len(entries))

View file

@ -743,7 +743,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
form['instructions'] = ('Choose a PubSub Jabber ID and verify '
'that Slixfeed has the necessary '
'permissions to publish into it.')
form.add_field(var='subscription',
form.add_field(var='url',
# TODO Make it possible to add several subscriptions at once;
# Similarly to BitTorrent trackers list
# ftype='text-multi',
@ -794,7 +794,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
# options.addOption('XEP-0060: Publish-Subscribe', '0060')
# options.addOption('XEP-0277: Microblogging over XMPP', '0277')
# options.addOption('XEP-0472: Pubsub Social Feed', '0472')
session['next'] = self._handle_subscription_new
session['next'] = self._handle_preview
session['payload'] = form
case 'post':
form = self['xep_0004'].make_form('form', 'Post')
@ -873,7 +873,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
return session
node = values['node']
url = values['url']
xep = values['xep']
# xep = values['xep']
if not node:
if jid == self.boundjid.bare:
node = 'urn:xmpp:microblog:0'
@ -919,6 +919,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
session['allow_prev'] = True
session['has_next'] = True
session['next'] = self._handle_post_complete
session['notes'] = None
session['prev'] = self._handle_publish
session['payload'] = form
break
@ -942,6 +943,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
session['allow_prev'] = True
session['has_next'] = True
session['next'] = self._handle_preview
session['notes'] = None
session['prev'] = self._handle_publish
session['payload'] = form
break
@ -967,9 +969,9 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
form.add_field(var='url',
ftype='hidden',
value=url)
form.add_field(var='xep',
ftype='hidden',
value=xep)
# form.add_field(var='xep',
# ftype='hidden',
# value=xep)
return session
async def _handle_post_complete(self, payload, session):
@ -982,7 +984,8 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
node = values['node'][0]
jid = values['jid'][0]
url = values['url'][0]
xep = values['xep'][0]
# xep = values['xep'][0]
xep = None
result = await fetch.http(url)
if 'content' in result:
document = result['content']
@ -1015,7 +1018,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
# title = "*** No title ***"
# if feed.entries[entry].has_key("summary"):
# summary = feed.entries[entry].summary
iq_create_entry = XmppPubsub.create_entry(
iq_create_entry = XmppPubsub._create_entry(
self, jid, node, feed_entry, feed_version)
await XmppIQ.send(self, iq_create_entry)
text_info = 'Posted {} entries.'.format(len(entries))

View file

@ -34,32 +34,7 @@ class XmppGroupchat:
self.join(self, inviter, jid)
async def autojoin(self, bookmarks):
jid_from = str(self.boundjid) if self.is_component else None
conferences = bookmarks["private"]["bookmarks"]["conferences"]
for conference in conferences:
if conference["jid"] and conference["autojoin"]:
if not conference["nick"]:
conference["nick"] = self.alias
logging.error('Alias (i.e. Nicknname) is missing for '
'bookmark {}'.format(conference['name']))
await self.plugin['xep_0045'].join_muc_wait(conference["jid"],
conference["nick"],
presence_options = {"pfrom" : jid_from},
password=None)
logging.info('Autojoin groupchat\n'
'Name : {}\n'
'JID : {}\n'
'Alias : {}\n'
.format(conference["name"],
conference["jid"],
conference["nick"]))
elif not conference["jid"]:
logging.error('JID is missing for bookmark {}'
.format(conference['name']))
async def join(self, inviter, jid):
async def join(self, jid, alias=None, password=None):
# token = await initdb(
# muc_jid,
# sqlite.get_setting_value,
@ -81,13 +56,13 @@ class XmppGroupchat:
# )
logging.info('Joining groupchat\n'
'JID : {}\n'
'Inviter : {}\n'
.format(jid, inviter))
.format(jid))
jid_from = str(self.boundjid) if self.is_component else None
if alias == None: self.alias
await self.plugin['xep_0045'].join_muc_wait(jid,
self.alias,
alias,
presence_options = {"pfrom" : jid_from},
password=None)
password=password)
def leave(self, jid):

View file

@ -897,7 +897,7 @@ async def message(self, message):
muc_jid = uri.check_xmpp_uri(message_text[5:])
if muc_jid:
# TODO probe JID and confirm it's a groupchat
XmppGroupchat.join(self, jid_bare, muc_jid)
XmppGroupchat.join(self, muc_jid)
# await XmppBookmark.add(self, jid=muc_jid)
response = ('Joined groupchat {}'
.format(message_text))
@ -981,6 +981,60 @@ async def message(self, message):
db_file, key, val)
response = 'Only new items of newly added feeds be delivered.'
XmppMessage.send_reply(self, message, response)
case _ if message_lowercase.startswith('pubsub delete '):
if is_operator(self, jid_bare):
info = message_text[14:]
info = info.split(' ')
jid = info[0]
nid = info[1]
if jid:
from slixfeed.xmpp.publish import XmppPubsub
XmppPubsub.delete_node(self, jid, nid)
response = 'Deleted node: ' + nid
else:
response = 'PubSub JID is missing. Enter PubSub JID.'
else:
response = ('This action is restricted. '
'Type: sending news to PubSub.')
XmppMessage.send_reply(self, message, response)
case _ if message_lowercase.startswith('pubsub purge '):
if is_operator(self, jid_bare):
info = message_text[13:]
info = info.split(' ')
jid = info[0]
nid = info[1]
if jid:
from slixfeed.xmpp.publish import XmppPubsub
XmppPubsub.purge_node(self, jid, nid)
response = 'Purged node: ' + nid
else:
response = 'PubSub JID is missing. Enter PubSub JID.'
else:
response = ('This action is restricted. '
'Type: sending news to PubSub.')
XmppMessage.send_reply(self, message, response)
case _ if message_lowercase.startswith('pubsub flash '):
if is_operator(self, jid_bare):
info = message_text[13:]
info = info.split(' ')
jid = info[0]
num = int(info[1])
if jid:
if num:
report = await action.xmpp_send_pubsub(self, jid,
num)
else:
report = await action.xmpp_send_pubsub(self, jid)
response = ''
for url in report:
if report[url]:
response += url + ' : ' + str(report[url]) + '\n'
else:
response = 'PubSub JID is missing. Enter PubSub JID.'
else:
response = ('This action is restricted. '
'Type: sending news to PubSub.')
XmppMessage.send_reply(self, message, response)
case _ if message_lowercase.startswith('next'):
num = message_text[5:]
if num:
@ -1398,7 +1452,7 @@ async def message(self, message):
muc_jid = uri.check_xmpp_uri(message_text)
if muc_jid:
# TODO probe JID and confirm it's a groupchat
XmppGroupchat.join(self, jid_bare, muc_jid)
XmppGroupchat.join(self, muc_jid)
# await XmppBookmark.add(self, jid=muc_jid)
response = ('Joined groupchat {}'
.format(message_text))

View file

@ -28,8 +28,23 @@ class XmppPubsub:
return jids
def delete_node(self, jid, node):
jid_from = str(self.boundjid) if self.is_component else None
self.plugin['xep_0060'].delete_node(jid, node, ifrom=jid_from)
def purge_node(self, jid, node):
jid_from = str(self.boundjid) if self.is_component else None
self.plugin['xep_0060'].purge(jid, node, ifrom=jid_from)
# iq = self.Iq(stype='set',
# sto=jid,
# sfrom=jid_from)
# iq['pubsub']['purge']['node'] = node
# return iq
# TODO Make use of var "xep" with match/case (XEP-0060, XEP-0277, XEP-0472)
def create_node(self, jid, node, xep ,title, summary=None):
def create_node(self, jid, node, xep ,title=None, summary=None):
jid_from = str(self.boundjid) if self.is_component else None
iq = self.Iq(stype='set',
sto=jid,
@ -85,7 +100,7 @@ class XmppPubsub:
# NOTE Warning: Entry might not have a link
# TODO Handle situation error
url_encoded = entry.link.encode()
url_encoded = entry['link'].encode()
url_hashed = hashlib.md5(url_encoded)
url_digest = url_hashed.hexdigest()
item['id'] = url_digest
@ -99,6 +114,53 @@ class XmppPubsub:
updated = ET.SubElement(node_entry, "updated")
updated.text = entry['updated']
# Content
content = ET.SubElement(node_entry, "content")
content.set('type', 'text/html')
content.text = entry['description']
# Links
link = ET.SubElement(node_entry, "link")
link.set('href', entry['link'])
item['payload'] = node_entry
iq['pubsub']['publish'].append(item)
return iq
def _create_entry(self, jid, node, entry, version):
iq = self.Iq(stype="set", sto=jid)
iq['pubsub']['publish']['node'] = node
item = pubsub.Item()
# From atomtopubsub:
# character / is causing a bug in movim. replacing : and , with - in id.
# It provides nicer urls.
# Respond to atomtopubsub:
# I think it would be beneficial to use md5 checksum of Url as Id for
# cross reference, and namely - in another project to utilize PubSub as
# links sharing system (see del.icio.us) - to share node entries.
# NOTE Warning: Entry might not have a link
# TODO Handle situation error
url_encoded = entry.link.encode()
url_hashed = hashlib.md5(url_encoded)
url_digest = url_hashed.hexdigest()
item['id'] = url_digest + '_html'
node_entry = ET.Element("entry")
node_entry.set('xmlns', 'http://www.w3.org/2005/Atom')
title = ET.SubElement(node_entry, "title")
title.text = entry.title
updated = ET.SubElement(node_entry, "updated")
updated.text = entry.updated
# Content
if version == 'atom3':