Improve groupchat functions to handle with erroneous cases

This commit is contained in:
Schimon Jehudah 2024-02-14 17:09:54 +00:00
parent 5c2ee8d51c
commit c8cd5e1b09
8 changed files with 149 additions and 76 deletions

View file

@ -113,6 +113,7 @@ class JabberComponent:
xmpp.register_plugin('xep_0084') # User Avatar
xmpp.register_plugin('xep_0085') # Chat State Notifications
xmpp.register_plugin('xep_0115') # Entity Capabilities
xmpp.register_plugin('xep_0122') # Data Forms Validation
xmpp.register_plugin('xep_0153') # vCard-Based Avatars
xmpp.register_plugin('xep_0199', {'keepalive': True}) # XMPP Ping
xmpp.register_plugin('xep_0249') # Direct MUC Invitations
@ -127,7 +128,6 @@ class JabberClient:
def __init__(self, jid, password, hostname=None, port=None, alias=None):
xmpp = Slixfeed(jid, password, hostname, port, alias)
xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0122') # Data Forms Validation
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0045') # Multi-User Chat
xmpp.register_plugin('xep_0048') # Bookmarks
@ -140,6 +140,7 @@ class JabberClient:
xmpp.register_plugin('xep_0084') # User Avatar
xmpp.register_plugin('xep_0085') # Chat State Notifications
xmpp.register_plugin('xep_0115') # Entity Capabilities
xmpp.register_plugin('xep_0122') # Data Forms Validation
xmpp.register_plugin('xep_0153') # vCard-Based Avatars
xmpp.register_plugin('xep_0199', {'keepalive': True}) # XMPP Ping
xmpp.register_plugin('xep_0249') # Direct MUC Invitations

View file

@ -1,13 +1,15 @@
about = """
Slixfeed
A Syndication bot for the XMPP communication network.
Slixfeed is a news broker bot for syndicated news which aims to be \
an easy to use and fully-featured news aggregator bot.
Slixfeed is a news broker which aims to be an easy to use and fully-\
featured news aggregator bot. It provides a convenient access to \
Blogs, News websites and even Fediverse instances, along with \
filtering functionality.
Slixfeed is primarily designed for XMPP (aka Jabber). \
Visit https://xmpp.org/software/ for more information.
Slixfeed provides a convenient access to Blogs, News websites and \
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.
https://gitgud.io/sjehuda/slixfeed
"""
authors = """
@ -58,7 +60,8 @@ No operator was specified for this instance.
platforms = """
Supported platforms: XMPP
Platforms to be added in future: ActivityPub, Briar, Email, IRC, LXMF, Matrix, MQTT, Nostr, Session, Tox.
Platforms to be added in future: ActivityPub, Briar, Email, IRC, LXMF, \
Matrix, MQTT, Nostr, Session, Tox.
For ideal experience, we recommend using XMPP.
"""
@ -86,6 +89,24 @@ XMPP
https://xmpp.org/about/
"""
sleekxmpp = """
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.
https://codeberg.org/fritzy/SleekXMPP
"""
slixmpp = """
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.
https://codeberg.org/poezio/slixmpp
"""
terms = """
Slixfeed is free software; you can redistribute it and/or \
modify it under the terms of the MIT License.
@ -129,6 +150,7 @@ Raphael Groner (Fedora, Germany); \
Remko Tronçon <mko.re> (Psi , Belgium); \
Simone "roughnecks" Canaletti <woodpeckersnest.space> (Italy); \
Richard Lapointe (SalixOS, Connecticut); \
Stephen Paul Weber <singpolyma.net>; \
Strix from Loqi; \
Thibaud Guerin (SalixOS); \
Thorsten Fröhlich (France); \
@ -144,6 +166,5 @@ chat, voice and video calls, collaboration, lightweight \
middleware, content syndication, and generalized routing of XML \
data.
Visit https://xmpp.org/about/ for more information on the XMPP \
protocol.
https://xmpp.org/about/
"""

View file

@ -1,2 +1,2 @@
__version__ = '0.1.6'
__version_info__ = (0, 1, 6)
__version__ = '0.1.7'
__version_info__ = (0, 1, 7)

View file

