From 5a2a2f9e3f65b781e76c857f457ba243f8d26006 Mon Sep 17 00:00:00 2001 From: "Schimon Jehudah, Adv." Date: Sun, 2 Jun 2024 11:23:26 +0300 Subject: [PATCH] Fix automatic update; Fix command search. --- slixfeed/__main__.py | 16 +- slixfeed/action.py | 46 +- slixfeed/sqlite.py | 2 +- slixfeed/task.py | 8 +- slixfeed/version.py | 4 +- slixfeed/xmpp/chat.py | 74 ++- slixfeed/xmpp/client.py | 127 ++-- slixfeed/xmpp/component.py | 1191 ++++++++++++++++++++++++------------ 8 files changed, 920 insertions(+), 548 deletions(-) diff --git a/slixfeed/__main__.py b/slixfeed/__main__.py index 5d57986..f204794 100644 --- a/slixfeed/__main__.py +++ b/slixfeed/__main__.py @@ -178,22 +178,28 @@ def main(): # Try configuration file if 'client' in account_xmpp: - from slixfeed.xmpp.client import Slixfeed + from slixfeed.xmpp.client import XmppClient jid = account_xmpp['client']['jid'] password = account_xmpp['client']['password'] alias = account_xmpp['client']['alias'] if 'alias' in account_xmpp['client'] else None hostname = account_xmpp['client']['hostname'] if 'hostname' in account_xmpp['client'] else None port = account_xmpp['client']['port'] if 'port' in account_xmpp['client'] else None - Slixfeed(jid, password, hostname, port, alias) + XmppClient(jid, password, hostname, port, alias) + # xmpp_client = Slixfeed(jid, password, hostname, port, alias) + # xmpp_client.connect((hostname, port)) if hostname and port else xmpp_client.connect() + # xmpp_client.process() if 'component' in account_xmpp: - from slixfeed.xmpp.component import SlixfeedComponent + from slixfeed.xmpp.component import XmppComponent jid = account_xmpp['component']['jid'] - password = account_xmpp['component']['password'] + secret = account_xmpp['component']['password'] alias = account_xmpp['component']['alias'] if 'alias' in account_xmpp['component'] else None hostname = account_xmpp['component']['hostname'] if 'hostname' in account_xmpp['component'] else None port = account_xmpp['component']['port'] if 'port' in account_xmpp['component'] else None - SlixfeedComponent(jid, password, hostname, port, alias).process() + XmppComponent(jid, secret, hostname, port, alias) + # xmpp_component = SlixfeedComponent(jid, secret, hostname, port, alias) + # xmpp_component.connect() + # xmpp_component.process() sys.exit(0) diff --git a/slixfeed/action.py b/slixfeed/action.py index e549650..f6ddaf7 100644 --- a/slixfeed/action.py +++ b/slixfeed/action.py @@ -71,9 +71,9 @@ except: logger = Logger(__name__) -def export_feeds(self, jid, jid_file, ext): +def export_feeds(self, jid_bare, ext): function_name = sys._getframe().f_code.co_name - logger.debug('{}: jid: {}: jid_file: {}: ext: {}'.format(function_name, jid, jid_file, ext)) + logger.debug('{}: jid_bare: {}: ext: {}'.format(function_name, jid_bare, ext)) cache_dir = config.get_default_cache_directory() if not os.path.isdir(cache_dir): os.mkdir(cache_dir) @@ -81,15 +81,15 @@ def export_feeds(self, jid, jid_file, ext): os.mkdir(cache_dir + '/' + ext) filename = os.path.join( cache_dir, ext, 'slixfeed_' + dt.timestamp() + '.' + ext) - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) results = sqlite.get_feeds(db_file) match ext: # case 'html': # response = 'Not yet implemented.' case 'md': - export_to_markdown(jid, filename, results) + export_to_markdown(jid_bare, filename, results) case 'opml': - export_to_opml(jid, filename, results) + export_to_opml(jid_bare, filename, results) # case 'xbel': # response = 'Not yet implemented.' return filename @@ -144,7 +144,7 @@ if (await get_chat_type(self, jid_bare) == 'chat' and """ -async def xmpp_send_status_message(self, jid): +async def xmpp_send_status_message(self, jid_bare): """ Send status message. @@ -154,13 +154,12 @@ async def xmpp_send_status_message(self, jid): Jabber ID. """ function_name = sys._getframe().f_code.co_name - logger.debug('{}: jid: {}'.format(function_name, jid)) + logger.debug('{}: jid: {}'.format(function_name, jid_bare)) status_text = '📜️ Slixfeed RSS News Bot' - jid_file = jid.replace('/', '_') - db_file = config.get_pathname_to_database(jid_file) - enabled = Config.get_setting_value(self.settings, jid, 'enabled') + db_file = config.get_pathname_to_database(jid_bare) + enabled = Config.get_setting_value(self.settings, jid_bare, 'enabled') if enabled: - jid_task = self.pending_tasks[jid] + jid_task = self.pending_tasks[jid_bare] if len(jid_task): status_mode = 'dnd' status_text = jid_task[list(jid_task.keys())[0]] @@ -189,7 +188,7 @@ async def xmpp_send_status_message(self, jid): status_text = '📪️ Send "Start" to receive updates' # breakpoint() # print(await current_time(), status_text, "for", jid) - XmppPresence.send(self, jid, status_text, status_type=status_mode) + XmppPresence.send(self, jid_bare, status_text, status_type=status_mode) # await asyncio.sleep(60 * 20) # await refresh_task(self, jid, send_status, 'status', '90') # loop.call_at( @@ -250,7 +249,7 @@ async def xmpp_pubsub_send_unread_items(self, jid_bare): """ function_name = sys._getframe().f_code.co_name logger.debug('{}: jid_bare: {}'.format(function_name, jid_bare)) - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) report = {} subscriptions = sqlite.get_active_feeds_url(db_file) for url in subscriptions: @@ -491,7 +490,7 @@ def create_rfc4287_entry(feed_entry): return node_entry -async def xmpp_chat_send_unread_items(self, jid, num=None): +async def xmpp_chat_send_unread_items(self, jid_bare, num=None): """ Send news items as messages. @@ -503,18 +502,17 @@ async def xmpp_chat_send_unread_items(self, jid, num=None): Number. The default is None. """ function_name = sys._getframe().f_code.co_name - logger.debug('{}: jid: {} num: {}'.format(function_name, jid, num)) - jid_file = jid.replace('/', '_') - db_file = config.get_pathname_to_database(jid_file) - show_media = Config.get_setting_value(self.settings, jid, 'media') + logger.debug('{}: jid: {} num: {}'.format(function_name, jid_bare, num)) + db_file = config.get_pathname_to_database(jid_bare) + show_media = Config.get_setting_value(self.settings, jid_bare, 'media') if not num: - num = Config.get_setting_value(self.settings, jid, 'quantum') + num = Config.get_setting_value(self.settings, jid_bare, 'quantum') else: num = int(num) results = sqlite.get_unread_entries(db_file, num) news_digest = '' media = None - chat_type = await get_chat_type(self, jid) + chat_type = await get_chat_type(self, jid_bare) for result in results: ix = result[0] title_e = result[1] @@ -526,7 +524,7 @@ async def xmpp_chat_send_unread_items(self, jid, num=None): if enclosure: enclosure = enclosure[0] title_f = sqlite.get_feed_title(db_file, feed_id) title_f = title_f[0] - news_digest += await list_unread_entries(self, result, title_f, jid) + news_digest += await list_unread_entries(self, result, title_f, jid_bare) # print(db_file) # print(result[0]) # breakpoint() @@ -546,14 +544,14 @@ async def xmpp_chat_send_unread_items(self, jid, num=None): if media and news_digest: # Send textual message - XmppMessage.send(self, jid, news_digest, chat_type) + XmppMessage.send(self, jid_bare, news_digest, chat_type) news_digest = '' # Send media - XmppMessage.send_oob(self, jid, media, chat_type) + XmppMessage.send_oob(self, jid_bare, media, chat_type) media = None if news_digest: - XmppMessage.send(self, jid, news_digest, chat_type) + XmppMessage.send(self, jid_bare, news_digest, chat_type) # TODO Add while loop to assure delivery. # print(await current_time(), ">>> ACT send_message",jid) # NOTE Do we need "if statement"? See NOTE at is_muc. diff --git a/slixfeed/sqlite.py b/slixfeed/sqlite.py index a95c55c..813da36 100644 --- a/slixfeed/sqlite.py +++ b/slixfeed/sqlite.py @@ -2989,7 +2989,7 @@ async def search_entries(db_file, query): LIMIT 50 """ ) - par = (f'%{query}%', f'%{query}%') + par = [f'%{query}%'] result = cur.execute(sql, par).fetchall() return result diff --git a/slixfeed/task.py b/slixfeed/task.py index 76d0193..9f50eaa 100644 --- a/slixfeed/task.py +++ b/slixfeed/task.py @@ -165,7 +165,7 @@ async def start_tasks_xmpp_pubsub(self, jid_bare, tasks=None): async def task_publish(self, jid_bare): - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) while True: @@ -232,7 +232,7 @@ async def task_status_message(self, jid): async def task_message(self, jid_bare): - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) update_interval = Config.get_setting_value(self.settings, jid_bare, 'interval') @@ -291,7 +291,7 @@ def refresh_task(self, jid_bare, callback, key, val=None): """ logging.info('Refreshing task {} for JID {}'.format(callback, jid_bare)) if not val: - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) val = Config.get_setting_value(self.settings, jid_bare, key) @@ -342,7 +342,7 @@ async def check_updates(self, jid_bare): # print('Scanning for updates for JID {}'.format(jid_bare)) logging.info('Scanning for updates for JID {}'.format(jid_bare)) while True: - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) urls = sqlite.get_active_feeds_url(db_file) for url in urls: url = url[0] diff --git a/slixfeed/version.py b/slixfeed/version.py index 5beb3ac..80840ab 100644 --- a/slixfeed/version.py +++ b/slixfeed/version.py @@ -1,2 +1,2 @@ -__version__ = '0.1.71' -__version_info__ = (0, 1, 71) +__version__ = '0.1.72' +__version_info__ = (0, 1, 72) diff --git a/slixfeed/xmpp/chat.py b/slixfeed/xmpp/chat.py index c81da51..e049df6 100644 --- a/slixfeed/xmpp/chat.py +++ b/slixfeed/xmpp/chat.py @@ -79,7 +79,6 @@ class Chat: """ if message['type'] in ('chat', 'groupchat', 'normal'): jid_bare = message['from'].bare - jid_file = jid_bare message_text = ' '.join(message['body'].split()) command_time_start = time.time() @@ -114,7 +113,7 @@ class Chat: # self.pending_tasks[jid_bare][self.pending_tasks_counter] = status_message XmppPresence.send(self, jid_bare, status_message, status_type=status_type) - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) result = await fetch.http(url) count = await action.import_opml(db_file, result) if count: @@ -324,7 +323,7 @@ class Chat: if url.startswith('http'): if not title: title = uri.get_hostname(url) - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) counter = 0 hostname = uri.get_hostname(url) hostname = hostname.replace('.','-') @@ -407,7 +406,7 @@ class Chat: key = message_text[:5].lower() val = message_text[7:] if val: - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) keywords = sqlite.get_filter_value(db_file, key) if keywords: keywords = str(keywords[0]) val = await config.add_to_list(val, keywords) @@ -428,7 +427,7 @@ class Chat: key = message_text[:5].lower() val = message_text[7:] if val: - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) keywords = sqlite.get_filter_value(db_file, key) if keywords: keywords = str(keywords[0]) val = await config.remove_from_list(val, keywords) @@ -456,7 +455,7 @@ class Chat: else: val_old = Config.get_setting_value( self.settings, jid_bare, key) - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) await Config.set_setting_value( self.settings, jid_bare, db_file, key, val_new) response = ('Maximum archived items has ' @@ -494,20 +493,20 @@ class Chat: case _ if message_lowercase.startswith('default '): key = message_text[8:] self.settings[jid_bare][key] = None - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) await sqlite.delete_setting(db_file, key) response = ('Setting {} has been restored to default value.' .format(key)) XmppMessage.send_reply(self, message, response) case 'defaults': del self.settings[jid_bare] - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) await sqlite.delete_settings(db_file) response = 'Default settings have been restored.' XmppMessage.send_reply(self, message, response) case _ if message_lowercase.startswith('clear '): key = message_text[6:] - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) await sqlite.delete_filter(db_file, key) response = 'Filter {} has been purged.'.format(key) XmppMessage.send_reply(self, message, response) @@ -523,7 +522,7 @@ class Chat: key = message_text[:4].lower() val = message_text[6:] if val: - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) keywords = sqlite.get_filter_value(db_file, key) if keywords: keywords = str(keywords[0]) val = await config.add_to_list(val, keywords) @@ -544,7 +543,7 @@ class Chat: key = message_text[:4].lower() val = message_text[6:] if val: - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) keywords = sqlite.get_filter_value(db_file, key) if keywords: keywords = str(keywords[0]) val = await config.remove_from_list(val, keywords) @@ -575,7 +574,7 @@ class Chat: # self.pending_tasks[jid_bare][self.pending_tasks_counter] = status_message XmppPresence.send(self, jid_bare, status_message, status_type=status_type) - filename = action.export_feeds(self, jid_bare, jid_file, ext) + filename = action.export_feeds(self, jid_bare, ext) url = await XmppUpload.start(self, jid_bare, filename) # response = ( # 'Feeds exported successfully to {}.\n{}' @@ -609,7 +608,7 @@ class Chat: # self.pending_tasks[jid_bare][self.pending_tasks_counter] = status_message XmppPresence.send(self, jid_bare, status_message, status_type=status_type) - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) result = await fetch.http(url) count = await action.import_opml(db_file, result) if count: @@ -778,7 +777,7 @@ class Chat: if url.startswith('feed:/') or url.startswith('rss:/'): url = uri.feed_to_http(url) url = (await uri.replace_hostname(url, 'feed')) or url - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) counter = 0 hostname = uri.get_hostname(url) hostname = hostname.replace('.','-') @@ -835,13 +834,13 @@ class Chat: query = message_text[6:] if query: if len(query) > 3: - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) result = sqlite.search_feeds(db_file, query) response = action.list_feeds_by_query(query, result) else: response = 'Enter at least 4 characters to search' else: - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) result = sqlite.get_feeds(db_file) response = action.list_feeds(result) XmppMessage.send_reply(self, message, response) @@ -859,7 +858,7 @@ class Chat: try: val_new = int(val) val_old = Config.get_setting_value(self.settings, jid_bare, key) - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) await Config.set_setting_value( self.settings, jid_bare, db_file, key, val_new) # NOTE Perhaps this should be replaced by functions @@ -901,7 +900,7 @@ class Chat: val_new = int(val) val_old = Config.get_setting_value( self.settings, jid_bare, key) - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) await Config.set_setting_value( self.settings, jid_bare, db_file, key, val_new) if val_new == 0: # if not val: @@ -945,7 +944,7 @@ class Chat: # response = 'Missing value.' XmppMessage.send_reply(self, message, response) case 'media off': - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) key = 'media' val = 0 await Config.set_setting_value( @@ -953,7 +952,7 @@ class Chat: response = 'Media is disabled.' XmppMessage.send_reply(self, message, response) case 'media on': - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) key = 'media' val = 1 await Config.set_setting_value(self.settings, jid_bare, @@ -961,7 +960,7 @@ class Chat: response = 'Media is enabled.' XmppMessage.send_reply(self, message, response) case 'new': - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) key = 'old' val = 0 await Config.set_setting_value(self.settings, jid_bare, @@ -1022,7 +1021,7 @@ class Chat: key_list = ['status'] await task.start_tasks_xmpp_chat(self, jid_bare, key_list) case 'old': - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) key = 'old' val = 1 await Config.set_setting_value(self.settings, jid_bare, @@ -1053,7 +1052,7 @@ class Chat: # response = ( # 'Every update will contain {} news items.' # ).format(response) - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) await Config.set_setting_value(self.settings, jid_bare, db_file, key, val_new) response = ('Next update will contain {} news items ' @@ -1183,7 +1182,7 @@ class Chat: if num < 1 or num > 50: response = 'Value must be ranged from 1 to 50.' else: - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) result = sqlite.get_last_entries(db_file, num) response = action.list_last_entries(result, num) except: @@ -1201,7 +1200,7 @@ class Chat: if ix_url: for i in ix_url: if i: - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) try: ix = int(i) url = sqlite.get_feed_url(db_file, ix) @@ -1256,9 +1255,9 @@ class Chat: # self.pending_tasks[jid_bare][self.pending_tasks_counter] = status_message XmppPresence.send(self, jid_bare, status_message, status_type=status_type) - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if ix_url: - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) try: ix = int(ix_url) url = sqlite.get_feed_url(db_file, ix) @@ -1303,7 +1302,7 @@ class Chat: query = message_text[7:] if query: if len(query) > 1: - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) results = await sqlite.search_entries(db_file, query) response = action.list_search_results(query, results) else: @@ -1316,7 +1315,7 @@ class Chat: case 'start': key = 'enabled' val = 1 - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) await Config.set_setting_value(self.settings, jid_bare, db_file, key, val) status_type = 'available' @@ -1329,12 +1328,12 @@ class Chat: response = 'Updates are enabled.' XmppMessage.send_reply(self, message, response) case 'stats': - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) response = await action.list_statistics(db_file) XmppMessage.send_reply(self, message, response) case _ if message_lowercase.startswith('disable '): feed_id = message_text[8:] - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) try: await sqlite.set_enabled_status(db_file, feed_id, 0) await sqlite.mark_feed_as_read(db_file, feed_id) @@ -1358,7 +1357,7 @@ class Chat: if name: try: feed_id = int(feed_id) - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) name_old = sqlite.get_feed_title(db_file, feed_id) if name_old: name_old = name_old[0] @@ -1390,7 +1389,7 @@ class Chat: XmppMessage.send_reply(self, message, response) case _ if message_lowercase.startswith('enable '): feed_id = message_text[7:] - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) try: await sqlite.set_enabled_status(db_file, feed_id, 1) name = sqlite.get_feed_title(db_file, feed_id)[0] @@ -1406,7 +1405,7 @@ class Chat: case 'stop': key = 'enabled' val = 0 - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) await Config.set_setting_value( self.settings, jid_bare, db_file, key, val) key_list = ['interval', 'status'] @@ -1461,16 +1460,15 @@ class Chat: if not os.path.isdir(data_dir + '/logs/'): os.mkdir(data_dir + '/logs/') action.log_to_markdown( - dt.current_time(), os.path.join(data_dir, 'logs', jid_file), + dt.current_time(), os.path.join(data_dir, 'logs', jid_bare), jid_bare, message_text) action.log_to_markdown( - dt.current_time(), os.path.join(data_dir, 'logs', jid_file), + dt.current_time(), os.path.join(data_dir, 'logs', jid_bare), jid_bare, response) print( 'Message : {}\n' 'JID : {}\n' - 'File : {}\n' '{}\n' - .format(message_text, jid_bare, jid_file, response) + .format(message_text, jid_bare, response) ) diff --git a/slixfeed/xmpp/client.py b/slixfeed/xmpp/client.py index 67746be..7e0616e 100644 --- a/slixfeed/xmpp/client.py +++ b/slixfeed/xmpp/client.py @@ -108,7 +108,7 @@ loop = asyncio.get_event_loop() logger = Logger(__name__) -class Slixfeed(slixmpp.ClientXMPP): +class XmppClient(slixmpp.ClientXMPP): """ Slixfeed: News bot that sends updates from RSS feeds. @@ -250,10 +250,7 @@ class Slixfeed(slixmpp.ClientXMPP): self.on_session_end) # Connect to the XMPP server and start processing XMPP stanzas. - if hostname and port: - self.connect((hostname, port)) - else: - self.connect() + self.connect((hostname, port)) if hostname and port else self.connect() self.process() @@ -414,8 +411,7 @@ class Slixfeed(slixmpp.ClientXMPP): message_log = '{}: jid_full: {}' logger.debug(message_log.format(function_name, jid_full)) jid_bare = message['from'].bare - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) if jid_bare == self.boundjid.bare: @@ -926,7 +922,7 @@ class Slixfeed(slixmpp.ClientXMPP): ftype='list-single', label='Jabber ID', value=jid_bare, - var='jid_file') + var='jid_bare') jids = [] contacts = await XmppRoster.get_contacts(self) for contact in contacts: @@ -1030,7 +1026,7 @@ class Slixfeed(slixmpp.ClientXMPP): session['prev'] = None session['payload'] = None return session - jid_file = values['jid_file'] + jid_bare = values['jid_bare'] node = values['node'] # xep = values['xep'] if not node: @@ -1046,11 +1042,11 @@ class Slixfeed(slixmpp.ClientXMPP): form.add_field(var='jid', ftype='hidden', value=jid) - form.add_field(var='jid_file', + form.add_field(var='jid_bare', ftype='hidden', - value=jid_file) + value=jid_bare) num = 100 - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) results = sqlite.get_entries(db_file, num) subtitle = 'Recent {} updates'.format(num) if results: @@ -1081,9 +1077,9 @@ class Slixfeed(slixmpp.ClientXMPP): async def _handle_publish_db_complete(self, payload, session): values = payload['values'] - jid_file = values['jid_file'][0] - print('jid_file') - print(jid_file) + jid_bare = values['jid_bare'][0] + print('jid_bare') + print(jid_bare) print("values['node']") print(values['node']) node_id = values['node'][0] @@ -1105,7 +1101,7 @@ class Slixfeed(slixmpp.ClientXMPP): # xep = None for ix in ixs: - await action.xmpp_pubsub_send_selected_entry(self, jid, jid_file, node_id, ix) + await action.xmpp_pubsub_send_selected_entry(self, jid, jid_bare, node_id, ix) text_info = 'Posted {} entries.'.format(len(ixs)) session['allow_prev'] = False session['has_next'] = False @@ -1317,8 +1313,7 @@ class Slixfeed(slixmpp.ClientXMPP): logger.debug('{}: jid_full: {}' .format(function_name, jid_full)) jid_bare = session['from'].bare - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) form = self['xep_0004'].make_form('form', 'Profile') @@ -1423,8 +1418,7 @@ class Slixfeed(slixmpp.ClientXMPP): chat_type = await get_chat_type(self, jid_bare) if is_access(self, jid_bare, jid_full, chat_type): jid = session['from'].bare - jid_file = jid - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) form = self['xep_0004'].make_form('form', 'Filters') form['instructions'] = ('Filters allow you to skip news items ' 'that you may not be interested at. Use ' @@ -1499,8 +1493,7 @@ class Slixfeed(slixmpp.ClientXMPP): jid_bare = session['from'].bare # form = self['xep_0004'].make_form('result', 'Done') # form['instructions'] = ('✅️ Filters have been updated') - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) # In this case (as is typical), the payload is a form values = payload['values'] for key in values: @@ -1648,14 +1641,11 @@ class Slixfeed(slixmpp.ClientXMPP): values = payload['values'] form = self['xep_0004'].make_form('form', 'Updates') if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'] - jid_file = jid + jid_bare = values['jid'] form.add_field(var='jid', ftype='hidden', - value=jid) - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + value=jid_bare) + db_file = config.get_pathname_to_database(jid_bare) num = 100 match values['action']: case 'all': @@ -1710,13 +1700,11 @@ class Slixfeed(slixmpp.ClientXMPP): form = self['xep_0004'].make_form('form', 'Article') if is_operator(self, jid_bare) and 'jid' in values: jid = values['jid'] - jid_file = jid[0] if isinstance(jid, list) else jid + jid_bare = jid[0] if isinstance(jid, list) else jid form.add_field(var='jid', ftype='hidden', value=jid) - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) title = sqlite.get_entry_title(db_file, ix) title = title[0] if title else 'Untitled' form['instructions'] = title @@ -1786,13 +1774,11 @@ class Slixfeed(slixmpp.ClientXMPP): jid_bare = session['from'].bare if is_operator(self, jid_bare) and 'jid' in values: jid = values['jid'] - jid_file = jid[0] if isinstance(jid, list) else jid + jid_bare = jid[0] if isinstance(jid, list) else jid form.add_field(var='jid', ftype='hidden', value=jid) - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if identifier and sqlite.check_identifier_exist(db_file, identifier): form['title'] = 'Conflict' form['instructions'] = ('Name "{}" already exists. Choose a ' @@ -1990,12 +1976,9 @@ class Slixfeed(slixmpp.ClientXMPP): jid_bare = session['from'].bare values = payload['values'] if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'][0] - jid_file = jid + jid_bare = values['jid'][0] del values['jid'] - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) for key in values: value = 1 if values[key] else 0 await sqlite.set_enabled_status(db_file, key, value) @@ -2018,12 +2001,9 @@ class Slixfeed(slixmpp.ClientXMPP): jid_bare = session['from'].bare values = payload['values'] if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'][0] - jid_file = jid + jid_bare = values['jid'][0] del values['jid'] - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) subscriptions ='' ixs = values['subscriptions'] for ix in ixs: @@ -2252,14 +2232,11 @@ class Slixfeed(slixmpp.ClientXMPP): jid_bare = session['from'].bare form = self['xep_0004'].make_form('form', 'Subscriptions') if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'] - jid_file = jid + jid_bare = values['jid'] form.add_field(ftype='hidden', - value=jid, + value=jid_bare, var='jid') - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) match values['action']: case 'browse': form['instructions'] = 'Editing subscriptions' @@ -2349,14 +2326,11 @@ class Slixfeed(slixmpp.ClientXMPP): jid_bare = session['from'].bare values = payload['values'] if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'][0] - jid_file = jid + jid_bare = values['jid'][0] form.add_field(ftype='hidden', - value=jid, + value=jid_bare, var='jid') - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) tag_id = values['tag'] tag_name = sqlite.get_tag_name(db_file, tag_id)[0] form['instructions'] = 'Subscriptions tagged with "{}"'.format(tag_name) @@ -2390,14 +2364,11 @@ class Slixfeed(slixmpp.ClientXMPP): jid_bare = session['from'].bare values = payload['values'] if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'][0] if values['jid'] else jid_bare - jid_file = jid + jid_bare = values['jid'][0] if values['jid'] else jid_bare form.add_field(ftype='hidden', - value=jid, + value=jid_bare, var='jid') - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if 'subscription' in values: urls = values['subscription'] elif 'subscriptions' in values: urls = values['subscriptions'] url_count = len(urls) @@ -2489,11 +2460,8 @@ class Slixfeed(slixmpp.ClientXMPP): jid_bare = session['from'].bare values = payload['values'] if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'][0] - jid_file = jid - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + jid_bare = values['jid'][0] + db_file = config.get_pathname_to_database(jid_bare) # url = values['url'] # feed_id = sqlite.get_feed_id(db_file, url) # feed_id = feed_id[0] @@ -2894,10 +2862,8 @@ class Slixfeed(slixmpp.ClientXMPP): jid_bare = session['from'].bare if is_operator(self, jid_bare) and 'jid' in values: jid = values['jid'] - jid_file = jid[0] if isinstance(jid, list) else jid - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + jid_bare = jid[0] if isinstance(jid, list) else jid + db_file = config.get_pathname_to_database(jid_bare) result = await fetch.http(url) count = await action.import_opml(db_file, result) try: @@ -2937,14 +2903,12 @@ class Slixfeed(slixmpp.ClientXMPP): jid_bare = session['from'].bare if is_operator(self, jid_bare) and 'jid' in values: jid = values['jid'] - jid_file = jid[0] if isinstance(jid, list) else jid - else: - jid_file = jid_bare + jid_bare = jid[0] if isinstance(jid, list) else jid # form = self['xep_0004'].make_form('result', 'Done') # form['instructions'] = ('✅️ Feeds have been exported') exts = values['filetype'] for ext in exts: - filename = action.export_feeds(self, jid_file, jid_file, ext) + filename = action.export_feeds(self, jid_bare, ext) url = await XmppUpload.start(self, jid_bare, filename) chat_type = await get_chat_type(self, jid_bare) XmppMessage.send_oob(self, jid_bare, url, chat_type) @@ -3411,8 +3375,7 @@ class Slixfeed(slixmpp.ClientXMPP): if key: jid_bare = key value = values[key] - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) await Config.set_setting_value(self.settings, jid_bare, @@ -3674,8 +3637,7 @@ class Slixfeed(slixmpp.ClientXMPP): jid_bare = session['from'].bare chat_type = await get_chat_type(self, jid_bare) if is_access(self, jid_bare, jid_full, chat_type): - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) form = self['xep_0004'].make_form('form', 'Settings') @@ -3792,8 +3754,7 @@ class Slixfeed(slixmpp.ClientXMPP): logger.debug('{}: jid_full: {}' .format(function_name, jid_full)) jid_bare = session['from'].bare - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) # In this case (as is typical), the payload is a form diff --git a/slixfeed/xmpp/component.py b/slixfeed/xmpp/component.py index 23111d5..11f981b 100644 --- a/slixfeed/xmpp/component.py +++ b/slixfeed/xmpp/component.py @@ -98,7 +98,7 @@ loop = asyncio.get_event_loop() logger = Logger(__name__) -class SlixfeedComponent(slixmpp.ComponentXMPP): +class XmppComponent(slixmpp.ComponentXMPP): """ Slixfeed: News bot that sends updates from RSS feeds. @@ -279,8 +279,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jids = await XmppPubsub.get_pubsub_services(self) for jid_bare in jids: if jid_bare not in self.settings: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) Config.add_settings_jid(self.settings, jid_bare, db_file) await task.start_tasks_xmpp_pubsub(self, jid_bare) # XmppCommand.adhoc_commands(self) @@ -330,8 +329,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): message_log = '{}: jid_full: {}' logger.debug(message_log.format(function_name, jid_full)) jid_bare = message['from'].bare - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) if jid_bare == self.boundjid.bare: @@ -674,7 +672,54 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): """ form = self['xep_0004'].make_form('form', 'Reactions Information') - +############################################################ +# # +# INSTRUCTIONS # +# # +############################################################ +# +# 1) Best practice: One form per functionality. +# 2) Use match/case only when it is intended to provide +# several actions of functionality of similar context. +# This will usually occur from first form which receives +# the parameters (self, iq, session) to second form which +# is yielded from a function with match/case. +# 3) Do not use same function for two different actions +# (i.e. match/case), because it makes context and future +# management difficult. +# +# TODO +# +# Add intermediate form to separate several forms from a function.. +# The intermediate form will have technical explanation about the next form. +# +# e.g when list-multi is engaged +# options.addOption('Bookmarks', 'bookmarks') +# options.addOption('Contacts', 'roster') +# options.addOption('Nodes', 'nodes') +# options.addOption('PubSub', 'pubsub') +# options.addOption('Subscribers', 'subscribers') +# +# NOTE +# +# 1) Utilize code session['notes'] to keep last form: +# +# text_note = 'Done.' +# session['next'] = None +# session['notes'] = [['info', text_note]] +# session['payload'] = None +# +# see _handle_subscription_toggle +# +# instead of +# +# form = payload +# form['title'] = 'Done' +# form['instructions'] = None +# session['payload'] = form +# +# 2) Set session['next'] = None to make form to disappear (Cheogram and monocles chat) +# def adhoc_commands(self): function_name = sys._getframe().f_code.co_name logger.debug('{}'.format(function_name)) @@ -735,152 +780,258 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): logger.debug('{}: jid_full: {}' .format(function_name, jid_full)) jid_bare = session['from'].bare - if is_operator(self, jid_bare): + chat_type = await get_chat_type(self, jid_bare) + if is_access(self, jid_bare, jid_full, chat_type): form = self['xep_0004'].make_form('form', 'PubSub') - form['instructions'] = 'Manage PubSub nodes and publish news items.' - options = form.add_field(desc='Send a set of selected posts or ' - 'set a new subscription.', + form['instructions'] = 'Publish news items to PubSub nodes.' + options = form.add_field(desc='From which medium source do you ' + 'want to select data to publish?', ftype='list-single', - label='Choose', + label='Source', required=True, var='option') - # options.addOption('Manage nodes', 'nodes') - # options.addOption('Manage subscriptions', 'manage') - options.addOption('Post a selection', 'post') - options.addOption('Publish a subscription', 'publish') + options.addOption('Database', 'database') + options.addOption('URL', 'url') + form.add_field(ftype='fixed', + label='* Attention', + desc='Results are viewed best with Movim and ' + 'Libervia.') session['allow_prev'] = False session['has_next'] = True - session['next'] = self._handle_publish_select + session['next'] = self._handle_publish_action session['prev'] = None session['payload'] = form else: - text_warn = 'This resource is restricted to operators.' + if not is_operator(self, jid_bare): + text_warn = 'This resource is restricted to operators.' + elif chat_type == 'groupchat': + text_warn = ('This resource is restricted to moderators of {}.' + .format(jid_bare)) + elif chat_type == 'error': + text_warn = ('Could not determine chat type of {}.' + .format(jid_bare)) + else: + text_warn = 'This resource is forbidden.' session['notes'] = [['warn', text_warn]] return session - async def _handle_publish_select(self, payload, session): + async def _handle_publish_action(self, payload, session): + jid_full = str(session['from']) + function_name = sys._getframe().f_code.co_name + logger.debug('{}: jid_full: {}' + .format(function_name, jid_full)) + jid_bare = session['from'].bare + chat_type = await get_chat_type(self, jid_bare) + if is_access(self, jid_bare, jid_full, chat_type): + values = payload['values'] + form = self['xep_0004'].make_form('form', 'Publish') + form['instructions'] = ('Choose a PubSub Jabber ID and verify ' + 'that Slixfeed has the necessary ' + 'permissions to publish into it.') + match values['option']: + case 'database': + session['next'] = self._handle_publish_db_preview + options = form.add_field(desc='Select a Jabber ID of ' + 'which data you want to ' + 'browse (The default Jabber ' + 'ID is {}).' + .format(jid_bare), + ftype='list-single', + label='Jabber ID', + value=jid_bare, + var='jid_bare') + jids = [] + contacts = await XmppRoster.get_contacts(self) + for contact in contacts: + jids.extend([contact]) + conferences = await XmppBookmark.get_bookmarks(self) + for conference in conferences: + jids.extend([conference['jid']]) + pubsubs = await XmppPubsub.get_pubsub_services(self) + for pubsub in pubsubs: + jids.extend([pubsub['jid']]) + for jid_bare in sorted(jids): + options.addOption(jid_bare, jid_bare) + case 'url': + session['next'] = self._handle_publish_url_preview + # TODO Make it possible to add several subscriptions at once; + # Similarly to BitTorrent trackers list + # ftype='text-multi', + # label='Subscription URLs', + # desc='Add subscriptions one time per ' + # 'subscription.', + form.add_field(desc='Enter a URL.', + ftype='text-single', + label='Subscription', + required=True, + value='http://', + var='url') + options = form.add_field(desc='Select a PubSub Service.', + ftype='list-single', + label='PubSub', + required=True, + value=self.boundjid.bare, + var='jid') + options.addOption(self.boundjid.bare, self.boundjid.bare) + iq = await self['xep_0030'].get_items(jid=self.boundjid.domain) + items = iq['disco_items']['items'] + for item in items: + iq = await self['xep_0030'].get_info(jid=item[0]) + identities = iq['disco_info']['identities'] + for identity in identities: + if identity[0] == 'pubsub' and identity[1] == 'service': + jid = item[0] + if item[1]: name = item[1] + elif item[2]: name = item[2] + else: name = jid + options.addOption(jid, name) + # form.add_field(desc='Enter a PubSub Jabber ID.', + # ftype='text-single', + # label='PubSub', + # required=True, + # # value='pubsub.' + self.boundjid.host, + # value=self.boundjid.bare, + # var='jid') + form.add_field(desc='Enter a node to publish to.', + ftype='text-single', + label='Node', + var='node') + # options = form.add_field(desc='Select XMPP Extension Protocol.', + # ftype='list-single', + # label='Protocol', + # required=True, + # value='0060', + # var='xep') + # options.addOption('XEP-0060: Publish-Subscribe', '0060') + # options.addOption('XEP-0277: Microblogging over XMPP', '0277') + # options.addOption('XEP-0472: Pubsub Social Feed', '0472') + session['payload'] = form + session['allow_prev'] = True + session['has_next'] = True + session['prev'] = self._handle_publish + else: + if not is_operator(self, jid_bare): + text_warn = 'This resource is restricted to operators.' + elif chat_type == 'groupchat': + text_warn = ('This resource is restricted to moderators of {}.' + .format(jid_bare)) + elif chat_type == 'error': + text_warn = ('Could not determine chat type of {}.' + .format(jid_bare)) + else: + text_warn = 'This resource is forbidden.' + session['notes'] = [['warn', text_warn]] + return session + + async def _handle_publish_db_preview(self, payload, session): jid_full = str(session['from']) function_name = sys._getframe().f_code.co_name logger.debug('{}: jid_full: {}' .format(function_name, jid_full)) values = payload['values'] - match values['option']: - case 'publish': - form = self['xep_0004'].make_form('form', 'Publish') - form['instructions'] = ('Choose a PubSub Jabber ID and verify ' - 'that Slixfeed has the necessary ' - 'permissions to publish into it.') - form.add_field(desc='Enter a subscription URL.', - # TODO Make it possible to add several subscriptions at once; - # Similarly to BitTorrent trackers list - # ftype='text-multi', - # label='Subscription URLs', - # desc='Add subscriptions one time per ' - # 'subscription.', - ftype='text-single', - label='URL', - required=True, - value='http://', - var='url') - options = form.add_field(desc='Select a PubSub Service.', - ftype='list-single', - label='PubSub', - required=True, - value=self.boundjid.bare, - var='jid') - options.addOption(self.boundjid.bare, self.boundjid.bare) - iq = await self['xep_0030'].get_items(jid=self.boundjid.domain) - items = iq['disco_items']['items'] - for item in items: - iq = await self['xep_0030'].get_info(jid=item[0]) - identities = iq['disco_info']['identities'] - for identity in identities: - if identity[0] == 'pubsub' and identity[1] == 'service': - jid = item[0] - if item[1]: name = item[1] - elif item[2]: name = item[2] - else: name = jid - options.addOption(jid, name) - # form.add_field(desc='Enter a PubSub Jabber ID.', - # ftype='text-single', - # label='PubSub', - # required=True, - # value=self.boundjid.bare, - # # value='pubsub.' + self.boundjid.host, - # var='jid') - form.add_field(desc='Enter a node to publish to.', - ftype='text-single', - label='Node', - var='node') - # options = form.add_field(desc='Select XMPP Extension Protocol.', - # ftype='list-single', - # label='Protocol', - # required=True, - # value='0060', - # var='xep') - # options.addOption('XEP-0060: Publish-Subscribe', '0060') - # options.addOption('XEP-0277: Microblogging over XMPP', '0277') - # options.addOption('XEP-0472: Pubsub Social Feed', '0472') - session['next'] = self._handle_preview - session['payload'] = form - case 'post': - form = self['xep_0004'].make_form('form', 'Post') - form['instructions'] = ('Choose a PubSub Jabber ID and verify ' - 'that Slixfeed has the necessary ' - 'permissions to publish into it.') - form.add_field(desc='Enter a subscription URL.', - ftype='text-single', - label='URL', - required=True, - value='http://', - var='url') - options = form.add_field(desc='Select a PubSub Service.', - ftype='list-single', - label='PubSub', - required=True, - value=self.boundjid.bare, - var='jid') - options.addOption(self.boundjid.bare, self.boundjid.bare) - iq = await self['xep_0030'].get_items(jid=self.boundjid.domain) - items = iq['disco_items']['items'] - for item in items: - iq = await self['xep_0030'].get_info(jid=item[0]) - identities = iq['disco_info']['identities'] - for identity in identities: - if identity[0] == 'pubsub' and identity[1] == 'service': - jid = item[0] - if item[1]: name = item[1] - elif item[2]: name = item[2] - else: name = jid - options.addOption(jid, name) - # form.add_field(desc='Enter a PubSub Jabber ID.', - # ftype='text-single', - # label='PubSub', - # required=True, - # # value='pubsub.' + self.boundjid.host, - # value=self.boundjid.bare, - # var='jid') - form.add_field(desc='Enter a node to publish to.', - ftype='text-single', - label='Node', - var='node') - # options = form.add_field(desc='Select XMPP Extension Protocol.', - # ftype='list-single', - # label='Protocol', - # required=True, - # value='0060', - # var='xep') - # options.addOption('XEP-0060: Publish-Subscribe', '0060') - # options.addOption('XEP-0277: Microblogging over XMPP', '0277') - # options.addOption('XEP-0472: Pubsub Social Feed', '0472') - session['next'] = self._handle_preview - session['payload'] = form + print('_handle_publish_db_preview') + print(values['jid']) + jid = values['jid'] if 'jid' in values else None + jid_bare = session['from'].bare + if jid != jid_bare and not is_operator(self, jid_bare): + text_warn = ('Posting to {} is restricted to operators only.' + .format(jid_bare)) # Should not this be self.boundjid.bare? + session['allow_prev'] = False + session['has_next'] = False + session['next'] = None + session['notes'] = [['warn', text_warn]] + session['prev'] = None + session['payload'] = None + return session + jid_bare = values['jid_bare'] + node = values['node'] + # xep = values['xep'] + if not node: + if jid == self.boundjid.bare: + node = 'urn:xmpp:microblog:0' + else: + node = 'slixfeed' + form = self['xep_0004'].make_form('form', 'Publish') + + form.add_field(var='node', + ftype='hidden', + value=node) + form.add_field(var='jid', + ftype='hidden', + value=jid) + form.add_field(var='jid_bare', + ftype='hidden', + value=jid_bare) + num = 100 + db_file = config.get_pathname_to_database(jid_bare) + results = sqlite.get_entries(db_file, num) + subtitle = 'Recent {} updates'.format(num) + if results: + form['instructions'] = subtitle + options = form.add_field(desc='Select news items to publish.', + ftype='list-multi', + label='News', + required=True, + var='entries') + for result in results: + title = result[1] # TODO Decide what title to display upon empty title + if not title: title = sqlite.get_feed_title(db_file, result[4]) + ix = str(result[0]) + options.addOption(title, ix) + session['has_next'] = True + session['next'] = self._handle_publish_db_complete + session['payload'] = form + else: + text_info = 'There are no news' + session['has_next'] = False + session['next'] = None + session['notes'] = [['info', text_info]] + session['payload'] = None session['allow_prev'] = True - session['has_next'] = True session['prev'] = self._handle_publish return session - - async def _handle_preview(self, payload, session): + + + async def _handle_publish_db_complete(self, payload, session): + values = payload['values'] + jid_bare = values['jid_bare'][0] + print('jid_bare') + print(jid_bare) + print("values['node']") + print(values['node']) + node_id = values['node'][0] + jid = values['jid'][0] + ixs = values['entries'] + #if jid: jid = jid[0] if isinstance(jid, list) else jid + jid_bare = session['from'].bare + if jid != jid_bare and not is_operator(self, jid_bare): + # TODO Report incident + text_warn = 'You are not suppose to be here.' + session['allow_prev'] = False + session['has_next'] = False + session['next'] = None + session['notes'] = [['warn', text_warn]] + session['prev'] = None + session['payload'] = None + return session + # xep = values['xep'][0] + # xep = None + + for ix in ixs: + await action.xmpp_pubsub_send_selected_entry(self, jid, jid_bare, node_id, ix) + text_info = 'Posted {} entries.'.format(len(ixs)) + session['allow_prev'] = False + session['has_next'] = False + session['next'] = None + session['notes'] = [['info', text_info]] + session['prev'] = None + session['payload'] = None + else: + session['payload'] = payload + return session + + + async def _handle_publish_url_preview(self, payload, session): jid_full = str(session['from']) function_name = sys._getframe().f_code.co_name logger.debug('{}: jid_full: {}' @@ -889,15 +1040,17 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jid = values['jid'] if 'jid' in values else None jid_bare = session['from'].bare if jid != jid_bare and not is_operator(self, jid_bare): - text_warn = ('Posting to {} is restricted to operators only.' - .format(jid_bare)) - session['allow_prev'] = False - session['has_next'] = False - session['next'] = None - session['notes'] = [['warn', text_warn]] - session['prev'] = None - session['payload'] = None - return session + # TODO Report incident + text_warn = 'You are not suppose to be here.' + # text_warn = ('Posting to {} is restricted to operators only.' + # .format(jid_bare)) # Should not this be self.boundjid.bare? + session['allow_prev'] = False + session['has_next'] = False + session['next'] = None + session['notes'] = [['warn', text_warn]] + session['prev'] = None + session['payload'] = None + return session node = values['node'] url = values['url'] # xep = values['xep'] @@ -945,7 +1098,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): break session['allow_prev'] = True session['has_next'] = True - session['next'] = self._handle_post_complete + session['next'] = self._handle_publish_url_complete session['notes'] = None session['prev'] = self._handle_publish session['payload'] = form @@ -969,7 +1122,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): options.addOption(title, url) session['allow_prev'] = True session['has_next'] = True - session['next'] = self._handle_preview + session['next'] = self._handle_publish_url_preview session['notes'] = None session['prev'] = self._handle_publish session['payload'] = form @@ -1001,7 +1154,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # value=xep) return session - async def _handle_post_complete(self, payload, session): + async def _handle_publish_url_complete(self, payload, session): values = payload['values'] entries = values['entries'] # It might not be good to pass feed object as its size might be too big @@ -1009,17 +1162,19 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # It is not possible to assign non-str to transfer. # feed = values['feed'] node = values['node'][0] - jid = values['jid'] if 'jid' in values else None + jid = values['jid'][0] if 'jid' in values else None + #if jid: jid = jid[0] if isinstance(jid, list) else jid jid_bare = session['from'].bare if jid != jid_bare and not is_operator(self, jid_bare): - text_warn = 'You are not suppose to be here.' - session['allow_prev'] = False - session['has_next'] = False - session['next'] = None - session['notes'] = [['warn', text_warn]] - session['prev'] = None - session['payload'] = None - return session + # TODO Report incident + text_warn = 'You are not suppose to be here.' + session['allow_prev'] = False + session['has_next'] = False + session['next'] = None + session['notes'] = [['warn', text_warn]] + session['prev'] = None + session['payload'] = None + return session url = values['url'][0] # xep = values['xep'][0] xep = None @@ -1075,8 +1230,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): logger.debug('{}: jid_full: {}' .format(function_name, jid_full)) jid_bare = session['from'].bare - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) form = self['xep_0004'].make_form('form', 'Profile') @@ -1084,7 +1238,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): .format(jid_bare)) form.add_field(ftype='fixed', label='News') - feeds_all = str(sqlite.get_number_of_items(db_file, 'feeds')) + feeds_all = str(sqlite.get_number_of_items(db_file, 'feeds_properties')) form.add_field(label='Subscriptions', ftype='text-single', value=feeds_all) @@ -1092,12 +1246,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): form.add_field(label='Active', ftype='text-single', value=feeds_act) - entries = sqlite.get_number_of_items(db_file, 'entries') - archive = sqlite.get_number_of_items(db_file, 'archive') - entries_all = str(entries + archive) + entries = sqlite.get_number_of_items(db_file, 'entries_properties') form.add_field(label='Items', ftype='text-single', - value=entries_all) + value=entries) unread = str(sqlite.get_number_of_entries_unread(db_file)) form.add_field(label='Unread', ftype='text-single', @@ -1181,15 +1333,17 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): .format(function_name, jid_full)) jid_bare = session['from'].bare chat_type = await get_chat_type(self, jid_bare) - moderator = None - if chat_type == 'groupchat': - moderator = is_moderator(self, jid_bare, jid_full) - if chat_type == 'chat' or moderator: + if is_access(self, jid_bare, jid_full, chat_type): jid = session['from'].bare - jid_file = jid - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) form = self['xep_0004'].make_form('form', 'Filters') - form['instructions'] = 'Editing filters' # 🪄️ 🛡️ + form['instructions'] = ('Filters allow you to skip news items ' + 'that you may not be interested at. Use ' + 'the "Exception list" to exceptionally ' + 'enforce items that contain keywords of ' + 'the "Deny list". Use the "Allow list" to ' + 'skip any item that does not include the ' + 'chosen keywords') value = sqlite.get_filter_value(db_file, 'allow') if value: value = str(value[0]) form.add_field(desc='Keywords to allow (comma-separated keywords).', @@ -1197,6 +1351,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): label='Allow list', value=value, var='allow') + form.add_field(ftype='fixed', + label='* Attention', + desc='The "Allow list" will skip any item that ' + 'does not include its keywords.') value = sqlite.get_filter_value(db_file, 'deny') if value: value = str(value[0]) form.add_field(desc='Keywords to deny (comma-separated keywords).', @@ -1204,13 +1362,26 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): label='Deny list', value=value, var='deny') + form.add_field(desc='Keywords to enforce (comma-separated keywords).', + ftype='text-single', + label='Exception list', + value=value, + var='exception') session['allow_complete'] = True session['has_next'] = False session['next'] = self._handle_filters_complete session['payload'] = form else: - text_warn = ('This resource is restricted to moderators of {}.' - .format(jid)) + if not is_operator(self, jid_bare): + text_warn = 'This resource is restricted to operators.' + elif chat_type == 'groupchat': + text_warn = ('This resource is restricted to moderators of {}.' + .format(jid_bare)) + elif chat_type == 'error': + text_warn = ('Could not determine chat type of {}.' + .format(jid_bare)) + else: + text_warn = 'This resource is forbidden.' session['notes'] = [['warn', text_warn]] return session @@ -1239,8 +1410,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jid_bare = session['from'].bare # form = self['xep_0004'].make_form('result', 'Done') # form['instructions'] = ('✅️ Filters have been updated') - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) # In this case (as is typical), the payload is a form values = payload['values'] for key in values: @@ -1272,13 +1442,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): .format(function_name, jid_full)) jid_bare = session['from'].bare chat_type = await get_chat_type(self, jid_bare) - moderator = None - if chat_type == 'groupchat': - moderator = is_moderator(self, jid_bare, jid_full) - if chat_type == 'chat' or moderator: - form = self['xep_0004'].make_form('form', 'Subscription') - form['instructions'] = 'Adding subscription' - form.add_field(desc='Enter a subscription URL.', + if is_access(self, jid_bare, jid_full, chat_type): + form = self['xep_0004'].make_form('form', 'Subscribe') + # form['instructions'] = 'Add a new custom subscription.' + form.add_field(desc='Enter a URL.', # TODO Make it possible to add several subscriptions at once; # Similarly to BitTorrent trackers list # ftype='text-multi', @@ -1286,18 +1453,25 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # desc='Add subscriptions one time per ' # 'subscription.', ftype='text-single', - label='URL', + label='Subscription', required=True, value='http://', var='subscription') if is_operator(self, jid_bare): + # form['instructions'] = ('Special section for operators:\n' + # 'This section allows you to add ' + # 'subscriptions for a JID of your ' + # 'choice.') form.add_field(ftype='fixed', - label='Subscriber') + label='* Operators', + desc='This section allows you to add ' + 'subscriptions for a JID of your ' + 'choice.') form.add_field(desc='Enter a Jabber ID to add the ' 'subscription to (The default Jabber ID is ' 'your own).', ftype='text-single', - label='Jabber ID', + label='Subscriber', var='jid') # form.add_field(desc='Scan URL for validity (recommended).', # ftype='boolean', @@ -1310,8 +1484,16 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): session['prev'] = None session['payload'] = form else: - text_warn = ('This resource is restricted to moderators of {}.' - .format(jid_bare)) + if not is_operator(self, jid_bare): + text_warn = 'This resource is restricted to operators.' + elif chat_type == 'groupchat': + text_warn = ('This resource is restricted to moderators of {}.' + .format(jid_bare)) + elif chat_type == 'error': + text_warn = ('Could not determine chat type of {}.' + .format(jid_bare)) + else: + text_warn = 'This resource is forbidden.' session['notes'] = [['warn', text_warn]] return session @@ -1323,24 +1505,28 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): .format(function_name, jid_full)) jid_bare = session['from'].bare form = self['xep_0004'].make_form('form', 'Updates') - form['instructions'] = 'Browse and read news' + # form['instructions'] = 'Browse and read news items.' options = form.add_field(desc='What would you want to read?', ftype='list-single', - label='Read', + label='News', required=True, var='action') - options.addOption('All news', 'all') + options.addOption('All', 'all') # options.addOption('News by subscription', 'feed') # options.addOption('News by tag', 'tag') - options.addOption('Rejected news', 'reject') - options.addOption('Unread news', 'unread') + options.addOption('Rejected', 'reject') + options.addOption('Unread', 'unread') if is_operator(self, jid_bare): + # form['instructions'] = ('Special section for operators:\n' + # 'This section allows you to view news items ' + # 'of a JID of your choice.') form.add_field(ftype='fixed', - label='Subscriber') - options = form.add_field(desc='Select a Jabber ID (The default ' - 'Jabber ID is your own).', + label='* Operators', + desc='This section allows you to view news items ' + 'of a JID of your choice.') + options = form.add_field(desc='Select a Jabber ID.', ftype='list-single', - label='Jabber ID', + label='Subscriber', value=jid_bare, var='jid') jids = [] @@ -1372,14 +1558,11 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): values = payload['values'] form = self['xep_0004'].make_form('form', 'Updates') if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'] - jid_file = jid + jid_bare = values['jid'] form.add_field(var='jid', ftype='hidden', - value=jid) - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + value=jid_bare) + db_file = config.get_pathname_to_database(jid_bare) num = 100 match values['action']: case 'all': @@ -1402,7 +1585,8 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): required=True, var='update') for result in results: - title = result[1] + title = result[1] # TODO Decide what title to display upon empty title + if not title: title = sqlite.get_feed_title(db_file, result[4]) ix = str(result[0]) options.addOption(title, ix) session['allow_prev'] = False # Cheogram changes style if that button - which should not be on this form - is present @@ -1421,6 +1605,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): return session + # FIXME async def _handle_recent_select(self, payload, session): jid_full = str(session['from']) function_name = sys._getframe().f_code.co_name @@ -1432,13 +1617,11 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): form = self['xep_0004'].make_form('form', 'Article') if is_operator(self, jid_bare) and 'jid' in values: jid = values['jid'] - jid_file = jid[0] if isinstance(jid, list) else jid + jid_bare = jid[0] if isinstance(jid, list) else jid form.add_field(var='jid', ftype='hidden', value=jid) - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) title = sqlite.get_entry_title(db_file, ix) title = title[0] if title else 'Untitled' form['instructions'] = title @@ -1495,60 +1678,6 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): return session - async def _handle_recent_action(self, payload, session): - jid_full = str(session['from']) - function_name = sys._getframe().f_code.co_name - logger.debug('{}: jid_full: {}' - .format(function_name, jid_full)) - ext = payload['values']['filetype'] - url = payload['values']['url'][0] - jid_bare = session['from'].bare - cache_dir = config.get_default_cache_directory() - if not os.path.isdir(cache_dir): - os.mkdir(cache_dir) - if not os.path.isdir(cache_dir + '/readability'): - os.mkdir(cache_dir + '/readability') - url = uri.remove_tracking_parameters(url) - url = (await uri.replace_hostname(url, 'link')) or url - result = await fetch.http(url) - if not result['error']: - data = result['content'] - code = result['status_code'] - title = action.get_document_title(data) - title = title.strip().lower() - for i in (' ', '-'): - title = title.replace(i, '_') - for i in ('?', '"', '\'', '!'): - title = title.replace(i, '') - filename = os.path.join( - cache_dir, 'readability', - title + '_' + dt.timestamp() + '.' + ext) - error = action.generate_document(data, url, ext, filename, - readability=True) - if error: - text_error = ('Failed to export {} fot {}' - '\n\n' - 'Reason: {}'.format(ext.upper(), url, error)) - session['notes'] = [['error', text_error]] - else: - url = await XmppUpload.start(self, jid_bare, filename) - chat_type = await get_chat_type(self, jid_bare) - XmppMessage.send_oob(self, jid_bare, url, chat_type) - form = self['xep_0004'].make_form('result', 'Download') - form['instructions'] = ('Download {} document.' - .format(ext.upper())) - field_url = form.add_field(var='url', - label='Link', - ftype='text-single', - value=url) - field_url['validate']['datatype'] = 'xs:anyURI' - session['payload'] = form - session['allow_complete'] = True - session['next'] = None - session['prev'] = None - return session - - async def _handle_subscription_new(self, payload, session): jid_full = str(session['from']) function_name = sys._getframe().f_code.co_name @@ -1562,13 +1691,11 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jid_bare = session['from'].bare if is_operator(self, jid_bare) and 'jid' in values: jid = values['jid'] - jid_file = jid[0] if isinstance(jid, list) else jid + jid_bare = jid[0] if isinstance(jid, list) else jid form.add_field(var='jid', ftype='hidden', value=jid) - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if identifier and sqlite.check_identifier_exist(db_file, identifier): form['title'] = 'Conflict' form['instructions'] = ('Name "{}" already exists. Choose a ' @@ -1715,7 +1842,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # Do all clients provide button "Cancel". session['allow_complete'] = False session['has_next'] = True - session['next'] = self._handle_subscription_editor + session['next'] = self._handle_subscription_edit session['payload'] = form # session['has_next'] = False # Single URL to subscribe @@ -1752,7 +1879,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # session['allow_prev'] = False # Gajim: Will offer next dialog but as a result, not as form. # session['has_next'] = False - session['next'] = self._handle_subscription_editor + session['next'] = self._handle_subscription_edit session['payload'] = form # session['prev'] = None return session @@ -1766,19 +1893,20 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jid_bare = session['from'].bare values = payload['values'] if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'][0] - jid_file = jid + jid_bare = values['jid'][0] del values['jid'] - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) for key in values: value = 1 if values[key] else 0 await sqlite.set_enabled_status(db_file, key, value) - text_note = 'Done.' - session['next'] = None - session['notes'] = [['info', text_note]] - session['payload'] = None + # text_note = 'Done.' + # session['next'] = None + # session['notes'] = [['info', text_note]] + # session['payload'] = None + form = payload + form['title'] = 'Done' + form['instructions'] = 'has been successful' + session['payload'] = form return session @@ -1790,14 +1918,11 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jid_bare = session['from'].bare values = payload['values'] if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'][0] - jid_file = jid + jid_bare = values['jid'][0] del values['jid'] - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) subscriptions ='' - ixs = payload['values']['subscriptions'] + ixs = values['subscriptions'] for ix in ixs: name = sqlite.get_feed_title(db_file, ix) url = sqlite.get_feed_url(db_file, ix) @@ -1833,11 +1958,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): .format(function_name, jid_full)) jid_bare = session['from'].bare chat_type = await get_chat_type(self, jid_bare) - moderator = None - if chat_type == 'groupchat': - moderator = is_moderator(self, jid_bare, jid_full) - # moderator = moderator if moderator else None - if chat_type == 'chat' or moderator: + if is_access(self, jid_bare, jid_full, chat_type): form = self['xep_0004'].make_form('form', 'Discover & Search') form['instructions'] = 'Discover news subscriptions of all kinds' options = form.add_field(desc='Select type of search.', @@ -1854,8 +1975,16 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): session['payload'] = form session['prev'] = None else: - text_warn = ('This resource is restricted to moderators of {}.' - .format(jid_bare)) + if not is_operator(self, jid_bare): + text_warn = 'This resource is restricted to operators.' + elif chat_type == 'groupchat': + text_warn = ('This resource is restricted to moderators of {}.' + .format(jid_bare)) + elif chat_type == 'error': + text_warn = ('Could not determine chat type of {}.' + .format(jid_bare)) + else: + text_warn = 'This resource is forbidden.' session['notes'] = [['warn', text_warn]] return session @@ -1953,12 +2082,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): .format(function_name, jid_full)) jid_bare = session['from'].bare chat_type = await get_chat_type(self, jid_bare) - moderator = None - if chat_type == 'groupchat': - moderator = is_moderator(self, jid_bare, jid_full) - if chat_type == 'chat' or moderator: + if is_access(self, jid_bare, jid_full, chat_type): form = self['xep_0004'].make_form('form', 'Subscriptions') - form['instructions'] = 'Managing subscriptions' + form['instructions'] = ('Browse, view, toggle or remove ' + 'tags and subscriptions.') options = form.add_field(desc='Select action type.', ftype='list-single', label='Action', @@ -1970,12 +2097,17 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): options.addOption('Remove subscriptions', 'delete') options.addOption('Toggle subscriptions', 'toggle') if is_operator(self, jid_bare): + form['instructions'] = None + # form['instructions'] = ('Special section for operators:\n' + # 'This section allows you to change ' + # 'and meddle with subscribers data.') form.add_field(ftype='fixed', - label='Subscriber') - options = form.add_field(desc='Select a Jabber ID (The ' - 'default Jabber ID is your own).', + label='* Operators', + desc='This section allows you to change ' + 'subscribers data.') + options = form.add_field(desc='Select a Jabber ID.', ftype='list-single', - label='Jabber ID', + label='Subscribers', value=jid_bare, var='jid') jids = [] @@ -1994,8 +2126,16 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): session['next'] = self._handle_subscriptions_result session['has_next'] = True else: - text_warn = ('This resource is restricted to moderators of {}.' - .format(jid_bare)) + if not is_operator(self, jid_bare): + text_warn = 'This resource is restricted to operators.' + elif chat_type == 'groupchat': + text_warn = ('This resource is restricted to moderators of {}.' + .format(jid_bare)) + elif chat_type == 'error': + text_warn = ('Could not determine chat type of {}.' + .format(jid_bare)) + else: + text_warn = 'This resource is forbidden.' session['notes'] = [['warn', text_warn]] return session @@ -2009,14 +2149,11 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jid_bare = session['from'].bare form = self['xep_0004'].make_form('form', 'Subscriptions') if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'] - jid_file = jid + jid_bare = values['jid'] form.add_field(ftype='hidden', - value=jid, + value=jid_bare, var='jid') - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) match values['action']: case 'browse': form['instructions'] = 'Editing subscriptions' @@ -2033,7 +2170,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): url = subscription[2] options.addOption(title, url) session['has_next'] = True - session['next'] = self._handle_subscription_editor + session['next'] = self._handle_subscription_edit session['allow_complete'] = False case 'delete': form['instructions'] = 'Removing subscriptions' @@ -2062,10 +2199,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # subscriptions = sorted(subscriptions, key=lambda x: x[1]) for subscription in subscriptions: ix = str(subscription[0]) - title = subscription[1] - url = subscription[2] - enabled_state = True if subscription[3] else False - enabled_state = subscription[3] + title = subscription[3] + url = subscription[1] + enabled_state = True if subscription[17] else False + enabled_state = subscription[17] form.add_field(desc=url, ftype='boolean', label=title, @@ -2106,14 +2243,11 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jid_bare = session['from'].bare values = payload['values'] if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'][0] - jid_file = jid + jid_bare = values['jid'][0] form.add_field(ftype='hidden', - value=jid, + value=jid_bare, var='jid') - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) tag_id = values['tag'] tag_name = sqlite.get_tag_name(db_file, tag_id)[0] form['instructions'] = 'Subscriptions tagged with "{}"'.format(tag_name) @@ -2132,13 +2266,13 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): session['allow_complete'] = False session['allow_prev'] = True session['has_next'] = True - session['next'] = self._handle_subscription_editor + session['next'] = self._handle_subscription_edit session['payload'] = form session['prev'] = self._handle_subscriptions return session - async def _handle_subscription_editor(self, payload, session): + async def _handle_subscription_edit(self, payload, session): jid_full = str(session['from']) function_name = sys._getframe().f_code.co_name logger.debug('{}: jid_full: {}' @@ -2147,18 +2281,13 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jid_bare = session['from'].bare values = payload['values'] if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'][0] - jid_file = jid + jid_bare = values['jid'][0] if values['jid'] else jid_bare form.add_field(ftype='hidden', - value=jid, + value=jid_bare, var='jid') - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) - if 'subscription' in values: - urls = values['subscription'] - elif 'subscriptions' in values: - urls = values['subscriptions'] + db_file = config.get_pathname_to_database(jid_bare) + if 'subscription' in values: urls = values['subscription'] + elif 'subscriptions' in values: urls = values['subscriptions'] url_count = len(urls) if isinstance(urls, list) and url_count > 1: form['instructions'] = 'Editing {} subscriptions'.format(url_count) @@ -2248,11 +2377,8 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jid_bare = session['from'].bare values = payload['values'] if is_operator(self, jid_bare) and 'jid' in values: - jid = values['jid'][0] - jid_file = jid - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + jid_bare = values['jid'][0] + db_file = config.get_pathname_to_database(jid_bare) # url = values['url'] # feed_id = sqlite.get_feed_id(db_file, url) # feed_id = feed_id[0] @@ -2316,18 +2442,14 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): .format(function_name, jid_full)) jid_bare = session['from'].bare chat_type = await get_chat_type(self, jid_bare) - moderator = None - if chat_type == 'groupchat': - moderator = is_moderator(self, jid_bare, jid_full) - if chat_type == 'chat' or moderator: + if is_access(self, jid_bare, jid_full, chat_type): form = self['xep_0004'].make_form('form', 'Advanced') form['instructions'] = 'Extended options' options = form.add_field(ftype='list-single', label='Choose', required=True, var='option') - jid = session['from'].bare - if is_operator(self, jid): + if is_operator(self, jid_bare): options.addOption('Administration', 'admin') # options.addOption('Activity', 'activity') # options.addOption('Filters', 'filter') @@ -2335,12 +2457,22 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # options.addOption('Scheduler', 'scheduler') options.addOption('Import', 'import') options.addOption('Export', 'export') + session['allow_prev'] = False session['payload'] = form - session['next'] = self._handle_advanced_result session['has_next'] = True + session['next'] = self._handle_advanced_result + session['prev'] = self._handle_advanced else: - text_warn = ('This resource is restricted to moderators of {}.' - .format(jid)) + if not is_operator(self, jid_bare): + text_warn = 'This resource is restricted to operators.' + elif chat_type == 'groupchat': + text_warn = ('This resource is restricted to moderators of {}.' + .format(jid_bare)) + elif chat_type == 'error': + text_warn = ('Could not determine chat type of {}.' + .format(jid_bare)) + else: + text_warn = 'This resource is forbidden.' session['notes'] = [['warn', text_warn]] return session @@ -2374,8 +2506,9 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): session['notes'] = [['info', text_info]] else: form = self['xep_0004'].make_form('form', 'Admin Panel') - form['instructions'] = 'Administration actions' - options = form.add_field(desc='Select action type.', + form['instructions'] = ('Choose the type of prospect ' + 'you want to handle with') + options = form.add_field(desc='Select a prospect type', ftype='list-single', label='Manage', required=True, @@ -2383,6 +2516,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): var='action') options.addOption('Bookmarks', 'bookmarks') options.addOption('Contacts', 'roster') + options.addOption('Nodes', 'nodes') options.addOption('PubSub', 'pubsub') options.addOption('Subscribers', 'subscribers') session['payload'] = form @@ -2421,7 +2555,9 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): url['validate']['datatype'] = 'xs:anyURI' if is_operator(self, jid_bare): form.add_field(ftype='fixed', - label='Subscriber') + label='* Operators', + desc='This section allows you to import ' + 'subscriptions for any subscriber.') form.add_field(desc='Enter a Jabber ID to import ' 'subscriptions to (The default Jabber ID ' 'is your own).', @@ -2434,12 +2570,12 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): session['payload'] = form case 'export': form = self['xep_0004'].make_form('form', 'Export') - form['instructions'] = ('To easily import subscriptions from ' - 'one News Reader to another, it is ' - 'always recommended to export ' - 'subscriptions into OPML file. See ' - 'About -> Software for a list of ' - 'News Readers offered for desktop and ' + form['instructions'] = ('It is always recommended to export ' + 'subscriptions into OPML file, in ' + 'order to easily import subscriptions ' + 'from one Feed Reader to another. See ' + 'About -> News Software for a list of ' + 'Feed Readers offered for desktop and ' 'mobile devices.') options = form.add_field(desc='Choose export format.', ftype='list-multi', @@ -2452,13 +2588,18 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # options.addOption('HTML', 'html') # options.addOption('XBEL', 'xbel') if is_operator(self, jid_bare): + # form['instructions'] = ('Special section for operators:\n' + # 'This section allows you to ' + # 'import and export subscriptions ' + # 'for a JID of your choice.') form.add_field(ftype='fixed', - label='Subscriber') + label='* Operators', + desc='This section allows you to export ' + 'subscriptions of any subscriber.') options = form.add_field(desc='Select a Jabber ID to ' - 'export subscriptions from (The ' - 'default Jabber ID is your own).', + 'export subscriptions from.', ftype='list-single', - label='Jabber ID', + label='Subscriber', value=jid_bare, var='jid') # options.addOption(self.boundjid.bare, self.boundjid.bare) @@ -2478,8 +2619,8 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): session['has_next'] = False session['next'] = self._handle_export_complete session['payload'] = form - session['allow_prev'] = True - session['prev'] = self._handle_advanced + # session['allow_prev'] = True + # session['prev'] = self._handle_advanced return session @@ -2545,8 +2686,12 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # form.add_field(ftype='fixed', # label=e_val) if e_key == 'Name': + desc = entry['desc'] if 'desc' in entry and entry['desc'] else None form.add_field(ftype='fixed', - label=e_val) + label=e_val, + desc=desc) + continue + if e_key == 'Desc': continue if isinstance(e_val, list): form_type = 'text-multi' @@ -2634,10 +2779,8 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jid_bare = session['from'].bare if is_operator(self, jid_bare) and 'jid' in values: jid = values['jid'] - jid_file = jid[0] if isinstance(jid, list) else jid - else: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + jid_bare = jid[0] if isinstance(jid, list) else jid + db_file = config.get_pathname_to_database(jid_bare) result = await fetch.http(url) count = await action.import_opml(db_file, result) try: @@ -2677,14 +2820,12 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jid_bare = session['from'].bare if is_operator(self, jid_bare) and 'jid' in values: jid = values['jid'] - jid_file = jid[0] if isinstance(jid, list) else jid - else: - jid_file = jid_bare + jid_bare = jid[0] if isinstance(jid, list) else jid # form = self['xep_0004'].make_form('result', 'Done') # form['instructions'] = ('✅️ Feeds have been exported') exts = values['filetype'] for ext in exts: - filename = action.export_feeds(self, jid_file, jid_file, ext) + filename = action.export_feeds(self, jid_bare, ext) url = await XmppUpload.start(self, jid_bare, filename) chat_type = await get_chat_type(self, jid_bare) XmppMessage.send_oob(self, jid_bare, url, chat_type) @@ -2710,10 +2851,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): jid_bare = session['from'].bare jid_full = str(session['from']) chat_type = await get_chat_type(self, jid_bare) - moderator = None - if chat_type == 'groupchat': - moderator = is_moderator(self, jid_bare, jid_full) - if chat_type == 'chat' or moderator: + if is_access(self, jid_bare, jid_full, chat_type): form = self['xep_0004'].make_form('form', 'Subscribe') # NOTE Refresh button would be of use form['instructions'] = 'Featured subscriptions' @@ -2732,9 +2870,9 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): for i in range(10): url = action.pick_a_feed() options.addOption(url['name'], url['link']) - jid = session['from'].bare - if '@' in jid: - hostname = jid.split('@')[1] + # jid_bare = session['from'].bare + if '@' in jid_bare: + hostname = jid_bare.split('@')[1] url = 'http://' + hostname result = await crawl.probe_page(url) if not result: @@ -2764,8 +2902,16 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): session['payload'] = form session['prev'] = self._handle_promoted else: - text_warn = ('This resource is restricted to moderators of {}.' - .format(jid)) + if not is_operator(self, jid_bare): + text_warn = 'This resource is restricted to operators.' + elif chat_type == 'groupchat': + text_warn = ('This resource is restricted to moderators of {}.' + .format(jid_bare)) + elif chat_type == 'error': + text_warn = ('Could not determine chat type of {}.' + .format(jid_bare)) + else: + text_warn = 'This resource is forbidden.' session['notes'] = [['warn', text_warn]] return session @@ -2788,7 +2934,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): for conference in conferences: options.addOption(conference['name'], conference['jid']) session['has_next'] = True - session['next'] = self._handle_bookmarks_editor + session['next'] = self._handle_bookmarks_edit case 'roster': form = self['xep_0004'].make_form('form', 'Contacts') form['instructions'] = 'Organizing contacts' @@ -2842,6 +2988,26 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): session['allow_complete'] = True session['has_next'] = False session['next'] = self._handle_subscribers_complete + case 'nodes': + form = self['xep_0004'].make_form('form', 'PubSub') + form['instructions'] = ('Select a Publish-Subscribe service ' + 'of which nodes you want to list.') + # jid_bare = self.boundjid.bare + # enabled_state = Config.get_setting_value(self.settings, jid_bare, 'enabled') + + results = await XmppPubsub.get_pubsub_services(self) + options = form.add_field(desc='Select a PubSub service.', + ftype='list-single', + label='Jabber ID', + value=self.boundjid.bare, + var='jid') + for result in results + [{'jid' : self.boundjid.bare, + 'name' : self.alias}]: + name = result['name'] + jid_bare = result['jid'] + options.addOption(name, jid_bare) + session['has_next'] = True + session['next'] = self._handle_nodes case 'pubsub': form = self['xep_0004'].make_form('form', 'PubSub') form['instructions'] = ('Designate Publish-Subscribe services ' @@ -2869,12 +3035,252 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): session['allow_complete'] = True session['has_next'] = False session['next'] = self._handle_pubsubs_complete + # session['allow_prev'] = True + session['payload'] = form + # session['prev'] = self._handle_advanced + return session + + + def _handle_nodes(self, payload, session): + jid_full = str(session['from']) + function_name = sys._getframe().f_code.co_name + logger.debug('{}: jid_full: {}' + .format(function_name, jid_full)) + values = payload['values'] + jid = values['jid'] + form = self['xep_0004'].make_form('form', 'PubSub') + options = form.add_field(desc='Select a desired action.', + ftype='list-single', + label='Action', + value='browse', + var='action') + options.addOption('Browse', 'browse') + # options.addOption('Edit', 'edit') + options.addOption('Purge', 'purge') + options.addOption('Delete', 'delete') + form.add_field(var='jid', + ftype='hidden', + value=jid) + session['has_next'] = True + session['next'] = self._handle_nodes_action session['allow_prev'] = True session['payload'] = form session['prev'] = self._handle_advanced return session + async def _handle_nodes_action(self, payload, session): + jid_full = str(session['from']) + function_name = sys._getframe().f_code.co_name + logger.debug('{}: jid_full: {}' + .format(function_name, jid_full)) + values = payload['values'] + action = values['action'] + jid = values['jid'][0] + form = self['xep_0004'].make_form('form', 'PubSub') + match action: + case 'browse': + session['has_next'] = False + session['next'] = self._handle_node_browse + form['instructions'] = 'Browsing nodes' + options = form.add_field(desc='Select a node to view.', + ftype='list-single', + label='Nodes', + var='node') + case 'delete': + session['allow_complete'] = True + session['has_next'] = False + session['next'] = self._handle_nodes_delete + form['instructions'] = 'Deleting nodes' + options = form.add_field(desc='Select nodes to delete.', + ftype='list-multi', + label='Nodes', + var='nodes') + case 'edit': + # session['allow_complete'] = False + session['has_next'] = False + session['next'] = self._handle_node_edit + form['instructions'] = 'Editing nodes' + options = form.add_field(desc='Select a node to edit.', + ftype='list-single', + label='Nodes', + var='node') + case 'purge': + session['allow_complete'] = True + session['has_next'] = False + session['next'] = self._handle_nodes_purge + form['instructions'] = 'Purging nodes' + options = form.add_field(desc='Select nodes to purge.', + ftype='list-multi', + label='Nodes', + var='nodes') + iq = await XmppPubsub.get_nodes(self, jid) + nodes = iq['disco_items'] + for node in nodes: + node_id = node['node'] + node_name = node['name'] + options.addOption(node_name, node_id) + form.add_field(var='jid', + ftype='hidden', + value=jid) + session['allow_prev'] = True + session['payload'] = form + return session + + + async def _handle_node_browse(self, payload, session): + jid_full = str(session['from']) + function_name = sys._getframe().f_code.co_name + logger.debug('{}: jid_full: {}' + .format(function_name, jid_full)) + values = payload['values'] + jid = values['jid'][0] + node = values['node'] + form = self['xep_0004'].make_form('form', 'PubSub') + form['instructions'] = 'Browsing node items' + options = form.add_field(desc='Select an item to view.', + ftype='list-single', + label='Items', + var='item_id') + iq = await XmppPubsub.get_items(self, jid, node) + items = iq['pubsub']['items'] + for item in items: + item_id = item['id'] + item_name = item_id + options.addOption(item_name, item_id) + form.add_field(var='jid', + ftype='hidden', + value=jid) + form.add_field(var='node', + ftype='hidden', + value=node) + session['next'] = self._handle_item_view + session['payload'] = form + return session + + + async def _handle_item_view(self, payload, session): + jid_full = str(session['from']) + function_name = sys._getframe().f_code.co_name + logger.debug('{}: jid_full: {}' + .format(function_name, jid_full)) + values = payload['values'] + item_id = values['item_id'] + jid = values['jid'][0] + node = values['node'][0] + iq = await XmppPubsub.get_item(self, jid, node, item_id) + form = self['xep_0004'].make_form('form', 'PubSub') + # for item in iq['pubsub']['items']['substanzas']: + # for item in iq['pubsub']['items']: + # item['payload'] + + content = '' + # TODO Check whether element of type Atom + atom_entry = iq['pubsub']['items']['item']['payload'] + for element in atom_entry: + if element.text: + content += element.text + '\n\n' + # content += action.remove_html_tags(element.text) + '\n\n' + if element.attrib: + for i in element.attrib: + content += element.attrib[i] + '\n\n' + # if element.text: content += element.text + '\n\n' + + form.add_field(ftype="text-multi", + label='Content', + value=content) + session['allow_prev'] = True + session['payload'] = form + return session + + async def _handle_node_edit(self, payload, session): + jid_full = str(session['from']) + function_name = sys._getframe().f_code.co_name + logger.debug('{}: jid_full: {}' + .format(function_name, jid_full)) + values = payload['values'] + jid = values['jid'][0] + node = values['node'] + properties = await XmppPubsub.get_node_properties(self, jid, node) + form['instructions'] = 'Editing bookmark' + jid_split = properties['jid'].split('@') + room = jid_split[0] + host = jid_split[1] + options = form.addField(var='jid', + ftype='list-single', + label='Jabber ID', + value=jid_bare) + options.addOption(jid_bare, jid_bare) + form.addField(var='alias', + ftype='text-single', + label='Alias', + value=properties['nick'], + required=True) + form.addField(var='name', + ftype='text-single', + label='Name', + value=properties['name'], + required=True) + form.addField(var='room', + ftype='text-single', + label='Room', + value=room, + required=True) + form.addField(var='host', + ftype='text-single', + label='Host', + value=host, + required=True) + form.addField(var='password', + ftype='text-private', + label='Password', + value=properties['password']) + form.addField(var='language', + ftype='text-single', + label='Language', + value=properties['lang']) + form.add_field(var='autojoin', + ftype='boolean', + label='Auto-join', + value=properties['autojoin']) + + + async def _handle_nodes_purge(self, payload, session): + jid_full = str(session['from']) + function_name = sys._getframe().f_code.co_name + logger.debug('{}: jid_full: {}' + .format(function_name, jid_full)) + values = payload['values'] + jid = values['jid'][0] + nodes = values['nodes'] + for node in nodes: + XmppPubsub.purge_node(self, jid, node) + form = payload + form['title'] = 'Done' + session['next'] = None + session['notes'] = [['info', 'Nodes have been purged!']] + session['payload'] = form + return session + + + async def _handle_nodes_delete(self, payload, session): + jid_full = str(session['from']) + function_name = sys._getframe().f_code.co_name + logger.debug('{}: jid_full: {}' + .format(function_name, jid_full)) + values = payload['values'] + jid = values['jid'][0] + nodes = values['nodes'] + for node in nodes: + XmppPubsub.delete_node(self, jid, node) + form = payload + form['title'] = 'Done' + session['next'] = None + session['notes'] = [['info', 'Nodes have been deleted!']] + session['payload'] = form + return session + + async def _handle_pubsubs_complete(self, payload, session): jid_full = str(session['from']) function_name = sys._getframe().f_code.co_name @@ -2886,8 +3292,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): if key: jid_bare = key value = values[key] - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) await Config.set_setting_value(self.settings, jid_bare, @@ -2954,12 +3359,13 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): function_name = sys._getframe().f_code.co_name logger.debug('{}: jid_full: {}' .format(function_name, jid_full)) - jid_bare = payload['values']['jid'] + values = payload['values'] + jid_bare = values['jid'] form = self['xep_0004'].make_form('form', 'Contacts') session['allow_complete'] = True roster = await XmppRoster.get_contacts(self) properties = roster[jid_bare] - match payload['values']['action']: + match values['action']: case 'edit': form['instructions'] = 'Editing contact' options = form.add_field(var='jid', @@ -3040,13 +3446,13 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): return session - async def _handle_bookmarks_editor(self, payload, session): + async def _handle_bookmarks_edit(self, payload, session): jid_full = str(session['from']) function_name = sys._getframe().f_code.co_name logger.debug('{}: jid_full: {}' .format(function_name, jid_full)) jid_bare = payload['values']['jid'] - properties = await XmppBookmark.properties(self, jid_bare) + properties = await XmppBookmark.get_bookmark_properties(self, jid_bare) form = self['xep_0004'].make_form('form', 'Bookmarks') form['instructions'] = 'Editing bookmark' jid_split = properties['jid'].split('@') @@ -3097,11 +3503,11 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # options.addOption('Join', 'join') # options.addOption('Remove', 'remove') session['allow_complete'] = True - session['allow_prev'] = True + # session['allow_prev'] = True session['has_next'] = False session['next'] = self._handle_bookmarks_complete session['payload'] = form - session['prev'] = self._handle_admin_action + # session['prev'] = self._handle_admin_action # FIXME (1) Not realized (2) Should be _handle_advanced return session @@ -3147,12 +3553,8 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): .format(function_name, jid_full)) jid_bare = session['from'].bare chat_type = await get_chat_type(self, jid_bare) - moderator = None - if chat_type == 'groupchat': - moderator = is_moderator(self, jid_bare, jid_full) - if chat_type == 'chat' or moderator: - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + if is_access(self, jid_bare, jid_full, chat_type): + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) form = self['xep_0004'].make_form('form', 'Settings') @@ -3249,8 +3651,16 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): session['next'] = self._handle_settings_complete session['payload'] = form else: - text_warn = ('This resource is restricted to moderators of {}.' - .format(jid_bare)) + if not is_operator(self, jid_bare): + text_warn = 'This resource is restricted to operators.' + elif chat_type == 'groupchat': + text_warn = ('This resource is restricted to moderators of {}.' + .format(jid_bare)) + elif chat_type == 'error': + text_warn = ('Could not determine chat type of {}.' + .format(jid_bare)) + else: + text_warn = 'This resource is forbidden.' session['notes'] = [['warn', text_warn]] return session @@ -3261,8 +3671,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): logger.debug('{}: jid_full: {}' .format(function_name, jid_full)) jid_bare = session['from'].bare - jid_file = jid_bare - db_file = config.get_pathname_to_database(jid_file) + db_file = config.get_pathname_to_database(jid_bare) if jid_bare not in self.settings: Config.add_settings_jid(self.settings, jid_bare, db_file) # In this case (as is typical), the payload is a form