Add PubSub functionality.
This commit is contained in:
parent
aed43f34dd
commit
35beab7802
7 changed files with 900 additions and 214 deletions
|
@ -745,11 +745,10 @@ def export_to_opml(jid, filename, results):
|
||||||
tree.write(filename)
|
tree.write(filename)
|
||||||
|
|
||||||
|
|
||||||
async def import_opml(db_file, url):
|
async def import_opml(db_file, result):
|
||||||
function_name = sys._getframe().f_code.co_name
|
function_name = sys._getframe().f_code.co_name
|
||||||
logger.debug('{}: db_file: {} url: {}'
|
logger.debug('{}: db_file: {}'
|
||||||
.format(function_name, db_file, url))
|
.format(function_name, db_file))
|
||||||
result = await fetch.http(url)
|
|
||||||
if not result['error']:
|
if not result['error']:
|
||||||
document = result['content']
|
document = result['content']
|
||||||
root = ET.fromstring(document)
|
root = ET.fromstring(document)
|
||||||
|
@ -1070,137 +1069,97 @@ async def scan_json(self, jid_bare, db_file, url):
|
||||||
new_entries)
|
new_entries)
|
||||||
|
|
||||||
|
|
||||||
async def view_feed(url):
|
def view_feed(url, feed):
|
||||||
function_name = sys._getframe().f_code.co_name
|
function_name = sys._getframe().f_code.co_name
|
||||||
logger.debug('{}: url: {}'
|
logger.debug('{}: url: {}'
|
||||||
.format(function_name, url))
|
.format(function_name, url))
|
||||||
while True:
|
if "title" in feed["feed"].keys():
|
||||||
result = await fetch.http(url)
|
title = feed["feed"]["title"]
|
||||||
if not result['error']:
|
else:
|
||||||
document = result['content']
|
title = urlsplit(url).netloc
|
||||||
status = result['status_code']
|
entries = feed.entries
|
||||||
feed = parse(document)
|
response = "Preview of {}:\n\n```\n".format(title)
|
||||||
# if is_feed(url, feed):
|
counter = 0
|
||||||
if is_feed(feed):
|
for entry in entries:
|
||||||
if "title" in feed["feed"].keys():
|
counter += 1
|
||||||
title = feed["feed"]["title"]
|
if entry.has_key("title"):
|
||||||
else:
|
title = entry.title
|
||||||
title = urlsplit(url).netloc
|
|
||||||
entries = feed.entries
|
|
||||||
response = "Preview of {}:\n\n```\n".format(title)
|
|
||||||
counter = 0
|
|
||||||
for entry in entries:
|
|
||||||
counter += 1
|
|
||||||
if entry.has_key("title"):
|
|
||||||
title = entry.title
|
|
||||||
else:
|
|
||||||
title = "*** No title ***"
|
|
||||||
if entry.has_key("link"):
|
|
||||||
# link = complete_url(source, entry.link)
|
|
||||||
link = join_url(url, entry.link)
|
|
||||||
link = trim_url(link)
|
|
||||||
else:
|
|
||||||
link = "*** No link ***"
|
|
||||||
if entry.has_key("published"):
|
|
||||||
date = entry.published
|
|
||||||
date = dt.rfc2822_to_iso8601(date)
|
|
||||||
elif entry.has_key("updated"):
|
|
||||||
date = entry.updated
|
|
||||||
date = dt.rfc2822_to_iso8601(date)
|
|
||||||
else:
|
|
||||||
date = "*** No date ***"
|
|
||||||
response += ("Title : {}\n"
|
|
||||||
"Date : {}\n"
|
|
||||||
"Link : {}\n"
|
|
||||||
"Count : {}\n"
|
|
||||||
"\n"
|
|
||||||
.format(title, date, link, counter))
|
|
||||||
if counter > 4:
|
|
||||||
break
|
|
||||||
response += (
|
|
||||||
"```\nSource: {}"
|
|
||||||
).format(url)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
result = await crawl.probe_page(url, document)
|
|
||||||
if isinstance(result, str):
|
|
||||||
response = result
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
url = result[0]
|
|
||||||
else:
|
else:
|
||||||
response = ('> {}\nFailed to load URL. Reason: {}'
|
title = "*** No title ***"
|
||||||
.format(url, status))
|
if entry.has_key("link"):
|
||||||
|
# link = complete_url(source, entry.link)
|
||||||
|
link = join_url(url, entry.link)
|
||||||
|
link = trim_url(link)
|
||||||
|
else:
|
||||||
|
link = "*** No link ***"
|
||||||
|
if entry.has_key("published"):
|
||||||
|
date = entry.published
|
||||||
|
date = dt.rfc2822_to_iso8601(date)
|
||||||
|
elif entry.has_key("updated"):
|
||||||
|
date = entry.updated
|
||||||
|
date = dt.rfc2822_to_iso8601(date)
|
||||||
|
else:
|
||||||
|
date = "*** No date ***"
|
||||||
|
response += ("Title : {}\n"
|
||||||
|
"Date : {}\n"
|
||||||
|
"Link : {}\n"
|
||||||
|
"Count : {}\n"
|
||||||
|
"\n"
|
||||||
|
.format(title, date, link, counter))
|
||||||
|
if counter > 4:
|
||||||
break
|
break
|
||||||
|
response += (
|
||||||
|
"```\nSource: {}"
|
||||||
|
).format(url)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
async def view_entry(url, num):
|
def view_entry(url, feed, num):
|
||||||
function_name = sys._getframe().f_code.co_name
|
function_name = sys._getframe().f_code.co_name
|
||||||
logger.debug('{}: url: {} num: {}'
|
logger.debug('{}: url: {} num: {}'
|
||||||
.format(function_name, url, num))
|
.format(function_name, url, num))
|
||||||
while True:
|
if "title" in feed["feed"].keys():
|
||||||
result = await fetch.http(url)
|
title = feed["feed"]["title"]
|
||||||
if not result['error']:
|
else:
|
||||||
document = result['content']
|
title = urlsplit(url).netloc
|
||||||
status = result['status_code']
|
entries = feed.entries
|
||||||
feed = parse(document)
|
num = int(num) - 1
|
||||||
# if is_feed(url, feed):
|
entry = entries[num]
|
||||||
if is_feed(feed):
|
response = "Preview of {}:\n\n```\n".format(title)
|
||||||
if "title" in feed["feed"].keys():
|
if entry.has_key("title"):
|
||||||
title = feed["feed"]["title"]
|
title = entry.title
|
||||||
else:
|
else:
|
||||||
title = urlsplit(url).netloc
|
title = "*** No title ***"
|
||||||
entries = feed.entries
|
if entry.has_key("published"):
|
||||||
num = int(num) - 1
|
date = entry.published
|
||||||
entry = entries[num]
|
date = dt.rfc2822_to_iso8601(date)
|
||||||
response = "Preview of {}:\n\n```\n".format(title)
|
elif entry.has_key("updated"):
|
||||||
if entry.has_key("title"):
|
date = entry.updated
|
||||||
title = entry.title
|
date = dt.rfc2822_to_iso8601(date)
|
||||||
else:
|
else:
|
||||||
title = "*** No title ***"
|
date = "*** No date ***"
|
||||||
if entry.has_key("published"):
|
if entry.has_key("summary"):
|
||||||
date = entry.published
|
summary = entry.summary
|
||||||
date = dt.rfc2822_to_iso8601(date)
|
# Remove HTML tags
|
||||||
elif entry.has_key("updated"):
|
summary = BeautifulSoup(summary, "lxml").text
|
||||||
date = entry.updated
|
# TODO Limit text length
|
||||||
date = dt.rfc2822_to_iso8601(date)
|
summary = summary.replace("\n\n\n", "\n\n")
|
||||||
else:
|
else:
|
||||||
date = "*** No date ***"
|
summary = "*** No summary ***"
|
||||||
if entry.has_key("summary"):
|
if entry.has_key("link"):
|
||||||
summary = entry.summary
|
# link = complete_url(source, entry.link)
|
||||||
# Remove HTML tags
|
link = join_url(url, entry.link)
|
||||||
summary = BeautifulSoup(summary, "lxml").text
|
link = trim_url(link)
|
||||||
# TODO Limit text length
|
else:
|
||||||
summary = summary.replace("\n\n\n", "\n\n")
|
link = "*** No link ***"
|
||||||
else:
|
response = ("{}\n"
|
||||||
summary = "*** No summary ***"
|
"\n"
|
||||||
if entry.has_key("link"):
|
# "> {}\n"
|
||||||
# link = complete_url(source, entry.link)
|
"{}\n"
|
||||||
link = join_url(url, entry.link)
|
"\n"
|
||||||
link = trim_url(link)
|
"{}\n"
|
||||||
else:
|
"\n"
|
||||||
link = "*** No link ***"
|
.format(title, summary, link))
|
||||||
response = ("{}\n"
|
|
||||||
"\n"
|
|
||||||
# "> {}\n"
|
|
||||||
"{}\n"
|
|
||||||
"\n"
|
|
||||||
"{}\n"
|
|
||||||
"\n"
|
|
||||||
.format(title, summary, link))
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
result = await crawl.probe_page(url, document)
|
|
||||||
if isinstance(result, str):
|
|
||||||
response = result
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
url = result[0]
|
|
||||||
else:
|
|
||||||
response = ('> {}\nFailed to load URL. Reason: {}'
|
|
||||||
.format(url, status))
|
|
||||||
break
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
__version__ = '0.1.46'
|
__version__ = '0.1.47'
|
||||||
__version_info__ = (0, 1, 46)
|
__version_info__ = (0, 1, 47)
|
||||||
|
|
|
@ -30,10 +30,12 @@ NOTE
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from feedparser import parse
|
||||||
import slixmpp
|
import slixmpp
|
||||||
import slixfeed.task as task
|
import slixfeed.task as task
|
||||||
|
from slixfeed.url import join_url, trim_url
|
||||||
# from slixmpp.plugins.xep_0363.http_upload import FileTooBig, HTTPError, UploadServiceNotFound
|
# from slixmpp.plugins.xep_0363.http_upload import FileTooBig, HTTPError, UploadServiceNotFound
|
||||||
# from slixmpp.plugins.xep_0402 import BookmarkStorage, Conference
|
# from slixmpp.plugins.xep_0402 import BookmarkStorage, Conference
|
||||||
# from slixmpp.plugins.xep_0048.stanza import Bookmarks
|
# from slixmpp.plugins.xep_0048.stanza import Bookmarks
|
||||||
|
@ -49,9 +51,11 @@ from slixfeed.version import __version__
|
||||||
from slixfeed.xmpp.bookmark import XmppBookmark
|
from slixfeed.xmpp.bookmark import XmppBookmark
|
||||||
from slixfeed.xmpp.connect import XmppConnect
|
from slixfeed.xmpp.connect import XmppConnect
|
||||||
from slixfeed.xmpp.muc import XmppGroupchat
|
from slixfeed.xmpp.muc import XmppGroupchat
|
||||||
|
from slixfeed.xmpp.iq import XmppIQ
|
||||||
from slixfeed.xmpp.message import XmppMessage
|
from slixfeed.xmpp.message import XmppMessage
|
||||||
import slixfeed.xmpp.process as process
|
import slixfeed.xmpp.process as process
|
||||||
import slixfeed.xmpp.profile as profile
|
import slixfeed.xmpp.profile as profile
|
||||||
|
from slixfeed.xmpp.publish import XmppPubsub
|
||||||
from slixfeed.xmpp.roster import XmppRoster
|
from slixfeed.xmpp.roster import XmppRoster
|
||||||
# import slixfeed.xmpp.service as service
|
# import slixfeed.xmpp.service as service
|
||||||
from slixfeed.xmpp.presence import XmppPresence
|
from slixfeed.xmpp.presence import XmppPresence
|
||||||
|
@ -123,6 +127,9 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
# Handler for task messages
|
# Handler for task messages
|
||||||
self.pending_tasks = {}
|
self.pending_tasks = {}
|
||||||
|
|
||||||
|
# Handler for task messages counter
|
||||||
|
# self.pending_tasks_counter = 0
|
||||||
|
|
||||||
# Handler for ping
|
# Handler for ping
|
||||||
self.task_ping_instance = {}
|
self.task_ping_instance = {}
|
||||||
|
|
||||||
|
@ -684,15 +691,18 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
|
|
||||||
# NOTE https://codeberg.org/poezio/slixmpp/issues/3515
|
# NOTE https://codeberg.org/poezio/slixmpp/issues/3515
|
||||||
# if is_operator(self, jid_bare):
|
# if is_operator(self, jid_bare):
|
||||||
self['xep_0050'].add_command(node='recent',
|
|
||||||
name='📰️ Browse',
|
|
||||||
handler=self._handle_recent)
|
|
||||||
self['xep_0050'].add_command(node='subscription',
|
self['xep_0050'].add_command(node='subscription',
|
||||||
name='🪶️ Subscribe',
|
name='🪶️ Subscribe',
|
||||||
handler=self._handle_subscription_add)
|
handler=self._handle_subscription_add)
|
||||||
self['xep_0050'].add_command(node='publish',
|
self['xep_0050'].add_command(node='publish',
|
||||||
name='📣️ Publish',
|
name='📻 Publish',
|
||||||
handler=self._handle_publish)
|
handler=self._handle_pubsub_add)
|
||||||
|
self['xep_0050'].add_command(node='post',
|
||||||
|
name='📣️ Post',
|
||||||
|
handler=self._handle_post)
|
||||||
|
self['xep_0050'].add_command(node='recent',
|
||||||
|
name='📰️ Browse',
|
||||||
|
handler=self._handle_recent)
|
||||||
self['xep_0050'].add_command(node='subscriptions',
|
self['xep_0050'].add_command(node='subscriptions',
|
||||||
name='🎫️ Subscriptions',
|
name='🎫️ Subscriptions',
|
||||||
handler=self._handle_subscriptions)
|
handler=self._handle_subscriptions)
|
||||||
|
@ -727,33 +737,56 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
# Special interface
|
# Special interface
|
||||||
# http://jabber.org/protocol/commands#actions
|
# http://jabber.org/protocol/commands#actions
|
||||||
|
|
||||||
async def _handle_publish(self, iq, session):
|
async def _handle_post(self, iq, session):
|
||||||
form = self['xep_0004'].make_form('form', 'Publish')
|
jid_bare = session['from'].bare
|
||||||
form['instructions'] = ('In order to publish via PubSub, you will '
|
if not is_operator(self, jid_bare):
|
||||||
'have to choose a PubSub hostname and '
|
text_warn = 'PubSub is not available.'
|
||||||
'have a privilege to publish into it.')
|
session['notes'] = [['warn', text_warn]]
|
||||||
# TODO Select from list-multi
|
return session
|
||||||
|
form = self['xep_0004'].make_form('form', 'Post')
|
||||||
|
form['instructions'] = ('In order to post to PubSub, you will have to '
|
||||||
|
'choose a PubSub Jabber ID (e.g. {}) and '
|
||||||
|
'Slixfeed has to be allowed to publish into '
|
||||||
|
'it.'
|
||||||
|
.format(self.boundjid.bare))
|
||||||
form.add_field(var='url',
|
form.add_field(var='url',
|
||||||
ftype='text-single',
|
ftype='text-single',
|
||||||
label='URL',
|
label='URL',
|
||||||
desc='Enter subscription URL.',
|
desc='Enter a subscription URL.',
|
||||||
value='http://',
|
value='http://',
|
||||||
required=True)
|
required=True)
|
||||||
form.add_field(var='pubsub',
|
# options = form.add_field(var='jid',
|
||||||
|
# ftype='list-single',
|
||||||
|
# label='PubSub',
|
||||||
|
# desc='Select a PubSub Jabber ID.',
|
||||||
|
# value=self.boundjid.bare,
|
||||||
|
# required=True)
|
||||||
|
# iq = await self['xep_0030'].get_items(jid=self.boundjid.domain)
|
||||||
|
# for item in iq['disco_items']['items']:
|
||||||
|
# jid = item[0]
|
||||||
|
# if item[1]: name = item[1]
|
||||||
|
# elif item[2]: name = item[2]
|
||||||
|
# else: name = jid
|
||||||
|
# options.addOption(jid, name)
|
||||||
|
form.add_field(var='jid',
|
||||||
ftype='text-single',
|
ftype='text-single',
|
||||||
label='PubSub',
|
label='PubSub',
|
||||||
desc='Enter a PubSub URL.',
|
desc='Enter a PubSub Jabber ID.',
|
||||||
value='pubsub.' + self.boundjid.host,
|
value=self.boundjid.bare,
|
||||||
|
# value='pubsub.' + self.boundjid.host,
|
||||||
required=True)
|
required=True)
|
||||||
form.add_field(var='node',
|
form.add_field(var='node',
|
||||||
ftype='text-single',
|
ftype='text-single',
|
||||||
label='Node',
|
label='Node',
|
||||||
desc='Node to publish at.',
|
desc=('Enter a node to publish to (The default value '
|
||||||
|
'is "urn:xmpp:microblog:0" which is allocated to '
|
||||||
|
'each Jabber ID in the Movim system).'),
|
||||||
|
value='urn:xmpp:microblog:0',
|
||||||
required=False)
|
required=False)
|
||||||
options = form.add_field(var='xep',
|
options = form.add_field(var='xep',
|
||||||
ftype='list-single',
|
ftype='list-single',
|
||||||
label='Type',
|
label='Protocol',
|
||||||
desc='Select XEP.',
|
desc='Select XMPP Extension Protocol.',
|
||||||
value='0060',
|
value='0060',
|
||||||
required=True)
|
required=True)
|
||||||
options.addOption('XEP-0060: Publish-Subscribe', '0060')
|
options.addOption('XEP-0060: Publish-Subscribe', '0060')
|
||||||
|
@ -766,14 +799,176 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
session['payload'] = form
|
session['payload'] = form
|
||||||
return session
|
return session
|
||||||
|
|
||||||
def _handle_preview(self, payload, session):
|
async def _handle_preview(self, payload, session):
|
||||||
jid_full = str(session['from'])
|
jid_full = str(session['from'])
|
||||||
function_name = sys._getframe().f_code.co_name
|
function_name = sys._getframe().f_code.co_name
|
||||||
logger.debug('{}: jid_full: {}'
|
logger.debug('{}: jid_full: {}'
|
||||||
.format(function_name, jid_full))
|
.format(function_name, jid_full))
|
||||||
text_note = ('PubSub support will be available soon.')
|
values = payload['values']
|
||||||
session['notes'] = [['info', text_note]]
|
jid = values['jid']
|
||||||
session['payload'] = None
|
jid_bare = session['from'].bare
|
||||||
|
if jid != jid_bare and not is_operator(self, jid_bare):
|
||||||
|
text_warn = ('Posting to {} is restricted to operators only.'
|
||||||
|
.format(jid_bare))
|
||||||
|
session['allow_prev'] = False
|
||||||
|
session['has_next'] = False
|
||||||
|
session['next'] = None
|
||||||
|
session['notes'] = [['warn', text_warn]]
|
||||||
|
session['prev'] = None
|
||||||
|
session['payload'] = None
|
||||||
|
return session
|
||||||
|
node = values['node']
|
||||||
|
url = values['url']
|
||||||
|
xep = values['xep']
|
||||||
|
form = self['xep_0004'].make_form('form', 'Publish')
|
||||||
|
while True:
|
||||||
|
result = await fetch.http(url)
|
||||||
|
status = result['status_code']
|
||||||
|
if not result['error']:
|
||||||
|
document = result['content']
|
||||||
|
feed = parse(document)
|
||||||
|
# if is_feed(url, feed):
|
||||||
|
if action.is_feed(feed):
|
||||||
|
form['instructions'] = 'Select entries to publish.'
|
||||||
|
options = form.add_field(var='entries',
|
||||||
|
ftype='list-multi',
|
||||||
|
label='Titles',
|
||||||
|
desc='Select entries to post.',
|
||||||
|
required=True)
|
||||||
|
if "title" in feed["feed"].keys():
|
||||||
|
title = feed["feed"]["title"]
|
||||||
|
else:
|
||||||
|
title = url
|
||||||
|
entries = feed.entries
|
||||||
|
entry_ix = 0
|
||||||
|
for entry in entries:
|
||||||
|
if entry.has_key("title"):
|
||||||
|
title = entry.title
|
||||||
|
else:
|
||||||
|
if entry.has_key("published"):
|
||||||
|
title = entry.published
|
||||||
|
title = dt.rfc2822_to_iso8601(title)
|
||||||
|
elif entry.has_key("updated"):
|
||||||
|
title = entry.updated
|
||||||
|
title = dt.rfc2822_to_iso8601(title)
|
||||||
|
else:
|
||||||
|
title = "*** No title ***"
|
||||||
|
options.addOption(title, str(entry_ix))
|
||||||
|
entry_ix += 1
|
||||||
|
if entry_ix > 9:
|
||||||
|
break
|
||||||
|
session['allow_prev'] = True
|
||||||
|
session['has_next'] = True
|
||||||
|
session['next'] = self._handle_post_complete
|
||||||
|
session['prev'] = self._handle_post
|
||||||
|
session['payload'] = form
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
result = await crawl.probe_page(url, document)
|
||||||
|
if isinstance(result, list):
|
||||||
|
form['instructions'] = ('Discovered {} subscriptions '
|
||||||
|
'from the given URL. Please '
|
||||||
|
'choose a subscription.'
|
||||||
|
.format(len(result)))
|
||||||
|
options = form.add_field(var='url',
|
||||||
|
ftype='list-single',
|
||||||
|
label='Feeds',
|
||||||
|
desc='Select a feed.',
|
||||||
|
required=True)
|
||||||
|
results = result
|
||||||
|
for result in results:
|
||||||
|
title = result['name']
|
||||||
|
url = result['link']
|
||||||
|
title = title if title else url
|
||||||
|
options.addOption(title, url)
|
||||||
|
session['allow_prev'] = True
|
||||||
|
session['has_next'] = True
|
||||||
|
session['next'] = self._handle_preview
|
||||||
|
session['prev'] = self._handle_post
|
||||||
|
session['payload'] = form
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
url = result['link']
|
||||||
|
else:
|
||||||
|
text_error = ('Failed to load URL {}'
|
||||||
|
'\n\n'
|
||||||
|
'Reason: {}'
|
||||||
|
.format(url, status))
|
||||||
|
session['notes'] = [['error', text_error]]
|
||||||
|
session['payload'] = None
|
||||||
|
break
|
||||||
|
form.add_field(var='node',
|
||||||
|
ftype='hidden',
|
||||||
|
value=node)
|
||||||
|
form.add_field(var='jid',
|
||||||
|
ftype='hidden',
|
||||||
|
value=jid)
|
||||||
|
# It is essential to place URL at the end, because it might mutate
|
||||||
|
# For example http://blacklistednews.com would change
|
||||||
|
# to https://www.blacklistednews.com/rss.php
|
||||||
|
form.add_field(var='url',
|
||||||
|
ftype='hidden',
|
||||||
|
value=url)
|
||||||
|
form.add_field(var='xep',
|
||||||
|
ftype='hidden',
|
||||||
|
value=xep)
|
||||||
|
return session
|
||||||
|
|
||||||
|
async def _handle_post_complete(self, payload, session):
|
||||||
|
values = payload['values']
|
||||||
|
entries = values['entries']
|
||||||
|
# It might not be good to pass feed object as its size might be too big
|
||||||
|
# Consider a handler self.feeds[url][feed] or self.feeds[jid][url][feed]
|
||||||
|
# It is not possible to assign non-str to transfer.
|
||||||
|
# feed = values['feed']
|
||||||
|
node = values['node'][0]
|
||||||
|
jid = values['jid'][0]
|
||||||
|
url = values['url'][0]
|
||||||
|
xep = values['xep'][0]
|
||||||
|
result = await fetch.http(url)
|
||||||
|
if 'content' in result:
|
||||||
|
document = result['content']
|
||||||
|
feed = parse(document)
|
||||||
|
if feed.feed.has_key('title'):
|
||||||
|
feed_title = feed.feed.title
|
||||||
|
if feed.feed.has_key('description'):
|
||||||
|
feed_summary = feed.feed.description
|
||||||
|
elif feed.feed.has_key('subtitle'):
|
||||||
|
feed_summary = feed.feed.subtitle
|
||||||
|
else:
|
||||||
|
feed_summary = None
|
||||||
|
iq_create_node = XmppPubsub.create_node(
|
||||||
|
self, jid, node, xep, feed_title, feed_summary)
|
||||||
|
await XmppIQ.send(self, iq_create_node)
|
||||||
|
feed_version = feed.version
|
||||||
|
for entry in entries:
|
||||||
|
entry = int(entry)
|
||||||
|
feed_entry = feed.entries[entry]
|
||||||
|
# if feed.entries[entry].has_key("title"):
|
||||||
|
# title = feed.entries[entry].title
|
||||||
|
# else:
|
||||||
|
# if feed.entries[entry].has_key("published"):
|
||||||
|
# title = feed.entries[entry].published
|
||||||
|
# title = dt.rfc2822_to_iso8601(title)
|
||||||
|
# elif feed.entries[entry].has_key("updated"):
|
||||||
|
# title = feed.entries[entry].updated
|
||||||
|
# title = dt.rfc2822_to_iso8601(title)
|
||||||
|
# else:
|
||||||
|
# title = "*** No title ***"
|
||||||
|
# if feed.entries[entry].has_key("summary"):
|
||||||
|
# summary = feed.entries[entry].summary
|
||||||
|
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))
|
||||||
|
session['allow_prev'] = False
|
||||||
|
session['has_next'] = False
|
||||||
|
session['next'] = None
|
||||||
|
session['notes'] = [['info', text_info]]
|
||||||
|
session['prev'] = None
|
||||||
|
session['payload'] = None
|
||||||
|
else:
|
||||||
|
session['payload'] = payload
|
||||||
return session
|
return session
|
||||||
|
|
||||||
async def _handle_profile(self, iq, session):
|
async def _handle_profile(self, iq, session):
|
||||||
|
@ -950,9 +1145,8 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
db_file = config.get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
# In this case (as is typical), the payload is a form
|
# In this case (as is typical), the payload is a form
|
||||||
values = payload['values']
|
values = payload['values']
|
||||||
for value in values:
|
for key in values:
|
||||||
key = value
|
val = values[key]
|
||||||
val = values[value]
|
|
||||||
# NOTE We might want to add new keywords from
|
# NOTE We might want to add new keywords from
|
||||||
# an empty form instead of editing a form.
|
# an empty form instead of editing a form.
|
||||||
# keywords = sqlite.get_filter_value(db_file, key)
|
# keywords = sqlite.get_filter_value(db_file, key)
|
||||||
|
@ -973,6 +1167,80 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
return session
|
return session
|
||||||
|
|
||||||
|
|
||||||
|
async def _handle_pubsub_add(self, iq, session):
|
||||||
|
jid_full = str(session['from'])
|
||||||
|
function_name = sys._getframe().f_code.co_name
|
||||||
|
logger.debug('{}: jid_full: {}'
|
||||||
|
.format(function_name, jid_full))
|
||||||
|
jid_bare = session['from'].bare
|
||||||
|
if not is_operator(self, jid_bare):
|
||||||
|
text_warn = 'PubSub is not available.'
|
||||||
|
session['notes'] = [['warn', text_warn]]
|
||||||
|
return session
|
||||||
|
chat_type = await get_chat_type(self, jid_bare)
|
||||||
|
moderator = None
|
||||||
|
if chat_type == 'groupchat':
|
||||||
|
moderator = is_moderator(self, jid_bare, jid_full)
|
||||||
|
if chat_type == 'chat' or moderator:
|
||||||
|
form = self['xep_0004'].make_form('form', 'Publish')
|
||||||
|
form['instructions'] = ('In order to publish via PubSub, you will '
|
||||||
|
'have to choose a PubSub Jabber ID (e.g. '
|
||||||
|
'{}) and Slixfeed has to be allowed to '
|
||||||
|
'publish into it.'
|
||||||
|
.format('pubsub.' + self.boundjid.host))
|
||||||
|
form.add_field(var='subscription',
|
||||||
|
# TODO Make it possible to add several subscriptions at once;
|
||||||
|
# Similarly to BitTorrent trackers list
|
||||||
|
# ftype='text-multi',
|
||||||
|
# label='Subscription URLs',
|
||||||
|
# desc=('Add subscriptions one time per '
|
||||||
|
# 'subscription.'),
|
||||||
|
ftype='text-single',
|
||||||
|
label='URL',
|
||||||
|
desc='Enter a subscription URL.',
|
||||||
|
value='http://',
|
||||||
|
required=True)
|
||||||
|
form.add_field(var='jid',
|
||||||
|
ftype='text-single',
|
||||||
|
label='PubSub',
|
||||||
|
desc='Enter a PubSub Jabber ID.',
|
||||||
|
value=self.boundjid.bare,
|
||||||
|
required=True)
|
||||||
|
form.add_field(var='node',
|
||||||
|
ftype='text-single',
|
||||||
|
label='Node',
|
||||||
|
desc=('Enter a node to publish to (The default '
|
||||||
|
'value is "urn:xmpp:microblog:0" which '
|
||||||
|
'is allocated to each Jabber ID in the '
|
||||||
|
'Movim system).'),
|
||||||
|
value='urn:xmpp:microblog:0',
|
||||||
|
required=False)
|
||||||
|
options = form.add_field(var='xep',
|
||||||
|
ftype='list-single',
|
||||||
|
label='Protocol',
|
||||||
|
desc='Select XMPP Extension Protocol.',
|
||||||
|
value='0060',
|
||||||
|
required=True)
|
||||||
|
options.addOption('XEP-0060: Publish-Subscribe', '0060')
|
||||||
|
options.addOption('XEP-0277: Microblogging over XMPP', '0277')
|
||||||
|
options.addOption('XEP-0472: Pubsub Social Feed', '0472')
|
||||||
|
# form.add_field(var='scan',
|
||||||
|
# ftype='boolean',
|
||||||
|
# label='Scan',
|
||||||
|
# desc='Scan URL for validity (recommended).',
|
||||||
|
# value=True)
|
||||||
|
session['allow_prev'] = False
|
||||||
|
session['has_next'] = True
|
||||||
|
session['next'] = self._handle_subscription_new
|
||||||
|
session['prev'] = None
|
||||||
|
session['payload'] = form
|
||||||
|
else:
|
||||||
|
text_warn = ('This resource is restricted to moderators of {}.'
|
||||||
|
.format(jid_bare))
|
||||||
|
session['notes'] = [['warn', text_warn]]
|
||||||
|
return session
|
||||||
|
|
||||||
|
|
||||||
async def _handle_subscription_add(self, iq, session):
|
async def _handle_subscription_add(self, iq, session):
|
||||||
jid_full = str(session['from'])
|
jid_full = str(session['from'])
|
||||||
function_name = sys._getframe().f_code.co_name
|
function_name = sys._getframe().f_code.co_name
|
||||||
|
@ -995,9 +1263,18 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
# 'subscription.'),
|
# 'subscription.'),
|
||||||
ftype='text-single',
|
ftype='text-single',
|
||||||
label='URL',
|
label='URL',
|
||||||
desc='Enter subscription URL.',
|
desc='Enter a subscription URL.',
|
||||||
value='http://',
|
value='http://',
|
||||||
required=True)
|
required=True)
|
||||||
|
if is_operator(self, jid_bare):
|
||||||
|
form.add_field(ftype='fixed',
|
||||||
|
label='Subscriber')
|
||||||
|
form.add_field(var='jid',
|
||||||
|
ftype='text-single',
|
||||||
|
label='Jabber ID',
|
||||||
|
desc=('Enter a Jabber ID to add the '
|
||||||
|
'subscription to (The default Jabber ID '
|
||||||
|
'is your own).'))
|
||||||
# form.add_field(var='scan',
|
# form.add_field(var='scan',
|
||||||
# ftype='boolean',
|
# ftype='boolean',
|
||||||
# label='Scan',
|
# label='Scan',
|
||||||
|
@ -2016,7 +2293,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
ftype='list-single',
|
ftype='list-single',
|
||||||
label='Choose',
|
label='Choose',
|
||||||
required=True,
|
required=True,
|
||||||
value='admin')
|
value='export')
|
||||||
jid = session['from'].bare
|
jid = session['from'].bare
|
||||||
if is_operator(self, jid):
|
if is_operator(self, jid):
|
||||||
options.addOption('Administration', 'admin')
|
options.addOption('Administration', 'admin')
|
||||||
|
@ -2291,7 +2568,8 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
jid_bare = session['from'].bare
|
jid_bare = session['from'].bare
|
||||||
jid_file = jid_bare.replace('/', '_')
|
jid_file = jid_bare.replace('/', '_')
|
||||||
db_file = config.get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
count = await action.import_opml(db_file, url)
|
result = await fetch.http(url)
|
||||||
|
count = await action.import_opml(db_file, result)
|
||||||
try:
|
try:
|
||||||
int(count)
|
int(count)
|
||||||
# form = self['xep_0004'].make_form('result', 'Done')
|
# form = self['xep_0004'].make_form('result', 'Done')
|
||||||
|
@ -2868,9 +3146,8 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
Config.add_settings_jid(self.settings, jid_bare, db_file)
|
Config.add_settings_jid(self.settings, jid_bare, db_file)
|
||||||
# In this case (as is typical), the payload is a form
|
# In this case (as is typical), the payload is a form
|
||||||
values = payload['values']
|
values = payload['values']
|
||||||
for value in values:
|
for key in values:
|
||||||
key = value
|
val = values[key]
|
||||||
val = values[value]
|
|
||||||
|
|
||||||
if key in ('enabled', 'media', 'old'):
|
if key in ('enabled', 'media', 'old'):
|
||||||
if val == True:
|
if val == True:
|
||||||
|
|
|
@ -37,9 +37,11 @@ from slixfeed.version import __version__
|
||||||
from slixfeed.xmpp.connect import XmppConnect
|
from slixfeed.xmpp.connect import XmppConnect
|
||||||
# NOTE MUC is possible for component
|
# NOTE MUC is possible for component
|
||||||
from slixfeed.xmpp.muc import XmppGroupchat
|
from slixfeed.xmpp.muc import XmppGroupchat
|
||||||
|
from slixfeed.xmpp.iq import XmppIQ
|
||||||
from slixfeed.xmpp.message import XmppMessage
|
from slixfeed.xmpp.message import XmppMessage
|
||||||
import slixfeed.xmpp.process as process
|
import slixfeed.xmpp.process as process
|
||||||
import slixfeed.xmpp.profile as profile
|
import slixfeed.xmpp.profile as profile
|
||||||
|
from slixfeed.xmpp.publish import XmppPubsub
|
||||||
# from slixfeed.xmpp.roster import XmppRoster
|
# from slixfeed.xmpp.roster import XmppRoster
|
||||||
# import slixfeed.xmpp.service as service
|
# import slixfeed.xmpp.service as service
|
||||||
from slixfeed.xmpp.presence import XmppPresence
|
from slixfeed.xmpp.presence import XmppPresence
|
||||||
|
@ -114,6 +116,9 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
# Handler for task messages
|
# Handler for task messages
|
||||||
self.pending_tasks = {}
|
self.pending_tasks = {}
|
||||||
|
|
||||||
|
# Handler for task messages counter
|
||||||
|
# self.pending_tasks_counter = 0
|
||||||
|
|
||||||
# Handler for ping
|
# Handler for ping
|
||||||
self.task_ping_instance = {}
|
self.task_ping_instance = {}
|
||||||
|
|
||||||
|
@ -643,15 +648,18 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
|
|
||||||
# NOTE https://codeberg.org/poezio/slixmpp/issues/3515
|
# NOTE https://codeberg.org/poezio/slixmpp/issues/3515
|
||||||
# if is_operator(self, jid_bare):
|
# if is_operator(self, jid_bare):
|
||||||
self['xep_0050'].add_command(node='recent',
|
|
||||||
name='📰️ Browse',
|
|
||||||
handler=self._handle_recent)
|
|
||||||
self['xep_0050'].add_command(node='subscription',
|
self['xep_0050'].add_command(node='subscription',
|
||||||
name='🪶️ Subscribe',
|
name='🪶️ Subscribe',
|
||||||
handler=self._handle_subscription_add)
|
handler=self._handle_subscription_add)
|
||||||
self['xep_0050'].add_command(node='publish',
|
self['xep_0050'].add_command(node='publish',
|
||||||
name='📣️ Publish',
|
name='📻 Publish',
|
||||||
handler=self._handle_publish)
|
handler=self._handle_pubsub_add)
|
||||||
|
self['xep_0050'].add_command(node='post',
|
||||||
|
name='📣️ Post',
|
||||||
|
handler=self._handle_post)
|
||||||
|
self['xep_0050'].add_command(node='recent',
|
||||||
|
name='📰️ Browse',
|
||||||
|
handler=self._handle_recent)
|
||||||
self['xep_0050'].add_command(node='subscriptions',
|
self['xep_0050'].add_command(node='subscriptions',
|
||||||
name='🎫️ Subscriptions',
|
name='🎫️ Subscriptions',
|
||||||
handler=self._handle_subscriptions)
|
handler=self._handle_subscriptions)
|
||||||
|
@ -686,33 +694,56 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
# Special interface
|
# Special interface
|
||||||
# http://jabber.org/protocol/commands#actions
|
# http://jabber.org/protocol/commands#actions
|
||||||
|
|
||||||
async def _handle_publish(self, iq, session):
|
async def _handle_post(self, iq, session):
|
||||||
form = self['xep_0004'].make_form('form', 'Publish')
|
jid_bare = session['from'].bare
|
||||||
form['instructions'] = ('In order to publish via PubSub, you will '
|
if not is_operator(self, jid_bare):
|
||||||
'have to choose a PubSub hostname and '
|
text_warn = 'PubSub is not available.'
|
||||||
'have a privilege to publish into it.')
|
session['notes'] = [['warn', text_warn]]
|
||||||
# TODO Select from list-multi
|
return session
|
||||||
|
form = self['xep_0004'].make_form('form', 'Post')
|
||||||
|
form['instructions'] = ('In order to post to PubSub, you will have to '
|
||||||
|
'choose a PubSub Jabber ID (e.g. {}) and '
|
||||||
|
'Slixfeed has to be allowed to publish into '
|
||||||
|
'it.'
|
||||||
|
.format(self.boundjid.bare))
|
||||||
form.add_field(var='url',
|
form.add_field(var='url',
|
||||||
ftype='text-single',
|
ftype='text-single',
|
||||||
label='URL',
|
label='URL',
|
||||||
desc='Enter subscription URL.',
|
desc='Enter a subscription URL.',
|
||||||
value='http://',
|
value='http://',
|
||||||
required=True)
|
required=True)
|
||||||
form.add_field(var='pubsub',
|
# options = form.add_field(var='jid',
|
||||||
|
# ftype='list-single',
|
||||||
|
# label='PubSub',
|
||||||
|
# desc='Select a PubSub Jabber ID.',
|
||||||
|
# value=self.boundjid.bare,
|
||||||
|
# required=True)
|
||||||
|
# iq = await self['xep_0030'].get_items(jid=self.boundjid.domain)
|
||||||
|
# for item in iq['disco_items']['items']:
|
||||||
|
# jid = item[0]
|
||||||
|
# if item[1]: name = item[1]
|
||||||
|
# elif item[2]: name = item[2]
|
||||||
|
# else: name = jid
|
||||||
|
# options.addOption(jid, name)
|
||||||
|
form.add_field(var='jid',
|
||||||
ftype='text-single',
|
ftype='text-single',
|
||||||
label='PubSub',
|
label='PubSub',
|
||||||
desc='Enter a PubSub URL.',
|
desc='Enter a PubSub Jabber ID.',
|
||||||
value='pubsub.' + self.boundjid.host,
|
value=self.boundjid.bare,
|
||||||
|
# value='pubsub.' + self.boundjid.host,
|
||||||
required=True)
|
required=True)
|
||||||
form.add_field(var='node',
|
form.add_field(var='node',
|
||||||
ftype='text-single',
|
ftype='text-single',
|
||||||
label='Node',
|
label='Node',
|
||||||
desc='Node to publish at.',
|
desc=('Enter a node to publish to (The default value '
|
||||||
|
'is "urn:xmpp:microblog:0" which is allocated to '
|
||||||
|
'each Jabber ID in the Movim system).'),
|
||||||
|
value='urn:xmpp:microblog:0',
|
||||||
required=False)
|
required=False)
|
||||||
options = form.add_field(var='xep',
|
options = form.add_field(var='xep',
|
||||||
ftype='list-single',
|
ftype='list-single',
|
||||||
label='Type',
|
label='Protocol',
|
||||||
desc='Select XEP.',
|
desc='Select XMPP Extension Protocol.',
|
||||||
value='0060',
|
value='0060',
|
||||||
required=True)
|
required=True)
|
||||||
options.addOption('XEP-0060: Publish-Subscribe', '0060')
|
options.addOption('XEP-0060: Publish-Subscribe', '0060')
|
||||||
|
@ -725,14 +756,176 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
session['payload'] = form
|
session['payload'] = form
|
||||||
return session
|
return session
|
||||||
|
|
||||||
def _handle_preview(self, payload, session):
|
async def _handle_preview(self, payload, session):
|
||||||
jid_full = str(session['from'])
|
jid_full = str(session['from'])
|
||||||
function_name = sys._getframe().f_code.co_name
|
function_name = sys._getframe().f_code.co_name
|
||||||
logger.debug('{}: jid_full: {}'
|
logger.debug('{}: jid_full: {}'
|
||||||
.format(function_name, jid_full))
|
.format(function_name, jid_full))
|
||||||
text_note = ('PubSub support will be available soon.')
|
values = payload['values']
|
||||||
session['notes'] = [['info', text_note]]
|
jid = values['jid']
|
||||||
session['payload'] = None
|
jid_bare = session['from'].bare
|
||||||
|
if jid != jid_bare and not is_operator(self, jid_bare):
|
||||||
|
text_warn = ('Posting to {} is restricted to operators only.'
|
||||||
|
.format(jid_bare))
|
||||||
|
session['allow_prev'] = False
|
||||||
|
session['has_next'] = False
|
||||||
|
session['next'] = None
|
||||||
|
session['notes'] = [['warn', text_warn]]
|
||||||
|
session['prev'] = None
|
||||||
|
session['payload'] = None
|
||||||
|
return session
|
||||||
|
node = values['node']
|
||||||
|
url = values['url']
|
||||||
|
xep = values['xep']
|
||||||
|
form = self['xep_0004'].make_form('form', 'Publish')
|
||||||
|
while True:
|
||||||
|
result = await fetch.http(url)
|
||||||
|
status = result['status_code']
|
||||||
|
if not result['error']:
|
||||||
|
document = result['content']
|
||||||
|
feed = parse(document)
|
||||||
|
# if is_feed(url, feed):
|
||||||
|
if action.is_feed(feed):
|
||||||
|
form['instructions'] = 'Select entries to publish.'
|
||||||
|
options = form.add_field(var='entries',
|
||||||
|
ftype='list-multi',
|
||||||
|
label='Titles',
|
||||||
|
desc='Select entries to post.',
|
||||||
|
required=True)
|
||||||
|
if "title" in feed["feed"].keys():
|
||||||
|
title = feed["feed"]["title"]
|
||||||
|
else:
|
||||||
|
title = url
|
||||||
|
entries = feed.entries
|
||||||
|
entry_ix = 0
|
||||||
|
for entry in entries:
|
||||||
|
if entry.has_key("title"):
|
||||||
|
title = entry.title
|
||||||
|
else:
|
||||||
|
if entry.has_key("published"):
|
||||||
|
title = entry.published
|
||||||
|
title = dt.rfc2822_to_iso8601(title)
|
||||||
|
elif entry.has_key("updated"):
|
||||||
|
title = entry.updated
|
||||||
|
title = dt.rfc2822_to_iso8601(title)
|
||||||
|
else:
|
||||||
|
title = "*** No title ***"
|
||||||
|
options.addOption(title, str(entry_ix))
|
||||||
|
entry_ix += 1
|
||||||
|
if entry_ix > 9:
|
||||||
|
break
|
||||||
|
session['allow_prev'] = True
|
||||||
|
session['has_next'] = True
|
||||||
|
session['next'] = self._handle_post_complete
|
||||||
|
session['prev'] = self._handle_post
|
||||||
|
session['payload'] = form
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
result = await crawl.probe_page(url, document)
|
||||||
|
if isinstance(result, list):
|
||||||
|
form['instructions'] = ('Discovered {} subscriptions '
|
||||||
|
'from the given URL. Please '
|
||||||
|
'choose a subscription.'
|
||||||
|
.format(len(result)))
|
||||||
|
options = form.add_field(var='url',
|
||||||
|
ftype='list-single',
|
||||||
|
label='Feeds',
|
||||||
|
desc='Select a feed.',
|
||||||
|
required=True)
|
||||||
|
results = result
|
||||||
|
for result in results:
|
||||||
|
title = result['name']
|
||||||
|
url = result['link']
|
||||||
|
title = title if title else url
|
||||||
|
options.addOption(title, url)
|
||||||
|
session['allow_prev'] = True
|
||||||
|
session['has_next'] = True
|
||||||
|
session['next'] = self._handle_preview
|
||||||
|
session['prev'] = self._handle_post
|
||||||
|
session['payload'] = form
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
url = result['link']
|
||||||
|
else:
|
||||||
|
text_error = ('Failed to load URL {}'
|
||||||
|
'\n\n'
|
||||||
|
'Reason: {}'
|
||||||
|
.format(url, status))
|
||||||
|
session['notes'] = [['error', text_error]]
|
||||||
|
session['payload'] = None
|
||||||
|
break
|
||||||
|
form.add_field(var='node',
|
||||||
|
ftype='hidden',
|
||||||
|
value=node)
|
||||||
|
form.add_field(var='jid',
|
||||||
|
ftype='hidden',
|
||||||
|
value=jid)
|
||||||
|
# It is essential to place URL at the end, because it might mutate
|
||||||
|
# For example http://blacklistednews.com would change
|
||||||
|
# to https://www.blacklistednews.com/rss.php
|
||||||
|
form.add_field(var='url',
|
||||||
|
ftype='hidden',
|
||||||
|
value=url)
|
||||||
|
form.add_field(var='xep',
|
||||||
|
ftype='hidden',
|
||||||
|
value=xep)
|
||||||
|
return session
|
||||||
|
|
||||||
|
async def _handle_post_complete(self, payload, session):
|
||||||
|
values = payload['values']
|
||||||
|
entries = values['entries']
|
||||||
|
# It might not be good to pass feed object as its size might be too big
|
||||||
|
# Consider a handler self.feeds[url][feed] or self.feeds[jid][url][feed]
|
||||||
|
# It is not possible to assign non-str to transfer.
|
||||||
|
# feed = values['feed']
|
||||||
|
node = values['node'][0]
|
||||||
|
jid = values['jid'][0]
|
||||||
|
url = values['url'][0]
|
||||||
|
xep = values['xep'][0]
|
||||||
|
result = await fetch.http(url)
|
||||||
|
if 'content' in result:
|
||||||
|
document = result['content']
|
||||||
|
feed = parse(document)
|
||||||
|
if feed.feed.has_key('title'):
|
||||||
|
feed_title = feed.feed.title
|
||||||
|
if feed.feed.has_key('description'):
|
||||||
|
feed_summary = feed.feed.description
|
||||||
|
elif feed.feed.has_key('subtitle'):
|
||||||
|
feed_summary = feed.feed.subtitle
|
||||||
|
else:
|
||||||
|
feed_summary = None
|
||||||
|
iq_create_node = XmppPubsub.create_node(
|
||||||
|
self, jid, node, xep, feed_title, feed_summary)
|
||||||
|
await XmppIQ.send(self, iq_create_node)
|
||||||
|
feed_version = feed.version
|
||||||
|
for entry in entries:
|
||||||
|
entry = int(entry)
|
||||||
|
feed_entry = feed.entries[entry]
|
||||||
|
# if feed.entries[entry].has_key("title"):
|
||||||
|
# title = feed.entries[entry].title
|
||||||
|
# else:
|
||||||
|
# if feed.entries[entry].has_key("published"):
|
||||||
|
# title = feed.entries[entry].published
|
||||||
|
# title = dt.rfc2822_to_iso8601(title)
|
||||||
|
# elif feed.entries[entry].has_key("updated"):
|
||||||
|
# title = feed.entries[entry].updated
|
||||||
|
# title = dt.rfc2822_to_iso8601(title)
|
||||||
|
# else:
|
||||||
|
# title = "*** No title ***"
|
||||||
|
# if feed.entries[entry].has_key("summary"):
|
||||||
|
# summary = feed.entries[entry].summary
|
||||||
|
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))
|
||||||
|
session['allow_prev'] = False
|
||||||
|
session['has_next'] = False
|
||||||
|
session['next'] = None
|
||||||
|
session['notes'] = [['info', text_info]]
|
||||||
|
session['prev'] = None
|
||||||
|
session['payload'] = None
|
||||||
|
else:
|
||||||
|
session['payload'] = payload
|
||||||
return session
|
return session
|
||||||
|
|
||||||
async def _handle_profile(self, iq, session):
|
async def _handle_profile(self, iq, session):
|
||||||
|
@ -909,9 +1102,8 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
db_file = config.get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
# In this case (as is typical), the payload is a form
|
# In this case (as is typical), the payload is a form
|
||||||
values = payload['values']
|
values = payload['values']
|
||||||
for value in values:
|
for key in values:
|
||||||
key = value
|
val = values[key]
|
||||||
val = values[value]
|
|
||||||
# NOTE We might want to add new keywords from
|
# NOTE We might want to add new keywords from
|
||||||
# an empty form instead of editing a form.
|
# an empty form instead of editing a form.
|
||||||
# keywords = sqlite.get_filter_value(db_file, key)
|
# keywords = sqlite.get_filter_value(db_file, key)
|
||||||
|
@ -932,6 +1124,80 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
return session
|
return session
|
||||||
|
|
||||||
|
|
||||||
|
async def _handle_pubsub_add(self, iq, session):
|
||||||
|
jid_full = str(session['from'])
|
||||||
|
function_name = sys._getframe().f_code.co_name
|
||||||
|
logger.debug('{}: jid_full: {}'
|
||||||
|
.format(function_name, jid_full))
|
||||||
|
jid_bare = session['from'].bare
|
||||||
|
if not is_operator(self, jid_bare):
|
||||||
|
text_warn = 'PubSub is not available.'
|
||||||
|
session['notes'] = [['warn', text_warn]]
|
||||||
|
return session
|
||||||
|
chat_type = await get_chat_type(self, jid_bare)
|
||||||
|
moderator = None
|
||||||
|
if chat_type == 'groupchat':
|
||||||
|
moderator = is_moderator(self, jid_bare, jid_full)
|
||||||
|
if chat_type == 'chat' or moderator:
|
||||||
|
form = self['xep_0004'].make_form('form', 'Publish')
|
||||||
|
form['instructions'] = ('In order to publish via PubSub, you will '
|
||||||
|
'have to choose a PubSub Jabber ID (e.g. '
|
||||||
|
'{}) and Slixfeed has to be allowed to '
|
||||||
|
'publish into it.'
|
||||||
|
.format('pubsub.' + self.boundjid.host))
|
||||||
|
form.add_field(var='subscription',
|
||||||
|
# TODO Make it possible to add several subscriptions at once;
|
||||||
|
# Similarly to BitTorrent trackers list
|
||||||
|
# ftype='text-multi',
|
||||||
|
# label='Subscription URLs',
|
||||||
|
# desc=('Add subscriptions one time per '
|
||||||
|
# 'subscription.'),
|
||||||
|
ftype='text-single',
|
||||||
|
label='URL',
|
||||||
|
desc='Enter a subscription URL.',
|
||||||
|
value='http://',
|
||||||
|
required=True)
|
||||||
|
form.add_field(var='jid',
|
||||||
|
ftype='text-single',
|
||||||
|
label='PubSub',
|
||||||
|
desc='Enter a PubSub Jabber ID.',
|
||||||
|
value=self.boundjid.bare,
|
||||||
|
required=True)
|
||||||
|
form.add_field(var='node',
|
||||||
|
ftype='text-single',
|
||||||
|
label='Node',
|
||||||
|
desc=('Enter a node to publish to (The default '
|
||||||
|
'value is "urn:xmpp:microblog:0" which '
|
||||||
|
'is allocated to each Jabber ID in the '
|
||||||
|
'Movim system).'),
|
||||||
|
value='urn:xmpp:microblog:0',
|
||||||
|
required=False)
|
||||||
|
options = form.add_field(var='xep',
|
||||||
|
ftype='list-single',
|
||||||
|
label='Protocol',
|
||||||
|
desc='Select XMPP Extension Protocol.',
|
||||||
|
value='0060',
|
||||||
|
required=True)
|
||||||
|
options.addOption('XEP-0060: Publish-Subscribe', '0060')
|
||||||
|
options.addOption('XEP-0277: Microblogging over XMPP', '0277')
|
||||||
|
options.addOption('XEP-0472: Pubsub Social Feed', '0472')
|
||||||
|
# form.add_field(var='scan',
|
||||||
|
# ftype='boolean',
|
||||||
|
# label='Scan',
|
||||||
|
# desc='Scan URL for validity (recommended).',
|
||||||
|
# value=True)
|
||||||
|
session['allow_prev'] = False
|
||||||
|
session['has_next'] = True
|
||||||
|
session['next'] = self._handle_subscription_new
|
||||||
|
session['prev'] = None
|
||||||
|
session['payload'] = form
|
||||||
|
else:
|
||||||
|
text_warn = ('This resource is restricted to moderators of {}.'
|
||||||
|
.format(jid_bare))
|
||||||
|
session['notes'] = [['warn', text_warn]]
|
||||||
|
return session
|
||||||
|
|
||||||
|
|
||||||
async def _handle_subscription_add(self, iq, session):
|
async def _handle_subscription_add(self, iq, session):
|
||||||
jid_full = str(session['from'])
|
jid_full = str(session['from'])
|
||||||
function_name = sys._getframe().f_code.co_name
|
function_name = sys._getframe().f_code.co_name
|
||||||
|
@ -954,9 +1220,18 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
# 'subscription.'),
|
# 'subscription.'),
|
||||||
ftype='text-single',
|
ftype='text-single',
|
||||||
label='URL',
|
label='URL',
|
||||||
desc='Enter subscription URL.',
|
desc='Enter a subscription URL.',
|
||||||
value='http://',
|
value='http://',
|
||||||
required=True)
|
required=True)
|
||||||
|
if is_operator(self, jid_bare):
|
||||||
|
form.add_field(ftype='fixed',
|
||||||
|
label='Subscriber')
|
||||||
|
form.add_field(var='jid',
|
||||||
|
ftype='text-single',
|
||||||
|
label='Jabber ID',
|
||||||
|
desc=('Enter a Jabber ID to add the '
|
||||||
|
'subscription to (The default Jabber ID '
|
||||||
|
'is your own).'))
|
||||||
# form.add_field(var='scan',
|
# form.add_field(var='scan',
|
||||||
# ftype='boolean',
|
# ftype='boolean',
|
||||||
# label='Scan',
|
# label='Scan',
|
||||||
|
@ -1975,7 +2250,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
ftype='list-single',
|
ftype='list-single',
|
||||||
label='Choose',
|
label='Choose',
|
||||||
required=True,
|
required=True,
|
||||||
value='admin')
|
value='export')
|
||||||
jid = session['from'].bare
|
jid = session['from'].bare
|
||||||
if is_operator(self, jid):
|
if is_operator(self, jid):
|
||||||
options.addOption('Administration', 'admin')
|
options.addOption('Administration', 'admin')
|
||||||
|
@ -2250,7 +2525,8 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
jid_bare = session['from'].bare
|
jid_bare = session['from'].bare
|
||||||
jid_file = jid_bare.replace('/', '_')
|
jid_file = jid_bare.replace('/', '_')
|
||||||
db_file = config.get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
count = await action.import_opml(db_file, url)
|
result = await fetch.http(url)
|
||||||
|
count = await action.import_opml(db_file, result)
|
||||||
try:
|
try:
|
||||||
int(count)
|
int(count)
|
||||||
# form = self['xep_0004'].make_form('result', 'Done')
|
# form = self['xep_0004'].make_form('result', 'Done')
|
||||||
|
@ -2827,9 +3103,8 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
Config.add_settings_jid(self.settings, jid_bare, db_file)
|
Config.add_settings_jid(self.settings, jid_bare, db_file)
|
||||||
# In this case (as is typical), the payload is a form
|
# In this case (as is typical), the payload is a form
|
||||||
values = payload['values']
|
values = payload['values']
|
||||||
for value in values:
|
for key in values:
|
||||||
key = value
|
val = values[key]
|
||||||
val = values[value]
|
|
||||||
|
|
||||||
if key in ('enabled', 'media', 'old'):
|
if key in ('enabled', 'media', 'old'):
|
||||||
if val == True:
|
if val == True:
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from slixmpp.exceptions import IqError
|
import logging
|
||||||
|
from slixmpp.exceptions import IqError, IqTimeout
|
||||||
|
|
||||||
class XmppIQ:
|
class XmppIQ:
|
||||||
|
|
||||||
async def send(self, iq):
|
async def send(self, iq):
|
||||||
try:
|
try:
|
||||||
await iq.send(timeout=5)
|
await iq.send(timeout=5)
|
||||||
except IqError as e:
|
except (IqError, IqTimeout) as e:
|
||||||
if e.etype == 'cancel' and e.condition == 'conflict':
|
logging.error('Error XmppIQ')
|
||||||
return
|
logging.error(str(e))
|
||||||
raise
|
|
||||||
|
|
|
@ -23,14 +23,13 @@ TODO
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from slixfeed.xmpp.publish import XmppPubsub
|
|
||||||
from slixfeed.xmpp.iq import XmppIQ
|
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from feedparser import parse
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import slixfeed.action as action
|
import slixfeed.action as action
|
||||||
import slixfeed.config as config
|
import slixfeed.config as config
|
||||||
|
import slixfeed.crawl as crawl
|
||||||
from slixfeed.config import Config
|
from slixfeed.config import Config
|
||||||
import slixfeed.dt as dt
|
import slixfeed.dt as dt
|
||||||
import slixfeed.fetch as fetch
|
import slixfeed.fetch as fetch
|
||||||
|
@ -109,15 +108,19 @@ async def message(self, message):
|
||||||
# pending_tasks_num = len(self.pending_tasks[jid_bare])
|
# pending_tasks_num = len(self.pending_tasks[jid_bare])
|
||||||
pending_tasks_num = randrange(10000, 99999)
|
pending_tasks_num = randrange(10000, 99999)
|
||||||
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
||||||
|
# self.pending_tasks_counter += 1
|
||||||
|
# self.pending_tasks[jid_bare][self.pending_tasks_counter] = status_message
|
||||||
XmppPresence.send(self, jid_bare, status_message,
|
XmppPresence.send(self, jid_bare, status_message,
|
||||||
status_type=status_type)
|
status_type=status_type)
|
||||||
db_file = config.get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
count = await action.import_opml(db_file, url)
|
result = await fetch.http(url)
|
||||||
|
count = await action.import_opml(db_file, result)
|
||||||
if count:
|
if count:
|
||||||
response = 'Successfully imported {} feeds.'.format(count)
|
response = 'Successfully imported {} feeds.'.format(count)
|
||||||
else:
|
else:
|
||||||
response = 'OPML file was not imported.'
|
response = 'OPML file was not imported.'
|
||||||
del self.pending_tasks[jid_bare][pending_tasks_num]
|
del self.pending_tasks[jid_bare][pending_tasks_num]
|
||||||
|
# del self.pending_tasks[jid_bare][self.pending_tasks_counter]
|
||||||
key_list = ['status']
|
key_list = ['status']
|
||||||
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
||||||
XmppMessage.send_reply(self, message, response)
|
XmppMessage.send_reply(self, message, response)
|
||||||
|
@ -286,13 +289,6 @@ async def message(self, message):
|
||||||
'Send "help" for further instructions.\n'
|
'Send "help" for further instructions.\n'
|
||||||
.format(self.alias))
|
.format(self.alias))
|
||||||
XmppMessage.send_reply(self, message, response)
|
XmppMessage.send_reply(self, message, response)
|
||||||
|
|
||||||
title = message_lowercase
|
|
||||||
summary = message_text
|
|
||||||
# from random import randrange
|
|
||||||
node = str(randrange(10000, 99999))
|
|
||||||
iq = XmppPubsub.create(self, title, summary, node)
|
|
||||||
await XmppIQ.send(self, iq)
|
|
||||||
|
|
||||||
# case _ if message_lowercase.startswith('activate'):
|
# case _ if message_lowercase.startswith('activate'):
|
||||||
# if message['type'] == 'groupchat':
|
# if message['type'] == 'groupchat':
|
||||||
|
@ -529,6 +525,8 @@ async def message(self, message):
|
||||||
# pending_tasks_num = len(self.pending_tasks[jid_bare])
|
# pending_tasks_num = len(self.pending_tasks[jid_bare])
|
||||||
pending_tasks_num = randrange(10000, 99999)
|
pending_tasks_num = randrange(10000, 99999)
|
||||||
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
||||||
|
# self.pending_tasks_counter += 1
|
||||||
|
# self.pending_tasks[jid_bare][self.pending_tasks_counter] = status_message
|
||||||
XmppPresence.send(self, jid_bare, status_message,
|
XmppPresence.send(self, jid_bare, status_message,
|
||||||
status_type=status_type)
|
status_type=status_type)
|
||||||
filename = await action.export_feeds(self, jid_bare,
|
filename = await action.export_feeds(self, jid_bare,
|
||||||
|
@ -541,6 +539,7 @@ async def message(self, message):
|
||||||
chat_type = await get_chat_type(self, jid_bare)
|
chat_type = await get_chat_type(self, jid_bare)
|
||||||
XmppMessage.send_oob(self, jid_bare, url, chat_type)
|
XmppMessage.send_oob(self, jid_bare, url, chat_type)
|
||||||
del self.pending_tasks[jid_bare][pending_tasks_num]
|
del self.pending_tasks[jid_bare][pending_tasks_num]
|
||||||
|
# del self.pending_tasks[jid_bare][self.pending_tasks_counter]
|
||||||
key_list = ['status']
|
key_list = ['status']
|
||||||
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
||||||
else:
|
else:
|
||||||
|
@ -579,6 +578,8 @@ async def message(self, message):
|
||||||
# pending_tasks_num = len(self.pending_tasks[jid_bare])
|
# pending_tasks_num = len(self.pending_tasks[jid_bare])
|
||||||
pending_tasks_num = randrange(10000, 99999)
|
pending_tasks_num = randrange(10000, 99999)
|
||||||
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
||||||
|
# self.pending_tasks_counter += 1
|
||||||
|
# self.pending_tasks[jid_bare][self.pending_tasks_counter] = status_message
|
||||||
XmppPresence.send(self, jid_bare, status_message,
|
XmppPresence.send(self, jid_bare, status_message,
|
||||||
status_type=status_type)
|
status_type=status_type)
|
||||||
db_file = config.get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
|
@ -642,6 +643,7 @@ async def message(self, message):
|
||||||
'Try: epub, html, md (markdown), '
|
'Try: epub, html, md (markdown), '
|
||||||
'pdf, or txt (text)')
|
'pdf, or txt (text)')
|
||||||
del self.pending_tasks[jid_bare][pending_tasks_num]
|
del self.pending_tasks[jid_bare][pending_tasks_num]
|
||||||
|
# del self.pending_tasks[jid_bare][self.pending_tasks_counter]
|
||||||
key_list = ['status']
|
key_list = ['status']
|
||||||
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
||||||
if response:
|
if response:
|
||||||
|
@ -657,16 +659,20 @@ async def message(self, message):
|
||||||
# pending_tasks_num = len(self.pending_tasks[jid_bare])
|
# pending_tasks_num = len(self.pending_tasks[jid_bare])
|
||||||
pending_tasks_num = randrange(10000, 99999)
|
pending_tasks_num = randrange(10000, 99999)
|
||||||
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
||||||
|
# self.pending_tasks_counter += 1
|
||||||
|
# self.pending_tasks[jid_bare][self.pending_tasks_counter] = status_message
|
||||||
XmppPresence.send(self, jid_bare, status_message,
|
XmppPresence.send(self, jid_bare, status_message,
|
||||||
status_type=status_type)
|
status_type=status_type)
|
||||||
db_file = config.get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
count = await action.import_opml(db_file, url)
|
result = await fetch.http(url)
|
||||||
|
count = await action.import_opml(db_file, result)
|
||||||
if count:
|
if count:
|
||||||
response = ('Successfully imported {} feeds.'
|
response = ('Successfully imported {} feeds.'
|
||||||
.format(count))
|
.format(count))
|
||||||
else:
|
else:
|
||||||
response = 'OPML file was not imported.'
|
response = 'OPML file was not imported.'
|
||||||
del self.pending_tasks[jid_bare][pending_tasks_num]
|
del self.pending_tasks[jid_bare][pending_tasks_num]
|
||||||
|
# del self.pending_tasks[jid_bare][self.pending_tasks_counter]
|
||||||
key_list = ['status']
|
key_list = ['status']
|
||||||
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
||||||
XmppMessage.send_reply(self, message, response)
|
XmppMessage.send_reply(self, message, response)
|
||||||
|
@ -680,7 +686,8 @@ async def message(self, message):
|
||||||
# pending_tasks_num = len(self.pending_tasks[jid_bare])
|
# pending_tasks_num = len(self.pending_tasks[jid_bare])
|
||||||
pending_tasks_num = randrange(10000, 99999)
|
pending_tasks_num = randrange(10000, 99999)
|
||||||
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
||||||
print(self.pending_tasks)
|
# self.pending_tasks_counter += 1
|
||||||
|
# self.pending_tasks[jid_bare][self.pending_tasks_counter] = status_message
|
||||||
XmppPresence.send(self, jid_bare, status_message,
|
XmppPresence.send(self, jid_bare, status_message,
|
||||||
status_type=status_type)
|
status_type=status_type)
|
||||||
if url.startswith('feed:'):
|
if url.startswith('feed:'):
|
||||||
|
@ -716,8 +723,8 @@ async def message(self, message):
|
||||||
'added to subscription list.'
|
'added to subscription list.'
|
||||||
.format(result['link'], result['name']))
|
.format(result['link'], result['name']))
|
||||||
# task.clean_tasks_xmpp(self, jid_bare, ['status'])
|
# task.clean_tasks_xmpp(self, jid_bare, ['status'])
|
||||||
print(self.pending_tasks)
|
|
||||||
del self.pending_tasks[jid_bare][pending_tasks_num]
|
del self.pending_tasks[jid_bare][pending_tasks_num]
|
||||||
|
# del self.pending_tasks[jid_bare][self.pending_tasks_counter]
|
||||||
print(self.pending_tasks)
|
print(self.pending_tasks)
|
||||||
key_list = ['status']
|
key_list = ['status']
|
||||||
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
||||||
|
@ -929,7 +936,7 @@ async def message(self, message):
|
||||||
status_type = 'dnd'
|
status_type = 'dnd'
|
||||||
status_message = ('📫️ Processing request to fetch data '
|
status_message = ('📫️ Processing request to fetch data '
|
||||||
'from {}'.format(url))
|
'from {}'.format(url))
|
||||||
|
pending_tasks_num = randrange(10000, 99999)
|
||||||
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
||||||
XmppPresence.send(self, jid_bare, status_message,
|
XmppPresence.send(self, jid_bare, status_message,
|
||||||
status_type=status_type)
|
status_type=status_type)
|
||||||
|
@ -939,7 +946,36 @@ async def message(self, message):
|
||||||
match len(data):
|
match len(data):
|
||||||
case 1:
|
case 1:
|
||||||
if url.startswith('http'):
|
if url.startswith('http'):
|
||||||
response = await action.view_feed(url)
|
while True:
|
||||||
|
result = await fetch.http(url)
|
||||||
|
if not result['error']:
|
||||||
|
document = result['content']
|
||||||
|
status = result['status_code']
|
||||||
|
feed = parse(document)
|
||||||
|
# if is_feed(url, feed):
|
||||||
|
if action.is_feed(feed):
|
||||||
|
response = action.view_feed(url, feed)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
result = await crawl.probe_page(url, document)
|
||||||
|
if isinstance(result, list):
|
||||||
|
results = result
|
||||||
|
response = ("Web feeds found for {}\n\n```\n"
|
||||||
|
.format(url))
|
||||||
|
for result in results:
|
||||||
|
response += ("Title : {}\n"
|
||||||
|
"Link : {}\n"
|
||||||
|
"\n"
|
||||||
|
.format(result['name'], result['link']))
|
||||||
|
response += ('```\nTotal of {} feeds.'
|
||||||
|
.format(len(results)))
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
url = result[0]
|
||||||
|
else:
|
||||||
|
response = ('> {}\nFailed to load URL. Reason: {}'
|
||||||
|
.format(url, status))
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
response = ('No action has been taken.'
|
response = ('No action has been taken.'
|
||||||
'\n'
|
'\n'
|
||||||
|
@ -947,7 +983,36 @@ async def message(self, message):
|
||||||
case 2:
|
case 2:
|
||||||
num = data[1]
|
num = data[1]
|
||||||
if url.startswith('http'):
|
if url.startswith('http'):
|
||||||
response = await action.view_entry(url, num)
|
while True:
|
||||||
|
result = await fetch.http(url)
|
||||||
|
if not result['error']:
|
||||||
|
document = result['content']
|
||||||
|
status = result['status_code']
|
||||||
|
feed = parse(document)
|
||||||
|
# if is_feed(url, feed):
|
||||||
|
if action.is_feed(feed):
|
||||||
|
response = action.view_entry(url, feed, num)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
result = await crawl.probe_page(url, document)
|
||||||
|
if isinstance(result, list):
|
||||||
|
results = result
|
||||||
|
response = ("Web feeds found for {}\n\n```\n"
|
||||||
|
.format(url))
|
||||||
|
for result in results:
|
||||||
|
response += ("Title : {}\n"
|
||||||
|
"Link : {}\n"
|
||||||
|
"\n"
|
||||||
|
.format(result['name'], result['link']))
|
||||||
|
response += ('```\nTotal of {} feeds.'
|
||||||
|
.format(len(results)))
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
url = result[0]
|
||||||
|
else:
|
||||||
|
response = ('> {}\nFailed to load URL. Reason: {}'
|
||||||
|
.format(url, status))
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
response = ('No action has been taken.'
|
response = ('No action has been taken.'
|
||||||
'\n'
|
'\n'
|
||||||
|
@ -1041,6 +1106,8 @@ async def message(self, message):
|
||||||
# pending_tasks_num = len(self.pending_tasks[jid_bare])
|
# pending_tasks_num = len(self.pending_tasks[jid_bare])
|
||||||
pending_tasks_num = randrange(10000, 99999)
|
pending_tasks_num = randrange(10000, 99999)
|
||||||
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
self.pending_tasks[jid_bare][pending_tasks_num] = status_message
|
||||||
|
# self.pending_tasks_counter += 1
|
||||||
|
# self.pending_tasks[jid_bare][self.pending_tasks_counter] = status_message
|
||||||
XmppPresence.send(self, jid_bare, status_message,
|
XmppPresence.send(self, jid_bare, status_message,
|
||||||
status_type=status_type)
|
status_type=status_type)
|
||||||
db_file = config.get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
|
@ -1083,6 +1150,7 @@ async def message(self, message):
|
||||||
response = 'All entries have been marked as read.'
|
response = 'All entries have been marked as read.'
|
||||||
XmppMessage.send_reply(self, message, response)
|
XmppMessage.send_reply(self, message, response)
|
||||||
del self.pending_tasks[jid_bare][pending_tasks_num]
|
del self.pending_tasks[jid_bare][pending_tasks_num]
|
||||||
|
# del self.pending_tasks[jid_bare][self.pending_tasks_counter]
|
||||||
key_list = ['status']
|
key_list = ['status']
|
||||||
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
||||||
case _ if message_lowercase.startswith('search '):
|
case _ if message_lowercase.startswith('search '):
|
||||||
|
|
|
@ -3,12 +3,18 @@
|
||||||
|
|
||||||
# TODO Implement XEP-0472: Pubsub Social Feed
|
# TODO Implement XEP-0472: Pubsub Social Feed
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import slixmpp.plugins.xep_0060.stanza.pubsub as pubsub
|
||||||
|
from slixmpp.xmlstream import ET
|
||||||
|
|
||||||
class XmppPubsub:
|
class XmppPubsub:
|
||||||
|
|
||||||
def create(self, title, summary, node, server):
|
|
||||||
|
# TODO Make use of var "xep" with match/case
|
||||||
|
def create_node(self, jid, node, xep ,title, summary=None):
|
||||||
jid_from = str(self.boundjid) if self.is_component else None
|
jid_from = str(self.boundjid) if self.is_component else None
|
||||||
iq = self.Iq(stype="set",
|
iq = self.Iq(stype='set',
|
||||||
sto=server,
|
sto=jid,
|
||||||
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']
|
||||||
|
@ -42,3 +48,104 @@ class XmppPubsub:
|
||||||
value='http://www.w3.org/2005/Atom')
|
value='http://www.w3.org/2005/Atom')
|
||||||
|
|
||||||
return iq
|
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 a good idea 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
|
||||||
|
|
||||||
|
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':
|
||||||
|
|
||||||
|
if hasattr(entry.content[0], 'type'):
|
||||||
|
content = ET.SubElement(node_entry, "content")
|
||||||
|
content.set('type', entry.content[0].type)
|
||||||
|
content.text = entry.content[0].value
|
||||||
|
|
||||||
|
elif version =='rss20' or 'rss10' or 'atom10':
|
||||||
|
if hasattr(entry, "content"):
|
||||||
|
content = ET.SubElement(node_entry, "content")
|
||||||
|
content.set('type', 'text/html')
|
||||||
|
content.text = entry.content[0].value
|
||||||
|
|
||||||
|
elif hasattr(entry, "description"):
|
||||||
|
content = ET.SubElement(node_entry,"content")
|
||||||
|
content.set('type', 'text/html')
|
||||||
|
content.text = entry.description
|
||||||
|
print('In Description - PublishX')
|
||||||
|
|
||||||
|
# Links
|
||||||
|
if hasattr(entry, 'links'):
|
||||||
|
for l in entry.links:
|
||||||
|
link = ET.SubElement(node_entry, "link")
|
||||||
|
if hasattr(l, 'href'):
|
||||||
|
link.set('href', l['href'])
|
||||||
|
link.set('type', l['type'])
|
||||||
|
link.set('rel', l['rel'])
|
||||||
|
elif hasattr(entry, 'link'):
|
||||||
|
link.set('href', entry['link'])
|
||||||
|
|
||||||
|
# Tags
|
||||||
|
if hasattr(entry, 'tags'):
|
||||||
|
for t in entry.tags:
|
||||||
|
tag = ET.SubElement(node_entry, "category")
|
||||||
|
tag.set('term', t.term)
|
||||||
|
|
||||||
|
# Categories
|
||||||
|
if hasattr(entry,'category'):
|
||||||
|
for c in entry["category"]:
|
||||||
|
cat = ET.SubElement(node_entry, "category")
|
||||||
|
cat.set('category', entry.category[0])
|
||||||
|
|
||||||
|
# Authors
|
||||||
|
if version == 'atom03':
|
||||||
|
if hasattr(entry, 'authors'):
|
||||||
|
author = ET.SubElement(node_entry, "author")
|
||||||
|
name = ET.SubElement(author, "name")
|
||||||
|
name.text = entry.authors[0].name
|
||||||
|
if hasattr(entry.authors[0], 'href'):
|
||||||
|
uri = ET.SubElement(author, "uri")
|
||||||
|
uri.text = entry.authors[0].href
|
||||||
|
|
||||||
|
elif version == 'rss20' or 'rss10' or 'atom10':
|
||||||
|
if hasattr(entry, 'author'):
|
||||||
|
author = ET.SubElement(node_entry, "author")
|
||||||
|
name = ET.SubElement(node_entry, "author")
|
||||||
|
name.text = entry.author
|
||||||
|
|
||||||
|
if hasattr(entry.author, 'href'):
|
||||||
|
uri = ET.SubElement(author, "uri")
|
||||||
|
uri.text = entry.authors[0].href
|
||||||
|
|
||||||
|
item['payload'] = node_entry
|
||||||
|
|
||||||
|
iq['pubsub']['publish'].append(item)
|
||||||
|
|
||||||
|
return iq
|
||||||
|
|
Loading…
Reference in a new issue