@ -44,6 +44,8 @@ class XmppBookmark:
groupchats.extend([conference])
if properties:
properties['jid'] = properties['room'] + '@' + properties['host']
if not properties['alias']: properties['alias'] = self.alias
else:
properties = {
'jid' : jid,

View file

@ -167,7 +167,7 @@ class Slixfeed(slixmpp.ClientXMPP):
inviter = message['from'].bare
muc_jid = message['groupchat_invite']['jid']
await XmppBookmark.add(self, muc_jid)
await XmppGroupchat.join(self, inviter, muc_jid)
XmppGroupchat.join(self, inviter, muc_jid)
message_body = ('Greetings! I am {}, the news anchor.\n'
'My job is to bring you the latest '
'news from sources you provide me with.\n'
@ -181,7 +181,7 @@ class Slixfeed(slixmpp.ClientXMPP):
inviter = message['from'].bare
muc_jid = message['groupchat_invite']['jid']
await XmppBookmark.add(self, muc_jid)
await XmppGroupchat.join(self, inviter, muc_jid)
XmppGroupchat.join(self, inviter, muc_jid)
message_body = ('Greetings! I am {}, the news anchor.\n'
'My job is to bring you the latest '
'news from sources you provide me with.\n'
@ -207,22 +207,23 @@ class Slixfeed(slixmpp.ClientXMPP):
self.service_reactions()
await self['xep_0115'].update_caps()
await self.get_roster()
await XmppGroupchat.autojoin(self)
await profile.update(self)
task.task_ping(self)
bookmarks = await self.plugin['xep_0048'].get_bookmarks()
XmppGroupchat.autojoin(self, bookmarks)
# Service.commands(self)
# Service.reactions(self)
async def on_session_resumed(self, event):
def on_session_resumed(self, event):
# self.send_presence()
profile.set_identity(self, 'client')
# self.service_commands()
# self.service_reactions()
self['xep_0115'].update_caps()
await XmppGroupchat.autojoin(self)
XmppGroupchat.autojoin(self)
# Service.commands(self)
# Service.reactions(self)
@ -358,7 +359,7 @@ class Slixfeed(slixmpp.ClientXMPP):
return
if message['type'] in ('chat', 'normal'):
# NOTE: Required for Cheogram
await self['xep_0115'].update_caps(jid=jid)
# await self['xep_0115'].update_caps(jid=jid)
# self.send_presence(pto=jid)
# task.clean_tasks_xmpp(self, jid, ['status'])
await asyncio.sleep(5)
@ -369,7 +370,7 @@ class Slixfeed(slixmpp.ClientXMPP):
if message['type'] in ('chat', 'normal'):
jid = message['from'].bare
# NOTE: Required for Cheogram
await self['xep_0115'].update_caps(jid=jid)
# await self['xep_0115'].update_caps(jid=jid)
# self.send_presence(pto=jid)
# task.clean_tasks_xmpp(self, jid, ['status'])
await asyncio.sleep(5)
@ -466,36 +467,42 @@ class Slixfeed(slixmpp.ClientXMPP):
# )
# if jid == config.get_value('accounts', 'XMPP', 'operator'):
self['xep_0050'].add_command(node='settings',
name='📮️ Edit settings',
handler=self._handle_settings)
self['xep_0050'].add_command(node='filters',
name='🕸️ Manage filters',
handler=self._handle_filters)
self['xep_0050'].add_command(node='bookmarks',
name='📔️ Organize bookmarks - Restricted',
handler=self._handle_bookmarks)
self['xep_0050'].add_command(node='roster',
name='🧾️ Organize roster - Restricted',
handler=self._handle_roster)
self['xep_0050'].add_command(node='subscriptions',
name='📰️ Subscriptions - All',
name='📰️ Subscriptions',
handler=self._handle_subscriptions)
self['xep_0050'].add_command(node='subscriptions_cat',
name='🔖️ Subscriptions - Categories',
name='🔖️ Categories',
handler=self._handle_subscription)
self['xep_0050'].add_command(node='subscriptions_tag',
name='🏷️ Subscriptions - Tags',
name='🏷️ Tags',
handler=self._handle_subscription)
self['xep_0050'].add_command(node='subscriptions_index',
name='📑️ Subscriptions - Indexed',
name='📑️ Index (A - Z)',
handler=self._handle_subscription)
self['xep_0050'].add_command(node='credit',
name='💡️ Credit',
handler=self._handle_credit)
self['xep_0050'].add_command(node='settings',
name='📮️ Settings',
handler=self._handle_settings)
self['xep_0050'].add_command(node='filters',
name='🛡️ Filters',
handler=self._handle_filters)
self['xep_0050'].add_command(node='bookmarks',
name='📕 Bookmarks',
handler=self._handle_bookmarks)
self['xep_0050'].add_command(node='roster',
name='📓 Roster', # 📋
handler=self._handle_roster)
self['xep_0050'].add_command(node='help',
name='🛟️ Help',
name='📔️ Manual',
handler=self._handle_help)
self['xep_0050'].add_command(node='motd',
name='🗓️ MOTD',
handler=self._handle_motd)
self['xep_0050'].add_command(node='credit',
name='Credits', # 💡️
handler=self._handle_credit)
self['xep_0050'].add_command(node='about',
name='About', # 📜️
handler=self._handle_about)
# self['xep_0050'].add_command(node='search',
# name='Search',
# handler=self._handle_search)
@ -547,7 +554,7 @@ class Slixfeed(slixmpp.ClientXMPP):
jid = session['from'].bare
form = self['xep_0004'].make_form('form', 'Filters for {}'.format(jid))
form['instructions'] = ('🛡 Filters have been updated')
form['instructions'] = (' Filters have been updated')
jid_file = jid
db_file = config.get_pathname_to_database(jid_file)
# In this case (as is typical), the payload is a form
@ -769,18 +776,55 @@ class Slixfeed(slixmpp.ClientXMPP):
pass
async def _handle_about(self, iq, session):
import slixfeed.action as action
# form = self['xep_0004'].make_form('result', 'Thanks')
# form['instructions'] = action.manual('information.toml', 'thanks')
# session['payload'] = form
# text = '💡️ About Slixfeed, slixmpp and XMPP\n\n'
# text += '\n\n'
# form = self['xep_0004'].make_form('result', 'About')
text = 'Slixfeed\n\n'
text += ''.join(action.manual('information.toml', 'about'))
text += '\n\n'
text += 'Slixmpp\n\n'
text += ''.join(action.manual('information.toml', 'slixmpp'))
text += '\n\n'
text += 'SleekXMPP\n\n'
text += ''.join(action.manual('information.toml', 'sleekxmpp'))
text += '\n\n'
text += 'XMPP\n\n'
text += ''.join(action.manual('information.toml', 'xmpp'))
session['notes'] = [['info', text]]
# form.add_field(var='about',
# ftype='text-multi',
# label='About',
# value=text)
# session['payload'] = form
return session
async def _handle_motd(self, iq, session):
# TODO add functionality to attach image.
text = ('Here you can add groupchat rules,post schedule, tasks or '
'anything elaborated you might deem fit. Good luck!')
session['notes'] = [['info', text]]
return session
async def _handle_credit(self, iq, session):
import slixfeed.action as action
# form = self['xep_0004'].make_form('result', 'Thanks')
# form['instructions'] = action.manual('information.toml', 'thanks')
# session['payload'] = form
text = '💡️ We are Jabber\n\n'
text = '💡️ We are XMPP\n\n'
fren = action.manual('information.toml', 'thanks')
fren = "".join(fren)
fren = fren.split(';')
fren = "\n".join(fren)
text += fren
text += '\n\nYOU!\n\n🫵️\n\n- Join us -\n\n🤝️'
# text += '\n\nYOU!\n\n🫵\n\n- Join us -\n\n🤝'
text += '\n\nYOU!\n\n🫵️\n\n- Join us -'
session['notes'] = [['info', text]]
return session
@ -819,8 +863,6 @@ class Slixfeed(slixmpp.ClientXMPP):
ftype='text-multi',
label=key,
value=value)
session['payload'] = form
return session
@ -864,7 +906,8 @@ class Slixfeed(slixmpp.ClientXMPP):
form.addField(var='name',
ftype='text-single',
label='Name',
value=properties['name'])
value=properties['name'],
required=True)
form.addField(var='room',
ftype='text-single',
label='Room',
@ -878,7 +921,8 @@ class Slixfeed(slixmpp.ClientXMPP):
form.addField(var='alias',
ftype='text-single',
label='Alias',
value=properties['nick'])
value=properties['nick'],
required=True)
form.addField(var='password',
ftype='text-private',
label='Password',
@ -920,13 +964,14 @@ class Slixfeed(slixmpp.ClientXMPP):
"""
form = self['xep_0004'].make_form('result', 'Bookmarks')
form['instructions'] = ('🛡 Bookmark has been saved')
form['instructions'] = (' Bookmark has been saved')
# In this case (as is typical), the payload is a form
values = payload['values']
await XmppBookmark.add(self, properties=values)
for value in values:
key = str(value)
val = str(values[value])
if not val: val = 'None' # '(empty)'
form.add_field(var=key,
ftype='text-single',
label=key.capitalize(),
@ -1067,7 +1112,7 @@ class Slixfeed(slixmpp.ClientXMPP):
jid = session['from'].bare
form = self['xep_0004'].make_form('form',
'Settings for {}'.format(jid))
form['instructions'] = ('🛡 Settings have been saved')
form['instructions'] = (' Settings have been saved')
jid_file = jid
db_file = config.get_pathname_to_database(jid_file)

View file

@ -27,21 +27,22 @@ class XmppGroupchat:
# jid = message['groupchat_invite']['jid']
# else:
# jid = message
async def accept_invitation(self, message):
def accept_invitation(self, message):
# operator muc_chat
inviter = message["from"].bare
muc_jid = message['groupchat_invite']['jid']
await self.join(self, inviter, muc_jid)
jid = message['groupchat_invite']['jid']
self.join(self, inviter, jid)
async def autojoin(self):
result = await self.plugin['xep_0048'].get_bookmarks()
bookmarks = result["private"]["bookmarks"]
conferences = bookmarks["conferences"]
def autojoin(self, bookmarks):
conferences = bookmarks["private"]["bookmarks"]["conferences"]
for conference in conferences:
if conference["autojoin"]:
muc_jid = conference["jid"]
self.plugin['xep_0045'].join_muc(muc_jid,
if conference["jid"] and conference["autojoin"]:
if not conference["nick"]:
conference["nick"] = self.alias
logging.error('Alias (i.e. Nicknname) is missing for '
'bookmark {}'.format(conference['name']))
self.plugin['xep_0045'].join_muc(conference["jid"],
conference["nick"],
# If a room password is needed, use:
# password=the_room_password,
@ -51,11 +52,14 @@ class XmppGroupchat:
'JID : {}\n'
'Alias : {}\n'
.format(conference["name"],
muc_jid,
conference["jid"],
conference["nick"]))
elif not conference["jid"]:
logging.error('JID is missing for bookmark {}'
.format(conference['name']))
async def join(self, inviter, muc_jid):
def join(self, inviter, jid):
# token = await initdb(
# muc_jid,
# get_settings_value,
@ -78,27 +82,26 @@ class XmppGroupchat:
logging.info('Joining groupchat\n'
'JID : {}\n'
'Inviter : {}\n'
.format(muc_jid, inviter))
self.plugin['xep_0045'].join_muc(muc_jid,
.format(jid, inviter))
self.plugin['xep_0045'].join_muc(jid,
self.alias,
# If a room password is needed, use:
# password=the_room_password,
)
async def leave(self, muc_jid):
jid = self.boundjid.bare
def leave(self, jid):
message = ('This news bot will now leave this groupchat.\n'
'The JID of this news bot is xmpp:{}?message'
.format(jid))
.format(self.boundjid.bare))
status_message = ('This bot has left the group. '
'It can be reached directly via {}'
.format(jid))
self.send_message(mto=muc_jid,
mfrom=self.boundjid.bare,
.format(self.boundjid.bare))
self.send_message(mto=jid,
mfrom=self.boundjid,
mbody=message,
mtype='groupchat')
self.plugin['xep_0045'].leave_muc(muc_jid,
self.plugin['xep_0045'].leave_muc(jid,
self.alias,
status_message,
self.boundjid.bare)
self.boundjid)

View file

@ -17,8 +17,9 @@ class XmppPresence:
def send(self, jid, status_message, presence_type=None, status_type=None):
jid_from = str(self.boundjid) if self.is_component else None
self.send_presence(pto=jid,
pfrom=self.boundjid,
pfrom=jid_from,
pshow=status_type,
pstatus=status_message,
ptype=presence_type)

View file

@ -565,7 +565,7 @@ async def message(self, message):
XmppMessage.send_reply(self, message, response)
case 'goodbye':
if message['type'] == 'groupchat':
await XmppGroupchat.leave(self, jid)
XmppGroupchat.leave(self, jid)
await XmppBookmark.remove(self, jid)
else:
response = 'This command is valid in groupchat only.'
@ -585,7 +585,7 @@ async def message(self, message):
muc_jid = uri.check_xmpp_uri(message_text[5:])
if muc_jid:
# TODO probe JID and confirm it's a groupchat
await XmppGroupchat.join(self, jid, muc_jid)
XmppGroupchat.join(self, jid, muc_jid)
# await XmppBookmark.add(self, jid=muc_jid)
response = ('Joined groupchat {}'
.format(message_text))
@ -923,7 +923,7 @@ async def message(self, message):
muc_jid = uri.check_xmpp_uri(message_text)
if muc_jid:
# TODO probe JID and confirm it's a groupchat
await XmppGroupchat.join(self, jid, muc_jid)
XmppGroupchat.join(self, jid, muc_jid)
# await XmppBookmark.add(self, jid=muc_jid)
response = ('Joined groupchat {}'
.format(message_text))