forked from sch/Slixfeed
Add ad-hoc commands
This commit is contained in:
parent
56aaccce68
commit
422e0669f1
12 changed files with 964 additions and 247 deletions
|
@ -129,7 +129,7 @@ class JabberComponent:
|
||||||
xmpp.register_plugin('xep_0066') # Out of Band Data
|
xmpp.register_plugin('xep_0066') # Out of Band Data
|
||||||
xmpp.register_plugin('xep_0071') # XHTML-IM
|
xmpp.register_plugin('xep_0071') # XHTML-IM
|
||||||
xmpp.register_plugin('xep_0084') # User Avatar
|
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_0115') # Entity Capabilities
|
||||||
xmpp.register_plugin('xep_0153') # vCard-Based Avatars
|
xmpp.register_plugin('xep_0153') # vCard-Based Avatars
|
||||||
xmpp.register_plugin('xep_0199', {'keepalive': True}) # XMPP Ping
|
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_0066') # Out of Band Data
|
||||||
xmpp.register_plugin('xep_0071') # XHTML-IM
|
xmpp.register_plugin('xep_0071') # XHTML-IM
|
||||||
xmpp.register_plugin('xep_0084') # User Avatar
|
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_0115') # Entity Capabilities
|
||||||
xmpp.register_plugin('xep_0153') # vCard-Based Avatars
|
xmpp.register_plugin('xep_0153') # vCard-Based Avatars
|
||||||
xmpp.register_plugin('xep_0199', {'keepalive': True}) # XMPP Ping
|
xmpp.register_plugin('xep_0199', {'keepalive': True}) # XMPP Ping
|
||||||
|
|
|
@ -48,7 +48,10 @@ from slixfeed.url import (
|
||||||
replace_hostname,
|
replace_hostname,
|
||||||
trim_url
|
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
|
import tomllib
|
||||||
from urllib import error
|
from urllib import error
|
||||||
from urllib.parse import parse_qs, urlsplit
|
from urllib.parse import parse_qs, urlsplit
|
||||||
|
@ -56,28 +59,28 @@ import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import xml2epub
|
import xml2epub
|
||||||
except:
|
except ImportError:
|
||||||
logging.info(
|
logging.info(
|
||||||
"Package xml2epub was not found.\n"
|
"Package xml2epub was not found.\n"
|
||||||
"ePUB support is disabled.")
|
"ePUB support is disabled.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import html2text
|
import html2text
|
||||||
except:
|
except ImportError:
|
||||||
logging.info(
|
logging.info(
|
||||||
"Package html2text was not found.\n"
|
"Package html2text was not found.\n"
|
||||||
"Markdown support is disabled.")
|
"Markdown support is disabled.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import pdfkit
|
import pdfkit
|
||||||
except:
|
except ImportError:
|
||||||
logging.info(
|
logging.info(
|
||||||
"Package pdfkit was not found.\n"
|
"Package pdfkit was not found.\n"
|
||||||
"PDF support is disabled.")
|
"PDF support is disabled.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from readability import Document
|
from readability import Document
|
||||||
except:
|
except ImportError:
|
||||||
logging.info(
|
logging.info(
|
||||||
"Package readability was not found.\n"
|
"Package readability was not found.\n"
|
||||||
"Arc90 Lab algorithm is disabled.")
|
"Arc90 Lab algorithm is disabled.")
|
||||||
|
@ -107,6 +110,45 @@ def manual(filename, section=None, command=None):
|
||||||
return cmd_list
|
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):
|
def log_to_markdown(timestamp, filename, jid, message):
|
||||||
"""
|
"""
|
||||||
Log message to file.
|
Log message to file.
|
||||||
|
@ -404,7 +446,7 @@ def list_feeds(results):
|
||||||
|
|
||||||
|
|
||||||
async def list_bookmarks(self):
|
async def list_bookmarks(self):
|
||||||
conferences = await bookmark.get(self)
|
conferences = await XmppBookmark.get(self)
|
||||||
message = "\nList of groupchats:\n\n```\n"
|
message = "\nList of groupchats:\n\n```\n"
|
||||||
for conference in conferences:
|
for conference in conferences:
|
||||||
message += (
|
message += (
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[action]
|
[action]
|
||||||
url = """
|
url = """
|
||||||
<url>
|
<url>
|
||||||
Add given <url> to subscription list.
|
Add given <url> to subscription list (prefix http. Default).
|
||||||
"""
|
"""
|
||||||
add = """
|
add = """
|
||||||
add <url> <title>
|
add <url> <title>
|
||||||
|
@ -30,6 +30,17 @@ backup news text
|
||||||
Send a Plain Text file of your news items.
|
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]
|
[custom]
|
||||||
new = """
|
new = """
|
||||||
new
|
new
|
||||||
|
@ -70,10 +81,18 @@ Reset deny list.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[groupchat]
|
[groupchat]
|
||||||
|
uri = """
|
||||||
|
<muc>
|
||||||
|
Join groupchat by given <muc> (prefix xmpp).
|
||||||
|
"""
|
||||||
join = """
|
join = """
|
||||||
join <muc>
|
join <muc>
|
||||||
Join groupchat by given <muc>.
|
Join groupchat by given <muc>.
|
||||||
"""
|
"""
|
||||||
|
leave = """
|
||||||
|
goodbye
|
||||||
|
Leave groupchat and delete it from bookmarks.
|
||||||
|
"""
|
||||||
|
|
||||||
[manual]
|
[manual]
|
||||||
help = """
|
help = """
|
||||||
|
@ -122,10 +141,18 @@ Set maximum length of news item description. (0 for no limit)
|
||||||
"""
|
"""
|
||||||
quantum = """
|
quantum = """
|
||||||
quantum <number>
|
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]
|
[modification]
|
||||||
|
archive = """
|
||||||
|
archive <number>
|
||||||
|
Number of news items to archive (maximum value 500).
|
||||||
|
"""
|
||||||
remove = """
|
remove = """
|
||||||
remove <id>
|
remove <id>
|
||||||
Remove feed of from subscription list by given <id>.
|
Remove feed of from subscription list by given <id>.
|
||||||
|
@ -158,7 +185,7 @@ Disable bot and stop updates.
|
||||||
[preview]
|
[preview]
|
||||||
read = """
|
read = """
|
||||||
read <url>
|
read <url>
|
||||||
Display most recent 20 titles of given <url>.
|
Display most recent 5 titles of given <url>.
|
||||||
"""
|
"""
|
||||||
read_num = """
|
read_num = """
|
||||||
read <url> <index>
|
read <url> <index>
|
||||||
|
@ -180,10 +207,14 @@ Search news items by given <text>.
|
||||||
"""
|
"""
|
||||||
recent = """
|
recent = """
|
||||||
recent <number>
|
recent <number>
|
||||||
List recent <number> news items (up to 50 items).
|
List recent <number> news items (max. 50).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[statistics]
|
[statistics]
|
||||||
|
stats = """
|
||||||
|
stats
|
||||||
|
Show general statistics.
|
||||||
|
"""
|
||||||
analyses = """
|
analyses = """
|
||||||
analyses
|
analyses
|
||||||
Show report and statistics of feeds.
|
Show report and statistics of feeds.
|
||||||
|
|
|
@ -61,7 +61,7 @@ No operator was specified for this instance.
|
||||||
|
|
||||||
platforms = """
|
platforms = """
|
||||||
Supported platforms: XMPP
|
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.
|
For ideal experience, we recommend using XMPP.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -97,27 +97,36 @@ https://gitgud.io/sjehuda/slixfeed
|
||||||
"""
|
"""
|
||||||
|
|
||||||
thanks = """
|
thanks = """
|
||||||
Alixander Court (alixandercourt.com, Utah), \
|
Alixander Court <alixandercourt.com> (Utah), \
|
||||||
|
Chriss Farrell (SalixOS, Oregon), \
|
||||||
Christian Dersch (SalixOS), \
|
Christian Dersch (SalixOS), \
|
||||||
Cyrille Pontvieux (SalixOS, France), \
|
Cyrille Pontvieux <enialis.net> (SalixOS, France), \
|
||||||
Denis Fomin (Gajim, Russia), \
|
Denis Fomin (Gajim, Russia), \
|
||||||
Dimitris Tzemos (SalixOS, Greece), \
|
Dimitris Tzemos (SalixOS, Greece), \
|
||||||
Emmanuel Gil Peyrot (Poezio, France), \
|
Emmanuel Gil Peyrot (Poezio, France), \
|
||||||
Florent Le Coz (Poezio, France), \
|
Florent Le Coz (Poezio, France), \
|
||||||
George Vlahavas (SalixOS, Greece), \
|
George Vlahavas <vlahavas.com> (SalixOS, Greece), \
|
||||||
Guus der Kinderen from IgniteRealtime.org (Openfire, Netherlands), \
|
Guus der Kinderen <igniterealtime.org> (Openfire, Netherlands), \
|
||||||
habnabit_ from #python on irc.libera.chat, \
|
habnabit_ from #python on irc.libera.chat, \
|
||||||
|
Imar van Erven Dorens <simplicit.nl> (SalixOS, Netherlands), \
|
||||||
imattau (atomtopubsub), \
|
imattau (atomtopubsub), \
|
||||||
Jaussoin Timothée (Movim, France), \
|
Jaussoin Timothée <mov.im> (Movim, France), \
|
||||||
Kevin Smith from Isode (Swift, Wales), \
|
Justin Karneges <jblog.andbit.net> (Psi, California), \
|
||||||
|
Kevin Smith <isode.com> (Swift IM, Wales), \
|
||||||
|
Luis Henrique Mello (SalixOS, Brazil), \
|
||||||
magicfelix, \
|
magicfelix, \
|
||||||
|
Markus Muttilainen (SalixOS), \
|
||||||
Mathieu Pasquet (slixmpp, France), \
|
Mathieu Pasquet (slixmpp, France), \
|
||||||
Maxime Buquet (slixmpp, France), \
|
Maxime Buquet (slixmpp, France), \
|
||||||
|
Phillip Watkins (United Kingdom, SalixOS), \
|
||||||
Pierrick Le Brun (SalixOS, France), \
|
Pierrick Le Brun (SalixOS, France), \
|
||||||
Raphael Groner (Fedora, Germany), \
|
Raphael Groner (Fedora, Germany), \
|
||||||
Remko Tronçon (Swift, Germany), \
|
Remko Tronçon <mko.re> (Psi , Belgium), \
|
||||||
Simone "roughnecks" Canaletti (woodpeckersnest.space, Italy), \
|
Simone "roughnecks" Canaletti <woodpeckersnest.space> (Italy), \
|
||||||
|
Richard Lapointe (SalixOS, Connecticut), \
|
||||||
Strix from Loqi, \
|
Strix from Loqi, \
|
||||||
|
Thibaud Guerin (SalixOS), \
|
||||||
|
Tim Beech (SalixOS, Brazil), \
|
||||||
Thorsten Mühlfelder (SalixOS, Germany), \
|
Thorsten Mühlfelder (SalixOS, Germany), \
|
||||||
Yann Leboulanger (Gajim, France).
|
Yann Leboulanger (Gajim, France).
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -12,14 +12,17 @@ TODO
|
||||||
from slixmpp.plugins.xep_0048.stanza import Bookmarks
|
from slixmpp.plugins.xep_0048.stanza import Bookmarks
|
||||||
|
|
||||||
|
|
||||||
async def get(self):
|
class XmppBookmark:
|
||||||
|
|
||||||
|
|
||||||
|
async def get(self):
|
||||||
result = await self.plugin['xep_0048'].get_bookmarks()
|
result = await self.plugin['xep_0048'].get_bookmarks()
|
||||||
bookmarks = result['private']['bookmarks']
|
bookmarks = result['private']['bookmarks']
|
||||||
conferences = bookmarks['conferences']
|
conferences = bookmarks['conferences']
|
||||||
return conferences
|
return conferences
|
||||||
|
|
||||||
|
|
||||||
async def add(self, muc_jid):
|
async def add(self, muc_jid):
|
||||||
result = await self.plugin['xep_0048'].get_bookmarks()
|
result = await self.plugin['xep_0048'].get_bookmarks()
|
||||||
bookmarks = result['private']['bookmarks']
|
bookmarks = result['private']['bookmarks']
|
||||||
conferences = bookmarks['conferences']
|
conferences = bookmarks['conferences']
|
||||||
|
@ -46,7 +49,7 @@ async def add(self, muc_jid):
|
||||||
# await self['xep_0402'].publish(bm)
|
# await self['xep_0402'].publish(bm)
|
||||||
|
|
||||||
|
|
||||||
async def remove(self, muc_jid):
|
async def remove(self, muc_jid):
|
||||||
result = await self.plugin['xep_0048'].get_bookmarks()
|
result = await self.plugin['xep_0048'].get_bookmarks()
|
||||||
bookmarks = result['private']['bookmarks']
|
bookmarks = result['private']['bookmarks']
|
||||||
conferences = bookmarks['conferences']
|
conferences = bookmarks['conferences']
|
||||||
|
|
|
@ -60,7 +60,9 @@ from slixmpp.plugins.xep_0048.stanza import Bookmarks
|
||||||
# import xml.etree.ElementTree as ET
|
# import xml.etree.ElementTree as ET
|
||||||
# from lxml import etree
|
# 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.connect as connect
|
||||||
import slixfeed.xmpp.muc as muc
|
import slixfeed.xmpp.muc as muc
|
||||||
import slixfeed.xmpp.process as process
|
import slixfeed.xmpp.process as process
|
||||||
|
@ -169,7 +171,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
inviter = message["from"].bare
|
inviter = message["from"].bare
|
||||||
muc_jid = message['groupchat_invite']['jid']
|
muc_jid = message['groupchat_invite']['jid']
|
||||||
await muc.join(self, inviter, muc_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
|
# NOTE Tested with Gajim and Psi
|
||||||
|
@ -177,7 +179,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
inviter = message["from"].bare
|
inviter = message["from"].bare
|
||||||
muc_jid = message['groupchat_invite']['jid']
|
muc_jid = message['groupchat_invite']['jid']
|
||||||
await muc.join(self, inviter, muc_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):
|
async def on_session_end(self, event):
|
||||||
|
@ -191,18 +193,33 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
|
|
||||||
|
|
||||||
async def on_session_start(self, event):
|
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)
|
await muc.autojoin(self)
|
||||||
profile.set_identity(self, "client")
|
profile.set_identity(self, "client")
|
||||||
await profile.update(self)
|
await profile.update(self)
|
||||||
task.ping_task(self)
|
task.ping_task(self)
|
||||||
|
|
||||||
|
# Service.commands(self)
|
||||||
|
# Service.reactions(self)
|
||||||
|
|
||||||
|
self.service_commands()
|
||||||
|
self.service_reactions()
|
||||||
|
|
||||||
|
|
||||||
async def on_session_resumed(self, event):
|
async def on_session_resumed(self, event):
|
||||||
await process.event(self)
|
self.send_presence()
|
||||||
|
self["xep_0115"].update_caps()
|
||||||
await muc.autojoin(self)
|
await muc.autojoin(self)
|
||||||
profile.set_identity(self, "client")
|
profile.set_identity(self, "client")
|
||||||
|
|
||||||
|
# Service.commands(self)
|
||||||
|
# Service.reactions(self)
|
||||||
|
|
||||||
|
self.service_commands()
|
||||||
|
self.service_reactions()
|
||||||
|
|
||||||
|
|
||||||
# TODO Request for subscription
|
# TODO Request for subscription
|
||||||
async def on_message(self, message):
|
async def on_message(self, message):
|
||||||
|
@ -305,3 +322,301 @@ class Slixfeed(slixmpp.ClientXMPP):
|
||||||
jid = message['from'].bare
|
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'])
|
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
|
||||||
|
|
|
@ -60,7 +60,9 @@ from slixmpp.plugins.xep_0048.stanza import Bookmarks
|
||||||
# import xml.etree.ElementTree as ET
|
# import xml.etree.ElementTree as ET
|
||||||
# from lxml import etree
|
# 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.connect as connect
|
||||||
# NOTE MUC is possible for component
|
# NOTE MUC is possible for component
|
||||||
# import slixfeed.xmpp.muc as muc
|
# 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
|
# and the XML streams are ready for use. We want to
|
||||||
# listen for this event so that we we can initialize
|
# listen for this event so that we we can initialize
|
||||||
# our roster.
|
# our roster.
|
||||||
self.add_event_handler("session_start", self.on_session_start)
|
self.add_event_handler("session_start",
|
||||||
self.add_event_handler("session_resumed", self.on_session_resumed)
|
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_offline", print("got_offline"))
|
||||||
# self.add_event_handler("got_online", self.check_readiness)
|
# self.add_event_handler("got_online", self.check_readiness)
|
||||||
self.add_event_handler("changed_status", self.on_changed_status)
|
self.add_event_handler("changed_status",
|
||||||
self.add_event_handler("presence_available", self.on_presence_available)
|
self.on_changed_status)
|
||||||
self.add_event_handler("presence_unavailable", self.on_presence_unavailable)
|
self.add_event_handler("presence_available",
|
||||||
self.add_event_handler("chatstate_active", self.on_chatstate_active)
|
self.on_presence_available)
|
||||||
self.add_event_handler("chatstate_gone", self.on_chatstate_gone)
|
self.add_event_handler("presence_unavailable",
|
||||||
self.add_event_handler("chatstate_composing", self.check_chatstate_composing)
|
self.on_presence_unavailable)
|
||||||
self.add_event_handler("chatstate_paused", self.check_chatstate_paused)
|
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
|
# The message event is triggered whenever a message
|
||||||
# stanza is received. Be aware that that includes
|
# stanza is received. Be aware that that includes
|
||||||
# MUC messages and error messages.
|
# 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_invite",
|
||||||
self.add_event_handler("groupchat_direct_invite", self.on_groupchat_direct_invite) # XEP_0249
|
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("groupchat_message", self.message)
|
||||||
|
|
||||||
# self.add_event_handler("disconnected", self.reconnect)
|
# self.add_event_handler("disconnected", self.reconnect)
|
||||||
# self.add_event_handler("disconnected", self.inspect_connection)
|
# self.add_event_handler("disconnected", self.inspect_connection)
|
||||||
|
|
||||||
self.add_event_handler("reactions", self.on_reactions)
|
self.add_event_handler("reactions",
|
||||||
self.add_event_handler("presence_error", self.on_presence_error)
|
self.on_reactions)
|
||||||
self.add_event_handler("presence_subscribe", self.on_presence_subscribe)
|
self.add_event_handler("presence_error",
|
||||||
self.add_event_handler("presence_subscribed", self.on_presence_subscribed)
|
self.on_presence_error)
|
||||||
self.add_event_handler("presence_unsubscribed", self.on_presence_unsubscribed)
|
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
|
# Initialize event loop
|
||||||
# self.loop = asyncio.get_event_loop()
|
# self.loop = asyncio.get_event_loop()
|
||||||
|
@ -165,31 +186,28 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
|
|
||||||
async def on_session_start(self, event):
|
async def on_session_start(self, event):
|
||||||
self.send_presence()
|
self.send_presence()
|
||||||
await process.event_component(self)
|
await self["xep_0115"].update_caps()
|
||||||
# await muc.autojoin(self)
|
# await muc.autojoin(self)
|
||||||
profile.set_identity(self, "service")
|
profile.set_identity(self, "service")
|
||||||
await profile.update(self)
|
await profile.update(self)
|
||||||
connect.ping_task(self)
|
task.ping_task(self)
|
||||||
|
|
||||||
# await Service.capabilities(self)
|
|
||||||
# Service.commands(self)
|
# Service.commands(self)
|
||||||
# Service.reactions(self)
|
# Service.reactions(self)
|
||||||
|
|
||||||
await self.service_capabilities()
|
|
||||||
self.service_commands()
|
self.service_commands()
|
||||||
self.service_reactions()
|
self.service_reactions()
|
||||||
|
|
||||||
|
|
||||||
async def on_session_resumed(self, event):
|
async def on_session_resumed(self, event):
|
||||||
await process.event_component(self)
|
self.send_presence()
|
||||||
|
self["xep_0115"].update_caps()
|
||||||
# await muc.autojoin(self)
|
# await muc.autojoin(self)
|
||||||
profile.set_identity(self, "service")
|
profile.set_identity(self, "service")
|
||||||
|
|
||||||
# await Service.capabilities(self)
|
|
||||||
# Service.commands(self)
|
# Service.commands(self)
|
||||||
# Service.reactions(self)
|
# Service.reactions(self)
|
||||||
|
|
||||||
await self.service_capabilities()
|
|
||||||
self.service_commands()
|
self.service_commands()
|
||||||
self.service_reactions()
|
self.service_reactions()
|
||||||
|
|
||||||
|
@ -207,7 +225,11 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
|
|
||||||
|
|
||||||
async def on_changed_status(self, presence):
|
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):
|
async def on_presence_subscribe(self, presence):
|
||||||
|
@ -228,7 +250,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
|
|
||||||
async def on_presence_available(self, presence):
|
async def on_presence_available(self, presence):
|
||||||
# TODO Add function to check whether task is already running or not
|
# 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):
|
async def on_presence_unsubscribed(self, presence):
|
||||||
|
@ -237,12 +262,20 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
|
|
||||||
async def on_presence_unavailable(self, presence):
|
async def on_presence_unavailable(self, presence):
|
||||||
jid = presence["from"].bare
|
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):
|
async def on_presence_error(self, presence):
|
||||||
print("on_presence_error")
|
print("on_presence_error")
|
||||||
print(presence)
|
print(presence)
|
||||||
|
jid = presence["from"].bare
|
||||||
|
await task.clean_tasks_xmpp(jid)
|
||||||
|
|
||||||
|
|
||||||
async def on_reactions(self, message):
|
async def on_reactions(self, message):
|
||||||
|
@ -253,28 +286,332 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
||||||
async def on_chatstate_active(self, message):
|
async def on_chatstate_active(self, message):
|
||||||
if message['type'] in ('chat', 'normal'):
|
if message['type'] in ('chat', 'normal'):
|
||||||
jid = message['from'].bare
|
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'])
|
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):
|
async def on_chatstate_gone(self, message):
|
||||||
if message['type'] in ('chat', 'normal'):
|
if message['type'] in ('chat', 'normal'):
|
||||||
jid = message['from'].bare
|
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'])
|
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'):
|
if message['type'] in ('chat', 'normal'):
|
||||||
jid = message['from'].bare
|
jid = message['from'].bare
|
||||||
await task.clean_tasks_xmpp(jid, ['status'])
|
# 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.start_tasks_xmpp(self, 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
37
slixfeed/xmpp/message.py
Normal 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()
|
|
@ -17,7 +17,6 @@ FIXME
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import slixfeed.xmpp.bookmark as bookmark
|
|
||||||
import slixfeed.xmpp.process as process
|
import slixfeed.xmpp.process as process
|
||||||
from slixfeed.dt import current_time
|
from slixfeed.dt import current_time
|
||||||
|
|
||||||
|
|
|
@ -21,32 +21,19 @@ TODO
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import slixfeed.action as action
|
import slixfeed.action as action
|
||||||
from slixfeed.config import (
|
import slixfeed.config as config
|
||||||
add_to_list,
|
|
||||||
get_default_cache_directory,
|
|
||||||
get_default_data_directory,
|
|
||||||
get_value,
|
|
||||||
get_pathname_to_database,
|
|
||||||
remove_from_list)
|
|
||||||
from slixfeed.dt import current_time, timestamp
|
from slixfeed.dt import current_time, timestamp
|
||||||
import slixfeed.fetch as fetch
|
import slixfeed.fetch as fetch
|
||||||
import slixfeed.sqlite as sqlite
|
import slixfeed.sqlite as sqlite
|
||||||
import slixfeed.task as task
|
import slixfeed.task as task
|
||||||
import slixfeed.url as uri
|
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.muc as groupchat
|
||||||
import slixfeed.xmpp.status as status
|
from slixfeed.xmpp.status import XmppStatus
|
||||||
import slixfeed.xmpp.upload as upload
|
import slixfeed.xmpp.upload as upload
|
||||||
from slixfeed.xmpp.utility import get_chat_type
|
from slixfeed.xmpp.utility import get_chat_type
|
||||||
import time
|
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:
|
# for task in main_task:
|
||||||
# task.cancel()
|
# task.cancel()
|
||||||
|
@ -101,9 +88,8 @@ async def message(self, message):
|
||||||
await task.clean_tasks_xmpp(jid, ['status'])
|
await task.clean_tasks_xmpp(jid, ['status'])
|
||||||
status_type = 'dnd'
|
status_type = 'dnd'
|
||||||
status_message = '📥️ Procesing request to import feeds...'
|
status_message = '📥️ Procesing request to import feeds...'
|
||||||
status.send(
|
await XmppStatus.send(self, jid, status_message, status_type)
|
||||||
self, jid, status_message, status_type)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
db_file = get_pathname_to_database(jid_file)
|
|
||||||
count = await action.import_opml(db_file, url)
|
count = await action.import_opml(db_file, url)
|
||||||
if count:
|
if count:
|
||||||
response = 'Successfully imported {} feeds.'.format(count)
|
response = 'Successfully imported {} feeds.'.format(count)
|
||||||
|
@ -163,7 +149,7 @@ async def message(self, message):
|
||||||
|
|
||||||
# # Begin processing new JID
|
# # Begin processing new JID
|
||||||
# # Deprecated in favour of event 'presence_available'
|
# # Deprecated in favour of event 'presence_available'
|
||||||
# db_dir = get_default_data_directory()
|
# db_dir = config.get_default_data_directory()
|
||||||
# os.chdir(db_dir)
|
# os.chdir(db_dir)
|
||||||
# if jid + '.db' not in os.listdir():
|
# if jid + '.db' not in os.listdir():
|
||||||
# await task_jid(jid)
|
# await task_jid(jid)
|
||||||
|
@ -301,7 +287,7 @@ async def message(self, message):
|
||||||
if not title:
|
if not title:
|
||||||
title = uri.get_hostname(url)
|
title = uri.get_hostname(url)
|
||||||
if url.startswith('http'):
|
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)
|
exist = await sqlite.get_feed_id_and_name(db_file, url)
|
||||||
if not exist:
|
if not exist:
|
||||||
await sqlite.insert_feed(db_file, url, title)
|
await sqlite.insert_feed(db_file, url, title)
|
||||||
|
@ -334,9 +320,9 @@ async def message(self, message):
|
||||||
key = 'filter-' + message_text[:5]
|
key = 'filter-' + message_text[:5]
|
||||||
val = message_text[7:]
|
val = message_text[7:]
|
||||||
if val:
|
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)
|
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):
|
if await sqlite.get_filters_value(db_file, key):
|
||||||
await sqlite.update_filters_value(db_file,
|
await sqlite.update_filters_value(db_file,
|
||||||
[key, val])
|
[key, val])
|
||||||
|
@ -352,9 +338,9 @@ async def message(self, message):
|
||||||
key = 'filter-' + message_text[:5]
|
key = 'filter-' + message_text[:5]
|
||||||
val = message_text[7:]
|
val = message_text[7:]
|
||||||
if val:
|
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)
|
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):
|
if await sqlite.get_filters_value(db_file, key):
|
||||||
await sqlite.update_filters_value(db_file,
|
await sqlite.update_filters_value(db_file,
|
||||||
[key, val])
|
[key, val])
|
||||||
|
@ -374,7 +360,7 @@ async def message(self, message):
|
||||||
if int(val) > 500:
|
if int(val) > 500:
|
||||||
response = 'Value may not be greater than 500.'
|
response = 'Value may not be greater than 500.'
|
||||||
else:
|
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,
|
if await sqlite.get_settings_value(db_file,
|
||||||
[key, val]):
|
[key, val]):
|
||||||
await sqlite.update_settings_value(db_file,
|
await sqlite.update_settings_value(db_file,
|
||||||
|
@ -391,9 +377,9 @@ async def message(self, message):
|
||||||
response = 'Missing value.'
|
response = 'Missing value.'
|
||||||
send_reply_message(self, message, response)
|
send_reply_message(self, message, response)
|
||||||
case _ if message_lowercase.startswith('bookmark -'):
|
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:]
|
muc_jid = message_text[11:]
|
||||||
await bookmark.remove(self, muc_jid)
|
await XmppBookmark.remove(self, muc_jid)
|
||||||
response = ('Groupchat {} has been removed '
|
response = ('Groupchat {} has been removed '
|
||||||
'from bookmarks.'
|
'from bookmarks.'
|
||||||
.format(muc_jid))
|
.format(muc_jid))
|
||||||
|
@ -402,7 +388,7 @@ async def message(self, message):
|
||||||
'Type: removing bookmarks.')
|
'Type: removing bookmarks.')
|
||||||
send_reply_message(self, message, response)
|
send_reply_message(self, message, response)
|
||||||
case 'bookmarks':
|
case 'bookmarks':
|
||||||
if jid == get_value('accounts', 'XMPP', 'operator'):
|
if jid == config.get_value('accounts', 'XMPP', 'operator'):
|
||||||
response = await action.list_bookmarks(self)
|
response = await action.list_bookmarks(self)
|
||||||
else:
|
else:
|
||||||
response = ('This action is restricted. '
|
response = ('This action is restricted. '
|
||||||
|
@ -412,9 +398,9 @@ async def message(self, message):
|
||||||
key = 'filter-' + message_text[:4]
|
key = 'filter-' + message_text[:4]
|
||||||
val = message_text[6:]
|
val = message_text[6:]
|
||||||
if val:
|
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)
|
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):
|
if await sqlite.get_filters_value(db_file, key):
|
||||||
await sqlite.update_filters_value(db_file,
|
await sqlite.update_filters_value(db_file,
|
||||||
[key, val])
|
[key, val])
|
||||||
|
@ -430,9 +416,9 @@ async def message(self, message):
|
||||||
key = 'filter-' + message_text[:4]
|
key = 'filter-' + message_text[:4]
|
||||||
val = message_text[6:]
|
val = message_text[6:]
|
||||||
if val:
|
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)
|
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):
|
if await sqlite.get_filters_value(db_file, key):
|
||||||
await sqlite.update_filters_value(db_file,
|
await sqlite.update_filters_value(db_file,
|
||||||
[key, val])
|
[key, val])
|
||||||
|
@ -451,16 +437,16 @@ async def message(self, message):
|
||||||
status_message = ('📤️ Procesing request to '
|
status_message = ('📤️ Procesing request to '
|
||||||
'export feeds into {}...'
|
'export feeds into {}...'
|
||||||
.format(ex))
|
.format(ex))
|
||||||
status.send(
|
await XmppStatus.send(self, jid, status_message,
|
||||||
self, jid, status_message, status_type)
|
status_type)
|
||||||
cache_dir = get_default_cache_directory()
|
cache_dir = config.get_default_cache_directory()
|
||||||
if not os.path.isdir(cache_dir):
|
if not os.path.isdir(cache_dir):
|
||||||
os.mkdir(cache_dir)
|
os.mkdir(cache_dir)
|
||||||
if not os.path.isdir(cache_dir + '/' + ex):
|
if not os.path.isdir(cache_dir + '/' + ex):
|
||||||
os.mkdir(cache_dir + '/' + ex)
|
os.mkdir(cache_dir + '/' + ex)
|
||||||
filename = os.path.join(
|
filename = os.path.join(
|
||||||
cache_dir, ex, 'slixfeed_' + timestamp() + '.' + ex)
|
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)
|
results = await sqlite.get_feeds(db_file)
|
||||||
match ex:
|
match ex:
|
||||||
case 'html':
|
case 'html':
|
||||||
|
@ -505,9 +491,10 @@ async def message(self, message):
|
||||||
status_message = ('📃️ Procesing request to '
|
status_message = ('📃️ Procesing request to '
|
||||||
'produce {} document...'
|
'produce {} document...'
|
||||||
.format(ext.upper()))
|
.format(ext.upper()))
|
||||||
status.send(self, jid, status_message, status_type)
|
await XmppStatus.send(self, jid, status_message,
|
||||||
db_file = get_pathname_to_database(jid_file)
|
status_type)
|
||||||
cache_dir = get_default_cache_directory()
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
|
cache_dir = config.get_default_cache_directory()
|
||||||
if ix_url:
|
if ix_url:
|
||||||
if not os.path.isdir(cache_dir):
|
if not os.path.isdir(cache_dir):
|
||||||
os.mkdir(cache_dir)
|
os.mkdir(cache_dir)
|
||||||
|
@ -578,9 +565,9 @@ async def message(self, message):
|
||||||
# status_message = (
|
# status_message = (
|
||||||
# '📥️ Procesing request to import feeds...'
|
# '📥️ Procesing request to import feeds...'
|
||||||
# )
|
# )
|
||||||
# status.send(
|
# await XmppStatus.send(
|
||||||
# self, jid, status_message, status_type)
|
# 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)
|
# count = await action.import_opml(db_file, url)
|
||||||
# if count:
|
# if count:
|
||||||
# response = (
|
# response = (
|
||||||
|
@ -603,11 +590,11 @@ async def message(self, message):
|
||||||
status_message = ('📫️ Processing request '
|
status_message = ('📫️ Processing request '
|
||||||
'to fetch data from {}'
|
'to fetch data from {}'
|
||||||
.format(url))
|
.format(url))
|
||||||
status.send(self, jid, status_message, status_type)
|
await XmppStatus.send(self, jid, status_message, status_type)
|
||||||
if url.startswith('feed:'):
|
if url.startswith('feed:'):
|
||||||
url = uri.feed_to_http(url)
|
url = uri.feed_to_http(url)
|
||||||
url = (uri.replace_hostname(url, 'feed')) or 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:
|
# try:
|
||||||
response = await action.add_feed(db_file, url)
|
response = await action.add_feed(db_file, url)
|
||||||
# await task.clean_tasks_xmpp(jid, ['status'])
|
# await task.clean_tasks_xmpp(jid, ['status'])
|
||||||
|
@ -623,49 +610,28 @@ async def message(self, message):
|
||||||
query = message_text[6:]
|
query = message_text[6:]
|
||||||
if query:
|
if query:
|
||||||
if len(query) > 3:
|
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)
|
result = await sqlite.search_feeds(db_file, query)
|
||||||
response = action.list_feeds_by_query(query, result)
|
response = action.list_feeds_by_query(query, result)
|
||||||
else:
|
else:
|
||||||
response = 'Enter at least 4 characters to search'
|
response = 'Enter at least 4 characters to search'
|
||||||
else:
|
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)
|
result = await sqlite.get_feeds(db_file)
|
||||||
response = action.list_feeds(result)
|
response = action.list_feeds(result)
|
||||||
send_reply_message(self, message, response)
|
send_reply_message(self, message, response)
|
||||||
case 'goodbye':
|
case 'goodbye':
|
||||||
if message['type'] == 'groupchat':
|
if message['type'] == 'groupchat':
|
||||||
await groupchat.leave(self, jid)
|
await groupchat.leave(self, jid)
|
||||||
await bookmark.remove(self, muc_jid)
|
await XmppBookmark.remove(self, muc_jid)
|
||||||
else:
|
else:
|
||||||
response = 'This command is valid in groupchat only.'
|
response = 'This command is valid in groupchat only.'
|
||||||
send_reply_message(self, message, response)
|
send_reply_message(self, message, response)
|
||||||
case _ if message_lowercase.startswith('interval'):
|
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]
|
key = message_text[:8]
|
||||||
val = message_text[9:]
|
val = message_text[9:]
|
||||||
if val:
|
await action.xmpp_change_interval(
|
||||||
# response = (
|
self, key, val, jid, jid_file, message=message)
|
||||||
# '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'):
|
case _ if message_lowercase.startswith('join'):
|
||||||
muc_jid = uri.check_xmpp_uri(message_text[5:])
|
muc_jid = uri.check_xmpp_uri(message_text[5:])
|
||||||
if muc_jid:
|
if muc_jid:
|
||||||
|
@ -684,7 +650,7 @@ async def message(self, message):
|
||||||
if val:
|
if val:
|
||||||
try:
|
try:
|
||||||
val = int(val)
|
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,
|
if await sqlite.get_settings_value(db_file,
|
||||||
[key, val]):
|
[key, val]):
|
||||||
await sqlite.update_settings_value(db_file,
|
await sqlite.update_settings_value(db_file,
|
||||||
|
@ -711,7 +677,7 @@ async def message(self, message):
|
||||||
# get_settings_value,
|
# get_settings_value,
|
||||||
# key
|
# key
|
||||||
# )
|
# )
|
||||||
# val = await add_to_list(
|
# val = await config.add_to_list(
|
||||||
# val,
|
# val,
|
||||||
# names
|
# names
|
||||||
# )
|
# )
|
||||||
|
@ -728,7 +694,7 @@ async def message(self, message):
|
||||||
# response = 'Missing value.'
|
# response = 'Missing value.'
|
||||||
send_reply_message(self, message, response)
|
send_reply_message(self, message, response)
|
||||||
case 'new':
|
case 'new':
|
||||||
db_file = get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
key = 'old'
|
key = 'old'
|
||||||
val = 0
|
val = 0
|
||||||
if await sqlite.get_settings_value(db_file, key):
|
if await sqlite.get_settings_value(db_file, key):
|
||||||
|
@ -765,7 +731,7 @@ async def message(self, message):
|
||||||
# )
|
# )
|
||||||
# await refresh_task(jid, key, val)
|
# await refresh_task(jid, key, val)
|
||||||
case 'old':
|
case 'old':
|
||||||
db_file = get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
key = 'old'
|
key = 'old'
|
||||||
val = 1
|
val = 1
|
||||||
if await sqlite.get_settings_value(db_file, key):
|
if await sqlite.get_settings_value(db_file, key):
|
||||||
|
@ -783,11 +749,13 @@ async def message(self, message):
|
||||||
# response = (
|
# response = (
|
||||||
# 'Every update will contain {} news items.'
|
# 'Every update will contain {} news items.'
|
||||||
# ).format(response)
|
# ).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):
|
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:
|
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.'
|
response = ('Next update will contain {} news items.'
|
||||||
.format(val))
|
.format(val))
|
||||||
except:
|
except:
|
||||||
|
@ -808,8 +776,7 @@ async def message(self, message):
|
||||||
status_type = 'dnd'
|
status_type = 'dnd'
|
||||||
status_message = ('📫️ Processing request to fetch data from {}'
|
status_message = ('📫️ Processing request to fetch data from {}'
|
||||||
.format(url))
|
.format(url))
|
||||||
status.send(
|
await XmppStatus.send(self, jid, status_message, status_type)
|
||||||
self, jid, status_message, status_type)
|
|
||||||
if url.startswith('feed:'):
|
if url.startswith('feed:'):
|
||||||
url = uri.feed_to_http(url)
|
url = uri.feed_to_http(url)
|
||||||
url = (uri.replace_hostname(url, 'feed')) or url
|
url = (uri.replace_hostname(url, 'feed')) or url
|
||||||
|
@ -839,7 +806,7 @@ async def message(self, message):
|
||||||
if num < 1 or num > 50:
|
if num < 1 or num > 50:
|
||||||
response = 'Value must be ranged from 1 to 50.'
|
response = 'Value must be ranged from 1 to 50.'
|
||||||
else:
|
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)
|
result = await sqlite.last_entries(db_file, num)
|
||||||
response = action.list_last_entries(result, num)
|
response = action.list_last_entries(result, num)
|
||||||
except:
|
except:
|
||||||
|
@ -850,7 +817,7 @@ async def message(self, message):
|
||||||
case _ if message_lowercase.startswith('remove'):
|
case _ if message_lowercase.startswith('remove'):
|
||||||
ix_url = message_text[7:]
|
ix_url = message_text[7:]
|
||||||
if ix_url:
|
if ix_url:
|
||||||
db_file = get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
try:
|
try:
|
||||||
ix = int(ix_url)
|
ix = int(ix_url)
|
||||||
url = sqlite.get_feed_url(db_file, ix)
|
url = sqlite.get_feed_url(db_file, ix)
|
||||||
|
@ -899,10 +866,10 @@ async def message(self, message):
|
||||||
await task.clean_tasks_xmpp(jid, ['status'])
|
await task.clean_tasks_xmpp(jid, ['status'])
|
||||||
status_type = 'dnd'
|
status_type = 'dnd'
|
||||||
status_message = '📫️ Marking entries as read...'
|
status_message = '📫️ Marking entries as read...'
|
||||||
status.send(self, jid, status_message, status_type)
|
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)
|
||||||
if ix_url:
|
if ix_url:
|
||||||
db_file = get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
try:
|
try:
|
||||||
ix = int(ix_url)
|
ix = int(ix_url)
|
||||||
url = sqlite.get_feed_url(db_file, ix)
|
url = sqlite.get_feed_url(db_file, ix)
|
||||||
|
@ -944,7 +911,7 @@ async def message(self, message):
|
||||||
query = message_text[7:]
|
query = message_text[7:]
|
||||||
if query:
|
if query:
|
||||||
if len(query) > 1:
|
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)
|
results = await sqlite.search_entries(db_file, query)
|
||||||
response = action.list_search_results(query, results)
|
response = action.list_search_results(query, results)
|
||||||
else:
|
else:
|
||||||
|
@ -956,7 +923,7 @@ async def message(self, message):
|
||||||
# response = 'Updates are enabled.'
|
# response = 'Updates are enabled.'
|
||||||
key = 'enabled'
|
key = 'enabled'
|
||||||
val = 1
|
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):
|
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:
|
else:
|
||||||
|
@ -967,12 +934,12 @@ async def message(self, message):
|
||||||
# print(task_manager[jid])
|
# print(task_manager[jid])
|
||||||
send_reply_message(self, message, response)
|
send_reply_message(self, message, response)
|
||||||
case 'stats':
|
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)
|
response = await action.list_statistics(db_file)
|
||||||
send_reply_message(self, message, response)
|
send_reply_message(self, message, response)
|
||||||
case _ if message_lowercase.startswith('disable '):
|
case _ if message_lowercase.startswith('disable '):
|
||||||
ix = message_text[8:]
|
ix = message_text[8:]
|
||||||
db_file = get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
try:
|
try:
|
||||||
await sqlite.set_enabled_status(db_file, ix, 0)
|
await sqlite.set_enabled_status(db_file, ix, 0)
|
||||||
response = ('Updates are now disabled for news source {}.'
|
response = ('Updates are now disabled for news source {}.'
|
||||||
|
@ -982,7 +949,7 @@ async def message(self, message):
|
||||||
send_reply_message(self, message, response)
|
send_reply_message(self, message, response)
|
||||||
case _ if message_lowercase.startswith('enable'):
|
case _ if message_lowercase.startswith('enable'):
|
||||||
ix = message_text[7:]
|
ix = message_text[7:]
|
||||||
db_file = get_pathname_to_database(jid_file)
|
db_file = config.get_pathname_to_database(jid_file)
|
||||||
try:
|
try:
|
||||||
await sqlite.set_enabled_status(db_file, ix, 1)
|
await sqlite.set_enabled_status(db_file, ix, 1)
|
||||||
response = ('Updates are now enabled for news source {}.'
|
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)
|
response = 'No news source with index {}.'.format(ix)
|
||||||
send_reply_message(self, message, response)
|
send_reply_message(self, message, response)
|
||||||
case 'stop':
|
case 'stop':
|
||||||
# FIXME
|
await action.xmpp_stop_updates(self, message, jid, jid_file)
|
||||||
# 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)
|
|
||||||
case 'support':
|
case 'support':
|
||||||
# TODO Send an invitation.
|
# TODO Send an invitation.
|
||||||
response = 'Join xmpp:slixfeed@chat.woodpeckersnest.space?join'
|
response = 'Join xmpp:slixfeed@chat.woodpeckersnest.space?join'
|
||||||
|
@ -1057,7 +991,7 @@ async def message(self, message):
|
||||||
send_reply_message(self, message, response)
|
send_reply_message(self, message, response)
|
||||||
|
|
||||||
if not response: response = 'EMPTY MESSAGE - ACTION ONLY'
|
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):
|
if not os.path.isdir(data_dir):
|
||||||
os.mkdir(data_dir)
|
os.mkdir(data_dir)
|
||||||
if not os.path.isdir(data_dir + '/logs/'):
|
if not os.path.isdir(data_dir + '/logs/'):
|
||||||
|
|
|
@ -31,6 +31,7 @@ from slixfeed.config import get_value, get_default_config_directory
|
||||||
# import logging
|
# import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
# class XmppProfile:
|
||||||
|
|
||||||
async def update(self):
|
async def update(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,10 +1,19 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
def send(self, jid, status_message, status_type=None):
|
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(
|
self.send_presence(
|
||||||
|
pto=jid,
|
||||||
|
pfrom=self.boundjid.bare,
|
||||||
pshow=status_type,
|
pshow=status_type,
|
||||||
pstatus=status_message,
|
pstatus=status_message,
|
||||||
pfrom=self.boundjid.bare,
|
ptype=chat_type
|
||||||
pto=jid
|
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue