From 31baf96430c9d9046d234e9c00b3161be8331a45 Mon Sep 17 00:00:00 2001 From: Schimon Jehudah Date: Sun, 26 Nov 2023 05:48:09 +0000 Subject: [PATCH] Add preview commands (read and select) and experimenting with XEP-0249 --- datasets/catalogues/gear.csv | 2 + datasets/events/xmpp.csv | 2 + datasets/forums/computer.csv | 2 + datasets/forums/graphics.csv | 3 +- datasets/forums/hacking.csv | 3 +- datasets/forums/intrenet.csv | 3 + datasets/news/art.csv | 1 + datasets/news/bsd.csv | 5 + datasets/news/business.csv | 2 + datasets/news/computer.csv | 11 +- datasets/news/cybersecurity.csv | 1 + datasets/news/design.csv | 3 + datasets/news/law.csv | 2 + datasets/news/linux.csv | 26 ++ datasets/news/literature.csv | 1 + datasets/news/north_carolina.csv | 2 + datasets/news/people.csv | 2 + datasets/news/russia.csv | 1 + datasets/news/technology.csv | 1 + datasets/news/telecom.csv | 1 + datasets/news/united_states.csv | 1 + .../news/{bsd_linux_unix.csv => unix.csv} | 1 + datasets/news/xmpp.csv | 2 + datasets/projects/internet.csv | 2 + datasets/projects/website.csv | 1 + slixfeed/__main__.py | 1 + slixfeed/confighandler.py | 2 + slixfeed/datahandler.py | 259 +++++++++--- slixfeed/sqlitehandler.py | 19 +- slixfeed/taskhandler.py | 31 +- slixfeed/xmpphandler.py | 367 +++++++++++++----- 31 files changed, 584 insertions(+), 176 deletions(-) create mode 100644 datasets/catalogues/gear.csv create mode 100644 datasets/events/xmpp.csv create mode 100644 datasets/forums/computer.csv create mode 100644 datasets/forums/intrenet.csv create mode 100644 datasets/news/bsd.csv create mode 100644 datasets/news/design.csv create mode 100644 datasets/news/law.csv create mode 100644 datasets/news/linux.csv create mode 100644 datasets/news/north_carolina.csv rename datasets/news/{bsd_linux_unix.csv => unix.csv} (97%) diff --git a/datasets/catalogues/gear.csv b/datasets/catalogues/gear.csv new file mode 100644 index 0000000..9a2cf09 --- /dev/null +++ b/datasets/catalogues/gear.csv @@ -0,0 +1,2 @@ +name, url, language, country +HELLOTUX Blog, https://www.hellotux.com/rss.php, en-US, Hungary diff --git a/datasets/events/xmpp.csv b/datasets/events/xmpp.csv new file mode 100644 index 0000000..760d8a8 --- /dev/null +++ b/datasets/events/xmpp.csv @@ -0,0 +1,2 @@ +name, url, language, country +Berlin XMPP Meetup, https://mov.im/feed/pubsub.movim.eu/berlin-xmpp-meetup, en-US, Germany diff --git a/datasets/forums/computer.csv b/datasets/forums/computer.csv new file mode 100644 index 0000000..6a238be --- /dev/null +++ b/datasets/forums/computer.csv @@ -0,0 +1,2 @@ +name, url, language, country +eGPU.io, https://egpu.io/feed/, en-US, Worldwide diff --git a/datasets/forums/graphics.csv b/datasets/forums/graphics.csv index 18926f1..e94a2e1 100644 --- a/datasets/forums/graphics.csv +++ b/datasets/forums/graphics.csv @@ -1,2 +1,3 @@ name, url, language, country -Blender Developer Forum - Latest posts, https://devtalk.blender.org/posts.rss, en-US, USA +Blender Developer Forum - Latest posts, https://devtalk.blender.org/posts.rss, en-US, United States +eGPU.io, https://egpu.io/feed/, en-US, Worldwide diff --git a/datasets/forums/hacking.csv b/datasets/forums/hacking.csv index 74c48ae..fdc52d8 100644 --- a/datasets/forums/hacking.csv +++ b/datasets/forums/hacking.csv @@ -1,2 +1,3 @@ name, url, language, country -Leak Zone - Leaking & Cracking Forum - All Forums, https://leakzone.net/syndication.php?type=atom1.0, en-US, Worldwide +Leak Zone - Leaking & Cracking Forum, https://leakzone.net/syndication.php?type=atom1.0, en-US, Worldwide +Pale Moon, https://forum.palemoon.org/app.php/feed/forum/1, en-US, Worldwide diff --git a/datasets/forums/intrenet.csv b/datasets/forums/intrenet.csv new file mode 100644 index 0000000..8990ab5 --- /dev/null +++ b/datasets/forums/intrenet.csv @@ -0,0 +1,3 @@ +name, url, language, country +Pale Moon, https://forum.palemoon.org/app.php/feed, en-US, Worldwide +Transmission BT, https://forum.transmissionbt.com/app.php/feed, en-US, Worldwide diff --git a/datasets/news/art.csv b/datasets/news/art.csv index 01a9bc7..053081e 100644 --- a/datasets/news/art.csv +++ b/datasets/news/art.csv @@ -3,3 +3,4 @@ The Public Domain Review, https://publicdomainreview.org/rss.xml, en-US, United třiapůl česky, https://triapul.cz/feed/cesky.xml, cz-CZ, Czech triapul.cz, http://triapul.cz/feed/english.xml, en-US, Czech techno-mage in:, https://bsd.network/@prahou.rss, en-US, United States +Behance Featured Projects, https://www.behance.net/feeds/projects, en-US, United States diff --git a/datasets/news/bsd.csv b/datasets/news/bsd.csv new file mode 100644 index 0000000..8a03840 --- /dev/null +++ b/datasets/news/bsd.csv @@ -0,0 +1,5 @@ +name, url, language, country +drkhsh, https://drkhsh.at/atom.xml, en-US, Austria +OpenBSD Webzine, https://webzine.puffy.cafe/atom.xml, en-US, United States +Solene'%, https://dataswamp.org/~solene/rss.xml, en-US, United States +Solene'%, https://dataswamp.org/~solene/rss-html.xml, en-US, United States diff --git a/datasets/news/business.csv b/datasets/news/business.csv index 3c55bd8..35d1976 100644 --- a/datasets/news/business.csv +++ b/datasets/news/business.csv @@ -12,3 +12,5 @@ ynet - כלכלה, https://www.ynet.co.il/Integration/StoryRss6.xml, he-IL, Isra דה מרקר - כותרות דף הבית, https://www.themarker.com/srv/tm-all-articles, he-IL, Israel דה מרקר - כותרות היום, https://www.themarker.com/srv/tm-news, he-IL, Israel דה מרקר - פרשנויות היום, https://www.themarker.com/srv/tm-opinions, he-IL, Israel +שוק ההון והשקעות, https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=585, he-IL, Israel +StockNews, https://stocknews.com/feed/, en-US, United States diff --git a/datasets/news/computer.csv b/datasets/news/computer.csv index 428ed9a..967df12 100644 --- a/datasets/news/computer.csv +++ b/datasets/news/computer.csv @@ -1,5 +1,6 @@ name, url, language, country -camen design, https://camendesign.com/rss, en-US, USA +Liliputing, https://liliputing.com/feed/, en-US, Worldwide +camen design, https://camendesign.com/rss, en-US, United States computers are bad, https://computer.rip/rss.xml, en-US, Worldwide Jacob McCormick, https://mccor.xyz/rss.xml, en-US, United States OpenNews.opennet.ru: Общая лента новостей, https://www.opennet.ru/opennews/opennews_all_utf.rss, ru-RU, Russia @@ -58,3 +59,11 @@ alexander cobleigh (articles), https://cblgh.org/articles.xml, en-US, United Sta Debricked, https://debricked.com/blog/feed/, en-US, United States out there in space, https://dataswamp.org/~lich/atom.xml, en-US, United States journal de prx, https://si3t.ch/log/atom.xml, fr-FR, Switzerland +Kitty Cat, https://kitty.social/@Kitty.rss, en-US, Worldwide +Stephen Foskett, Pack Rat, http://feeds.fosketts.net/StephenFoskettPackRat, en-US, United States +Apple, http://feeds.fosketts.net/StephenFoskettPackRat_Apple, en-US, United States +Computer History, http://feeds.fosketts.net/StephenFoskettPackRat_ComputerHistory, en-US, United States +Enterprise Storage, http://feeds.fosketts.net/StephenFoskettPackRat_EnterpriseStorage, en-US, United States +Personal, http://feeds.fosketts.net/StephenFoskettPackRat_Personal, en-US, United States +Terabyte Home, http://feeds.fosketts.net/StephenFoskettPackRat_TerabyteHome, en-US, United States +Virtual Storage, http://feeds.fosketts.net/StephenFoskettPackRat_VirtualStorage, en-US, United States diff --git a/datasets/news/cybersecurity.csv b/datasets/news/cybersecurity.csv index 47eef77..c1c2419 100644 --- a/datasets/news/cybersecurity.csv +++ b/datasets/news/cybersecurity.csv @@ -6,3 +6,4 @@ ollieparanoid, https://ollieparanoid.github.io/feed.xml, en-US, United States FSFE News, https://fsfe.org/news/news.en.rss, en-US, Germany FSFE News (DE), https://fsfe.org/news/news.de.rss, de-DE, Germany FSFE News (FR), https://fsfe.org/news/news.fr.rss, fr-FR, Germany +Schneier on Security, https://www.schneier.com/feed/atom/, en-US, United States diff --git a/datasets/news/design.csv b/datasets/news/design.csv new file mode 100644 index 0000000..080a471 --- /dev/null +++ b/datasets/news/design.csv @@ -0,0 +1,3 @@ +name, url, language, country +Crumina, https://crumina.net/feed/, en-US, Ukraine +Behance Featured Projects, https://www.behance.net/feeds/projects, en-US, United States diff --git a/datasets/news/law.csv b/datasets/news/law.csv new file mode 100644 index 0000000..0f8ec96 --- /dev/null +++ b/datasets/news/law.csv @@ -0,0 +1,2 @@ +name, url, language, country +North Carolina Lawyers Weekly, https://nclawyersweekly.com/feed/, en-US, United States diff --git a/datasets/news/linux.csv b/datasets/news/linux.csv new file mode 100644 index 0000000..410a50a --- /dev/null +++ b/datasets/news/linux.csv @@ -0,0 +1,26 @@ +name, url, language, country +Linux Professional Institute (LPI), https://www.lpi.org/feed/, en-US, Canada +LinuxConfig, https://linuxconfig.org/feed, en-US, Worldwide +The Logs, https://www.flu0r1ne.net/logs/rss.xml, en-US, United States +Ctrl blog: Fedora Linux, https://feed.ctrl.blog/topic/fedora-linux.atom, en-US, United States +Ctrl blog: Linux, https://feed.ctrl.blog/topic/linux.atom, en-US, United States +Anjan Momi, https://momi.ca/feed.xml, en-US, United States +postmarketOS, https://postmarketos.org/blog/feed.atom, en-US, United States +PureTryOut (Alpine Linux, postmarketOS), https://mastodon.fam-ribbers.com/@bart.rss, en-US, Netherlands +Blog on Bart Ribbers - PureTryOut (Alpine Linux, postmarketOS), https://fam-ribbers.com/blog/index.xml, en-US, Netherlands +LZone | Devops, https://lzone.de/feed/devops.xml, en-US, Germany +Necuno Solutions, https://necunos.com/feed.xml, en-US, Finland +Laslo Hunhold, https://laslo.hunhold.de/index.xml, en-US, Germany +Alpine Linux, https://fosstodon.org/@alpinelinux.rss, en-US, United States +ollieparanoid, https://ollieparanoid.github.io/feed.xml, en-US, United States +FSFE News, https://fsfe.org/news/news.en.rss, en-US, Germany +FSFE News (DE), https://fsfe.org/news/news.de.rss, de-DE, Germany +9to5Linux, https://9to5linux.com/feed, en-US, United States +FSFE News (FR), https://fsfe.org/news/news.fr.rss, fr-FR, Germany +Justine Smithies blog, https://justine.smithies.me.uk/atom.xml, en-US, United States +Justine Smithies, https://fosstodon.org/@JustineSmithies.rss, en-US, United States +TuxPhones, https://tuxphones.com/rss/, en-US, United States +Collabora Office and Collabora Online, https://www.collaboraoffice.com/feed/, en-US, United States +LGUG2Z, https://lgug2z.com/index.xml, en-US, United States +Hugo Barrera's site, https://whynothugo.nl/posts.xml, en-US, United States +ShadowKat Studios, https://shadowkat.net/rss.xml, en-US, Worldwide diff --git a/datasets/news/literature.csv b/datasets/news/literature.csv index ffe24c6..fb63ed9 100644 --- a/datasets/news/literature.csv +++ b/datasets/news/literature.csv @@ -2,3 +2,4 @@ name, url, language, country Stories by Williams, https://storiesbywilliams.com/feed/, en-US, United States Lili Saintcrow, https://www.lilithsaintcrow.com/feed/, en-US, United States Shannon Kay, https://blog.shannonkay.me/feed.xml, en-US, United States +V.K. Dixon, https://vkdixon.substack.com/feed, en-US, United States diff --git a/datasets/news/north_carolina.csv b/datasets/news/north_carolina.csv new file mode 100644 index 0000000..0f8ec96 --- /dev/null +++ b/datasets/news/north_carolina.csv @@ -0,0 +1,2 @@ +name, url, language, country +North Carolina Lawyers Weekly, https://nclawyersweekly.com/feed/, en-US, United States diff --git a/datasets/news/people.csv b/datasets/news/people.csv index 0fe5666..b39c5c2 100644 --- a/datasets/news/people.csv +++ b/datasets/news/people.csv @@ -11,6 +11,7 @@ Blog on Bart Ribbers - PureTryOut, https://fam-ribbers.com/blog/index.xml, en-US ollieparanoid, https://ollieparanoid.github.io/feed.xml, en-US, United States Oliver Smith, https://fosstodon.org/@ollieparanoid, en-US, Germany drkhsh, https://drkhsh.at/atom.xml, en-US, Austria +Personal, http://feeds.fosketts.net/StephenFoskettPackRat_Personal, en-US, United States Codemadness, https://codemadness.org/atom.xml, en-US, United States Keywan Tonekaboni, https://social.heise.de/@ktn.rss, de-DE, Germany keywan, https://chaos.social/@keywan.rss, de-DE, Germany @@ -39,3 +40,4 @@ alexander cobleigh, https://cblgh.org/all.xml, en-US, United States alexander cobleigh (updates), https://cblgh.org/updates.xml, en-US, United States Daniel Aleksandersen, https://mastodon.daniel.priv.no/@da.rss, en-US, United States Laslo Hunhold, https://laslo.hunhold.de/index.xml, en-US, Germany +Kitty Cat, https://kitty.social/@Kitty.rss, en-US, Worldwide diff --git a/datasets/news/russia.csv b/datasets/news/russia.csv index 0e807a4..a0d5e34 100644 --- a/datasets/news/russia.csv +++ b/datasets/news/russia.csv @@ -1,2 +1,3 @@ name, url, language, country Kasparov, https://www.kasparov.com/feed/, en-US, United States +РЖЕВСКАЯ ПРАВДА, https://presska.ru/feed/, ru-RU, Russia diff --git a/datasets/news/technology.csv b/datasets/news/technology.csv index 779fb5a..fac5ce6 100644 --- a/datasets/news/technology.csv +++ b/datasets/news/technology.csv @@ -5,6 +5,7 @@ The Logs, https://www.flu0r1ne.net/logs/rss.xml, en-US, United States CryptoSlate, https://cryptoslate.com/feed/, en-US, United States Codemadness, https://codemadness.org/atom.xml, en-US, United States Daily Stormer, https://dailystormer.in/feed/, en-US, United States +ShadowKat Studios, https://shadowkat.net/rss.xml, en-US, Worldwide Futurism, https://futurism.com/feed, en-US, United States drkhsh, https://drkhsh.at/atom.xml, en-US, Austria Automation Rhapsody, https://automationrhapsody.com/feed/, en-US, United States diff --git a/datasets/news/telecom.csv b/datasets/news/telecom.csv index 8a672a9..0a3e950 100644 --- a/datasets/news/telecom.csv +++ b/datasets/news/telecom.csv @@ -2,3 +2,4 @@ name, url, language, country The XMPP Blog on XMPP, https://xmpp.org/feeds/all.atom.xml, en-US, Worldwide Redecentralize Blog, https://redecentralize.org/blog/feed.rss, en-US, United States ollieparanoid, https://ollieparanoid.github.io/feed.xml, en-US, United States +ShadowKat Studios, https://shadowkat.net/rss.xml, en-US, Worldwide diff --git a/datasets/news/united_states.csv b/datasets/news/united_states.csv index b304e3c..a1de527 100644 --- a/datasets/news/united_states.csv +++ b/datasets/news/united_states.csv @@ -7,3 +7,4 @@ BlackListed News, https://www.blacklistednews.com/rss.php, en-US, United States Tampa Free Press, https://www.tampafp.com/feed/, en-US, United States Real Liberty Media, https://www.reallibertymedia.com/feed/, en-US, United States Robert Reich, https://robertreich.substack.com/feed, en-US, United States +North Carolina Lawyers Weekly, https://nclawyersweekly.com/feed/, en-US, United States diff --git a/datasets/news/bsd_linux_unix.csv b/datasets/news/unix.csv similarity index 97% rename from datasets/news/bsd_linux_unix.csv rename to datasets/news/unix.csv index 669271b..99a00d9 100644 --- a/datasets/news/bsd_linux_unix.csv +++ b/datasets/news/unix.csv @@ -30,3 +30,4 @@ Solene'%, https://dataswamp.org/~solene/rss.xml, en-US, United States Solene'%, https://dataswamp.org/~solene/rss-html.xml, en-US, United States drkhsh, https://drkhsh.at/atom.xml, en-US, Austria OpenBSD Webzine, https://webzine.puffy.cafe/atom.xml, en-US, United States +ShadowKat Studios, https://shadowkat.net/rss.xml, en-US, Worldwide diff --git a/datasets/news/xmpp.csv b/datasets/news/xmpp.csv index 1a82a51..4a5ecb4 100644 --- a/datasets/news/xmpp.csv +++ b/datasets/news/xmpp.csv @@ -1,3 +1,5 @@ name, url, language, country The XMPP Blog on XMPP, https://xmpp.org/feeds/all.atom.xml, en-US, Worldwide Swift IM, https://swift.im/wordpress/index.php/feed/, en-US, Worldwide +Berlin XMPP Meetup, https://mov.im/feed/pubsub.movim.eu/berlin-xmpp-meetup, en-US, Germany +Хомяк diSabler'а, https://dsy.name/feed/, ru-RU, Russia diff --git a/datasets/projects/internet.csv b/datasets/projects/internet.csv index de303e6..7944827 100644 --- a/datasets/projects/internet.csv +++ b/datasets/projects/internet.csv @@ -1,5 +1,6 @@ name, url, language, country DownThemAll!, https://www.downthemall.org/feed, en-US, Worldwide +Pale Moon, https://forum.palemoon.org/app.php/feed/forum/1, en-US, Worldwide Falkon - KDE web browser, https://www.falkon.org/atom.xml, en-US, Czech What's Up, Fraidycat?, https://fraidyc.at/blog/feed.xml, en-US, Worldwide geomyidae, branch HEAD, gopher://bitreich.org:70/0/scm/geomyidae/atom.xml, en-US, United States @@ -16,3 +17,4 @@ Swift IM, https://swift.im/wordpress/index.php/feed/, en-US, Worldwide Kaidan, https://www.kaidan.im/atom.xml, en-US, Worldwide Dino, https://dino.im/index.xml, en-US, Worldwide Vivaldi Browser, https://vivaldi.com/feed/, en-US, United States +wicd Announcements, http://feeds.launchpad.net/wicd/announcements.atom, en-US, Worldwide diff --git a/datasets/projects/website.csv b/datasets/projects/website.csv index b067c8f..43dffe4 100644 --- a/datasets/projects/website.csv +++ b/datasets/projects/website.csv @@ -15,3 +15,4 @@ WordPress, https://wordpress.org/news/feed/, en-US, Worldwide Foswiki, https://foswiki.org/Home/WebRss, en-US, Worldwide Eleventy, https://www.11ty.dev/blog/feed.xml, en-US, Worldwide bliper, https://git.2f30.org/bliper/atom.xml, en-US, Worldwide +LinkStack, https://linkstack.org/feed/, en-US, Worldwide diff --git a/slixfeed/__main__.py b/slixfeed/__main__.py index 1e011f8..9590dbd 100644 --- a/slixfeed/__main__.py +++ b/slixfeed/__main__.py @@ -115,6 +115,7 @@ if __name__ == '__main__': xmpp.register_plugin('xep_0045') # Multi-User Chat xmpp.register_plugin('xep_0060') # PubSub xmpp.register_plugin('xep_0199') # XMPP Ping + xmpp.register_plugin('xep_0249') # Multi-User Chat # Connect to the XMPP server and start processing XMPP stanzas. xmpp.connect() diff --git a/slixfeed/confighandler.py b/slixfeed/confighandler.py index 8389015..8e1e954 100644 --- a/slixfeed/confighandler.py +++ b/slixfeed/confighandler.py @@ -42,6 +42,8 @@ async def get_value_default(key): result = 3 case "random": result = 0 + case "read": + result = "https://www.blacklistednews.com/rss.php" return result diff --git a/slixfeed/datahandler.py b/slixfeed/datahandler.py index 56a1bd3..234cdd7 100644 --- a/slixfeed/datahandler.py +++ b/slixfeed/datahandler.py @@ -34,6 +34,7 @@ from urllib.parse import urlunsplit from lxml import html +# NOTE Why (if res[0]) and (if res[1] == 200)? async def download_updates(db_file, url=None): """ Check feeds for new entries. @@ -127,6 +128,9 @@ async def download_updates(db_file, url=None): if entry.has_key("published"): date = entry.published date = await datetimehandler.rfc2822_to_iso8601(date) + elif entry.has_key("updated"): + date = entry.updated + date = await datetimehandler.rfc2822_to_iso8601(date) else: date = None exist = await sqlitehandler.check_entry_exist( @@ -139,19 +143,7 @@ async def download_updates(db_file, url=None): ) if not exist: # new_entry = new_entry + 1 - if entry.has_key("published"): - date = entry.published - date = await datetimehandler.rfc2822_to_iso8601(date) - # try: - # date = datetime.strptime(date, "%a, %d %b %Y %H:%M:%S %z") - # except: - # date = datetime.strptime(date, '%a, %d %b %Y %H:%M:%S %Z') - # finally: - # date = date.isoformat() - # if parsedate(date): # Is RFC 2822 format - # date = parsedate_to_datetime(date) # Process timestamp - # date = date.isoformat() # Convert to ISO 8601 - else: + if not date: # TODO Just set date = "*** No date ***" # date = await datetime.now().isoformat() date = await datetimehandler.now() @@ -215,6 +207,162 @@ async def download_updates(db_file, url=None): # print(await datetimehandler.current_time(), exist, title) +# NOTE Why (if result[0]) and (if result[1] == 200)? +async def view_feed(db_file, url): + """ + Check feeds for new entries. + + Parameters + ---------- + db_file : str + Path to database file. + url : str, optional + URL. The default is None. + + Returns + ------- + msg : str + Feed content or error message. + """ + result = await download_feed(url) + if result[0]: + try: + feed = feedparser.parse(result[0]) + if feed.bozo: + # msg = ( + # ">{}\n" + # "WARNING: Bozo detected!\n" + # "For more information, visit " + # "https://pythonhosted.org/feedparser/bozo.html" + # ).format(url) + # msg = await probe_page(view_feed, url, result[0]) + msg = await probe_page(view_feed, url, result[0], db_file) + return msg + except ( + IncompleteReadError, + IncompleteRead, + error.URLError + ) as e: + # print(e) + # TODO Print error to log + msg = ( + "> {}\n" + "Error: {}" + ).format(url, e) + breakpoint() + if result[1] == 200: + title = await get_title(url, result[0]) + entries = feed.entries + msg = "Extracted {} entries from {}:\n```\n".format( + len(entries), + title + ) + count = 0 + for entry in entries: + count += 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 = await join_url(url, entry.link) + link = await trim_url(link) + else: + link = "*** No link ***" + if entry.has_key("published"): + date = entry.published + date = await datetimehandler.rfc2822_to_iso8601(date) + elif entry.has_key("updated"): + date = entry.updated + date = await datetimehandler.rfc2822_to_iso8601(date) + else: + date = "*** No date ***" + msg += ( + "Title : {}\n" + "Date : {}\n" + "Link : {}\n" + "Count : {}\n" + "\n" + ).format( + title, + date, + link, + count + ) + msg += ( + "```\n" + "Source: {}\n" + "Enter a number from 1 - {} using command `select` " + "to view a specific item from the list." + ).format(url, count) + await sqlitehandler.set_settings_value(db_file, ["read", url]) + else: + msg = ( + ">{}\nFailed to load URL. Reason: {}" + ).format(url, result[1]) + return msg + + +async def view_entry(db_file, num): + num = int(num) - 1 + url = await sqlitehandler.get_settings_value(db_file, "read") + result = await download_feed(url) + if result[1] == 200: + feed = feedparser.parse(result[0]) + title = await get_title(url, result[0]) + entries = feed.entries + entry = entries[num] + if entry.has_key("title"): + title = entry.title + else: + title = "*** No title ***" + if entry.has_key("published"): + date = entry.published + date = await datetimehandler.rfc2822_to_iso8601(date) + elif entry.has_key("updated"): + date = entry.updated + date = await datetimehandler.rfc2822_to_iso8601(date) + else: + date = "*** No date ***" + if entry.has_key("summary"): + summary = entry.summary + # Remove HTML tags + # summary = BeautifulSoup(summary, "lxml").text + # TODO Limit text length + # summary = summary.replace("\n\n", "\n") + else: + summary = "*** No summary ***" + if entry.has_key("link"): + # link = complete_url(source, entry.link) + link = await join_url(url, entry.link) + link = await trim_url(link) + else: + link = "*** No link ***" + msg = ( + "{}\n" + "\n" + "{}\n" + "\n" + "{}\n" + "\n" + "{}\n" + "\n" + ).format( + title, + date, + summary, + link + ) + else: + msg = ( + ">{}\n" + "Failed to load URL. Reason: {}\n" + "Try again momentarily." + ).format(url, result[1]) + return msg + + async def add_feed_no_check(db_file, data): """ Add given feed without validity check. @@ -278,26 +426,7 @@ async def add_feed(db_file, url): "Bozo detected. Failed to load: {}." ).format(url) print(bozo) - try: - # tree = etree.fromstring(res[0]) # etree is for xml - tree = html.fromstring(res[0]) - except: - msg = ( - "> {}\nFailed to parse URL as feed." - ).format(url) - if not msg: - print("RSS Auto-Discovery Engaged") - msg = await feed_mode_auto_discovery(db_file, url, tree) - if not msg: - print("RSS Scan Mode Engaged") - msg = await feed_mode_scan(db_file, url, tree) - if not msg: - print("RSS Arbitrary Mode Engaged") - msg = await feed_mode_request(db_file, url, tree) - if not msg: - msg = ( - "> {}\nNo news feeds were found for URL." - ).format(url) + msg = await probe_page(add_feed, url, res[0], db_file) else: status = res[1] msg = await sqlitehandler.add_feed( @@ -310,7 +439,7 @@ async def add_feed(db_file, url): else: status = res[1] msg = ( - "> {}\nFailed to get URL. Reason: {}" + "> {}\nFailed to load URL. Reason: {}" ).format(url, status) else: ix = exist[0] @@ -323,6 +452,41 @@ async def add_feed(db_file, url): return msg +# TODO callback for use with add_feed and view_feed +async def probe_page(callback, url, doc, db_file=None): + msg = None + try: + # tree = etree.fromstring(res[0]) # etree is for xml + tree = html.fromstring(doc) + except: + msg = ( + "> {}\nFailed to parse URL as feed." + ).format(url) + if not msg: + print("RSS Auto-Discovery Engaged") + msg = await feed_mode_auto_discovery(url, tree) + if not msg: + print("RSS Scan Mode Engaged") + msg = await feed_mode_scan(url, tree) + if not msg: + print("RSS Arbitrary Mode Engaged") + msg = await feed_mode_request(url, tree) + if not msg: + msg = ( + "> {}\nNo news feeds were found for URL." + ).format(url) + # elif msg: + else: + if isinstance(msg, str): + return msg + elif isinstance(msg, list): + url = msg[0] + if db_file: + return await callback(db_file, url) + else: + return await callback(url) + + async def download_feed(url): """ Download content of given URL. @@ -560,7 +724,7 @@ async def trim_url(url): # TODO Improve scan by gradual decreasing of path -async def feed_mode_request(db_file, url, tree): +async def feed_mode_request(url, tree): """ Lookup for feeds by pathname using HTTP Requests. @@ -639,8 +803,8 @@ async def feed_mode_request(db_file, url, tree): positive = 1 msg += ( "Title: {}\n" - " Link: {}\n" - "Count: {}\n" + "Link : {}\n" + "Items: {}\n" "\n" ).format( feed_name, @@ -656,12 +820,10 @@ async def feed_mode_request(db_file, url, tree): ).format(url) return msg elif feeds: - feed_addr = list(feeds)[0] - msg = await add_feed(db_file, feed_addr) - return msg + return feeds -async def feed_mode_scan(db_file, url, tree): +async def feed_mode_scan(url, tree): """ Scan page for potential feeds by pathname. @@ -760,12 +922,10 @@ async def feed_mode_scan(db_file, url, tree): ).format(url) return msg elif feeds: - feed_addr = list(feeds)[0] - msg = await add_feed(db_file, feed_addr) - return msg + return feeds -async def feed_mode_auto_discovery(db_file, url, tree): +async def feed_mode_auto_discovery(url, tree): """ Lookup for feeds using RSS autodiscovery technique. @@ -819,14 +979,7 @@ async def feed_mode_auto_discovery(db_file, url, tree): return msg elif feeds: feed_addr = await join_url(url, feeds[0].xpath('@href')[0]) - # if feed_addr.startswith("/"): - # feed_addr = url + feed_addr - # NOTE Why wouldn't add_feed return a message - # upon success unless return is explicitly - # mentioned, yet upon failure it wouldn't? - # return await add_feed(db_file, feed_addr) - msg = await add_feed(db_file, feed_addr) - return msg + return [feed_addr] async def feed_to_http(url): diff --git a/slixfeed/sqlitehandler.py b/slixfeed/sqlitehandler.py index ffa76a4..fe54a2e 100644 --- a/slixfeed/sqlitehandler.py +++ b/slixfeed/sqlitehandler.py @@ -669,7 +669,7 @@ async def toggle_status(db_file, ix): "id": ix }) msg = ( - "Updates for '{}' are now {}." + "Updates from '{}' are now {}." ).format(title, state) except: msg = ( @@ -1157,12 +1157,13 @@ async def search_feeds(db_file, query): """ cur = get_cursor(db_file) sql = ( - "SELECT name, address, id " + "SELECT name, address, id, enabled " "FROM feeds " "WHERE name LIKE ? " + "OR address LIKE ? " "LIMIT 50" ) - results = cur.execute(sql, [f'%{query}%']) + results = cur.execute(sql, [f'%{query}%', f'%{query}%']) results_list = ( "Feeds containing '{}':\n```" ).format(query) @@ -1170,14 +1171,16 @@ async def search_feeds(db_file, query): for result in results: counter += 1 results_list += ( - "\nName: {}" - "\n URL: {}" - "\n ID: {}" + "\nName : {}" + "\nURL : {}" + "\nIndex : {}" + "\nMode : {}" "\n" ).format( str(result[0]), str(result[1]), - str(result[2]) + str(result[2]), + str(result[3]) ) if counter: return results_list + "\n```\nTotal of {} feeds".format(counter) @@ -1359,7 +1362,7 @@ async def set_settings_value(db_file, key_value): # TODO Place settings also in a file async def set_settings_value_default(cur, key): """ - Set default settings value. + Set default settings value, if no value found. Parameters ---------- diff --git a/slixfeed/taskhandler.py b/slixfeed/taskhandler.py index 9fe15a5..0527b4b 100644 --- a/slixfeed/taskhandler.py +++ b/slixfeed/taskhandler.py @@ -23,6 +23,10 @@ TODO 4) Do not send updates when busy or away. See https://slixmpp.readthedocs.io/en/latest/event_index.html#term-changed_status +5) Animate "You have X news items" + 📬️ when sent + 📫️ after sent + NOTE 1) Self presence @@ -69,10 +73,9 @@ await taskhandler.start_tasks( async def start_tasks_xmpp(self, jid, tasks): task_manager[jid] = {} for task in tasks: - print("task") - print(task) - print("tasks") - print(tasks) + # print("task:", task) + # print("tasks:") + # print(tasks) # breakpoint() match task: case "check": @@ -174,7 +177,7 @@ async def task_jid(self, jid): async def send_update(self, jid, num=None): - print(await datetimehandler.current_time(), jid, "def send_update") + # print(await datetimehandler.current_time(), jid, "def send_update") """ Send news items as messages. @@ -194,7 +197,7 @@ async def send_update(self, jid, num=None): ) if new: # TODO Add while loop to assure delivery. - print(await datetimehandler.current_time(), ">>> ACT send_message",jid) + # print(await datetimehandler.current_time(), ">>> ACT send_message",jid) xmpphandler.Slixfeed.send_message( self, mto=jid, @@ -235,7 +238,7 @@ async def send_update(self, jid, num=None): async def send_status(self, jid): - print(await datetimehandler.current_time(), jid, "def send_status") + # print(await datetimehandler.current_time(), jid, "def send_status") """ Send status message. @@ -253,19 +256,19 @@ async def send_status(self, jid): ) if not enabled: status_mode = "xa" - status_text = "Send \"Start\" to receive news." + status_text = "📫️ Send \"Start\" to receive updates" else: feeds = await filehandler.initdb( jid, sqlitehandler.get_number_of_items, "feeds" ) - print(">>> feeds:", feeds, "jid:", jid) + # print(await datetimehandler.current_time(), jid, "has", feeds, "feeds") if not feeds: print(">>> not feeds:", feeds, "jid:", jid) status_mode = "available" status_text = ( - "📂️ Send a URL from a blog or a news website." + "📭️ Send a URL from a blog or a news website" ) else: unread = await filehandler.initdb( @@ -275,7 +278,7 @@ async def send_status(self, jid): if unread: status_mode = "chat" status_text = ( - "📰 You have {} news items to read." + "📬️ You have {} news items" ).format(str(unread)) # status_text = ( # "📰 News items: {}" @@ -285,10 +288,10 @@ async def send_status(self, jid): # ).format(str(unread)) else: status_mode = "available" - status_text = "🗞 No news" + status_text = "📪️ No news" # breakpoint() - print(status_text, "for", jid) + # print(await datetimehandler.current_time(), status_text, "for", jid) xmpphandler.Slixfeed.send_presence( self, pshow=status_mode, @@ -357,7 +360,7 @@ async def refresh_task(self, jid, callback, key, val=None): # TODO Take this function out of # async def check_updates(jid): - print(await datetimehandler.current_time(), jid, "def check_updates") + # print(await datetimehandler.current_time(), jid, "def check_updates") """ Start calling for update check up. diff --git a/slixfeed/xmpphandler.py b/slixfeed/xmpphandler.py index eb95a4e..2e34fa2 100644 --- a/slixfeed/xmpphandler.py +++ b/slixfeed/xmpphandler.py @@ -33,12 +33,18 @@ NOTE JID: self.boundjid.bare MUC: self.nick +2) Extracting attribute using xmltodict. + import xmltodict + message = xmltodict.parse(str(message)) + jid = message["message"]["x"]["@jid"] + """ import asyncio import logging import os import slixmpp +from random import randrange from slixmpp.plugins.xep_0363.http_upload import FileTooBig, HTTPError, UploadServiceNotFound @@ -71,7 +77,7 @@ class Slixfeed(slixmpp.ClientXMPP): ------- News bot that sends updates from RSS feeds. """ - def __init__(self, jid, password): + def __init__(self, jid, password, room=None, nick=None): slixmpp.ClientXMPP.__init__(self, jid, password) # The session_start event will be triggered when @@ -82,8 +88,9 @@ class Slixfeed(slixmpp.ClientXMPP): self.add_event_handler("session_start", self.start_session) self.add_event_handler("session_resumed", self.start_session) self.add_event_handler("got_offline", print("got_offline")) - self.add_event_handler("got_online", self.check_readiness) + # self.add_event_handler("got_online", self.check_readiness) self.add_event_handler("changed_status", self.check_readiness) + self.add_event_handler("presence_unavailable", self.stop_tasks) # self.add_event_handler("changed_subscription", self.check_subscription) @@ -96,9 +103,10 @@ class Slixfeed(slixmpp.ClientXMPP): # stanza is received. Be aware that that includes # MUC messages and error messages. self.add_event_handler("message", self.message) + self.add_event_handler("message", self.settle) - self.add_event_handler("groupchat_invite", self.muc_invite) - self.add_event_handler("groupchat_direct_invite", self.muc_invite) + self.add_event_handler("groupchat_invite", self.accept_muc_invite) + self.add_event_handler("groupchat_direct_invite", self.accept_muc_invite) # self.add_event_handler("groupchat_message", self.message) # self.add_event_handler("disconnected", self.reconnect) @@ -109,9 +117,8 @@ class Slixfeed(slixmpp.ClientXMPP): self.add_event_handler("presence_error", self.presence_error) self.add_event_handler("presence_subscribe", self.presence_subscribe) self.add_event_handler("presence_subscribed", self.presence_subscribed) - self.add_event_handler("presence_unavailable", self.presence_unavailable) self.add_event_handler("presence_unsubscribe", self.presence_unsubscribe) - self.add_event_handler("presence_unsubscribed", self.presence_unsubscribed) + self.add_event_handler("presence_unsubscribed", self.unsubscribe) # Initialize event loop # self.loop = asyncio.get_event_loop() @@ -141,7 +148,7 @@ class Slixfeed(slixmpp.ClientXMPP): """ async def presence_available(self, presence): - print("def presence_available", presence["from"].bare) + # print("def presence_available", presence["from"].bare) if presence["from"].bare not in self.boundjid.bare: jid = presence["from"].bare await taskhandler.clean_tasks_xmpp( @@ -157,10 +164,14 @@ class Slixfeed(slixmpp.ClientXMPP): # main_task.extend([asyncio.create_task(taskhandler.task_jid(jid))]) # print(main_task) - async def presence_unavailable(self, presence): + async def stop_tasks(self, presence): if not self.boundjid.bare: - print("presence_unavailable", presence["from"].bare, presence["type"]) - print(presence) + jid = presence["from"].bare + print(">>> unavailable:", jid) + await taskhandler.clean_tasks_xmpp( + jid, + ["interval", "status", "check"] + ) async def presence_error(self, presence): @@ -175,32 +186,30 @@ class Slixfeed(slixmpp.ClientXMPP): print("presence_subscribed") print(presence) - async def presence_unsubscribe(self, presence): - print("presence_unsubscribe") - print(presence) - - async def presence_unsubscribed(self, presence): - print("presence_unsubscribed") - print(presence) - async def reactions(self, message): print("reactions") print(message) - async def muc_invite(self, message): - print(message) - breakpoint() - muc = message - self.add_event_handler( - "muc::[room]::message", - self.message - ) + async def accept_muc_invite(self, message): + ctr = message["from"].bare + jid = message['groupchat_invite']['jid'] + tkn = randrange(10000, 99999) self.plugin['xep_0045'].join_muc( - self.room, - self.nick, + jid, + "Slixfeed (RSS News Bot)", # If a room password is needed, use: # password=the_room_password, ) + self.send_message( + mto=ctr, + mbody=( + "Send activation token {} to groupchat xmpp:{}?join." + ).format(tkn, jid) + ) + # self.add_event_handler( + # "muc::[room]::message", + # self.message + # ) async def on_session_end(self, event): @@ -279,33 +288,14 @@ class Slixfeed(slixmpp.ClientXMPP): ------- None. """ - print("def check_readiness", presence["from"].bare, presence["type"]) + # print("def check_readiness", presence["from"].bare, presence["type"]) # # available unavailable away (chat) dnd xa # print(">>> type", presence["type"], presence["from"].bare) # # away chat dnd xa # print(">>> show", presence["show"], presence["from"].bare) jid = presence["from"].bare - if presence["type"] == "unavailable": - print(">>> unavailable:", jid) - await taskhandler.clean_tasks_xmpp( - jid, - ["interval", "status", "check"] - ) - # elif presence["type"] == "available": - # # elif presence["type"] == "available" or presence["show"] == "chat": - # print(">>> available:", jid) - # # breakpoint() - # try: - # if task_manager[jid]: - # for task in task_manager[jid]: - # # print(">>>", jid, "cancel", task) - # task_manager[jid][task].cancel() - # except: - # print(">>> EXC: No task_manager for:", jid) - # await taskhandler.task_jid(jid) - # # print(task_manager[jid]) - elif presence["show"] in ("away", "dnd", "xa"): + if presence["show"] in ("away", "dnd", "xa"): print(">>> away, dnd, xa:", jid) await taskhandler.clean_tasks_xmpp( jid, @@ -351,6 +341,81 @@ class Slixfeed(slixmpp.ClientXMPP): # await taskhandler.select_file() + async def settle(self, msg): + """ + Add JID to roster and settle subscription. + + Parameters + ---------- + jid : str + Jabber ID. + + Returns + ------- + None. + """ + jid = msg["from"].bare + await self.get_roster() + # Check whether JID is in roster; otherwise, add it. + if jid not in self.client_roster.keys(): + self.send_presence_subscription( + pto=jid, + ptype="subscribe", + pnick="Slixfeed RSS News Bot" + ) + self.update_roster( + jid, + subscription="both" + ) + # Check whether JID is subscribed; otherwise, ask for presence. + if not self.client_roster[jid]["to"]: + self.send_presence_subscription( + pto=jid, + pfrom=self.boundjid.bare, + ptype="subscribe", + pnick="Slixfeed RSS News Bot" + ) + self.send_message( + mto=jid, + mtype="headline", + msubject="RSS News Bot", + mbody=("Accept subscription request to receive updates."), + mfrom=self.boundjid.bare, + mnick="Slixfeed RSS News Bot" + ) + self.send_presence( + pto=jid, + pfrom=self.boundjid.bare, + # Accept symbol 🉑️ 👍️ ✍ + pstatus="✒️ Accept subscription request to receive updates", + # ptype="subscribe", + pnick="Slixfeed RSS News Bot" + ) + + + async def presence_unsubscribe(self, presence): + print("presence_unsubscribe") + print(presence) + + + async def unsubscribe(self, presence): + jid = presence["from"].bare + self.send_presence( + pto=jid, + pfrom=self.boundjid.bare, + pstatus="🖋️ Subscribe to receive updates", + pnick="Slixfeed RSS News Bot" + ) + self.send_message( + mto=jid, + mbody="You have been unsubscribed." + ) + self.update_roster( + jid, + subscription="remove" + ) + + async def message(self, msg): """ Process incoming message stanzas. Be aware that this also @@ -367,18 +432,31 @@ class Slixfeed(slixmpp.ClientXMPP): """ # print("message") # print(msg) - if msg["type"] in ("chat", "normal"): + if msg["type"] in ("chat", "groupchat", "normal"): action = 0 jid = msg["from"].bare - + if msg["type"] == "groupchat": + ctr = await filehandler.initdb( + jid, + sqlitehandler.get_settings_value, + "masters" + ) + if (msg["from"][msg["from"].index("/")+1:] not in ctr + or not msg["body"].startswith("!")): + return + # # Begin processing new JID # # Deprecated in favour of event "presence_available" # db_dir = filehandler.get_default_dbdir() # os.chdir(db_dir) # if jid + ".db" not in os.listdir(): # await taskhandler.task_jid(jid) - + print(msg["body"]) + print(msg["body"].split()) message = " ".join(msg["body"].split()) + if msg["type"] == "groupchat": + message = message[1:] + print(message) message_lowercase = message.lower() print(await datetimehandler.current_time(), "ACCOUNT: " + str(msg["from"])) @@ -387,13 +465,23 @@ class Slixfeed(slixmpp.ClientXMPP): match message_lowercase: case "help": action = print_help() - case _ if message_lowercase in ["greetings", "hello", "hey"]: + case "info": + action = print_info() + case _ if message_lowercase in [ + "greetings", "hallo", "hello", "hey", + "hi", "hola", "holla", "hollo"]: action = ( - "Greeting! I'm Slixfeed The News Bot!" - "\n" - "Send a URL of a news website to start." + "Greeting!\n" + "I'm Slixfeed, an RSS News Bot!\n" + "Send \"help\" for instructions." ) - print(task_manager[jid]) + # print("task_manager[jid]") + # print(task_manager[jid]) + await self.get_roster() + print("roster 1") + print(self.client_roster) + print("roster 2") + print(self.client_roster.keys()) case _ if message_lowercase.startswith("add"): message = message[4:] url = message.split(" ")[0] @@ -461,6 +549,9 @@ class Slixfeed(slixmpp.ClientXMPP): ).format(val) else: action = "Missing keywords." + case _ if (message_lowercase.startswith("gemini") or + message_lowercase.startswith("gopher:")): + action = "Gemini and Gopher are not supported yet." case _ if (message_lowercase.startswith("http") or message_lowercase.startswith("feed:")): url = message @@ -581,8 +672,19 @@ class Slixfeed(slixmpp.ClientXMPP): ).format(val) else: action = "Missing value." - case _ if message_lowercase.startswith("random"): + case "random": action = "Updates will be sent randomly." + case _ if message_lowercase.startswith("read"): + url = message[5:] + if url.startswith("http"): + # action = await datahandler.view_feed(url) + action = await filehandler.initdb( + jid, + datahandler.view_feed, + url + ) + else: + action = "Missing URL." case _ if message_lowercase.startswith("recent"): num = message[7:] if num: @@ -657,6 +759,16 @@ class Slixfeed(slixmpp.ClientXMPP): jid, sqlitehandler.statistics ) + case _ if message_lowercase.startswith("select"): + num = message[7:] + if num: + action = await filehandler.initdb( + jid, + datahandler.view_entry, + num + ) + else: + action = "Missing number." case _ if message_lowercase.startswith("status "): ix = message[7:] action = await filehandler.initdb( @@ -711,6 +823,82 @@ class Slixfeed(slixmpp.ClientXMPP): if action: msg.reply(action).send() +def print_info(): + """ + Print information. + + Returns + ------- + msg : str + Message. + """ + msg = ( + "```\n" + "NAME\n" + "Slixfeed - News syndication bot for Jabber/XMPP\n" + "\n" + "DESCRIPTION\n" + " Slixfeed is a news aggregator bot for online news feeds.\n" + " This program is primarily designed for XMPP.\n" + " For more information, visit https://xmpp.org/software/\n" + "\n" + # "PROTOCOLS\n" + # " Supported prootcols are IRC, Matrix and XMPP.\n" + # " For the best experience, we recommend you to use XMPP.\n" + # "\n" + "FILETYPES\n" + " Supported filetypes are Atom, RDF and RSS.\n" + "\n" + "AUTHORS\n" + " Laura Harbinger, Schimon Zackary.\n" + "\n" + "THANKS\n" + " Christian Dersch (SalixOS)," + " Cyrille Pontvieux (SalixOS, France)," + "\n" + " Denis Fomin (Gajim, Russia)," + " Dimitris Tzemos (SalixOS, Greece)," + "\n" + " Emmanuel Gil Peyrot (poezio, France)," + " George Vlahavas (SalixOS, Greece)," + "\n" + " Pierrick Le Brun (SalixOS, France)," + " Thorsten Mühlfelder (SalixOS, Germany)," + "\n" + " Yann Leboulanger (Gajim, France).\n" + "\n" + "COPYRIGHT\n" + " Slixfeed is free software; you can redistribute it and/or\n" + " modify it under the terms of the GNU General Public License\n" + " as published by the Free Software Foundation; version 3 only\n" + "\n" + " Slixfeed is distributed in the hope that it will be useful,\n" + " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + " GNU General Public License for more details.\n" + "\n" + "NOTE\n" + " Make Slixfeed your own.\n" + "\n" + " You can run Slixfeed on your own computer, server, and\n" + " even on a Linux phone (i.e. Droidian, Mobian NixOS,\n" + " postmarketOS). You can also use Termux.\n" + "\n" + " All you need is one of the above and an XMPP account to\n" + " connect Slixfeed to.\n" + "\n" + "DOCUMENTATION\n" + " Slixfeed\n" + " https://gitgud.io/sjehuda/slixfeed\n" + " Slixmpp\n" + " https://slixmpp.readthedocs.io/\n" + " feedparser\n" + " https://pythonhosted.org/feedparser\n" + "```" + ) + return msg + + def print_help(): """ Print help manual. @@ -735,6 +923,10 @@ def print_help(): " Enable bot and send updates.\n" " stop\n" " Disable bot and stop updates.\n" + " URL\n" + " Add URL to subscription list.\n" + " add URL TITLE\n" + " Add URL to subscription list (without validity check).\n" " feeds\n" " List subscriptions.\n" " interval N\n" @@ -742,7 +934,21 @@ def print_help(): " next N\n" " Send N next updates.\n" " quantum N\n" - " Set N updates for each interval.\n" + " Set amount of updates for each interval.\n" + " read URL\n" + " Display most recent 20 titles of given URL.\n" + " read URL NUM\n" + " Display specified entry from given URL.\n" + "\n" + "GROUPCHAT OPTIONS\n" + " ! (command initiation)\n" + " Use exclamation mark to initiate an actionable command.\n" + " demaster NICKNAME\n" + " Remove master privilege.\n" + " mastership NICKNAME\n" + " Add master privilege.\n" + " ownership NICKNAME\n" + " Set new owner.\n" "\n" "FILTER OPTIONS\n" " allow\n" @@ -755,10 +961,6 @@ def print_help(): # " Reset deny list.\n" "\n" "EDIT OPTIONS\n" - " URL\n" - " Add URL to subscription list.\n" - " add URL TITLE\n" - " Add URL to subscription list (without validity check).\n" " remove ID\n" " Remove feed from subscription list.\n" " status ID\n" @@ -793,46 +995,15 @@ def print_help(): "SUPPORT\n" " help\n" " Print this help manual.\n" + " info\n" + " Print information page.\n" " support\n" " Join xmpp:slixmpp@muc.poez.io?join\n" - "\n" + # "\n" # "PROTOCOLS\n" # " Supported prootcols are IRC, Matrix and XMPP.\n" # " For the best experience, we recommend you to use XMPP.\n" # "\n" - "FILETYPES\n" - " Supported filetypes are Atom, RDF and RSS.\n" - "\n" - "AUTHORS\n" - " Laura Harbinger, Schimon Zackary.\n" - "\n" - "COPYRIGHT\n" - " Slixfeed is free software; you can redistribute it and/or\n" - " modify it under the terms of the GNU General Public License\n" - " as published by the Free Software Foundation; version 3 only\n" - "\n" - " Slixfeed is distributed in the hope that it will be useful,\n" - " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - " GNU General Public License for more details.\n" - "\n" - "NOTE\n" - " Make Slixfeed your own.\n" - "\n" - " You can run Slixfeed on your own computer, server, and\n" - " even on a Linux phone (i.e. Droidian, Mobian NixOS,\n" - " postmarketOS). You can also use Termux.\n" - "\n" - " All you need is one of the above and an XMPP account to\n" - " connect Slixfeed to.\n" - "\n" - "DOCUMENTATION\n" - " Slixfeed\n" - " https://gitgud.io/sjehuda/slixfeed\n" - " Slixmpp\n" - " https://slixmpp.readthedocs.io/\n" - " feedparser\n" - " https://pythonhosted.org/feedparser\n" - "\n```" + "```" ) return msg