Add ad-hoc commands

This commit is contained in:
Schimon Jehudah 2024-02-06 03:04:43 +00:00
parent 56aaccce68
commit 422e0669f1
12 changed files with 964 additions and 247 deletions

View file

@ -129,7 +129,7 @@ class JabberComponent:
xmpp.register_plugin('xep_0066') # Out of Band Data
xmpp.register_plugin('xep_0071') # XHTML-IM
xmpp.register_plugin('xep_0084') # User Avatar
xmpp.register_plugin('xep_0085') # Chat State Notifications
# xmpp.register_plugin('xep_0085') # Chat State Notifications
xmpp.register_plugin('xep_0115') # Entity Capabilities
xmpp.register_plugin('xep_0153') # vCard-Based Avatars
xmpp.register_plugin('xep_0199', {'keepalive': True}) # XMPP Ping
@ -155,7 +155,7 @@ class JabberClient:
xmpp.register_plugin('xep_0066') # Out of Band Data
xmpp.register_plugin('xep_0071') # XHTML-IM
xmpp.register_plugin('xep_0084') # User Avatar
xmpp.register_plugin('xep_0085') # Chat State Notifications
# xmpp.register_plugin('xep_0085') # Chat State Notifications
xmpp.register_plugin('xep_0115') # Entity Capabilities
xmpp.register_plugin('xep_0153') # vCard-Based Avatars
xmpp.register_plugin('xep_0199', {'keepalive': True}) # XMPP Ping

View file

@ -48,7 +48,10 @@ from slixfeed.url import (
replace_hostname,
trim_url
)
import slixfeed.xmpp.bookmark as bookmark
import slixfeed.task as task
from slixfeed.xmpp.bookmark import XmppBookmark
from slixfeed.xmpp.message import XmppMessage
from slixfeed.xmpp.status import XmppStatus
import tomllib
from urllib import error
from urllib.parse import parse_qs, urlsplit
@ -56,28 +59,28 @@ import xml.etree.ElementTree as ET
try:
import xml2epub
except:
except ImportError:
logging.info(
"Package xml2epub was not found.\n"
"ePUB support is disabled.")
try:
import html2text
except:
except ImportError:
logging.info(
"Package html2text was not found.\n"
"Markdown support is disabled.")
try:
import pdfkit
except:
except ImportError:
logging.info(
"Package pdfkit was not found.\n"
"PDF support is disabled.")
try:
from readability import Document
except:
except ImportError:
logging.info(
"Package readability was not found.\n"
"Arc90 Lab algorithm is disabled.")
@ -107,6 +110,45 @@ def manual(filename, section=None, command=None):
return cmd_list
async def xmpp_change_interval(self, key, val, jid, jid_file, message=None, session=None):
if val:
# response = (
# 'Updates will be sent every {} minutes.'
# ).format(response)
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])
# 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.'
if message:
XmppMessage.send_reply(self, message, response)
if session:
await XmppMessage.send(self, jid, response, chat_type='chat')
async def xmpp_stop_updates(self, message, jid, jid_file):
key = 'enabled'
val = 0
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.clean_tasks_xmpp(jid, ['interval', 'status'])
response = 'Updates are disabled.'
XmppMessage.send_reply(self, message, response)
status_type = 'xa'
status_message = '💡️ Send "Start" to receive Jabber updates'
await XmppStatus.send(self, jid, status_message, status_type)
def log_to_markdown(timestamp, filename, jid, message):
"""
Log message to file.
@ -404,7 +446,7 @@ def list_feeds(results):
async def list_bookmarks(self):
conferences = await bookmark.get(self)
conferences = await XmppBookmark.get(self)
message = "\nList of groupchats:\n\n```\n"
for conference in conferences:
message += (

View file

@ -1,7 +1,7 @@
[action]
url = """
<url>
Add given <url> to subscription list.
Add given <url> to subscription list (prefix http. Default).
"""
add = """
add <url> <title>
@ -30,6 +30,17 @@ backup news text
Send a Plain Text file of your news items.
"""
[bookmarks]
bookmark = """
bookmark [+|-] <muc>
Groupchat to add or remove.
'+' appends to, '-' removes from.
"""
bookmarks = """
bookmarks
List bookmarked groupchats.
"""
[custom]
new = """
new
@ -70,10 +81,18 @@ Reset deny list.
"""
[groupchat]
uri = """
<muc>
Join groupchat by given <muc> (prefix xmpp).
"""
join = """
join <muc>
Join groupchat by given <muc>.
"""
leave = """
goodbye
Leave groupchat and delete it from bookmarks.
"""
[manual]
help = """
@ -122,10 +141,18 @@ Set maximum length of news item description. (0 for no limit)
"""
quantum = """
quantum <number>
Set amount of updates per interval by given <number>.
Set amount of updates per message by given <number>.
"""
random = """
random
Send messages by random order instead of date.
"""
[modification]
archive = """
archive <number>
Number of news items to archive (maximum value 500).
"""
remove = """
remove <id>
Remove feed of from subscription list by given <id>.
@ -158,7 +185,7 @@ Disable bot and stop updates.
[preview]
read = """
read <url>
Display most recent 20 titles of given <url>.
Display most recent 5 titles of given <url>.
"""
read_num = """
read <url> <index>
@ -180,10 +207,14 @@ Search news items by given <text>.
"""
recent = """
recent <number>
List recent <number> news items (up to 50 items).
List recent <number> news items (max. 50).
"""
[statistics]
stats = """
stats
Show general statistics.
"""
analyses = """
analyses
Show report and statistics of feeds.

View file

@ -61,7 +61,7 @@ No operator was specified for this instance.
platforms = """
Supported platforms: XMPP
Platforms to be added in future: Briar, Email, IRC, Matrix, MQTT, Tox.
Platforms to be added in future: Briar, Email, IRC, Matrix, MQTT, Nostr, Tox.
For ideal experience, we recommend using XMPP.
"""
@ -97,27 +97,36 @@ https://gitgud.io/sjehuda/slixfeed
"""
thanks = """
Alixander Court (alixandercourt.com, Utah), \
Alixander Court <alixandercourt.com> (Utah), \
Chriss Farrell (SalixOS, Oregon), \
Christian Dersch (SalixOS), \
Cyrille Pontvieux (SalixOS, France), \
Cyrille Pontvieux <enialis.net> (SalixOS, France), \
Denis Fomin (Gajim, Russia), \
Dimitris Tzemos (SalixOS, Greece), \
Emmanuel Gil Peyrot (Poezio, France), \
Florent Le Coz (Poezio, France), \
George Vlahavas (SalixOS, Greece), \
Guus der Kinderen from IgniteRealtime.org (Openfire, Netherlands), \
George Vlahavas <vlahavas.com> (SalixOS, Greece), \
Guus der Kinderen <igniterealtime.org> (Openfire, Netherlands), \
habnabit_ from #python on irc.libera.chat, \
Imar van Erven Dorens <simplicit.nl> (SalixOS, Netherlands), \
imattau (atomtopubsub), \
Jaussoin Timothée (Movim, France), \
Kevin Smith from Isode (Swift, Wales), \
Jaussoin Timothée <mov.im> (Movim, France), \
Justin Karneges <jblog.andbit.net> (Psi, California), \
Kevin Smith <isode.com> (Swift IM, Wales), \
Luis Henrique Mello (SalixOS, Brazil), \
magicfelix, \
Markus Muttilainen (SalixOS), \
Mathieu Pasquet (slixmpp, France), \
Maxime Buquet (slixmpp, France), \
Phillip Watkins (United Kingdom, SalixOS), \
Pierrick Le Brun (SalixOS, France), \
Raphael Groner (Fedora, Germany), \
Remko Tronçon (Swift, Germany), \
Simone "roughnecks" Canaletti (woodpeckersnest.space, Italy), \
Remko Tronçon <mko.re> (Psi , Belgium), \
Simone "roughnecks" Canaletti <woodpeckersnest.space> (Italy), \
Richard Lapointe (SalixOS, Connecticut), \
Strix from Loqi, \
Thibaud Guerin (SalixOS), \
Tim Beech (SalixOS, Brazil), \
Thorsten Mühlfelder (SalixOS, Germany), \
Yann Leboulanger (Gajim, France).
"""

