From dba7eefb967dfacc864a12c712f3332b7a3e2e1c Mon Sep 17 00:00:00 2001 From: Schimon Jehudah Date: Wed, 14 Feb 2024 03:04:49 +0000 Subject: [PATCH] Add About, Bookmarks and Manual forms --- slixfeed/action.py | 5 +- slixfeed/assets/information.toml | 79 ++++++++------- slixfeed/sqlite.py | 2 +- slixfeed/xmpp/bookmark.py | 71 ++++++++------ slixfeed/xmpp/client.py | 160 +++++++++++++++++++++++++------ slixfeed/xmpp/process.py | 20 +++- 6 files changed, 234 insertions(+), 103 deletions(-) diff --git a/slixfeed/action.py b/slixfeed/action.py index 30b6384..a01f38f 100644 --- a/slixfeed/action.py +++ b/slixfeed/action.py @@ -617,9 +617,10 @@ async def list_bookmarks(self): conferences = await XmppBookmark.get(self) message = '\nList of groupchats:\n\n```\n' for conference in conferences: - message += ('{}\n' + message += ('Name: {}\n' + 'Room: {}\n' '\n' - .format(conference['jid'])) + .format(conference['name'], conference['jid'])) message += ('```\nTotal of {} groupchats.\n' .format(len(conferences))) return message diff --git a/slixfeed/assets/information.toml b/slixfeed/assets/information.toml index d543102..35bedca 100644 --- a/slixfeed/assets/information.toml +++ b/slixfeed/assets/information.toml @@ -46,11 +46,10 @@ DEALINGS IN THE SOFTWARE. note = """ You can run Slixfeed as a client, from your own computer, server, \ -and even on a Linux phone (i.e. Droidian, Kupfer, Mobian, NixOS, \ -postmarketOS). You can even use Termux. - -All you need is one of the above and an XMPP account to \ -connect Slixfeed to. +and even from a Linux phone (i.e. Droidian, Kupfer, Mobian, NixOS, \ +postmarketOS), and even from Termux. +All you need is one of the above and an XMPP account to connect \ +Slixfeed to. Good luck! """ operators = """ @@ -100,41 +99,41 @@ https://gitgud.io/sjehuda/slixfeed """ thanks = """ -Alixander Court (Utah), \ -Chriss Farrell (SalixOS, Oregon), \ -Christian Dersch (SalixOS), \ -Cyrille Pontvieux (SalixOS, France), \ -Denis Fomin (Gajim, Russia), \ -Dimitris Tzemos (SalixOS, Greece), \ -Emmanuel Gil Peyrot (Poezio, France), \ -Florent Le Coz (Poezio, France), \ -George Vlahavas (SalixOS, Greece), \ -Guus der Kinderen (Openfire, Netherlands), \ -habnabit_ from #python on irc.libera.chat, \ -Imar van Erven Dorens (SalixOS, Netherlands), \ -imattau (atomtopubsub), \ -Jaussoin Timothée (Movim, France), \ -Justin Karneges (Psi, California), \ -Kevin Smith (Swift IM, Wales), \ -Lars Windolf (Liferea, Germany), \ -Luis Henrique Mello (SalixOS, Brazil), \ -magicfelix, \ -Markus Muttilainen (SalixOS), \ -Martin (Debian, Germany), \ -Mathieu Pasquet (slixmpp, France), \ -Maxime Buquet (slixmpp, France), \ -mirux (Germany), \ -Phillip Watkins (SalixOS, United Kingdom), \ -Pierrick Le Brun (SalixOS, France), \ -Raphael Groner (Fedora, Germany), \ -Remko Tronçon (Psi , Belgium), \ -Simone "roughnecks" Canaletti (Italy), \ -Richard Lapointe (SalixOS, Connecticut), \ -Strix from Loqi, \ -Thibaud Guerin (SalixOS), \ -Thorsten Fröhlich (France), \ -Thorsten Mühlfelder (SalixOS, Germany), \ -Tim Beech (SalixOS, Brazil), \ +Alixander Court (Utah); \ +Chriss Farrell (SalixOS, Oregon); \ +Christian Dersch (SalixOS); \ +Cyrille Pontvieux (SalixOS, France); \ +Denis Fomin (Gajim, Russia); \ +Dimitris Tzemos (SalixOS, Greece); \ +Emmanuel Gil Peyrot (Poezio, France); \ +Florent Le Coz (Poezio, France); \ +George Vlahavas (SalixOS, Greece); \ +Guus der Kinderen (Openfire, Netherlands); \ +habnabit_ from #python on irc.libera.chat; \ +Imar van Erven Dorens (SalixOS, Netherlands); \ +imattau (atomtopubsub); \ +Jaussoin Timothée (Movim, France); \ +Justin Karneges (Psi, California); \ +Kevin Smith (Swift IM, Wales); \ +Lars Windolf (Liferea, Germany); \ +Luis Henrique Mello (SalixOS, Brazil); \ +magicfelix; \ +Markus Muttilainen (SalixOS); \ +Martin (Debian, Germany); \ +Mathieu Pasquet (slixmpp, France); \ +Maxime Buquet (slixmpp, France); \ +mirux (Germany); \ +Phillip Watkins (SalixOS, United Kingdom); \ +Pierrick Le Brun (SalixOS, France); \ +Raphael Groner (Fedora, Germany); \ +Remko Tronçon (Psi , Belgium); \ +Simone "roughnecks" Canaletti (Italy); \ +Richard Lapointe (SalixOS, Connecticut); \ +Strix from Loqi; \ +Thibaud Guerin (SalixOS); \ +Thorsten Fröhlich (France); \ +Thorsten Mühlfelder (SalixOS, Germany); \ +Tim Beech (SalixOS, Brazil); \ Yann Leboulanger (Gajim, France). """ diff --git a/slixfeed/sqlite.py b/slixfeed/sqlite.py index ee164c8..b4c9a0f 100644 --- a/slixfeed/sqlite.py +++ b/slixfeed/sqlite.py @@ -797,7 +797,7 @@ async def mark_entry_as_read(cur, ix): def get_number_of_unread_entries_by_feed(db_file, feed_id): """ - Count entries of goven feed. + Count entries of given feed. Parameters ---------- diff --git a/slixfeed/xmpp/bookmark.py b/slixfeed/xmpp/bookmark.py index 2dfd08f..921132a 100644 --- a/slixfeed/xmpp/bookmark.py +++ b/slixfeed/xmpp/bookmark.py @@ -17,9 +17,7 @@ class XmppBookmark: async def get(self): result = await self.plugin['xep_0048'].get_bookmarks() - bookmarks = result['private']['bookmarks'] - conferences = bookmarks['conferences'] # We might not want this here - # conferences = bookmarks + conferences = result['private']['bookmarks']['conferences'] return conferences @@ -38,21 +36,37 @@ class XmppBookmark: return properties - async def add(self, muc_jid): + async def add(self, jid=None, properties=None): result = await self.plugin['xep_0048'].get_bookmarks() - bookmarks = result['private']['bookmarks'] - conferences = bookmarks['conferences'] - mucs = [] + conferences = result['private']['bookmarks']['conferences'] + groupchats = [] for conference in conferences: - jid = conference['jid'] - mucs.extend([jid]) - if muc_jid not in mucs: + groupchats.extend([conference]) + if properties: + properties['jid'] = properties['room'] + '@' + properties['host'] + else: + properties = { + 'jid' : jid, + 'alias' : self.alias, + 'name' : jid.split('@')[0], + 'autojoin' : True, + 'password' : None, + } + if jid not in groupchats: bookmarks = Bookmarks() - mucs.extend([muc_jid]) - for muc in mucs: - bookmarks.add_conference(muc, - self.alias, - autojoin=True) + for groupchat in groupchats: + # if groupchat['jid'] == groupchat['name']: + # groupchat['name'] = groupchat['name'].split('@')[0] + bookmarks.add_conference(groupchat['jid'], + groupchat['alias'], + name=groupchat['name'], + autojoin=groupchat['autojoin'], + password=groupchat['password']) + bookmarks.add_conference(properties['jid'], + properties['alias'], + name=properties['name'], + autojoin=properties['autojoin'], + password=properties['password']) await self.plugin['xep_0048'].set_bookmarks(bookmarks) # bookmarks = Bookmarks() # await self.plugin['xep_0048'].set_bookmarks(bookmarks) @@ -63,19 +77,18 @@ class XmppBookmark: # await self['xep_0402'].publish(bm) - async def remove(self, muc_jid): + async def remove(self, jid): result = await self.plugin['xep_0048'].get_bookmarks() - bookmarks = result['private']['bookmarks'] - conferences = bookmarks['conferences'] - mucs = [] + conferences = result['private']['bookmarks']['conferences'] + groupchats = [] for conference in conferences: - jid = conference['jid'] - mucs.extend([jid]) - if muc_jid in mucs: - bookmarks = Bookmarks() - mucs.remove(muc_jid) - for muc in mucs: - bookmarks.add_conference(muc, - self.alias, - autojoin=True) - await self.plugin['xep_0048'].set_bookmarks(bookmarks) + if not conference['jid'] == jid: + groupchats.extend([conference]) + bookmarks = Bookmarks() + for groupchat in groupchats: + bookmarks.add_conference(groupchat['jid'], + groupchat['nick'], + name=groupchat['name'], + autojoin=groupchat['autojoin'], + password=groupchat['password']) + await self.plugin['xep_0048'].set_bookmarks(bookmarks) diff --git a/slixfeed/xmpp/client.py b/slixfeed/xmpp/client.py index c073593..e6e1e93 100644 --- a/slixfeed/xmpp/client.py +++ b/slixfeed/xmpp/client.py @@ -201,6 +201,7 @@ class Slixfeed(slixmpp.ClientXMPP): async def on_session_start(self, event): + # self.send_presence() profile.set_identity(self, 'client') self.service_commands() self.service_reactions() @@ -212,28 +213,28 @@ class Slixfeed(slixmpp.ClientXMPP): # Service.commands(self) # Service.reactions(self) + async def on_session_resumed(self, event): - self.send_presence() - self['xep_0115'].update_caps(preserve=True) - await XmppGroupchat.autojoin(self) + # self.send_presence() profile.set_identity(self, 'client') + # self.service_commands() + # self.service_reactions() + self['xep_0115'].update_caps() + await XmppGroupchat.autojoin(self) # Service.commands(self) # Service.reactions(self) - self.service_commands() - self.service_reactions() async def on_disco_info(self, DiscoInfo): jid = DiscoInfo['from'] - self.send_presence(pto=jid) - await self['xep_0115'].update_caps(jid=jid, - preserve=True) - self.service_commands() - self.service_reactions() + # self.service_commands() + # self.service_reactions() + # self.send_presence(pto=jid) + await self['xep_0115'].update_caps(jid=jid) # TODO Request for subscription @@ -356,19 +357,25 @@ class Slixfeed(slixmpp.ClientXMPP): if jid in self.boundjid.bare: return if message['type'] in ('chat', 'normal'): - # task.clean_tasks_xmpp(self, jid, ['status']) - await task.start_tasks_xmpp(self, jid, ['status']) # NOTE: Required for Cheogram + await self['xep_0115'].update_caps(jid=jid) + # self.send_presence(pto=jid) + # task.clean_tasks_xmpp(self, jid, ['status']) + await asyncio.sleep(5) + await task.start_tasks_xmpp(self, jid, ['status']) async def on_chatstate_composing(self, message): if message['type'] in ('chat', 'normal'): jid = message['from'].bare + # NOTE: Required for Cheogram + await self['xep_0115'].update_caps(jid=jid) + # self.send_presence(pto=jid) # task.clean_tasks_xmpp(self, jid, ['status']) + await asyncio.sleep(5) status_message = ('💡 Send "help" for manual, or "info" for ' 'information.') XmppPresence.send(self, jid, status_message) - # NOTE: Required for Cheogram async def on_chatstate_gone(self, message): @@ -465,12 +472,12 @@ class Slixfeed(slixmpp.ClientXMPP): self['xep_0050'].add_command(node='filters', name='🕸️ Manage filters', handler=self._handle_filters) - self['xep_0050'].add_command(node='roster', - name='🧾️ Manage roster', - handler=self._handle_roster) self['xep_0050'].add_command(node='bookmarks', - name='📔️ Organize 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', handler=self._handle_subscriptions) @@ -483,6 +490,12 @@ class Slixfeed(slixmpp.ClientXMPP): self['xep_0050'].add_command(node='subscriptions_index', name='📑️ Subscriptions - Indexed', handler=self._handle_subscription) + self['xep_0050'].add_command(node='credit', + name='💡️ Credit', + handler=self._handle_credit) + self['xep_0050'].add_command(node='help', + name='🛟️ Help', + handler=self._handle_help) # self['xep_0050'].add_command(node='search', # name='Search', # handler=self._handle_search) @@ -756,6 +769,62 @@ class Slixfeed(slixmpp.ClientXMPP): pass + 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' + 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🤝️' + session['notes'] = [['info', text]] + return session + + + async def _handle_help(self, iq, session): + filename = 'commands.toml' + import tomllib + config_dir = config.get_default_config_directory() + with open(config_dir + '/' + filename, mode="rb") as commands: + cmds = tomllib.load(commands) + + form = self['xep_0004'].make_form('result', 'Manual') + form['instructions'] = '🛟️ Help manual for interactive chat' + + # text = '🛟️ Help and Information about Slixfeed\n\n' + # for cmd in cmds: + # name = cmd.capitalize() + # elements = cmds[cmd] + # text += name + '\n' + # for key, value in elements.items(): + # text += " " + key.capitalize() + '\n' + # for line in value.split("\n"): + # text += " " + line + '\n' + # form['instructions'] = text + + for cmd in cmds: + name = cmd.capitalize() + form.add_field(var='title', + ftype='fixed', + value=name) + elements = cmds[cmd] + for key, value in elements.items(): + key = key.replace('_', ' ') + key = key.capitalize() + form.add_field(var='title', + ftype='text-multi', + label=key, + value=value) + + + session['payload'] = form + return session + + async def _handle_bookmarks(self, iq, session): jid = session['from'].bare if jid == config.get_value('accounts', 'XMPP', 'operator'): @@ -769,9 +838,9 @@ class Slixfeed(slixmpp.ClientXMPP): conferences = await XmppBookmark.get(self) for conference in conferences: options.addOption(conference['name'], conference['jid']) + session['payload'] = form session['next'] = self._handle_bookmarks_editor session['has_next'] = True - session['payload'] = form else: logging.warning('An unauthorized attempt to access bookmarks has ' 'been detected!\n' @@ -788,7 +857,7 @@ class Slixfeed(slixmpp.ClientXMPP): properties = await XmppBookmark.properties(self, jid) jid = session['from'].bare form = self['xep_0004'].make_form('form', 'Edit bookmark') - form['instructions'] = 'Edit bookmark {}'.format(properties['name']) + form['instructions'] = '📝️ Edit bookmark {}'.format(properties['name']) jid = properties['jid'].split('@') room = jid[0] host = jid[1] @@ -822,16 +891,49 @@ class Slixfeed(slixmpp.ClientXMPP): ftype='boolean', label='Auto-join', value=properties['autojoin']) - options = form.add_field(var='action', - ftype='list-single', - label='Action', - value='join') + # options = form.add_field(var='action', + # ftype='list-single', + # label='Action', + # value='join') # options.addOption('Add', 'add') - options.addOption('Join', 'join') - options.addOption('Remove', 'remove') + # options.addOption('Join', 'join') + # options.addOption('Remove', 'remove') session['payload'] = form - session['next'] = False - session['has_next'] = False + session['next'] = self._handle_bookmarks_complete + session['has_next'] = True + return session + + + async def _handle_bookmarks_complete(self, payload, session): + """ + Process a command result from the user. + + Arguments: + payload -- Either a single item, such as a form, or a list + of items or forms if more than one form was + provided to the user. The payload may be any + stanza, such as jabber:x:oob for out of band + data, or jabber:x:data for typical data forms. + session -- A dictionary of data relevant to the command + session. Additional, custom data may be saved + here to persist across handler callbacks. + """ + + form = self['xep_0004'].make_form('result', 'Bookmarks') + 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]) + form.add_field(var=key, + ftype='text-single', + label=key.capitalize(), + value=val) + session['payload'] = form # Comment when this is fixed in Gajim + session["has_next"] = False + session['next'] = None return session @@ -927,7 +1029,7 @@ class Slixfeed(slixmpp.ClientXMPP): options = form.add_field(var='quantum', ftype='list-single', label='Amount', - desc='Set amount of updates per update.', + desc='Set amount of items per update.', value=value) i = 1 while i <= 5: @@ -961,7 +1063,7 @@ class Slixfeed(slixmpp.ClientXMPP): jid = session['from'].bare form = self['xep_0004'].make_form('form', 'Settings for {}'.format(jid)) - form['instructions'] = ('🛡️ Settings have beem saved') + form['instructions'] = ('🛡️ Settings have been saved') jid_file = jid db_file = config.get_pathname_to_database(jid_file) diff --git a/slixfeed/xmpp/process.py b/slixfeed/xmpp/process.py index e69a300..4ddc8b1 100644 --- a/slixfeed/xmpp/process.py +++ b/slixfeed/xmpp/process.py @@ -16,6 +16,11 @@ TODO message.reply("Share online status to activate bot.").send() return +3) Set timeout for moderator interaction. + If moderator interaction has been made, and moderator approves the bot, then + the bot will add the given groupchat to bookmarks; otherwise, the bot will + send a message that it was not approved and therefore leaves the groupchat. + """ import logging @@ -379,12 +384,21 @@ async def message(self, message): else: response = 'Missing value.' XmppMessage.send_reply(self, message, response) + case _ if message_lowercase.startswith('bookmark +'): + if jid == config.get_value('accounts', 'XMPP', 'operator'): + muc_jid = message_text[11:] + await XmppBookmark.add(self, jid=muc_jid) + response = ('Groupchat {} has been added to bookmarks.' + .format(muc_jid)) + else: + response = ('This action is restricted. ' + 'Type: adding bookmarks.') + XmppMessage.send_reply(self, message, response) case _ if message_lowercase.startswith('bookmark -'): if jid == config.get_value('accounts', 'XMPP', 'operator'): muc_jid = message_text[11:] await XmppBookmark.remove(self, muc_jid) - response = ('Groupchat {} has been removed ' - 'from bookmarks.' + response = ('Groupchat {} has been removed from bookmarks.' .format(muc_jid)) else: response = ('This action is restricted. ' @@ -572,6 +586,7 @@ async def message(self, message): if muc_jid: # TODO probe JID and confirm it's a groupchat await XmppGroupchat.join(self, jid, muc_jid) + # await XmppBookmark.add(self, jid=muc_jid) response = ('Joined groupchat {}' .format(message_text)) else: @@ -909,6 +924,7 @@ async def message(self, message): if muc_jid: # TODO probe JID and confirm it's a groupchat await XmppGroupchat.join(self, jid, muc_jid) + # await XmppBookmark.add(self, jid=muc_jid) response = ('Joined groupchat {}' .format(message_text)) else: