Add more permissions to ad-hoc commands from MUC (see Prosody mod_muc_adhoc_bots)

Handle more errors.
Add ad-hoc command Profile.
Support Python 3.10 (tomli).
Add table for scraping HTML (WIP).
Minor fixes.
This commit is contained in:
Schimon Jehudah 2024-02-25 01:52:24 +00:00
parent 56f85fdf26
commit afeaa8707b
9 changed files with 449 additions and 256 deletions

View file

@ -29,10 +29,12 @@ keywords = [
"chat", "chat",
"im", "im",
"jabber", "jabber",
"json",
"news", "news",
"rdf", "rdf",
"rss", "rss",
"syndication", "syndication",
"xml",
"xmpp", "xmpp",
] ]
# urls = {Homepage = "https://gitgud.io/sjehuda/slixfeed"} # urls = {Homepage = "https://gitgud.io/sjehuda/slixfeed"}
@ -43,6 +45,7 @@ dependencies = [
"bs4", "bs4",
"feedparser", "feedparser",
"lxml", "lxml",
"tomli", # Python 3.10
"tomli_w", "tomli_w",
"slixmpp", "slixmpp",
@ -50,7 +53,7 @@ dependencies = [
# listed here (testing) # listed here (testing)
"html2text", "html2text",
"pdfkit", "pdfkit",
"pysocks", # "pysocks",
"readability-lxml", "readability-lxml",
"xml2epub", "xml2epub",
] ]

View file

@ -87,6 +87,9 @@ import socket
xmpp_type = config.get_value('accounts', 'XMPP', 'type') xmpp_type = config.get_value('accounts', 'XMPP', 'type')
if not xmpp_type:
raise Exception('Key type is missing from accounts.ini.')
match xmpp_type: match xmpp_type:
case 'client': case 'client':
from slixfeed.xmpp.client import Slixfeed from slixfeed.xmpp.client import Slixfeed

View file

@ -32,7 +32,10 @@ import os
import slixfeed.sqlite as sqlite import slixfeed.sqlite as sqlite
import sys import sys
import tomli_w import tomli_w
import tomllib try:
import tomllib
except:
import tomli as tomllib
async def set_setting_value(db_file, key, val): async def set_setting_value(db_file, key, val):
@ -140,6 +143,7 @@ def clear_values(input):
return '' return ''
# TODO Return dict instead of list
def get_value(filename, section, keys): def get_value(filename, section, keys):
""" """
Get setting value. Get setting value.

View file