View file

@ -12,55 +12,58 @@ TODO
from slixmpp.plugins.xep_0048.stanza import Bookmarks
async def get(self):
result = await self.plugin['xep_0048'].get_bookmarks()
bookmarks = result['private']['bookmarks']
conferences = bookmarks['conferences']
return conferences
class XmppBookmark:
async def add(self, muc_jid):
result = await self.plugin['xep_0048'].get_bookmarks()
bookmarks = result['private']['bookmarks']
conferences = bookmarks['conferences']
mucs = []
for conference in conferences:
jid = conference['jid']
mucs.extend([jid])
if muc_jid not in mucs:
bookmarks = Bookmarks()
mucs.extend([muc_jid])
for muc in mucs:
bookmarks.add_conference(
muc,
self.alias,
autojoin=True
)
await self.plugin['xep_0048'].set_bookmarks(bookmarks)
# bookmarks = Bookmarks()
# await self.plugin['xep_0048'].set_bookmarks(bookmarks)
# print(await self.plugin['xep_0048'].get_bookmarks())
# bm = BookmarkStorage()
# bm.conferences.append(Conference(muc_jid, autojoin=True, nick=self.alias))
# await self['xep_0402'].publish(bm)
async def get(self):
result = await self.plugin['xep_0048'].get_bookmarks()
bookmarks = result['private']['bookmarks']
conferences = bookmarks['conferences']
return conferences
async def remove(self, muc_jid):
result = await self.plugin['xep_0048'].get_bookmarks()
bookmarks = result['private']['bookmarks']
conferences = bookmarks['conferences']
mucs = []
for conference in conferences:
jid = conference['jid']
mucs.extend([jid])
if muc_jid in mucs:
bookmarks = Bookmarks()
mucs.remove(muc_jid)
for muc in mucs:
bookmarks.add_conference(
muc,
self.alias,
autojoin=True
)
await self.plugin['xep_0048'].set_bookmarks(bookmarks)
async def add(self, muc_jid):
result = await self.plugin['xep_0048'].get_bookmarks()
bookmarks = result['private']['bookmarks']
conferences = bookmarks['conferences']
mucs = []
for conference in conferences:
jid = conference['jid']
mucs.extend([jid])
if muc_jid not in mucs:
bookmarks = Bookmarks()
mucs.extend([muc_jid])
for muc in mucs:
bookmarks.add_conference(
muc,
self.alias,
autojoin=True
)
await self.plugin['xep_0048'].set_bookmarks(bookmarks)
# bookmarks = Bookmarks()
# await self.plugin['xep_0048'].set_bookmarks(bookmarks)
# print(await self.plugin['xep_0048'].get_bookmarks())
# bm = BookmarkStorage()
# bm.conferences.append(Conference(muc_jid, autojoin=True, nick=self.alias))
# await self['xep_0402'].publish(bm)
async def remove(self, muc_jid):
result = await self.plugin['xep_0048'].get_bookmarks()
bookmarks = result['private']['bookmarks']
conferences = bookmarks['conferences']
mucs = []
for conference in conferences:
jid = conference['jid']
mucs.extend([jid])
if muc_jid in mucs:
bookmarks = Bookmarks()
mucs.remove(muc_jid)
for muc in mucs:
bookmarks.add_conference(
muc,
self.alias,
autojoin=True
)
await self.plugin['xep_0048'].set_bookmarks(bookmarks)

View file

