From 00a8ed180aac2a5fade644f4285a8872a9ef44b9 Mon Sep 17 00:00:00 2001 From: Schimon Jehudah Date: Wed, 7 Feb 2024 00:26:42 +0000 Subject: [PATCH] Fix presence and subscription handling. Segregate and atomize into classes. --- slixfeed/action.py | 176 +++++++++++++++---- slixfeed/assets/commands.toml | 11 +- slixfeed/assets/information.toml | 1 + slixfeed/task.py | 52 ++---- slixfeed/url.py | 81 +++------ slixfeed/xmpp/bookmark.py | 16 +- slixfeed/xmpp/client.py | 144 ++++++++++----- slixfeed/xmpp/component.py | 89 ++++++---- slixfeed/xmpp/connect.py | 112 ++++++------ slixfeed/xmpp/message.py | 78 +++++++-- slixfeed/xmpp/muc.py | 172 +++++++++--------- slixfeed/xmpp/presence.py | 37 ++++ slixfeed/xmpp/process.py | 291 ++++++++----------------------- slixfeed/xmpp/roster.py | 79 +++------ slixfeed/xmpp/state.py | 71 -------- slixfeed/xmpp/status.py | 19 -- slixfeed/xmpp/upload.py | 65 +++---- slixfeed/xmpp/utility.py | 22 ++- 18 files changed, 734 insertions(+), 782 deletions(-) create mode 100644 slixfeed/xmpp/presence.py delete mode 100644 slixfeed/xmpp/state.py delete mode 100644 slixfeed/xmpp/status.py diff --git a/slixfeed/action.py b/slixfeed/action.py index 594c71d..31132de 100644 --- a/slixfeed/action.py +++ b/slixfeed/action.py @@ -24,6 +24,7 @@ TODO """ +import asyncio from asyncio.exceptions import IncompleteReadError from bs4 import BeautifulSoup from feedparser import parse @@ -34,13 +35,11 @@ from lxml import html import os import slixfeed.config as config import slixfeed.crawl as crawl -from slixfeed.dt import ( - current_date, current_time, now, - convert_struct_time_to_iso8601, - rfc2822_to_iso8601 - ) + +import slixfeed.dt as dt import slixfeed.fetch as fetch import slixfeed.sqlite as sqlite +import slixfeed.url as uri from slixfeed.url import ( complete_url, join_url, @@ -51,7 +50,9 @@ from slixfeed.url import ( import slixfeed.task as task from slixfeed.xmpp.bookmark import XmppBookmark from slixfeed.xmpp.message import XmppMessage -from slixfeed.xmpp.status import XmppStatus +from slixfeed.xmpp.presence import XmppPresence +from slixfeed.xmpp.upload import XmppUpload +from slixfeed.xmpp.utility import get_chat_type import tomllib from urllib import error from urllib.parse import parse_qs, urlsplit @@ -122,8 +123,7 @@ async def xmpp_change_interval(self, key, val, jid, jid_file, message=None, sess await sqlite.set_settings_value(db_file, [key, val]) # NOTE Perhaps this should be replaced # by functions clean and start - await task.refresh_task(self, jid, task.send_update, - key, val) + await task.refresh_task(self, jid, task.send_update, key, val) response = ('Updates will be sent every {} minutes.' .format(val)) else: @@ -131,7 +131,24 @@ async def xmpp_change_interval(self, key, val, jid, jid_file, message=None, sess if message: XmppMessage.send_reply(self, message, response) if session: - await XmppMessage.send(self, jid, response, chat_type='chat') + XmppMessage.send(self, jid, response, chat_type='chat') + + +async def xmpp_start_updates(self, message, jid, jid_file): + key = 'enabled' + val = 1 + db_file = config.get_pathname_to_database(jid_file) + if await sqlite.get_settings_value(db_file, key): + await sqlite.update_settings_value(db_file, [key, val]) + else: + await sqlite.set_settings_value(db_file, [key, val]) + status_type = 'available' + status_message = 'πŸ’‘οΈ Welcome back!' + XmppPresence.send(self, jid, status_message, status_type=status_type) + message_body = 'Updates are enabled.' + XmppMessage.send_reply(self, message, message_body) + await asyncio.sleep(5) + await task.start_tasks_xmpp(self, jid, ['check', 'status', 'interval']) async def xmpp_stop_updates(self, message, jid, jid_file): @@ -143,11 +160,12 @@ async def xmpp_stop_updates(self, message, jid, jid_file): else: await sqlite.set_settings_value(db_file, [key, val]) await task.clean_tasks_xmpp(jid, ['interval', 'status']) - response = 'Updates are disabled.' - XmppMessage.send_reply(self, message, response) + message_body = 'Updates are disabled.' + XmppMessage.send_reply(self, message, message_body) status_type = 'xa' status_message = 'πŸ’‘οΈ Send "Start" to receive Jabber updates' - await XmppStatus.send(self, jid, status_message, status_type) + XmppPresence.send(self, jid, status_message, status_type=status_type) + def log_to_markdown(timestamp, filename, jid, message): """ @@ -473,7 +491,7 @@ def export_to_markdown(jid, filename, results): file.write( '\n\n* * *\n\nThis list was saved on {} from xmpp:{} using ' '[Slixfeed](https://gitgud.io/sjehuda/slixfeed)\n'.format( - current_date(), jid)) + dt.current_date(), jid)) # TODO Consider adding element jid as a pointer of import @@ -487,7 +505,7 @@ def export_to_opml(jid, filename, results): ET.SubElement(head, "generator").text = "Slixfeed" ET.SubElement(head, "urlPublic").text = ( "https://gitgud.io/sjehuda/slixfeed") - time_stamp = current_time() + time_stamp = dt.current_time() ET.SubElement(head, "dateCreated").text = time_stamp ET.SubElement(head, "dateModified").text = time_stamp body = ET.SubElement(root, "body") @@ -548,7 +566,7 @@ async def add_feed(db_file, url): if "updated_parsed" in feed["feed"].keys(): updated = feed["feed"]["updated_parsed"] try: - updated = convert_struct_time_to_iso8601(updated) + updated = dt.convert_struct_time_to_iso8601(updated) except: updated = '' else: @@ -594,7 +612,7 @@ async def add_feed(db_file, url): if "date_published" in feed.keys(): updated = feed["date_published"] try: - updated = convert_struct_time_to_iso8601(updated) + updated = dt.convert_struct_time_to_iso8601(updated) except: updated = '' else: @@ -676,7 +694,7 @@ async def scan_json(db_file, url): if "date_published" in feed.keys(): updated = feed["date_published"] try: - updated = convert_struct_time_to_iso8601(updated) + updated = dt.convert_struct_time_to_iso8601(updated) except: updated = '' else: @@ -697,12 +715,12 @@ async def scan_json(db_file, url): for entry in entries: if "date_published" in entry.keys(): date = entry["date_published"] - date = rfc2822_to_iso8601(date) + date = dt.rfc2822_to_iso8601(date) elif "date_modified" in entry.keys(): date = entry["date_modified"] - date = rfc2822_to_iso8601(date) + date = dt.rfc2822_to_iso8601(date) else: - date = now() + date = dt.now() if "url" in entry.keys(): # link = complete_url(source, entry.link) link = join_url(url, entry["url"]) @@ -820,10 +838,10 @@ async def view_feed(url): link = "*** No link ***" if entry.has_key("published"): date = entry.published - date = rfc2822_to_iso8601(date) + date = dt.rfc2822_to_iso8601(date) elif entry.has_key("updated"): date = entry.updated - date = rfc2822_to_iso8601(date) + date = dt.rfc2822_to_iso8601(date) else: date = "*** No date ***" response += ( @@ -877,10 +895,10 @@ async def view_entry(url, num): title = "*** No title ***" if entry.has_key("published"): date = entry.published - date = rfc2822_to_iso8601(date) + date = dt.rfc2822_to_iso8601(date) elif entry.has_key("updated"): date = entry.updated - date = rfc2822_to_iso8601(date) + date = dt.rfc2822_to_iso8601(date) else: date = "*** No date ***" if entry.has_key("summary"): @@ -965,7 +983,7 @@ async def scan(db_file, url): if "updated_parsed" in feed["feed"].keys(): updated = feed["feed"]["updated_parsed"] try: - updated = convert_struct_time_to_iso8601(updated) + updated = dt.convert_struct_time_to_iso8601(updated) except: updated = '' else: @@ -986,12 +1004,12 @@ async def scan(db_file, url): for entry in entries: if entry.has_key("published"): date = entry.published - date = rfc2822_to_iso8601(date) + date = dt.rfc2822_to_iso8601(date) elif entry.has_key("updated"): date = entry.updated - date = rfc2822_to_iso8601(date) + date = dt.rfc2822_to_iso8601(date) else: - date = now() + date = dt.now() if entry.has_key("link"): # link = complete_url(source, entry.link) link = join_url(url, entry.link) @@ -1073,6 +1091,89 @@ async def scan(db_file, url): db_file, feed_id, new_entries) +async def download_document(self, message, jid, jid_file, message_text, ix_url, + readability): + ext = ' '.join(message_text.split(' ')[1:]) + ext = ext if ext else 'pdf' + url = None + error = None + response = None + if ext in ('epub', 'html', 'markdown', + 'md', 'pdf', 'text', 'txt'): + match ext: + case 'markdown': + ext = 'md' + case 'text': + ext = 'txt' + status_type = 'dnd' + status_message = ('πŸ“ƒοΈ Procesing request to produce {} document...' + .format(ext.upper())) + XmppPresence.send(self, jid, status_message, + status_type=status_type) + db_file = config.get_pathname_to_database(jid_file) + cache_dir = config.get_default_cache_directory() + if ix_url: + if not os.path.isdir(cache_dir): + os.mkdir(cache_dir) + if not os.path.isdir(cache_dir + '/readability'): + os.mkdir(cache_dir + '/readability') + try: + ix = int(ix_url) + try: + url = sqlite.get_entry_url(db_file, ix) + except: + response = 'No entry with index {}'.format(ix) + except: + url = ix_url + if url: + logging.info('Original URL: {}' + .format(url)) + url = uri.remove_tracking_parameters(url) + logging.info('Processed URL (tracker removal): {}' + .format(url)) + url = (uri.replace_hostname(url, 'link')) or url + logging.info('Processed URL (replace hostname): {}' + .format(url)) + result = await fetch.http(url) + data = result[0] + code = result[1] + if data: + title = 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 = generate_document(data, url, ext, filename, + readability) + if error: + response = ('> {}\n' + 'Failed to export {}. Reason: {}' + .format(url, ext.upper(), error)) + else: + url = await XmppUpload.start(self, jid, + filename) + chat_type = await get_chat_type(self, jid) + XmppMessage.send_oob(self, jid, url, chat_type) + else: + response = ('> {}\n' + 'Failed to fetch URL. Reason: {}' + .format(url, code)) + await task.start_tasks_xmpp(self, jid, ['status']) + else: + response = 'Missing entry index number.' + else: + response = ('Unsupported filetype.\n' + 'Try: epub, html, md (markdown), ' + 'pdf, or txt (text)') + if response: + logging.warning('Error for URL {}: {}'.format(url, error)) + XmppMessage.send_reply(self, message, response) + + def get_document_title(data): try: document = Document(data) @@ -1083,14 +1184,17 @@ def get_document_title(data): return title -def generate_document(data, url, ext, filename): +def generate_document(data, url, ext, filename, readability=False): error = None - try: - document = Document(data) - content = document.summary() - except: + if readability: + try: + document = Document(data) + content = document.summary() + except: + content = data + logging.warning('Check that package readability is installed.') + else: content = data - logging.warning('Check that package readability is installed.') match ext: case "epub": error = generate_epub(content, filename) @@ -1319,7 +1423,7 @@ async def remove_nonexistent_entries(db_file, url, feed): # print("compare11:", title, link, time) # print("compare22:", entry_title, entry_link, timestamp) # print("============") - time = rfc2822_to_iso8601(entry.published) + time = dt.rfc2822_to_iso8601(entry.published) if (entry_title == title and entry_link == link and timestamp == time): @@ -1423,7 +1527,7 @@ async def remove_nonexistent_entries_json(db_file, url, feed): link = url # "date_published" "date_modified" if entry.has_key("date_published") and timestamp: - time = rfc2822_to_iso8601(entry["date_published"]) + time = dt.rfc2822_to_iso8601(entry["date_published"]) if (entry_title == title and entry_link == link and timestamp == time): diff --git a/slixfeed/assets/commands.toml b/slixfeed/assets/commands.toml index 3245d2a..d563f48 100644 --- a/slixfeed/assets/commands.toml +++ b/slixfeed/assets/commands.toml @@ -52,9 +52,14 @@ Send all items of newly added feeds. """ [document] -get = """ -get -Send an article as file. Specify and . +content = """ +content / +Send a readability (arc90) version of an article as file. Specify or and . +Supported types are ePUB, HTML, MD and PDF (default). +""" +page = """ +page / +Send an article as file. Specify or and . Supported types are ePUB, HTML, MD and PDF (default). """ diff --git a/slixfeed/assets/information.toml b/slixfeed/assets/information.toml index 0f6498a..d844dae 100644 --- a/slixfeed/assets/information.toml +++ b/slixfeed/assets/information.toml @@ -116,6 +116,7 @@ Kevin Smith (Swift IM, Wales), \ Luis Henrique Mello (SalixOS, Brazil), \ magicfelix, \ Markus Muttilainen (SalixOS), \ +Martin (Debian, Germany), \ Mathieu Pasquet (slixmpp, France), \ Maxime Buquet (slixmpp, France), \ Phillip Watkins (United Kingdom, SalixOS), \ diff --git a/slixfeed/task.py b/slixfeed/task.py index 03ff68b..a657751 100644 --- a/slixfeed/task.py +++ b/slixfeed/task.py @@ -47,9 +47,10 @@ import slixfeed.config as config # from slixfeed.dt import current_time import slixfeed.sqlite as sqlite # from xmpp import Slixfeed -import slixfeed.xmpp.client as xmpp -import slixfeed.xmpp.connect as connect -import slixfeed.xmpp.utility as utility +from slixfeed.xmpp.presence import XmppPresence +from slixfeed.xmpp.message import XmppMessage +from slixfeed.xmpp.connect import XmppConnect +from slixfeed.xmpp.utility import get_chat_type import time main_task = [] @@ -70,12 +71,12 @@ loop = asyncio.get_event_loop() def ping_task(self): - global ping_task + global ping_task_instance try: - ping_task.cancel() + ping_task_instance.cancel() except: logging.info('No ping task to cancel.') - ping_task = asyncio.create_task(connect.ping(self)) + ping_task_instance = asyncio.create_task(XmppConnect.ping(self)) """ @@ -102,7 +103,7 @@ async def start_tasks_xmpp(self, jid, tasks=None): logging.debug('KeyError:', str(e)) logging.info('Creating new task manager for JID {}'.format(jid)) if not tasks: - tasks = ['interval', 'status', 'check'] + tasks = ['status', 'check', 'interval'] logging.info('Stopping tasks {} for JID {}'.format(tasks, jid)) for task in tasks: # if task_manager[jid][task]: @@ -201,7 +202,7 @@ async def send_update(self, jid, num=None): results = await sqlite.get_unread_entries(db_file, num) news_digest = '' media = None - chat_type = await utility.get_chat_type(self, jid) + chat_type = await get_chat_type(self, jid) for result in results: ix = result[0] title_e = result[1] @@ -230,24 +231,10 @@ async def send_update(self, jid, num=None): if media and news_digest: # Send textual message - xmpp.Slixfeed.send_message( - self, - mto=jid, - mfrom=self.boundjid.bare, - mbody=news_digest, - mtype=chat_type - ) + XmppMessage.send(self, jid, news_digest, chat_type) news_digest = '' # Send media - message = xmpp.Slixfeed.make_message( - self, - mto=jid, - mfrom=self.boundjid.bare, - mbody=media, - mtype=chat_type - ) - message['oob']['url'] = media - message.send() + XmppMessage.send_oob(self, jid, media, chat_type) media = None if news_digest: @@ -256,13 +243,8 @@ async def send_update(self, jid, num=None): # NOTE Do we need "if statement"? See NOTE at is_muc. if chat_type in ('chat', 'groupchat'): # TODO Provide a choice (with or without images) - xmpp.Slixfeed.send_message( - self, - mto=jid, - mfrom=self.boundjid.bare, - mbody=news_digest, - mtype=chat_type - ) + XmppMessage.send(self, jid, news_digest, chat_type) + # See XEP-0367 # if media: # # message = xmpp.Slixfeed.make_message( # # self, mto=jid, mbody=new, mtype=chat_type) @@ -340,13 +322,7 @@ async def send_status(self, jid): # breakpoint() # print(await current_time(), status_text, "for", jid) - xmpp.Slixfeed.send_presence( - self, - pto=jid, - pfrom=self.boundjid.bare, - pshow=status_mode, - pstatus=status_text - ) + XmppPresence.send(self, jid, status_text, status_type=status_mode) # await asyncio.sleep(60 * 20) await refresh_task(self, jid, send_status, 'status', '90') # loop.call_at( diff --git a/slixfeed/url.py b/slixfeed/url.py index 2d456c2..4295427 100644 --- a/slixfeed/url.py +++ b/slixfeed/url.py @@ -77,13 +77,8 @@ def replace_hostname(url, url_type): parted_proxy_url = urlsplit(proxy_url) protocol_new = parted_proxy_url.scheme hostname_new = parted_proxy_url.netloc - url_new = urlunsplit([ - protocol_new, - hostname_new, - pathname, - queries, - fragment - ]) + url_new = urlunsplit([protocol_new, hostname_new, + pathname, queries, fragment]) response = fetch.http_response(url_new) if (response and response.status_code == 200 and @@ -96,8 +91,11 @@ def replace_hostname(url, url_type): proxies_file = config_dir + '/proxies.toml' if not os.path.isfile(proxies_obsolete_file): config.create_skeleton(proxies_file) - config.backup_obsolete(proxies_obsolete_file, proxy_name, proxy_type, proxy_url) - config.update_proxies(proxies_file, proxy_name, proxy_type, proxy_url) + config.backup_obsolete(proxies_obsolete_file, + proxy_name, proxy_type, + proxy_url) + config.update_proxies(proxies_file, proxy_name, + proxy_type, proxy_url) url_new = None else: logging.warning( @@ -132,13 +130,7 @@ def remove_tracking_parameters(url): for tracker in trackers: if tracker in queries: del queries[tracker] queries_new = urlencode(queries, doseq=True) - url = urlunsplit([ - protocol, - hostname, - pathname, - queries_new, - fragment - ]) + url = urlunsplit([protocol, hostname, pathname, queries_new, fragment]) return url @@ -157,13 +149,8 @@ def feed_to_http(url): URL. """ par_url = urlsplit(url) - new_url = urlunsplit([ - 'http', - par_url.netloc, - par_url.path, - par_url.query, - par_url.fragment - ]) + new_url = urlunsplit(['http', par_url.netloc, par_url.path, par_url.query, + par_url.fragment]) return new_url @@ -215,21 +202,13 @@ def complete_url(source, link): return link if link.startswith('//'): if parted_link.netloc and parted_link.path: - new_link = urlunsplit([ - parted_feed.scheme, - parted_link.netloc, - parted_link.path, - parted_link.query, - parted_link.fragment - ]) + new_link = urlunsplit([parted_feed.scheme, parted_link.netloc, + parted_link.path, parted_link.query, + parted_link.fragment]) elif link.startswith('/'): - new_link = urlunsplit([ - parted_feed.scheme, - parted_feed.netloc, - parted_link.path, - parted_link.query, - parted_link.fragment - ]) + new_link = urlunsplit([parted_feed.scheme, parted_feed.netloc, + parted_link.path, parted_link.query, + parted_link.fragment]) elif link.startswith('../'): pathlink = parted_link.path.split('/') pathfeed = parted_feed.path.split('/') @@ -246,13 +225,9 @@ def complete_url(source, link): break pathlink = '/'.join(pathlink) pathfeed.extend([pathlink]) - new_link = urlunsplit([ - parted_feed.scheme, - parted_feed.netloc, - '/'.join(pathfeed), - parted_link.query, - parted_link.fragment - ]) + new_link = urlunsplit([parted_feed.scheme, parted_feed.netloc, + '/'.join(pathfeed), parted_link.query, + parted_link.fragment]) else: pathlink = parted_link.path.split('/') pathfeed = parted_feed.path.split('/') @@ -262,13 +237,9 @@ def complete_url(source, link): pathfeed.pop() pathlink = '/'.join(pathlink) pathfeed.extend([pathlink]) - new_link = urlunsplit([ - parted_feed.scheme, - parted_feed.netloc, - '/'.join(pathfeed), - parted_link.query, - parted_link.fragment - ]) + new_link = urlunsplit([parted_feed.scheme, parted_feed.netloc, + '/'.join(pathfeed), parted_link.query, + parted_link.fragment]) return new_link @@ -333,13 +304,7 @@ def trim_url(url): fragment = parted_url.fragment while '//' in pathname: pathname = pathname.replace('//', '/') - url = urlunsplit([ - protocol, - hostname, - pathname, - queries, - fragment - ]) + url = urlunsplit([protocol, hostname, pathname, queries, fragment]) return url diff --git a/slixfeed/xmpp/bookmark.py b/slixfeed/xmpp/bookmark.py index ddc123a..81148b4 100644 --- a/slixfeed/xmpp/bookmark.py +++ b/slixfeed/xmpp/bookmark.py @@ -34,11 +34,9 @@ class XmppBookmark: bookmarks = Bookmarks() mucs.extend([muc_jid]) for muc in mucs: - bookmarks.add_conference( - muc, - self.alias, - autojoin=True - ) + bookmarks.add_conference(muc, + self.alias, + autojoin=True) await self.plugin['xep_0048'].set_bookmarks(bookmarks) # bookmarks = Bookmarks() # await self.plugin['xep_0048'].set_bookmarks(bookmarks) @@ -61,9 +59,7 @@ class XmppBookmark: bookmarks = Bookmarks() mucs.remove(muc_jid) for muc in mucs: - bookmarks.add_conference( - muc, - self.alias, - autojoin=True - ) + bookmarks.add_conference(muc, + self.alias, + autojoin=True) await self.plugin['xep_0048'].set_bookmarks(bookmarks) diff --git a/slixfeed/xmpp/client.py b/slixfeed/xmpp/client.py index 8740245..9a5e633 100644 --- a/slixfeed/xmpp/client.py +++ b/slixfeed/xmpp/client.py @@ -63,15 +63,15 @@ from slixmpp.plugins.xep_0048.stanza import Bookmarks import slixfeed.config as config import slixfeed.sqlite as sqlite from slixfeed.xmpp.bookmark import XmppBookmark -import slixfeed.xmpp.connect as connect -import slixfeed.xmpp.muc as muc +from slixfeed.xmpp.connect import XmppConnect +from slixfeed.xmpp.message import XmppMessage +from slixfeed.xmpp.muc import XmppGroupchat import slixfeed.xmpp.process as process import slixfeed.xmpp.profile as profile -import slixfeed.xmpp.roster as roster +from slixfeed.xmpp.roster import XmppRoster # import slixfeed.xmpp.service as service -import slixfeed.xmpp.state as state -import slixfeed.xmpp.status as status -import slixfeed.xmpp.utility as utility +from slixfeed.xmpp.presence import XmppPresence +from slixfeed.xmpp.utility import get_chat_type main_task = [] jid_tasker = {} @@ -146,8 +146,8 @@ class Slixfeed(slixmpp.ClientXMPP): self.add_event_handler("reactions", self.on_reactions) - self.add_event_handler("presence_error", - self.on_presence_error) + # self.add_event_handler("presence_error", + # self.on_presence_error) self.add_event_handler("presence_subscribe", self.on_presence_subscribe) self.add_event_handler("presence_subscribed", @@ -161,43 +161,59 @@ class Slixfeed(slixmpp.ClientXMPP): # handlers for connection events self.connection_attempts = 0 self.max_connection_attempts = 10 - self.add_event_handler("connection_failed", self.on_connection_failed) - self.add_event_handler("session_end", self.on_session_end) + self.add_event_handler('connection_failed', + self.on_connection_failed) + self.add_event_handler('session_end', + self.on_session_end) # TODO Test async def on_groupchat_invite(self, message): logging.warning("on_groupchat_invite") - inviter = message["from"].bare + inviter = message['from'].bare muc_jid = message['groupchat_invite']['jid'] - await muc.join(self, inviter, muc_jid) await XmppBookmark.add(self, muc_jid) + await XmppGroupchat.join(self, inviter, muc_jid) + message_body = ('Greetings!\n' + 'I am {}, the news anchor.\n' + 'My job is to bring you the latest ' + 'news from sources you provide me with.\n' + 'You may always reach me via xmpp:{}?message' + .format(self.alias, self.boundjid.bare)) + XmppMessage.send(self, muc_jid, message_body, 'groupchat') # NOTE Tested with Gajim and Psi async def on_groupchat_direct_invite(self, message): - inviter = message["from"].bare + inviter = message['from'].bare muc_jid = message['groupchat_invite']['jid'] - await muc.join(self, inviter, muc_jid) await XmppBookmark.add(self, muc_jid) + await XmppGroupchat.join(self, inviter, muc_jid) + message_body = ('Greetings!\n' + 'I am {}, the news anchor.\n' + 'My job is to bring you the latest ' + 'news from sources you provide me with.\n' + 'You may always reach me via xmpp:{}?message' + .format(self.alias, self.boundjid.bare)) + XmppMessage.send(self, muc_jid, message_body, 'groupchat') async def on_session_end(self, event): - message = "Session has ended." - await connect.recover_connection(self, message) + message = 'Session has ended.' + await XmppConnect.recover(self, message) async def on_connection_failed(self, event): - message = "Connection has failed. Reason: {}".format(event) - await connect.recover_connection(self, message) + message = 'Connection has failed. Reason: {}'.format(event) + await XmppConnect.recover(self, message) async def on_session_start(self, event): self.send_presence() - await self["xep_0115"].update_caps() + await self['xep_0115'].update_caps() await self.get_roster() - await muc.autojoin(self) - profile.set_identity(self, "client") + await XmppGroupchat.autojoin(self) + profile.set_identity(self, 'client') await profile.update(self) task.ping_task(self) @@ -210,9 +226,9 @@ class Slixfeed(slixmpp.ClientXMPP): async def on_session_resumed(self, event): self.send_presence() - self["xep_0115"].update_caps() - await muc.autojoin(self) - profile.set_identity(self, "client") + self['xep_0115'].update_caps() + await XmppGroupchat.autojoin(self) + profile.set_identity(self, 'client') # Service.commands(self) # Service.reactions(self) @@ -224,9 +240,16 @@ class Slixfeed(slixmpp.ClientXMPP): # TODO Request for subscription async def on_message(self, message): jid = message["from"].bare - if "chat" == await utility.get_chat_type(self, jid): - await roster.add(self, jid) - await state.request(self, jid) + if (await get_chat_type(self, jid) == 'chat' and + not self.client_roster[jid]['to']): + XmppPresence.subscribe(self, jid) + await XmppRoster.add(self, jid) + status_message = 'βœ’οΈ Share online status to receive updates' + XmppPresence.send(self, jid, status_message) + message_subject = 'RSS News Bot' + message_body = 'Share online status to receive updates.' + XmppMessage.send_headline(self, jid, message_subject, message_body, + 'chat') await process.message(self, message) # chat_type = message["type"] # message_body = message["body"] @@ -242,31 +265,54 @@ class Slixfeed(slixmpp.ClientXMPP): async def on_presence_subscribe(self, presence): - jid = presence["from"].bare - await state.request(self, jid) + jid = presence['from'].bare + if not self.client_roster[jid]['to']: + XmppPresence.subscribe(self, jid) + await XmppRoster.add(self, jid) + status_message = 'βœ’οΈ Share online status to receive updates' + XmppPresence.send(self, jid, status_message) + message_subject = 'RSS News Bot' + message_body = 'Share online status to receive updates.' + XmppMessage.send_headline(self, jid, message_subject, message_body, + 'chat') async def on_presence_subscribed(self, presence): - jid = presence["from"].bare - process.greet(self, jid) + jid = presence['from'].bare + message_subject = 'RSS News Bot' + message_body = ('Greetings!\n' + 'I am {}, the news anchor.\n' + 'My job is to bring you the latest ' + 'news from sources you provide me with.\n' + 'You may always reach me via xmpp:{}?message' + .format(self.alias, self.boundjid.bare)) + XmppMessage.send_headline(self, jid, message_subject, message_body, + 'chat') async def on_presence_available(self, presence): # TODO Add function to check whether task is already running or not # await task.start_tasks(self, presence) # NOTE Already done inside the start-task function - jid = presence["from"].bare + jid = presence['from'].bare + # FIXME TODO Find out what is the source responsible for a couple presences with empty message + # NOTE This is a temporary solution + await asyncio.sleep(10) await task.start_tasks_xmpp(self, jid) async def on_presence_unsubscribed(self, presence): - await state.unsubscribed(self, presence) - jid = presence["from"].bare - await roster.remove(self, jid) + jid = presence['from'].bare + message_body = 'You have been unsubscribed.' + status_message = 'πŸ–‹οΈ Subscribe to receive updates' + XmppMessage.send(self, jid, message_body, 'chat') + XmppPresence.send(self, jid, status_message, + presence_type='unsubscribed') + await XmppRoster.remove(self, jid) async def on_presence_unavailable(self, presence): - jid = presence["from"].bare + jid = presence['from'].bare # await task.stop_tasks(self, jid) await task.clean_tasks_xmpp(jid) @@ -299,8 +345,8 @@ class Slixfeed(slixmpp.ClientXMPP): if message['type'] in ('chat', 'normal'): jid = message['from'].bare # await task.clean_tasks_xmpp(jid, ['status']) - status_text='Press "help" for manual, or "info" for information.' - status.send(self, jid, status_text) + status_message='Press "help" for manual, or "info" for information.' + XmppPresence.send(self, jid, status_message) async def on_chatstate_gone(self, message): @@ -324,6 +370,25 @@ class Slixfeed(slixmpp.ClientXMPP): await task.start_tasks_xmpp(self, jid, ['status']) + + # NOTE Failed attempt + # Need to use Super or Inheritance or both + # self['xep_0050'].add_command(node='settings', + # name='Settings', + # handler=self._handle_settings) + # self['xep_0050'].add_command(node='subscriptions', + # name='Subscriptions', + # handler=self._handle_subscriptions) + + + # async def _handle_settings(self, iq, session): + # await XmppCommand._handle_settings(self, iq, session) + + + # async def _handle_subscriptions(self, iq, session): + # await XmppCommand._handle_subscriptions(self, iq, session) + + # TODO Move class Service to a separate file # class Service(Slixfeed): # def __init__(self): @@ -534,7 +599,8 @@ class Slixfeed(slixmpp.ClientXMPP): value = True form.add_field(var='old', ftype='boolean', - desc='Mark items of newly added subscriptions as read.', + desc='Do not mark items of newly added subscriptions ' + 'as read.', # label='Send only new items', label='Include old news', value=value) diff --git a/slixfeed/xmpp/component.py b/slixfeed/xmpp/component.py index 7f02afa..fa1eadb 100644 --- a/slixfeed/xmpp/component.py +++ b/slixfeed/xmpp/component.py @@ -66,13 +66,16 @@ from slixfeed.xmpp.bookmark import XmppBookmark import slixfeed.xmpp.connect as connect # NOTE MUC is possible for component # import slixfeed.xmpp.muc as muc +from slixfeed.xmpp.message import XmppMessage import slixfeed.xmpp.process as process import slixfeed.xmpp.profile as profile # import slixfeed.xmpp.roster as roster # import slixfeed.xmpp.service as service -import slixfeed.xmpp.state as state -import slixfeed.xmpp.status as status -import slixfeed.xmpp.utility as utility +from slixfeed.xmpp.presence import XmppPresence +from slixfeed.xmpp.utility import XmppUtility +from slixmpp.xmlstream import ET +# from slixmpp.xmlstream.handler import Callback +# from slixmpp.xmlstream.matcher import MatchXPath main_task = [] jid_tasker = {} @@ -154,13 +157,15 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # handlers for connection events self.connection_attempts = 0 self.max_connection_attempts = 10 - self.add_event_handler("connection_failed", self.on_connection_failed) - self.add_event_handler("session_end", self.on_session_end) + self.add_event_handler('connection_failed', + self.on_connection_failed) + self.add_event_handler('session_end', + self.on_session_end) # async def on_groupchat_invite(self, message): # logging.warning("on_groupchat_invite") - # inviter = message["from"].bare + # inviter = message['from'].bare # muc_jid = message['groupchat_invite']['jid'] # await muc.join(self, inviter, muc_jid) # await bookmark.add(self, muc_jid) @@ -168,27 +173,27 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # NOTE Tested with Gajim and Psi # async def on_groupchat_direct_invite(self, message): - # inviter = message["from"].bare + # inviter = message['from'].bare # muc_jid = message['groupchat_invite']['jid'] # await muc.join(self, inviter, muc_jid) # await bookmark.add(self, muc_jid) async def on_session_end(self, event): - message = "Session has ended." + message = 'Session has ended.' await connect.recover_connection(self, message) async def on_connection_failed(self, event): - message = "Connection has failed. Reason: {}".format(event) + message = 'Connection has failed. Reason: {}'.format(event) await connect.recover_connection(self, message) async def on_session_start(self, event): self.send_presence() - await self["xep_0115"].update_caps() + await self['xep_0115'].update_caps() # await muc.autojoin(self) - profile.set_identity(self, "service") + profile.set_identity(self, 'service') await profile.update(self) task.ping_task(self) @@ -201,9 +206,9 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): async def on_session_resumed(self, event): self.send_presence() - self["xep_0115"].update_caps() + self['xep_0115'].update_caps() # await muc.autojoin(self) - profile.set_identity(self, "service") + profile.set_identity(self, 'service') # Service.commands(self) # Service.reactions(self) @@ -214,13 +219,21 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): # TODO Request for subscription async def on_message(self, message): - # jid = message["from"].bare - # if "chat" == await utility.get_chat_type(self, jid): - # await roster.add(self, jid) - # await state.request(self, jid) + # jid = message['from'].bare + + # if "chat" == await XmppUtility.get_chat_type(self, jid): + # presence_probe = ET.Element('presence') + # presence_probe.attrib['type'] = 'probe' + # presence_probe.attrib['to'] = jid + # print('presence_probe') + # print(presence_probe) + # self.send_raw(str(presence_probe)) + # presence_probe.send() + await process.message(self, message) - # chat_type = message["type"] - # message_body = message["body"] + + # chat_type = message['type'] + # message_body = message['body'] # message_reply = message.reply @@ -233,31 +246,40 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): async def on_presence_subscribe(self, presence): - jid = presence["from"].bare - # await state.request(self, jid) - self.send_presence_subscription( - pto=jid, - pfrom=self.boundjid.bare, - ptype="subscribe", - pnick=self.alias - ) + jid = presence['from'].bare + # XmppPresence.request(self, jid) + XmppPresence.subscribe(self, jid) async def on_presence_subscribed(self, presence): - jid = presence["from"].bare - process.greet(self, jid) + jid = presence['from'].bare + if await XmppUtility.get_chat_type(self, jid) == 'chat': + message_subject = 'RSS News Bot' + message_body = ('Greetings!\n' + 'I am {}, the news anchor.\n' + 'My job is to bring you the latest ' + 'news from sources you provide me with.\n' + 'You may always reach me via xmpp:{}?message' + .format(self.alias, self.boundjid.bare)) + XmppMessage.send_headline(self, jid, message_subject, message_body, + 'chat') async def on_presence_available(self, presence): # TODO Add function to check whether task is already running or not # await task.start_tasks(self, presence) # NOTE Already done inside the start-task function - jid = presence["from"].bare + jid = presence['from'].bare await task.start_tasks_xmpp(self, jid) async def on_presence_unsubscribed(self, presence): - await state.unsubscribed(self, presence) + jid = presence['from'].bare + message = 'You have been unsubscribed.' + status_message = 'πŸ–‹οΈ Subscribe to receive updates' + chat_type = await XmppUtility.get_chat_type(self, jid) + XmppMessage.send(self, jid, message, chat_type) + XmppPresence.send(self, jid, status_message, chat_type) async def on_presence_unavailable(self, presence): @@ -294,8 +316,9 @@ class SlixfeedComponent(slixmpp.ComponentXMPP): if message['type'] in ('chat', 'normal'): jid = message['from'].bare # await task.clean_tasks_xmpp(jid, ['status']) - status_text='Press "help" for manual, or "info" for information.' - status.send(self, jid, status_text) + status_message='Press "help" for manual, or "info" for information.' + chat_type = await XmppUtility.get_chat_type(self, jid) + XmppPresence.send(self, jid, status_message, chat_type) async def on_chatstate_gone(self, message): diff --git a/slixfeed/xmpp/connect.py b/slixfeed/xmpp/connect.py index 9f7d6bc..949e533 100644 --- a/slixfeed/xmpp/connect.py +++ b/slixfeed/xmpp/connect.py @@ -21,63 +21,65 @@ from time import sleep import logging -async def ping(self, jid=None): - """ - Check for ping and disconnect if no ping has been received. +class XmppConnect: - Parameters - ---------- - jid : str, optional - Jabber ID. The default is None. - Returns - ------- - None. + async def ping(self, jid=None): + """ + Check for ping and disconnect if no ping has been received. - """ - if not jid: - jid = self.boundjid.bare - while True: - rtt = None + Parameters + ---------- + jid : str, optional + Jabber ID. The default is None. + + Returns + ------- + None. + + """ + if not jid: + jid = self.boundjid.bare + while True: + rtt = None + try: + rtt = await self['xep_0199'].ping(jid, timeout=10) + logging.info("Success! RTT: %s", rtt) + except IqError as e: + logging.info("Error pinging %s: %s", + jid, + e.iq['error']['condition']) + except IqTimeout: + logging.info("No response from %s", jid) + if not rtt: + self.disconnect() + await asyncio.sleep(60 * 1) + + + async def recover(self, message): + logging.warning(message) + print(current_time(), message, "Attempting to reconnect.") + self.connection_attempts += 1 + # if self.connection_attempts <= self.max_connection_attempts: + # self.reconnect(wait=5.0) # wait a bit before attempting to reconnect + # else: + # print(current_time(),"Maximum connection attempts exceeded.") + # logging.error("Maximum connection attempts exceeded.") + print(current_time(), "Attempt number", self.connection_attempts) + seconds = (get_value("accounts", "XMPP", "reconnect_timeout")) or 30 + seconds = int(seconds) + print(current_time(), "Next attempt within", seconds, "seconds") + # NOTE asyncio.sleep doesn't interval as expected + # await asyncio.sleep(seconds) + sleep(seconds) + self.reconnect(wait=5.0) + + + async def inspect(self): + print('Disconnected\n' + 'Reconnecting...') try: - rtt = await self['xep_0199'].ping(jid, timeout=10) - logging.info("Success! RTT: %s", rtt) - except IqError as e: - logging.info("Error pinging %s: %s", - jid, - e.iq['error']['condition']) - except IqTimeout: - logging.info("No response from %s", jid) - if not rtt: + self.reconnect + except: self.disconnect() - await asyncio.sleep(60 * 1) - - -async def recover_connection(self, message): - logging.warning(message) - print(current_time(), message, "Attempting to reconnect.") - self.connection_attempts += 1 - # if self.connection_attempts <= self.max_connection_attempts: - # self.reconnect(wait=5.0) # wait a bit before attempting to reconnect - # else: - # print(current_time(),"Maximum connection attempts exceeded.") - # logging.error("Maximum connection attempts exceeded.") - print(current_time(), "Attempt number", self.connection_attempts) - seconds = (get_value( - "accounts", "XMPP", "reconnect_timeout")) or 30 - seconds = int(seconds) - print(current_time(), "Next attempt within", seconds, "seconds") - # NOTE asyncio.sleep doesn't interval as expected - # await asyncio.sleep(seconds) - sleep(seconds) - self.reconnect(wait=5.0) - - -async def inspect_connection(self, event): - print("Disconnected\nReconnecting...") - print(event) - try: - self.reconnect - except: - self.disconnect() - print("Problem reconnecting") + print('Problem reconnecting') diff --git a/slixfeed/xmpp/message.py b/slixfeed/xmpp/message.py index 378d67c..2c7cb52 100644 --- a/slixfeed/xmpp/message.py +++ b/slixfeed/xmpp/message.py @@ -1,37 +1,77 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import logging +import os +# import slixfeed.action as action +import slixfeed.config as config +from slixfeed.dt import current_time, timestamp +import slixfeed.fetch as fetch +import slixfeed.sqlite as sqlite +import slixfeed.task as task +import slixfeed.url as uri +from slixfeed.xmpp.bookmark import XmppBookmark +# from slixfeed.xmpp.muc import XmppGroupchat +# from slixfeed.xmpp.message import XmppMessage +from slixfeed.xmpp.presence import XmppPresence +from slixfeed.xmpp.upload import XmppUpload from slixfeed.xmpp.utility import get_chat_type +import time +""" + +NOTE + +See XEP-0367: Message Attaching + +""" class XmppMessage: - async def send(self, jid, message, chat_type=None): - if not chat_type: - chat_type = await get_chat_type(self, jid) - self.send_message( - mto=jid, - mfrom=self.boundjid.bare, - mbody=message, - mtype=chat_type - ) + + # def process(): - async def send_oob(self, jid, url): - chat_type = await get_chat_type(self, jid) + def send(self, jid, message_body, chat_type): + self.send_message(mto=jid, + mfrom=self.boundjid.bare, + mbody=message_body, + mtype=chat_type) + + + def send_headline(self, jid, message_subject, message_body, chat_type): + self.send_message(mto=jid, + mfrom=self.boundjid.bare, + # mtype='headline', + msubject=message_subject, + mbody=message_body, + mtype=chat_type, + mnick=self.alias) + + + def send_oob(self, jid, url, chat_type): html = ( f'' f'{url}') - message = self.make_message( - mto=jid, - mfrom=self.boundjid.bare, - mbody=url, - mhtml=html, - mtype=chat_type - ) + message = self.make_message(mto=jid, + mfrom=self.boundjid.bare, + mbody=url, + mhtml=html, + mtype=chat_type) message['oob']['url'] = url message.send() + # FIXME Solve this function + def send_oob_reply_message(message, url, response): + reply = message.reply(response) + reply['oob']['url'] = url + reply.send() + + + # def send_reply(self, message, message_body): + # message.reply(message_body).send() + + def send_reply(self, message, response): - message.reply(response).send() \ No newline at end of file + message.reply(response).send() diff --git a/slixfeed/xmpp/muc.py b/slixfeed/xmpp/muc.py index f699eeb..8dcb100 100644 --- a/slixfeed/xmpp/muc.py +++ b/slixfeed/xmpp/muc.py @@ -17,102 +17,90 @@ FIXME """ import logging -import slixfeed.xmpp.process as process from slixfeed.dt import current_time +from slixfeed.xmpp.message import XmppMessage -# async def accept_muc_invite(self, message, ctr=None): -# # if isinstance(message, str): -# if not ctr: -# ctr = message["from"].bare -# jid = message['groupchat_invite']['jid'] -# else: -# jid = message -async def accept_invitation(self, message): - # operator muc_chat - inviter = message["from"].bare - muc_jid = message['groupchat_invite']['jid'] - await join(self, inviter, muc_jid) +class XmppGroupchat: + + # async def accept_muc_invite(self, message, ctr=None): + # # if isinstance(message, str): + # if not ctr: + # ctr = message["from"].bare + # jid = message['groupchat_invite']['jid'] + # else: + # jid = message + async def accept_invitation(self, message): + # operator muc_chat + inviter = message["from"].bare + muc_jid = message['groupchat_invite']['jid'] + await self.join(self, inviter, muc_jid) -async def autojoin(self): - result = await self.plugin['xep_0048'].get_bookmarks() - bookmarks = result["private"]["bookmarks"] - conferences = bookmarks["conferences"] - for conference in conferences: - if conference["autojoin"]: - muc_jid = conference["jid"] - logging.info( - 'Autojoin groupchat\n' - 'Name : {}\n' - 'JID : {}\n' - 'Alias : {}\n' - .format( - conference["name"], - muc_jid, - conference["nick"] - )) - self.plugin['xep_0045'].join_muc( - muc_jid, - conference["nick"], - # If a room password is needed, use: - # password=the_room_password, - ) - -async def join(self, inviter, muc_jid): - # token = await initdb( - # muc_jid, - # get_settings_value, - # "token" - # ) - # if token != "accepted": - # token = randrange(10000, 99999) - # await initdb( - # muc_jid, - # update_settings_value, - # ["token", token] - # ) - # self.send_message( - # mto=inviter, - # mfrom=self.boundjid.bare, - # mbody=( - # "Send activation token {} to groupchat xmpp:{}?join." - # ).format(token, muc_jid) - # ) - logging.info( - 'Joining groupchat\n' - 'JID : {}\n' - 'Inviter : {}\n' - .format( - muc_jid, - inviter - )) - self.plugin['xep_0045'].join_muc( - muc_jid, - self.alias, - # If a room password is needed, use: - # password=the_room_password, - ) - process.greet(self, muc_jid, chat_type="groupchat") + async def autojoin(self): + result = await self.plugin['xep_0048'].get_bookmarks() + bookmarks = result["private"]["bookmarks"] + conferences = bookmarks["conferences"] + for conference in conferences: + if conference["autojoin"]: + muc_jid = conference["jid"] + self.plugin['xep_0045'].join_muc(muc_jid, + conference["nick"], + # If a room password is needed, use: + # password=the_room_password, + ) + logging.info('Autojoin groupchat\n' + 'Name : {}\n' + 'JID : {}\n' + 'Alias : {}\n' + .format(conference["name"], + muc_jid, + conference["nick"])) -async def leave(self, muc_jid): - messages = [ - "Whenever you need an RSS service again, " - "please don’t hesitate to contact me.", - "My personal contact is xmpp:{}?message".format(self.boundjid.bare), - "Farewell, and take care." - ] - for message in messages: - self.send_message( - mto=muc_jid, - mfrom=self.boundjid.bare, - mbody=message, - mtype="groupchat" - ) - self.plugin['xep_0045'].leave_muc( - muc_jid, - self.alias, - "Goodbye!", - self.boundjid.bare - ) + async def join(self, inviter, muc_jid): + # token = await initdb( + # muc_jid, + # get_settings_value, + # "token" + # ) + # if token != "accepted": + # token = randrange(10000, 99999) + # await initdb( + # muc_jid, + # update_settings_value, + # ["token", token] + # ) + # self.send_message( + # mto=inviter, + # mfrom=self.boundjid.bare, + # mbody=( + # "Send activation token {} to groupchat xmpp:{}?join." + # ).format(token, muc_jid) + # ) + logging.info('Joining groupchat\n' + 'JID : {}\n' + 'Inviter : {}\n' + .format(muc_jid, inviter)) + self.plugin['xep_0045'].join_muc(muc_jid, + self.alias, + # If a room password is needed, use: + # password=the_room_password, + ) + + async def leave(self, muc_jid): + messages = [ + "Whenever you need an RSS service again, " + "please don’t hesitate to contact me.", + "My JID is xmpp:{}?message".format(self.boundjid.bare), + "Farewell." + ] + for message in messages: + self.send_message(mto=muc_jid, + mfrom=self.boundjid.bare, + mbody=message, + mtype='groupchat') + self.plugin['xep_0045'].leave_muc(muc_jid, + self.alias, + 'Goodbye!', + self.boundjid.bare) diff --git a/slixfeed/xmpp/presence.py b/slixfeed/xmpp/presence.py new file mode 100644 index 0000000..b586408 --- /dev/null +++ b/slixfeed/xmpp/presence.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" + +NOTE + +Accept symbols πŸ‰‘οΈ πŸ‘οΈ ✍ + +""" + +class XmppPresence: + + + def send(self, jid, status_message, presence_type=None, status_type=None): + self.send_presence(pto=jid, + pfrom=self.boundjid.bare, + pshow=status_type, + pstatus=status_message, + ptype=presence_type) + + + def subscribe(self, jid): + self.send_presence_subscription(pto=jid, + pfrom=self.boundjid.bare, + ptype='subscribe', + pnick=self.alias) + + + def remove(self): + """ + Remove subscription from JID that do not (stopped) share presence. + + Returns + ------- + None. + """ diff --git a/slixfeed/xmpp/process.py b/slixfeed/xmpp/process.py index a822323..b166ba0 100644 --- a/slixfeed/xmpp/process.py +++ b/slixfeed/xmpp/process.py @@ -9,7 +9,7 @@ TODO Slixfeed: Do you still want to add this URL to subscription list? See: case _ if message_lowercase.startswith("add"): -2) If subscription is inadequate (see state.request), send a message that says so. +2) If subscription is inadequate (see XmppPresence.request), send a message that says so. elif not self.client_roster[jid]["to"]: breakpoint() @@ -28,9 +28,10 @@ import slixfeed.sqlite as sqlite import slixfeed.task as task import slixfeed.url as uri from slixfeed.xmpp.bookmark import XmppBookmark -import slixfeed.xmpp.muc as groupchat -from slixfeed.xmpp.status import XmppStatus -import slixfeed.xmpp.upload as upload +from slixfeed.xmpp.muc import XmppGroupchat +from slixfeed.xmpp.message import XmppMessage +from slixfeed.xmpp.presence import XmppPresence +from slixfeed.xmpp.upload import XmppUpload from slixfeed.xmpp.utility import get_chat_type import time @@ -88,7 +89,8 @@ async def message(self, message): await task.clean_tasks_xmpp(jid, ['status']) status_type = 'dnd' status_message = 'πŸ“₯️ Procesing request to import feeds...' - await XmppStatus.send(self, jid, status_message, status_type) + XmppPresence.send(self, jid, status_message, + status_type=status_type) db_file = config.get_pathname_to_database(jid_file) count = await action.import_opml(db_file, url) if count: @@ -97,7 +99,7 @@ async def message(self, message): response = 'OPML file was not imported.' # await task.clean_tasks_xmpp(jid, ['status']) await task.start_tasks_xmpp(self, jid, ['status']) - send_reply_message(self, message, response) + XmppMessage.send_reply(self, message, response) return @@ -190,7 +192,7 @@ async def message(self, message): # 'This action is restricted. ' # 'Type: breakpoint.' # ) - # send_reply_message(self, message, response) + # XmppMessage.send_reply(self, message, response) case 'help': command_list = ' '.join(action.manual('commands.toml')) response = ('Available command keys:\n' @@ -198,7 +200,7 @@ async def message(self, message): 'Usage: `help `' .format(command_list)) print(response) - send_reply_message(self, message, response) + XmppMessage.send_reply(self, message, response) case _ if message_lowercase.startswith('help'): command = message_text[5:] command = command.split(' ') @@ -228,14 +230,14 @@ async def message(self, message): else: response = ('Invalid. Enter command key ' 'or command key & name') - send_reply_message(self, message, response) + XmppMessage.send_reply(self, message, response) case 'info': command_list = ' '.join(action.manual('information.toml')) response = ('Available command options:\n' '```\n{}\n```\n' 'Usage: `info