@ -158,6 +158,24 @@ def create_tables(db_file):
); );
""" """
) )
feeds_rules_table_sql = (
"""
CREATE TABLE IF NOT EXISTS feeds_rules (
id INTEGER NOT NULL,
feed_id INTEGER NOT NULL UNIQUE,
type TEXT NOT NULL,
base TEXT NOT NULL,
title TEXT NOT NULL,
link TEXT NOT NULL,
enclosure TEXT,
summary TEXT,
FOREIGN KEY ("feed_id") REFERENCES "feeds" ("id")
ON UPDATE CASCADE
ON DELETE CASCADE,
PRIMARY KEY (id)
);
"""
)
feeds_state_table_sql = ( feeds_state_table_sql = (
""" """
CREATE TABLE IF NOT EXISTS feeds_state ( CREATE TABLE IF NOT EXISTS feeds_state (
@ -235,6 +253,7 @@ def create_tables(db_file):
cur.execute(feeds_table_sql) cur.execute(feeds_table_sql)
cur.execute(feeds_state_table_sql) cur.execute(feeds_state_table_sql)
cur.execute(feeds_properties_table_sql) cur.execute(feeds_properties_table_sql)
cur.execute(feeds_rules_table_sql)
cur.execute(filters_table_sql) cur.execute(filters_table_sql)
# cur.execute(statistics_table_sql) # cur.execute(statistics_table_sql)
cur.execute(settings_table_sql) cur.execute(settings_table_sql)

View file

@ -1,2 +1,2 @@
__version__ = '0.1.20' __version__ = '0.1.21'
__version_info__ = (0, 1, 20) __version_info__ = (0, 1, 21)

View file

@ -31,12 +31,13 @@ NOTE
""" """
import asyncio import asyncio
from datetime import datetime
import logging import logging
import os import os
from random import randrange from random import randrange
import slixmpp import slixmpp
import slixfeed.task as task import slixfeed.task as task
from time import sleep import time
from slixmpp.plugins.xep_0363.http_upload import FileTooBig, HTTPError, UploadServiceNotFound from slixmpp.plugins.xep_0363.http_upload import FileTooBig, HTTPError, UploadServiceNotFound
# from slixmpp.plugins.xep_0402 import BookmarkStorage, Conference # from slixmpp.plugins.xep_0402 import BookmarkStorage, Conference
@ -64,7 +65,7 @@ from slixfeed.xmpp.roster import XmppRoster
# import slixfeed.xmpp.service as service # import slixfeed.xmpp.service as service
from slixfeed.xmpp.presence import XmppPresence from slixfeed.xmpp.presence import XmppPresence
from slixfeed.xmpp.upload import XmppUpload from slixfeed.xmpp.upload import XmppUpload
from slixfeed.xmpp.utility import get_chat_type from slixfeed.xmpp.utility import get_chat_type, is_moderator
main_task = [] main_task = []
jid_tasker = {} jid_tasker = {}
@ -505,12 +506,12 @@ class Slixfeed(slixmpp.ClientXMPP):
self['xep_0050'].add_command(node='advanced', self['xep_0050'].add_command(node='advanced',
name='📓 Advanced', name='📓 Advanced',
handler=self._handle_advanced) handler=self._handle_advanced)
self['xep_0050'].add_command(node='profile',
name='💼️ Profile',
handler=self._handle_profile)
self['xep_0050'].add_command(node='about', self['xep_0050'].add_command(node='about',
name='📜️ About', name='📜️ About',
handler=self._handle_about) handler=self._handle_about)
self['xep_0050'].add_command(node='exploit',
name='Exploit',
handler=self._handle_reveal_jid)
# self['xep_0050'].add_command(node='search', # self['xep_0050'].add_command(node='search',
# name='Search', # name='Search',
# handler=self._handle_search) # handler=self._handle_search)
@ -518,35 +519,124 @@ class Slixfeed(slixmpp.ClientXMPP):
# Special interface # Special interface
# http://jabber.org/protocol/commands#actions # http://jabber.org/protocol/commands#actions
async def _handle_reveal_jid(self, iq, session): async def _handle_profile(self, iq, session):
jid = session['from'].bare jid = session['from'].bare
session['notes'] = [['info', jid]] jid_file = jid
db_file = config.get_pathname_to_database(jid_file)
feeds_all = str(await sqlite.get_number_of_items(db_file, 'feeds'))
feeds_act = str(await sqlite.get_number_of_feeds_active(db_file))
unread = str(await sqlite.get_number_of_entries_unread(db_file))
entries = await sqlite.get_number_of_items(db_file, 'entries')
archive = await sqlite.get_number_of_items(db_file, 'archive')
entries_all = str(entries + archive)
key_archive = str(config.get_setting_value(db_file, 'archive'))
key_enabled = str(config.get_setting_value(db_file, 'enabled'))
key_interval = str(config.get_setting_value(db_file, 'interval'))
key_media = str(config.get_setting_value(db_file, 'media'))
key_old = str(config.get_setting_value(db_file, 'old'))
key_quantum = str(config.get_setting_value(db_file, 'quantum'))
update_interval = config.get_setting_value(db_file, 'interval')
update_interval = 60 * int(update_interval)
last_update_time = float(await sqlite.get_last_update_time(db_file))
dt_object = datetime.fromtimestamp(last_update_time)
last_update = dt_object.strftime('%H:%M:%S')
if int(key_enabled):
next_update_time = last_update_time + update_interval
dt_object = datetime.fromtimestamp(next_update_time)
next_update = dt_object.strftime('%H:%M:%S')
else:
next_update = 'n/a'
form = self['xep_0004'].make_form('form', 'Profile')
form['instructions'] = ('Displaying information\nJabber ID {}'
.format(jid))
form.add_field(ftype='fixed',
value='Data')
form.add_field(label='Subscriptions',
ftype='text-single',
value=feeds_all)
form.add_field(label='Active subscriptions',
ftype='text-single',
value=feeds_act)
form.add_field(label='Items',
ftype='text-single',
value=entries_all)
form.add_field(label='Unread',
ftype='text-single',
value=unread)
form.add_field(ftype='fixed',
value='Schedule')
form.add_field(label='Last update',
ftype='text-single',
value=last_update)
form.add_field(label='Next update',
ftype='text-single',
value=next_update)
form.add_field(ftype='fixed',
value='Options')
form.add_field(label='Archive',
ftype='text-single',
value=key_archive)
form.add_field(label='Enabled',
ftype='text-single',
value=key_enabled)
form.add_field(label='Interval',
ftype='text-single',
value=key_interval)
form.add_field(label='Media',
ftype='text-single',
value=key_media)
form.add_field(label='Old',
ftype='text-single',
value=key_old)
form.add_field(label='Quantum',
ftype='text-single',
value=key_quantum)
session['payload'] = form
# text_note = ('Jabber ID: {}'
# '\n'
# 'Last update: {}'
# '\n'
# 'Next update: {}'
# ''.format(jid, last_update, next_update))
# session['notes'] = [['info', text_note]]
return session return session
async def _handle_filters(self, iq, session): async def _handle_filters(self, iq, session):
jid = session['from'].bare jid = session['from'].bare
jid_file = jid jid_full = str(session['from'])
db_file = config.get_pathname_to_database(jid_file) chat_type = await get_chat_type(self, jid)
form = self['xep_0004'].make_form('form', 'Filters') if chat_type == 'groupchat':
form['instructions'] = 'Editing filters' # 🪄️ 🛡️ moderator = is_moderator(self, jid, jid_full)
value = sqlite.get_filter_value(db_file, 'allow') if chat_type == 'chat' or moderator:
if value: value = str(value[0]) jid = session['from'].bare
form.add_field(var='allow', jid_file = jid
ftype='text-single', db_file = config.get_pathname_to_database(jid_file)
label='Allow list', form = self['xep_0004'].make_form('form', 'Filters')
value=value, form['instructions'] = 'Editing filters' # 🪄️ 🛡️
desc=('Keywords to allow (comma-separated keywords).')) value = sqlite.get_filter_value(db_file, 'allow')
value = sqlite.get_filter_value(db_file, 'deny') if value: value = str(value[0])
if value: value = str(value[0]) form.add_field(var='allow',
form.add_field(var='deny', ftype='text-single',
ftype='text-single', label='Allow list',
label='Deny list', value=value,
value=value, desc=('Keywords to allow (comma-separated keywords).'))
desc=('Keywords to deny (comma-separated keywords).')) value = sqlite.get_filter_value(db_file, 'deny')
session['allow_complete'] = True if value: value = str(value[0])
session['has_next'] = False form.add_field(var='deny',
session['next'] = self._handle_filters_complete ftype='text-single',
session['payload'] = form label='Deny list',
value=value,
desc=('Keywords to deny (comma-separated keywords).'))
session['allow_complete'] = True
session['has_next'] = False
session['next'] = self._handle_filters_complete
session['payload'] = form
else:
text_warn = ('This resource is restricted to moderators of {}.'
.format(jid))
session['notes'] = [['warn', text_warn]]
return session return session
@ -598,30 +688,40 @@ class Slixfeed(slixmpp.ClientXMPP):
async def _handle_subscription_add(self, iq, session): async def _handle_subscription_add(self, iq, session):
form = self['xep_0004'].make_form('form', 'Subscription') jid = session['from'].bare
form['instructions'] = 'Adding subscription' jid_full = str(session['from'])
form.add_field(var='subscription', chat_type = await get_chat_type(self, jid)
# TODO Make it possible to add several subscriptions at once; if chat_type == 'groupchat':
# Similarly to BitTorrent trackers list moderator = is_moderator(self, jid, jid_full)
# ftype='text-multi', if chat_type == 'chat' or moderator:
# label='Subscription URLs', form = self['xep_0004'].make_form('form', 'Subscription')
# desc=('Add subscriptions one time per ' form['instructions'] = 'Adding subscription'
# 'subscription.'), form.add_field(var='subscription',
ftype='text-single', # TODO Make it possible to add several subscriptions at once;
label='URL', # Similarly to BitTorrent trackers list
desc='Enter subscription URL.', # ftype='text-multi',
value='http://', # label='Subscription URLs',
required=True) # desc=('Add subscriptions one time per '
# form.add_field(var='scan', # 'subscription.'),
# ftype='boolean', ftype='text-single',
# label='Scan', label='URL',
# desc='Scan URL for validity (recommended).', desc='Enter subscription URL.',
# value=True) value='http://',
session['allow_prev'] = False required=True)
session['has_next'] = True # form.add_field(var='scan',
session['next'] = self._handle_subscription_new # ftype='boolean',
session['prev'] = None # label='Scan',
session['payload'] = form # desc='Scan URL for validity (recommended).',
# value=True)
session['allow_prev'] = False
session['has_next'] = True
session['next'] = self._handle_subscription_new
session['prev'] = None
session['payload'] = form
else:
text_warn = ('This resource is restricted to moderators of {}.'
.format(jid))
session['notes'] = [['warn', text_warn]]
return session return session
@ -636,7 +736,7 @@ class Slixfeed(slixmpp.ClientXMPP):
options = form.add_field(var='update', options = form.add_field(var='update',
ftype='list-single', ftype='list-single',
label='News', label='News',
desc=('Select a news update to read.'), desc=('Select a news item to read.'),
required=True) required=True)
for result in results: for result in results:
title = result[1] title = result[1]
@ -770,6 +870,7 @@ class Slixfeed(slixmpp.ClientXMPP):
# scan = payload['values']['scan'] # scan = payload['values']['scan']
url = payload['values']['subscription'] url = payload['values']['subscription']
if isinstance(url, list) and len(url) > 1: if isinstance(url, list) and len(url) > 1:
url_count = len(url)
urls = url urls = url
agree_count = 0 agree_count = 0
error_count = 0 error_count = 0
@ -785,7 +886,7 @@ class Slixfeed(slixmpp.ClientXMPP):
form = self['xep_0004'].make_form('form', 'Subscription') form = self['xep_0004'].make_form('form', 'Subscription')
if agree_count: if agree_count:
response = ('Added {} new subscription(s) out of {}' response = ('Added {} new subscription(s) out of {}'
.format(agree_count, len(url))) .format(agree_count, url_count))
session['notes'] = [['info', response]] session['notes'] = [['info', response]]
else: else:
response = ('No new subscription was added. ' response = ('No new subscription was added. '
@ -984,21 +1085,32 @@ class Slixfeed(slixmpp.ClientXMPP):
return session return session
def _handle_discover(self, iq, session): async def _handle_discover(self, iq, session):
form = self['xep_0004'].make_form('form', 'Discover & Search') jid = session['from'].bare
options = form.add_field(var='search_type', jid_full = str(session['from'])
ftype='list-single', chat_type = await get_chat_type(self, jid)
label='Browse', if chat_type == 'groupchat':
desc=('Select type of search.'), moderator = is_moderator(self, jid, jid_full)
required=True) if chat_type == 'chat' or moderator:
options.addOption('All', 'all') form = self['xep_0004'].make_form('form', 'Discover & Search')
options.addOption('Categories', 'cat') # Should we write this in a singular form form['instructions'] = 'Discover news subscriptions of all kinds'
# options.addOption('Tags', 'tag') options = form.add_field(var='search_type',
session['allow_prev'] = False ftype='list-single',
session['has_next'] = True label='Browse',
session['next'] = self._handle_discover_type desc=('Select type of search.'),
session['payload'] = form required=True)
session['prev'] = None options.addOption('All', 'all')
options.addOption('Categories', 'cat') # Should we write this in a singular form
# options.addOption('Tags', 'tag')
session['allow_prev'] = False
session['has_next'] = True
session['next'] = self._handle_discover_type
session['payload'] = form
session['prev'] = None
else:
text_warn = ('This resource is restricted to moderators of {}.'
.format(jid))
session['notes'] = [['warn', text_warn]]
return session return session
@ -1011,6 +1123,7 @@ class Slixfeed(slixmpp.ClientXMPP):
form = self['xep_0004'].make_form('form', 'Discover & Search') form = self['xep_0004'].make_form('form', 'Discover & Search')
match search_type: match search_type:
case 'all': case 'all':
form['instructions'] = 'Browsing subscriptions'
options = form.add_field(var='subscription', options = form.add_field(var='subscription',
# ftype='list-multi', # TODO To be added soon # ftype='list-multi', # TODO To be added soon
ftype='list-single', ftype='list-single',
@ -1027,6 +1140,7 @@ class Slixfeed(slixmpp.ClientXMPP):
# session['allow_complete'] = True # session['allow_complete'] = True
session['next'] = self._handle_subscription_new session['next'] = self._handle_subscription_new
case 'cat': case 'cat':
form['instructions'] = 'Browsing categories'
session['next'] = self._handle_discover_category session['next'] = self._handle_discover_category
options = form.add_field(var='category', options = form.add_field(var='category',
ftype='list-single', ftype='list-single',
@ -1058,6 +1172,7 @@ class Slixfeed(slixmpp.ClientXMPP):
config_dir = config.get_default_config_directory() config_dir = config.get_default_config_directory()
db_file = config_dir + '/feeds.sqlite' db_file = config_dir + '/feeds.sqlite'
form = self['xep_0004'].make_form('form', 'Discover & Search') form = self['xep_0004'].make_form('form', 'Discover & Search')
form['instructions'] = 'Browsing category "{}"'.format(category)
options = form.add_field(var='subscription', options = form.add_field(var='subscription',
# ftype='list-multi', # TODO To be added soon # ftype='list-multi', # TODO To be added soon
ftype='list-single', ftype='list-single',
@ -1078,20 +1193,30 @@ class Slixfeed(slixmpp.ClientXMPP):
async def _handle_subscriptions(self, iq, session): async def _handle_subscriptions(self, iq, session):
form = self['xep_0004'].make_form('form', 'Contacts') jid = session['from'].bare
form['instructions'] = 'Managing subscriptions' jid_full = str(session['from'])
options = form.add_field(var='action', chat_type = await get_chat_type(self, jid)
ftype='list-single', if chat_type == 'groupchat':
label='Action', moderator = is_moderator(self, jid, jid_full)
desc='Select action type.', if chat_type == 'chat' or moderator:
required=True) form = self['xep_0004'].make_form('form', 'Subscriptions')
options.addOption('Enable subscriptions', 'enable') form['instructions'] = 'Managing subscriptions'
options.addOption('Disable subscriptions', 'disable') options = form.add_field(var='action',
options.addOption('Modify subscriptions', 'edit') ftype='list-single',
options.addOption('Remove subscriptions', 'delete') label='Action',
session['payload'] = form desc='Select action type.',
session['next'] = self._handle_subscriptions_result required=True)
session['has_next'] = True options.addOption('Enable subscriptions', 'enable')
options.addOption('Disable subscriptions', 'disable')
options.addOption('Modify subscriptions', 'edit')
options.addOption('Remove subscriptions', 'delete')
session['payload'] = form
session['next'] = self._handle_subscriptions_result
session['has_next'] = True
else:
text_warn = ('This resource is restricted to moderators of {}.'
.format(jid))
session['notes'] = [['warn', text_warn]]
return session return session
@ -1372,24 +1497,34 @@ class Slixfeed(slixmpp.ClientXMPP):
async def _handle_advanced(self, iq, session): async def _handle_advanced(self, iq, session):
form = self['xep_0004'].make_form('form', 'Advanced Options')
form['instructions'] = 'Extended options and information'
options = form.add_field(var='option',
ftype='list-single',
label='Choose',
required=True)
# options.addOption('Activity', 'activity')
# options.addOption('Filters', 'filter')
# options.addOption('Statistics', 'statistics')
# options.addOption('Scheduler', 'scheduler')
options.addOption('Import', 'import')
options.addOption('Export', 'export')
jid = session['from'].bare jid = session['from'].bare
if jid == config.get_value('accounts', 'XMPP', 'operator'): jid_full = str(session['from'])
options.addOption('Administration', 'admin') chat_type = await get_chat_type(self, jid)
session['payload'] = form if chat_type == 'groupchat':
session['next'] = self._handle_advanced_result moderator = is_moderator(self, jid, jid_full)
session['has_next'] = True if chat_type == 'chat' or moderator:
form = self['xep_0004'].make_form('form', 'Advanced')
form['instructions'] = 'Extended options'
options = form.add_field(var='option',
ftype='list-single',
label='Choose',
required=True)
# options.addOption('Activity', 'activity')
# options.addOption('Filters', 'filter')
# options.addOption('Statistics', 'statistics')
# options.addOption('Scheduler', 'scheduler')
options.addOption('Import', 'import')
options.addOption('Export', 'export')
jid = session['from'].bare
if jid == config.get_value('accounts', 'XMPP', 'operator'):
options.addOption('Administration', 'admin')
session['payload'] = form
session['next'] = self._handle_advanced_result
session['has_next'] = True
else:
text_warn = ('This resource is restricted to moderators of {}.'
.format(jid))
session['notes'] = [['warn', text_warn]]
return session return session
@ -1708,56 +1843,65 @@ class Slixfeed(slixmpp.ClientXMPP):
# TODO Attempt to look up for feeds of hostname of JID (i.e. scan # TODO Attempt to look up for feeds of hostname of JID (i.e. scan
# jabber.de for feeds for juliet@jabber.de) # jabber.de for feeds for juliet@jabber.de)
async def _handle_promoted(self, iq, session): async def _handle_promoted(self, iq, session):
form = self['xep_0004'].make_form('form', 'Subscribe')
# NOTE Refresh button would be of use
form['instructions'] = 'Featured subscriptions'
url = action.pick_a_feed()
# options = form.add_field(var='choice',
# ftype="boolean",
# label='Subscribe to {}?'.format(url['name']),
# desc='Click to subscribe.')
# form.add_field(var='subscription',
# ftype='hidden',
# value=url['link'])
options = form.add_field(var='subscription',
ftype="list-single",
label='Subscribe',
desc='Click to subscribe.')
for i in range(10):
url = action.pick_a_feed()
options.addOption(url['name'], url['link'])
jid = session['from'].bare jid = session['from'].bare
if '@' in jid: jid_full = str(session['from'])
hostname = jid.split('@')[1] chat_type = await get_chat_type(self, jid)
url = 'http://' + hostname if chat_type == 'groupchat':
result = await crawl.probe_page(url) moderator = is_moderator(self, jid, jid_full)
if not result: if chat_type == 'chat' or moderator:
url = {'url' : url, form = self['xep_0004'].make_form('form', 'Subscribe')
'index' : None, # NOTE Refresh button would be of use
'name' : None, form['instructions'] = 'Featured subscriptions'
'code' : None, url = action.pick_a_feed()
'error' : True, # options = form.add_field(var='choice',
'exist' : False} # ftype="boolean",
elif isinstance(result, list): # label='Subscribe to {}?'.format(url['name']),
for url in result: # desc='Click to subscribe.')
if url['link']: options.addOption('{}\n{}'.format(url['name'], url['link']), url['link']) # form.add_field(var='subscription',
# ftype='hidden',
# value=url['link'])
options = form.add_field(var='subscription',
ftype="list-single",
label='Subscribe',
desc='Click to subscribe.')
for i in range(10):
url = action.pick_a_feed()
options.addOption(url['name'], url['link'])
jid = session['from'].bare
if '@' in jid:
hostname = jid.split('@')[1]
url = 'http://' + hostname
result = await crawl.probe_page(url)
if not result:
url = {'url' : url,
'index' : None,
'name' : None,
'code' : None,
'error' : True,
'exist' : False}
elif isinstance(result, list):
for url in result:
if url['link']: options.addOption('{}\n{}'.format(url['name'], url['link']), url['link'])
else:
url = result
# Automatically set priority to 5 (highest)
if url['link']: options.addOption(url['name'], url['link'])
session['allow_complete'] = False
session['allow_prev'] = True
# singpolyma: Don't use complete action if there may be more steps
# https://gitgud.io/sjehuda/slixfeed/-/merge_requests/13
# Gajim: On next form Gajim offers no button other than "Commands".
# Psi: Psi closes the dialog.
# Conclusion, change session['has_next'] from False to True
# session['has_next'] = False
session['has_next'] = True
session['next'] = self._handle_subscription_new
session['payload'] = form
session['prev'] = self._handle_promoted
else: else:
url = result text_warn = ('This resource is restricted to moderators of {}.'
# Automatically set priority to 5 (highest) .format(jid))
if url['link']: options.addOption(url['name'], url['link']) session['notes'] = [['warn', text_warn]]
session['allow_complete'] = False
session['allow_prev'] = True
# singpolyma: Don't use complete action if there may be more steps
# https://gitgud.io/sjehuda/slixfeed/-/merge_requests/13
# Gajim: On next form Gajim offers no button other than "Commands".
# Psi: Psi closes the dialog.
# Conclusion, change session['has_next'] from False to True
# session['has_next'] = False
session['has_next'] = True
session['next'] = self._handle_subscription_new
session['payload'] = form
session['prev'] = self._handle_promoted
return session return session
@ -2062,102 +2206,111 @@ class Slixfeed(slixmpp.ClientXMPP):
here to persist across handler callbacks. here to persist across handler callbacks.
""" """
jid = session['from'].bare jid = session['from'].bare
jid_file = jid jid_full = str(session['from'])
db_file = config.get_pathname_to_database(jid_file) chat_type = await get_chat_type(self, jid)
form = self['xep_0004'].make_form('form', 'Settings') if chat_type == 'groupchat':
form['instructions'] = 'Editing settings' moderator = is_moderator(self, jid, jid_full)
if chat_type == 'chat' or moderator:
jid_file = jid
db_file = config.get_pathname_to_database(jid_file)
form = self['xep_0004'].make_form('form', 'Settings')
form['instructions'] = 'Editing settings'
value = config.get_setting_value(db_file, 'enabled') value = config.get_setting_value(db_file, 'enabled')
value = int(value) value = int(value)
if value: if value:
value = True value = True
else:
value = False
form.add_field(var='enabled',
ftype='boolean',
label='Enabled',
desc='Enable news updates.',
value=value)
value = config.get_setting_value(db_file, 'media')
value = int(value)
if value:
value = True
else:
value = False
form.add_field(var='media',
ftype='boolean',
desc='Send audio, images or videos if found.',
label='Display media',
value=value)
value = config.get_setting_value(db_file, 'old')
value = int(value)
if value:
value = True
else:
value = False
form.add_field(var='old',
ftype='boolean',
desc='Treat all items of newly added subscriptions as new.',
# label='Send only new items',
label='Include old news',
value=value)
value = config.get_setting_value(db_file, 'interval')
value = int(value)
value = value/60
value = int(value)
value = str(value)
options = form.add_field(var='interval',
ftype='list-single',
label='Interval',
desc='Interval update (in hours).',
value=value)
options['validate']['datatype'] = 'xs:integer'
options['validate']['range'] = { 'minimum': 1, 'maximum': 48 }
i = 1
while i <= 48:
x = str(i)
options.addOption(x, x)
if i >= 12:
i += 6
else: else:
value = False
form.add_field(var='enabled',
ftype='boolean',
label='Enabled',
desc='Enable news updates.',
value=value)
value = config.get_setting_value(db_file, 'media')
value = int(value)
if value:
value = True
else:
value = False
form.add_field(var='media',
ftype='boolean',
desc='Send audio, images or videos if found.',
label='Display media',
value=value)
value = config.get_setting_value(db_file, 'old')
value = int(value)
if value:
value = True
else:
value = False
form.add_field(var='old',
ftype='boolean',
desc='Treat all items of newly added subscriptions as new.',
# label='Send only new items',
label='Include old news',
value=value)
value = config.get_setting_value(db_file, 'interval')
value = int(value)
value = value/60
value = int(value)
value = str(value)
options = form.add_field(var='interval',
ftype='list-single',
label='Interval',
desc='Interval update (in hours).',
value=value)
options['validate']['datatype'] = 'xs:integer'
options['validate']['range'] = { 'minimum': 1, 'maximum': 48 }
i = 1
while i <= 48:
x = str(i)
options.addOption(x, x)
if i >= 12:
i += 6
else:
i += 1
value = config.get_setting_value(db_file, 'quantum')
value = str(value)
options = form.add_field(var='quantum',
ftype='list-single',
label='Amount',
desc='Amount of items per update.',
value=value)
options['validate']['datatype'] = 'xs:integer'
options['validate']['range'] = { 'minimum': 1, 'maximum': 5 }
i = 1
while i <= 5:
x = str(i)
options.addOption(x, x)
i += 1 i += 1
value = config.get_setting_value(db_file, 'quantum') value = config.get_setting_value(db_file, 'archive')
value = str(value) value = str(value)
options = form.add_field(var='quantum', options = form.add_field(var='archive',
ftype='list-single', ftype='list-single',
label='Amount', label='Archive',
desc='Amount of items per update.', desc='Number of news items to archive.',
value=value) value=value)
options['validate']['datatype'] = 'xs:integer' options['validate']['datatype'] = 'xs:integer'
options['validate']['range'] = { 'minimum': 1, 'maximum': 5 } options['validate']['range'] = { 'minimum': 0, 'maximum': 500 }
i = 1 i = 0
while i <= 5: while i <= 500:
x = str(i) x = str(i)
options.addOption(x, x) options.addOption(x, x)
i += 1 i += 50
session['allow_complete'] = True
value = config.get_setting_value(db_file, 'archive') session['has_next'] = False
value = str(value) session['next'] = self._handle_settings_complete
options = form.add_field(var='archive', session['payload'] = form
ftype='list-single', else:
label='Archive', text_warn = ('This resource is restricted to moderators of {}.'
desc='Number of news items to archive.', .format(jid))
value=value) session['notes'] = [['warn', text_warn]]
options['validate']['datatype'] = 'xs:integer'
options['validate']['range'] = { 'minimum': 0, 'maximum': 500 }
i = 0
while i <= 500:
x = str(i)
options.addOption(x, x)
i += 50
session['allow_complete'] = True
session['has_next'] = False
session['next'] = self._handle_settings_complete
session['payload'] = form
return session return session

View file

@ -81,8 +81,8 @@ async def message(self, message):
if (message['muc']['nick'] == self.alias): if (message['muc']['nick'] == self.alias):
return return
jid_full = str(message['from']) jid_full = str(message['from'])
role = self.plugin['xep_0045'].get_jid_property( alias = jid_full[jid_full.index('/')+1:]
jid, jid_full[jid_full.index('/')+1:], 'role') role = self.plugin['xep_0045'].get_jid_property(jid, alias, 'role')
if role != 'moderator': if role != 'moderator':
return return

View file

@ -55,12 +55,13 @@ async def set_avatar(self):
config_dir.pop() config_dir.pop()
config_dir = '/'.join(config_dir) config_dir = '/'.join(config_dir)
filename = glob.glob(config_dir + '/assets/image.*') filename = glob.glob(config_dir + '/assets/image.*')
filename = filename[0] if len(filename):
image_file = os.path.join(config_dir, filename) filename = filename[0]
with open(image_file, 'rb') as avatar_file: image_file = os.path.join(config_dir, filename)
avatar = avatar_file.read() with open(image_file, 'rb') as avatar_file:
# await self.plugin['xep_0084'].publish_avatar(avatar) avatar = avatar_file.read()
await self.plugin['xep_0153'].set_avatar(avatar=avatar) # await self.plugin['xep_0084'].publish_avatar(avatar)
await self.plugin['xep_0153'].set_avatar(avatar=avatar)
def set_identity(self, category): def set_identity(self, category):

View file

@ -7,9 +7,19 @@ import logging
# class XmppChat # class XmppChat
# class XmppUtility: # class XmppUtility:
def is_moderator(self, jid, jid_full):
alias = jid_full[jid_full.index('/')+1:]
role = self.plugin['xep_0045'].get_jid_property(jid, alias, 'role')
if role == 'moderator':
return True
else:
return False
# TODO Rename to get_jid_type
async def get_chat_type(self, jid): async def get_chat_type(self, jid):
""" """
Check whether a JID is of MUC. Check chat (i.e. JID) type.
If iqresult["disco_info"]["features"] contains XML namespace If iqresult["disco_info"]["features"] contains XML namespace
of 'http://jabber.org/protocol/muc', then it is a 'groupchat'. of 'http://jabber.org/protocol/muc', then it is a 'groupchat'.