Fix presence and subscription handling.
Segregate and atomize into classes.
This commit is contained in:
parent
422e0669f1
commit
00a8ed180a
18 changed files with 734 additions and 782 deletions
|
@ -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
|
||||
if readability:
|
||||
try:
|
||||
document = Document(data)
|
||||
content = document.summary()
|
||||
except:
|
||||
content = data
|
||||
logging.warning('Check that package readability is installed.')
|
||||
else:
|
||||
content = data
|
||||
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):
|
||||
|
|
|
@ -52,9 +52,14 @@ Send all items of newly added feeds.
|
|||
"""
|
||||
|
||||
[document]
|
||||
get = """
|
||||
get <id> <type>
|
||||
Send an article as file. Specify <id> and <type>.
|
||||
content = """
|
||||
content <id>/<url> <type>
|
||||
Send a readability (arc90) version of an article as file. Specify <id> or <url> and <type>.
|
||||
Supported types are ePUB, HTML, MD and PDF (default).
|
||||
"""
|
||||
page = """
|
||||
page <id>/<url> <type>
|
||||
Send an article as file. Specify <id> or <url> and <type>.
|
||||
Supported types are ePUB, HTML, MD and PDF (default).
|
||||
"""
|
||||
|
||||
|
|
|
@ -116,6 +116,7 @@ Kevin Smith <isode.com> (Swift IM, Wales), \
|
|||
Luis Henrique Mello (SalixOS, Brazil), \
|
||||
magicfelix, \
|
||||
Markus Muttilainen (SalixOS), \
|
||||
Martin <debacle@debian.org> (Debian, Germany), \
|
||||
Mathieu Pasquet (slixmpp, France), \
|
||||
Maxime Buquet (slixmpp, France), \
|
||||
Phillip Watkins (United Kingdom, SalixOS), \
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -34,11 +34,9 @@ class XmppBookmark:
|
|||
bookmarks = Bookmarks()
|
||||
mucs.extend([muc_jid])
|
||||
for muc in mucs:
|
||||
bookmarks.add_conference(
|
||||
muc,
|
||||
bookmarks.add_conference(muc,
|
||||
self.alias,
|
||||
autojoin=True
|
||||
)
|
||||
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,
|
||||
bookmarks.add_conference(muc,
|
||||
self.alias,
|
||||
autojoin=True
|
||||
)
|
||||
autojoin=True)
|
||||
await self.plugin['xep_0048'].set_bookmarks(bookmarks)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -21,7 +21,10 @@ from time import sleep
|
|||
import logging
|
||||
|
||||
|
||||
async def ping(self, jid=None):
|
||||
class XmppConnect:
|
||||
|
||||
|
||||
async def ping(self, jid=None):
|
||||
"""
|
||||
Check for ping and disconnect if no ping has been received.
|
||||
|
||||
|
@ -53,7 +56,7 @@ async def ping(self, jid=None):
|
|||
await asyncio.sleep(60 * 1)
|
||||
|
||||
|
||||
async def recover_connection(self, message):
|
||||
async def recover(self, message):
|
||||
logging.warning(message)
|
||||
print(current_time(), message, "Attempting to reconnect.")
|
||||
self.connection_attempts += 1
|
||||
|
@ -63,8 +66,7 @@ async def recover_connection(self, message):
|
|||
# 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 = (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
|
||||
|
@ -73,11 +75,11 @@ async def recover_connection(self, message):
|
|||
self.reconnect(wait=5.0)
|
||||
|
||||
|
||||
async def inspect_connection(self, event):
|
||||
print("Disconnected\nReconnecting...")
|
||||
print(event)
|
||||
async def inspect(self):
|
||||
print('Disconnected\n'
|
||||
'Reconnecting...')
|
||||
try:
|
||||
self.reconnect
|
||||
except:
|
||||
self.disconnect()
|
||||
print("Problem reconnecting")
|
||||
print('Problem reconnecting')
|
||||
|
|
|
@ -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,
|
||||
|
||||
# def process():
|
||||
|
||||
|
||||
def send(self, jid, message_body, chat_type):
|
||||
self.send_message(mto=jid,
|
||||
mfrom=self.boundjid.bare,
|
||||
mbody=message,
|
||||
mtype=chat_type
|
||||
)
|
||||
mbody=message_body,
|
||||
mtype=chat_type)
|
||||
|
||||
|
||||
async def send_oob(self, jid, url):
|
||||
chat_type = await get_chat_type(self, jid)
|
||||
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'<body xmlns="http://www.w3.org/1999/xhtml">'
|
||||
f'<a href="{url}">{url}</a></body>')
|
||||
message = self.make_message(
|
||||
mto=jid,
|
||||
message = self.make_message(mto=jid,
|
||||
mfrom=self.boundjid.bare,
|
||||
mbody=url,
|
||||
mhtml=html,
|
||||
mtype=chat_type
|
||||
)
|
||||
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()
|
|
@ -17,48 +17,47 @@ 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):
|
||||
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 join(self, inviter, muc_jid)
|
||||
await self.join(self, inviter, muc_jid)
|
||||
|
||||
|
||||
async def autojoin(self):
|
||||
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,
|
||||
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 join(self, inviter, muc_jid):
|
||||
|
||||
async def join(self, inviter, muc_jid):
|
||||
# token = await initdb(
|
||||
# muc_jid,
|
||||
# get_settings_value,
|
||||
|
@ -78,41 +77,30 @@ async def join(self, inviter, muc_jid):
|
|||
# "Send activation token {} to groupchat xmpp:{}?join."
|
||||
# ).format(token, muc_jid)
|
||||
# )
|
||||
logging.info(
|
||||
'Joining groupchat\n'
|
||||
logging.info('Joining groupchat\n'
|
||||
'JID : {}\n'
|
||||
'Inviter : {}\n'
|
||||
.format(
|
||||
muc_jid,
|
||||
inviter
|
||||
))
|
||||
self.plugin['xep_0045'].join_muc(
|
||||
muc_jid,
|
||||
.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 leave(self, muc_jid):
|
||||
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."
|
||||
"My JID is xmpp:{}?message".format(self.boundjid.bare),
|
||||
"Farewell."
|
||||
]
|
||||
for message in messages:
|
||||
self.send_message(
|
||||
mto=muc_jid,
|
||||
self.send_message(mto=muc_jid,
|
||||
mfrom=self.boundjid.bare,
|
||||
mbody=message,
|
||||
mtype="groupchat"
|
||||
)
|
||||
self.plugin['xep_0045'].leave_muc(
|
||||
muc_jid,
|
||||
mtype='groupchat')
|
||||
self.plugin['xep_0045'].leave_muc(muc_jid,
|
||||
self.alias,
|
||||
"Goodbye!",
|
||||
self.boundjid.bare
|
||||
)
|
||||
|
||||
'Goodbye!',
|
||||
self.boundjid.bare)
|
||||
|
|
37
slixfeed/xmpp/presence.py
Normal file
37
slixfeed/xmpp/presence.py
Normal file
|
@ -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.
|
||||
"""
|
|
@ -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 <key>`'
|
||||
.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 <option>`'
|
||||
.format(command_list))
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('info'):
|
||||
command = message_text[5:]
|
||||
command_list = action.manual('information.toml', command)
|
||||
|
@ -245,7 +247,7 @@ async def message(self, message):
|
|||
else:
|
||||
response = ('KeyError for {}'
|
||||
.format(command))
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase in ['greetings', 'hallo', 'hello',
|
||||
'hey', 'hi', 'hola', 'holla',
|
||||
'hollo']:
|
||||
|
@ -253,7 +255,7 @@ async def message(self, message):
|
|||
'I am an RSS News Bot.\n'
|
||||
'Send "help" for further instructions.\n'
|
||||
.format(self.alias))
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
|
||||
# case _ if message_lowercase.startswith('activate'):
|
||||
# if message['type'] == 'groupchat':
|
||||
|
@ -315,7 +317,7 @@ async def message(self, message):
|
|||
.format(url, name, ix))
|
||||
else:
|
||||
response = 'Missing URL.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('allow +'):
|
||||
key = 'filter-' + message_text[:5]
|
||||
val = message_text[7:]
|
||||
|
@ -333,7 +335,7 @@ async def message(self, message):
|
|||
.format(val))
|
||||
else:
|
||||
response = 'Missing keywords.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('allow -'):
|
||||
key = 'filter-' + message_text[:5]
|
||||
val = message_text[7:]
|
||||
|
@ -351,7 +353,7 @@ async def message(self, message):
|
|||
.format(val))
|
||||
else:
|
||||
response = 'Missing keywords.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('archive'):
|
||||
key = message_text[:7]
|
||||
val = message_text[8:]
|
||||
|
@ -375,7 +377,7 @@ async def message(self, message):
|
|||
response = 'Enter a numeric value only.'
|
||||
else:
|
||||
response = 'Missing value.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('bookmark -'):
|
||||
if jid == config.get_value('accounts', 'XMPP', 'operator'):
|
||||
muc_jid = message_text[11:]
|
||||
|
@ -386,14 +388,14 @@ async def message(self, message):
|
|||
else:
|
||||
response = ('This action is restricted. '
|
||||
'Type: removing bookmarks.')
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case 'bookmarks':
|
||||
if jid == config.get_value('accounts', 'XMPP', 'operator'):
|
||||
response = await action.list_bookmarks(self)
|
||||
else:
|
||||
response = ('This action is restricted. '
|
||||
'Type: viewing bookmarks.')
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('deny +'):
|
||||
key = 'filter-' + message_text[:4]
|
||||
val = message_text[6:]
|
||||
|
@ -411,7 +413,7 @@ async def message(self, message):
|
|||
.format(val))
|
||||
else:
|
||||
response = 'Missing keywords.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('deny -'):
|
||||
key = 'filter-' + message_text[:4]
|
||||
val = message_text[6:]
|
||||
|
@ -429,7 +431,7 @@ async def message(self, message):
|
|||
.format(val))
|
||||
else:
|
||||
response = 'Missing keywords.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('export'):
|
||||
ex = message_text[7:]
|
||||
if ex in ('opml', 'html', 'md', 'xbel'):
|
||||
|
@ -437,8 +439,8 @@ async def message(self, message):
|
|||
status_message = ('📤️ Procesing request to '
|
||||
'export feeds into {}...'
|
||||
.format(ex))
|
||||
await XmppStatus.send(self, jid, status_message,
|
||||
status_type)
|
||||
XmppPresence.send(self, jid, status_message,
|
||||
status_type=status_type)
|
||||
cache_dir = config.get_default_cache_directory()
|
||||
if not os.path.isdir(cache_dir):
|
||||
os.mkdir(cache_dir)
|
||||
|
@ -457,105 +459,33 @@ async def message(self, message):
|
|||
action.export_to_opml(jid, filename, results)
|
||||
case 'xbel':
|
||||
response = 'Not yet implemented.'
|
||||
url = await upload.start(self, jid, filename)
|
||||
url = await XmppUpload.start(self, jid, filename)
|
||||
# response = (
|
||||
# 'Feeds exported successfully to {}.\n{}'
|
||||
# ).format(ex, url)
|
||||
# send_oob_reply_message(message, url, response)
|
||||
await send_oob_message(self, jid, url)
|
||||
# XmppMessage.send_oob_reply_message(message, url, response)
|
||||
chat_type = await get_chat_type(self, jid)
|
||||
XmppMessage.send_oob(self, jid, url, chat_type)
|
||||
await task.start_tasks_xmpp(self, jid, ['status'])
|
||||
else:
|
||||
response = ('Unsupported filetype.\n'
|
||||
'Try: html, md, opml, or xbel')
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if (message_lowercase.startswith('gemini:') or
|
||||
message_lowercase.startswith('gopher:')):
|
||||
response = 'Gemini and Gopher are not supported yet.'
|
||||
send_reply_message(self, message, response)
|
||||
# TODO xHTML, HTMLZ, Markdown, MHTML, PDF, TXT
|
||||
case _ if (message_lowercase.startswith('get')):
|
||||
message_text = message_text[4:]
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
# TODO xHTML, HTMLZ, MHTML
|
||||
case _ if (message_lowercase.startswith('page')):
|
||||
message_text = message_text[5:]
|
||||
ix_url = message_text.split(' ')[0]
|
||||
ext = ' '.join(message_text.split(' ')[1:])
|
||||
ext = ext if ext else 'pdf'
|
||||
url = None
|
||||
error = 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()))
|
||||
await XmppStatus.send(self, jid, status_message,
|
||||
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 = 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 + '_' + timestamp() + '.' + ext)
|
||||
error = action.generate_document(data, url,
|
||||
ext, filename)
|
||||
if error:
|
||||
response = ('> {}\n'
|
||||
'Failed to export {}. '
|
||||
'Reason: {}'
|
||||
.format(url, ext.upper(),
|
||||
error))
|
||||
else:
|
||||
url = await upload.start(self, jid,
|
||||
filename)
|
||||
await send_oob_message(self, jid, url)
|
||||
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))
|
||||
send_reply_message(self, message, response)
|
||||
await action.download_document(self, message, jid, jid_file,
|
||||
message_text, ix_url, False)
|
||||
case _ if (message_lowercase.startswith('content')):
|
||||
message_text = message_text[8:]
|
||||
ix_url = message_text.split(' ')[0]
|
||||
await action.download_document(self, message, jid, jid_file,
|
||||
message_text, ix_url, True)
|
||||
# case _ if (message_lowercase.startswith('http')) and(
|
||||
# message_lowercase.endswith('.opml')):
|
||||
# url = message_text
|
||||
|
@ -565,8 +495,8 @@ async def message(self, message):
|
|||
# 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:
|
||||
|
@ -581,7 +511,7 @@ async def message(self, message):
|
|||
# jid, ['status'])
|
||||
# await task.start_tasks_xmpp(
|
||||
# self, jid, ['status'])
|
||||
# send_reply_message(self, message, response)
|
||||
# XmppMessage.send_reply(self, message, response)
|
||||
case _ if (message_lowercase.startswith('http') or
|
||||
message_lowercase.startswith('feed:')):
|
||||
url = message_text
|
||||
|
@ -590,7 +520,8 @@ async def message(self, message):
|
|||
status_message = ('📫️ Processing request '
|
||||
'to fetch data from {}'
|
||||
.format(url))
|
||||
await XmppStatus.send(self, jid, status_message, status_type)
|
||||
XmppPresence.send(self, jid, status_message,
|
||||
status_type=status_type)
|
||||
if url.startswith('feed:'):
|
||||
url = uri.feed_to_http(url)
|
||||
url = (uri.replace_hostname(url, 'feed')) or url
|
||||
|
@ -605,7 +536,7 @@ async def message(self, message):
|
|||
# 'of being added to the subscription '
|
||||
# 'list.'.format(url)
|
||||
# )
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('feeds'):
|
||||
query = message_text[6:]
|
||||
if query:
|
||||
|
@ -619,14 +550,14 @@ async def message(self, message):
|
|||
db_file = config.get_pathname_to_database(jid_file)
|
||||
result = await sqlite.get_feeds(db_file)
|
||||
response = action.list_feeds(result)
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case 'goodbye':
|
||||
if message['type'] == 'groupchat':
|
||||
await groupchat.leave(self, jid)
|
||||
await XmppGroupchat.leave(self, jid)
|
||||
await XmppBookmark.remove(self, muc_jid)
|
||||
else:
|
||||
response = 'This command is valid in groupchat only.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('interval'):
|
||||
key = message_text[:8]
|
||||
val = message_text[9:]
|
||||
|
@ -636,14 +567,14 @@ async def message(self, message):
|
|||
muc_jid = uri.check_xmpp_uri(message_text[5:])
|
||||
if muc_jid:
|
||||
# TODO probe JID and confirm it's a groupchat
|
||||
await groupchat.join(self, jid, muc_jid)
|
||||
await XmppGroupchat.join(self, jid, muc_jid)
|
||||
response = ('Joined groupchat {}'
|
||||
.format(message_text))
|
||||
else:
|
||||
response = ('> {}\n'
|
||||
'XMPP URI is not valid.'
|
||||
.format(message_text))
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('length'):
|
||||
key = message_text[:6]
|
||||
val = message_text[7:]
|
||||
|
@ -692,7 +623,7 @@ async def message(self, message):
|
|||
# ).format(val)
|
||||
# else:
|
||||
# response = 'Missing value.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case 'new':
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
key = 'old'
|
||||
|
@ -702,7 +633,7 @@ async def message(self, message):
|
|||
else:
|
||||
await sqlite.set_settings_value(db_file, [key, val])
|
||||
response = 'Only new items of newly added feeds will be sent.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
# TODO Will you add support for number of messages?
|
||||
case 'next':
|
||||
# num = message_text[5:]
|
||||
|
@ -739,7 +670,7 @@ async def message(self, message):
|
|||
else:
|
||||
await sqlite.set_settings_value(db_file, [key, val])
|
||||
response = 'All items of newly added feeds will be sent.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('quantum'):
|
||||
key = message_text[:7]
|
||||
val = message_text[8:]
|
||||
|
@ -762,12 +693,12 @@ async def message(self, message):
|
|||
response = 'Enter a numeric value only.'
|
||||
else:
|
||||
response = 'Missing value.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case 'random':
|
||||
# TODO /questions/2279706/select-random-row-from-a-sqlite-table
|
||||
# NOTE sqlitehandler.get_entry_unread
|
||||
response = 'Updates will be sent by random order.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('read'):
|
||||
data = message_text[5:]
|
||||
data = data.split()
|
||||
|
@ -776,7 +707,8 @@ async def message(self, message):
|
|||
status_type = 'dnd'
|
||||
status_message = ('📫️ Processing request to fetch data from {}'
|
||||
.format(url))
|
||||
await XmppStatus.send(self, jid, status_message, status_type)
|
||||
XmppPresence.send(self, jid, status_message,
|
||||
status_type=status_type)
|
||||
if url.startswith('feed:'):
|
||||
url = uri.feed_to_http(url)
|
||||
url = (uri.replace_hostname(url, 'feed')) or url
|
||||
|
@ -796,7 +728,7 @@ async def message(self, message):
|
|||
response = ('Enter command as follows:\n'
|
||||
'`read <url>` or `read <url> <number>`\n'
|
||||
'URL must not contain white space.')
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
await task.start_tasks_xmpp(self, jid, ['status'])
|
||||
case _ if message_lowercase.startswith('recent'):
|
||||
num = message_text[7:]
|
||||
|
@ -813,7 +745,7 @@ async def message(self, message):
|
|||
response = 'Enter a numeric value only.'
|
||||
else:
|
||||
response = 'Missing value.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('remove'):
|
||||
ix_url = message_text[7:]
|
||||
if ix_url:
|
||||
|
@ -859,14 +791,15 @@ async def message(self, message):
|
|||
await task.start_tasks_xmpp(self, jid, ['status'])
|
||||
else:
|
||||
response = 'Missing feed URL or index number.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('reset'):
|
||||
# TODO Reset also by ID
|
||||
ix_url = message_text[6:]
|
||||
await task.clean_tasks_xmpp(jid, ['status'])
|
||||
status_type = 'dnd'
|
||||
status_message = '📫️ Marking entries as read...'
|
||||
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)
|
||||
if ix_url:
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
|
@ -905,7 +838,7 @@ async def message(self, message):
|
|||
else:
|
||||
await sqlite.mark_all_as_read(db_file)
|
||||
response = 'All entries have been marked as read.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
await task.start_tasks_xmpp(self, jid, ['status'])
|
||||
case _ if message_lowercase.startswith('search'):
|
||||
query = message_text[7:]
|
||||
|
@ -918,25 +851,13 @@ async def message(self, message):
|
|||
response = 'Enter at least 2 characters to search'
|
||||
else:
|
||||
response = 'Missing search query.'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case 'start':
|
||||
# response = 'Updates are enabled.'
|
||||
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])
|
||||
await task.start_tasks_xmpp(self, jid)
|
||||
response = 'Updates are enabled.'
|
||||
# print(current_time(), 'task_manager[jid]')
|
||||
# print(task_manager[jid])
|
||||
send_reply_message(self, message, response)
|
||||
await action.xmpp_start_updates(self, message, jid, jid_file)
|
||||
case 'stats':
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
response = await action.list_statistics(db_file)
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('disable '):
|
||||
ix = message_text[8:]
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
|
@ -946,7 +867,7 @@ async def message(self, message):
|
|||
.format(ix))
|
||||
except:
|
||||
response = 'No news source with index {}.'.format(ix)
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('enable'):
|
||||
ix = message_text[7:]
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
|
@ -956,29 +877,29 @@ async def message(self, message):
|
|||
.format(ix))
|
||||
except:
|
||||
response = 'No news source with index {}.'.format(ix)
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case 'stop':
|
||||
await action.xmpp_stop_updates(self, message, jid, jid_file)
|
||||
case 'support':
|
||||
# TODO Send an invitation.
|
||||
response = 'Join xmpp:slixfeed@chat.woodpeckersnest.space?join'
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('xmpp:'):
|
||||
muc_jid = uri.check_xmpp_uri(message_text)
|
||||
if muc_jid:
|
||||
# TODO probe JID and confirm it's a groupchat
|
||||
await groupchat.join(self, jid, muc_jid)
|
||||
await XmppGroupchat.join(self, jid, muc_jid)
|
||||
response = ('Joined groupchat {}'
|
||||
.format(message_text))
|
||||
else:
|
||||
response = ('> {}\n'
|
||||
'XMPP URI is not valid.'
|
||||
.format(message_text))
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _:
|
||||
response = ('Unknown command. '
|
||||
'Press "help" for list of commands')
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
# TODO Use message correction here
|
||||
# NOTE This might not be a good idea if
|
||||
# commands are sent one close to the next
|
||||
|
@ -988,7 +909,7 @@ async def message(self, message):
|
|||
command_time_total = command_time_finish - command_time_start
|
||||
command_time_total = round(command_time_total, 3)
|
||||
response = 'Finished. Total time: {}s'.format(command_time_total)
|
||||
send_reply_message(self, message, response)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
|
||||
if not response: response = 'EMPTY MESSAGE - ACTION ONLY'
|
||||
data_dir = config.get_default_data_directory()
|
||||
|
@ -1010,67 +931,3 @@ async def message(self, message):
|
|||
'{}\n'
|
||||
.format(message_text, jid, jid_file, response)
|
||||
)
|
||||
|
||||
|
||||
def send_reply_message(self, message, response):
|
||||
message.reply(response).send()
|
||||
|
||||
|
||||
# TODO Solve this function
|
||||
def send_oob_reply_message(message, url, response):
|
||||
reply = message.reply(response)
|
||||
reply['oob']['url'] = url
|
||||
reply.send()
|
||||
|
||||
|
||||
async def send_oob_message(self, jid, url):
|
||||
chat_type = await get_chat_type(self, jid)
|
||||
html = (
|
||||
f'<body xmlns="http://www.w3.org/1999/xhtml">'
|
||||
f'<a href="{url}">{url}</a></body>')
|
||||
message = self.make_message(
|
||||
mto=jid,
|
||||
mfrom=self.boundjid.bare,
|
||||
mbody=url,
|
||||
mhtml=html,
|
||||
mtype=chat_type
|
||||
)
|
||||
message['oob']['url'] = url
|
||||
message.send()
|
||||
|
||||
|
||||
# def greet(self, jid, chat_type='chat'):
|
||||
# messages = [
|
||||
# 'Greetings!',
|
||||
# 'I'm {}, the news anchor.'.format(self.nick),
|
||||
# 'My job is to bring you the latest news '
|
||||
# 'from sources you provide me with.',
|
||||
# 'You may always reach me via '
|
||||
# 'xmpp:{}?message'.format(self.boundjid.bare)
|
||||
# ]
|
||||
# for message in messages:
|
||||
# self.send_message(
|
||||
# mto=jid,
|
||||
# mfrom=self.boundjid.bare,
|
||||
# mbody=message,
|
||||
# mtype=chat_type
|
||||
# )
|
||||
|
||||
|
||||
def greet(self, jid, chat_type='chat'):
|
||||
message = (
|
||||
'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
|
||||
)
|
||||
self.send_message(
|
||||
mto=jid,
|
||||
mfrom=self.boundjid.bare,
|
||||
mbody=message,
|
||||
mtype=chat_type
|
||||
)
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ TODO
|
|||
|
||||
"""
|
||||
|
||||
import slixfeed.xmpp.utility as utility
|
||||
class XmppRoster:
|
||||
|
||||
async def remove(self, jid):
|
||||
async def remove(self, jid):
|
||||
"""
|
||||
Remove JID to roster.
|
||||
|
||||
|
@ -25,16 +25,15 @@ async def remove(self, jid):
|
|||
-------
|
||||
None.
|
||||
"""
|
||||
self.update_roster(
|
||||
jid,
|
||||
subscription="remove"
|
||||
)
|
||||
self.update_roster(jid, subscription="remove")
|
||||
|
||||
|
||||
async def add(self, jid):
|
||||
async def add(self, jid):
|
||||
"""
|
||||
Add JID to roster.
|
||||
|
||||
Add JID to roster if it is not already in roster.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
jid : str
|
||||
|
@ -44,31 +43,7 @@ async def add(self, jid):
|
|||
-------
|
||||
None.
|
||||
"""
|
||||
if await utility.get_chat_type(self, jid) == "groupchat":
|
||||
# Check whether JID is in bookmarks; otherwise, add it.
|
||||
print(jid, "is muc")
|
||||
return
|
||||
else:
|
||||
await self.get_roster()
|
||||
# Check whether JID is in roster; otherwise, add it.
|
||||
if jid not in self.client_roster.keys():
|
||||
self.send_presence_subscription(
|
||||
pto=jid,
|
||||
pfrom=self.boundjid.bare,
|
||||
ptype="subscribe",
|
||||
pnick=self.alias
|
||||
)
|
||||
self.update_roster(
|
||||
jid,
|
||||
subscription="both"
|
||||
)
|
||||
self.update_roster(jid, subscription="both")
|
||||
|
||||
|
||||
def remove_subscription(self):
|
||||
"""
|
||||
Remove subscription from contacts that don't share their presence.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None.
|
||||
"""
|
|
@ -1,71 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from slixmpp.xmlstream.matcher import MatchXPath
|
||||
from slixmpp.xmlstream.handler import Callback
|
||||
from slixmpp.xmlstream import ET
|
||||
|
||||
async def request(self, jid):
|
||||
"""
|
||||
Ask contant to settle subscription.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
jid : str
|
||||
Jabber ID.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None.
|
||||
"""
|
||||
# Check whether JID is subscribed; otherwise, ask for presence.
|
||||
if self.is_component:
|
||||
presence_probe = ET.Element('presence')
|
||||
presence_probe.attrib['type'] = 'probe'
|
||||
presence_probe.attrib['to'] = jid
|
||||
print(presence_probe)
|
||||
breakpoint()
|
||||
self.send_raw(str(presence_probe))
|
||||
presence_probe.send()
|
||||
elif not self.client_roster[jid]["to"]:
|
||||
self.send_presence_subscription(
|
||||
pto=jid,
|
||||
pfrom=self.boundjid.bare,
|
||||
ptype="subscribe",
|
||||
pnick=self.alias
|
||||
)
|
||||
self.send_message(
|
||||
mto=jid,
|
||||
mfrom=self.boundjid.bare,
|
||||
# mtype="headline",
|
||||
msubject="RSS News Bot",
|
||||
mbody=(
|
||||
"Share online status to receive updates."
|
||||
),
|
||||
mnick=self.alias
|
||||
)
|
||||
self.send_presence(
|
||||
pto=jid,
|
||||
pfrom=self.boundjid.bare,
|
||||
# Accept symbol 🉑️ 👍️ ✍
|
||||
pstatus=(
|
||||
"✒️ Share online status to receive updates."
|
||||
),
|
||||
# ptype="subscribe",
|
||||
pnick=self.alias
|
||||
)
|
||||
|
||||
|
||||
async def unsubscribed(self, presence):
|
||||
jid = presence["from"].bare
|
||||
self.send_message(
|
||||
mto=jid,
|
||||
mfrom=self.boundjid.bare,
|
||||
mbody="You have been unsubscribed."
|
||||
)
|
||||
self.send_presence(
|
||||
pto=jid,
|
||||
pfrom=self.boundjid.bare,
|
||||
pstatus="🖋️ Subscribe to receive updates",
|
||||
pnick=self.alias
|
||||
)
|
|
@ -1,19 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from slixfeed.xmpp.utility import get_chat_type
|
||||
|
||||
|
||||
class XmppStatus:
|
||||
|
||||
|
||||
async def send(self, jid, status_message, status_type=None, chat_type=None):
|
||||
if not chat_type:
|
||||
chat_type = await get_chat_type(self, jid)
|
||||
self.send_presence(
|
||||
pto=jid,
|
||||
pfrom=self.boundjid.bare,
|
||||
pshow=status_type,
|
||||
pstatus=status_message,
|
||||
ptype=chat_type
|
||||
)
|
|
@ -11,8 +11,9 @@ from slixmpp.exceptions import IqTimeout, IqError
|
|||
from slixmpp.plugins.xep_0363.http_upload import HTTPError
|
||||
# import sys
|
||||
|
||||
class XmppUpload:
|
||||
|
||||
async def start(self, jid, filename, domain=None):
|
||||
async def start(self, jid, filename, domain=None):
|
||||
logging.info('Uploading file %s...', filename)
|
||||
try:
|
||||
upload_file = self['xep_0363'].upload_file
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from slixmpp.exceptions import IqTimeout
|
||||
from slixmpp.exceptions import IqError, IqTimeout
|
||||
import logging
|
||||
|
||||
# class XmppChat
|
||||
# class XmppUtility:
|
||||
|
||||
async def get_chat_type(self, jid):
|
||||
"""
|
||||
|
@ -43,11 +45,15 @@ async def get_chat_type(self, jid):
|
|||
'Chat Type: {}'.format(jid, chat_type))
|
||||
return chat_type
|
||||
# TODO Test whether this exception is realized
|
||||
except IqTimeout as e:
|
||||
messages = [
|
||||
("Timeout IQ"),
|
||||
("IQ Stanza:", e),
|
||||
("Jabber ID:", jid)
|
||||
]
|
||||
for message in messages:
|
||||
except IqError as e:
|
||||
message = ('IQ Error\n'
|
||||
'IQ Stanza: {}'
|
||||
'Jabber ID: {}'
|
||||
.format(e, jid))
|
||||
logging.error(message)
|
||||
except IqTimeout as e:
|
||||
message = ('IQ Timeout\n'
|
||||
'IQ Stanza: {}'
|
||||
'Jabber ID: {}'
|
||||
.format(e, jid))
|
||||
logging.error(message)
|
Loading…
Reference in a new issue