From 020a5174b37c56e88934756338085f0929748fb7 Mon Sep 17 00:00:00 2001 From: "Schimon Jehudah, Adv." Date: Thu, 21 Nov 2024 21:00:13 +0200 Subject: [PATCH] Handle bookmarks locally; Atomize group chat join functionality. --- kaikout/configs/bookmarks.toml | 1 + kaikout/xmpp/bookmark.py | 99 ---------------------------------- kaikout/xmpp/client.py | 82 ++++++++++++---------------- kaikout/xmpp/commands.py | 37 ++++++------- kaikout/xmpp/groupchat.py | 88 ++++++++++++++---------------- kaikout/xmpp/muc.py | 25 ++++----- 6 files changed, 103 insertions(+), 229 deletions(-) create mode 100644 kaikout/configs/bookmarks.toml delete mode 100644 kaikout/xmpp/bookmark.py diff --git a/kaikout/configs/bookmarks.toml b/kaikout/configs/bookmarks.toml new file mode 100644 index 0000000..01f9215 --- /dev/null +++ b/kaikout/configs/bookmarks.toml @@ -0,0 +1 @@ +bookmarks = [] diff --git a/kaikout/xmpp/bookmark.py b/kaikout/xmpp/bookmark.py deleted file mode 100644 index 5116045..0000000 --- a/kaikout/xmpp/bookmark.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" - -TODO - -1) Save groupchat name instead of jid in field name. - -""" - -from slixmpp.plugins.xep_0048.stanza import Bookmarks - - -class XmppBookmark: - - - async def get_bookmarks(self): - result = await self.plugin['xep_0048'].get_bookmarks() - conferences = result['private']['bookmarks']['conferences'] - return conferences - - - async def get_bookmark_properties(self, jid): - result = await self.plugin['xep_0048'].get_bookmarks() - groupchats = result['private']['bookmarks']['conferences'] - for groupchat in groupchats: - if jid == groupchat['jid']: - properties = {'password': groupchat['password'], - 'jid': groupchat['jid'], - 'name': groupchat['name'], - 'nick': groupchat['nick'], - 'autojoin': groupchat['autojoin'], - 'lang': groupchat['lang']} - break - return properties - - - async def add(self, jid=None, properties=None): - result = await self.plugin['xep_0048'].get_bookmarks() - conferences = result['private']['bookmarks']['conferences'] - groupchats = [] - if properties: - properties['jid'] = properties['room'] + '@' + properties['host'] - if not properties['alias']: properties['alias'] = self.alias - else: - properties = { - 'jid' : jid, - 'alias' : self.alias, - 'name' : jid.split('@')[0], - 'autojoin' : True, - 'password' : None, - } - for conference in conferences: - if conference['jid'] != properties['jid']: - groupchats.extend([conference]) - # FIXME Ad-hoc bookmark form is stuck - # if jid not in groupchats: - if properties['jid'] not in groupchats: - bookmarks = Bookmarks() - for groupchat in groupchats: - # if groupchat['jid'] == groupchat['name']: - # groupchat['name'] = groupchat['name'].split('@')[0] - bookmarks.add_conference(groupchat['jid'], - groupchat['nick'], - 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) - self.plugin['xep_0048'].set_bookmarks(bookmarks) - # bookmarks = Bookmarks() - # await self.plugin['xep_0048'].set_bookmarks(bookmarks) - # print(await self.plugin['xep_0048'].get_bookmarks()) - - # bm = BookmarkStorage() - # bm.conferences.append(Conference(muc_jid, autojoin=True, nick=self.alias)) - # await self['xep_0402'].publish(bm) - - - async def remove(self, jid): - result = await self.plugin['xep_0048'].get_bookmarks() - conferences = result['private']['bookmarks']['conferences'] - groupchats = [] - for conference in conferences: - 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/kaikout/xmpp/client.py b/kaikout/xmpp/client.py index fc99003..be97470 100644 --- a/kaikout/xmpp/client.py +++ b/kaikout/xmpp/client.py @@ -7,7 +7,6 @@ from kaikout.about import Documentation from kaikout.database import DatabaseToml from kaikout.log import Logger from kaikout.utilities import Config, Log, BlockList, Toml -from kaikout.xmpp.bookmark import XmppBookmark from kaikout.xmpp.chat import XmppChat from kaikout.xmpp.commands import XmppCommands from kaikout.xmpp.groupchat import XmppGroupchat @@ -60,6 +59,10 @@ class XmppClient(slixmpp.ClientXMPP): self.action_count = 0 # A handler for alias. self.alias = alias + # A handler for bookmarks. + self.filename_bookmarks = os.path.join(self.directory_config, 'bookmarks.toml') + self.data_bookmarks = Toml.open_file(self.filename_bookmarks) + self.bookmarks = self.data_bookmarks['bookmarks'] # A handler for configuration. self.defaults = self.data_settings['defaults'] # Handlers for connectivity. @@ -135,30 +138,13 @@ class XmppClient(slixmpp.ClientXMPP): async def on_groupchat_invite(self, message): jid_full = str(message['from']) room = message['groupchat_invite']['jid'] - result = await XmppMuc.join(self, room) - if result == 'ban': - message_body = '{} is banned from {}'.format(self.alias, room) - jid_bare = message['from'].bare - # This might not be necessary because JID might not be of the inviter, but rather of the MUC - XmppMessage.send(self, jid_bare, message_body, 'chat') - logger.warning(message_body) - print("on_groupchat_invite") - print("BAN BAN BAN BAN BAN") - print("on_groupchat_invite") - print(jid_full) - print(jid_full) - print(jid_full) - print("on_groupchat_invite") - print("BAN BAN BAN BAN BAN") - print("on_groupchat_invite") - else: - await XmppBookmark.add(self, room) - 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' - 'You may always reach me via xmpp:{}?message' - .format(self.alias, self.boundjid.bare)) + result = await XmppGroupchat.join(self, room) + if result != 'ban': + #self.bookmarks.append({'jid' : room, 'lang' : '', 'pass' : ''}) + if room not in self.bookmarks: self.bookmarks.append(room) + Toml.save_file(self.filename_bookmarks, self.data_bookmarks) + message_body = ('/me moderation chat bot. Jabber ID: xmpp:' + f'{self.boundjid.bare}?message (groupchat_invite)') XmppMessage.send(self, room, message_body, 'groupchat') XmppStatus.send_status_message(self, room) self.add_event_handler("muc::%s::got_online" % room, self.on_muc_got_online) @@ -168,16 +154,13 @@ class XmppClient(slixmpp.ClientXMPP): async def on_groupchat_direct_invite(self, message): room = message['groupchat_invite']['jid'] - result = await XmppMuc.join(self, room) - if result == 'ban': - message_body = '{} is banned from {}'.format(self.alias, room) - jid_bare = message['from'].bare - XmppMessage.send(self, jid_bare, message_body, 'chat') - logger.warning(message_body) - else: - await XmppBookmark.add(self, room) - message_body = ('/me moderation chat bot. Jabber ID: xmpp:{}?message' - .format(self.boundjid.bare)) + result = await XmppGroupchat.join(self, room) + if result != 'ban': + #self.bookmarks.append({'jid' : room, 'lang' : '', 'pass' : ''}) + if room not in self.bookmarks: self.bookmarks.append(room) + Toml.save_file(self.filename_bookmarks, self.data_bookmarks) + message_body = ('/me moderation chat bot. Jabber ID: xmpp:' + f'{self.boundjid.bare}?message') XmppMessage.send(self, room, message_body, 'groupchat') XmppStatus.send_status_message(self, room) self.add_event_handler("muc::%s::got_online" % room, self.on_muc_got_online) @@ -204,7 +187,8 @@ class XmppClient(slixmpp.ClientXMPP): jid_full = XmppMuc.get_full_jid(self, room, alias) if jid_full and '/' in jid_full: jid_bare = jid_full.split('/')[0] - XmppCommands.update_last_activity(self, room, jid_bare, db_file, timestamp) + XmppCommands.update_last_activity( + self, room, jid_bare, db_file, timestamp) # DatabaseToml.load_jid_settings(self, room) # await XmppChat.process_message(self, message) if (XmppMuc.is_moderator(self, room, self.alias) and @@ -216,9 +200,11 @@ class XmppClient(slixmpp.ClientXMPP): fields = [alias, message_body, identifier, timestamp] Log.toml(self, room, fields, 'message') # Check for message - await XmppObservation.observe_message(self, db_file, alias, message_body, room) + await XmppObservation.observe_message(self, db_file, alias, + message_body, room) # Check for inactivity - await XmppObservation.observe_inactivity(self, db_file, room) + await XmppObservation.observe_inactivity(self, db_file, + room) async def on_muc_got_online(self, presence): @@ -261,9 +247,9 @@ class XmppClient(slixmpp.ClientXMPP): status_codes = presence['muc']['status_codes'] actor_alias = presence['muc']['item']['actor']['nick'] if 301 in status_codes: - presence_body = 'User has been banned by {}'.format(actor_alias) + presence_body = f'User has been banned by {actor_alias}' elif 307 in status_codes: - presence_body = 'User has been kicked by {}'.format(actor_alias) + presence_body = f'User has been kicked by {actor_alias}' else: presence_body = presence['status'] room = presence['muc']['room'] @@ -283,7 +269,8 @@ class XmppClient(slixmpp.ClientXMPP): await XmppObservation.observe_strikes(self, db_file, presence, room) if jid_bare and jid_bare not in self.settings[room]['jid_whitelist']: # Check for status message - await XmppObservation.observe_status_message(self, alias, db_file, jid_bare, presence_body, room) + await XmppObservation.observe_status_message( + self, alias, db_file, jid_bare, presence_body, room) # Check for inactivity await XmppObservation.observe_inactivity(self, db_file, room) @@ -292,14 +279,16 @@ class XmppClient(slixmpp.ClientXMPP): actor = presence['muc']['item']['actor']['nick'] alias = presence['muc']['nick'] room = presence['muc']['room'] - if actor and alias == self.alias: XmppStatus.send_status_message(self, room) + if actor and alias == self.alias: XmppStatus.send_status_message(self, + room) # TODO Check whether group chat is not anonymous if XmppMuc.is_moderator(self, room, self.alias): timestamp_iso = datetime.now().isoformat() for alias in XmppMuc.get_roster(self, room): jid_bare = XmppMuc.get_full_jid(self, room, alias).split('/')[0] fields = [jid_bare, alias, timestamp_iso] - if not Log.alias_jid_exist(room, fields): Log.csv_jid(room, fields) + if not Log.alias_jid_exist(room, fields): Log.csv_jid(room, + fields) def on_reactions(self, message): @@ -358,11 +347,10 @@ class XmppClient(slixmpp.ClientXMPP): del self.filename_blocklist # subscribe['from'] = xmppbl.org # subscribe['pubsub']['subscription']['node'] = 'muc_bans_sha256' - subscriptions = await XmppPubsub.get_node_subscriptions(self, jabber_id, node_id) + subscriptions = await XmppPubsub.get_node_subscriptions( + self, jabber_id, node_id) await self['xep_0115'].update_caps() - bookmarks = await XmppBookmark.get_bookmarks(self) - print(bookmarks) - rooms = await XmppGroupchat.autojoin(self, bookmarks) + rooms = await XmppGroupchat.autojoin(self) # See also get_joined_rooms of slixmpp.plugins.xep_0045 for room in rooms: XmppStatus.send_status_message(self, room) diff --git a/kaikout/xmpp/commands.py b/kaikout/xmpp/commands.py index a9f0583..6b862bd 100644 --- a/kaikout/xmpp/commands.py +++ b/kaikout/xmpp/commands.py @@ -6,9 +6,8 @@ import kaikout.config as config from kaikout.config import Config from kaikout.log import Logger from kaikout.database import DatabaseToml -from kaikout.utilities import Documentation, Url +from kaikout.utilities import Documentation, Toml, Url from kaikout.version import __version__ -from kaikout.xmpp.bookmark import XmppBookmark from kaikout.xmpp.muc import XmppMuc from kaikout.xmpp.status import XmppStatus from kaikout.xmpp.utilities import XmppUtilities @@ -81,18 +80,17 @@ class XmppCommands: async def bookmark_add(self, muc_jid): - await XmppBookmark.add(self, jid=muc_jid) - message = ('Groupchat {} has been added to bookmarks.' - .format(muc_jid)) - return message + if muc_jid not in self.bookmarks: self.bookmarks.append(muc_jid) + Toml.save_file(self.filename_bookmarks, self.data_bookmarks) + return f'Groupchat {muc_jid} has been added to bookmarks.' async def bookmark_del(self, muc_jid): - await XmppBookmark.remove(self, muc_jid) - message = ('Groupchat {} has been removed from bookmarks.' - .format(muc_jid)) - return message - + if muc_jid in self.bookmarks: self.bookmarks.remove(muc_jid) + Toml.save_file(self.filename_bookmarks, self.data_bookmarks) + return f'Groupchat {muc_jid} has been removed from bookmarks.' + + async def invite_jid_to_muc(self, jid_bare): muc_jid = 'slixfeed@chat.woodpeckersnest.space' if await XmppUtilities.get_chat_type(self, jid_bare) == 'chat': @@ -122,12 +120,13 @@ class XmppCommands: if muc_jid: # TODO probe JID and confirm it's a groupchat result = await XmppMuc.join(self, muc_jid) - # await XmppBookmark.add(self, jid=muc_jid) if result == 'ban': message = '{} is banned from {}'.format(self.alias, muc_jid) + if room in self.bookmarks: self.bookmarks.remove(room) else: - await XmppBookmark.add(self, muc_jid) + if room not in self.bookmarks: self.bookmarks.append(room) message = 'Joined groupchat {}'.format(muc_jid) + Toml.save_file(self.filename_bookmarks, self.data_bookmarks) else: message = '> {}\nGroupchat JID appears to be invalid.'.format(muc_jid) else: @@ -137,7 +136,8 @@ class XmppCommands: async def muc_leave(self, room): XmppMuc.leave(self, room) - await XmppBookmark.remove(self, room) + if room in self.bookmarks: self.bookmarks.remove(room) + Toml.save_file(self.filename_bookmarks, self.data_bookmarks) async def outcast(self, room, alias, reason): @@ -163,14 +163,11 @@ class XmppCommands: async def print_bookmarks(self): - conferences = await XmppBookmark.get_bookmarks(self) + conferences = self.bookmarks message = '\nList of groupchats:\n\n```\n' for conference in conferences: - message += ('Name: {}\n' - 'Room: {}\n' - '\n' - .format(conference['name'], conference['jid'])) - message += ('```\nTotal of {} groupchats.\n'.format(len(conferences))) + message += f'{conference}\n' + message += (f'```\nTotal of {len(conferences)} groupchats.\n') return message diff --git a/kaikout/xmpp/groupchat.py b/kaikout/xmpp/groupchat.py index af3ef03..e92cb6b 100644 --- a/kaikout/xmpp/groupchat.py +++ b/kaikout/xmpp/groupchat.py @@ -1,63 +1,53 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" - -TODO - -1) Send message to inviter that bot has joined to groupchat. - -2) If groupchat requires captcha, send the consequent message. - -3) If groupchat error is received, send that error message to inviter. - -FIXME - -1) Save name of groupchat instead of jid as name - -""" from kaikout.xmpp.bookmark import XmppBookmark +from kaikout.xmpp.message import XmppMessage from kaikout.xmpp.muc import XmppMuc from kaikout.xmpp.status import XmppStatus +from kaikout.utilities import Toml from kaikout.log import Logger, Message import random logger = Logger(__name__) - class XmppGroupchat: - async def autojoin(self, bookmarks): - mucs_join_success = [] - for bookmark in bookmarks: - if bookmark["jid"] and bookmark["autojoin"]: - if not bookmark["nick"]: - bookmark["nick"] = self.alias - logger.error('Alias (i.e. Nicknname) is missing for ' - 'bookmark {}'.format(bookmark['name'])) - alias = bookmark["nick"] - room = bookmark["jid"] - Message.printer('Joining to MUC {} ...'.format(room)) - - result = await XmppMuc.join(self, room, alias) - if result == 'ban': - await XmppBookmark.remove(self, room) - logger.warning('{} is banned from {}'.format(self.alias, room)) - logger.warning('Groupchat {} has been removed from bookmarks' - .format(room)) - elif result == 'conflict': + async def join(self, room): + result = await XmppMuc.join(self, room) + if result == 'ban': + message_body = '{} is banned from {}'.format(self.alias, room) + jid_bare = message['from'].bare + XmppMessage.send(self, jid_bare, message_body, 'chat') + logger.warning(message_body) + elif result == 'conflict': + while result == 'conflict': + number = str(random.randrange(1000, 5000)) + print(f'Conflict. Atempting to join to {room} as {self.alias} #{number}') + result = await XmppMuc.join(self, room, f'{self.alias} #{number}') + else: + #self.bookmarks.append({'jid' : room, 'lang' : '', 'pass' : ''}) + if room not in self.bookmarks: self.bookmarks.append(room) + Toml.save_file(self.filename_bookmarks, self.data_bookmarks) + return result + + async def autojoin(self): + mucs_joined = [] + for room in self.bookmarks: + alias = self.alias + print(f'Joining to MUC {room} ...') + #Message.printer(f'Joining to MUC {room} ...') + result = await XmppMuc.join(self, room) + if result == 'ban': + if room in self.bookmarks: self.bookmarks.remove(room) + Toml.save_file(self.filename_bookmarks, self.data_bookmarks) + logger.warning(f'{alias} is banned from {room}') + logger.warning(f'Groupchat {room} has been removed from bookmarks') + elif result == 'conflict': + while result == 'conflict': number = str(random.randrange(1000, 5000)) - await XmppMuc.join(self, room, alias + ' #' + number) - else: - mucs_join_success.append(room) - logger.info('Autojoin groupchat\n' - 'Name : {}\n' - 'JID : {}\n' - 'Alias : {}\n' - .format(bookmark["name"], - bookmark["jid"], - bookmark["nick"])) - elif not bookmark["jid"]: - logger.error('JID is missing for bookmark {}' - .format(bookmark['name'])) - return mucs_join_success + print(f'Conflict. Atempting to join to {room} as {self.alias} #{number}') + result = await XmppMuc.join(self, room, f'{alias} #{number}') + else: + mucs_joined.append(room) + return mucs_joined diff --git a/kaikout/xmpp/muc.py b/kaikout/xmpp/muc.py index 5512967..df4d4b2 100644 --- a/kaikout/xmpp/muc.py +++ b/kaikout/xmpp/muc.py @@ -93,21 +93,18 @@ class XmppMuc: def is_moderator(self, room, alias): """Check if given JID is a moderator""" role = self.plugin['xep_0045'].get_jid_property(room, alias, 'role') - if role == 'moderator': - result = True - else: - result = False + result = True if role == 'moderator' else False return result - async def join(self, jid, alias=None, password=None): - logger.info('Joining groupchat\nJID : {}\n'.format(jid)) - jid_from = str(self.boundjid) if self.is_component else None + async def join(self, jid_bare, alias=None, password=None): + logger.info('Joining groupchat\nJID : {}\n'.format(jid_bare)) + #jid_from = str(self.boundjid) if self.is_component else None if not alias: alias = self.alias try: - await self.plugin['xep_0045'].join_muc_wait(jid, + await self.plugin['xep_0045'].join_muc_wait(jid_bare, alias, - presence_options = {"pfrom" : jid_from}, + #presence_options = {"pfrom" : jid_from}, password=password, maxchars=0, maxstanzas=0, @@ -118,24 +115,24 @@ class XmppMuc: except IqError as e: logger.error('Error XmppIQ') logger.error(str(e)) - logger.error(jid) + logger.error(jid_bare) result = 'error' except IqTimeout as e: logger.error('Timeout XmppIQ') logger.error(str(e)) - logger.error(jid) + logger.error(jid_bare) result = 'timeout' except TimeoutError as e: logger.error('Timeout AsyncIO') logger.error(str(e)) - logger.error(jid) + logger.error(jid_bare) result = 'timeout' except PresenceError as e: logger.error('Error Presence') logger.error(str(e)) if (e.condition == 'forbidden' and e.presence['error']['code'] == '403'): - logger.warning('{} is banned from {}'.format(self.alias, jid)) + logger.warning('{} is banned from {}'.format(self.alias, jid_bare)) result = 'ban' elif e.condition == 'conflict': logger.warning(e.presence['error']['text']) @@ -145,7 +142,7 @@ class XmppMuc: except Exception as e: logger.error('Unknown error') logger.error(str(e)) - logger.error(jid) + logger.error(jid_bare) result = 'unknown' return result