@ -60,7 +60,9 @@ from slixmpp.plugins.xep_0048.stanza import Bookmarks
# import xml.etree.ElementTree as ET
# from lxml import etree
import slixfeed.xmpp.bookmark as bookmark
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
import slixfeed.xmpp.process as process
@ -169,7 +171,7 @@ class Slixfeed(slixmpp.ClientXMPP):
inviter = message["from"].bare
muc_jid = message['groupchat_invite']['jid']
await muc.join(self, inviter, muc_jid)
await bookmark.add(self, muc_jid)
await XmppBookmark.add(self, muc_jid)
# NOTE Tested with Gajim and Psi
@ -177,7 +179,7 @@ class Slixfeed(slixmpp.ClientXMPP):
inviter = message["from"].bare
muc_jid = message['groupchat_invite']['jid']
await muc.join(self, inviter, muc_jid)
await bookmark.add(self, muc_jid)
await XmppBookmark.add(self, muc_jid)
async def on_session_end(self, event):
@ -191,17 +193,32 @@ class Slixfeed(slixmpp.ClientXMPP):
async def on_session_start(self, event):
await process.event(self)
self.send_presence()
await self["xep_0115"].update_caps()
await self.get_roster()
await muc.autojoin(self)
profile.set_identity(self, "client")
await profile.update(self)
task.ping_task(self)
# Service.commands(self)
# Service.reactions(self)
self.service_commands()
self.service_reactions()
async def on_session_resumed(self, event):
await process.event(self)
self.send_presence()
self["xep_0115"].update_caps()
await muc.autojoin(self)
profile.set_identity(self, "client")
# Service.commands(self)
# Service.reactions(self)
self.service_commands()
self.service_reactions()
# TODO Request for subscription
@ -305,3 +322,301 @@ class Slixfeed(slixmpp.ClientXMPP):
jid = message['from'].bare
# await task.clean_tasks_xmpp(jid, ['status'])
await task.start_tasks_xmpp(self, jid, ['status'])
# TODO Move class Service to a separate file
# class Service(Slixfeed):
# def __init__(self):
# super().__init__()
# TODO https://xmpp.org/extensions/xep-0115.html
# https://xmpp.org/extensions/xep-0444.html#disco
# TODO https://xmpp.org/extensions/xep-0444.html#disco-restricted
def service_reactions(self):
"""
Publish allow list of reactions.
Parameters
----------
None.
Returns
-------
None.
"""
form = self['xep_0004'].make_form(
'form', 'Reactions Information'
)
# TODO Move class Command to a separate file
# class Command(Slixfeed):
# def __init__(self):
# super().__init__()
def service_commands(self):
# self["xep_0050"].add_command(
# node="updates_enable",
# name="Enable/Disable News Updates",
# handler=option_enable_updates,
# )
# if jid == config.get_value('accounts', 'XMPP', 'operator'):
# self['xep_0050'].add_command(node='bookmarks',
# name='Bookmarks',
# handler=self._handle_bookmarks)
# self['xep_0050'].add_command(node='roster',
# name='Roster',
# handler=self._handle_roster)
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)
# self['xep_0050'].add_command(node='search',
# name='Search',
# handler=self._handle_search)
# self['xep_0050'].add_command(node='filters',
# name='Filters',
# handler=self._handle_filters)
async def _handle_subscriptions(self, iq, session):
form = self['xep_0004'].make_form('form', 'Subscriptions')
form['instructions'] = '📰️ Manage subscriptions.'
# form.addField(var='interval',
# ftype='text-single',
# label='Interval period')
options = form.add_field(var='subscriptions',
ftype='list-multi',
label='Select subscriptions',
desc='Select subscription(s) to edit.')
jid = session['from'].bare
jid_file = jid
db_file = config.get_pathname_to_database(jid_file)
subscriptions = await sqlite.get_feeds(db_file)
for subscription in subscriptions:
title = subscription[0]
url = subscription[1]
options.addOption(title, url)
session['payload'] = form
session['next'] = self._handle_subscription_editor
session['has_next'] = True
# Other useful session values:
# session['to'] -- The JID that received the
# command request.
# session['from'] -- The JID that sent the
# command request.
# session['has_next'] = True -- There are more steps to complete
# session['allow_complete'] = True -- Allow user to finish immediately
# and possibly skip steps
# session['cancel'] = handler -- Assign a handler for if the user
# cancels the command.
# session['notes'] = [ -- Add informative notes about the
# ('info', 'Info message'), command's results.
# ('warning', 'Warning message'),
# ('error', 'Error message')]
return session
# TODO Make form for a single subscription and several subscriptions
# single: Delete, Disable, Reset and Rename
# several: Delete, Disable, Reset
async def _handle_subscription_editor(self, iq, session):
form = self['xep_0004'].make_form('form', 'Subscriptions')
form['instructions'] = '🗞️ Edit subscriptions.'
options = form.add_field(var='enable',
ftype='boolean',
label='Enable',
value=True)
options = form.add_field(var='action',
ftype='list-single',
label='Action',
value='reset')
options.addOption('Delete', 'delete')
options.addOption('Reset', 'reset')
session['payload'] = form
session['next'] = None
session['has_next'] = False
return session
async def _handle_bookmarks(self, iq, session):
form = self['xep_0004'].make_form('form', 'Bookmarks')
form['instructions'] = '📑️ Organize bookmarks.'
options = form.add_field(var='bookmarks',
# ftype='list-multi'
ftype='list-single',
label='Select a bookmark',
desc='Select a bookmark to edit.')
conferences = await XmppBookmark.get(self)
for conference in conferences:
options.addOption(conference['jid'], conference['jid'])
session['payload'] = form
session['next'] = self._handle_command_complete
session['has_next'] = False
return session
async def _handle_bookmarks_editor(self, iq, session):
form = self['xep_0004'].make_form('form', 'Bookmarks')
form['instructions'] = '📝️ Edit bookmarks.'
form.addField(var='name',
ftype='text-single',
label='Name')
form.addField(var='host',
ftype='text-single',
label='Host',
required=True)
form.addField(var='room',
ftype='text-single',
label='Room',
required=True)
form.addField(var='alias',
ftype='text-single',
label='Alias')
form.addField(var='password',
ftype='text-private',
label='Password')
form.add_field(var='autojoin',
ftype='boolean',
label='Auto-join',
value=True)
options = form.add_field(var='action',
ftype='list-single',
label='Action',
value='join')
options.addOption('Add', 'add')
options.addOption('Join', 'join')
options.addOption('Remove', 'remove')
session['payload'] = form
session['next'] = None
session['has_next'] = False
return session
async def _handle_settings(self, iq, session):
"""
Respond to the initial request for a command.
Arguments:
iq -- The iq stanza containing the command request.
session -- A dictionary of data relevant to the command
session. Additional, custom data may be saved
here to persist across handler callbacks.
"""
form = self['xep_0004'].make_form('form', 'Settings')
form['instructions'] = ('📮️ Customize news updates.')
jid = session['from'].bare
jid_file = jid
db_file = config.get_pathname_to_database(jid_file)
value = await config.get_setting_value(db_file, 'enabled')
value = int(value)
if value:
value = True
else:
value = False
form.add_field(var='enabled',
ftype='boolean',
label='Enable',
desc='Enable news updates.',
value=value)
value = await config.get_setting_value(db_file, 'old')
value = int(value)
if value:
value = False
else:
value = True
form.add_field(var='old',
ftype='boolean',
desc='Mark items of newly added subscriptions as read.',
# label='Send only new items',
label='Include old news',
value=value)
value = await config.get_setting_value(db_file, 'interval')
value = str(int(value/60))
options = form.add_field(var='interval',
ftype='list-single',
label='Interval',
desc='Set interval update (in hours).',
value=value)
i = 60
while i <= 2880:
var = str(i)
lab = str(int(i/60))
options.addOption(lab, var)
i += 60
value = await config.get_setting_value(db_file, 'archive')
value = str(value)
options = form.add_field(var='archive',
ftype='list-single',
label='Archive',
desc='Number of news items to archive.',
value=value)
i = 0
while i <= 500:
x = str(i)
options.addOption(x, x)
i += 1
value = await config.get_setting_value(db_file, 'quantum')
value = str(value)
options = form.add_field(var='quantum',
ftype='list-single',
label='Amount',
desc='Set amount of updates per update.',
value='3')
i = 1
while i <= 10:
x = str(i)
options.addOption(x, x)
i += 1
session['payload'] = form
session['next'] = self._handle_settings_complete
session['has_next'] = False
return session
async def _handle_settings_complete(self, payload, session):
"""
Process a command result from the user.
Arguments:
payload -- Either a single item, such as a form, or a list
of items or forms if more than one form was
provided to the user. The payload may be any
stanza, such as jabber:x:oob for out of band
data, or jabber:x:data for typical data forms.
session -- A dictionary of data relevant to the command
session. Additional, custom data may be saved
here to persist across handler callbacks.
"""
jid = session['from'].bare
jid_file = jid
db_file = config.get_pathname_to_database(jid_file)
# In this case (as is typical), the payload is a form
form = payload
values = form['values']
for value in values:
key = value
val = values[value]
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])
match value:
case 'enabled':
pass
case 'interval':
pass
# Having no return statement is the same as unsetting the 'payload'
# and 'next' session values and returning the session.
# Unless it is the final step, always return the session dictionary.
session['payload'] = None
session['next'] = None
return session

