Slixfeed/slixfeed/xmpp/process.py

1214 lines
52 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
TODO
1) Deprecate "add" (see above) and make it interactive.
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.
elif not self.client_roster[jid]["to"]:
breakpoint()
message.reply("Share online status to activate bot.").send()
return
"""
2024-01-09 13:34:10 +01:00
import logging
import os
import slixfeed.action as action
from slixfeed.config import (
add_to_list,
2024-01-22 16:27:01 +01:00
get_default_cache_directory,
get_default_data_directory,
get_value,
get_pathname_to_database,
remove_from_list)
2024-01-18 21:57:49 +01:00
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
import slixfeed.xmpp.bookmark as bookmark
2024-01-09 16:53:19 +01:00
import slixfeed.xmpp.manual as manual
import slixfeed.xmpp.muc as groupchat
import slixfeed.xmpp.upload as upload
from slixfeed.xmpp.utility import get_chat_type
import time
async def event_component(self, event):
self.send_presence()
async def event(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.send_presence()
await self.get_roster()
# for task in main_task:
# task.cancel()
# Deprecated in favour of event "presence_available"
# if not main_task:
# await select_file()
async def message(self, message):
"""
Process incoming message stanzas. Be aware that this also
includes MUC messages and error messages. It is usually
a good practice to check the messages's type before
processing or sending replies.
Parameters
----------
message : str
The received message stanza. See the documentation
for stanza objects and the Message stanza to see
how it may be used.
"""
if message['type'] in ('chat', 'groupchat', 'normal'):
jid = message['from'].bare
jid_file = jid
message_text = ' '.join(message['body'].split())
command_time_start = time.time()
# if (message['type'] == 'groupchat' and
# message['muc']['nick'] == self.alias):
# return
# FIXME Code repetition. See below.
# TODO Check alias by nickname associated with conference
if message['type'] == 'groupchat':
if (message['muc']['nick'] == self.alias):
return
jid_full = str(message['from'])
role = self.plugin['xep_0045'].get_jid_property(
jid,
jid_full[jid_full.index('/')+1:],
'role')
if role != 'moderator':
return
# NOTE This is an exceptional case in which we treat
# type groupchat the same as type chat in a way that
# doesn't require an exclamation mark for actionable
# command.
if (message_text.lower().startswith('http') and
message_text.lower().endswith('.opml')):
2024-01-09 13:34:10 +01:00
url = message_text
await task.clean_tasks_xmpp(
jid, ['status'])
status_type = 'dnd'
2024-01-09 13:34:10 +01:00
status_message = (
'📥️ Procesing request to import feeds...'
2024-01-09 13:34:10 +01:00
)
send_status_message(
self, jid, status_type, status_message)
db_file = get_pathname_to_database(jid_file)
2024-01-09 13:34:10 +01:00
count = await action.import_opml(db_file, url)
if count:
response = (
'Successfully imported {} feeds.'
2024-01-09 13:34:10 +01:00
).format(count)
else:
response = (
'OPML file was not imported.'
2024-01-09 13:34:10 +01:00
)
await task.clean_tasks_xmpp(
jid, ['status'])
2024-01-09 13:34:10 +01:00
await task.start_tasks_xmpp(
self, jid, ['status'])
2024-01-09 13:34:10 +01:00
send_reply_message(self, message, response)
return
2024-01-09 13:34:10 +01:00
if message['type'] == 'groupchat':
# nick = message['from'][message['from'].index('/')+1:]
# nick = str(message['from'])
# nick = nick[nick.index('/')+1:]
if (message['muc']['nick'] == self.alias or
not message['body'].startswith('!')):
return
# token = await initdb(
# jid,
# get_settings_value,
# 'token'
# )
# if token == 'accepted':
# operator = await initdb(
# jid,
# get_settings_value,
# 'masters'
# )
# if operator:
# if nick not in operator:
# return
# approved = False
jid_full = str(message['from'])
role = self.plugin['xep_0045'].get_jid_property(
jid,
jid_full[jid_full.index('/')+1:],
'role')
if role != 'moderator':
return
# if role == 'moderator':
# approved = True
# TODO Implement a list of temporary operators
# Once an operator is appointed, the control would last
# untile the participant has been disconnected from MUC
# An operator is a function to appoint non moderators.
# Changing nickname is fine and consist of no problem.
# if not approved:
# operator = await initdb(
# jid,
# get_settings_value,
# 'masters'
# )
# if operator:
# if nick in operator:
# approved = True
# if not approved:
# return
# # Begin processing new JID
# # Deprecated in favour of event 'presence_available'
# db_dir = get_default_data_directory()
# os.chdir(db_dir)
# if jid + '.db' not in os.listdir():
# await task_jid(jid)
# await compose.message(self, jid, message)
if message['type'] == 'groupchat':
message_text = message_text[1:]
message_lowercase = message_text.lower()
logging.debug([str(message['from']), ':', message_text])
# Support private message via groupchat
2024-01-28 12:17:31 +01:00
# See https://codeberg.org/poezio/slixmpp/issues/3506
if message['type'] == 'chat' and message.get_plugin('muc', check=True):
jid_bare = message['from'].bare
jid_full = str(message['from'])
if (jid_bare == jid_full[:jid_full.index('/')]):
jid = str(message['from'])
jid_file = jid_full.replace('/', '_')
2024-01-09 13:34:10 +01:00
response = None
match message_lowercase:
# case 'breakpoint':
# if jid == get_value('accounts', 'XMPP', 'operator'):
2024-01-02 13:52:37 +01:00
# breakpoint()
# print('task_manager[jid]')
2024-01-09 13:34:10 +01:00
# print(task_manager[jid])
# await self.get_roster()
# print('roster 1')
2024-01-09 13:34:10 +01:00
# print(self.client_roster)
# print('roster 2')
2024-01-09 13:34:10 +01:00
# print(self.client_roster.keys())
# print('jid')
2024-01-09 13:34:10 +01:00
# print(jid)
2024-01-02 13:52:37 +01:00
# else:
# response = (
# 'This action is restricted. '
# 'Type: breakpoint.'
2024-01-02 13:52:37 +01:00
# )
# send_reply_message(self, message, response)
case 'help':
2024-01-28 12:17:31 +01:00
command_list = ' '.join(action.manual('commands.toml'))
2024-01-27 20:21:45 +01:00
response = (
2024-01-28 12:17:31 +01:00
'Available command keys:\n',
'```\n', command_list, '\n```\n'
'> Info: `help key`')
2024-01-27 20:21:45 +01:00
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('help'):
command = message_text[5:]
command = command.split(' ')
if len(command) == 2:
command_root = command[0]
command_name = command[1]
2024-01-28 12:17:31 +01:00
command_list = action.manual(
'commands.toml',
section=command_root,
command=command_name)
if command_list:
command_list = ''.join(command_list)
response = (command_list)
else:
response = (
'KeyError for {} {}'.format(
command_root, command_name))
2024-01-27 20:21:45 +01:00
elif len(command) == 1:
command = command[0]
2024-01-28 12:17:31 +01:00
command_list = action.manual('commands.toml', command)
if command_list:
command_list = ' '.join(command_list)
response = (
'Available commands:\n'
'```\n', command_list, '\n```\n'
'> Info: `help {} command`'
.format(command))
else:
response = ('KeyError for {}'.format(command))
2024-01-27 20:21:45 +01:00
else:
response = (
2024-01-28 12:17:31 +01:00
'Invalid. Enter command key or command key & name')
send_reply_message(self, message, response)
case 'info':
2024-01-28 12:17:31 +01:00
command_list = ' '.join(action.manual('information.toml'))
response = (
'Available options:\n',
'```\n', command_list, '\n```\n'
'> Info: `info option`')
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('info'):
command = message_text[5:]
command_list = action.manual(
'information.toml', command)
if command_list:
# command_list = '\n'.join(command_list)
response = (command_list)
else:
response = (
'KeyError for {}'.format(command))
send_reply_message(self, message, response)
case _ if message_lowercase in [
'greetings', 'hallo', 'hello', 'hey',
'hi', 'hola', 'holla', 'hollo']:
response = (
'Greeting! My name is {}, and I am an RSS News Bot.\n'
'Send "help" for further instructions.\n'
.format(self.alias)
)
send_reply_message(self, message, response)
# case _ if message_lowercase.startswith('activate'):
# if message['type'] == 'groupchat':
# acode = message[9:]
# token = await initdb(
# jid,
# get_settings_value,
# 'token'
# )
# if int(acode) == token:
# await initdb(
# jid,
# update_settings_value,
# ['masters', nick]
# )
# await initdb(
# jid,
# update_settings_value,
# ['token', 'accepted']
# )
# response = '{}, your are in command.'.format(nick)
# else:
# response = 'Activation code is not valid.'
# else:
# response = 'This command is valid for groupchat only.'
case _ if message_lowercase.startswith('add'):
# Add given feed without validity check.
message_text = message_text[4:]
url = message_text.split(' ')[0]
title = ' '.join(message_text.split(' ')[1:])
if url.startswith('http'):
db_file = get_pathname_to_database(jid_file)
exist = await sqlite.get_feed_id_and_name(
db_file, url)
if not exist:
await sqlite.insert_feed(
db_file, url, title)
await action.scan(db_file, url)
2024-01-04 13:38:22 +01:00
old = (
await sqlite.get_settings_value(db_file, 'old') or
get_value('settings', 'Settings', 'old')
2024-01-04 13:38:22 +01:00
)
if old:
2024-01-04 13:38:22 +01:00
await task.clean_tasks_xmpp(
jid, ['status'])
# await send_status(jid)
2024-01-04 13:38:22 +01:00
await task.start_tasks_xmpp(
self, jid, ['status'])
else:
await sqlite.mark_feed_as_read(
2024-01-04 13:38:22 +01:00
db_file, url)
response = (
'> {}\nNews source has been '
'added to subscription list.'
).format(url)
else:
ix = exist[0]
name = exist[1]
response = (
'> {}\nNews source "{}" is already '
'listed in the subscription list at '
'index {}'
2024-01-09 13:34:10 +01:00
).format(url, name, ix)
else:
response = 'Missing URL.'
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('allow +'):
key = 'filter-' + message_text[:5]
val = message_text[7:]
if val:
db_file = get_pathname_to_database(jid_file)
2024-01-04 14:58:06 +01:00
keywords = await sqlite.get_filters_value(
db_file, key)
val = await add_to_list(
val, keywords)
if await sqlite.get_filters_value(db_file, key):
await sqlite.update_filters_value(
db_file, [key, val])
else:
await sqlite.set_filters_value(db_file, [key, val])
response = (
'Approved keywords\n'
'```\n{}\n```'
).format(val)
else:
response = 'Missing keywords.'
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('allow -'):
key = 'filter-' + message_text[:5]
val = message_text[7:]
if val:
db_file = get_pathname_to_database(jid_file)
2024-01-04 14:58:06 +01:00
keywords = await sqlite.get_filters_value(
db_file, key)
val = await remove_from_list(
val, keywords)
if await sqlite.get_filters_value(db_file, key):
await sqlite.update_filters_value(
db_file, [key, val])
else:
await sqlite.set_filters_value(db_file, [key, val])
response = (
'Approved keywords\n'
'```\n{}\n```'
).format(val)
else:
response = 'Missing keywords.'
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('archive'):
key = message_text[:7]
val = message_text[8:]
if val:
try:
if int(val) > 500:
response = 'Value may not be greater than 500.'
else:
db_file = get_pathname_to_database(jid_file)
if await sqlite.get_settings_value(
db_file, [key, val]):
await sqlite.update_settings_value(
db_file, [key, val])
else:
await sqlite.set_settings_value(
db_file, [key, val])
response = (
'Maximum archived items has been set to {}.'
).format(val)
except:
response = 'Enter a numeric value only.'
else:
response = 'Missing value.'
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('bookmark -'):
2024-01-17 15:36:28 +01:00
if jid == get_value(
'accounts', 'XMPP', 'operator'):
muc_jid = message_text[11:]
await bookmark.remove(self, muc_jid)
response = (
'Groupchat {} has been removed from bookmarks.'
).format(muc_jid)
else:
response = (
'This action is restricted. '
'Type: removing bookmarks.'
)
send_reply_message(self, message, response)
case 'bookmarks':
2024-01-04 14:58:06 +01:00
if jid == get_value(
'accounts', 'XMPP', 'operator'):
response = await action.list_bookmarks(self)
2024-01-02 13:12:29 +01:00
else:
response = (
'This action is restricted. '
'Type: viewing bookmarks.'
2024-01-02 13:12:29 +01:00
)
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('deny +'):
key = 'filter-' + message_text[:4]
val = message_text[6:]
if val:
db_file = get_pathname_to_database(jid_file)
2024-01-04 14:58:06 +01:00
keywords = await sqlite.get_filters_value(
db_file, key)
val = await add_to_list(
val, keywords)
if await sqlite.get_filters_value(db_file, key):
await sqlite.update_filters_value(
db_file, [key, val])
else:
await sqlite.set_filters_value(db_file, [key, val])
response = (
'Rejected keywords\n'
'```\n{}\n```'
).format(val)
else:
response = 'Missing keywords.'
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('deny -'):
key = 'filter-' + message_text[:4]
val = message_text[6:]
if val:
db_file = get_pathname_to_database(jid_file)
2024-01-04 14:58:06 +01:00
keywords = await sqlite.get_filters_value(
db_file, key)
val = await remove_from_list(
val, keywords)
if await sqlite.get_filters_value(db_file, key):
await sqlite.update_filters_value(
db_file, [key, val])
else:
await sqlite.set_filters_value(db_file, [key, val])
response = (
'Rejected keywords\n'
'```\n{}\n```'
).format(val)
else:
response = 'Missing keywords.'
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('export'):
ex = message_text[7:]
if ex in ('opml', 'html', 'md', 'xbel'):
status_type = 'dnd'
status_message = (
'📤️ Procesing request to export feeds into {}...'
).format(ex)
send_status_message(
self, jid, status_type, status_message)
2024-01-22 16:27:01 +01:00
cache_dir = get_default_cache_directory()
if not os.path.isdir(cache_dir):
os.mkdir(cache_dir)
if not os.path.isdir(cache_dir + '/' + ex):
os.mkdir(cache_dir + '/' + ex)
filename = os.path.join(
cache_dir, ex, 'slixfeed_' + timestamp() + '.' + ex)
db_file = get_pathname_to_database(jid_file)
results = await sqlite.get_feeds(db_file)
match ex:
case 'html':
response = 'Not yet implemented.'
case 'md':
action.export_to_markdown(
jid, filename, results)
case 'opml':
action.export_to_opml(
jid, filename, results)
case 'xbel':
response = 'Not yet implemented.'
url = await upload.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)
await task.start_tasks_xmpp(
self, jid, ['status'])
else:
response = (
'Unsupported filetype. '
'Try: html, md, opml, or xbel')
send_reply_message(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)
2024-01-09 16:53:19 +01:00
# TODO xHTML, HTMLZ, Markdown, MHTML, PDF, TXT
case _ if (message_lowercase.startswith('get')):
2024-01-09 16:53:19 +01:00
message_text = message_text[4:]
ix_url = message_text.split(' ')[0]
ext = ' '.join(message_text.split(' ')[1:])
ext = ext if ext else 'pdf'
url = None
2024-01-22 16:27:01 +01:00
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())
send_status_message(
self, jid, status_type, status_message)
db_file = get_pathname_to_database(jid_file)
2024-01-22 16:27:01 +01:00
cache_dir = get_default_cache_directory()
if ix_url:
2024-01-22 16:27:01 +01:00
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:
2024-01-28 12:17:31 +01:00
logging.info(
'Original URL: {}'.format(url))
url = uri.remove_tracking_parameters(url)
2024-01-28 12:17:31 +01:00
logging.info(
'Processed URL (tracker removal): {}'
.format(url))
url = (uri.replace_hostname(url, 'link')) or url
2024-01-28 12:17:31 +01:00
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)
2024-01-22 16:27:01 +01:00
if error:
response = (
'> {}\n'
'Failed to export {}. Reason: {}'
).format(url, ext.upper(), error)
2024-01-22 16:01:52 +01:00
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'])
2024-01-09 16:53:19 +01:00
else:
response = 'Missing entry index number.'
2024-01-09 16:53:19 +01:00
else:
response = (
'Unsupported filetype. '
'Try: epub, html, md (markdown), pdf, or txt (text)')
2024-01-09 16:53:19 +01:00
if response:
logging.warning('Error for URL {}: {}'.format(url, error))
2024-01-09 16:53:19 +01:00
send_reply_message(self, message, response)
# case _ if (message_lowercase.startswith('http')) and(
# message_lowercase.endswith('.opml')):
2024-01-09 13:34:10 +01:00
# url = message_text
# await task.clean_tasks_xmpp(
# jid, ['status'])
# status_type = 'dnd'
2024-01-09 13:34:10 +01:00
# status_message = (
# '📥️ Procesing request to import feeds...'
2024-01-09 13:34:10 +01:00
# )
# send_status_message(
# self, jid, status_type, status_message)
# db_file = get_pathname_to_database(jid_file)
2024-01-09 13:34:10 +01:00
# count = await action.import_opml(db_file, url)
# if count:
# response = (
# 'Successfully imported {} feeds.'
2024-01-09 13:34:10 +01:00
# ).format(count)
# else:
# response = (
# 'OPML file was not imported.'
2024-01-09 13:34:10 +01:00
# )
# await task.clean_tasks_xmpp(
# jid, ['status'])
2024-01-09 13:34:10 +01:00
# await task.start_tasks_xmpp(
# self, jid, ['status'])
2024-01-09 13:34:10 +01:00
# send_reply_message(self, message, response)
case _ if (message_lowercase.startswith('http') or
message_lowercase.startswith('feed:')):
url = message_text
await task.clean_tasks_xmpp(
jid, ['status'])
status_type = 'dnd'
status_message = (
'📫️ Processing request to fetch data from {}'
).format(url)
send_status_message(
self, jid, status_type, status_message)
if url.startswith('feed:'):
url = uri.feed_to_http(url)
url = (uri.replace_hostname(url, 'feed')) or url
db_file = get_pathname_to_database(jid_file)
2024-01-28 12:17:31 +01:00
# try:
response = await action.add_feed(
db_file, url)
await task.clean_tasks_xmpp(
jid, ['status'])
await task.start_tasks_xmpp(
self, jid, ['status'])
# except:
# response = (
# '> {}\nNews source is in the process '
# 'of being added to the subscription '
# 'list.'.format(url)
# )
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('feeds'):
query = message_text[6:]
if query:
if len(query) > 3:
db_file = get_pathname_to_database(jid_file)
2024-01-09 13:34:10 +01:00
result = await sqlite.search_feeds(
db_file, query)
response = action.list_feeds_by_query(
query, result)
else:
response = (
'Enter at least 4 characters to search'
)
else:
db_file = get_pathname_to_database(jid_file)
result = await sqlite.get_feeds(db_file)
response = action.list_feeds(result)
send_reply_message(self, message, response)
case 'goodbye':
if message['type'] == 'groupchat':
await groupchat.leave(self, jid)
else:
response = 'This command is valid for groupchat only.'
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('interval'):
# FIXME
# The following error occurs only upon first attempt to set interval.
# /usr/lib/python3.11/asyncio/events.py:73: RuntimeWarning: coroutine 'Slixfeed.send_update' was never awaited
# self._args = None
# RuntimeWarning: Enable tracemalloc to get the object allocation traceback
key = message_text[:8]
val = message_text[9:]
if val:
# response = (
# 'Updates will be sent every {} minutes.'
# ).format(response)
db_file = 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])
# NOTE Perhaps this should be replaced
# by functions clean and start
await task.refresh_task(
self, jid, task.send_update, key, val)
response = (
'Updates will be sent every {} minutes.'
).format(val)
else:
response = 'Missing value.'
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('join'):
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)
response = (
'Joined groupchat {}'
2024-01-09 13:34:10 +01:00
).format(message_text)
else:
response = (
'> {}\nXMPP URI is not valid.'
2024-01-09 13:34:10 +01:00
).format(message_text)
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('length'):
key = message_text[:6]
val = message_text[7:]
if val:
try:
val = int(val)
db_file = get_pathname_to_database(jid_file)
if await sqlite.get_settings_value(
db_file, [key, val]):
await sqlite.update_settings_value(
db_file, [key, val])
else:
await sqlite.set_settings_value(
db_file, [key, val])
2024-01-04 14:58:06 +01:00
if val == 0: # if not val:
response = (
'Summary length limit is disabled.'
)
else:
response = (
'Summary maximum length '
'is set to {} characters.'
).format(val)
except:
response = 'Enter a numeric value only.'
else:
response = 'Missing value.'
# case _ if message_lowercase.startswith('mastership'):
# key = message_text[:7]
# val = message_text[11:]
# if val:
# names = await initdb(
# jid,
# get_settings_value,
# key
# )
# val = await add_to_list(
# val,
# names
# )
# await initdb(
# jid,
# update_settings_valuevv,
# [key, val]
# )
# response = (
# 'Operators\n'
# '```\n{}\n```'
# ).format(val)
# else:
# response = 'Missing value.'
send_reply_message(self, message, response)
case 'new':
db_file = get_pathname_to_database(jid_file)
key = 'old'
val = 0
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])
response = (
'Only new items of newly added feeds will be sent.'
)
send_reply_message(self, message, response)
# TODO Will you add support for number of messages?
case 'next':
# num = message_text[5:]
# await task.send_update(self, jid, num)
await task.send_update(self, jid)
# await task.clean_tasks_xmpp(
# jid, ['interval', 'status'])
# await task.start_tasks_xmpp(
# self, jid, ['interval', 'status'])
# await refresh_task(
# self,
# jid,
# send_update,
# 'interval',
# num
# )
# await refresh_task(
# self,
# jid,
# send_status,
# 'status',
# 20
# )
# await refresh_task(jid, key, val)
case 'old':
db_file = get_pathname_to_database(jid_file)
key = 'old'
val = 1
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])
response = (
'All items of newly added feeds will be sent.'
)
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('quantum'):
key = message_text[:7]
val = message_text[8:]
if val:
try:
val = int(val)
# response = (
# 'Every update will contain {} news items.'
# ).format(response)
db_file = 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])
response = (
'Next update will contain {} news items.'
).format(val)
except:
response = 'Enter a numeric value only.'
else:
response = 'Missing value.'
send_reply_message(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)
case _ if message_lowercase.startswith('read'):
data = message_text[5:]
data = data.split()
url = data[0]
await task.clean_tasks_xmpp(
jid, ['status'])
status_type = 'dnd'
status_message = (
'📫️ Processing request to fetch data from {}'
).format(url)
send_status_message(
self, jid, status_type, status_message)
if url.startswith('feed:'):
url = uri.feed_to_http(url)
url = (uri.replace_hostname(url, 'feed')) or url
match len(data):
case 1:
if url.startswith('http'):
response = await action.view_feed(url)
else:
response = 'Missing URL.'
case 2:
num = data[1]
if url.startswith('http'):
response = await action.view_entry(url, num)
else:
response = 'Missing URL.'
case _:
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)
2024-01-04 14:58:06 +01:00
await task.start_tasks_xmpp(
self, jid, ['status'])
case _ if message_lowercase.startswith('recent'):
num = message_text[7:]
if num:
try:
num = int(num)
if num < 1 or num > 50:
response = 'Value must be ranged from 1 to 50.'
else:
db_file = get_pathname_to_database(jid_file)
result = await sqlite.last_entries(db_file, num)
response = action.list_last_entries(result, num)
except:
response = 'Enter a numeric value only.'
else:
response = 'Missing value.'
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('remove'):
ix_url = message_text[7:]
if ix_url:
db_file = get_pathname_to_database(jid_file)
try:
ix = int(ix_url)
try:
url = await sqlite.remove_feed_by_index(
db_file, ix)
response = (
'> {}\nNews source "{}" has been '
'removed from subscription list.'
2024-01-09 13:34:10 +01:00
).format(url, ix)
except:
response = (
'No news source with index {}.'
2024-01-09 13:34:10 +01:00
).format(ix)
except:
url = ix_url
await sqlite.remove_feed_by_url(db_file, url)
response = (
'> {}\nNews source has been removed '
'from subscription list.'
2024-01-09 13:34:10 +01:00
).format(url)
# await refresh_task(
# self,
# jid,
# send_status,
# 'status',
# 20
# )
await task.clean_tasks_xmpp(jid, ['status'])
await task.start_tasks_xmpp(self, jid, ['status'])
else:
response = 'Missing feed index number.'
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('reset'):
url = message_text[6:]
await task.clean_tasks_xmpp(jid, ['status'])
status_type = 'dnd'
status_message = '📫️ Marking entries as read...'
send_status_message(
self, jid, status_type, status_message)
if url:
db_file = get_pathname_to_database(jid_file)
await sqlite.mark_feed_as_read(
db_file, url)
response = (
'All entries of {} have been '
'marked as read.'.format(url)
)
else:
db_file = get_pathname_to_database(jid_file)
await sqlite.mark_all_as_read(db_file)
response = 'All entries have been marked as read.'
send_reply_message(self, message, response)
2024-01-04 14:58:06 +01:00
await task.start_tasks_xmpp(
self, jid, ['status'])
case _ if message_lowercase.startswith('search'):
query = message_text[7:]
if query:
if len(query) > 1:
db_file = get_pathname_to_database(jid_file)
2024-01-04 14:58:06 +01:00
results = await sqlite.search_entries(
db_file, query)
response = action.list_search_results(
2024-01-04 14:58:06 +01:00
query, results)
else:
response = (
'Enter at least 2 characters to search'
)
else:
response = 'Missing search query.'
send_reply_message(self, message, response)
case 'start':
# response = 'Updates are enabled.'
key = 'enabled'
val = 1
db_file = 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])
# asyncio.create_task(task_jid(self, jid))
await task.start_tasks_xmpp(
self, jid, ['interval', 'status', 'check'])
response = 'Updates are enabled.'
# print(current_time(), 'task_manager[jid]')
# print(task_manager[jid])
send_reply_message(self, message, response)
case 'stats':
db_file = get_pathname_to_database(jid_file)
2024-01-14 19:05:12 +01:00
response = await action.list_statistics(db_file)
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('disable '):
ix = message_text[8:]
db_file = get_pathname_to_database(jid_file)
try:
await sqlite.set_enabled_status(db_file, ix, 0)
response = (
'Updates are now disabled for news source {}.'
).format(ix)
except:
response = 'No news source with index {}.'.format(ix)
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('enable'):
ix = message_text[7:]
db_file = get_pathname_to_database(jid_file)
try:
await sqlite.set_enabled_status(db_file, ix, 1)
response = (
'Updates are now enabled for news source {}.'
).format(ix)
except:
response = 'No news source with index {}.'.format(ix)
send_reply_message(self, message, response)
case 'stop':
# FIXME
# The following error occurs only upon first attempt to stop.
# /usr/lib/python3.11/asyncio/events.py:73: RuntimeWarning: coroutine 'Slixfeed.send_update' was never awaited
# self._args = None
# RuntimeWarning: Enable tracemalloc to get the object allocation traceback
# response = 'Updates are disabled.'
# try:
# # task_manager[jid]['check'].cancel()
# # task_manager[jid]['status'].cancel()
# task_manager[jid]['interval'].cancel()
# key = 'enabled'
# val = 0
# response = await initdb(
# jid,
# update_settings_value,
# [key, val]
# )
# except:
# response = 'Updates are already disabled.'
# # print('Updates are already disabled. Nothing to do.')
# # await send_status(jid)
key = 'enabled'
val = 0
db_file = 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.clean_tasks_xmpp(
jid, ['interval', 'status'])
response = 'Updates are disabled.'
send_reply_message(self, message, response)
status_type = 'xa'
status_message = (
'💡️ Send "Start" to receive Jabber updates')
send_status_message(
self, jid, status_type, status_message)
case 'support':
# TODO Send an invitation.
response = (
'Join xmpp:slixfeed@chat.woodpeckersnest.space?join'
2024-01-04 14:58:06 +01:00
)
send_reply_message(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)
response = (
'Joined groupchat {}'
2024-01-09 13:34:10 +01:00
).format(message_text)
else:
response = (
'> {}\nXMPP URI is not valid.'
2024-01-09 13:34:10 +01:00
).format(message_text)
send_reply_message(self, message, response)
case _:
response = (
'Unknown command. '
'Press "help" for list of commands'
)
send_reply_message(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
# if response: message.reply(response).send()
2024-01-28 12:17:31 +01:00
print(
'Message : {}\n'
'JID : {}\n'
'File : {}\n'
.format(message_text, jid, jid_file)
)
command_time_finish = time.time()
command_time_total = command_time_finish - command_time_start
print('Finished. Total time: {}s'.format(round(command_time_total, 3)))
logging.info('Finished. Total time: {}s'.format(command_time_total))
if not response: response = 'EMPTY MESSAGE - ACTION ONLY'
data_dir = get_default_data_directory()
if not os.path.isdir(data_dir):
os.mkdir(data_dir)
if not os.path.isdir(data_dir + '/logs/'):
os.mkdir(data_dir + '/logs/')
action.log_to_markdown(
current_time(), os.path.join(data_dir, 'logs', jid_file),
jid, message_text)
action.log_to_markdown(
current_time(), os.path.join(data_dir, 'logs', jid_file),
self.boundjid.bare, response)
def send_status_message(self, jid, status_type, status_message):
self.send_presence(
pshow=status_type,
pstatus=status_message,
pto=jid)
def send_reply_message(self, message, response):
message.reply(response).send()
2024-01-04 14:58:06 +01:00
# 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>')
2024-01-04 14:58:06 +01:00
message = self.make_message(
mto=jid,
mfrom=self.boundjid.bare,
2024-01-04 14:58:06 +01:00
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
)