forked from sch/Slixfeed
Replace configuration file INI by TOML.
Fix ping functionality when activated as component (thank you Guus and MattJ). Add initial code for XEP-0060: Publish-Subscribe. Fix case-sensitivity with setting keys sent in-chat-command (Thank you mirux)
This commit is contained in:
parent
ba61250f84
commit
b1a1955545
22 changed files with 802 additions and 1168 deletions
|
@ -85,21 +85,24 @@ from slixfeed.version import __version__
|
|||
# import socks
|
||||
# import socket
|
||||
|
||||
xmpp_type = config.get_value('accounts', 'XMPP', 'type')
|
||||
account = config.get_values('accounts.toml', 'xmpp')
|
||||
|
||||
if not 'mode' in account['settings']:
|
||||
account_mode = 'client'
|
||||
print('Key "mode" was not found.\nSetting value to "client".')
|
||||
# raise Exception('Key type is missing from accounts.toml.')
|
||||
else:
|
||||
account_mode = 'component'
|
||||
|
||||
if not xmpp_type:
|
||||
raise Exception('Key type is missing from accounts.ini.')
|
||||
|
||||
match xmpp_type:
|
||||
match account_mode:
|
||||
case 'client':
|
||||
from slixfeed.xmpp.client import Slixfeed
|
||||
from slixfeed.config import ConfigClient as ConfigAccount
|
||||
# from slixfeed.config import ConfigClient as ConfigAccount
|
||||
case 'component':
|
||||
from slixfeed.xmpp.component import SlixfeedComponent
|
||||
from slixfeed.config import ConfigComponent as ConfigAccount
|
||||
# from slixfeed.config import ConfigComponent as ConfigAccount
|
||||
|
||||
account = ConfigAccount() # TODO Delete as soon as posible after is no longer needed
|
||||
# account = ConfigAccount() # TODO Delete as soon as posible after is no longer needed
|
||||
|
||||
class JabberComponent:
|
||||
def __init__(self, jid, secret, hostname, port, alias=None):
|
||||
|
@ -175,9 +178,8 @@ class JabberClient:
|
|||
# xmpp.proxy = {'socks5': ('localhost', 9050)}
|
||||
|
||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||
|
||||
if account.setting['hostname'] and account.setting['port']:
|
||||
xmpp.connect((account.setting['hostname'], account.setting['port']))
|
||||
if hostname and port:
|
||||
xmpp.connect((hostname, port))
|
||||
else:
|
||||
xmpp.connect()
|
||||
xmpp.process()
|
||||
|
@ -200,7 +202,7 @@ def main():
|
|||
# # socket.socket = socks.socksocket
|
||||
|
||||
# Setup the command line arguments.
|
||||
match xmpp_type:
|
||||
match account_mode:
|
||||
case 'client':
|
||||
parser = ArgumentParser(description=Slixfeed.__doc__)
|
||||
case 'component':
|
||||
|
@ -242,11 +244,11 @@ def main():
|
|||
# logwrn = logger.warning
|
||||
|
||||
# Try configuration file
|
||||
alias = account.setting['alias']
|
||||
jid = account.setting['jid']
|
||||
password = account.setting['password']
|
||||
hostname = account.setting['hostname']
|
||||
port = account.setting['port']
|
||||
jid = account[account_mode]['jid']
|
||||
password = account[account_mode]['password']
|
||||
alias = account[account_mode]['alias'] if 'alias' in account[account_mode] else None
|
||||
hostname = account[account_mode]['hostname'] if 'hostname' in account[account_mode] else None
|
||||
port = account[account_mode]['port'] if 'port' in account[account_mode] else None
|
||||
|
||||
# Use arguments if were given
|
||||
if args.jid:
|
||||
|
@ -268,7 +270,7 @@ def main():
|
|||
if not alias:
|
||||
alias = (input('Alias: ')) or 'Slixfeed'
|
||||
|
||||
match xmpp_type:
|
||||
match account_mode:
|
||||
case 'client':
|
||||
JabberClient(jid, password, hostname=hostname, port=port, alias=alias)
|
||||
case 'component':
|
||||
|
|
|
@ -113,6 +113,27 @@ async def export_feeds(self, jid, jid_file, ext):
|
|||
# response = 'Not yet implemented.'
|
||||
return filename
|
||||
|
||||
|
||||
"""
|
||||
TODO
|
||||
|
||||
Consider to append text to remind to share presence
|
||||
'✒️ Share online status to receive updates'
|
||||
|
||||
# TODO Request for subscription
|
||||
if (await get_chat_type(self, jid_bare) == 'chat' and
|
||||
not self.client_roster[jid_bare]['to']):
|
||||
XmppPresence.subscription(self, jid_bare, 'subscribe')
|
||||
await XmppRoster.add(self, jid_bare)
|
||||
status_message = '✒️ Share online status to receive updates'
|
||||
XmppPresence.send(self, jid_bare, status_message)
|
||||
message_subject = 'RSS News Bot'
|
||||
message_body = 'Share online status to receive updates.'
|
||||
XmppMessage.send_headline(self, jid_bare, message_subject,
|
||||
message_body, 'chat')
|
||||
|
||||
"""
|
||||
|
||||
async def xmpp_send_status(self, jid):
|
||||
"""
|
||||
Send status message.
|
||||
|
@ -127,7 +148,7 @@ async def xmpp_send_status(self, jid):
|
|||
status_text = '📜️ Slixfeed RSS News Bot'
|
||||
jid_file = jid.replace('/', '_')
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
enabled = self.settings[jid]['enabled'] or self.settings['default']['enabled']
|
||||
enabled = Config.get_setting_value(self.settings, jid, 'enabled')
|
||||
if not enabled:
|
||||
status_mode = 'xa'
|
||||
status_text = '📪️ Send "Start" to receive updates'
|
||||
|
@ -179,11 +200,11 @@ async def xmpp_send_update(self, jid, num=None):
|
|||
logger.debug('{}: jid: {} num: {}'.format(function_name, jid, num))
|
||||
jid_file = jid.replace('/', '_')
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
enabled = self.settings[jid]['enabled'] or self.settings['default']['enabled']
|
||||
enabled = Config.get_setting_value(self.settings, jid, 'enabled')
|
||||
if enabled:
|
||||
show_media = self.settings[jid]['media'] or self.settings['default']['media']
|
||||
show_media = Config.get_setting_value(self.settings, jid, 'media')
|
||||
if not num:
|
||||
num = self.settings[jid]['quantum'] or self.settings['default']['quantum']
|
||||
num = Config.get_setting_value(self.settings, jid, 'quantum')
|
||||
else:
|
||||
num = int(num)
|
||||
results = sqlite.get_unread_entries(db_file, num)
|
||||
|
@ -465,7 +486,7 @@ def list_unread_entries(self, result, feed_title, jid):
|
|||
summary = summary.replace('\n', ' ')
|
||||
summary = summary.replace(' ', ' ')
|
||||
summary = summary.replace(' ', ' ')
|
||||
length = self.settings[jid]['length'] or self.settings['default']['length']
|
||||
length = Config.get_setting_value(self.settings, jid, 'length')
|
||||
length = int(length)
|
||||
summary = summary[:length] + " […]"
|
||||
# summary = summary.strip().split('\n')
|
||||
|
@ -476,13 +497,13 @@ def list_unread_entries(self, result, feed_title, jid):
|
|||
link = (replace_hostname(link, "link")) or link
|
||||
# news_item = ("\n{}\n{}\n{} [{}]\n").format(str(title), str(link),
|
||||
# str(feed_title), str(ix))
|
||||
formatting = self.settings[jid]['formatting'] or self.settings['default']['formatting']
|
||||
formatting = Config.get_setting_value(self.settings, jid, 'formatting')
|
||||
news_item = formatting.format(feed_title=feed_title,
|
||||
title=title,
|
||||
summary=summary,
|
||||
link=link,
|
||||
ix=ix)
|
||||
news_item = news_item.replace('\\n', '\n')
|
||||
# news_item = news_item.replace('\\n', '\n')
|
||||
return news_item
|
||||
|
||||
|
||||
|
@ -502,11 +523,9 @@ def list_search_results(query, results):
|
|||
return message
|
||||
|
||||
|
||||
def list_feeds_by_query(db_file, query):
|
||||
def list_feeds_by_query(query, results):
|
||||
function_name = sys._getframe().f_code.co_name
|
||||
logger.debug('{}: db_file: {} query: {}'
|
||||
.format(function_name, db_file, query))
|
||||
results = sqlite.search_feeds(db_file, query)
|
||||
logger.debug('{}'.format(function_name))
|
||||
message = ('Feeds containing "{}":\n\n```'
|
||||
.format(query))
|
||||
for result in results:
|
||||
|
@ -549,10 +568,10 @@ async def list_options(self, jid_bare):
|
|||
# value = "Default"
|
||||
# values.extend([value])
|
||||
|
||||
value_archive = self.settings[jid_bare]['archive'] or self.settings['default']['archive']
|
||||
value_interval = self.settings[jid_bare]['interval'] or self.settings['default']['interval']
|
||||
value_quantum = self.settings[jid_bare]['quantum'] or self.settings['default']['quantum']
|
||||
value_enabled = self.settings[jid_bare]['archive'] or self.settings['default']['enabled']
|
||||
value_archive = Config.get_setting_value(self.settings, jid_bare, 'archive')
|
||||
value_interval = Config.get_setting_value(self.settings, jid_bare, 'interval')
|
||||
value_quantum = Config.get_setting_value(self.settings, jid_bare, 'quantum')
|
||||
value_enabled = Config.get_setting_value(self.settings, jid_bare, 'enabled')
|
||||
|
||||
message = ("Options:"
|
||||
"\n"
|
||||
|
@ -647,13 +666,10 @@ def list_feeds(results):
|
|||
logger.debug('{}'.format(function_name))
|
||||
message = "\nList of subscriptions:\n\n```\n"
|
||||
for result in results:
|
||||
message += ("Name : {}\n"
|
||||
"URL : {}\n"
|
||||
# "Updated : {}\n"
|
||||
# "Status : {}\n"
|
||||
"ID : {}\n"
|
||||
"\n"
|
||||
.format(str(result[0]), str(result[1]), str(result[2])))
|
||||
message += ("{} [{}]\n"
|
||||
"{}\n"
|
||||
"\n\n"
|
||||
.format(str(result[1]), str(result[0]), str(result[2])))
|
||||
if len(results):
|
||||
message += ('```\nTotal of {} subscriptions.\n'
|
||||
.format(len(results)))
|
||||
|
@ -793,7 +809,7 @@ async def add_feed(self, jid_bare, db_file, url):
|
|||
status_code=status_code,
|
||||
updated=updated)
|
||||
await scan(self, jid_bare, db_file, url)
|
||||
old = self.settings[jid_bare]['old'] or self.settings['default']['old']
|
||||
old = Config.get_setting_value(self.settings, jid_bare, 'old')
|
||||
feed_id = sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
if not old:
|
||||
|
@ -804,9 +820,6 @@ async def add_feed(self, jid_bare, db_file, url):
|
|||
'code' : status_code,
|
||||
'error' : False,
|
||||
'exist' : False}
|
||||
response = ('> {}\nNews source "{}" has been '
|
||||
'added to subscription list.'
|
||||
.format(url, title))
|
||||
break
|
||||
# NOTE This elif statement be unnecessary
|
||||
# when feedparser be supporting json feed.
|
||||
|
@ -843,7 +856,7 @@ async def add_feed(self, jid_bare, db_file, url):
|
|||
status_code=status_code,
|
||||
updated=updated)
|
||||
await scan_json(self, jid_bare, db_file, url)
|
||||
old = self.settings[jid_bare]['old'] or self.settings['default']['old']
|
||||
old = Config.get_setting_value(self.settings, jid_bare, 'old')
|
||||
if not old:
|
||||
feed_id = sqlite.get_feed_id(db_file, url)
|
||||
feed_id = feed_id[0]
|
||||
|
@ -854,9 +867,6 @@ async def add_feed(self, jid_bare, db_file, url):
|
|||
'code' : status_code,
|
||||
'error' : False,
|
||||
'exist' : False}
|
||||
response = ('> {}\nNews source "{}" has been '
|
||||
'added to subscription list.'
|
||||
.format(url, title))
|
||||
break
|
||||
else:
|
||||
# NOTE Do not be tempted to return a compact dictionary.
|
||||
|
@ -886,8 +896,6 @@ async def add_feed(self, jid_bare, db_file, url):
|
|||
'code' : status_code,
|
||||
'error' : True,
|
||||
'exist' : False}
|
||||
response = ('> {}\nFailed to load URL. Reason: {}'
|
||||
.format(url, status_code))
|
||||
break
|
||||
else:
|
||||
ix = exist[0]
|
||||
|
@ -898,9 +906,6 @@ async def add_feed(self, jid_bare, db_file, url):
|
|||
'code' : None,
|
||||
'error' : False,
|
||||
'exist' : True}
|
||||
response = ('> {}\nNews source "{}" is already '
|
||||
'listed in the subscription list at '
|
||||
'index {}'.format(url, name, ix))
|
||||
break
|
||||
return result_final
|
||||
|
||||
|
@ -1254,7 +1259,7 @@ async def scan(self, jid_bare, db_file, url):
|
|||
return
|
||||
# new_entry = 0
|
||||
for entry in entries:
|
||||
logger.debug('{}: entry: {}'.format(function_name, entry.title))
|
||||
logger.debug('{}: entry: {}'.format(function_name, entry.link))
|
||||
if entry.has_key("published"):
|
||||
date = entry.published
|
||||
date = dt.rfc2822_to_iso8601(date)
|
||||
|
@ -1481,6 +1486,7 @@ async def extract_image_from_html(url):
|
|||
'contains(@src, "emoji") or '
|
||||
'contains(@src, "icon") or '
|
||||
'contains(@src, "logo") or '
|
||||
'contains(@src, "letture") or '
|
||||
'contains(@src, "search") or '
|
||||
'contains(@src, "share") or '
|
||||
'contains(@src, "smiley")'
|
||||
|
@ -1614,7 +1620,7 @@ async def remove_nonexistent_entries(self, jid_bare, db_file, url, feed):
|
|||
feed_id = feed_id[0]
|
||||
items = sqlite.get_entries_of_feed(db_file, feed_id)
|
||||
entries = feed.entries
|
||||
limit = self.settings[jid_bare]['archive'] or self.settings['default']['archive']
|
||||
limit = Config.get_setting_value(self.settings, jid_bare, 'archive')
|
||||
for item in items:
|
||||
ix = item[0]
|
||||
entry_title = item[1]
|
||||
|
@ -1723,7 +1729,7 @@ async def remove_nonexistent_entries_json(self, jid_bare, db_file, url, feed):
|
|||
feed_id = feed_id[0]
|
||||
items = sqlite.get_entries_of_feed(db_file, feed_id)
|
||||
entries = feed["items"]
|
||||
limit = self.settings[jid_bare]['archive'] or self.settings['default']['archive']
|
||||
limit = Config.get_setting_value(self.settings, jid_bare, 'archive')
|
||||
for item in items:
|
||||
ix = item[0]
|
||||
entry_title = item[1]
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
# Settings to tell the bot to which accounts to connect
|
||||
# and also from which accounts it receives instructions.
|
||||
|
||||
[XMPP]
|
||||
operator =
|
||||
reconnect_timeout = 30
|
||||
type = client
|
||||
#type = component
|
||||
|
||||
[XMPP Client]
|
||||
alias = Slixfeed
|
||||
jid =
|
||||
password =
|
||||
#hostname =
|
||||
#port =
|
||||
|
||||
[XMPP Component]
|
||||
alias = Slixfeed
|
||||
jid =
|
||||
password =
|
||||
hostname =
|
||||
port =
|
||||
|
||||
[XMPP Profile]
|
||||
name = Slixfeed
|
||||
nickname = Slixfeed
|
||||
role = Syndication News Bot
|
||||
organization = RSS Task Force
|
||||
url = https://gitgud.io/sjehuda/slixfeed
|
||||
description = XMPP news bot (supports Atom, JSON, RDF and RSS).
|
||||
note = This is a syndication news bot powered by Slixfeed.
|
||||
birthday = 21 June 2022
|
||||
|
||||
[XMPP Proxy]
|
||||
# NOTE You might want to consider support for socks4 too (this
|
||||
# note was written when keys were proxy_host and proxy_port)
|
||||
|
||||
# NOTE Consider not to use a version number as it might give an
|
||||
# impression of an archaic feature in the future.
|
||||
|
||||
#socks5_host =
|
||||
#socks5_port =
|
||||
#socks5_username =
|
||||
#socks5_password =
|
||||
|
||||
[ActivityPub]
|
||||
username =
|
||||
password =
|
||||
operator =
|
||||
|
||||
[Email]
|
||||
recipient_emails =
|
||||
sender_emails =
|
||||
|
||||
[IMAP]
|
||||
username =
|
||||
password =
|
||||
#port = 993
|
||||
|
||||
[SMTP]
|
||||
host =
|
||||
#port = 465
|
||||
|
||||
[IRC]
|
||||
username =
|
||||
password =
|
||||
#port = 6667
|
||||
operator =
|
||||
|
||||
[LXMF]
|
||||
username =
|
||||
password =
|
||||
operator =
|
||||
|
||||
[Matrix]
|
||||
username =
|
||||
password =
|
||||
operator =
|
||||
|
||||
[Nostr]
|
||||
username =
|
||||
password =
|
||||
operator =
|
||||
|
||||
[Session]
|
||||
username =
|
||||
password =
|
||||
operator =
|
||||
|
||||
[SIP]
|
||||
username =
|
||||
password =
|
||||
operator =
|
||||
|
||||
[TOX]
|
||||
username =
|
||||
password =
|
||||
operator =
|
108
slixfeed/assets/accounts.toml
Normal file
108
slixfeed/assets/accounts.toml
Normal file
|
@ -0,0 +1,108 @@
|
|||
# Settings to tell the bot to which accounts to connect
|
||||
# and also from which accounts it receives instructions.
|
||||
|
||||
[xmpp.settings]
|
||||
#mode = "component"
|
||||
reconnect_timeout = 30
|
||||
|
||||
[[xmpp.operators]]
|
||||
name = "Mr. Operator"
|
||||
jid = ""
|
||||
|
||||
[[xmpp.operators]]
|
||||
name = "Mrs. Operator"
|
||||
jid = ""
|
||||
|
||||
[xmpp.proxy.socks5]
|
||||
#host = "127.0.0.1"
|
||||
#port = 9050
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[xmpp.profile]
|
||||
FN = "Slixfeed"
|
||||
NICKNAME = "Slixfeed"
|
||||
ROLE = "Syndication News Bot"
|
||||
ORG = "RSS Task Force"
|
||||
URL = "https://gitgud.io/sjehuda/slixfeed"
|
||||
NOTE = """
|
||||
This is a syndication news bot powered by Slixfeed.
|
||||
This bot can read Atom, JSON, RDF and RSS feeds.
|
||||
This bot can communicate to 1:1 chats and groupchats as one.
|
||||
You are welcome to join our groupchat at:
|
||||
xmpp:slixfeed@chat.woodpeckersnest.space?join
|
||||
"""
|
||||
BDAY = "21 June 2022"
|
||||
TITLE = "XMPP News Bot"
|
||||
DESC = "Syndication bot made for XMPP."
|
||||
|
||||
[xmpp.client]
|
||||
alias = "Slixfeed"
|
||||
jid = "slixfeed@your.server/slixfeed"
|
||||
password = ""
|
||||
#hostname =
|
||||
#port =
|
||||
|
||||
[xmpp.component]
|
||||
alias = "Slixfeed"
|
||||
jid = "rss.your.server"
|
||||
password = ""
|
||||
hostname = "your.server"
|
||||
#port =
|
||||
|
||||
[activitypub]
|
||||
username = ""
|
||||
password = ""
|
||||
operator = ""
|
||||
|
||||
[activitypub.profile]
|
||||
user_agent = "Slixfeed"
|
||||
|
||||
[email]
|
||||
recipient_emails = ""
|
||||
sender_emails = ""
|
||||
|
||||
[email.imap]
|
||||
username = ""
|
||||
password = ""
|
||||
#port = 993
|
||||
|
||||
[email.smtp]
|
||||
host = ""
|
||||
#port = 465
|
||||
|
||||
[irc]
|
||||
username = ""
|
||||
password = ""
|
||||
#port = 6667
|
||||
operator = ""
|
||||
|
||||
[deltachat]
|
||||
username = ""
|
||||
password = ""
|
||||
operator = ""
|
||||
|
||||
[lxmf]
|
||||
username = ""
|
||||
password = ""
|
||||
operator = ""
|
||||
|
||||
[nostr]
|
||||
username = ""
|
||||
password = ""
|
||||
operator = ""
|
||||
|
||||
[session]
|
||||
username = ""
|
||||
password = ""
|
||||
operator = ""
|
||||
|
||||
[sip]
|
||||
username = ""
|
||||
password = ""
|
||||
operator = ""
|
||||
|
||||
[tox]
|
||||
username = ""
|
||||
password = ""
|
||||
operator = ""
|
|
@ -1,9 +1,5 @@
|
|||
[[about]]
|
||||
title = "About"
|
||||
subtitle = "Slixfeed news bot"
|
||||
|
||||
[[about]]
|
||||
info = ["""
|
||||
[about]
|
||||
info = """
|
||||
Slixfeed is a news broker bot for syndicated news which aims to be \
|
||||
an easy to use and fully-featured news aggregating bot.
|
||||
|
||||
|
@ -12,10 +8,13 @@ even Fediverse instances, along with filtering and other privacy \
|
|||
driven functionalities.
|
||||
|
||||
Slixfeed is designed primarily for the XMPP communication network \
|
||||
(aka Jabber). Visit https://xmpp.org/software/ for more information.
|
||||
"""]
|
||||
(aka Jabber).
|
||||
|
||||
note = ["""
|
||||
https://gitgud.io/sjehuda/slixfeed
|
||||
"""
|
||||
|
||||
[note]
|
||||
note = """
|
||||
You can run your own Slixfeed instance as a client, from your own \
|
||||
computer, server, and even from a Linux phone (i.e. Droidian, Kupfer, \
|
||||
Mobian, NixOS, postmarketOS), as well as from Termux.
|
||||
|
@ -24,107 +23,59 @@ All you need is one of the above and an XMPP account to connect \
|
|||
Slixfeed with.
|
||||
|
||||
Good luck!
|
||||
"""]
|
||||
|
||||
filetypes = "Atom, JSON, RDF, RSS, XML."
|
||||
platforms = "XMPP"
|
||||
# platforms = "ActivityPub, Briar, Email, IRC, LXMF, MQTT, Nostr, Session, Tox."
|
||||
comment = "For ideal experience, we recommend using XMPP."
|
||||
url = "https://gitgud.io/sjehuda/slixfeed"
|
||||
|
||||
[[authors]]
|
||||
title = "Authors"
|
||||
subtitle = "The people who have made Slixfeed"
|
||||
|
||||
[[authors]]
|
||||
name = "Laura Lapina"
|
||||
role = "Co-Author, Instructor and Mentor"
|
||||
type = "AsyncIO, SQLite"
|
||||
|
||||
[[authors]]
|
||||
name = "Schimon Zackary"
|
||||
role = "Creator and Author"
|
||||
|
||||
[[contributors]]
|
||||
title = "Contributors"
|
||||
subtitle = "The people who have contributed to Slixfeed"
|
||||
|
||||
[[contributors]]
|
||||
name = "Stephen Paul Weber"
|
||||
role = "Contributor and forms coordinator"
|
||||
type = "XEP-0004, XEP-0050, XEP-0122"
|
||||
project = "Cheogram"
|
||||
|
||||
[[friends]]
|
||||
title = "Similar Projects"
|
||||
subtitle = """
|
||||
From Argentina to Germany. Syndication bots made by our counterparts.
|
||||
"""
|
||||
|
||||
[[friends]]
|
||||
name = "err-rssreader"
|
||||
info = ["A port of old Brutal's RSS Reader for Errbot"]
|
||||
url = "https://github.com/errbotters/err-rssreader"
|
||||
[authors]
|
||||
info = """
|
||||
Schimon Zackary
|
||||
Laura Lapina
|
||||
"""
|
||||
|
||||
[[friends]]
|
||||
name = "feed-to-muc"
|
||||
info = ["""
|
||||
An XMPP bot which posts to a MUC (groupchat) if there is an update in newsfeeds.
|
||||
"""]
|
||||
url = "https://salsa.debian.org/mdosch/feed-to-muc"
|
||||
[contributors]
|
||||
info = """
|
||||
Guus der Kinderen
|
||||
grym (from #python IRC channel)
|
||||
Stephen Paul Weber
|
||||
"""
|
||||
|
||||
[[friends]]
|
||||
name = "JabRSS (fork)"
|
||||
info = ["""
|
||||
Never miss a headline again! JabRSS is a simple RSS (RDF Site Summary) \
|
||||
headline notification service for Jabber.
|
||||
[bots]
|
||||
info = """
|
||||
Syndication bots made by our counterparts.
|
||||
|
||||
It is based on jabrss@cmeerw.net from Christof.
|
||||
Morbot
|
||||
https://codeberg.org/TheCoffeMaker/Morbot
|
||||
|
||||
It was restructured and offers additional features (see help, help filter and \
|
||||
show plugins).
|
||||
"""]
|
||||
url = "http://www.jotwewe.de/de/xmpp/jabrss/jabrss_en.htm"
|
||||
feed-to-muc
|
||||
https://salsa.debian.org/mdosch/feed-to-muc
|
||||
|
||||
[[friends]]
|
||||
name = "JabRSS"
|
||||
info = ["""
|
||||
A simple RSS (RDF Site Summary) headline notification service for Jabber/XMPP.
|
||||
JabRSS
|
||||
http://www.jotwewe.de/de/xmpp/jabrss/jabrss_en.htm
|
||||
|
||||
A public instance of the bot is available via xmpp:jabrss@cmeerw.net
|
||||
"""]
|
||||
url = "https://dev.cmeerw.org/Projects/jabrss"
|
||||
JabRSS
|
||||
https://dev.cmeerw.org/Projects/jabrss
|
||||
|
||||
[[friends]]
|
||||
name = "Morbot"
|
||||
info = ["""
|
||||
Morbo is a simple Slixmpp bot that will take new articles from listed RSS \
|
||||
feeds and send them to assigned XMPP MUCs (groupchats).
|
||||
"""]
|
||||
url = "https://codeberg.org/TheCoffeMaker/Morbot"
|
||||
err-rssreader"
|
||||
https://github.com/errbotters/err-rssreader
|
||||
|
||||
[[legal]]
|
||||
title = "Legal"
|
||||
subtitle = "Legal Notice"
|
||||
XMPP Bot
|
||||
https://github.com/nioc/xmpp-bot
|
||||
"""
|
||||
|
||||
[[legal]]
|
||||
info = ["""
|
||||
[legal]
|
||||
info = """
|
||||
Slixfeed is free software; you can redistribute it and/or modify it under the \
|
||||
terms of the MIT License.
|
||||
|
||||
Slixfeed is distributed in the hope that it will be useful, but WITHOUT ANY \
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR \
|
||||
A PARTICULAR PURPOSE. See the MIT License for more details.
|
||||
"""]
|
||||
link = "https://gitgud.io/sjehuda/slixfeed"
|
||||
|
||||
[[license]]
|
||||
title = "License"
|
||||
subtitle = "MIT License"
|
||||
https://gitgud.io/sjehuda/slixfeed
|
||||
"""
|
||||
|
||||
[[license]]
|
||||
license = ["""
|
||||
Copyright 2022 - 2024 Schimon Zackary Jehudah
|
||||
[license]
|
||||
info = """
|
||||
Copyright 2022 - 2024 Schimon Jehudah Zackary
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy \
|
||||
of this software and associated documentation files (the “Software”), to deal \
|
||||
|
@ -143,558 +94,136 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER \
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING \
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS \
|
||||
IN THE SOFTWARE.
|
||||
"""]
|
||||
owner = "Schimon Zackary"
|
||||
"""
|
||||
|
||||
[[support]]
|
||||
title = "Support"
|
||||
subtitle = "Slixfeed Support Groupchat"
|
||||
[support]
|
||||
info = """
|
||||
Slixfeed Support Groupchat
|
||||
xmpp:slixfeed@chat.woodpeckersnest.space?join
|
||||
"""
|
||||
|
||||
[[support]]
|
||||
jid = "xmpp:slixfeed@chat.woodpeckersnest.space?join"
|
||||
|
||||
[[thanks]]
|
||||
title = "Thanks"
|
||||
subtitle = """
|
||||
[thanks]
|
||||
info = """
|
||||
From SalixOS to Gajim. A journey of 15 years. \
|
||||
The people who have made all this possible.
|
||||
|
||||
Alixander Court (Utah), Arne-Brün Vogelsang (Germany), Chris Farrell (Oregon) \
|
||||
Christian Dersch, Cyrille Pontvieux (France), Denis Fomin (Russia), Dimitris \
|
||||
Tzemos (Greece), Emmanuel Gil Peyrot (France), Florent Le Coz (France), George \
|
||||
Vlahavas (Greece), Guus der Kinderen (Netherlands), habnabit_ (#python), Imar \
|
||||
van Erven Dorens (Netherlands), imattau (atomtopubsub), Jaussoin Timothée \
|
||||
(France), Justin Karneges (California), Kevin Smith (Wales), Lars Windolf \
|
||||
(Germany), Luis Henrique Mello (Brazil), magicfelix, Markus Muttilainen \
|
||||
(SalixOS), Martin (Germany), Mathieu Pasquet (France), Maxime Buquet (France), \
|
||||
mirux (Germany), Phillip Watkins (England), Pierrick Le Brun (France), Raphael \
|
||||
Groner (Germany), Remko Tronçon (Belgium), Richard Lapointe (Connecticut), \
|
||||
Simone "roughnecks" Canaletti (Italy), Strix from Loqi, Thibaud Guerin \
|
||||
(SalixOS), Thorsten Fröhlich (France), Thorsten Mühlfelder (Germany), Tim \
|
||||
Beech (Brazil), Tomoki Tsuchiya (SalixOS), Yann Leboulanger (France)
|
||||
|
||||
Thanks also to the friends of #python at irc.libera.chat
|
||||
"""
|
||||
|
||||
[[thanks]]
|
||||
name = "Alixander Court"
|
||||
country = "Utah"
|
||||
url = "https://alixandercourt.com"
|
||||
[operators]
|
||||
info = """
|
||||
No operator was specified for this instance.
|
||||
"""
|
||||
|
||||
[[thanks]]
|
||||
name = "Arne-Brün Vogelsang"
|
||||
country = "Germany"
|
||||
project = "monocles"
|
||||
url = "https://monocles.eu/more"
|
||||
|
||||
[[thanks]]
|
||||
name = "Chris Farrell"
|
||||
alias = "timcowchip"
|
||||
country = "Oregon"
|
||||
project = "SalixOS"
|
||||
|
||||
[[thanks]]
|
||||
name = "Christian Dersch"
|
||||
alias = "christian"
|
||||
project = "SalixOS"
|
||||
|
||||
[[thanks]]
|
||||
name = "Cyrille Pontvieux"
|
||||
alias = "JRD"
|
||||
country = "France"
|
||||
project = "SalixOS"
|
||||
url = "http://animeka.com http://enialis.net"
|
||||
jabber = "xmpp:jrd@jabber.cz?message"
|
||||
|
||||
[[thanks]]
|
||||
name = "Denis Fomin"
|
||||
alias = "Dicson"
|
||||
country = "Russia"
|
||||
project = "Gajim"
|
||||
url = "https://juick.com/dicson"
|
||||
|
||||
[[thanks]]
|
||||
name = "Dimitris Tzemos"
|
||||
alias = "djemos"
|
||||
country = "Greece"
|
||||
projects = "SalixOS, Slackel"
|
||||
url = "http://slackel.gr"
|
||||
jabber = "xmpp:djemos@jabber.org?message"
|
||||
|
||||
[[thanks]]
|
||||
name = "Emmanuel Gil Peyrot"
|
||||
alias = "Link mauve"
|
||||
country = "France"
|
||||
projects = "Poezio, slixmpp"
|
||||
jabber = "xmpp:linkmauve@linkmauve.fr?message"
|
||||
url = "https://linkmauve.fr"
|
||||
|
||||
[[thanks]]
|
||||
name = "Florent Le Coz"
|
||||
alias = "louiz"
|
||||
country = "France"
|
||||
projects = "Poezio, slixmpp"
|
||||
jabber = "xmpp:louiz@louiz.org?message"
|
||||
url = "https://louiz.org"
|
||||
|
||||
[[thanks]]
|
||||
name = "George Vlahavas"
|
||||
alias = "gapan"
|
||||
country = "Greece"
|
||||
project = "SalixOS"
|
||||
url = "https://salixos.org https://vlahavas.com"
|
||||
|
||||
[[thanks]]
|
||||
name = "Guus der Kinderen"
|
||||
country = "Netherlands"
|
||||
project = "Openfire"
|
||||
url = "https://igniterealtime.org"
|
||||
|
||||
[[thanks]]
|
||||
name = "habnabit_"
|
||||
alias = "habnabit_"
|
||||
irc = "irc://irc.libera.chat/#python"
|
||||
|
||||
[[thanks]]
|
||||
name = "Imar van Erven Dorens"
|
||||
country = "Netherlands"
|
||||
project = "SalixOS"
|
||||
url = "https://simplicit.nl"
|
||||
|
||||
[[thanks]]
|
||||
name = "imattau"
|
||||
alias = "imattau"
|
||||
project = "atomtopubsub"
|
||||
|
||||
[[thanks]]
|
||||
name = "Jaussoin Timothée"
|
||||
alias = "edhelas"
|
||||
country = "France"
|
||||
projects = "atomtopubsub, Movim"
|
||||
url = "https://mov.im"
|
||||
|
||||
[[thanks]]
|
||||
name = "Justin Karneges"
|
||||
country = "California"
|
||||
project = "Psi"
|
||||
url = "https://jblog.andbit.net https://psi-im.org"
|
||||
|
||||
[[thanks]]
|
||||
name = "Kevin Smith"
|
||||
alias = "Kev"
|
||||
country = "Wales"
|
||||
projects = "Psi, SleekXMPP, Swift IM"
|
||||
url = "http://kismith.co.uk https://isode.com https://swift.im"
|
||||
|
||||
[[thanks]]
|
||||
name = "Lars Windolf"
|
||||
alias = "lwindolf"
|
||||
country = "Germany"
|
||||
project = "Liferea"
|
||||
url = "https://lzone.de"
|
||||
|
||||
[[thanks]]
|
||||
name = "Luis Henrique Mello"
|
||||
alias = "lmello"
|
||||
country = "Brazil"
|
||||
project = "SalixOS"
|
||||
|
||||
[[thanks]]
|
||||
name = "magicfelix"
|
||||
alias = "magicfelix"
|
||||
|
||||
[[thanks]]
|
||||
name = "Markus Muttilainen"
|
||||
alias = "stillborn"
|
||||
project = "SalixOS"
|
||||
|
||||
[[thanks]]
|
||||
name = "Martin"
|
||||
alias = "debacle"
|
||||
country = "Germany"
|
||||
projects = "Debian, sms4you"
|
||||
email = "mailto:debacle@debian.org"
|
||||
|
||||
[[thanks]]
|
||||
name = "Mathieu Pasquet"
|
||||
alias = "mathieui"
|
||||
country = "France"
|
||||
project = "slixmpp"
|
||||
jabber = "xmpp:mathieui@mathieui.net?message"
|
||||
url = "https://blog.mathieui.net"
|
||||
|
||||
[[thanks]]
|
||||
name = "Maxime Buquet"
|
||||
alias = "pep"
|
||||
country = "France"
|
||||
project = "slixmpp"
|
||||
jabber = "xmpp:pep@bouah.net?message"
|
||||
url = "https://bouah.net"
|
||||
|
||||
[[thanks]]
|
||||
name = "mirux"
|
||||
alias = "mirux"
|
||||
country = "Germany"
|
||||
|
||||
[[thanks]]
|
||||
name = "Phillip Watkins"
|
||||
alias = "pwatk"
|
||||
country = "England"
|
||||
project = "SalixOS"
|
||||
|
||||
[[thanks]]
|
||||
name = "Pierrick Le Brun"
|
||||
alias = "akuna"
|
||||
country = "France"
|
||||
project = "SalixOS"
|
||||
url = "https://mossieur-ballon.com"
|
||||
|
||||
[[thanks]]
|
||||
name = "Raphael Groner"
|
||||
alias = "rapgro"
|
||||
country = "Germany"
|
||||
project = "Fedora"
|
||||
|
||||
[[thanks]]
|
||||
name = "Remko Tronçon"
|
||||
country = "Belgium"
|
||||
projects = "Psi, SleekXMPP, Swift IM"
|
||||
url = "http://el-tramo.be https://mko.re https://psi-im.org"
|
||||
|
||||
[[thanks]]
|
||||
name = "Richard Lapointe"
|
||||
alias = "laprjns"
|
||||
country = "Connecticut"
|
||||
projects = "SalixOS, Zenwalk"
|
||||
|
||||
[[thanks]]
|
||||
name = "Simone Canaletti"
|
||||
alias = "roughnecks"
|
||||
country = "Italy"
|
||||
url = "https://woodpeckersnest.space"
|
||||
|
||||
[[thanks]]
|
||||
name = "Stephen Paul Weber"
|
||||
alias = "singpolyma"
|
||||
projects = "Cheogram, JMP, Sopranica"
|
||||
url = "https://singpolyma.net"
|
||||
|
||||
[[thanks]]
|
||||
name = "Strix from Loqi"
|
||||
alias = "Strix"
|
||||
|
||||
[[thanks]]
|
||||
name = "Thibaud Guerin"
|
||||
alias = "guth"
|
||||
project = "SalixOS"
|
||||
|
||||
[[thanks]]
|
||||
name = "Thorsten Fröhlich"
|
||||
country = "France"
|
||||
|
||||
[[thanks]]
|
||||
name = "Thorsten Mühlfelder"
|
||||
alias = "thenktor"
|
||||
country = "Germany"
|
||||
project = "SalixOS"
|
||||
|
||||
[[thanks]]
|
||||
name = "Tim Beech"
|
||||
alias = "mimosa"
|
||||
country = "Brazil"
|
||||
project = "SalixOS"
|
||||
url = "https://apipucos.wordpress.com"
|
||||
|
||||
[[thanks]]
|
||||
name = "Tomoki Tsuchiya"
|
||||
alias = "tsuren"
|
||||
project = "SalixOS"
|
||||
|
||||
[[thanks]]
|
||||
name = "Yann Leboulanger"
|
||||
alias = "asterix"
|
||||
country = "France"
|
||||
project = "Gajim"
|
||||
jabber = "xmpp:asterix@jabber.lagaule.org?message"
|
||||
url = "https://gajim.org"
|
||||
|
||||
[[thanks]]
|
||||
name = "#python (IRC Channel)"
|
||||
irc = "irc://irc.libera.chat/#python"
|
||||
|
||||
[[thanks]]
|
||||
name = "The Salix Team"
|
||||
about = ["""
|
||||
Previously part of the Zenwalk team.
|
||||
|
||||
The stubbornness of the Salix OS team members, and their determination to the \
|
||||
cause, no matter whether popular or else, you are the people who have lead \
|
||||
the creator of this software to the XMPP network.
|
||||
|
||||
It may well be said, that without you, gentlemen, and without your kind \
|
||||
honesty, sincerity and even the arguments however difficult these arguments \
|
||||
were, Slixfeed would have never been existed today.
|
||||
|
||||
All this from an XMPP groupchat that started out from 5 to 8 participants, \
|
||||
fifteen years ago (2009).
|
||||
|
||||
Thank you.
|
||||
"""]
|
||||
irc = "irc://irc.libera.chat/#salix"
|
||||
jabber = "xmpp:salix@chat.meticul.eu?join"
|
||||
url = "https://docs.salixos.org/wiki/Salix_OS:Team"
|
||||
|
||||
[[thanks]]
|
||||
name = "The XMPP Community"
|
||||
about = ["""
|
||||
For over a couple of decades, the people of XMPP form a strong community \
|
||||
which strives to provide you and your loved ones, private, secure and \
|
||||
stable communication experience.
|
||||
|
||||
While we are for private property and high standard of living, in the XMPP \
|
||||
realm we cooperate and we compete together to provide you with the best \
|
||||
communication platform in the world.
|
||||
|
||||
With governments and intelligence agencies around the world making an \
|
||||
extensive - and sometimes exclusive - use of XMPP, you can be rest assured \
|
||||
that you can never be wrong by making XMPP your prime and premier choice \
|
||||
for communications.
|
||||
|
||||
We are XMPP.
|
||||
Join us!
|
||||
"""]
|
||||
|
||||
[[operators]]
|
||||
title = "Operators"
|
||||
subtitle = "Slixfeed Operators"
|
||||
|
||||
[[operators]]
|
||||
name = "Mr. Operator"
|
||||
jid = "No operator was specified for this instance."
|
||||
|
||||
[[policies]]
|
||||
title = "Policies"
|
||||
subtitle = "Terms of service"
|
||||
|
||||
[[policies]]
|
||||
name = "Terms and Conditions"
|
||||
info = ["""
|
||||
[terms]
|
||||
info = """
|
||||
You are bound to these terms.
|
||||
"""]
|
||||
|
||||
[[policies]]
|
||||
name = "Privacy Policy"
|
||||
info = ["""
|
||||
All your data belongs to us.
|
||||
"""]
|
||||
|
||||
[[clients]]
|
||||
title = "Recommended Clients"
|
||||
subtitle = """
|
||||
As a chat bot, Slixfeed works with any XMPP messenger, yet we have deemed it \
|
||||
appropriate to list the software that work best with Slixfeed, namely those \
|
||||
that provide support for XEP-0050: Ad-Hoc Commands.
|
||||
"""
|
||||
|
||||
[[clients]]
|
||||
name = "Cheogram"
|
||||
info = "XMPP client for mobile"
|
||||
url = "https://cheogram.com"
|
||||
[privacy]
|
||||
info = """
|
||||
All your data belongs to us.
|
||||
"""
|
||||
|
||||
# [[clients]]
|
||||
# name = "Conversations"
|
||||
# info = "XMPP client for mobile"
|
||||
# url = "https://conversations.im"
|
||||
[clients]
|
||||
info = """
|
||||
Recommended Clients:
|
||||
|
||||
[[clients]]
|
||||
name = "Converse"
|
||||
info = "XMPP client for desktop and mobile"
|
||||
url = "https://conversejs.org"
|
||||
Cheogram
|
||||
https://cheogram.com
|
||||
|
||||
# [[clients]]
|
||||
# name = "Gajim"
|
||||
# info = "XMPP client for desktop"
|
||||
# url = "https://gajim.org"
|
||||
Converse
|
||||
https://conversejs.org
|
||||
|
||||
# [[clients]]
|
||||
# name = "Monal IM"
|
||||
# info = "XMPP client for desktop and mobile"
|
||||
# url = "https://monal-im.org"
|
||||
Gajim
|
||||
https://gajim.org
|
||||
|
||||
[[clients]]
|
||||
name = "monocles chat"
|
||||
info = "XMPP client for mobile"
|
||||
url = "https://monocles.chat"
|
||||
monocles chat
|
||||
https://monocles.chat
|
||||
|
||||
[[clients]]
|
||||
name = "Movim"
|
||||
info = "XMPP client for desktop and mobile"
|
||||
url = "https://mov.im"
|
||||
Movim
|
||||
https://mov.im
|
||||
|
||||
# [[clients]]
|
||||
# name = "Moxxy"
|
||||
# info = "XMPP client for mobile"
|
||||
# url = "https://moxxy.org"
|
||||
Poezio
|
||||
https://poez.io
|
||||
"""
|
||||
|
||||
[[clients]]
|
||||
name = "Psi"
|
||||
info = "XMPP client for desktop"
|
||||
url = "https://psi-im.org"
|
||||
[services]
|
||||
info = """
|
||||
Recommended Syndication Services
|
||||
|
||||
[[clients]]
|
||||
name = "Psi+"
|
||||
info = "XMPP client for desktop"
|
||||
url = "https://psi-plus.com"
|
||||
Feed Creator
|
||||
https://www.fivefilters.org/feed-creator/"
|
||||
|
||||
# [[clients]]
|
||||
# name = "Swift"
|
||||
# info = "XMPP client for desktop"
|
||||
# url = "https://swift.im"
|
||||
Kill the Newsletter
|
||||
https://kill-the-newsletter.com
|
||||
|
||||
# [[clients]]
|
||||
# name = "yaxim"
|
||||
# info = "XMPP client for mobile"
|
||||
# url = "https://yaxim.org"
|
||||
Open RSS
|
||||
https://openrss.org
|
||||
|
||||
[[services]]
|
||||
title = "Recommended News Services"
|
||||
subtitle = ["""
|
||||
Below are online services that extend the syndication experience by means \
|
||||
of bookmarking and multimedia, and also enhance it by restoring access to \
|
||||
news web feeds.
|
||||
"""]
|
||||
RSS-Bridge
|
||||
https://rss-bridge.org/bridge01/
|
||||
|
||||
[[services]]
|
||||
name = "Feed Creator"
|
||||
info = ["""
|
||||
Feed Creator is a service that creates feeds from HTML pages. \
|
||||
It generates RSS and JSON feeds from a set of links or other HTML elements.
|
||||
"""]
|
||||
link = "https://www.fivefilters.org/feed-creator/"
|
||||
RSSHub
|
||||
https://docs.rsshub.app
|
||||
"""
|
||||
|
||||
[[services]]
|
||||
name = "Kill the Newsletter"
|
||||
info = "Kill the Newsletter converts email newsletters into Web feeds."
|
||||
link = "https://kill-the-newsletter.com"
|
||||
[software]
|
||||
info = """
|
||||
Recommended News Software
|
||||
|
||||
[[services]]
|
||||
name = "Open RSS"
|
||||
info = ["""
|
||||
Open RSS is a nonprofit organization that provides free RSS feeds for \
|
||||
websites and applications that don't already provide them, so RSS feeds can \
|
||||
continue to be a reliable way for people to stay up-to-date with content \
|
||||
anywhere on the internet.
|
||||
"""]
|
||||
link = "https://openrss.org"
|
||||
CommaFeed
|
||||
https://commafeed.com
|
||||
|
||||
[[services]]
|
||||
name = "RSS-Bridge"
|
||||
info = ["""
|
||||
RSS-Bridge is free and open source software for generating Atom or RSS \
|
||||
feeds from websites which don’t have one. It is written in PHP and intended \
|
||||
to run on a Web server.
|
||||
"""]
|
||||
link = "https://rss-bridge.org/bridge01/"
|
||||
FreshRSS
|
||||
https://freshrss.org
|
||||
|
||||
[[services]]
|
||||
name = "RSSHub"
|
||||
info = ["""
|
||||
RSSHub is an open source, easy to use, and extensible RSS feed generator. \
|
||||
It's capable of generating RSS feeds from pretty much everything.
|
||||
"""]
|
||||
link = "https://docs.rsshub.app"
|
||||
Liferea
|
||||
https://lzone.de/liferea/
|
||||
|
||||
[[software]]
|
||||
title = "Recommended News Software"
|
||||
subtitle = ["""
|
||||
Take back control of your news. With free, quality, software for your \
|
||||
desktop, home and mobile devices.
|
||||
"""]
|
||||
NetNewsWire
|
||||
https://netnewswire.com
|
||||
|
||||
[[software]]
|
||||
name = "CommaFeed"
|
||||
info = ["""
|
||||
A self-hosted RSS reader, based on Dropwizard and React/TypeScript.
|
||||
"""]
|
||||
link = "https://commafeed.com"
|
||||
os = "Any (HTML)"
|
||||
Newsboat
|
||||
https://newsboat.org
|
||||
|
||||
[[software]]
|
||||
name = "FreshRSS"
|
||||
info = ["""
|
||||
FreshRSS is a self-hosted RSS and Atom feed aggregator.
|
||||
It is lightweight, easy to work with, powerful, and customizable.
|
||||
"""]
|
||||
link = "https://freshrss.org"
|
||||
os = "Any (HTML)"
|
||||
Spot-On
|
||||
https://textbrowser.github.io/spot-on/
|
||||
|
||||
[[software]]
|
||||
name = "Liferea"
|
||||
info = ["""
|
||||
Liferea is a feed reader/news aggregator that brings together all of the \
|
||||
content from your favorite subscriptions into a simple interface that makes \
|
||||
it easy to organize and browse feeds. Its GUI is similar to a desktop \
|
||||
mail/news client, with an embedded web browser.
|
||||
"""]
|
||||
link = "https://lzone.de/liferea/"
|
||||
os = "FreeBSD and Linux"
|
||||
Vienna RSS
|
||||
https://vienna-rss.com
|
||||
"""
|
||||
|
||||
[[software]]
|
||||
name = "NetNewsWire"
|
||||
info = ["""
|
||||
NetNewsWire shows you articles from your favorite blogs and news sites and \
|
||||
keeps track of what you’ve read.
|
||||
[resources]
|
||||
info = """
|
||||
Useful Resources:
|
||||
|
||||
This means you can stop going from page to page in your browser looking for \
|
||||
new articles to read. Do it the easy way instead: let NetNewsWire bring you \
|
||||
the news.
|
||||
feedparser
|
||||
https://pythonhosted.org/feedparser
|
||||
|
||||
And, if you’ve been getting your news via the commercial Social Networks — \
|
||||
with their ads, algorithms, user tracking, outrage, and misinformation — you \
|
||||
can switch to NetNewsWire to get news directly and more reliably from the \
|
||||
sites you trust.
|
||||
"""]
|
||||
link = "https://netnewswire.com"
|
||||
os = "MacOS"
|
||||
Slixmpp
|
||||
https://slixmpp.readthedocs.io
|
||||
|
||||
[[software]]
|
||||
name = "Newsboat"
|
||||
info = ["""
|
||||
Newsboat is an RSS/Atom feed reader for the text console. It’s an actively \
|
||||
maintained fork of Newsbeuter
|
||||
"""]
|
||||
link = "https://newsboat.org"
|
||||
os = "Any"
|
||||
XMPP
|
||||
https://xmpp.org/about
|
||||
"""
|
||||
|
||||
[[software]]
|
||||
name = "Spot-On"
|
||||
info = ["""
|
||||
Spot-On is a software carnival which brings chat, email, news, newsgroups, \
|
||||
search and other forms of communications into a single communications \
|
||||
orchestra.
|
||||
"""]
|
||||
link = "https://textbrowser.github.io/spot-on/"
|
||||
os = "Any"
|
||||
|
||||
[[software]]
|
||||
name = "Vienna RSS"
|
||||
info = ["""
|
||||
Vienna is an RSS/Atom reader for macOS, packed with powerful features that \
|
||||
help you make sense of the flood of information that is distributed via \
|
||||
these formats today.
|
||||
"""]
|
||||
link = "https://vienna-rss.com"
|
||||
os = "MacOS"
|
||||
|
||||
[[resources]]
|
||||
title = "Useful Resources"
|
||||
subtitle = "Technologies which Slixfeed is based upon"
|
||||
|
||||
[[resources]]
|
||||
name = "feedparser"
|
||||
info = "Syndication Library"
|
||||
url = "https://pythonhosted.org/feedparser"
|
||||
|
||||
[[resources]]
|
||||
name = "Slixmpp"
|
||||
info = "XMPP Library"
|
||||
url = "https://slixmpp.readthedocs.io"
|
||||
|
||||
[[resources]]
|
||||
name = "XMPP"
|
||||
info = "Messaging Protocol"
|
||||
url = "https://xmpp.org/about"
|
||||
|
||||
[[rss_task_force]]
|
||||
title = "About RSS Task Force"
|
||||
subtitle = "Swiss Organization"
|
||||
|
||||
[[rss_task_force]]
|
||||
info = ["""
|
||||
[rss-task-force]
|
||||
info = """
|
||||
The RSS Task Force (previously known as The Syndication Society) is an \
|
||||
international organization headquartered in Switzerland.
|
||||
|
||||
|
@ -706,44 +235,35 @@ Thanks to a joint effort of transport and travel companies, in 2021 we have \
|
|||
expanded our cause towards all entities of all types and sorts.
|
||||
|
||||
The RSS Task Force was founded by two taxicab drivers in 2018.
|
||||
"""]
|
||||
"""
|
||||
|
||||
[[sleekxmpp]]
|
||||
title = "About Project SleekXMPP"
|
||||
subtitle = "SleekXMPP XMPP Library"
|
||||
|
||||
[[sleekxmpp]]
|
||||
info = ["""
|
||||
[sleekxmpp]
|
||||
info = """
|
||||
SleekXMPP is an MIT licensed XMPP library for Python 2.6/3.1+, and is \
|
||||
featured in examples in the book XMPP: The Definitive Guide by Kevin Smith, \
|
||||
Remko Tronçon, and Peter Saint-Andre.
|
||||
"""]
|
||||
url = "https://codeberg.org/fritzy/SleekXMPP"
|
||||
|
||||
[[slixmpp]]
|
||||
title = "About Project Slixmpp"
|
||||
subtitle = "Slixmpp XMPP Library"
|
||||
https://codeberg.org/fritzy/SleekXMPP
|
||||
"""
|
||||
|
||||
[[slixmpp]]
|
||||
info = ["""
|
||||
[slixmpp]
|
||||
info = """
|
||||
Slixmpp is an MIT licensed XMPP library for Python 3.7+. It is a fork of \
|
||||
SleekXMPP.
|
||||
|
||||
Slixmpp's goals is to only rewrite the core of the SleekXMPP library \
|
||||
(the low level socket handling, the timers, the events dispatching) \
|
||||
in order to remove all threads.
|
||||
"""]
|
||||
url = "https://codeberg.org/poezio/slixmpp"
|
||||
|
||||
[[xmpp]]
|
||||
title = "About XMPP"
|
||||
subtitle = "Previously known as Jabber"
|
||||
https://codeberg.org/poezio/slixmpp
|
||||
"""
|
||||
|
||||
[[xmpp]]
|
||||
info = ["""
|
||||
[xmpp]
|
||||
info = """
|
||||
XMPP is the Extensible Messaging and Presence Protocol, a set of open \
|
||||
technologies for instant messaging, presence, multi-party chat, voice and \
|
||||
video calls, collaboration, lightweight middleware, content syndication, and \
|
||||
generalized routing of XML data.
|
||||
"""]
|
||||
link = "https://xmpp.org/about"
|
||||
|
||||
https://xmpp.org/about
|
||||
"""
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
# This file lists default settings per database.
|
||||
# See file /usr/share/slixfeed/settings.ini
|
||||
|
||||
#[Bot Settings]
|
||||
|
||||
# Check interval
|
||||
#interval = 90
|
||||
|
||||
[Settings]
|
||||
#[Contact Settings]
|
||||
|
||||
# Maximum items to archive (0 - 500)
|
||||
archive = 50
|
||||
|
||||
# Source check interval (recommended 90; minimum 10)
|
||||
check = 120
|
||||
|
||||
# Work status (Value 0 to disable)
|
||||
enabled = 1
|
||||
|
||||
# Enable filters (Value 1 to enable)
|
||||
filter = 0
|
||||
|
||||
# Update interval in minutes (Minimum value 10)
|
||||
interval = 300
|
||||
|
||||
# Maximum length of summary (Value 0 to disable)
|
||||
length = 300
|
||||
|
||||
# Display media (audio, image, video) when available
|
||||
media = 0
|
||||
|
||||
# Mark entries of newly added entries as unread
|
||||
old = 0
|
||||
|
||||
# Amount of entries per update
|
||||
quantum = 3
|
||||
|
||||
# Pick random item from database
|
||||
random = 0
|
||||
|
||||
# Set message formatting
|
||||
formatting = {title}\n> {summary}\n{link}\n{feed_title} [{ix}]\n\n
|
||||
|
||||
# Utilized in case of missing protocol support.
|
||||
[Bridge]
|
||||
gopher =
|
||||
|
||||
i2p =
|
||||
|
||||
nostr =
|
||||
|
||||
tor =
|
||||
|
||||
yggdrasil =
|
||||
|
||||
[Network]
|
||||
# Example http://localhost:8118 (privoxy)
|
||||
http_proxy =
|
||||
|
||||
# User Agent
|
||||
user_agent = Slixfeed/0.1
|
||||
|
||||
# Enable policed DNS system (not recommended)
|
||||
# clearnet = 1
|
||||
|
||||
# Enable I2P mixnet system (safer)
|
||||
i2p = 1
|
||||
|
||||
# Enable Loki mixnet system (safer)
|
||||
loki = 1
|
||||
|
||||
# Enable Tor semi-mixnet system (semi-safer)
|
||||
tor = 1
|
||||
|
||||
# Enable Yggdrasil mixnet system (safer)
|
||||
yggdrasil = 1
|
47
slixfeed/assets/settings.toml
Normal file
47
slixfeed/assets/settings.toml
Normal file
|
@ -0,0 +1,47 @@
|
|||
# This file lists default settings per database.
|
||||
# See file /usr/share/slixfeed/settings.toml
|
||||
|
||||
[default]
|
||||
archive = 50 # Maximum items to archive (0 - 500)
|
||||
check = 120 # Source check interval (recommended 90; minimum 10)
|
||||
enabled = 1 # Work status (Value 0 to disable)
|
||||
interval = 300 # Update interval (Minimum value 10)
|
||||
length = 300 # Maximum length of summary (Value 0 to disable)
|
||||
media = 0 # Display media (audio, image, video) when available
|
||||
old = 0 # Mark entries of newly added entries as unread
|
||||
quantum = 3 # Amount of entries per update
|
||||
random = 0 # Pick random item from database
|
||||
|
||||
# Message styling is not to be modified from bot
|
||||
# * title = Title of item
|
||||
# * summary = Summary of item
|
||||
# * link = Link of item
|
||||
# * feed_title = Title of news source
|
||||
# * ix = Index of item
|
||||
formatting = """
|
||||
{title}
|
||||
> {summary}
|
||||
{link}
|
||||
{feed_title} [{ix}]
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# Utilized in case of missing protocol support.
|
||||
[bridge]
|
||||
gopher = ""
|
||||
i2p = ""
|
||||
ipfs = ""
|
||||
nostr = ""
|
||||
tor = ""
|
||||
yggdrasil = ""
|
||||
|
||||
[network]
|
||||
http_proxy = "http://localhost:8118"
|
||||
user_agent = "Slixfeed/0.1"
|
||||
clearnet = 0 # Enable policed DNS system (not recommended)
|
||||
i2p = 1 # Enable I2P mixnet system (safer)
|
||||
ipfs = 1 # Enable IPFS DHT system (safer)
|
||||
loki = 1 # Enable Loki mixnet system (safer)
|
||||
tor = 1 # Enable Tor semi-mixnet system (semi-safer)
|
||||
yggdrasil = 1 # Enable Yggdrasil mixnet system (safer)
|
|
@ -50,26 +50,21 @@ except:
|
|||
class Config:
|
||||
|
||||
def add_settings_default(settings):
|
||||
settings['default'] = {}
|
||||
for key in ('archive', 'check', 'enabled', 'filter', 'formatting',
|
||||
'interval', 'length', 'media', 'old', 'quantum'):
|
||||
value = get_value('settings', 'Settings', key)
|
||||
settings['default'][key] = value
|
||||
settings_default = get_values('settings.toml', 'settings')
|
||||
settings['default'] = settings_default
|
||||
|
||||
# TODO Open SQLite file once
|
||||
def add_settings_jid(settings, jid_bare, db_file):
|
||||
settings[jid_bare] = {}
|
||||
for key in ('archive', 'enabled', 'filter', 'formatting', 'interval',
|
||||
'length', 'media', 'old', 'quantum'):
|
||||
value = sqlite.get_setting_value(db_file, key)
|
||||
if value: value = value[0]
|
||||
settings[jid_bare][key] = value
|
||||
|
||||
def add_settings_xmpp(settings):
|
||||
settings['xmpp'] = {}
|
||||
for key in ('operator', 'reconnect_timeout', 'type'):
|
||||
value = get_value('accounts', 'XMPP', key)
|
||||
settings['xmpp'][key] = value
|
||||
if value: settings[jid_bare][key] = value[0]
|
||||
|
||||
def get_settings_xmpp(key=None):
|
||||
result = get_values('accounts.toml', 'xmpp')
|
||||
result = result[key] if key else result
|
||||
return result
|
||||
|
||||
async def set_setting_value(settings, jid_bare, db_file, key, val):
|
||||
key = key.lower()
|
||||
|
@ -81,73 +76,12 @@ class Config:
|
|||
await sqlite.set_setting_value(db_file, key_val)
|
||||
|
||||
def get_setting_value(settings, jid_bare, key):
|
||||
if key in settings[jid_bare]:
|
||||
if jid_bare in settings and key in settings[jid_bare]:
|
||||
value = settings[jid_bare][key]
|
||||
else:
|
||||
value = settings['default'][key]
|
||||
return value
|
||||
|
||||
|
||||
# self.settings = {}
|
||||
# initiate an empty dict and the rest would be:
|
||||
# settings['account'] = {}
|
||||
# settings['default'] = {}
|
||||
# settings['jabber@id'] = {}
|
||||
# def __init__(self, db_file):
|
||||
# self.archive = get_setting_value(db_file, 'archive')
|
||||
# self.enabled = get_setting_value(db_file, 'enabled')
|
||||
# self.formatting = get_setting_value(db_file, 'formatting')
|
||||
# self.interval = get_setting_value(db_file, 'interval')
|
||||
# self.length = get_setting_value(db_file, 'length')
|
||||
# self.media = get_setting_value(db_file, 'media')
|
||||
# self.old = get_setting_value(db_file, 'old')
|
||||
# self.quantum = get_setting_value(db_file, 'quantum')
|
||||
|
||||
# def default():
|
||||
# archive = get_value('settings', 'Settings', 'archive')
|
||||
# enabled = get_value('settings', 'Settings', 'enabled')
|
||||
# formatting = get_value('settings', 'Settings', 'formatting')
|
||||
# interval = get_value('settings', 'Settings', 'interval')
|
||||
# length = get_value('settings', 'Settings', 'length')
|
||||
# media = get_value('settings', 'Settings', 'media')
|
||||
# old = get_value('settings', 'Settings', 'old')
|
||||
# quantum = get_value('settings', 'Settings', 'quantum')
|
||||
|
||||
# def jid(db_file):
|
||||
# archive = sqlite.get_setting_value(db_file, 'archive')
|
||||
# enabled = sqlite.get_setting_value(db_file, 'enabled')
|
||||
# formatting = sqlite.get_setting_value(db_file, 'formatting')
|
||||
# interval = sqlite.get_setting_value(db_file, 'interval')
|
||||
# length = sqlite.get_setting_value(db_file, 'length')
|
||||
# media = sqlite.get_setting_value(db_file, 'media')
|
||||
# old = sqlite.get_setting_value(db_file, 'old')
|
||||
# quantum = sqlite.get_setting_value(db_file, 'quantum')
|
||||
|
||||
|
||||
class ConfigXMPP:
|
||||
def __init__(self):
|
||||
self.setting = {}
|
||||
for key in ('operator', 'reconnect_timeout', 'type'):
|
||||
value = get_value('accounts', 'XMPP', key)
|
||||
self.setting[key] = value
|
||||
|
||||
|
||||
class ConfigClient:
|
||||
def __init__(self):
|
||||
self.setting = {}
|
||||
for key in ('alias', 'jid', 'operator', 'password', 'hostname', 'port'):
|
||||
value = get_value('accounts', 'XMPP Client', key)
|
||||
self.setting[key] = value
|
||||
|
||||
|
||||
class ConfigDefault:
|
||||
def __init__(self, settings):
|
||||
settings['default'] = {}
|
||||
for key in ('archive', 'check', 'enabled', 'filter', 'formatting',
|
||||
'interval', 'length', 'media', 'old', 'quantum'):
|
||||
value = get_value('settings', 'Settings', key)
|
||||
settings['default'][key] = value
|
||||
|
||||
class ConfigNetwork:
|
||||
def __init__(self, settings):
|
||||
settings['network'] = {}
|
||||
|
@ -167,6 +101,19 @@ class ConfigJabberID:
|
|||
settings[jid_bare][key] = value
|
||||
|
||||
|
||||
def get_values(filename, key=None):
|
||||
config_dir = get_default_config_directory()
|
||||
if not os.path.isdir(config_dir):
|
||||
config_dir = '/usr/share/slixfeed/'
|
||||
if not os.path.isdir(config_dir):
|
||||
config_dir = os.path.dirname(__file__) + "/assets"
|
||||
config_file = os.path.join(config_dir, filename)
|
||||
with open(config_file, mode="rb") as defaults:
|
||||
result = tomllib.load(defaults)
|
||||
values = result[key] if key else result
|
||||
return values
|
||||
|
||||
|
||||
def get_setting_value(db_file, key):
|
||||
value = sqlite.get_setting_value(db_file, key)
|
||||
if value:
|
||||
|
@ -299,9 +246,7 @@ def get_value(filename, section, keys):
|
|||
for key in keys:
|
||||
if key in section_res:
|
||||
value = section_res[key]
|
||||
logging.debug(
|
||||
"Found value {} for key {}".format(value, key)
|
||||
)
|
||||
logging.debug("Found value {} for key {}".format(value, key))
|
||||
else:
|
||||
value = ''
|
||||
logging.debug("Missing key:", key)
|
||||
|
@ -310,9 +255,7 @@ def get_value(filename, section, keys):
|
|||
key = keys
|
||||
if key in section_res:
|
||||
result = section_res[key]
|
||||
logging.debug(
|
||||
"Found value {} for key {}".format(result, key)
|
||||
)
|
||||
logging.debug("Found value {} for key {}".format(result, key))
|
||||
else:
|
||||
result = ''
|
||||
# logging.error("Missing key:", key)
|
||||
|
|
|
@ -119,10 +119,11 @@ async def http(url):
|
|||
msg: list or str
|
||||
Document or error message.
|
||||
"""
|
||||
user_agent = (config.get_value("settings", "Network", "user_agent")
|
||||
user_agent = (config.get_values('settings.toml', 'network')['user_agent']
|
||||
or 'Slixfeed/0.1')
|
||||
headers = {'User-Agent': user_agent}
|
||||
proxy = (config.get_value("settings", "Network", "http_proxy") or '')
|
||||
proxy = (config.get_values('settings.toml', 'network')['http_proxy']
|
||||
or '')
|
||||
timeout = ClientTimeout(total=10)
|
||||
async with ClientSession(headers=headers) as session:
|
||||
# async with ClientSession(trust_env=True) as session:
|
||||
|
|
|
@ -2243,7 +2243,7 @@ def get_feeds(db_file):
|
|||
cur = conn.cursor()
|
||||
sql = (
|
||||
"""
|
||||
SELECT name, url, id
|
||||
SELECT id, name, url
|
||||
FROM feeds
|
||||
"""
|
||||
)
|
||||
|
|
|
@ -172,7 +172,7 @@ async def task_send(self, jid_bare):
|
|||
db_file = config.get_pathname_to_database(jid_file)
|
||||
if jid_bare not in self.settings:
|
||||
Config.add_settings_jid(self.settings, jid_bare, db_file)
|
||||
update_interval = self.settings[jid_bare]['interval'] or self.settings['default']['interval']
|
||||
update_interval = Config.get_setting_value(self.settings, jid_bare, 'interval')
|
||||
update_interval = 60 * int(update_interval)
|
||||
last_update_time = sqlite.get_last_update_time(db_file)
|
||||
if last_update_time:
|
||||
|
@ -232,7 +232,7 @@ def refresh_task(self, jid_bare, callback, key, val=None):
|
|||
db_file = config.get_pathname_to_database(jid_file)
|
||||
if jid_bare not in self.settings:
|
||||
Config.add_settings_jid(self.settings, jid_bare, db_file)
|
||||
val = self.settings[jid_bare][key] or self.settings['default'][key]
|
||||
val = Config.get_setting_value(self.settings, jid_bare, key)
|
||||
# if self.task_manager[jid][key]:
|
||||
if jid_bare in self.task_manager:
|
||||
try:
|
||||
|
@ -268,7 +268,7 @@ async def wait_and_run(self, callback, jid_bare, val):
|
|||
|
||||
# TODO Take this function out of
|
||||
# <class 'slixmpp.clientxmpp.ClientXMPP'>
|
||||
async def check_updates(self, jid):
|
||||
async def check_updates(self, jid_bare):
|
||||
"""
|
||||
Start calling for update check up.
|
||||
|
||||
|
@ -277,15 +277,15 @@ async def check_updates(self, jid):
|
|||
jid : str
|
||||
Jabber ID.
|
||||
"""
|
||||
logging.info('Scanning for updates for JID {}'.format(jid))
|
||||
logging.info('Scanning for updates for JID {}'.format(jid_bare))
|
||||
while True:
|
||||
jid_file = jid.replace('/', '_')
|
||||
jid_file = jid_bare.replace('/', '_')
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
urls = sqlite.get_active_feeds_url(db_file)
|
||||
for url in urls:
|
||||
await action.scan(self, jid, db_file, url)
|
||||
await action.scan(self, jid_bare, db_file, url)
|
||||
await asyncio.sleep(50)
|
||||
val = self.settings['default']['check']
|
||||
val = Config.get_setting_value(self.settings, jid_bare, 'check')
|
||||
await asyncio.sleep(60 * float(val))
|
||||
# Schedule to call this function again in 90 minutes
|
||||
# loop.call_at(
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
__version__ = '0.1.36'
|
||||
__version_info__ = (0, 1, 36)
|
||||
__version__ = '0.1.37'
|
||||
__version_info__ = (0, 1, 37)
|
||||
|
|
|
@ -55,7 +55,7 @@ import slixfeed.xmpp.profile as profile
|
|||
from slixfeed.xmpp.roster import XmppRoster
|
||||
# import slixfeed.xmpp.service as service
|
||||
from slixfeed.xmpp.presence import XmppPresence
|
||||
from slixfeed.xmpp.utility import get_chat_type
|
||||
from slixfeed.xmpp.utility import get_chat_type, is_operator
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
@ -70,6 +70,7 @@ import logging
|
|||
import os
|
||||
import slixfeed.action as action
|
||||
import slixfeed.config as config
|
||||
from slixfeed.config import Config
|
||||
import slixfeed.crawl as crawl
|
||||
import slixfeed.dt as dt
|
||||
import slixfeed.fetch as fetch
|
||||
|
@ -123,14 +124,18 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
self.task_ping_instance = {}
|
||||
|
||||
# Handler for configuration
|
||||
self.settings = {}
|
||||
# Populate handler
|
||||
Config.add_settings_default(self.settings)
|
||||
Config.add_settings_xmpp(self.settings)
|
||||
self.settings = config.get_values('settings.toml')
|
||||
# Handler for operators
|
||||
self.operators = config.get_values('accounts.toml', 'xmpp')['operators']
|
||||
|
||||
# self.settings = {}
|
||||
# # Populate dict handler
|
||||
# Config.add_settings_default(self.settings)
|
||||
|
||||
# Handlers for connection events
|
||||
self.connection_attempts = 0
|
||||
self.max_connection_attempts = 10
|
||||
self.reconnect_timeout = config.get_values('accounts.toml', 'xmpp')['settings']['reconnect_timeout']
|
||||
|
||||
self.add_event_handler("session_start",
|
||||
self.on_session_start)
|
||||
|
@ -261,16 +266,14 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
function_name = sys._getframe().f_code.co_name
|
||||
message_log = '{}'
|
||||
logger.debug(message_log.format(function_name))
|
||||
jid_operator = config.get_value('accounts', 'XMPP', 'operator')
|
||||
if jid_operator:
|
||||
status_message = ('Wait while Slixfeed {} is being loaded...'
|
||||
.format(__version__))
|
||||
XmppPresence.send(self, jid_operator, status_message)
|
||||
status_message = 'Slixfeed version {}'.format(__version__)
|
||||
for operator in self.operators:
|
||||
XmppPresence.send(self, operator['jid'], status_message)
|
||||
await profile.update(self)
|
||||
profile.set_identity(self, 'client')
|
||||
await self['xep_0115'].update_caps()
|
||||
# self.send_presence()
|
||||
# await self.get_roster()
|
||||
await self.get_roster()
|
||||
bookmarks = await self.plugin['xep_0048'].get_bookmarks()
|
||||
XmppGroupchat.autojoin(self, bookmarks)
|
||||
# XmppCommand.adhoc_commands(self)
|
||||
|
@ -335,16 +338,16 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
XmppPresence.send(self, jid_bare, status_message)
|
||||
else:
|
||||
# TODO Request for subscription
|
||||
if (await get_chat_type(self, jid_bare) == 'chat' and
|
||||
not self.client_roster[jid_bare]['to']):
|
||||
XmppPresence.subscription(self, jid_bare, 'subscribe')
|
||||
await XmppRoster.add(self, jid_bare)
|
||||
status_message = '✒️ Share online status to receive updates'
|
||||
XmppPresence.send(self, jid_bare, status_message)
|
||||
message_subject = 'RSS News Bot'
|
||||
message_body = 'Share online status to receive updates.'
|
||||
XmppMessage.send_headline(self, jid_bare, message_subject,
|
||||
message_body, 'chat')
|
||||
# if (await get_chat_type(self, jid_bare) == 'chat' and
|
||||
# not self.client_roster[jid_bare]['to']):
|
||||
# XmppPresence.subscription(self, jid_bare, 'subscribe')
|
||||
# await XmppRoster.add(self, jid_bare)
|
||||
# status_message = '✒️ Share online status to receive updates'
|
||||
# XmppPresence.send(self, jid_bare, status_message)
|
||||
# message_subject = 'RSS News Bot'
|
||||
# message_body = 'Share online status to receive updates.'
|
||||
# XmppMessage.send_headline(self, jid_bare, message_subject,
|
||||
# message_body, 'chat')
|
||||
await process.message(self, message)
|
||||
# chat_type = message["type"]
|
||||
# message_body = message["body"]
|
||||
|
@ -671,13 +674,16 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
# )
|
||||
|
||||
# NOTE https://codeberg.org/poezio/slixmpp/issues/3515
|
||||
# if jid == self.settings['xmpp']['operator']:
|
||||
# if is_operator(self, jid_bare):
|
||||
self['xep_0050'].add_command(node='recent',
|
||||
name='📰️ Browse',
|
||||
handler=self._handle_recent)
|
||||
self['xep_0050'].add_command(node='subscription',
|
||||
name='🪶️ Subscribe',
|
||||
handler=self._handle_subscription_add)
|
||||
self['xep_0050'].add_command(node='publish',
|
||||
name='📣️ Publish',
|
||||
handler=self._handle_publish)
|
||||
self['xep_0050'].add_command(node='subscriptions',
|
||||
name='🎫️ Subscriptions',
|
||||
handler=self._handle_subscriptions)
|
||||
|
@ -712,6 +718,42 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
# Special interface
|
||||
# http://jabber.org/protocol/commands#actions
|
||||
|
||||
async def _handle_publish(self, iq, session):
|
||||
form = self['xep_0004'].make_form('form', 'Publish')
|
||||
form['instructions'] = ('In order to publish via Pubsub Social Feed '
|
||||
'(XEP-0472), you will have to choose a '
|
||||
'Publish-Subscribe (XEP-0060) hostname and '
|
||||
'be permitted to publish into it.')
|
||||
# TODO Select from list-multi
|
||||
form.add_field(var='subscription',
|
||||
ftype='text-single',
|
||||
label='URL',
|
||||
desc='Enter subscription URL.',
|
||||
value='http://',
|
||||
required=True)
|
||||
form.add_field(var='subscription',
|
||||
ftype='text-single',
|
||||
label='PubSub',
|
||||
desc='Enter a PubSub URL.',
|
||||
value='pubsub.' + self.boundjid.host,
|
||||
required=True)
|
||||
session['allow_prev'] = False
|
||||
session['has_next'] = True
|
||||
session['next'] = self._handle_preview
|
||||
session['prev'] = None
|
||||
session['payload'] = form
|
||||
return session
|
||||
|
||||
def _handle_preview(self, payload, session):
|
||||
jid_full = str(session['from'])
|
||||
function_name = sys._getframe().f_code.co_name
|
||||
logger.debug('{}: jid_full: {}'
|
||||
.format(function_name, jid_full))
|
||||
text_note = ('XEP-0472: Pubsub Social Feed will be available soon.')
|
||||
session['notes'] = [['info', text_note]]
|
||||
session['payload'] = None
|
||||
return session
|
||||
|
||||
async def _handle_profile(self, iq, session):
|
||||
jid_full = str(session['from'])
|
||||
function_name = sys._getframe().f_code.co_name
|
||||
|
@ -747,42 +789,42 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
value=unread)
|
||||
form.add_field(ftype='fixed',
|
||||
value='Options')
|
||||
key_archive = self.settings[jid_bare]['archive'] or self.settings['default']['archive']
|
||||
key_archive = Config.get_setting_value(self.settings, jid_bare, 'archive')
|
||||
key_archive = str(key_archive)
|
||||
form.add_field(label='Archive',
|
||||
ftype='text-single',
|
||||
value=key_archive)
|
||||
key_enabled = self.settings[jid_bare]['enabled'] or self.settings['default']['enabled']
|
||||
key_enabled = Config.get_setting_value(self.settings, jid_bare, 'enabled')
|
||||
key_enabled = str(key_enabled)
|
||||
form.add_field(label='Enabled',
|
||||
ftype='text-single',
|
||||
value=key_enabled)
|
||||
key_interval = self.settings[jid_bare]['interval'] or self.settings['default']['interval']
|
||||
key_interval = Config.get_setting_value(self.settings, jid_bare, 'interval')
|
||||
key_interval = str(key_interval)
|
||||
form.add_field(label='Interval',
|
||||
ftype='text-single',
|
||||
value=key_interval)
|
||||
key_length = self.settings[jid_bare]['length'] or self.settings['default']['length']
|
||||
key_length = Config.get_setting_value(self.settings, jid_bare, 'length')
|
||||
key_length = str(key_length)
|
||||
form.add_field(label='Length',
|
||||
ftype='text-single',
|
||||
value=key_length)
|
||||
key_media = self.settings[jid_bare]['media'] or self.settings['default']['media']
|
||||
key_media = Config.get_setting_value(self.settings, jid_bare, 'media')
|
||||
key_media = str(key_media)
|
||||
form.add_field(label='Media',
|
||||
ftype='text-single',
|
||||
value=key_media)
|
||||
key_old = self.settings[jid_bare]['old'] or self.settings['default']['old']
|
||||
key_old = Config.get_setting_value(self.settings, jid_bare, 'old')
|
||||
key_old = str(key_old)
|
||||
form.add_field(label='Old',
|
||||
ftype='text-single',
|
||||
value=key_old)
|
||||
key_quantum = self.settings[jid_bare]['quantum'] or self.settings['default']['quantum']
|
||||
key_quantum = Config.get_setting_value(self.settings, jid_bare, 'quantum')
|
||||
key_quantum = str(key_quantum)
|
||||
form.add_field(label='Quantum',
|
||||
ftype='text-single',
|
||||
value=key_quantum)
|
||||
update_interval = self.settings[jid_bare]['interval'] or self.settings['default']['interval']
|
||||
update_interval = Config.get_setting_value(self.settings, jid_bare, 'interval')
|
||||
update_interval = str(update_interval)
|
||||
update_interval = 60 * int(update_interval)
|
||||
last_update_time = sqlite.get_last_update_time(db_file)
|
||||
|
@ -797,7 +839,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
else:
|
||||
next_update = 'n/a'
|
||||
else:
|
||||
last_update_time = 'n/a'
|
||||
last_update = 'n/a'
|
||||
next_update = 'n/a'
|
||||
form.add_field(ftype='fixed',
|
||||
value='Schedule')
|
||||
|
@ -1394,8 +1436,10 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
.format(function_name, jid_full))
|
||||
jid_bare = session['from'].bare
|
||||
chat_type = await get_chat_type(self, jid_bare)
|
||||
moderator = None
|
||||
if chat_type == 'groupchat':
|
||||
moderator = is_moderator(self, jid_bare, jid_full)
|
||||
# moderator = moderator if moderator else None
|
||||
if chat_type == 'chat' or moderator:
|
||||
form = self['xep_0004'].make_form('form', 'Discover & Search')
|
||||
form['instructions'] = 'Discover news subscriptions of all kinds'
|
||||
|
@ -1557,10 +1601,10 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
desc=('Select a subscription to edit.'),
|
||||
required=True)
|
||||
subscriptions = sqlite.get_feeds(db_file)
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[0])
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[1])
|
||||
for subscription in subscriptions:
|
||||
title = subscription[0]
|
||||
url = subscription[1]
|
||||
title = subscription[1]
|
||||
url = subscription[2]
|
||||
options.addOption(title, url)
|
||||
session['has_next'] = True
|
||||
session['next'] = self._handle_subscription_editor
|
||||
|
@ -1576,10 +1620,10 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
desc=('Select subscriptions to remove.'),
|
||||
required=True)
|
||||
subscriptions = sqlite.get_feeds(db_file)
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[0])
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[1])
|
||||
for subscription in subscriptions:
|
||||
title = subscription[0]
|
||||
ix = str(subscription[2])
|
||||
title = subscription[1]
|
||||
ix = str(subscription[0])
|
||||
options.addOption(title, ix)
|
||||
session['cancel'] = self._handle_cancel
|
||||
session['has_next'] = False
|
||||
|
@ -1693,8 +1737,8 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
# subscriptions = set(subscriptions)
|
||||
categorized_subscriptions = {}
|
||||
for subscription in subscriptions:
|
||||
title = subscription[0]
|
||||
url = subscription[1]
|
||||
title = subscription[1]
|
||||
url = subscription[2]
|
||||
try:
|
||||
letter = title[0].capitalize()
|
||||
if letter not in categorized_subscriptions:
|
||||
|
@ -1906,10 +1950,10 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
jid_file = jid_bare
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
subscriptions = sqlite.get_feeds(db_file)
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[0])
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[1])
|
||||
for subscription in subscriptions:
|
||||
title = subscription[0]
|
||||
url = subscription[1]
|
||||
title = subscription[1]
|
||||
url = subscription[2]
|
||||
options.addOption(title, url)
|
||||
# options = form.add_field(var='action',
|
||||
# ftype='list-single',
|
||||
|
@ -1949,7 +1993,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
options.addOption('Import', 'import')
|
||||
options.addOption('Export', 'export')
|
||||
jid = session['from'].bare
|
||||
if jid == self.settings['xmpp']['operator']:
|
||||
if is_operator(self, jid):
|
||||
options.addOption('Administration', 'admin')
|
||||
session['payload'] = form
|
||||
session['next'] = self._handle_advanced_result
|
||||
|
@ -1976,7 +2020,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
# NOTE Even though this check is already conducted on previous
|
||||
# form, this check is being done just in case.
|
||||
jid_bare = session['from'].bare
|
||||
if jid_bare == self.settings['xmpp']['operator']:
|
||||
if is_operator(self, jid_bare):
|
||||
if self.is_component:
|
||||
# NOTE This will be changed with XEP-0222 XEP-0223
|
||||
text_info = ('Subscriber management options are '
|
||||
|
@ -2076,9 +2120,10 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
options = form.add_field(var='option',
|
||||
ftype='list-single',
|
||||
label='About',
|
||||
required=True)
|
||||
required=True,
|
||||
value='about')
|
||||
config_dir = config.get_default_config_directory()
|
||||
with open(config_dir + '/' + 'information.toml', mode="rb") as information:
|
||||
with open(config_dir + '/' + 'about.toml', mode="rb") as information:
|
||||
entries = tomllib.load(information)
|
||||
for entry in entries:
|
||||
label = entries[entry][0]['title']
|
||||
|
@ -2096,7 +2141,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
logger.debug('{}: jid_full: {}'
|
||||
.format(function_name, jid_full))
|
||||
config_dir = config.get_default_config_directory()
|
||||
with open(config_dir + '/' + 'information.toml', mode="rb") as information:
|
||||
with open(config_dir + '/' + 'about.toml', mode="rb") as information:
|
||||
entries = tomllib.load(information)
|
||||
entry_key = payload['values']['option']
|
||||
# case 'terms':
|
||||
|
@ -2174,7 +2219,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
cmds = tomllib.load(commands)
|
||||
|
||||
form = self['xep_0004'].make_form('result', 'Manual')
|
||||
form['instructions'] = '🛟️ Help manual for interactive chat'
|
||||
form['instructions'] = 'Help manual for interactive chat'
|
||||
|
||||
# text = '🛟️ Help and Information about Slixfeed\n\n'
|
||||
# for cmd in cmds:
|
||||
|
@ -2350,7 +2395,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
jid_file = jid_bare
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
subscriptions = sqlite.get_feeds(db_file)
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[0])
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[1])
|
||||
form = self['xep_0004'].make_form('form', 'Subscriptions')
|
||||
match payload['values']['action']:
|
||||
case 'bookmarks':
|
||||
|
@ -2680,7 +2725,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
Config.add_settings_jid(self.settings, jid_bare, db_file)
|
||||
form = self['xep_0004'].make_form('form', 'Settings')
|
||||
form['instructions'] = 'Editing settings'
|
||||
value = self.settings[jid_bare]['enabled'] or self.settings['default']['enabled']
|
||||
value = Config.get_setting_value(self.settings, jid_bare, 'enabled')
|
||||
value = str(value)
|
||||
value = int(value)
|
||||
if value:
|
||||
|
@ -2692,7 +2737,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
label='Enabled',
|
||||
desc='Enable news updates.',
|
||||
value=value)
|
||||
value = self.settings[jid_bare]['media'] or self.settings['default']['media']
|
||||
value = Config.get_setting_value(self.settings, jid_bare, 'media')
|
||||
value = str(value)
|
||||
value = int(value)
|
||||
if value:
|
||||
|
@ -2704,7 +2749,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
desc='Send audio, images or videos if found.',
|
||||
label='Display media',
|
||||
value=value)
|
||||
value = self.settings[jid_bare]['old'] or self.settings['default']['old']
|
||||
value = Config.get_setting_value(self.settings, jid_bare, 'old')
|
||||
value = str(value)
|
||||
value = int(value)
|
||||
if value:
|
||||
|
@ -2717,7 +2762,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
# label='Send only new items',
|
||||
label='Include old news',
|
||||
value=value)
|
||||
value = self.settings[jid_bare]['interval'] or self.settings['default']['interval']
|
||||
value = Config.get_setting_value(self.settings, jid_bare, 'interval')
|
||||
value = str(value)
|
||||
value = int(value)
|
||||
value = value/60
|
||||
|
@ -2738,7 +2783,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
i += 6
|
||||
else:
|
||||
i += 1
|
||||
value = self.settings[jid_bare]['quantum'] or self.settings['default']['quantum']
|
||||
value = Config.get_setting_value(self.settings, jid_bare, 'quantum')
|
||||
value = str(value)
|
||||
options = form.add_field(var='quantum',
|
||||
ftype='list-single',
|
||||
|
@ -2752,7 +2797,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
x = str(i)
|
||||
options.addOption(x, x)
|
||||
i += 1
|
||||
value = self.settings[jid_bare]['archive'] or self.settings['default']['archive']
|
||||
value = Config.get_setting_value(self.settings, jid_bare, 'archive')
|
||||
value = str(value)
|
||||
options = form.add_field(var='archive',
|
||||
ftype='list-single',
|
||||
|
@ -2807,7 +2852,7 @@ class Slixfeed(slixmpp.ClientXMPP):
|
|||
if val < 1: val = 1
|
||||
val = val * 60
|
||||
|
||||
is_enabled = self.settings[jid_bare]['enabled'] or self.settings['default']['enabled']
|
||||
is_enabled = Config.get_setting_value(self.settings, jid_bare, 'enabled')
|
||||
|
||||
if (key == 'enabled' and
|
||||
val == 1 and
|
||||
|
|
|
@ -36,7 +36,7 @@ from slixfeed.log import Logger
|
|||
from slixfeed.version import __version__
|
||||
from slixfeed.xmpp.connect import XmppConnect
|
||||
# NOTE MUC is possible for component
|
||||
# from slixfeed.xmpp.muc import XmppGroupchat
|
||||
from slixfeed.xmpp.muc import XmppGroupchat
|
||||
from slixfeed.xmpp.message import XmppMessage
|
||||
import slixfeed.xmpp.process as process
|
||||
import slixfeed.xmpp.profile as profile
|
||||
|
@ -46,7 +46,7 @@ from slixfeed.xmpp.presence import XmppPresence
|
|||
# from slixmpp.xmlstream import ET
|
||||
# from slixmpp.xmlstream.handler import Callback
|
||||
# from slixmpp.xmlstream.matcher import MatchXPath
|
||||
from slixfeed.xmpp.utility import get_chat_type
|
||||
from slixfeed.xmpp.utility import get_chat_type, is_operator
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
@ -115,14 +115,18 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
self.task_ping_instance = {}
|
||||
|
||||
# Handler for configuration
|
||||
self.settings = {}
|
||||
# Populate handler
|
||||
Config.add_settings_default(self.settings)
|
||||
Config.add_settings_xmpp(self.settings)
|
||||
self.settings = config.get_values('settings.toml')
|
||||
# Handler for operators
|
||||
self.operators = config.get_values('accounts.toml', 'xmpp')['operators']
|
||||
|
||||
# self.settings = {}
|
||||
# # Populate dict handler
|
||||
# Config.add_settings_default(self.settings)
|
||||
|
||||
# Handlers for connection events
|
||||
self.connection_attempts = 0
|
||||
self.max_connection_attempts = 10
|
||||
self.reconnect_timeout = config.get_values('accounts.toml', 'xmpp')['settings']['reconnect_timeout']
|
||||
|
||||
self.add_event_handler("session_start",
|
||||
self.on_session_start)
|
||||
|
@ -155,10 +159,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
self.add_event_handler("message",
|
||||
self.on_message)
|
||||
|
||||
# self.add_event_handler("groupchat_invite",
|
||||
# self.on_groupchat_invite) # XEP_0045
|
||||
# self.add_event_handler("groupchat_direct_invite",
|
||||
# self.on_groupchat_direct_invite) # XEP_0249
|
||||
self.add_event_handler("groupchat_invite",
|
||||
self.on_groupchat_invite) # XEP_0045
|
||||
self.add_event_handler("groupchat_direct_invite",
|
||||
self.on_groupchat_direct_invite) # XEP_0249
|
||||
# self.add_event_handler("groupchat_message", self.message)
|
||||
|
||||
# self.add_event_handler("disconnected", self.reconnect)
|
||||
|
@ -184,19 +188,19 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
self.on_session_end)
|
||||
|
||||
|
||||
# async def on_groupchat_invite(self, message):
|
||||
async def on_groupchat_invite(self, message):
|
||||
# logging.warning("on_groupchat_invite")
|
||||
# inviter = message['from'].bare
|
||||
# muc_jid = message['groupchat_invite']['jid']
|
||||
# await muc.join(self, inviter, muc_jid)
|
||||
inviter = message['from'].bare
|
||||
muc_jid = message['groupchat_invite']['jid']
|
||||
XmppGroupchat.join(self, inviter, muc_jid)
|
||||
# await bookmark.add(self, muc_jid)
|
||||
|
||||
|
||||
# NOTE Tested with Gajim and Psi
|
||||
# async def on_groupchat_direct_invite(self, message):
|
||||
# inviter = message['from'].bare
|
||||
# muc_jid = message['groupchat_invite']['jid']
|
||||
# await muc.join(self, inviter, muc_jid)
|
||||
async def on_groupchat_direct_invite(self, message):
|
||||
inviter = message['from'].bare
|
||||
muc_jid = message['groupchat_invite']['jid']
|
||||
XmppGroupchat.join(self, inviter, muc_jid)
|
||||
# await bookmark.add(self, muc_jid)
|
||||
|
||||
|
||||
|
@ -234,10 +238,9 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
task.task_ping(self)
|
||||
# bookmarks = await self.plugin['xep_0048'].get_bookmarks()
|
||||
# XmppGroupchat.autojoin(self, bookmarks)
|
||||
jid_operator = config.get_value('accounts', 'XMPP', 'operator')
|
||||
if jid_operator:
|
||||
status_message = 'Slixfeed version {}'.format(__version__)
|
||||
XmppPresence.send(self, jid_operator, status_message)
|
||||
for operator in self.operators:
|
||||
XmppPresence.send(self, operator['jid'], status_message)
|
||||
time_end = time.time()
|
||||
difference = time_end - time_begin
|
||||
if difference > 1: logger.warning('{} (time: {})'.format(function_name,
|
||||
|
@ -346,7 +349,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
if not self.client_roster[jid_bare]['to']:
|
||||
# XmppPresence.subscription(self, jid, 'subscribe')
|
||||
XmppPresence.subscription(self, jid_bare, 'subscribed')
|
||||
await XmppRoster.add(self, jid_bare)
|
||||
# await XmppRoster.add(self, jid_bare)
|
||||
status_message = '✒️ Share online status to receive updates'
|
||||
XmppPresence.send(self, jid_bare, status_message)
|
||||
message_subject = 'RSS News Bot'
|
||||
|
@ -418,9 +421,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
# status_message = None
|
||||
XmppMessage.send(self, jid_bare, message_body, 'chat')
|
||||
XmppPresence.subscription(self, jid_bare, 'unsubscribed')
|
||||
# XmppPresence.send(self, jid, status_message,
|
||||
# status_message = '🖋️ You have been uubscribed'
|
||||
# XmppPresence.send(self, jid_bare, status_message,
|
||||
# presence_type='unsubscribed')
|
||||
XmppRoster.remove(self, jid_bare)
|
||||
# XmppRoster.remove(self, jid_bare)
|
||||
time_end = time.time()
|
||||
difference = time_end - time_begin
|
||||
if difference > 1: logger.warning('{} (time: {})'.format(function_name,
|
||||
|
@ -631,13 +635,16 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
# )
|
||||
|
||||
# NOTE https://codeberg.org/poezio/slixmpp/issues/3515
|
||||
# if jid == self.settings['xmpp']['operator']:
|
||||
# if is_operator(self, jid_bare):
|
||||
self['xep_0050'].add_command(node='recent',
|
||||
name='📰️ Browse',
|
||||
handler=self._handle_recent)
|
||||
self['xep_0050'].add_command(node='subscription',
|
||||
name='🪶️ Subscribe',
|
||||
handler=self._handle_subscription_add)
|
||||
self['xep_0050'].add_command(node='publish',
|
||||
name='📣️ Publish',
|
||||
handler=self._handle_publish)
|
||||
self['xep_0050'].add_command(node='subscriptions',
|
||||
name='🎫️ Subscriptions',
|
||||
handler=self._handle_subscriptions)
|
||||
|
@ -672,6 +679,42 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
# Special interface
|
||||
# http://jabber.org/protocol/commands#actions
|
||||
|
||||
async def _handle_publish(self, iq, session):
|
||||
form = self['xep_0004'].make_form('form', 'Publish')
|
||||
form['instructions'] = ('In order to publish via Pubsub Social Feed '
|
||||
'(XEP-0472), you will have to choose a '
|
||||
'Publish-Subscribe (XEP-0060) hostname and '
|
||||
'be permitted to publish into it.')
|
||||
# TODO Select from list-multi
|
||||
form.add_field(var='subscription',
|
||||
ftype='text-single',
|
||||
label='URL',
|
||||
desc='Enter subscription URL.',
|
||||
value='http://',
|
||||
required=True)
|
||||
form.add_field(var='subscription',
|
||||
ftype='text-single',
|
||||
label='PubSub',
|
||||
desc='Enter a PubSub URL.',
|
||||
value='pubsub.' + self.boundjid.host,
|
||||
required=True)
|
||||
session['allow_prev'] = False
|
||||
session['has_next'] = True
|
||||
session['next'] = self._handle_preview
|
||||
session['prev'] = None
|
||||
session['payload'] = form
|
||||
return session
|
||||
|
||||
def _handle_preview(self, payload, session):
|
||||
jid_full = str(session['from'])
|
||||
function_name = sys._getframe().f_code.co_name
|
||||
logger.debug('{}: jid_full: {}'
|
||||
.format(function_name, jid_full))
|
||||
text_note = ('XEP-0472: Pubsub Social Feed will be available soon.')
|
||||
session['notes'] = [['info', text_note]]
|
||||
session['payload'] = None
|
||||
return session
|
||||
|
||||
async def _handle_profile(self, iq, session):
|
||||
jid_full = str(session['from'])
|
||||
function_name = sys._getframe().f_code.co_name
|
||||
|
@ -707,42 +750,42 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
value=unread)
|
||||
form.add_field(ftype='fixed',
|
||||
value='Options')
|
||||
key_archive = self.settings[jid_bare]['archive'] or self.settings['default']['archive']
|
||||
key_archive = Config.get_setting_value(self.settings, jid_bare, 'archive')
|
||||
key_archive = str(key_archive)
|
||||
form.add_field(label='Archive',
|
||||
ftype='text-single',
|
||||
value=key_archive)
|
||||
key_enabled = self.settings[jid_bare]['enabled'] or self.settings['default']['enabled']
|
||||
key_enabled = Config.get_setting_value(self.settings, jid_bare, 'enabled')
|
||||
key_enabled = str(key_enabled)
|
||||
form.add_field(label='Enabled',
|
||||
ftype='text-single',
|
||||
value=key_enabled)
|
||||
key_interval = self.settings[jid_bare]['interval'] or self.settings['default']['interval']
|
||||
key_interval = Config.get_setting_value(self.settings, jid_bare, 'interval')
|
||||
key_interval = str(key_interval)
|
||||
form.add_field(label='Interval',
|
||||
ftype='text-single',
|
||||
value=key_interval)
|
||||
key_length = self.settings[jid_bare]['length'] or self.settings['default']['length']
|
||||
key_length = Config.get_setting_value(self.settings, jid_bare, 'length')
|
||||
key_length = str(key_length)
|
||||
form.add_field(label='Length',
|
||||
ftype='text-single',
|
||||
value=key_length)
|
||||
key_media = self.settings[jid_bare]['media'] or self.settings['default']['media']
|
||||
key_media = Config.get_setting_value(self.settings, jid_bare, 'media')
|
||||
key_media = str(key_media)
|
||||
form.add_field(label='Media',
|
||||
ftype='text-single',
|
||||
value=key_media)
|
||||
key_old = self.settings[jid_bare]['old'] or self.settings['default']['old']
|
||||
key_old = Config.get_setting_value(self.settings, jid_bare, 'old')
|
||||
key_old = str(key_old)
|
||||
form.add_field(label='Old',
|
||||
ftype='text-single',
|
||||
value=key_old)
|
||||
key_quantum = self.settings[jid_bare]['quantum'] or self.settings['default']['quantum']
|
||||
key_quantum = Config.get_setting_value(self.settings, jid_bare, 'quantum')
|
||||
key_quantum = str(key_quantum)
|
||||
form.add_field(label='Quantum',
|
||||
ftype='text-single',
|
||||
value=key_quantum)
|
||||
update_interval = self.settings[jid_bare]['interval'] or self.settings['default']['interval']
|
||||
update_interval = Config.get_setting_value(self.settings, jid_bare, 'interval')
|
||||
update_interval = str(update_interval)
|
||||
update_interval = 60 * int(update_interval)
|
||||
last_update_time = sqlite.get_last_update_time(db_file)
|
||||
|
@ -757,7 +800,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
else:
|
||||
next_update = 'n/a'
|
||||
else:
|
||||
last_update_time = 'n/a'
|
||||
last_update = 'n/a'
|
||||
next_update = 'n/a'
|
||||
form.add_field(ftype='fixed',
|
||||
value='Schedule')
|
||||
|
@ -1354,8 +1397,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
.format(function_name, jid_full))
|
||||
jid_bare = session['from'].bare
|
||||
chat_type = await get_chat_type(self, jid_bare)
|
||||
moderator = None
|
||||
if chat_type == 'groupchat':
|
||||
moderator = is_moderator(self, jid_bare, jid_full)
|
||||
# moderator = moderator if moderator else None
|
||||
if chat_type == 'chat' or moderator:
|
||||
form = self['xep_0004'].make_form('form', 'Discover & Search')
|
||||
form['instructions'] = 'Discover news subscriptions of all kinds'
|
||||
|
@ -1517,10 +1562,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
desc=('Select a subscription to edit.'),
|
||||
required=True)
|
||||
subscriptions = sqlite.get_feeds(db_file)
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[0])
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[1])
|
||||
for subscription in subscriptions:
|
||||
title = subscription[0]
|
||||
url = subscription[1]
|
||||
title = subscription[1]
|
||||
url = subscription[2]
|
||||
options.addOption(title, url)
|
||||
session['has_next'] = True
|
||||
session['next'] = self._handle_subscription_editor
|
||||
|
@ -1536,10 +1581,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
desc=('Select subscriptions to remove.'),
|
||||
required=True)
|
||||
subscriptions = sqlite.get_feeds(db_file)
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[0])
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[1])
|
||||
for subscription in subscriptions:
|
||||
title = subscription[0]
|
||||
ix = str(subscription[2])
|
||||
title = subscription[1]
|
||||
ix = str(subscription[0])
|
||||
options.addOption(title, ix)
|
||||
session['cancel'] = self._handle_cancel
|
||||
session['has_next'] = False
|
||||
|
@ -1653,8 +1698,8 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
# subscriptions = set(subscriptions)
|
||||
categorized_subscriptions = {}
|
||||
for subscription in subscriptions:
|
||||
title = subscription[0]
|
||||
url = subscription[1]
|
||||
title = subscription[1]
|
||||
url = subscription[2]
|
||||
try:
|
||||
letter = title[0].capitalize()
|
||||
if letter not in categorized_subscriptions:
|
||||
|
@ -1866,10 +1911,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
jid_file = jid_bare
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
subscriptions = sqlite.get_feeds(db_file)
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[0])
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[1])
|
||||
for subscription in subscriptions:
|
||||
title = subscription[0]
|
||||
url = subscription[1]
|
||||
title = subscription[1]
|
||||
url = subscription[2]
|
||||
options.addOption(title, url)
|
||||
# options = form.add_field(var='action',
|
||||
# ftype='list-single',
|
||||
|
@ -1909,7 +1954,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
options.addOption('Import', 'import')
|
||||
options.addOption('Export', 'export')
|
||||
jid = session['from'].bare
|
||||
if jid == self.settings['xmpp']['operator']:
|
||||
if is_operator(self, jid):
|
||||
options.addOption('Administration', 'admin')
|
||||
session['payload'] = form
|
||||
session['next'] = self._handle_advanced_result
|
||||
|
@ -1936,7 +1981,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
# NOTE Even though this check is already conducted on previous
|
||||
# form, this check is being done just in case.
|
||||
jid_bare = session['from'].bare
|
||||
if jid_bare == self.settings['xmpp']['operator']:
|
||||
if is_operator(self, jid_bare):
|
||||
if self.is_component:
|
||||
# NOTE This will be changed with XEP-0222 XEP-0223
|
||||
text_info = ('Subscriber management options are '
|
||||
|
@ -2036,9 +2081,10 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
options = form.add_field(var='option',
|
||||
ftype='list-single',
|
||||
label='About',
|
||||
required=True)
|
||||
required=True,
|
||||
value='about')
|
||||
config_dir = config.get_default_config_directory()
|
||||
with open(config_dir + '/' + 'information.toml', mode="rb") as information:
|
||||
with open(config_dir + '/' + 'about.toml', mode="rb") as information:
|
||||
entries = tomllib.load(information)
|
||||
for entry in entries:
|
||||
label = entries[entry][0]['title']
|
||||
|
@ -2056,7 +2102,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
logger.debug('{}: jid_full: {}'
|
||||
.format(function_name, jid_full))
|
||||
config_dir = config.get_default_config_directory()
|
||||
with open(config_dir + '/' + 'information.toml', mode="rb") as information:
|
||||
with open(config_dir + '/' + 'about.toml', mode="rb") as information:
|
||||
entries = tomllib.load(information)
|
||||
entry_key = payload['values']['option']
|
||||
# case 'terms':
|
||||
|
@ -2086,7 +2132,6 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
e_key = e_key.capitalize()
|
||||
# form.add_field(ftype='fixed',
|
||||
# value=e_val)
|
||||
print(type(e_val))
|
||||
if e_key == 'Name':
|
||||
form.add_field(ftype='fixed',
|
||||
value=e_val)
|
||||
|
@ -2135,7 +2180,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
cmds = tomllib.load(commands)
|
||||
|
||||
form = self['xep_0004'].make_form('result', 'Manual')
|
||||
form['instructions'] = '🛟️ Help manual for interactive chat'
|
||||
form['instructions'] = 'Help manual for interactive chat'
|
||||
|
||||
# text = '🛟️ Help and Information about Slixfeed\n\n'
|
||||
# for cmd in cmds:
|
||||
|
@ -2311,7 +2356,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
jid_file = jid_bare
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
subscriptions = sqlite.get_feeds(db_file)
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[0])
|
||||
subscriptions = sorted(subscriptions, key=lambda x: x[1])
|
||||
form = self['xep_0004'].make_form('form', 'Subscriptions')
|
||||
match payload['values']['action']:
|
||||
case 'bookmarks':
|
||||
|
@ -2641,7 +2686,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
Config.add_settings_jid(self.settings, jid_bare, db_file)
|
||||
form = self['xep_0004'].make_form('form', 'Settings')
|
||||
form['instructions'] = 'Editing settings'
|
||||
value = self.settings[jid_bare]['enabled'] or self.settings['default']['enabled']
|
||||
value = Config.get_setting_value(self.settings, jid_bare, 'enabled')
|
||||
value = str(value)
|
||||
value = int(value)
|
||||
if value:
|
||||
|
@ -2653,7 +2698,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
label='Enabled',
|
||||
desc='Enable news updates.',
|
||||
value=value)
|
||||
value = self.settings[jid_bare]['media'] or self.settings['default']['media']
|
||||
value = Config.get_setting_value(self.settings, jid_bare, 'media')
|
||||
value = str(value)
|
||||
value = int(value)
|
||||
if value:
|
||||
|
@ -2665,7 +2710,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
desc='Send audio, images or videos if found.',
|
||||
label='Display media',
|
||||
value=value)
|
||||
value = self.settings[jid_bare]['old'] or self.settings['default']['old']
|
||||
value = Config.get_setting_value(self.settings, jid_bare, 'old')
|
||||
value = str(value)
|
||||
value = int(value)
|
||||
if value:
|
||||
|
@ -2678,7 +2723,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
# label='Send only new items',
|
||||
label='Include old news',
|
||||
value=value)
|
||||
value = self.settings[jid_bare]['interval'] or self.settings['default']['interval']
|
||||
value = Config.get_setting_value(self.settings, jid_bare, 'interval')
|
||||
value = str(value)
|
||||
value = int(value)
|
||||
value = value/60
|
||||
|
@ -2699,7 +2744,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
i += 6
|
||||
else:
|
||||
i += 1
|
||||
value = self.settings[jid_bare]['quantum'] or self.settings['default']['quantum']
|
||||
value = Config.get_setting_value(self.settings, jid_bare, 'quantum')
|
||||
value = str(value)
|
||||
options = form.add_field(var='quantum',
|
||||
ftype='list-single',
|
||||
|
@ -2713,7 +2758,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
x = str(i)
|
||||
options.addOption(x, x)
|
||||
i += 1
|
||||
value = self.settings[jid_bare]['archive'] or self.settings['default']['archive']
|
||||
value = Config.get_setting_value(self.settings, jid_bare, 'archive')
|
||||
value = str(value)
|
||||
options = form.add_field(var='archive',
|
||||
ftype='list-single',
|
||||
|
@ -2768,7 +2813,7 @@ class SlixfeedComponent(slixmpp.ComponentXMPP):
|
|||
if val < 1: val = 1
|
||||
val = val * 60
|
||||
|
||||
is_enabled = self.settings[jid_bare]['enabled'] or self.settings['default']['enabled']
|
||||
is_enabled = Config.get_setting_value(self.settings, jid_bare, 'enabled')
|
||||
|
||||
if (key == 'enabled' and
|
||||
val == 1 and
|
||||
|
|
|
@ -3,6 +3,13 @@
|
|||
|
||||
"""
|
||||
|
||||
FIXME
|
||||
|
||||
Message from OpenFire server log.
|
||||
|
||||
2024.03.12 14:21:22.518 ERROR [nioEventLoopGroup-3-2]: org.jivesoftware.openfire.IQRouter - Unable to process a stanza that has no 'from' attribute, addressed to a remote entity. Stanza is being dropped: <iq id="8e4e60ae0d894b40a2fc465268d46d0b" type="get" to="rss.simon.goodbytes.im"><ping xmlns="urn:xmpp:ping"></ping></iq>
|
||||
|
||||
|
||||
TODO
|
||||
|
||||
1) Check interval, and if no connection is establish after 30 seconds
|
||||
|
@ -38,21 +45,23 @@ class XmppConnect:
|
|||
None.
|
||||
|
||||
"""
|
||||
jid_from = str(self.boundjid) if self.is_component else None
|
||||
if not jid:
|
||||
jid = self.boundjid.bare
|
||||
while True:
|
||||
rtt = None
|
||||
try:
|
||||
rtt = await self['xep_0199'].ping(jid, timeout=10)
|
||||
rtt = await self['xep_0199'].ping(jid,
|
||||
ifrom=jid_from,
|
||||
timeout=10)
|
||||
logging.info('Success! RTT: %s', rtt)
|
||||
except IqError as e:
|
||||
logging.info('Error pinging %s: %s',
|
||||
jid,
|
||||
logging.error('Error pinging %s: %s', jid,
|
||||
e.iq['error']['condition'])
|
||||
except IqTimeout:
|
||||
logging.info('No response from %s', jid)
|
||||
logging.warning('No response from %s', jid)
|
||||
if not rtt:
|
||||
logging.info('Disconnecting...')
|
||||
logging.warning('Disconnecting...')
|
||||
self.disconnect()
|
||||
break
|
||||
await asyncio.sleep(60 * 1)
|
||||
|
@ -68,7 +77,7 @@ class XmppConnect:
|
|||
# print(current_time(),"Maximum connection attempts exceeded.")
|
||||
# logging.error("Maximum connection attempts exceeded.")
|
||||
print(current_time(), 'Attempt number', self.connection_attempts)
|
||||
seconds = (get_value('accounts', 'XMPP', 'reconnect_timeout')) or 30
|
||||
seconds = self.reconnect_timeout or 30
|
||||
seconds = int(seconds)
|
||||
print(current_time(), 'Next attempt within', seconds, 'seconds')
|
||||
# NOTE asyncio.sleep doesn't interval as expected
|
||||
|
|
14
slixfeed/xmpp/iq.py
Normal file
14
slixfeed/xmpp/iq.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from slixmpp.exceptions import IqError
|
||||
|
||||
class XmppIQ:
|
||||
|
||||
async def send(self, iq):
|
||||
try:
|
||||
await iq.send(timeout=5)
|
||||
except IqError as e:
|
||||
if e.etype == 'cancel' and e.condition == 'conflict':
|
||||
return
|
||||
raise
|
|
@ -34,15 +34,17 @@ class XmppMessage:
|
|||
|
||||
|
||||
def send(self, jid, message_body, chat_type):
|
||||
jid_from = str(self.boundjid) if self.is_component else None
|
||||
self.send_message(mto=jid,
|
||||
mfrom=self.boundjid.bare,
|
||||
mfrom=jid_from,
|
||||
mbody=message_body,
|
||||
mtype=chat_type)
|
||||
|
||||
|
||||
def send_headline(self, jid, message_subject, message_body, chat_type):
|
||||
jid_from = str(self.boundjid) if self.is_component else None
|
||||
self.send_message(mto=jid,
|
||||
mfrom=self.boundjid.bare,
|
||||
mfrom=jid_from,
|
||||
# mtype='headline',
|
||||
msubject=message_subject,
|
||||
mbody=message_body,
|
||||
|
@ -58,13 +60,14 @@ class XmppMessage:
|
|||
# }
|
||||
# return saxutils.escape(raw_string, escape_map)
|
||||
def send_oob(self, jid, url, chat_type):
|
||||
jid_from = str(self.boundjid) if self.is_component else None
|
||||
url = saxutils.escape(url)
|
||||
# try:
|
||||
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,
|
||||
mfrom=jid_from,
|
||||
mbody=url,
|
||||
mhtml=html,
|
||||
mtype=chat_type)
|
||||
|
|
|
@ -42,8 +42,10 @@ class XmppGroupchat:
|
|||
conference["nick"] = self.alias
|
||||
logging.error('Alias (i.e. Nicknname) is missing for '
|
||||
'bookmark {}'.format(conference['name']))
|
||||
# jid_from = str(self.boundjid) if self.is_component else None
|
||||
self.plugin['xep_0045'].join_muc(conference["jid"],
|
||||
conference["nick"],
|
||||
# pfrom=jid_from,
|
||||
# If a room password is needed, use:
|
||||
# password=the_room_password,
|
||||
)
|
||||
|
@ -83,8 +85,10 @@ class XmppGroupchat:
|
|||
'JID : {}\n'
|
||||
'Inviter : {}\n'
|
||||
.format(jid, inviter))
|
||||
jid_from = str(self.boundjid) if self.is_component else None
|
||||
self.plugin['xep_0045'].join_muc(jid,
|
||||
self.alias,
|
||||
pfrom=jid_from
|
||||
# If a room password is needed, use:
|
||||
# password=the_room_password,
|
||||
)
|
||||
|
|
|
@ -23,6 +23,9 @@ TODO
|
|||
|
||||
"""
|
||||
|
||||
from slixfeed.xmpp.publish import XmppPubsub
|
||||
from slixfeed.xmpp.iq import XmppIQ
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
|
@ -40,9 +43,14 @@ from slixfeed.xmpp.muc import XmppGroupchat
|
|||
from slixfeed.xmpp.message import XmppMessage
|
||||
from slixfeed.xmpp.presence import XmppPresence
|
||||
from slixfeed.xmpp.upload import XmppUpload
|
||||
from slixfeed.xmpp.utility import get_chat_type, is_moderator
|
||||
from slixfeed.xmpp.utility import get_chat_type, is_moderator, is_operator
|
||||
import time
|
||||
|
||||
try:
|
||||
import tomllib
|
||||
except:
|
||||
import tomli as tomllib
|
||||
|
||||
|
||||
# for task in main_task:
|
||||
# task.cancel()
|
||||
|
@ -181,7 +189,7 @@ async def message(self, message):
|
|||
response = None
|
||||
match message_lowercase:
|
||||
# case 'breakpoint':
|
||||
# if jid == get_value('accounts', 'XMPP', 'operator'):
|
||||
# if is_operator(self, jid_bare):
|
||||
# breakpoint()
|
||||
# print('task_manager[jid]')
|
||||
# print(task_manager[jid])
|
||||
|
@ -244,21 +252,25 @@ async def message(self, message):
|
|||
'or command key & name')
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case 'info':
|
||||
command_list = action.manual('information.toml')
|
||||
config_dir = config.get_default_config_directory()
|
||||
with open(config_dir + '/' + 'information.toml', mode="rb") as information:
|
||||
entries = tomllib.load(information)
|
||||
response = ('Available command options:\n'
|
||||
'```\n{}\n```\n'
|
||||
'Usage: `info <option>`'
|
||||
.format(command_list))
|
||||
.format(', '.join(entries)))
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('info'):
|
||||
command = message_text[5:].lower()
|
||||
command_list = action.manual('information.toml', command)
|
||||
if command_list:
|
||||
entry = message_text[5:].lower()
|
||||
config_dir = config.get_default_config_directory()
|
||||
with open(config_dir + '/' + 'information.toml', mode="rb") as information:
|
||||
entries = tomllib.load(information)
|
||||
if entry in entries:
|
||||
# command_list = '\n'.join(command_list)
|
||||
response = (command_list)
|
||||
response = (entries[entry]['info'])
|
||||
else:
|
||||
response = ('KeyError for {}'
|
||||
.format(command))
|
||||
.format(entry))
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase in ['greetings', 'hallo', 'hello',
|
||||
'hey', 'hi', 'hola', 'holla',
|
||||
|
@ -293,7 +305,7 @@ async def message(self, message):
|
|||
# response = 'Activation code is not valid.'
|
||||
# else:
|
||||
# response = 'This command is valid for groupchat only.'
|
||||
case _ if message_lowercase.startswith('add'):
|
||||
case _ if message_lowercase.startswith('add '):
|
||||
# Add given feed without validity check.
|
||||
message_text = message_text[4:]
|
||||
url = message_text.split(' ')[0]
|
||||
|
@ -338,7 +350,7 @@ async def message(self, message):
|
|||
'Missing URL.')
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('allow +'):
|
||||
key = message_text[:5]
|
||||
key = message_text[:5].lower()
|
||||
val = message_text[7:]
|
||||
if val:
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
|
@ -359,7 +371,7 @@ async def message(self, message):
|
|||
'Missing keywords.')
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('allow -'):
|
||||
key = message_text[:5]
|
||||
key = message_text[:5].lower()
|
||||
val = message_text[7:]
|
||||
if val:
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
|
@ -379,8 +391,8 @@ async def message(self, message):
|
|||
'\n'
|
||||
'Missing keywords.')
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('archive'):
|
||||
key = message_text[:7]
|
||||
case _ if message_lowercase.startswith('archive '):
|
||||
key = message_text[:7].lower()
|
||||
val = message_text[8:]
|
||||
if val:
|
||||
try:
|
||||
|
@ -406,7 +418,7 @@ async def message(self, message):
|
|||
'Missing value.')
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('bookmark +'):
|
||||
if jid_bare == config.get_value('accounts', 'XMPP', 'operator'):
|
||||
if is_operator(self, jid_bare):
|
||||
muc_jid = message_text[11:]
|
||||
await XmppBookmark.add(self, jid=muc_jid)
|
||||
response = ('Groupchat {} has been added to bookmarks.'
|
||||
|
@ -416,7 +428,7 @@ async def message(self, message):
|
|||
'Type: adding bookmarks.')
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('bookmark -'):
|
||||
if jid_bare == config.get_value('accounts', 'XMPP', 'operator'):
|
||||
if is_operator(self, jid_bare):
|
||||
muc_jid = message_text[11:]
|
||||
await XmppBookmark.remove(self, muc_jid)
|
||||
response = ('Groupchat {} has been removed from bookmarks.'
|
||||
|
@ -446,14 +458,14 @@ async def message(self, message):
|
|||
response = 'Filter {} has been purged.'.format(key)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case 'bookmarks':
|
||||
if jid_bare == config.get_value('accounts', 'XMPP', 'operator'):
|
||||
if is_operator(self, jid_bare):
|
||||
response = await action.list_bookmarks(self)
|
||||
else:
|
||||
response = ('This action is restricted. '
|
||||
'Type: viewing bookmarks.')
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('deny +'):
|
||||
key = message_text[:4]
|
||||
key = message_text[:4].lower()
|
||||
val = message_text[6:]
|
||||
if val:
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
|
@ -474,7 +486,7 @@ async def message(self, message):
|
|||
'Missing keywords.')
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('deny -'):
|
||||
key = message_text[:4]
|
||||
key = message_text[:4].lower()
|
||||
val = message_text[6:]
|
||||
if val:
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
|
@ -494,7 +506,7 @@ async def message(self, message):
|
|||
'\n'
|
||||
'Missing keywords.')
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('export'):
|
||||
case _ if message_lowercase.startswith('export '):
|
||||
ext = message_text[7:]
|
||||
if ext in ('md', 'opml'): # html xbel
|
||||
status_type = 'dnd'
|
||||
|
@ -687,7 +699,8 @@ async def message(self, message):
|
|||
if query:
|
||||
if len(query) > 3:
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
response = action.list_feeds_by_query(db_file, query)
|
||||
result = sqlite.search_feeds(db_file, query)
|
||||
response = action.list_feeds_by_query(query, result)
|
||||
else:
|
||||
response = 'Enter at least 4 characters to search'
|
||||
else:
|
||||
|
@ -702,8 +715,8 @@ async def message(self, message):
|
|||
else:
|
||||
response = 'This command is valid in groupchat only.'
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('interval'):
|
||||
key = message_text[:8]
|
||||
case _ if message_lowercase.startswith('interval '):
|
||||
key = message_text[:8].lower()
|
||||
val = message_text[9:]
|
||||
if val:
|
||||
try:
|
||||
|
@ -738,8 +751,8 @@ async def message(self, message):
|
|||
'XMPP URI is not valid.'
|
||||
.format(message_text))
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('length'):
|
||||
key = message_text[:6]
|
||||
case _ if message_lowercase.startswith('length '):
|
||||
key = message_text[:6].lower()
|
||||
val = message_text[7:]
|
||||
if val:
|
||||
try:
|
||||
|
@ -832,7 +845,7 @@ async def message(self, message):
|
|||
case 'options':
|
||||
response = 'Options:\n```'
|
||||
for key in self.settings[jid_bare]:
|
||||
val = self.settings[jid_bare][key] or self.settings['default'][key]
|
||||
val = Config.get_setting_value(self.settings, jid_bare, key)
|
||||
# val = Config.get_setting_value(self.settings, jid_bare, key)
|
||||
steps = 11 - len(key)
|
||||
pulse = ''
|
||||
|
@ -842,8 +855,8 @@ async def message(self, message):
|
|||
print(response)
|
||||
response += '\n```'
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('quantum'):
|
||||
key = message_text[:7]
|
||||
case _ if message_lowercase.startswith('quantum '):
|
||||
key = message_text[:7].lower()
|
||||
val = message_text[8:]
|
||||
if val:
|
||||
try:
|
||||
|
@ -872,7 +885,7 @@ async def message(self, message):
|
|||
# NOTE sqlitehandler.get_entry_unread
|
||||
response = 'Updates will be sent by random order.'
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('read'):
|
||||
case _ if message_lowercase.startswith('read '):
|
||||
data = message_text[5:]
|
||||
data = data.split()
|
||||
url = data[0]
|
||||
|
@ -914,7 +927,7 @@ async def message(self, message):
|
|||
XmppMessage.send_reply(self, message, response)
|
||||
key_list = ['status']
|
||||
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
||||
case _ if message_lowercase.startswith('recent'):
|
||||
case _ if message_lowercase.startswith('recent '):
|
||||
num = message_text[7:]
|
||||
if num:
|
||||
try:
|
||||
|
@ -934,12 +947,15 @@ async def message(self, message):
|
|||
'\n'
|
||||
'Missing value.')
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('remove'):
|
||||
case _ if message_lowercase.startswith('remove '):
|
||||
ix_url = message_text[7:]
|
||||
ix_url = ix_url.split(' ')
|
||||
if ix_url:
|
||||
for i in ix_url:
|
||||
if i:
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
try:
|
||||
ix = int(ix_url)
|
||||
ix = int(i)
|
||||
url = sqlite.get_feed_url(db_file, ix)
|
||||
if url:
|
||||
url = url[0]
|
||||
|
@ -954,7 +970,7 @@ async def message(self, message):
|
|||
response = ('No news source with index {}.'
|
||||
.format(ix))
|
||||
except:
|
||||
url = ix_url
|
||||
url = i
|
||||
feed_id = sqlite.get_feed_id(db_file, url)
|
||||
if feed_id:
|
||||
feed_id = feed_id[0]
|
||||
|
@ -972,12 +988,12 @@ async def message(self, message):
|
|||
# task.clean_tasks_xmpp(self, jid_bare, ['status'])
|
||||
key_list = ['status']
|
||||
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
else:
|
||||
response = ('No action has been taken.'
|
||||
'\n'
|
||||
'Missing argument. '
|
||||
'Enter feed URL or index number.')
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('reset'):
|
||||
# TODO Reset also by ID
|
||||
ix_url = message_text[6:]
|
||||
|
@ -1028,7 +1044,7 @@ async def message(self, message):
|
|||
XmppMessage.send_reply(self, message, response)
|
||||
key_list = ['status']
|
||||
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
||||
case _ if message_lowercase.startswith('search'):
|
||||
case _ if message_lowercase.startswith('search '):
|
||||
query = message_text[7:]
|
||||
if query:
|
||||
if len(query) > 1:
|
||||
|
@ -1080,7 +1096,7 @@ async def message(self, message):
|
|||
XmppMessage.send_reply(self, message, response)
|
||||
key_list = ['status']
|
||||
await task.start_tasks_xmpp(self, jid_bare, key_list)
|
||||
case _ if message_lowercase.startswith('rename'):
|
||||
case _ if message_lowercase.startswith('rename '):
|
||||
message_text = message_text[7:]
|
||||
feed_id = message_text.split(' ')[0]
|
||||
name = ' '.join(message_text.split(' ')[1:])
|
||||
|
@ -1117,7 +1133,7 @@ async def message(self, message):
|
|||
'Missing argument. '
|
||||
'Enter subscription Id and name.')
|
||||
XmppMessage.send_reply(self, message, response)
|
||||
case _ if message_lowercase.startswith('enable'):
|
||||
case _ if message_lowercase.startswith('enable '):
|
||||
feed_id = message_text[7:]
|
||||
db_file = config.get_pathname_to_database(jid_file)
|
||||
try:
|
||||
|
|
|
@ -26,7 +26,8 @@ TODO
|
|||
"""
|
||||
|
||||
import glob
|
||||
from slixfeed.config import get_value, get_default_config_directory
|
||||
from slixfeed.config import Config
|
||||
import slixfeed.config as config
|
||||
# from slixmpp.exceptions import IqTimeout, IqError
|
||||
# import logging
|
||||
import os
|
||||
|
@ -42,7 +43,7 @@ async def update(self):
|
|||
|
||||
|
||||
async def set_avatar(self):
|
||||
config_dir = get_default_config_directory()
|
||||
config_dir = config.get_default_config_directory()
|
||||
if not os.path.isdir(config_dir):
|
||||
config_dir = '/usr/share/slixfeed/'
|
||||
filename = glob.glob(config_dir + '/image.*')
|
||||
|
@ -89,18 +90,8 @@ def set_identity(self, category):
|
|||
|
||||
async def set_vcard(self):
|
||||
vcard = self.plugin['xep_0054'].make_vcard()
|
||||
fields = {
|
||||
'BDAY': 'birthday',
|
||||
'DESC': 'description',
|
||||
'FN': 'name',
|
||||
'NICKNAME': 'nickname',
|
||||
'NOTE': 'note',
|
||||
'ORG': 'organization',
|
||||
'ROLE': 'role',
|
||||
'TITLE': 'title',
|
||||
'URL': 'url',
|
||||
}
|
||||
for key in fields:
|
||||
vcard[key] = get_value('accounts', 'XMPP Profile', fields[key])
|
||||
profile = config.get_values('accounts.toml', 'xmpp')['profile']
|
||||
for key in profile:
|
||||
vcard[key] = profile[key]
|
||||
await self.plugin['xep_0054'].publish_vcard(vcard)
|
||||
|
||||
|
|
44
slixfeed/xmpp/publish.py
Normal file
44
slixfeed/xmpp/publish.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# TODO Implement XEP-0472: Pubsub Social Feed
|
||||
|
||||
class XmppPubsub:
|
||||
|
||||
def create(self, title, summary, node, server):
|
||||
jid_from = str(self.boundjid) if self.is_component else None
|
||||
iq = self.Iq(stype="set",
|
||||
sto=server,
|
||||
sfrom=jid_from)
|
||||
iq['pubsub']['create']['node'] = node
|
||||
form = iq['pubsub']['configure']['form']
|
||||
form['type'] = 'submit'
|
||||
form.addField('pubsub#title',
|
||||
ftype='text-single',
|
||||
value=title)
|
||||
form.addField('pubsub#description',
|
||||
ftype='text-single',
|
||||
value=summary)
|
||||
form.addField('pubsub#notify_retract',
|
||||
ftype='boolean',
|
||||
value=1)
|
||||
form.addField('pubsub#max_items',
|
||||
ftype='text-single',
|
||||
value='20')
|
||||
form.addField('pubsub#persist_items',
|
||||
ftype='boolean',
|
||||
value=1)
|
||||
form.addField('pubsub#send_last_published_item',
|
||||
ftype='text-single',
|
||||
value='never')
|
||||
form.addField('pubsub#deliver_payloads',
|
||||
ftype='boolean',
|
||||
value=0)
|
||||
|
||||
# TODO
|
||||
|
||||
form.addField('pubsub#type',
|
||||
ftype='text-single',
|
||||
value='http://www.w3.org/2005/Atom')
|
||||
|
||||
return iq
|
|
@ -7,13 +7,24 @@ import logging
|
|||
# class XmppChat
|
||||
# class XmppUtility:
|
||||
|
||||
|
||||
def is_operator(self, jid_bare):
|
||||
result = False
|
||||
for operator in self.operators:
|
||||
if jid_bare == operator['jid']:
|
||||
result = True
|
||||
# operator_name = operator['name']
|
||||
break
|
||||
return result
|
||||
|
||||
def is_moderator(self, jid_bare, jid_full):
|
||||
alias = jid_full[jid_full.index('/')+1:]
|
||||
role = self.plugin['xep_0045'].get_jid_property(jid_bare, alias, 'role')
|
||||
if role == 'moderator':
|
||||
return True
|
||||
result = True
|
||||
else:
|
||||
return False
|
||||
result = False
|
||||
return result
|
||||
|
||||
|
||||
# TODO Rename to get_jid_type
|
||||
|
|
Loading…
Reference in a new issue