View file

@ -60,7 +60,9 @@ from slixmpp.plugins.xep_0048.stanza import Bookmarks
# import xml.etree.ElementTree as ET
# from lxml import etree
# import slixfeed.xmpp.bookmark as bookmark
import slixfeed.config as config
import slixfeed.sqlite as sqlite
from slixfeed.xmpp.bookmark import XmppBookmark
import slixfeed.xmpp.connect as connect
# NOTE MUC is possible for component
# import slixfeed.xmpp.muc as muc
@ -97,35 +99,54 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.on_session_start)
self.add_event_handler("session_resumed", self.on_session_resumed)
self.add_event_handler("session_start",
self.on_session_start)
self.add_event_handler("session_resumed",
self.on_session_resumed)
self.add_event_handler("got_offline", print("got_offline"))
# self.add_event_handler("got_online", self.check_readiness)
self.add_event_handler("changed_status", self.on_changed_status)
self.add_event_handler("presence_available", self.on_presence_available)
self.add_event_handler("presence_unavailable", self.on_presence_unavailable)
self.add_event_handler("chatstate_active", self.on_chatstate_active)
self.add_event_handler("chatstate_gone", self.on_chatstate_gone)
self.add_event_handler("chatstate_composing", self.check_chatstate_composing)
self.add_event_handler("chatstate_paused", self.check_chatstate_paused)
self.add_event_handler("changed_status",
self.on_changed_status)
self.add_event_handler("presence_available",
self.on_presence_available)
self.add_event_handler("presence_unavailable",
self.on_presence_unavailable)
self.add_event_handler("chatstate_active",
self.on_chatstate_active)
self.add_event_handler("chatstate_composing",
self.on_chatstate_composing)
self.add_event_handler("chatstate_gone",
self.on_chatstate_gone)
self.add_event_handler("chatstate_inactive",
self.on_chatstate_inactive)
self.add_event_handler("chatstate_paused",
self.on_chatstate_paused)
# The message event is triggered whenever a message
# stanza is received. Be aware that that includes
# MUC messages and error messages.
self.add_event_handler("message", self.on_message)
self.add_event_handler("message",
self.on_message)
self.add_event_handler("groupchat_invite", self.on_groupchat_invite) # XEP_0045
self.add_event_handler("groupchat_direct_invite", self.on_groupchat_direct_invite) # XEP_0249
self.add_event_handler("groupchat_invite",
self.on_groupchat_invite) # XEP_0045
self.add_event_handler("groupchat_direct_invite",
self.on_groupchat_direct_invite) # XEP_0249
# self.add_event_handler("groupchat_message", self.message)
# self.add_event_handler("disconnected", self.reconnect)
# self.add_event_handler("disconnected", self.inspect_connection)
self.add_event_handler("reactions", self.on_reactions)
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", self.on_presence_subscribed)
self.add_event_handler("presence_unsubscribed", self.on_presence_unsubscribed)
self.add_event_handler("reactions",
self.on_reactions)
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",
self.on_presence_subscribed)
self.add_event_handler("presence_unsubscribed",
self.on_presence_unsubscribed)
# Initialize event loop
# self.loop = asyncio.get_event_loop()
@ -159,37 +180,34 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
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 process.event_component(self)
await self["xep_0115"].update_caps()
# await muc.autojoin(self)
profile.set_identity(self, "service")
await profile.update(self)
connect.ping_task(self)
task.ping_task(self)
# await Service.capabilities(self)
# Service.commands(self)
# Service.reactions(self)
await self.service_capabilities()
self.service_commands()
self.service_reactions()
async def on_session_resumed(self, event):
await process.event_component(self)
self.send_presence()
self["xep_0115"].update_caps()
# await muc.autojoin(self)
profile.set_identity(self, "service")
# await Service.capabilities(self)
# Service.commands(self)
# Service.reactions(self)
await self.service_capabilities()
self.service_commands()
self.service_reactions()
@ -207,7 +225,11 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
async def on_changed_status(self, presence):
await task.check_readiness(self, presence)
# await task.check_readiness(self, presence)
jid = presence['from'].bare
if presence['show'] in ('away', 'dnd', 'xa'):
await task.clean_tasks_xmpp(jid, ['interval'])
await task.start_tasks_xmpp(self, jid, ['status', 'check'])
async def on_presence_subscribe(self, presence):
@ -228,7 +250,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
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)
# await task.start_tasks(self, presence)
# NOTE Already done inside the start-task function
jid = presence["from"].bare
await task.start_tasks_xmpp(self, jid)
async def on_presence_unsubscribed(self, presence):
@ -237,12 +262,20 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
async def on_presence_unavailable(self, presence):
jid = presence["from"].bare
await task.stop_tasks(self, jid)
# await task.stop_tasks(self, jid)
await task.clean_tasks_xmpp(jid)
# TODO
# Send message that database will be deleted within 30 days
# Check whether JID is in bookmarks or roster
# If roster, remove contact JID into file
# If bookmarks, remove groupchat JID into file
async def on_presence_error(self, presence):
print("on_presence_error")
print(presence)
jid = presence["from"].bare
await task.clean_tasks_xmpp(jid)
async def on_reactions(self, message):
@ -253,28 +286,332 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
async def on_chatstate_active(self, message):
if message['type'] in ('chat', 'normal'):
jid = message['from'].bare
await task.clean_tasks_xmpp(jid, ['status'])
# await task.clean_tasks_xmpp(jid, ['status'])
await task.start_tasks_xmpp(self, jid, ['status'])
async def on_chatstate_composing(self, message):
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)
async def on_chatstate_gone(self, message):
if message['type'] in ('chat', 'normal'):
jid = message['from'].bare
await task.clean_tasks_xmpp(jid, ['status'])
# await task.clean_tasks_xmpp(jid, ['status'])
await task.start_tasks_xmpp(self, jid, ['status'])
async def check_chatstate_composing(self, message):
async def on_chatstate_inactive(self, message):
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)
async def check_chatstate_paused(self, message):
if message['type'] in ('chat', 'normal'):
jid = message['from'].bare
await task.clean_tasks_xmpp(jid, ['status'])
# await task.clean_tasks_xmpp(jid, ['status'])
await task.start_tasks_xmpp(self, jid, ['status'])
async def on_chatstate_paused(self, message):
if message['type'] in ('chat', 'normal'):
jid = message['from'].bare
# await task.clean_tasks_xmpp(jid, ['status'])
await task.start_tasks_xmpp(self, jid, ['status'])
# TODO Move class Service to a separate file
# class Service(Slixfeed):
# def __init__(self):
# super().__init__()
# TODO https://xmpp.org/extensions/xep-0115.html
# https://xmpp.org/extensions/xep-0444.html#disco
# TODO https://xmpp.org/extensions/xep-0444.html#disco-restricted
def service_reactions(self):
"""
Publish allow list of reactions.
Parameters
----------
None.
Returns
-------
None.
"""
form = self['xep_0004'].make_form(
'form', 'Reactions Information'
)
# TODO Move class Command to a separate file
# class Command(Slixfeed):
# def __init__(self):
# super().__init__()
def service_commands(self):
# self["xep_0050"].add_command(
# node="updates_enable",
# name="Enable/Disable News Updates",
# handler=option_enable_updates,
# )
# if jid == config.get_value('accounts', 'XMPP', 'operator'):
# self['xep_0050'].add_command(node='bookmarks',
# name='Bookmarks',
# handler=self._handle_bookmarks)
# self['xep_0050'].add_command(node='roster',
# name='Roster',
# handler=self._handle_roster)
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)
# self['xep_0050'].add_command(node='search',
# name='Search',
# handler=self._handle_search)
# self['xep_0050'].add_command(node='filters',
# name='Filters',
# handler=self._handle_filters)
async def _handle_subscriptions(self, iq, session):
form = self['xep_0004'].make_form('form', 'Subscriptions')
form['instructions'] = '📰️ Manage subscriptions.'
# form.addField(var='interval',
# ftype='text-single',
# label='Interval period')
options = form.add_field(var='subscriptions',
ftype='list-multi',
label='Select subscriptions',
desc='Select subscription(s) to edit.')
jid = session['from'].bare
jid_file = jid
db_file = config.get_pathname_to_database(jid_file)
subscriptions = await sqlite.get_feeds(db_file)
for subscription in subscriptions:
title = subscription[0]
url = subscription[1]
options.addOption(title, url)
session['payload'] = form
session['next'] = self._handle_subscription_editor
session['has_next'] = True
# Other useful session values:
# session['to'] -- The JID that received the
# command request.
# session['from'] -- The JID that sent the
# command request.
# session['has_next'] = True -- There are more steps to complete
# session['allow_complete'] = True -- Allow user to finish immediately
# and possibly skip steps
# session['cancel'] = handler -- Assign a handler for if the user
# cancels the command.
# session['notes'] = [ -- Add informative notes about the
# ('info', 'Info message'), command's results.
# ('warning', 'Warning message'),
# ('error', 'Error message')]
return session
# TODO Make form for a single subscription and several subscriptions
# single: Delete, Disable, Reset and Rename
# several: Delete, Disable, Reset
async def _handle_subscription_editor(self, iq, session):
form = self['xep_0004'].make_form('form', 'Subscriptions')
form['instructions'] = '🗞️ Edit subscriptions.'
options = form.add_field(var='enable',
ftype='boolean',
label='Enable',
value=True)
options = form.add_field(var='action',
ftype='list-single',
label='Action',
value='reset')
options.addOption('Delete', 'delete')
options.addOption('Reset', 'reset')
session['payload'] = form
session['next'] = None
session['has_next'] = False
return session
async def _handle_bookmarks(self, iq, session):
form = self['xep_0004'].make_form('form', 'Bookmarks')
form['instructions'] = '📑️ Organize bookmarks.'
options = form.add_field(var='bookmarks',
# ftype='list-multi'
ftype='list-single',
label='Select a bookmark',
desc='Select a bookmark to edit.')
conferences = await XmppBookmark.get(self)
for conference in conferences:
options.addOption(conference['jid'], conference['jid'])
session['payload'] = form
session['next'] = self._handle_command_complete
session['has_next'] = False
return session
async def _handle_bookmarks_editor(self, iq, session):
form = self['xep_0004'].make_form('form', 'Bookmarks')
form['instructions'] = '📝️ Edit bookmarks.'
form.addField(var='name',
ftype='text-single',
label='Name')
form.addField(var='host',
ftype='text-single',
label='Host',
required=True)
form.addField(var='room',
ftype='text-single',
label='Room',
required=True)
form.addField(var='alias',
ftype='text-single',
label='Alias')
form.addField(var='password',
ftype='text-private',
label='Password')
form.add_field(var='autojoin',
ftype='boolean',
label='Auto-join',
value=True)
options = form.add_field(var='action',
ftype='list-single',
label='Action',
value='join')
options.addOption('Add', 'add')
options.addOption('Join', 'join')
options.addOption('Remove', 'remove')
session['payload'] = form
session['next'] = None
session['has_next'] = False
return session
async def _handle_settings(self, iq, session):
"""
Respond to the initial request for a command.
Arguments:
iq -- The iq stanza containing the command request.
session -- A dictionary of data relevant to the command
session. Additional, custom data may be saved
here to persist across handler callbacks.
"""
form = self['xep_0004'].make_form('form', 'Settings')
form['instructions'] = ('📮️ Customize news updates.')
jid = session['from'].bare
jid_file = jid
db_file = config.get_pathname_to_database(jid_file)
value = await config.get_setting_value(db_file, 'enabled')
value = int(value)
if value:
value = True
else:
value = False
form.add_field(var='enabled',
ftype='boolean',
label='Enable',
desc='Enable news updates.',
value=value)
value = await config.get_setting_value(db_file, 'old')
value = int(value)
if value:
value = False
else:
value = True
form.add_field(var='old',
ftype='boolean',
desc='Mark items of newly added subscriptions as read.',
# label='Send only new items',
label='Include old news',
value=value)
value = await config.get_setting_value(db_file, 'interval')
value = str(int(value/60))
options = form.add_field(var='interval',
ftype='list-single',
label='Interval',
desc='Set interval update (in hours).',
value=value)
i = 60
while i <= 2880:
var = str(i)
lab = str(int(i/60))
options.addOption(lab, var)
i += 60
value = await config.get_setting_value(db_file, 'archive')
value = str(value)
options = form.add_field(var='archive',
ftype='list-single',
label='Archive',
desc='Number of news items to archive.',
value=value)
i = 0
while i <= 500:
x = str(i)
options.addOption(x, x)
i += 1
value = await config.get_setting_value(db_file, 'quantum')
value = str(value)
options = form.add_field(var='quantum',
ftype='list-single',
label='Amount',
desc='Set amount of updates per update.',
value='3')
i = 1
while i <= 10:
x = str(i)
options.addOption(x, x)
i += 1
session['payload'] = form
session['next'] = self._handle_settings_complete
session['has_next'] = False
return session
async def _handle_settings_complete(self, payload, session):
"""
Process a command result from the user.
Arguments:
payload -- Either a single item, such as a form, or a list
of items or forms if more than one form was
provided to the user. The payload may be any
stanza, such as jabber:x:oob for out of band
data, or jabber:x:data for typical data forms.
session -- A dictionary of data relevant to the command
session. Additional, custom data may be saved
here to persist across handler callbacks.
"""
jid = session['from'].bare
jid_file = jid
db_file = config.get_pathname_to_database(jid_file)
# In this case (as is typical), the payload is a form
form = payload
values = form['values']
for value in values:
key = value
val = values[value]
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])
match value:
case 'enabled':
pass
case 'interval':
pass
# Having no return statement is the same as unsetting the 'payload'
# and 'next' session values and returning the session.
# Unless it is the final step, always return the session dictionary.
session['payload'] = None
session['next'] = None
return session

