From 634b0e3ce62f15343dcc1313fb9445f7add3c3b8 Mon Sep 17 00:00:00 2001 From: Schimon Jehudah Date: Mon, 27 Nov 2023 17:22:56 +0000 Subject: [PATCH] Add MUC support including bookmarks and improve filtering --- slixfeed/__main__.py | 3 +- slixfeed/listhandler.py | 32 ++++++- slixfeed/sqlitehandler.py | 14 ++- slixfeed/taskhandler.py | 2 +- slixfeed/xmpphandler.py | 173 ++++++++++++++++++++++++++++++++------ 5 files changed, 193 insertions(+), 31 deletions(-) diff --git a/slixfeed/__main__.py b/slixfeed/__main__.py index 6802792..c50b185 100644 --- a/slixfeed/__main__.py +++ b/slixfeed/__main__.py @@ -115,8 +115,9 @@ if __name__ == '__main__': xmpp.register_plugin('xep_0045') # Multi-User Chat xmpp.register_plugin('xep_0048') # Bookmarks xmpp.register_plugin('xep_0060') # PubSub - xmpp.register_plugin('xep_0199') # XMPP Ping + xmpp.register_plugin('xep_0199', {'keepalive': True, 'frequency': 15}) # XMPP Ping xmpp.register_plugin('xep_0249') # Multi-User Chat + xmpp.register_plugin('xep_0402') # PEP Native Bookmarks # Connect to the XMPP server and start processing XMPP stanzas. xmpp.connect() diff --git a/slixfeed/listhandler.py b/slixfeed/listhandler.py index e5063c8..462f3fa 100644 --- a/slixfeed/listhandler.py +++ b/slixfeed/listhandler.py @@ -18,7 +18,7 @@ TODO import sqlitehandler -async def set_list(newwords, keywords): +async def add_to_list(newwords, keywords): """ Append new keywords to list. @@ -48,6 +48,36 @@ async def set_list(newwords, keywords): return val +async def remove_from_list(newwords, keywords): + """ + Remove given keywords from list. + + Parameters + ---------- + newwords : str + List of new keywords. + keywords : str + List of current keywords. + + Returns + ------- + val : str + List of new keywords. + """ + try: + keywords = keywords.split(",") + except: + keywords = [] + newwords = newwords.lower().split(",") + for word in newwords: + word = word.strip() + if len(word) and word in keywords: + keywords.remove(word) + keywords.sort() + val = ",".join(keywords) + return val + + async def is_listed(db_file, key, string): """ Check keyword match. diff --git a/slixfeed/sqlitehandler.py b/slixfeed/sqlitehandler.py index b766eda..2775f06 100644 --- a/slixfeed/sqlitehandler.py +++ b/slixfeed/sqlitehandler.py @@ -1128,15 +1128,21 @@ async def last_entries(db_file, num): "LIMIT :num " ) results = cur.execute(sql, (num,)) - titles_list = "Recent {} titles:\n".format(num) + titles_list = "Recent {} titles:\n```".format(num) + counter = 0 for result in results: + counter += 1 titles_list += ( "\n{}\n{}\n" ).format( str(result[0]), str(result[1]) ) - return titles_list + if counter: + titles_list += "```\n" + return titles_list + else: + return "There are no news at the moment." async def search_feeds(db_file, query): @@ -1185,7 +1191,7 @@ async def search_feeds(db_file, query): if counter: return results_list + "\n```\nTotal of {} feeds".format(counter) else: - return "No feeds found for: {}".format(query) + return "No feeds were found for: {}".format(query) async def search_entries(db_file, query): @@ -1234,7 +1240,7 @@ async def search_entries(db_file, query): if counter: return results_list + "```\nTotal of {} results".format(counter) else: - return "No results found for: {}".format(query) + return "No results were found for: {}".format(query) """ FIXME Error due to missing date, but it appears that date is present: diff --git a/slixfeed/taskhandler.py b/slixfeed/taskhandler.py index 4adda25..3c3f70f 100644 --- a/slixfeed/taskhandler.py +++ b/slixfeed/taskhandler.py @@ -282,7 +282,7 @@ async def send_status(self, jid): if unread: status_mode = "chat" status_text = ( - "📬️ You have {} news items" + "📬️ There are {} news items" ).format(str(unread)) # status_text = ( # "📰 News items: {}" diff --git a/slixfeed/xmpphandler.py b/slixfeed/xmpphandler.py index 3e70915..3ad52fe 100644 --- a/slixfeed/xmpphandler.py +++ b/slixfeed/xmpphandler.py @@ -56,6 +56,8 @@ import slixmpp from random import randrange from slixmpp.plugins.xep_0363.http_upload import FileTooBig, HTTPError, UploadServiceNotFound +# from slixmpp.plugins.xep_0402 import BookmarkStorage, Conference +from slixmpp.plugins.xep_0048.stanza import Bookmarks import datahandler import datetimehandler @@ -64,6 +66,10 @@ import listhandler import sqlitehandler import taskhandler +import xmltodict +import xml.etree.ElementTree as ET +from lxml import etree + main_task = [] jid_tasker = {} task_manager = {} @@ -96,6 +102,8 @@ class Slixfeed(slixmpp.ClientXMPP): # our roster. self.add_event_handler("session_start", self.start_session) self.add_event_handler("session_resumed", self.start_session) + self.add_event_handler("session_start", self.autojoin_muc) + self.add_event_handler("session_resumed", self.autojoin_muc) self.add_event_handler("got_offline", print("got_offline")) # self.add_event_handler("got_online", self.check_readiness) self.add_event_handler("changed_status", self.check_readiness) @@ -238,18 +246,73 @@ class Slixfeed(slixmpp.ClientXMPP): # If a room password is needed, use: # password=the_room_password, ) - # self.add_event_handler( - # "muc::[room]::message", - # self.message - # ) - # await self.get_bookmarks() - # bookmark = self.plugin['xep_0048'].instantiate_pep() - # print(bookmark) - # nick = "Slixfeed (RSS News Bot)" - # bookmark.add_bookmark(muc_jid, nick=nick) - # await self['xep_0048'].set_bookmarks(bookmark) - # print(bookmark) + result = await self.plugin['xep_0048'].get_bookmarks() + bookmarks = result["private"]["bookmarks"] + conferences = bookmarks["conferences"] + mucs = [] + for conference in conferences: + jid = conference["jid"] + mucs.extend([jid]) + if muc_jid not in mucs: + bookmarks = Bookmarks() + mucs.extend([muc_jid]) + for muc in mucs: + bookmarks.add_conference( + muc, + "Slixfeed (RSS News Bot)", + autojoin=True + ) + await self.plugin['xep_0048'].set_bookmarks(bookmarks) + # bookmarks = Bookmarks() + # await self.plugin['xep_0048'].set_bookmarks(bookmarks) + # print(await self.plugin['xep_0048'].get_bookmarks()) + + # bm = BookmarkStorage() + # bm.conferences.append(Conference(muc_jid, autojoin=True, nick="Slixfeed (RSS News Bot)")) + # await self['xep_0402'].publish(bm) + + + async def remove_and_leave_muc(self, muc_jid): + result = await self.plugin['xep_0048'].get_bookmarks() + bookmarks = result["private"]["bookmarks"] + conferences = bookmarks["conferences"] + mucs = [] + for conference in conferences: + jid = conference["jid"] + mucs.extend([jid]) + if muc_jid in mucs: + bookmarks = Bookmarks() + mucs.remove(muc_jid) + for muc in mucs: + bookmarks.add_conference( + muc, + "Slixfeed (RSS News Bot)", + autojoin=True + ) + await self.plugin['xep_0048'].set_bookmarks(bookmarks) + self.plugin['xep_0045'].leave_muc( + muc_jid, + "Slixfeed (RSS News Bot)", + "Goodbye!", + self.boundjid.bare + ) + + + async def autojoin_muc(self, event): + result = await self.plugin['xep_0048'].get_bookmarks() + bookmarks = result["private"]["bookmarks"] + conferences = bookmarks["conferences"] + for conference in conferences: + if conference["autojoin"]: + muc = conference["jid"] + print(muc) + self.plugin['xep_0045'].join_muc( + muc, + "Slixfeed (RSS News Bot)", + # If a room password is needed, use: + # password=the_room_password, + ) async def on_session_end(self, event): @@ -562,6 +625,7 @@ class Slixfeed(slixmpp.ClientXMPP): print(self.client_roster.keys()) print("jid") print(jid) + await self.autojoin_muc() case _ if message_lowercase.startswith("activate"): if msg["type"] == "groupchat": @@ -606,16 +670,16 @@ class Slixfeed(slixmpp.ClientXMPP): ) else: action = "Missing URL." - case _ if message_lowercase.startswith("allow"): + case _ if message_lowercase.startswith("allow +"): key = "filter-" + message[:5] - val = message[6:] + val = message[7:] if val: keywords = await filehandler.initdb( jid, sqlitehandler.get_settings_value, key ) - val = await listhandler.set_list( + val = await listhandler.add_to_list( val, keywords ) @@ -630,16 +694,64 @@ class Slixfeed(slixmpp.ClientXMPP): ).format(val) else: action = "Missing keywords." - case _ if message_lowercase.startswith("deny"): - key = "filter-" + message[:4] - val = message[5:] + case _ if message_lowercase.startswith("allow -"): + key = "filter-" + message[:5] + val = message[7:] if val: keywords = await filehandler.initdb( jid, sqlitehandler.get_settings_value, key ) - val = await listhandler.set_list( + val = await listhandler.remove_from_list( + val, + keywords + ) + await filehandler.initdb( + jid, + sqlitehandler.set_settings_value, + [key, val] + ) + action = ( + "Approved keywords\n" + "```\n{}\n```" + ).format(val) + else: + action = "Missing keywords." + case _ if message_lowercase.startswith("deny +"): + key = "filter-" + message[:4] + val = message[6:] + if val: + keywords = await filehandler.initdb( + jid, + sqlitehandler.get_settings_value, + key + ) + val = await listhandler.add_to_list( + val, + keywords + ) + await filehandler.initdb( + jid, + sqlitehandler.set_settings_value, + [key, val] + ) + action = ( + "Rejected keywords\n" + "```\n{}\n```" + ).format(val) + else: + action = "Missing keywords." + case _ if message_lowercase.startswith("deny -"): + key = "filter-" + message[:4] + val = message[6:] + if val: + keywords = await filehandler.initdb( + jid, + sqlitehandler.get_settings_value, + key + ) + val = await listhandler.remove_from_list( val, keywords ) @@ -705,6 +817,11 @@ class Slixfeed(slixmpp.ClientXMPP): jid, sqlitehandler.list_feeds ) + case "goodbye": + if msg["type"] == "groupchat": + await self.remove_and_leave_muc(jid) + else: + action = "This command is valid for groupchat only." case _ if message_lowercase.startswith("interval"): # FIXME # The following error occurs only upon first attempt to set interval. @@ -746,7 +863,7 @@ class Slixfeed(slixmpp.ClientXMPP): sqlitehandler.get_settings_value, key ) - val = await listhandler.set_list( + val = await listhandler.add_to_list( val, names ) @@ -955,6 +1072,9 @@ class Slixfeed(slixmpp.ClientXMPP): "Unknown command. " "Press \"help\" for list of commands" ) + # TODO Use message correction here + # NOTE This might not be a good idea if + # commands are sent one close to the next if action: msg.reply(action).send() @@ -995,11 +1115,12 @@ def print_info(): " Dimitris Tzemos (SalixOS, Greece)," "\n" " Emmanuel Gil Peyrot (poezio, France)," + " Florent Le Coz (poezio, France)," + "\n" " George Vlahavas (SalixOS, Greece)," - "\n" " Pierrick Le Brun (SalixOS, France)," - " Thorsten Mühlfelder (SalixOS, Germany)," "\n" + " Thorsten Mühlfelder (SalixOS, Germany)," " Yann Leboulanger (Gajim, France).\n" "\n" "COPYRIGHT\n" @@ -1088,10 +1209,14 @@ def print_help(): " Set new owner.\n" "\n" "FILTER OPTIONS\n" - " allow\n" - " Keywords to allow (comma separates).\n" - " deny\n" + " allow +\n" + " Add keywords to allow (comma separates).\n" + " allow -\n" + " Delete keywords from allow list (comma separates).\n" + " deny +\n" " Keywords to block (comma separates).\n" + " deny -\n" + " Delete keywords from deny list (comma separates).\n" # " filter clear allow\n" # " Reset allow list.\n" # " filter clear deny\n"