forked from sch/Slixfeed
Fix keywords extracted from sqlite.
Improve modiles fetch and crawl. Add form featured feeds. Add form roster manager. Add form subscibers manager. WIP
This commit is contained in:
parent
c1dec9d808
commit
7b98d32d7f
9 changed files with 1543 additions and 456 deletions
|
@ -498,22 +498,17 @@ def list_unread_entries(result, feed_title):
|
|||
link = result[2]
|
||||
link = remove_tracking_parameters(link)
|
||||
link = (replace_hostname(link, "link")) or link
|
||||
news_item = (
|
||||
"\n{}\n{}\n{} [{}]\n"
|
||||
).format(
|
||||
str(title), str(link), str(feed_title), str(ix)
|
||||
)
|
||||
news_item = ("\n{}\n{}\n{} [{}]\n").format(str(title), str(link),
|
||||
str(feed_title), str(ix))
|
||||
return news_item
|
||||
|
||||
|
||||
def list_search_results(query, results):
|
||||
message = (
|
||||
"Search results for '{}':\n\n```"
|
||||
).format(query)
|
||||
message = ("Search results for '{}':\n\n```"
|
||||
.format(query))
|
||||
for result in results:
|
||||
message += (
|
||||
"\n{}\n{}\n"
|
||||
).format(str(result[0]), str(result[1]))
|
||||
message += ("\n{}\n{}\n"
|
||||
.format(str(result[0]), str(result[1])))
|
||||
if len(results):
|
||||
message += "```\nTotal of {} results".format(len(results))
|
||||
else:
|
||||
|
@ -523,15 +518,13 @@ def list_search_results(query, results):
|
|||
|
||||
def list_feeds_by_query(db_file, query):
|
||||
results = sqlite.search_feeds(db_file, query)
|
||||
message = (
|
||||
'Feeds containing "{}":\n\n```'
|
||||
.format(query))
|
||||
message = ('Feeds containing "{}":\n\n```'
|
||||
.format(query))
|
||||
for result in results:
|
||||
message += (
|
||||
'\nName : {} [{}]'
|
||||
'\nURL : {}'
|
||||
'\n'
|
||||
.format(str(result[0]), str(result[1]), str(result[2])))
|
||||
message += ('\nName : {} [{}]'
|
||||
'\nURL : {}'
|
||||
'\n'
|
||||
.format(str(result[0]), str(result[1]), str(result[2])))
|
||||
if len(results):
|
||||
message += "\n```\nTotal of {} feeds".format(len(results))
|
||||
else:
|
||||
|
@ -607,6 +600,15 @@ def list_last_entries(results, num):
|
|||
return message
|
||||
|
||||
|
||||
def pick_a_feed(lang=None):
|
||||
config_dir = config.get_default_config_directory()
|
||||
with open(config_dir + '/' + 'feeds.toml', mode="rb") as feeds:
|
||||
urls = tomllib.load(feeds)
|
||||
import random
|
||||
url = random.choice(urls['feeds'])
|
||||
return url
|
||||
|
||||
|
||||
def list_feeds(results):
|
||||
message = "\nList of subscriptions:\n\n```\n"
|
||||
for result in results:
|
||||
|
@ -621,10 +623,11 @@ def list_feeds(results):
|
|||
message += ('```\nTotal of {} subscriptions.\n'
|
||||
.format(len(results)))
|
||||
else:
|
||||
message = ('List of subscriptions is empty.\n'
|
||||
'To add feed, send a URL\n'
|
||||
'Featured feed: '
|
||||
'https://reclaimthenet.org/feed/')
|
||||
url = pick_a_feed()
|
||||
message = ('List of subscriptions is empty. To add a feed, send a URL.'
|
||||
'Featured feed:\n*{}*\n{}'
|
||||
.format(url['name'],
|
||||
url['link']))
|
||||
return message
|
||||
|
||||
|
||||
|
@ -678,8 +681,8 @@ def export_to_opml(jid, filename, results):
|
|||
|
||||
async def import_opml(db_file, url):
|
||||
result = await fetch.http(url)
|
||||
document = result[0]
|
||||
if document:
|
||||
if not result['error']:
|
||||
document = result['content']
|
||||
root = ET.fromstring(document)
|
||||
before = await sqlite.get_number_of_items(
|
||||
db_file, 'feeds')
|
||||
|
@ -703,9 +706,9 @@ async def add_feed(db_file, url):
|
|||
exist = await sqlite.get_feed_id_and_name(db_file, url)
|
||||
if not exist:
|
||||
result = await fetch.http(url)
|
||||
document = result[0]
|
||||
status_code = result[1]
|
||||
if document:
|
||||
if not result['error']:
|
||||
document = result['content']
|
||||
status_code = result['status_code']
|
||||
feed = parse(document)
|
||||
# if is_feed(url, feed):
|
||||
if is_feed(feed):
|
||||
|
@ -745,7 +748,7 @@ async def add_feed(db_file, url):
|
|||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
await sqlite.mark_feed_as_read(db_file, feed_id)
|
||||
result_final = {'url' : url,
|
||||
result_final = {'link' : url,
|
||||
'index' : feed_id,
|
||||
'name' : title,
|
||||
'code' : status_code,
|
||||
|
@ -795,7 +798,7 @@ async def add_feed(db_file, url):
|
|||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
await sqlite.mark_feed_as_read(db_file, feed_id)
|
||||
result_final = {'url' : url,
|
||||
result_final = {'link' : url,
|
||||
'index' : feed_id,
|
||||
'name' : title,
|
||||
'code' : status_code,
|
||||
|
@ -808,15 +811,26 @@ async def add_feed(db_file, url):
|
|||
else:
|
||||
# NOTE Do not be tempted to return a compact dictionary.
|
||||
# That is, dictionary within dictionary
|
||||
# Return multimple dictionaries.
|
||||
# Return multiple dictionaries in a list or tuple.
|
||||
result = await crawl.probe_page(url, document)
|
||||
if isinstance(result, list):
|
||||
if not result:
|
||||
# Get out of the loop with dict indicating error.
|
||||
result_final = {'link' : url,
|
||||
'index' : None,
|
||||
'name' : None,
|
||||
'code' : status_code,
|
||||
'error' : True,
|
||||
'exist' : False}
|
||||
break
|
||||
elif isinstance(result, list):
|
||||
# Get out of the loop and deliver a list of dicts.
|
||||
result_final = result
|
||||
break
|
||||
else:
|
||||
url = result['url']
|
||||
# Go back up to the while loop and try again.
|
||||
url = result['link']
|
||||
else:
|
||||
result_final = {'url' : url,
|
||||
result_final = {'link' : url,
|
||||
'index' : None,
|
||||
'name' : None,
|
||||
'code' : status_code,
|
||||
|
@ -828,7 +842,7 @@ async def add_feed(db_file, url):
|
|||
else:
|
||||
ix = exist[0]
|
||||
name = exist[1]
|
||||
result_final = {'url' : url,
|
||||
result_final = {'link' : url,
|
||||
'index' : ix,
|
||||
'name' : name,
|
||||
'code' : None,
|
||||
|
@ -854,145 +868,142 @@ async def scan_json(db_file, url):
|
|||
"""
|
||||
if isinstance(url, tuple): url = url[0]
|
||||
result = await fetch.http(url)
|
||||
try:
|
||||
document = result[0]
|
||||
status = result[1]
|
||||
except:
|
||||
return
|
||||
new_entries = []
|
||||
if document and status == 200:
|
||||
feed = json.loads(document)
|
||||
entries = feed["items"]
|
||||
await remove_nonexistent_entries_json(
|
||||
db_file, url, feed)
|
||||
try:
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
# await sqlite.update_feed_validity(
|
||||
# db_file, feed_id, valid)
|
||||
if "date_published" in feed.keys():
|
||||
updated = feed["date_published"]
|
||||
try:
|
||||
updated = dt.convert_struct_time_to_iso8601(updated)
|
||||
except:
|
||||
if not result['error']:
|
||||
document = result['content']
|
||||
status = result['status_code']
|
||||
new_entries = []
|
||||
if document and status == 200:
|
||||
feed = json.loads(document)
|
||||
entries = feed["items"]
|
||||
await remove_nonexistent_entries_json(
|
||||
db_file, url, feed)
|
||||
try:
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
# await sqlite.update_feed_validity(
|
||||
# db_file, feed_id, valid)
|
||||
if "date_published" in feed.keys():
|
||||
updated = feed["date_published"]
|
||||
try:
|
||||
updated = dt.convert_struct_time_to_iso8601(updated)
|
||||
except:
|
||||
updated = ''
|
||||
else:
|
||||
updated = ''
|
||||
else:
|
||||
updated = ''
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
await sqlite.update_feed_properties(
|
||||
db_file, feed_id, len(feed["items"]), updated)
|
||||
# await update_feed_status
|
||||
except (
|
||||
IncompleteReadError,
|
||||
IncompleteRead,
|
||||
error.URLError
|
||||
) as e:
|
||||
logging.error(e)
|
||||
return
|
||||
# new_entry = 0
|
||||
for entry in entries:
|
||||
if "date_published" in entry.keys():
|
||||
date = entry["date_published"]
|
||||
date = dt.rfc2822_to_iso8601(date)
|
||||
elif "date_modified" in entry.keys():
|
||||
date = entry["date_modified"]
|
||||
date = dt.rfc2822_to_iso8601(date)
|
||||
else:
|
||||
date = dt.now()
|
||||
if "url" in entry.keys():
|
||||
# link = complete_url(source, entry.link)
|
||||
link = join_url(url, entry["url"])
|
||||
link = trim_url(link)
|
||||
else:
|
||||
link = url
|
||||
# title = feed["feed"]["title"]
|
||||
# title = "{}: *{}*".format(feed["feed"]["title"], entry.title)
|
||||
title = entry["title"] if "title" in entry.keys() else date
|
||||
entry_id = entry["id"] if "id" in entry.keys() else link
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
exist = await sqlite.check_entry_exist(
|
||||
db_file, feed_id, entry_id=entry_id,
|
||||
title=title, link=link, date=date)
|
||||
if not exist:
|
||||
summary = entry["summary"] if "summary" in entry.keys() else ''
|
||||
if not summary:
|
||||
summary = (entry["content_html"]
|
||||
if "content_html" in entry.keys()
|
||||
else '')
|
||||
if not summary:
|
||||
summary = (entry["content_text"]
|
||||
if "content_text" in entry.keys()
|
||||
else '')
|
||||
read_status = 0
|
||||
pathname = urlsplit(link).path
|
||||
string = (
|
||||
"{} {} {}"
|
||||
).format(
|
||||
title, summary, pathname)
|
||||
allow_list = config.is_include_keyword(db_file, "allow",
|
||||
string)
|
||||
if not allow_list:
|
||||
reject_list = config.is_include_keyword(db_file, "deny",
|
||||
string)
|
||||
if reject_list:
|
||||
read_status = 1
|
||||
logging.debug('Rejected : {}'
|
||||
'\n'
|
||||
'Keyword : {}'
|
||||
.format(link, reject_list))
|
||||
if isinstance(date, int):
|
||||
logging.error('Variable "date" is int: {}'.format(date))
|
||||
media_link = ''
|
||||
if "attachments" in entry.keys():
|
||||
for e_link in entry["attachments"]:
|
||||
try:
|
||||
# if (link.rel == "enclosure" and
|
||||
# (link.type.startswith("audio/") or
|
||||
# link.type.startswith("image/") or
|
||||
# link.type.startswith("video/"))
|
||||
# ):
|
||||
media_type = e_link["mime_type"][:e_link["mime_type"].index("/")]
|
||||
if media_type in ("audio", "image", "video"):
|
||||
media_link = e_link["url"]
|
||||
media_link = join_url(url, e_link["url"])
|
||||
media_link = trim_url(media_link)
|
||||
break
|
||||
except:
|
||||
logging.info('KeyError: "url"\n'
|
||||
'Missing "url" attribute for {}'
|
||||
.format(url))
|
||||
logging.info('Continue scanning for next '
|
||||
'potential enclosure of {}'
|
||||
.format(link))
|
||||
entry = {
|
||||
"title": title,
|
||||
"link": link,
|
||||
"enclosure": media_link,
|
||||
"entry_id": entry_id,
|
||||
"date": date,
|
||||
"read_status": read_status
|
||||
}
|
||||
new_entries.extend([entry])
|
||||
# await sqlite.add_entry(
|
||||
# db_file, title, link, entry_id,
|
||||
# url, date, read_status)
|
||||
# await sqlite.set_date(db_file, url)
|
||||
if len(new_entries):
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
await sqlite.update_feed_properties(
|
||||
db_file, feed_id, len(feed["items"]), updated)
|
||||
# await update_feed_status
|
||||
except (
|
||||
IncompleteReadError,
|
||||
IncompleteRead,
|
||||
error.URLError
|
||||
) as e:
|
||||
logging.error(e)
|
||||
return
|
||||
# new_entry = 0
|
||||
for entry in entries:
|
||||
if "date_published" in entry.keys():
|
||||
date = entry["date_published"]
|
||||
date = dt.rfc2822_to_iso8601(date)
|
||||
elif "date_modified" in entry.keys():
|
||||
date = entry["date_modified"]
|
||||
date = dt.rfc2822_to_iso8601(date)
|
||||
else:
|
||||
date = dt.now()
|
||||
if "url" in entry.keys():
|
||||
# link = complete_url(source, entry.link)
|
||||
link = join_url(url, entry["url"])
|
||||
link = trim_url(link)
|
||||
else:
|
||||
link = url
|
||||
# title = feed["feed"]["title"]
|
||||
# title = "{}: *{}*".format(feed["feed"]["title"], entry.title)
|
||||
title = entry["title"] if "title" in entry.keys() else date
|
||||
entry_id = entry["id"] if "id" in entry.keys() else link
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
exist = await sqlite.check_entry_exist(
|
||||
db_file, feed_id, entry_id=entry_id,
|
||||
title=title, link=link, date=date)
|
||||
if not exist:
|
||||
summary = entry["summary"] if "summary" in entry.keys() else ''
|
||||
if not summary:
|
||||
summary = (entry["content_html"]
|
||||
if "content_html" in entry.keys()
|
||||
else '')
|
||||
if not summary:
|
||||
summary = (entry["content_text"]
|
||||
if "content_text" in entry.keys()
|
||||
else '')
|
||||
read_status = 0
|
||||
pathname = urlsplit(link).path
|
||||
string = (
|
||||
"{} {} {}"
|
||||
).format(
|
||||
title, summary, pathname)
|
||||
allow_list = await config.is_include_keyword(
|
||||
db_file, "allow", string)
|
||||
if not allow_list:
|
||||
reject_list = await config.is_include_keyword(
|
||||
db_file, "deny", string)
|
||||
if reject_list:
|
||||
read_status = 1
|
||||
logging.debug(
|
||||
"Rejected : {}\n"
|
||||
"Keyword : {}".format(
|
||||
link, reject_list))
|
||||
if isinstance(date, int):
|
||||
logging.error(
|
||||
"Variable 'date' is int: {}".format(date))
|
||||
media_link = ''
|
||||
if "attachments" in entry.keys():
|
||||
for e_link in entry["attachments"]:
|
||||
try:
|
||||
# if (link.rel == "enclosure" and
|
||||
# (link.type.startswith("audio/") or
|
||||
# link.type.startswith("image/") or
|
||||
# link.type.startswith("video/"))
|
||||
# ):
|
||||
media_type = e_link["mime_type"][:e_link["mime_type"].index("/")]
|
||||
if media_type in ("audio", "image", "video"):
|
||||
media_link = e_link["url"]
|
||||
media_link = join_url(url, e_link["url"])
|
||||
media_link = trim_url(media_link)
|
||||
break
|
||||
except:
|
||||
logging.info('KeyError: "url"\n'
|
||||
'Missing "url" attribute for {}'
|
||||
.format(url))
|
||||
logging.info('Continue scanning for next '
|
||||
'potential enclosure of {}'
|
||||
.format(link))
|
||||
entry = {
|
||||
"title": title,
|
||||
"link": link,
|
||||
"enclosure": media_link,
|
||||
"entry_id": entry_id,
|
||||
"date": date,
|
||||
"read_status": read_status
|
||||
}
|
||||
new_entries.extend([entry])
|
||||
# await sqlite.add_entry(
|
||||
# db_file, title, link, entry_id,
|
||||
# url, date, read_status)
|
||||
# await sqlite.set_date(db_file, url)
|
||||
if len(new_entries):
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
await sqlite.add_entries_and_update_timestamp(
|
||||
db_file, feed_id, new_entries)
|
||||
await sqlite.add_entries_and_update_timestamp(db_file, feed_id,
|
||||
new_entries)
|
||||
|
||||
|
||||
async def view_feed(url):
|
||||
while True:
|
||||
result = await fetch.http(url)
|
||||
document = result[0]
|
||||
status = result[1]
|
||||
if document:
|
||||
if not result['error']:
|
||||
document = result['content']
|
||||
status = result['status_code']
|
||||
feed = parse(document)
|
||||
# if is_feed(url, feed):
|
||||
if is_feed(feed):
|
||||
|
@ -1023,13 +1034,12 @@ async def view_feed(url):
|
|||
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)
|
||||
response += ("Title : {}\n"
|
||||
"Date : {}\n"
|
||||
"Link : {}\n"
|
||||
"Count : {}\n"
|
||||
"\n"
|
||||
.format(title, date, link, counter))
|
||||
if counter > 4:
|
||||
break
|
||||
response += (
|
||||
|
@ -1037,8 +1047,7 @@ async def view_feed(url):
|
|||
).format(url)
|
||||
break
|
||||
else:
|
||||
result = await crawl.probe_page(
|
||||
url, document)
|
||||
result = await crawl.probe_page(url, document)
|
||||
if isinstance(result, str):
|
||||
response = result
|
||||
break
|
||||
|
@ -1054,9 +1063,9 @@ async def view_feed(url):
|
|||
async def view_entry(url, num):
|
||||
while True:
|
||||
result = await fetch.http(url)
|
||||
document = result[0]
|
||||
status = result[1]
|
||||
if document:
|
||||
if not result['error']:
|
||||
document = result['content']
|
||||
status = result['status_code']
|
||||
feed = parse(document)
|
||||
# if is_feed(url, feed):
|
||||
if is_feed(feed):
|
||||
|
@ -1094,19 +1103,17 @@ async def view_entry(url, num):
|
|||
link = trim_url(link)
|
||||
else:
|
||||
link = "*** No link ***"
|
||||
response = (
|
||||
"{}\n"
|
||||
"\n"
|
||||
# "> {}\n"
|
||||
"{}\n"
|
||||
"\n"
|
||||
"{}\n"
|
||||
"\n"
|
||||
).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)
|
||||
result = await crawl.probe_page(url, document)
|
||||
if isinstance(result, str):
|
||||
response = result
|
||||
break
|
||||
|
@ -1119,6 +1126,7 @@ async def view_entry(url, num):
|
|||
return response
|
||||
|
||||
|
||||
# TODO Rename function name (idea: scan_and_populate)
|
||||
async def scan(db_file, url):
|
||||
"""
|
||||
Check feeds for new entries.
|
||||
|
@ -1132,142 +1140,136 @@ async def scan(db_file, url):
|
|||
"""
|
||||
if isinstance(url, tuple): url = url[0]
|
||||
result = await fetch.http(url)
|
||||
try:
|
||||
document = result[0]
|
||||
status = result[1]
|
||||
except:
|
||||
return
|
||||
new_entries = []
|
||||
if document and status == 200:
|
||||
feed = parse(document)
|
||||
entries = feed.entries
|
||||
# length = len(entries)
|
||||
await remove_nonexistent_entries(
|
||||
db_file, url, feed)
|
||||
try:
|
||||
if feed.bozo:
|
||||
# bozo = (
|
||||
# "WARNING: Bozo detected for feed: {}\n"
|
||||
# "For more information, visit "
|
||||
# "https://pythonhosted.org/feedparser/bozo.html"
|
||||
# ).format(url)
|
||||
# print(bozo)
|
||||
valid = 0
|
||||
else:
|
||||
valid = 1
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
await sqlite.update_feed_validity(
|
||||
db_file, feed_id, valid)
|
||||
if "updated_parsed" in feed["feed"].keys():
|
||||
updated = feed["feed"]["updated_parsed"]
|
||||
try:
|
||||
updated = dt.convert_struct_time_to_iso8601(updated)
|
||||
except:
|
||||
if not result['error']:
|
||||
document = result['content']
|
||||
status = result['status_code']
|
||||
new_entries = []
|
||||
if document and status == 200:
|
||||
feed = parse(document)
|
||||
entries = feed.entries
|
||||
# length = len(entries)
|
||||
await remove_nonexistent_entries(db_file, url, feed)
|
||||
try:
|
||||
if feed.bozo:
|
||||
# bozo = (
|
||||
# "WARNING: Bozo detected for feed: {}\n"
|
||||
# "For more information, visit "
|
||||
# "https://pythonhosted.org/feedparser/bozo.html"
|
||||
# ).format(url)
|
||||
# print(bozo)
|
||||
valid = 0
|
||||
else:
|
||||
valid = 1
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
await sqlite.update_feed_validity(
|
||||
db_file, feed_id, valid)
|
||||
if "updated_parsed" in feed["feed"].keys():
|
||||
updated = feed["feed"]["updated_parsed"]
|
||||
try:
|
||||
updated = dt.convert_struct_time_to_iso8601(updated)
|
||||
except:
|
||||
updated = ''
|
||||
else:
|
||||
updated = ''
|
||||
else:
|
||||
updated = ''
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
await sqlite.update_feed_properties(db_file, feed_id,
|
||||
len(feed["entries"]), updated)
|
||||
# await update_feed_status
|
||||
except (IncompleteReadError, IncompleteRead, error.URLError) as e:
|
||||
logging.error(e)
|
||||
return
|
||||
# new_entry = 0
|
||||
for entry in entries:
|
||||
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 = dt.now()
|
||||
if entry.has_key("link"):
|
||||
# link = complete_url(source, entry.link)
|
||||
link = join_url(url, entry.link)
|
||||
link = trim_url(link)
|
||||
else:
|
||||
link = url
|
||||
# title = feed["feed"]["title"]
|
||||
# title = "{}: *{}*".format(feed["feed"]["title"], entry.title)
|
||||
title = entry.title if entry.has_key("title") else date
|
||||
entry_id = entry.id if entry.has_key("id") else link
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
exist = await sqlite.check_entry_exist(db_file, feed_id,
|
||||
entry_id=entry_id,
|
||||
title=title, link=link,
|
||||
date=date)
|
||||
if not exist:
|
||||
summary = entry.summary if entry.has_key("summary") else ''
|
||||
read_status = 0
|
||||
pathname = urlsplit(link).path
|
||||
string = (
|
||||
"{} {} {}"
|
||||
).format(
|
||||
title, summary, pathname)
|
||||
allow_list = config.is_include_keyword(db_file, "allow",
|
||||
string)
|
||||
if not allow_list:
|
||||
reject_list = config.is_include_keyword(db_file, "deny",
|
||||
string)
|
||||
if reject_list:
|
||||
read_status = 1
|
||||
logging.debug('Rejected : {}'
|
||||
'\n'
|
||||
'Keyword : {}'.format(link,
|
||||
reject_list))
|
||||
if isinstance(date, int):
|
||||
logging.error('Variable "date" is int: {}'
|
||||
.format(date))
|
||||
media_link = ''
|
||||
if entry.has_key("links"):
|
||||
for e_link in entry.links:
|
||||
try:
|
||||
# if (link.rel == "enclosure" and
|
||||
# (link.type.startswith("audio/") or
|
||||
# link.type.startswith("image/") or
|
||||
# link.type.startswith("video/"))
|
||||
# ):
|
||||
media_type = e_link.type[:e_link.type.index("/")]
|
||||
if e_link.has_key("rel"):
|
||||
if (e_link.rel == "enclosure" and
|
||||
media_type in ("audio", "image", "video")):
|
||||
media_link = e_link.href
|
||||
media_link = join_url(url, e_link.href)
|
||||
media_link = trim_url(media_link)
|
||||
break
|
||||
except:
|
||||
logging.info('KeyError: "href"\n'
|
||||
'Missing "href" attribute for {}'
|
||||
.format(url))
|
||||
logging.info('Continue scanning for next '
|
||||
'potential enclosure of {}'
|
||||
.format(link))
|
||||
entry = {
|
||||
"title": title,
|
||||
"link": link,
|
||||
"enclosure": media_link,
|
||||
"entry_id": entry_id,
|
||||
"date": date,
|
||||
"read_status": read_status
|
||||
}
|
||||
new_entries.extend([entry])
|
||||
# await sqlite.add_entry(
|
||||
# db_file, title, link, entry_id,
|
||||
# url, date, read_status)
|
||||
# await sqlite.set_date(db_file, url)
|
||||
if len(new_entries):
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
await sqlite.update_feed_properties(
|
||||
db_file, feed_id, len(feed["entries"]), updated)
|
||||
# await update_feed_status
|
||||
except (
|
||||
IncompleteReadError,
|
||||
IncompleteRead,
|
||||
error.URLError
|
||||
) as e:
|
||||
logging.error(e)
|
||||
return
|
||||
# new_entry = 0
|
||||
for entry in entries:
|
||||
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 = dt.now()
|
||||
if entry.has_key("link"):
|
||||
# link = complete_url(source, entry.link)
|
||||
link = join_url(url, entry.link)
|
||||
link = trim_url(link)
|
||||
else:
|
||||
link = url
|
||||
# title = feed["feed"]["title"]
|
||||
# title = "{}: *{}*".format(feed["feed"]["title"], entry.title)
|
||||
title = entry.title if entry.has_key("title") else date
|
||||
entry_id = entry.id if entry.has_key("id") else link
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
exist = await sqlite.check_entry_exist(
|
||||
db_file, feed_id, entry_id=entry_id,
|
||||
title=title, link=link, date=date)
|
||||
if not exist:
|
||||
summary = entry.summary if entry.has_key("summary") else ''
|
||||
read_status = 0
|
||||
pathname = urlsplit(link).path
|
||||
string = (
|
||||
"{} {} {}"
|
||||
).format(
|
||||
title, summary, pathname)
|
||||
allow_list = await config.is_include_keyword(
|
||||
db_file, "allow", string)
|
||||
if not allow_list:
|
||||
reject_list = await config.is_include_keyword(
|
||||
db_file, "deny", string)
|
||||
if reject_list:
|
||||
read_status = 1
|
||||
logging.debug(
|
||||
"Rejected : {}\n"
|
||||
"Keyword : {}".format(
|
||||
link, reject_list))
|
||||
if isinstance(date, int):
|
||||
logging.error('Variable "date" is int: {}'
|
||||
.format(date))
|
||||
media_link = ''
|
||||
if entry.has_key("links"):
|
||||
for e_link in entry.links:
|
||||
try:
|
||||
# if (link.rel == "enclosure" and
|
||||
# (link.type.startswith("audio/") or
|
||||
# link.type.startswith("image/") or
|
||||
# link.type.startswith("video/"))
|
||||
# ):
|
||||
media_type = e_link.type[:e_link.type.index("/")]
|
||||
if e_link.has_key("rel"):
|
||||
if (e_link.rel == "enclosure" and
|
||||
media_type in ("audio", "image", "video")):
|
||||
media_link = e_link.href
|
||||
media_link = join_url(url, e_link.href)
|
||||
media_link = trim_url(media_link)
|
||||
break
|
||||
except:
|
||||
logging.info('KeyError: "href"\n'
|
||||
'Missing "href" attribute for {}'
|
||||
.format(url))
|
||||
logging.info('Continue scanning for next '
|
||||
'potential enclosure of {}'
|
||||
.format(link))
|
||||
entry = {
|
||||
"title": title,
|
||||
"link": link,
|
||||
"enclosure": media_link,
|
||||
"entry_id": entry_id,
|
||||
"date": date,
|
||||
"read_status": read_status
|
||||
}
|
||||
new_entries.extend([entry])
|
||||
# await sqlite.add_entry(
|
||||
# db_file, title, link, entry_id,
|
||||
# url, date, read_status)
|
||||
# await sqlite.set_date(db_file, url)
|
||||
if len(new_entries):
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
await sqlite.add_entries_and_update_timestamp(
|
||||
db_file, feed_id, new_entries)
|
||||
await sqlite.add_entries_and_update_timestamp(db_file, feed_id,
|
||||
new_entries)
|
||||
|
||||
|
||||
async def download_document(self, message, jid, jid_file, message_text, ix_url,
|
||||
|
@ -1286,8 +1288,7 @@ async def download_document(self, message, jid, jid_file, message_text, ix_url,
|
|||
status_type = 'dnd'
|
||||
status_message = ('📃️ Procesing request to produce {} document...'
|
||||
.format(ext.upper()))
|
||||
XmppPresence.send(self, jid, status_message,
|
||||
status_type=status_type)
|
||||
XmppPresence.send(self, jid, status_message, status_type=status_type)
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
cache_dir = config.get_default_cache_directory()
|
||||
if ix_url:
|
||||
|
@ -1313,9 +1314,9 @@ async def download_document(self, message, jid, jid_file, message_text, ix_url,
|
|||
logging.info('Processed URL (replace hostname): {}'
|
||||
.format(url))
|
||||
result = await fetch.http(url)
|
||||
data = result[0]
|
||||
code = result[1]
|
||||
if data:
|
||||
if not result['error']:
|
||||
data = result['content']
|
||||
code = result['status_code']
|
||||
title = get_document_title(data)
|
||||
title = title.strip().lower()
|
||||
for i in (' ', '-'):
|
||||
|
@ -1332,8 +1333,7 @@ async def download_document(self, message, jid, jid_file, message_text, ix_url,
|
|||
'Failed to export {}. Reason: {}'
|
||||
.format(url, ext.upper(), error))
|
||||
else:
|
||||
url = await XmppUpload.start(self, jid,
|
||||
filename)
|
||||
url = await XmppUpload.start(self, jid, filename)
|
||||
chat_type = await get_chat_type(self, jid)
|
||||
XmppMessage.send_oob(self, jid, url, chat_type)
|
||||
else:
|
||||
|
@ -1416,8 +1416,8 @@ async def extract_image_from_feed(db_file, feed_id, url):
|
|||
feed_url = sqlite.get_feed_url(db_file, feed_id)
|
||||
feed_url = feed_url[0]
|
||||
result = await fetch.http(feed_url)
|
||||
document = result[0]
|
||||
if document:
|
||||
if not result['error']:
|
||||
document = result['content']
|
||||
feed = parse(document)
|
||||
for entry in feed.entries:
|
||||
try:
|
||||
|
@ -1434,8 +1434,8 @@ async def extract_image_from_feed(db_file, feed_id, url):
|
|||
|
||||
async def extract_image_from_html(url):
|
||||
result = await fetch.http(url)
|
||||
data = result[0]
|
||||
if data:
|
||||
if not result['error']:
|
||||
data = result['content']
|
||||
try:
|
||||
document = Document(data)
|
||||
content = document.summary()
|
||||
|
@ -1723,4 +1723,4 @@ async def remove_nonexistent_entries_json(db_file, url, feed):
|
|||
else:
|
||||
await sqlite.archive_entry(db_file, ix)
|
||||
limit = config.get_setting_value(db_file, "archive")
|
||||
await sqlite.maintain_archive(db_file, limit)
|
||||
await sqlite.maintain_archive(db_file, limit)
|
||||
|
|
840
slixfeed/assets/feeds.toml
Normal file
840
slixfeed/assets/feeds.toml
Normal file
|
@ -0,0 +1,840 @@
|
|||
# This is a default set of featured feeds allocated by locality (i.e. language code).
|
||||
# Copy this file to ~/.config/slixfeed/feeds.toml if you want to modify the list.
|
||||
|
||||
# NOTE <presence xml:lang="fr"></presence>
|
||||
# TODO Consider splitting into files feeds-cz.toml, feeds-de.toml, feeds-el.toml.
|
||||
|
||||
[[feeds]]
|
||||
lang = "cs-cz"
|
||||
name = "Česká pirátská strana"
|
||||
link = "https://www.pirati.cz/feeds/atom/"
|
||||
tags = ["czech", "party", "pirate"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "cs-cz"
|
||||
name = "Paralelní Polis"
|
||||
link = "https://www.paralelnipolis.cz/feed/"
|
||||
tags = ["communication", "czech", "hackerspace", "hardware", "internet", "makers", "technology", "telecom"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-at"
|
||||
name = "Alle Neuigkeiten von jotwewe.de"
|
||||
link = "http://www.jotwewe.de/alles_de.xml"
|
||||
tags = ["jabber", "jabrss", "xmpp", "syndication"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-at"
|
||||
name = "Piratenpartei Österreichs"
|
||||
link = "https://piratenpartei.at/feed/"
|
||||
tags = ["austria", "party", "pirate"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-ch"
|
||||
name = "Chaos Computer Club Basel"
|
||||
link = "https://ccc-basel.ch/feeds/all.atom.xml"
|
||||
tags = ["ccc", "club", "computer", "switzerland"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-ch"
|
||||
name = "GNU/Linux.ch"
|
||||
link = "https://gnulinux.ch/rss.xml"
|
||||
tags = ["computer", "industry", "computer", "linux", "mobile", "pda", "switzerland"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-ch"
|
||||
name = "Piratenpartei Schweiz"
|
||||
link = "https://www.piratenpartei.ch/feed/"
|
||||
tags = ["party", "pirate", "switzerland"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "0d - Zeroday"
|
||||
link = "https://0x0d.de/feed/mp3/"
|
||||
tags = ["computer", "germany", "podcast"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "Berlin XMPP Meetup"
|
||||
link = "https://mov.im/?feed/pubsub.movim.eu/berlin-xmpp-meetup"
|
||||
tags = ["event", "germany", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "CCC Event Blog"
|
||||
link = "https://events.ccc.de/feed"
|
||||
tags = ["ccc", "club", "event"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "Chaos Computer Club: Updates"
|
||||
link = "https://www.ccc.de/de/rss/updates.rdf"
|
||||
tags = ["ccc", "news", "politics", "privacy", "surveillance", "technology", "germany"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "classless Kulla"
|
||||
link = "https://www.classless.org/feed/atom/"
|
||||
tags = ["europe ,germany ,history ,literature ,war"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "Digitalcourage"
|
||||
link = "https://digitalcourage.de/rss.xml"
|
||||
tags = ["culture", "digital", "germany"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "FSFE Events"
|
||||
link = "https://fsfe.org/events/events.de.rss"
|
||||
tags = ["computer", "events", "germany", "internet", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "FSFE News"
|
||||
link = "https://fsfe.org/news/news.de.rss"
|
||||
tags = ["computer", "events", "germany", "internet", "privacy", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "Jabber.de"
|
||||
link = "https://www.jabber.de/?feed=rss2"
|
||||
tags = ["germany", "jabber", "telecom", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "KI-Verband"
|
||||
link = "https://ki-verband.de/feed/"
|
||||
tags = ["ai", "germany", "machine", "model", "learning", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "media.ccc.de"
|
||||
link = "https://media.ccc.de/news.atom"
|
||||
tags = ["ccc", "technology", "video"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "pimux.de"
|
||||
link = "https://pimux.de/blog.rss"
|
||||
tags = ["germany", "jabber", "mastodon", "telecom", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "Sovereign Tech Fund"
|
||||
link = "https://www.sovereigntechfund.de/de/feed.rss"
|
||||
tags = ["germany", "technology", "open source"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "de-de"
|
||||
name = "WinFuture News"
|
||||
link = "https://static.winfuture.de/feeds/WinFuture-News-rss2.0.xml"
|
||||
tags = ["computer", "germany", "technology", "germany"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "el-gr"
|
||||
name = "Ηρακλής Παπαθεοδώρου • heracl.es"
|
||||
link = "https://heracl.es/el/feed.xml"
|
||||
tags = ["computer", "electronics", "greece", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "el-gr"
|
||||
name = "Κόμμα Πειρατών Ελλάδας – Pirate party of Greece"
|
||||
link = "https://www.pirateparty.gr/feed/"
|
||||
tags = ["greece", "party", "pirate"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-au"
|
||||
name = "Pirate Party Australia"
|
||||
link = "https://pirateparty.org.au/feed/podcast/"
|
||||
tags = ["australia", "party", "pirate"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-ca"
|
||||
name = "CAFE - The Canadian Association for Free Expression"
|
||||
link = "http://cafe.nfshost.com/?feed=rss2"
|
||||
tags = ["canada", "freedom", "government", "immigration", "society", "speech"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-ca"
|
||||
name = "Christian Faith and Copyfreedom"
|
||||
link = "https://singpolyma.net/2015/07/christian-faith-and-copyfreedom/feed/"
|
||||
tags = ["christianity", "copy", "freedom", "religion", "software", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-ca"
|
||||
name = "blog.jmp.chat's blog"
|
||||
link = "https://blog.jmp.chat/atom.xml"
|
||||
tags = ["jmp", "service", "sms", "telecom", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-ca"
|
||||
name = "RantMedia Forum"
|
||||
link = "https://smf.rantradio.com/index.php?type=atom;action=.xml"
|
||||
tags = ["canada", "forum", "politics", "privacy", "surveillance"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-ca"
|
||||
name = "Singpolyma"
|
||||
link = "https://singpolyma.net/feed/"
|
||||
tags = ["code", "computer", "culture", "jmp", "people", "software", "technology", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-gb"
|
||||
name = "A sysadmin's (mis)adventures"
|
||||
link = "https://blog.woodpeckersnest.space/feed/"
|
||||
tags = ["computer", "internet", "people", "technology", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-gb"
|
||||
name = "media.ccc.de"
|
||||
link = "https://media.ccc.de/news.atom"
|
||||
tags = ["ccc", "technology", "video"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-gb"
|
||||
name = "Christof Meerwald"
|
||||
link = "https://cmeerw.org/blog.atom"
|
||||
tags = ["austria", "computer", "linux", "people", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-gb"
|
||||
name = "German AI Association"
|
||||
link = "https://ki-verband.de/en/feed/"
|
||||
tags = ["ai", "machine", "model", "learning", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-gb"
|
||||
name = "La Quadrature du Net"
|
||||
link = "https://www.laquadrature.net/en/feed/"
|
||||
tags = ["news", "politics", "privacy", "surveillance"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-gb"
|
||||
name = "Pimux XMPP News"
|
||||
link = "https://pimux.de/blog.rss"
|
||||
tags = ["germany", "jabber", "mastodon", "telecom", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-gb"
|
||||
name = "Pirate Party UK"
|
||||
link = "https://pirateparty.org.uk/feed.xml"
|
||||
tags = ["party", "pirate", "uk"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-gb"
|
||||
name = "RIAT Institute"
|
||||
link = "https://riat.at/feed/"
|
||||
tags = ["art", "economics", "education", "hardware", "research", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-gb"
|
||||
name = "Snikket Blog on Snikket Chat"
|
||||
link = "https://snikket.org/blog/index.xml"
|
||||
tags = ["chat", "jabber", "telecom", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-gb"
|
||||
name = "The Brexit Party"
|
||||
link = "https://www.thebrexitparty.org/feed/"
|
||||
tags = ["europe", "politics", "uk"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "153 News - Videos Being Watched"
|
||||
link = "https://153news.net/rss.php?mode=watching"
|
||||
tags = ["news", "politics", "usa", "video"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "AudioBook Bay (ABB)"
|
||||
link = "https://audiobookbay.is/feed/atom/"
|
||||
tags = ["audiobook", "torrent"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Bald & Beards"
|
||||
link = "https://www.baldandbeards.com/feed/"
|
||||
tags = ["lifestyle", "men"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "BlackListed News"
|
||||
link = "https://www.blacklistednews.com/rss.php"
|
||||
tags = ["news", "politics", "usa", "world"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Canoe Mail"
|
||||
link = "https://canoemail.net/rss.xml"
|
||||
tags = ["email", "jabber", "telecom", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "CODEPINK - Women for Peace"
|
||||
link = "https://www.codepink.org/news.rss"
|
||||
tags = ["activism", "peace", "war", "women"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Ctrl blog"
|
||||
link = "https://feed.ctrl.blog/latest.atom"
|
||||
tags = ["computer", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Disroot Blog"
|
||||
link = "https://disroot.org/en/blog.atom"
|
||||
tags = ["decentralization", "privacy"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "F-Droid"
|
||||
link = "https://f-droid.org/feed.xml"
|
||||
tags = ["android", "pda", "privacy"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Fairphone"
|
||||
link = "https://www.fairphone.com/en/feed/"
|
||||
tags = ["pda", "privacy"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Fakeologist.com"
|
||||
link = "https://fakeologist.com/feed/"
|
||||
tags = ["news", "politics", "usa", "world"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Fakeologist Forums"
|
||||
link = "https://fakeologist.com/forums2/app.php/feed/news"
|
||||
tags = ["forum", "politics", "usa", "world"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "FSFE Events"
|
||||
link = "https://fsfe.org/events/events.en.rss"
|
||||
tags = ["computer", "events", "internet", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "FSFE News"
|
||||
link = "https://fsfe.org/news/news.en.rss"
|
||||
tags = ["computer", "events", "internet", "privacy", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Hacker Public Radio"
|
||||
link = "https://hackerpublicradio.org/hpr_ogg_rss.php"
|
||||
tags = ["computer", "internet", "podcast", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Hardy Fruit Tree Nursery"
|
||||
link = "https://www.hardyfruittrees.ca/feed/"
|
||||
tags = ["farming", "food", "gardening", "survival"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Ice Age Farmer"
|
||||
link = "https://www.iceagefarmer.com/feed/"
|
||||
tags = ["farming", "food", "gardening", "survival"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "International Consortium of Investigative Journalists"
|
||||
link = "https://www.icij.org/feed/"
|
||||
tags = ["news", "politics", "usa", "world"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Jacob's Unnamed Blog"
|
||||
link = "https://jacobwsmith.xyz/feed.xml"
|
||||
tags = ["book", "community", "culture", "family", "finance", "lifestyle", "market", "usa"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Juicing for Health"
|
||||
link = "https://juicing-for-health.com/feed"
|
||||
tags = ["beverage", "drink", "recipe"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "karson777"
|
||||
link = "https://videos.danksquad.org/feeds/videos.xml?videoChannelId=4711"
|
||||
tags = ["computer", "game", "internet", "linux", "software", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Larken Rose"
|
||||
link = "http://larkenrose.com/?format=feed&type=atom"
|
||||
tags = ["news", "politics", "usa"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "LibriVox's New Releases"
|
||||
link = "https://librivox.org/rss/latest_releases"
|
||||
tags = ["audiobook,"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Massachusetts Pirate Party"
|
||||
link = "https://masspirates.org/blog/feed/"
|
||||
tags = ["massachusetts", "party", "pirate", "usa"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Mercola.com"
|
||||
link = "https://articles.mercola.com/sites/articles/rss.aspx"
|
||||
tags = ["health"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Monal IM"
|
||||
link = "https://monal-im.org/index.xml"
|
||||
tags = ["iphone", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Mom on a Mission"
|
||||
link = "https://www.mom-on-a-mission.blog/all-posts?format=rss"
|
||||
tags = ["family", "farming", "food", "gardening", "survival"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "NLnet News"
|
||||
link = "https://nlnet.nl/feed.atom"
|
||||
tags = ["decentralization", "privacy"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "nobulart"
|
||||
link = "https://nobulart.com/feed/"
|
||||
tags = ["news", "survival", "politics", "usa", "world"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Opt Out Podcast"
|
||||
link = "https://optoutpod.com/index.xml"
|
||||
tags = ["podcast", "politics", "privacy", "surveillance"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Phish.in Music Updates"
|
||||
link = "https://phish.in/feeds/rss"
|
||||
tags = ["music"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "PINE64"
|
||||
link = "https://www.pine64.org/feed/"
|
||||
tags = ["pda", "privacy"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "PineTalk Podcast"
|
||||
link = "https://www.pine64.org/feed/opus/"
|
||||
tags = ["pda", "podcast", "privacy"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Pirate Parties International"
|
||||
link = "https://pp-international.net/feed/"
|
||||
tags = ["party", "pirate", "international"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Planet Jabber"
|
||||
link = "https://planet.jabber.org/atom.xml"
|
||||
tags = ["xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Ploum.net"
|
||||
link = "https://ploum.net/atom_en.xml"
|
||||
tags = ["computer", "decentralization", "internet", "linux", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "PrivacySavvy"
|
||||
link = "https://privacysavvy.com/"
|
||||
tags = ["privacy"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "postmarketOS Podcast"
|
||||
link = "https://cast.postmarketos.org/feed.rss"
|
||||
tags = ["pda", "podcast", "privacy"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Project Gemini news"
|
||||
link = "https://gemini.circumlunar.space/news/atom.xml"
|
||||
tags = ["gemini", "internet"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "PUNCH"
|
||||
link = "https://punchdrink.com/feed/"
|
||||
tags = ["beverage", "drink", "recipe"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Radio 3Fourteen"
|
||||
link = "https://redice.tv/rss/radio-3fourteen"
|
||||
tags = ["culture", "podcast", "politics", "radio", "usa"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Real Liberty Media"
|
||||
link = "https://www.reallibertymedia.com/category/podcasts/feed/?redirect=no"
|
||||
tags = ["culture", "news", "podcast", "politics", "privacy", "surveillance", "usa"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Reclaim The Net"
|
||||
link = "https://reclaimthenet.org/feed"
|
||||
tags = ["news", "politics", "privacy", "surveillance"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Red Ice News"
|
||||
link = "https://redice.tv/rss/news"
|
||||
tags = ["culture", "news", "politics", "usa"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Red Ice Radio"
|
||||
link = "https://redice.tv/rss/red-ice-radio"
|
||||
tags = ["culture", "podcast", "politics", "radio", "usa"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Red Ice TV"
|
||||
link = "https://redice.tv/rss/red-ice-tv"
|
||||
tags = ["culture", "podcast", "politics", "usa", "vodcast"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Redecentralize Blog"
|
||||
link = "https://redecentralize.org/blog/feed.rss"
|
||||
tags = ["podcast", "privacy", "surveillance", "vodcast"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Road Runners Club of America"
|
||||
link = "https://www.rrca.org/feed/"
|
||||
tags = ["jog", "road", "run", "sports", "usa"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Seymour Hersh"
|
||||
link = "https://seymourhersh.substack.com/feed"
|
||||
tags = ["news", "politics", "usa", "war"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Signs of the Times"
|
||||
link = "https://www.sott.net/xml_engine/signs_rss"
|
||||
tags = ["europe", "industry", "news", "politics", "usa", "war", "world"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Simplified Privacy"
|
||||
link = "https://simplifiedprivacy.com/feed/"
|
||||
tags = ["news", "privacy", "surveillance", "vodcast"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Software Freedom Conservancy News"
|
||||
link = "https://sfconservancy.org/feeds/news/"
|
||||
tags = ["culture", "free software", "freedom", "liberty", "open source", "software"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Software Freedom Podcast"
|
||||
link = "http://fsfe.org/news/podcast.en.rss"
|
||||
tags = ["computer", "podcast", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Stop Spraying Us!"
|
||||
link = "http://www.stopsprayingus.com/feed/"
|
||||
tags = ["activism", "geoengineering"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Sweet Home 3D Blog"
|
||||
link = "http://www.sweethome3d.com/blog/rss.xml"
|
||||
tags = ["3d", "architecture", "design", "game"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Take Back Our Tech"
|
||||
link = "https://takebackourtech.org/rss/"
|
||||
tags = ["internet", "privacy", "surveillance"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "The Bald Brothers"
|
||||
link = "https://thebaldbrothers.com/feed/"
|
||||
tags = ["lifestyle", "men"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "The 250kb Club"
|
||||
link = "https://250kb.club/rss.xml"
|
||||
tags = ["webring"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "The Conscious Resistance Network"
|
||||
link = "https://theconsciousresistance.com/feed/"
|
||||
tags = ["culture", "government", "podcast", "politics", "privacy", "surveillance", "usa"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "The Corbett Report"
|
||||
link = "https://www.corbettreport.com/feed/"
|
||||
tags = ["podcast", "politics", "privacy", "surveillance", "usa", "vodcast"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "The Organic Prepper"
|
||||
link = "https://www.theorganicprepper.com/feed/"
|
||||
tags = ["farming", "food", "gardening", "survival"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "The XMPP Blog on XMPP"
|
||||
link = "https://xmpp.org/feeds/all.atom.xml"
|
||||
tags = ["jabber", "telecom", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Truthstream Media"
|
||||
link = "http://truthstreammedia.com/feed/"
|
||||
tags = ["culture", "privacy", "surveillance", "usa", "vodcast"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "United States Pirate Party"
|
||||
link = "https://uspirates.org/feed/"
|
||||
tags = ["party", "pirate", "usa"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "Xonotic"
|
||||
link = "https://xonotic.org/index.xml"
|
||||
tags = ["3d", "game"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "en-us"
|
||||
name = "yaxim"
|
||||
link = "https://yaxim.org/atom.xml"
|
||||
tags = ["android", "germany", "jabber", "telecom", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "es-es"
|
||||
name = "Disroot Blog"
|
||||
link = "https://disroot.org/es/blog.atom"
|
||||
tags = ["decentralization", "privacy"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "fr-fr"
|
||||
name = "Agate Blue"
|
||||
link = "https://agate.blue/feed.xml"
|
||||
tags = ["computer", "music"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "fr-fr"
|
||||
name = "Archlinux.fr [Forums]"
|
||||
link = "https://forums.archlinux.fr/app.php/feed"
|
||||
tags = ["forum", "linux"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "fr-fr"
|
||||
name = "Cours et Tutoriels sur le Langage SQL"
|
||||
link = "https://sql.sh/feed"
|
||||
tags = ["sql", "tutorial"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "fr-fr"
|
||||
name = "Developpez"
|
||||
link = "https://www.developpez.com/index/atom"
|
||||
tags = ["technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "fr-fr"
|
||||
name = "Disroot Blog"
|
||||
link = "https://disroot.org/fr/blog.atom"
|
||||
tags = ["decentralization", "privacy"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "fr-fr"
|
||||
name = "Framablog"
|
||||
link = "https://framablog.org/feed/"
|
||||
tags = ["fediverse", "framasoft", "open source", "peertube", "privacy", "software", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "fr-fr"
|
||||
name = "FSFE Events"
|
||||
link = "https://fsfe.org/events/events.fr.rss"
|
||||
tags = ["computer", "events", "internet", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "fr-fr"
|
||||
name = "FSFE News"
|
||||
link = "https://fsfe.org/news/news.fr.rss"
|
||||
tags = ["computer", "events", "internet", "privacy", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "fr-fr"
|
||||
name = "La Quadrature du Net"
|
||||
link = "https://www.laquadrature.net/feed/"
|
||||
tags = ["news", "politics", "privacy", "surveillance"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "fr-fr"
|
||||
name = "Ploum.net"
|
||||
link = "https://ploum.net/atom_fr.xml"
|
||||
tags = ["computer", "decentralization", "internet", "linux", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "fr-fr"
|
||||
name = "Wiki de sebsauvage.net"
|
||||
link = "https://sebsauvage.net/wiki/feed.php"
|
||||
tags = ["computer", "network"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "he-il"
|
||||
name = "ALIVE528"
|
||||
link = "https://alive528.com/feed/"
|
||||
tags = ["health", "news", "politics", "privacy", "surveillance"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "he-il"
|
||||
name = "אגוגו מגזין בריאות"
|
||||
link = "https://www.agogo.co.il/feed/"
|
||||
tags = ["food", "health"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "he-il"
|
||||
name = "האייל הקורא"
|
||||
link = "http://www.haayal.co.il/xml/rss"
|
||||
tags = ["news", "politics"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "he-il"
|
||||
name = "העין השביעית"
|
||||
link = "https://www.the7eye.org.il/feed"
|
||||
tags = ["industry", "media", "news", "propaganda"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "he-il"
|
||||
name = "מזבלה"
|
||||
link = "https://mizbala.com/feed"
|
||||
tags = ["industry", "media", "news", "propaganda"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "he-il"
|
||||
name = "פרויקט אמת אחרת"
|
||||
link = "https://www.emetaheret.org.il/feed/"
|
||||
tags = ["food", "health", "media", "news", "politics"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "he-il"
|
||||
name = "שיחה מקומית"
|
||||
link = "https://www.mekomit.co.il/feed/"
|
||||
tags = ["news", "politics"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "it-it"
|
||||
name = "Diggita / Prima Pagina"
|
||||
link = "https://diggita.com/rss.php"
|
||||
tags = ["computer", "culture", "food", "technology"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "it-it"
|
||||
name = "Feddit.it"
|
||||
link = "https://feddit.it/feeds/local.xml?sort=Active"
|
||||
tags = ["fediverse", "forum"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "it-it"
|
||||
name = "A sysadmin's (mis)adventures"
|
||||
link = "https://blog.woodpeckersnest.space/feed/"
|
||||
tags = ["computer", "internet", "people", "technology", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "it-it"
|
||||
name = "Avvocato a Roma"
|
||||
link = "https://www.studiosabatino.it/feed/"
|
||||
tags = ["law"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "it-it"
|
||||
name = "Disroot Blog"
|
||||
link = "https://disroot.org/it/blog.atom"
|
||||
tags = ["decentralization", "privacy"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "it-it"
|
||||
name = "LinuxTrent"
|
||||
link = "https://www.linuxtrent.it/feed/"
|
||||
tags = ["computer", "internet", "linux", "technology", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "it-it"
|
||||
name = "Mario Sabatino Gemini capsule"
|
||||
link = "https://gemini.sabatino.cloud/rss.xml"
|
||||
tags = ["computer", "people", "xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "it-it"
|
||||
name = "XMPP-IT Italian Community"
|
||||
link = "https://www.xmpp-it.net/feed/"
|
||||
tags = ["xmpp"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "ja-jp"
|
||||
name = "ニュース速報(総合)"
|
||||
link = "https://mainichi.jp/rss/etc/mainichi-flash.rss"
|
||||
tags = ["japan", "news", "politics", "world"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "nl-nl"
|
||||
name = "Piratenpartij"
|
||||
link = "https://piratenpartij.nl/feed/"
|
||||
tags = ["netherlands", "party", "pirate"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "pl-pl"
|
||||
name = "Fundacja Internet. Czas działać!"
|
||||
link = "https://www.internet-czas-dzialac.pl/rss/"
|
||||
tags = ["computer", "technology", "design"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "ru-ru"
|
||||
name = "Disroot Blog"
|
||||
link = "https://disroot.org/ru/blog.atom"
|
||||
tags = ["decentralization", "privacy"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "ru-ru"
|
||||
name = "За вашу и нашу Свободу!"
|
||||
link = "https://lev-sharansky2.livejournal.com/data/atom"
|
||||
tags = ["culture"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "sv-se"
|
||||
name = "Piratpartiet"
|
||||
link = "https://piratpartiet.se/feed/"
|
||||
tags = ["party", "pirate", "sweden"]
|
||||
|
||||
[[feeds]]
|
||||
lang = "vi-vn"
|
||||
name = "Trần H. Trung"
|
||||
link = "https://trung.fun/atom.xml"
|
||||
tags = ["artist", "computer", "filmmaker", "people", "technology", "vietnam", "xmpp"]
|
||||
|
|
@ -35,8 +35,11 @@ import tomli_w
|
|||
import tomllib
|
||||
|
||||
def get_setting_value(db_file, key):
|
||||
value = (sqlite.get_setting_value(db_file, key)[0] or
|
||||
get_value("settings", "Settings", key))
|
||||
value = sqlite.get_setting_value(db_file, key)
|
||||
if value:
|
||||
value = value[0]
|
||||
else:
|
||||
value = get_value("settings", "Settings", key)
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError as e:
|
||||
|
@ -448,7 +451,7 @@ async def remove_from_list(newwords, keywords):
|
|||
return val
|
||||
|
||||
|
||||
async def is_include_keyword(db_file, key, string):
|
||||
def is_include_keyword(db_file, key, string):
|
||||
"""
|
||||
Check keyword match.
|
||||
|
||||
|
@ -468,7 +471,8 @@ async def is_include_keyword(db_file, key, string):
|
|||
"""
|
||||
# async def reject(db_file, string):
|
||||
# async def is_blacklisted(db_file, string):
|
||||
keywords = sqlite.get_filter_value(db_file, key) or ''
|
||||
keywords = sqlite.get_filter_value(db_file, key)
|
||||
keywords = keywords[0] if keywords else ''
|
||||
keywords = keywords.split(",")
|
||||
keywords = keywords + (open_config_file("lists.toml")[key])
|
||||
for keyword in keywords:
|
||||
|
|
|
@ -108,7 +108,7 @@ from urllib.parse import urlsplit, urlunsplit
|
|||
# return await callback(url)
|
||||
|
||||
|
||||
async def probe_page(url, document):
|
||||
async def probe_page(url, document=None):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
|
@ -122,32 +122,34 @@ async def probe_page(url, document):
|
|||
result : list or str
|
||||
Single URL as list or selection of URLs as str.
|
||||
"""
|
||||
result = None
|
||||
if not document:
|
||||
response = await fetch.http(url)
|
||||
if not response['error']:
|
||||
document = response['content']
|
||||
try:
|
||||
# tree = etree.fromstring(res[0]) # etree is for xml
|
||||
tree = html.fromstring(document)
|
||||
result = None
|
||||
except:
|
||||
result = (
|
||||
"> {}\nFailed to parse URL as feed."
|
||||
).format(url)
|
||||
logging.debug("Failed to parse URL as feed for {}.".format(url))
|
||||
result = {'link' : None,
|
||||
'index' : None,
|
||||
'name' : None,
|
||||
'code' : None,
|
||||
'error' : True,
|
||||
'exist' : None}
|
||||
if not result:
|
||||
logging.debug(
|
||||
"Feed auto-discovery engaged for {}".format(url))
|
||||
logging.debug("Feed auto-discovery engaged for {}".format(url))
|
||||
result = await feed_mode_auto_discovery(url, tree)
|
||||
if not result:
|
||||
logging.debug(
|
||||
"Feed link scan mode engaged for {}".format(url))
|
||||
logging.debug("Feed link scan mode engaged for {}".format(url))
|
||||
result = await feed_mode_scan(url, tree)
|
||||
if not result:
|
||||
logging.debug(
|
||||
"Feed arbitrary mode engaged for {}".format(url))
|
||||
logging.debug("Feed arbitrary mode engaged for {}".format(url))
|
||||
result = await feed_mode_guess(url, tree)
|
||||
if not result:
|
||||
logging.debug(
|
||||
"No feeds were found for {}".format(url))
|
||||
result = (
|
||||
"> {}\nNo news feeds were found for URL."
|
||||
).format(url)
|
||||
logging.debug("No feeds were found for {}".format(url))
|
||||
result = None
|
||||
return result
|
||||
|
||||
|
||||
|
@ -182,8 +184,9 @@ async def feed_mode_guess(url, tree):
|
|||
) if '.rss' not in paths else -1
|
||||
# if paths.index('.rss'):
|
||||
# paths.extend([".atom", ".feed", ".rdf", ".rss"])
|
||||
parted_url_path = parted_url.path if parted_url.path else '/'
|
||||
for path in paths:
|
||||
address = join_url(url, parted_url.path.split('/')[1] + path)
|
||||
address = join_url(url, parted_url_path.split('/')[1] + path)
|
||||
if address not in urls:
|
||||
urls.extend([address])
|
||||
# breakpoint()
|
||||
|
@ -299,13 +302,15 @@ async def feed_mode_auto_discovery(url, tree):
|
|||
async def process_feed_selection(url, urls):
|
||||
feeds = {}
|
||||
for i in urls:
|
||||
res = await fetch.http(i)
|
||||
status_code = res[1]
|
||||
if status_code == 200:
|
||||
try:
|
||||
feeds[i] = [parse(res[0])]
|
||||
except:
|
||||
continue
|
||||
result = await fetch.http(i)
|
||||
if not result['error']:
|
||||
document = result['content']
|
||||
status_code = result['status_code']
|
||||
if status_code == 200: # NOTE This line might be redundant
|
||||
try:
|
||||
feeds[i] = [parse(document)]
|
||||
except:
|
||||
continue
|
||||
message = (
|
||||
"Web feeds found for {}\n\n```\n"
|
||||
).format(url)
|
||||
|
@ -337,7 +342,7 @@ async def process_feed_selection(url, urls):
|
|||
# URL has been fetched, so that the receiving
|
||||
# function will scan that single URL instead of
|
||||
# listing it as a message.
|
||||
url = {'url' : feed_url,
|
||||
url = {'link' : feed_url,
|
||||
'index' : None,
|
||||
'name' : feed_name,
|
||||
'code' : status_code,
|
||||
|
|
|
@ -106,6 +106,7 @@ def http_response(url):
|
|||
response = None
|
||||
return response
|
||||
|
||||
|
||||
async def http(url):
|
||||
"""
|
||||
Download content of given URL.
|
||||
|
@ -120,13 +121,10 @@ async def http(url):
|
|||
msg: list or str
|
||||
Document or error message.
|
||||
"""
|
||||
user_agent = (
|
||||
config.get_value(
|
||||
"settings", "Network", "user-agent")
|
||||
) or 'Slixfeed/0.1'
|
||||
user_agent = (config.get_value("settings", "Network", "user-agent")
|
||||
or 'Slixfeed/0.1')
|
||||
headers = {'User-Agent': user_agent}
|
||||
proxy = (config.get_value(
|
||||
"settings", "Network", "http_proxy")) or ''
|
||||
proxy = (config.get_value("settings", "Network", "http_proxy") or '')
|
||||
timeout = ClientTimeout(total=10)
|
||||
async with ClientSession(headers=headers) as session:
|
||||
# async with ClientSession(trust_env=True) as session:
|
||||
|
@ -136,36 +134,39 @@ async def http(url):
|
|||
timeout=timeout
|
||||
) as response:
|
||||
status = response.status
|
||||
if response.status == 200:
|
||||
if status == 200:
|
||||
try:
|
||||
doc = await response.text()
|
||||
# print (response.content_type)
|
||||
msg = [doc, status]
|
||||
document = await response.text()
|
||||
result = {'charset': response.charset,
|
||||
'content': document,
|
||||
'content_length': response.content_length,
|
||||
'content_type': response.content_type,
|
||||
'error': False,
|
||||
'message': None,
|
||||
'original_url': url,
|
||||
'status_code': status,
|
||||
'response_url': response.url}
|
||||
except:
|
||||
# msg = [
|
||||
# False,
|
||||
# ("The content of this document "
|
||||
# "doesn't appear to be textual."
|
||||
# )
|
||||
# ]
|
||||
msg = [
|
||||
False, "Document is too large or is not textual."
|
||||
]
|
||||
result = {'error': True,
|
||||
'message': 'Could not get document.',
|
||||
'original_url': url,
|
||||
'status_code': status,
|
||||
'response_url': response.url}
|
||||
else:
|
||||
msg = [
|
||||
False, "HTTP Error: " + str(status)
|
||||
]
|
||||
result = {'error': True,
|
||||
'message': 'HTTP Error:' + str(status),
|
||||
'original_url': url,
|
||||
'status_code': status,
|
||||
'response_url': response.url}
|
||||
except ClientError as e:
|
||||
# print('Error', str(e))
|
||||
msg = [
|
||||
False, "Error: " + str(e)
|
||||
]
|
||||
result = {'error': True,
|
||||
'message': 'Error:' + str(e),
|
||||
'original_url': url}
|
||||
except TimeoutError as e:
|
||||
# print('Timeout:', str(e))
|
||||
msg = [
|
||||
False, "Timeout: " + str(e)
|
||||
]
|
||||
return msg
|
||||
result = {'error': True,
|
||||
'message': 'Timeout:' + str(e),
|
||||
'original_url': url}
|
||||
return result
|
||||
|
||||
|
||||
async def magnet(link):
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
__version__ = '0.1.10'
|
||||
__version_info__ = (0, 1, 10)
|
||||
__version__ = '0.1.11'
|
||||
__version_info__ = (0, 1, 11)
|
||||
|
|
|
@ -48,6 +48,8 @@ from slixmpp.plugins.xep_0048.stanza import Bookmarks
|
|||
|
||||
import slixfeed.action as action
|
||||
import slixfeed.config as config
|
||||
import slixfeed.crawl as crawl
|
||||
import slixfeed.fetch as fetch
|
||||
from slixfeed.dt import timestamp
|
||||
import slixfeed.sqlite as sqlite
|
||||
from slixfeed.version import __version__
|
||||
|
@ -464,12 +466,15 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
# )
|
||||
|
||||
# if jid == config.get_value('accounts', 'XMPP', 'operator'):
|
||||
self['xep_0050'].add_command(node='subscription',
|
||||
name='➕️ Add',
|
||||
handler=self._handle_subscription_add)
|
||||
self['xep_0050'].add_command(node='subscriptions',
|
||||
name='📰️ Subscriptions',
|
||||
handler=self._handle_subscriptions)
|
||||
self['xep_0050'].add_command(node='promoted',
|
||||
name='🔮️ Featured',
|
||||
handler=self._handle_promoted)
|
||||
self['xep_0050'].add_command(node='subscription',
|
||||
name='🔗️ Add', # 🪶️
|
||||
handler=self._handle_subscription_add)
|
||||
# self['xep_0050'].add_command(node='subscriptions_cat',
|
||||
# name='🔖️ Categories',
|
||||
# handler=self._handle_subscription)
|
||||
|
@ -479,29 +484,32 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
# self['xep_0050'].add_command(node='subscriptions_index',
|
||||
# name='📑️ Index (A - Z)',
|
||||
# handler=self._handle_subscription)
|
||||
# TODO Join Filters and Settings into Preferences
|
||||
self['xep_0050'].add_command(node='filters',
|
||||
name='🛡️ Filters',
|
||||
handler=self._handle_filters)
|
||||
# TODO Join Filters, Schedule and Settings into Preferences
|
||||
self['xep_0050'].add_command(node='settings',
|
||||
name='📮️ Settings',
|
||||
handler=self._handle_settings)
|
||||
if not self.is_component: # This will be changed with XEP-0222 XEP-0223
|
||||
self['xep_0050'].add_command(node='bookmarks',
|
||||
name='📕 Bookmarks',
|
||||
handler=self._handle_bookmarks)
|
||||
self['xep_0050'].add_command(node='roster',
|
||||
name='📓 Roster', # 📋
|
||||
handler=self._handle_roster)
|
||||
self['xep_0050'].add_command(node='filters',
|
||||
name='🛡️ Filters',
|
||||
handler=self._handle_filters)
|
||||
self['xep_0050'].add_command(node='schedule',
|
||||
name='📅 Schedule',
|
||||
handler=self._handle_schedule)
|
||||
self['xep_0050'].add_command(node='help',
|
||||
name='📔️ Manual',
|
||||
handler=self._handle_help)
|
||||
self['xep_0050'].add_command(node='totd',
|
||||
name='💡️ TOTD',
|
||||
name='💡️ Tips',
|
||||
handler=self._handle_totd)
|
||||
self['xep_0050'].add_command(node='fotd',
|
||||
name='🗓️ FOTD',
|
||||
handler=self._handle_fotd)
|
||||
if not self.is_component: # This will be changed with XEP-0222 XEP-0223
|
||||
self['xep_0050'].add_command(node='subscribers',
|
||||
name='🏡️ Subscribers', # 🎫
|
||||
handler=self._handle_subscribers)
|
||||
self['xep_0050'].add_command(node='bookmarks',
|
||||
name='📕 Bookmarks',
|
||||
handler=self._handle_bookmarks)
|
||||
self['xep_0050'].add_command(node='roster',
|
||||
name='📓 Roster', # 📋
|
||||
handler=self._handle_contacts)
|
||||
self['xep_0050'].add_command(node='activity',
|
||||
name='📠️ Activity',
|
||||
handler=self._handle_activity)
|
||||
|
@ -542,8 +550,8 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
jid = session['from'].bare
|
||||
jid_file = jid
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
form = self['xep_0004'].make_form('form', 'Filters')
|
||||
form['instructions'] = '🛡️ Manage filters' # 🪄️
|
||||
form = self['xep_0004'].make_form('form', 'Filter editor')
|
||||
form['instructions'] = '🛡️ Edit and manage filters' # 🪄️
|
||||
value = sqlite.get_filter_value(db_file, 'allow')
|
||||
if value: value = str(value[0])
|
||||
form.add_field(var='allow',
|
||||
|
@ -582,7 +590,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
form = payload
|
||||
|
||||
jid = session['from'].bare
|
||||
form = self['xep_0004'].make_form('result', 'Filters')
|
||||
form = self['xep_0004'].make_form('result', 'Done')
|
||||
form['instructions'] = ('✅️ Filters have been updated')
|
||||
jid_file = jid
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
|
@ -611,7 +619,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
|
||||
async def _handle_subscription_add(self, iq, session):
|
||||
jid = session['from'].bare
|
||||
form = self['xep_0004'].make_form('form', 'Add Subscriptions')
|
||||
form = self['xep_0004'].make_form('form', 'Add Subscription')
|
||||
form['instructions'] = '📰️ Add a new subscription'
|
||||
options = form.add_field(var='subscription',
|
||||
# TODO Make it possible to add several subscriptions at once;
|
||||
|
@ -643,7 +651,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
result = await action.add_feed(db_file, url)
|
||||
if isinstance(result, list):
|
||||
results = result
|
||||
form = self['xep_0004'].make_form('form', 'Subscriptions')
|
||||
form = self['xep_0004'].make_form('form', 'Select subscription')
|
||||
form['instructions'] = ('🔍️ Discovered {} subscriptions for {}'
|
||||
.format(len(results), url))
|
||||
options = form.add_field(var='subscriptions',
|
||||
|
@ -652,7 +660,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
desc=('Select a subscription to add.'),
|
||||
required=True)
|
||||
for result in results:
|
||||
options.addOption(result['name'], result['url'])
|
||||
options.addOption(result['name'], result['link'])
|
||||
session['payload'] = form
|
||||
session['next'] = self._handle_subscription_editor
|
||||
session['has_next'] = True
|
||||
|
@ -660,45 +668,41 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
# response = ('News source "{}" is already listed '
|
||||
# 'in the subscription list at index '
|
||||
# '{}.\n{}'.format(result['name'], result['index'],
|
||||
# result['url']))
|
||||
# result['link']))
|
||||
# session['notes'] = [['warn', response]] # Not supported by Gajim
|
||||
# session['notes'] = [['info', response]]
|
||||
form = self['xep_0004'].make_form('result', 'Subscriptions')
|
||||
form['instructions'] = ('⚠️ Feed "{}" already exist as index {}'
|
||||
.format(result['name'], result['index']))
|
||||
form = self['xep_0004'].make_form('form', 'Edit subscription')
|
||||
form['instructions'] = ('📰️ ' + result['name'])
|
||||
options = form.add_field(var='subscriptions',
|
||||
ftype='text-single',
|
||||
label=result['url'],
|
||||
desc='Choose next to edit subscription.',
|
||||
value=result['url'])
|
||||
# FIXME payload value does not pass, only [].
|
||||
ftype='list-single',
|
||||
label='Edit sbscription #{}'.format(result['index']),
|
||||
# desc='Click URL to edit subscription.',
|
||||
value=result['link'])
|
||||
options.addOption(result['name'], result['link'])
|
||||
session['payload'] = form
|
||||
session['next'] = self._handle_subscription_editor
|
||||
session['has_next'] = True
|
||||
# session['has_next'] = False
|
||||
elif result['error']:
|
||||
response = ('Failed to load URL.'
|
||||
response = ('Failed to load URL {}'
|
||||
'\n\n'
|
||||
'Reason: {}'
|
||||
'\n\n'
|
||||
'URL: {}'
|
||||
.format(result['code'], url))
|
||||
.format(url, result['code']))
|
||||
session['notes'] = [['error', response]]
|
||||
session['next'] = None
|
||||
else:
|
||||
# response = ('News source "{}" has been '
|
||||
# 'added to subscription list.\n{}'
|
||||
# .format(result['name'], result['url']))
|
||||
# .format(result['name'], result['link']))
|
||||
# session['notes'] = [['info', response]]
|
||||
form = self['xep_0004'].make_form('result', 'Subscriptions')
|
||||
form = self['xep_0004'].make_form('result', 'Done')
|
||||
form['instructions'] = ('✅️ News source "{}" has been added to '
|
||||
'subscription list as index {}'
|
||||
.format(result['name'], result['index']))
|
||||
options = form.add_field(var='subscriptions',
|
||||
ftype='text-single',
|
||||
label=result['url'],
|
||||
label=result['link'],
|
||||
desc='Choose next to edit subscription.',
|
||||
value=result['url'])
|
||||
# FIXME payload value does not pass, only [].
|
||||
value=result['link'])
|
||||
session['payload'] = form
|
||||
session['next'] = self._handle_subscription_editor
|
||||
session['has_next'] = True
|
||||
|
@ -758,8 +762,8 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
# FIXME There are feeds that are missing (possibly because of sortings)
|
||||
async def _handle_subscription(self, iq, session):
|
||||
jid = session['from'].bare
|
||||
form = self['xep_0004'].make_form('form', 'Subscriptions')
|
||||
form['instructions'] = '📰️ Edit subscription'
|
||||
form = self['xep_0004'].make_form('form', 'Subscription editor')
|
||||
form['instructions'] = '📰️ Edit subscription preferences and properties'
|
||||
# form.addField(var='interval',
|
||||
# ftype='text-single',
|
||||
# label='Interval period')
|
||||
|
@ -829,7 +833,11 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
options.addOption('Delete {} subscriptions'.format(url_count), 'delete')
|
||||
options.addOption('Export {} subscriptions'.format(url_count), 'export')
|
||||
else:
|
||||
url = urls[0]
|
||||
if isinstance(urls, list):
|
||||
url = urls[0]
|
||||
# elif isinstance(urls, str):
|
||||
else:
|
||||
url = urls
|
||||
feed_id = await sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
title = sqlite.get_feed_title(db_file, feed_id)
|
||||
|
@ -1019,7 +1027,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
count = await action.import_opml(db_file, url)
|
||||
try:
|
||||
int(count)
|
||||
form = self['xep_0004'].make_form('result', 'Import')
|
||||
form = self['xep_0004'].make_form('result', 'Done')
|
||||
form['instructions'] = ('✅️ Feeds have been imported')
|
||||
message = '{} feeds have been imported to {}.'.format(count, jid)
|
||||
form.add_field(var='message',
|
||||
|
@ -1058,7 +1066,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
async def _handle_export_complete(self, payload, session):
|
||||
jid = session['from'].bare
|
||||
jid_file = jid.replace('/', '_')
|
||||
form = self['xep_0004'].make_form('result', 'Export')
|
||||
form = self['xep_0004'].make_form('result', 'Done')
|
||||
form['instructions'] = ('✅️ Feeds have been exported')
|
||||
exts = payload['values']['filetype']
|
||||
for ext in exts:
|
||||
|
@ -1082,12 +1090,51 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
return session
|
||||
|
||||
|
||||
async def _handle_fotd(self, iq, session):
|
||||
text = ('Here we publish featured news feeds!')
|
||||
async def _handle_schedule(self, iq, session):
|
||||
text = ('Schedule')
|
||||
text += '\n\n'
|
||||
text += 'Set days and hours to receive news.'
|
||||
session['notes'] = [['info', text]]
|
||||
return session
|
||||
|
||||
|
||||
# TODO Exclude feeds that are already in database or requester.
|
||||
# TODO Attempt to look up for feeds of hostname of JID (i.e. scan
|
||||
# jabber.de for feeds for julient@jabber.de)
|
||||
async def _handle_promoted(self, iq, session):
|
||||
url = action.pick_a_feed()
|
||||
form = self['xep_0004'].make_form('form', 'Subscribe')
|
||||
# NOTE Refresh button would be of use
|
||||
form['instructions'] = '🔮️ Featured subscriptions' # 🎲️
|
||||
options = form.add_field(var='subscription',
|
||||
ftype="list-single",
|
||||
label=url['name'],
|
||||
value=url['link'])
|
||||
options.addOption(url['name'], url['link'])
|
||||
jid = session['from'].bare
|
||||
if '@' in jid:
|
||||
hostname = jid.split('@')[1]
|
||||
url = 'http://' + hostname
|
||||
result = await crawl.probe_page(url)
|
||||
if not result:
|
||||
url = {'url' : url,
|
||||
'index' : None,
|
||||
'name' : None,
|
||||
'code' : None,
|
||||
'error' : True,
|
||||
'exist' : False}
|
||||
elif isinstance(result, list):
|
||||
for url in result:
|
||||
if url['link']: options.addOption(url['name'], url['link'])
|
||||
else:
|
||||
url = result
|
||||
# Automatically set priority to 5 (highest)
|
||||
if url['link']: options.addOption(url['name'], url['link'])
|
||||
session['payload'] = form
|
||||
session['next'] = self._handle_subscription_new
|
||||
return session
|
||||
|
||||
|
||||
async def _handle_motd(self, iq, session):
|
||||
# TODO add functionality to attach image.
|
||||
text = ('Here you can add groupchat rules,post schedule, tasks or '
|
||||
|
@ -1103,9 +1150,11 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
|
||||
|
||||
async def _handle_credit(self, iq, session):
|
||||
wrjx = action.manual('information.toml', 'thanks')
|
||||
form = self['xep_0004'].make_form('result', 'Credits')
|
||||
form['instructions'] = "We are XMPP"
|
||||
form.add_field(ftype="text-multi", value=action.manual('information.toml', 'thanks'))
|
||||
form.add_field(ftype="text-multi",
|
||||
value=wrjx)
|
||||
|
||||
# Gajim displays all form['instructions'] on top
|
||||
# Psi ignore the latter form['instructions']
|
||||
|
@ -1153,6 +1202,188 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
return session
|
||||
|
||||
|
||||
async def _handle_subscribers(self, iq, session):
|
||||
jid = session['from'].bare
|
||||
if jid == config.get_value('accounts', 'XMPP', 'operator'):
|
||||
form = self['xep_0004'].make_form('form', 'Subscribers')
|
||||
form['instructions'] = '📖️ Organize subscribers'
|
||||
options = form.add_field(var='jid',
|
||||
ftype='list-single',
|
||||
label='Contacts',
|
||||
desc='Select a contact.',
|
||||
required=True)
|
||||
contacts = await XmppRoster.get(self)
|
||||
for contact in contacts:
|
||||
contact_name = contacts[contact]['name']
|
||||
contact_name = contact_name if contact_name else contact
|
||||
options.addOption(contact_name, contact)
|
||||
options = form.add_field(var='action',
|
||||
ftype='list-single',
|
||||
label='Action',
|
||||
value='message')
|
||||
options.addOption('Resend authorization To', 'to')
|
||||
options.addOption('Request authorization From', 'from')
|
||||
options.addOption('Send message', 'message')
|
||||
options.addOption('Remove', 'remove')
|
||||
form.add_field(var='message',
|
||||
ftype='text-multi',
|
||||
label='Message',
|
||||
desc='Add a descriptive message.')
|
||||
session['payload'] = form
|
||||
session['next'] = self._handle_subscribers_complete
|
||||
session['has_next'] = True
|
||||
else:
|
||||
logging.warning('An unauthorized attempt to access bookmarks has '
|
||||
'been detected!\n'
|
||||
'Details:\n'
|
||||
' Jabber ID: {}\n'
|
||||
' Timestamp: {}\n'
|
||||
.format(jid, timestamp()))
|
||||
session['notes'] = [['warn', 'This resource is restricted.']]
|
||||
return session
|
||||
|
||||
|
||||
async def _handle_subscribers_complete(self, iq, session):
|
||||
pass
|
||||
|
||||
|
||||
async def _handle_contacts(self, iq, session):
|
||||
jid = session['from'].bare
|
||||
if jid == config.get_value('accounts', 'XMPP', 'operator'):
|
||||
form = self['xep_0004'].make_form('form', 'Contacts')
|
||||
form['instructions'] = '📖️ Organize contacts'
|
||||
options = form.add_field(var='jid',
|
||||
ftype='list-single',
|
||||
label='Contact',
|
||||
desc='Select a contact.',
|
||||
required=True)
|
||||
contacts = await XmppRoster.get(self)
|
||||
for contact in contacts:
|
||||
contact_name = contacts[contact]['name']
|
||||
contact_name = contact_name if contact_name else contact
|
||||
options.addOption(contact_name, contact)
|
||||
options = form.add_field(var='action',
|
||||
ftype='list-single',
|
||||
label='Action',
|
||||
value='view')
|
||||
options.addOption('Display', 'view')
|
||||
options.addOption('Edit', 'edit')
|
||||
session['payload'] = form
|
||||
session['next'] = self._handle_contact_action
|
||||
session['has_next'] = True
|
||||
else:
|
||||
logging.warning('An unauthorized attempt to access bookmarks has '
|
||||
'been detected!\n'
|
||||
'Details:\n'
|
||||
' Jabber ID: {}\n'
|
||||
' Timestamp: {}\n'
|
||||
.format(jid, timestamp()))
|
||||
session['notes'] = [['warn', 'This resource is restricted.']]
|
||||
return session
|
||||
|
||||
|
||||
async def _handle_contact_action(self, payload, session):
|
||||
jid = payload['values']['jid']
|
||||
form = self['xep_0004'].make_form('form', 'Contact editor')
|
||||
session['allow_complete'] = True
|
||||
match payload['values']['action']:
|
||||
case 'edit':
|
||||
form['instructions'] = '📖️ Edit contact'
|
||||
roster = await XmppRoster.get(self)
|
||||
properties = roster[jid]
|
||||
form.add_field(var='name',
|
||||
ftype='text-single',
|
||||
label='Name',
|
||||
value=properties['name'])
|
||||
case 'view':
|
||||
session['has_next'] = False
|
||||
session['next'] = None
|
||||
session['allow_complete'] = None
|
||||
form = self['xep_0004'].make_form('form', 'Contact info')
|
||||
form['instructions'] = '📖️ Contact details'
|
||||
roster = await XmppRoster.get(self)
|
||||
properties = roster[jid]
|
||||
contact_name = properties['name']
|
||||
contact_name = contact_name if contact_name else jid
|
||||
form.add_field(var='name',
|
||||
ftype='text-single',
|
||||
label='Name',
|
||||
value=properties['name'])
|
||||
form.add_field(var='from',
|
||||
ftype='boolean',
|
||||
label='From',
|
||||
value=properties['from'])
|
||||
form.add_field(var='to',
|
||||
ftype='boolean',
|
||||
label='To',
|
||||
value=properties['to'])
|
||||
form.add_field(var='pending_in',
|
||||
ftype='boolean',
|
||||
label='Pending in',
|
||||
value=properties['pending_in'])
|
||||
form.add_field(var='pending_out',
|
||||
ftype='boolean',
|
||||
label='Pending out',
|
||||
value=properties['pending_out'])
|
||||
form.add_field(var='whitelisted',
|
||||
ftype='boolean',
|
||||
label='Whitelisted',
|
||||
value=properties['whitelisted'])
|
||||
form.add_field(var='subscription',
|
||||
ftype='fixed',
|
||||
label='Subscription',
|
||||
value=properties['subscription'])
|
||||
session['payload'] = form
|
||||
session['prev'] = self._handle_contacts
|
||||
session['allow_prev'] = True
|
||||
# session['next'] = None
|
||||
# session['has_next'] = False
|
||||
# session['allow_complete'] = True
|
||||
return session
|
||||
|
||||
|
||||
async def _handle_contacts_complete(self, payload, session):
|
||||
pass
|
||||
|
||||
|
||||
async def _handle_contacts_view(self, payload, session):
|
||||
jid = payload['values']['jid']
|
||||
roster = await XmppRoster.get(self)
|
||||
properties = roster[jid]
|
||||
form = self['xep_0004'].make_form('result', 'Contact')
|
||||
contact_name = properties['name']
|
||||
contact_name = contact_name if contact_name else jid
|
||||
form['instructions'] = '📝️ Edit contact {}'.format(contact_name)
|
||||
form.add_field(var='name',
|
||||
ftype='boolean',
|
||||
label='From',
|
||||
value=properties['from'])
|
||||
form.add_field(var='to',
|
||||
ftype='boolean',
|
||||
label='To',
|
||||
value=properties['to'])
|
||||
form.add_field(var='pending_in',
|
||||
ftype='boolean',
|
||||
label='Pending in',
|
||||
value=properties['pending_in'])
|
||||
form.add_field(var='pending_out',
|
||||
ftype='boolean',
|
||||
label='Pending out',
|
||||
value=properties['pending_out'])
|
||||
form.add_field(var='whitelisted',
|
||||
ftype='boolean',
|
||||
label='Whitelisted',
|
||||
value=properties['whitelisted'])
|
||||
form.add_field(var='subscription',
|
||||
ftype='text-single',
|
||||
label='Subscription',
|
||||
value=properties['subscription'])
|
||||
session['payload'] = form
|
||||
session['next'] = self._handle_bookmarks_complete
|
||||
session['has_next'] = True
|
||||
return session
|
||||
|
||||
|
||||
async def _handle_bookmarks(self, iq, session):
|
||||
jid = session['from'].bare
|
||||
if jid == config.get_value('accounts', 'XMPP', 'operator'):
|
||||
|
@ -1175,15 +1406,14 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
' Jabber ID: {}\n'
|
||||
' Timestamp: {}\n'
|
||||
.format(jid, timestamp()))
|
||||
session['notes'] = [['warn', 'You are not allowed to access this resource.']]
|
||||
session['notes'] = [['warn', 'This resource is restricted.']]
|
||||
return session
|
||||
|
||||
|
||||
async def _handle_bookmarks_editor(self, payload, session):
|
||||
jid = payload['values']['bookmarks']
|
||||
properties = await XmppBookmark.properties(self, jid)
|
||||
jid = session['from'].bare
|
||||
form = self['xep_0004'].make_form('form', 'Edit bookmark')
|
||||
form = self['xep_0004'].make_form('form', 'Bookmark editor')
|
||||
form['instructions'] = '📝️ Edit bookmark {}'.format(properties['name'])
|
||||
jid = properties['jid'].split('@')
|
||||
room = jid[0]
|
||||
|
@ -1248,7 +1478,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
here to persist across handler callbacks.
|
||||
"""
|
||||
|
||||
form = self['xep_0004'].make_form('result', 'Bookmarks')
|
||||
form = self['xep_0004'].make_form('result', 'Done')
|
||||
form['instructions'] = ('✅️ Bookmark has been saved')
|
||||
# In this case (as is typical), the payload is a form
|
||||
values = payload['values']
|
||||
|
@ -1280,7 +1510,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
jid = session['from'].bare
|
||||
jid_file = jid
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
form = self['xep_0004'].make_form('form', 'Settings')
|
||||
form = self['xep_0004'].make_form('form', 'Setting editor')
|
||||
form['instructions'] = ('📮️ Customize news updates')
|
||||
|
||||
value = config.get_setting_value(db_file, 'enabled')
|
||||
|
@ -1395,7 +1625,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
# form = payload
|
||||
|
||||
jid = session['from'].bare
|
||||
form = self['xep_0004'].make_form('form', 'Settings')
|
||||
form = self['xep_0004'].make_form('form', 'Done')
|
||||
form['instructions'] = ('✅️ Settings have been saved')
|
||||
|
||||
jid_file = jid
|
||||
|
|
|
@ -546,13 +546,13 @@ async def message(self, message):
|
|||
response += ("Title : {}\n"
|
||||
"Link : {}\n"
|
||||
"\n"
|
||||
.format(result['name'], result['url']))
|
||||
.format(result['name'], result['link']))
|
||||
response += ('```\nTotal of {} feeds.'
|
||||
.format(len(results)))
|
||||
elif result['exist']:
|
||||
response = ('> {}\nNews source "{}" is already '
|
||||
'listed in the subscription list at '
|
||||
'index {}'.format(result['url'],
|
||||
'index {}'.format(result['link'],
|
||||
result['name'],
|
||||
result['index']))
|
||||
elif result['error']:
|
||||
|
@ -561,7 +561,7 @@ async def message(self, message):
|
|||
else:
|
||||
response = ('> {}\nNews source "{}" has been '
|
||||
'added to subscription list.'
|
||||
.format(result['url'], result['name']))
|
||||
.format(result['link'], result['name']))
|
||||
# task.clean_tasks_xmpp(self, jid, ['status'])
|
||||
await task.start_tasks_xmpp(self, jid, ['status'])
|
||||
# except:
|
||||
|
|
|
@ -12,6 +12,13 @@ TODO
|
|||
|
||||
class XmppRoster:
|
||||
|
||||
|
||||
async def get(self):
|
||||
await self.get_roster()
|
||||
contacts = self.client_roster
|
||||
return contacts
|
||||
|
||||
|
||||
async def add(self, jid):
|
||||
"""
|
||||
Add JID to roster.
|
||||
|
|
Loading…
Reference in a new issue