37
slixfeed/xmpp/message.py Normal file
View file

@ -0,0 +1,37 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from slixfeed.xmpp.utility import get_chat_type
class XmppMessage:
async def send(self, jid, message, chat_type=None):
if not chat_type:
chat_type = await get_chat_type(self, jid)
self.send_message(
mto=jid,
mfrom=self.boundjid.bare,
mbody=message,
mtype=chat_type
)
async def send_oob(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 send_reply(self, message, response):
message.reply(response).send()

View file

@ -17,7 +17,6 @@ FIXME
"""
import logging
import slixfeed.xmpp.bookmark as bookmark
import slixfeed.xmpp.process as process
from slixfeed.dt import current_time

View file

@ -21,32 +21,19 @@ TODO
import logging
import os
import slixfeed.action as action
from slixfeed.config import (
add_to_list,
get_default_cache_directory,
get_default_data_directory,
get_value,
get_pathname_to_database,
remove_from_list)
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
import slixfeed.xmpp.bookmark as bookmark
from slixfeed.xmpp.bookmark import XmppBookmark
import slixfeed.xmpp.muc as groupchat
import slixfeed.xmpp.status as status
from slixfeed.xmpp.status import XmppStatus
import slixfeed.xmpp.upload as upload
from slixfeed.xmpp.utility import get_chat_type
import time
async def event_component(self):
self.send_presence()
async def event(self):
self.send_presence()
await self.get_roster()
# for task in main_task:
# task.cancel()
@ -101,9 +88,8 @@ async def message(self, message):
await task.clean_tasks_xmpp(jid, ['status'])
status_type = 'dnd'
status_message = '📥️ Procesing request to import feeds...'
status.send(
self, jid, status_message, status_type)
db_file = get_pathname_to_database(jid_file)
await XmppStatus.send(self, jid, status_message, status_type)
db_file = config.get_pathname_to_database(jid_file)
count = await action.import_opml(db_file, url)
if count:
response = 'Successfully imported {} feeds.'.format(count)
@ -163,7 +149,7 @@ async def message(self, message):
# # Begin processing new JID
# # Deprecated in favour of event 'presence_available'
# db_dir = get_default_data_directory()
# db_dir = config.get_default_data_directory()
# os.chdir(db_dir)
# if jid + '.db' not in os.listdir():
# await task_jid(jid)
@ -301,7 +287,7 @@ async def message(self, message):
if not title:
title = uri.get_hostname(url)
if url.startswith('http'):
db_file = get_pathname_to_database(jid_file)
db_file = config.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)
@ -334,9 +320,9 @@ async def message(self, message):
key = 'filter-' + message_text[:5]
val = message_text[7:]
if val:
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
keywords = await sqlite.get_filters_value(db_file, key)
val = await add_to_list(val, keywords)
val = await config.add_to_list(val, keywords)
if await sqlite.get_filters_value(db_file, key):
await sqlite.update_filters_value(db_file,
[key, val])
@ -352,9 +338,9 @@ async def message(self, message):
key = 'filter-' + message_text[:5]
val = message_text[7:]
if val:
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
keywords = await sqlite.get_filters_value(db_file, key)
val = await remove_from_list(val, keywords)
val = await config.remove_from_list(val, keywords)
if await sqlite.get_filters_value(db_file, key):
await sqlite.update_filters_value(db_file,
[key, val])
@ -374,7 +360,7 @@ async def message(self, message):
if int(val) > 500:
response = 'Value may not be greater than 500.'
else:
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
if await sqlite.get_settings_value(db_file,
[key, val]):
await sqlite.update_settings_value(db_file,
@ -391,9 +377,9 @@ async def message(self, message):
response = 'Missing value.'
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('bookmark -'):
if jid == get_value('accounts', 'XMPP', 'operator'):
if jid == config.get_value('accounts', 'XMPP', 'operator'):
muc_jid = message_text[11:]
await bookmark.remove(self, muc_jid)
await XmppBookmark.remove(self, muc_jid)
response = ('Groupchat {} has been removed '
'from bookmarks.'
.format(muc_jid))
@ -402,7 +388,7 @@ async def message(self, message):
'Type: removing bookmarks.')
send_reply_message(self, message, response)
case 'bookmarks':
if jid == get_value('accounts', 'XMPP', 'operator'):
if jid == config.get_value('accounts', 'XMPP', 'operator'):
response = await action.list_bookmarks(self)
else:
response = ('This action is restricted. '
@ -412,9 +398,9 @@ async def message(self, message):
key = 'filter-' + message_text[:4]
val = message_text[6:]
if val:
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
keywords = await sqlite.get_filters_value(db_file, key)
val = await add_to_list(val, keywords)
val = await config.add_to_list(val, keywords)
if await sqlite.get_filters_value(db_file, key):
await sqlite.update_filters_value(db_file,
[key, val])
@ -430,9 +416,9 @@ async def message(self, message):
key = 'filter-' + message_text[:4]
val = message_text[6:]
if val:
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
keywords = await sqlite.get_filters_value(db_file, key)
val = await remove_from_list(val, keywords)
val = await config.remove_from_list(val, keywords)
if await sqlite.get_filters_value(db_file, key):
await sqlite.update_filters_value(db_file,
[key, val])
@ -451,16 +437,16 @@ async def message(self, message):
status_message = ('📤️ Procesing request to '
'export feeds into {}...'
.format(ex))
status.send(
self, jid, status_message, status_type)
cache_dir = get_default_cache_directory()
await XmppStatus.send(self, jid, status_message,
status_type)
cache_dir = config.get_default_cache_directory()
if not os.path.isdir(cache_dir):
os.mkdir(cache_dir)
if not os.path.isdir(cache_dir + '/' + ex):
os.mkdir(cache_dir + '/' + ex)
filename = os.path.join(
cache_dir, ex, 'slixfeed_' + timestamp() + '.' + ex)
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
results = await sqlite.get_feeds(db_file)
match ex:
case 'html':
@ -505,9 +491,10 @@ async def message(self, message):
status_message = ('📃️ Procesing request to '
'produce {} document...'
.format(ext.upper()))
status.send(self, jid, status_message, status_type)
db_file = get_pathname_to_database(jid_file)
cache_dir = get_default_cache_directory()
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)
@ -578,9 +565,9 @@ async def message(self, message):
# status_message = (
# '📥️ Procesing request to import feeds...'
# )
# status.send(
# await XmppStatus.send(
# self, jid, status_message, status_type)
# db_file = get_pathname_to_database(jid_file)
# db_file = config.get_pathname_to_database(jid_file)
# count = await action.import_opml(db_file, url)
# if count:
# response = (
@ -603,11 +590,11 @@ async def message(self, message):
status_message = ('📫️ Processing request '
'to fetch data from {}'
.format(url))
status.send(self, jid, status_message, status_type)
await XmppStatus.send(self, jid, status_message, status_type)
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)
db_file = config.get_pathname_to_database(jid_file)
# try:
response = await action.add_feed(db_file, url)
# await task.clean_tasks_xmpp(jid, ['status'])
@ -623,49 +610,28 @@ async def message(self, message):
query = message_text[6:]
if query:
if len(query) > 3:
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
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)
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)
case 'goodbye':
if message['type'] == 'groupchat':
await groupchat.leave(self, jid)
await bookmark.remove(self, muc_jid)
await XmppBookmark.remove(self, muc_jid)
else:
response = 'This command is valid in 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)
await action.xmpp_change_interval(
self, key, val, jid, jid_file, message=message)
case _ if message_lowercase.startswith('join'):
muc_jid = uri.check_xmpp_uri(message_text[5:])
if muc_jid:
@ -684,7 +650,7 @@ async def message(self, message):
if val:
try:
val = int(val)
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
if await sqlite.get_settings_value(db_file,
[key, val]):
await sqlite.update_settings_value(db_file,
@ -711,7 +677,7 @@ async def message(self, message):
# get_settings_value,
# key
# )
# val = await add_to_list(
# val = await config.add_to_list(
# val,
# names
# )
@ -728,7 +694,7 @@ async def message(self, message):
# response = 'Missing value.'
send_reply_message(self, message, response)
case 'new':
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
key = 'old'
val = 0
if await sqlite.get_settings_value(db_file, key):
@ -765,7 +731,7 @@ async def message(self, message):
# )
# await refresh_task(jid, key, val)
case 'old':
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
key = 'old'
val = 1
if await sqlite.get_settings_value(db_file, key):
@ -783,11 +749,13 @@ async def message(self, message):
# response = (
# 'Every update will contain {} news items.'
# ).format(response)
db_file = get_pathname_to_database(jid_file)
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])
await sqlite.update_settings_value(db_file,
[key, val])
else:
await sqlite.set_settings_value(db_file, [key, val])
await sqlite.set_settings_value(db_file,
[key, val])
response = ('Next update will contain {} news items.'
.format(val))
except:
@ -808,8 +776,7 @@ async def message(self, message):
status_type = 'dnd'
status_message = ('📫️ Processing request to fetch data from {}'
.format(url))
status.send(
self, jid, status_message, status_type)
await XmppStatus.send(self, jid, status_message, status_type)
if url.startswith('feed:'):
url = uri.feed_to_http(url)
url = (uri.replace_hostname(url, 'feed')) or url
@ -839,7 +806,7 @@ async def message(self, message):
if num < 1 or num > 50:
response = 'Value must be ranged from 1 to 50.'
else:
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
result = await sqlite.last_entries(db_file, num)
response = action.list_last_entries(result, num)
except:
@ -850,7 +817,7 @@ async def message(self, message):
case _ if message_lowercase.startswith('remove'):
ix_url = message_text[7:]
if ix_url:
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
try:
ix = int(ix_url)
url = sqlite.get_feed_url(db_file, ix)
@ -899,10 +866,10 @@ async def message(self, message):
await task.clean_tasks_xmpp(jid, ['status'])
status_type = 'dnd'
status_message = '📫️ Marking entries as read...'
status.send(self, jid, status_message, status_type)
db_file = get_pathname_to_database(jid_file)
await XmppStatus.send(self, jid, status_message, status_type)
db_file = config.get_pathname_to_database(jid_file)
if ix_url:
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
try:
ix = int(ix_url)
url = sqlite.get_feed_url(db_file, ix)
@ -944,7 +911,7 @@ async def message(self, message):
query = message_text[7:]
if query:
if len(query) > 1:
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
results = await sqlite.search_entries(db_file, query)
response = action.list_search_results(query, results)
else:
@ -956,7 +923,7 @@ async def message(self, message):
# response = 'Updates are enabled.'
key = 'enabled'
val = 1
db_file = get_pathname_to_database(jid_file)
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:
@ -967,12 +934,12 @@ async def message(self, message):
# print(task_manager[jid])
send_reply_message(self, message, response)
case 'stats':
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
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)
db_file = config.get_pathname_to_database(jid_file)
try:
await sqlite.set_enabled_status(db_file, ix, 0)
response = ('Updates are now disabled for news source {}.'
@ -982,7 +949,7 @@ async def message(self, message):
send_reply_message(self, message, response)
case _ if message_lowercase.startswith('enable'):
ix = message_text[7:]
db_file = get_pathname_to_database(jid_file)
db_file = config.get_pathname_to_database(jid_file)
try:
await sqlite.set_enabled_status(db_file, ix, 1)
response = ('Updates are now enabled for news source {}.'
@ -991,40 +958,7 @@ async def message(self, message):
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'
status.send(self, jid, status_message, status_type)
await action.xmpp_stop_updates(self, message, jid, jid_file)
case 'support':
# TODO Send an invitation.
response = 'Join xmpp:slixfeed@chat.woodpeckersnest.space?join'
@ -1057,7 +991,7 @@ async def message(self, message):
send_reply_message(self, message, response)
if not response: response = 'EMPTY MESSAGE - ACTION ONLY'
data_dir = get_default_data_directory()
data_dir = config.get_default_data_directory()
if not os.path.isdir(data_dir):
os.mkdir(data_dir)
if not os.path.isdir(data_dir + '/logs/'):

View file

@ -31,6 +31,7 @@ from slixfeed.config import get_value, get_default_config_directory
# import logging
import os
# class XmppProfile:
async def update(self):
"""

View file

@ -1,10 +1,19 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
def send(self, jid, status_message, status_type=None):
self.send_presence(
pshow=status_type,
pstatus=status_message,
pfrom=self.boundjid.bare,
pto=jid
)
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
)