Handle bookmarks locally;

Atomize group chat join functionality.
This commit is contained in:
Schimon Jehudah, Adv. 2024-11-21 21:00:13 +02:00
parent f167625765
commit 020a5174b3
6 changed files with 103 additions and 229 deletions

View file

@ -0,0 +1 @@
bookmarks = []

View file

@ -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)

View file

@ -7,7 +7,6 @@ from kaikout.about import Documentation
from kaikout.database import DatabaseToml from kaikout.database import DatabaseToml
from kaikout.log import Logger from kaikout.log import Logger
from kaikout.utilities import Config, Log, BlockList, Toml from kaikout.utilities import Config, Log, BlockList, Toml
from kaikout.xmpp.bookmark import XmppBookmark
from kaikout.xmpp.chat import XmppChat from kaikout.xmpp.chat import XmppChat
from kaikout.xmpp.commands import XmppCommands from kaikout.xmpp.commands import XmppCommands
from kaikout.xmpp.groupchat import XmppGroupchat from kaikout.xmpp.groupchat import XmppGroupchat
@ -60,6 +59,10 @@ class XmppClient(slixmpp.ClientXMPP):
self.action_count = 0 self.action_count = 0
# A handler for alias. # A handler for alias.
self.alias = 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. # A handler for configuration.
self.defaults = self.data_settings['defaults'] self.defaults = self.data_settings['defaults']
# Handlers for connectivity. # Handlers for connectivity.
@ -135,30 +138,13 @@ class XmppClient(slixmpp.ClientXMPP):
async def on_groupchat_invite(self, message): async def on_groupchat_invite(self, message):
jid_full = str(message['from']) jid_full = str(message['from'])
room = message['groupchat_invite']['jid'] room = message['groupchat_invite']['jid']
result = await XmppMuc.join(self, room) result = await XmppGroupchat.join(self, room)
if result == 'ban': if result != 'ban':
message_body = '{} is banned from {}'.format(self.alias, room) #self.bookmarks.append({'jid' : room, 'lang' : '', 'pass' : ''})
jid_bare = message['from'].bare if room not in self.bookmarks: self.bookmarks.append(room)
# This might not be necessary because JID might not be of the inviter, but rather of the MUC Toml.save_file(self.filename_bookmarks, self.data_bookmarks)
XmppMessage.send(self, jid_bare, message_body, 'chat') message_body = ('/me moderation chat bot. Jabber ID: xmpp:'
logger.warning(message_body) f'{self.boundjid.bare}?message (groupchat_invite)')
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))
XmppMessage.send(self, room, message_body, 'groupchat') XmppMessage.send(self, room, message_body, 'groupchat')
XmppStatus.send_status_message(self, room) XmppStatus.send_status_message(self, room)
self.add_event_handler("muc::%s::got_online" % room, self.on_muc_got_online) 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): async def on_groupchat_direct_invite(self, message):
room = message['groupchat_invite']['jid'] room = message['groupchat_invite']['jid']
result = await XmppMuc.join(self, room) result = await XmppGroupchat.join(self, room)
if result == 'ban': if result != 'ban':
message_body = '{} is banned from {}'.format(self.alias, room) #self.bookmarks.append({'jid' : room, 'lang' : '', 'pass' : ''})
jid_bare = message['from'].bare if room not in self.bookmarks: self.bookmarks.append(room)
XmppMessage.send(self, jid_bare, message_body, 'chat') Toml.save_file(self.filename_bookmarks, self.data_bookmarks)
logger.warning(message_body) message_body = ('/me moderation chat bot. Jabber ID: xmpp:'
else: f'{self.boundjid.bare}?message')
await XmppBookmark.add(self, room)
message_body = ('/me moderation chat bot. Jabber ID: xmpp:{}?message'
.format(self.boundjid.bare))
XmppMessage.send(self, room, message_body, 'groupchat') XmppMessage.send(self, room, message_body, 'groupchat')
XmppStatus.send_status_message(self, room) XmppStatus.send_status_message(self, room)
self.add_event_handler("muc::%s::got_online" % room, self.on_muc_got_online) 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) jid_full = XmppMuc.get_full_jid(self, room, alias)
if jid_full and '/' in jid_full: if jid_full and '/' in jid_full:
jid_bare = jid_full.split('/')[0] 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) # DatabaseToml.load_jid_settings(self, room)
# await XmppChat.process_message(self, message) # await XmppChat.process_message(self, message)
if (XmppMuc.is_moderator(self, room, self.alias) and if (XmppMuc.is_moderator(self, room, self.alias) and
@ -216,9 +200,11 @@ class XmppClient(slixmpp.ClientXMPP):
fields = [alias, message_body, identifier, timestamp] fields = [alias, message_body, identifier, timestamp]
Log.toml(self, room, fields, 'message') Log.toml(self, room, fields, 'message')
# Check for 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 # 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): async def on_muc_got_online(self, presence):
@ -261,9 +247,9 @@ class XmppClient(slixmpp.ClientXMPP):
status_codes = presence['muc']['status_codes'] status_codes = presence['muc']['status_codes']
actor_alias = presence['muc']['item']['actor']['nick'] actor_alias = presence['muc']['item']['actor']['nick']
if 301 in status_codes: 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: 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: else:
presence_body = presence['status'] presence_body = presence['status']
room = presence['muc']['room'] room = presence['muc']['room']
@ -283,7 +269,8 @@ class XmppClient(slixmpp.ClientXMPP):
await XmppObservation.observe_strikes(self, db_file, presence, room) await XmppObservation.observe_strikes(self, db_file, presence, room)
if jid_bare and jid_bare not in self.settings[room]['jid_whitelist']: if jid_bare and jid_bare not in self.settings[room]['jid_whitelist']:
# Check for status message # 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 # Check for inactivity
await XmppObservation.observe_inactivity(self, db_file, room) await XmppObservation.observe_inactivity(self, db_file, room)
@ -292,14 +279,16 @@ class XmppClient(slixmpp.ClientXMPP):
actor = presence['muc']['item']['actor']['nick'] actor = presence['muc']['item']['actor']['nick']
alias = presence['muc']['nick'] alias = presence['muc']['nick']
room = presence['muc']['room'] 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 # TODO Check whether group chat is not anonymous
if XmppMuc.is_moderator(self, room, self.alias): if XmppMuc.is_moderator(self, room, self.alias):
timestamp_iso = datetime.now().isoformat() timestamp_iso = datetime.now().isoformat()
for alias in XmppMuc.get_roster(self, room): for alias in XmppMuc.get_roster(self, room):
jid_bare = XmppMuc.get_full_jid(self, room, alias).split('/')[0] jid_bare = XmppMuc.get_full_jid(self, room, alias).split('/')[0]
fields = [jid_bare, alias, timestamp_iso] 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): def on_reactions(self, message):
@ -358,11 +347,10 @@ class XmppClient(slixmpp.ClientXMPP):
del self.filename_blocklist del self.filename_blocklist
# subscribe['from'] = xmppbl.org # subscribe['from'] = xmppbl.org
# subscribe['pubsub']['subscription']['node'] = 'muc_bans_sha256' # 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() await self['xep_0115'].update_caps()
bookmarks = await XmppBookmark.get_bookmarks(self) rooms = await XmppGroupchat.autojoin(self)
print(bookmarks)
rooms = await XmppGroupchat.autojoin(self, bookmarks)
# See also get_joined_rooms of slixmpp.plugins.xep_0045 # See also get_joined_rooms of slixmpp.plugins.xep_0045
for room in rooms: for room in rooms:
XmppStatus.send_status_message(self, room) XmppStatus.send_status_message(self, room)

View file

@ -6,9 +6,8 @@ import kaikout.config as config
from kaikout.config import Config from kaikout.config import Config
from kaikout.log import Logger from kaikout.log import Logger
from kaikout.database import DatabaseToml 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.version import __version__
from kaikout.xmpp.bookmark import XmppBookmark
from kaikout.xmpp.muc import XmppMuc from kaikout.xmpp.muc import XmppMuc
from kaikout.xmpp.status import XmppStatus from kaikout.xmpp.status import XmppStatus
from kaikout.xmpp.utilities import XmppUtilities from kaikout.xmpp.utilities import XmppUtilities
@ -81,17 +80,16 @@ class XmppCommands:
async def bookmark_add(self, muc_jid): async def bookmark_add(self, muc_jid):
await XmppBookmark.add(self, jid=muc_jid) if muc_jid not in self.bookmarks: self.bookmarks.append(muc_jid)
message = ('Groupchat {} has been added to bookmarks.' Toml.save_file(self.filename_bookmarks, self.data_bookmarks)
.format(muc_jid)) return f'Groupchat {muc_jid} has been added to bookmarks.'
return message
async def bookmark_del(self, muc_jid): async def bookmark_del(self, muc_jid):
await XmppBookmark.remove(self, muc_jid) if muc_jid in self.bookmarks: self.bookmarks.remove(muc_jid)
message = ('Groupchat {} has been removed from bookmarks.' Toml.save_file(self.filename_bookmarks, self.data_bookmarks)
.format(muc_jid)) return f'Groupchat {muc_jid} has been removed from bookmarks.'
return message
async def invite_jid_to_muc(self, jid_bare): async def invite_jid_to_muc(self, jid_bare):
muc_jid = 'slixfeed@chat.woodpeckersnest.space' muc_jid = 'slixfeed@chat.woodpeckersnest.space'
@ -122,12 +120,13 @@ class XmppCommands:
if muc_jid: if muc_jid:
# TODO probe JID and confirm it's a groupchat # TODO probe JID and confirm it's a groupchat
result = await XmppMuc.join(self, muc_jid) result = await XmppMuc.join(self, muc_jid)
# await XmppBookmark.add(self, jid=muc_jid)
if result == 'ban': if result == 'ban':
message = '{} is banned from {}'.format(self.alias, muc_jid) message = '{} is banned from {}'.format(self.alias, muc_jid)
if room in self.bookmarks: self.bookmarks.remove(room)
else: else:
await XmppBookmark.add(self, muc_jid) if room not in self.bookmarks: self.bookmarks.append(room)
message = 'Joined groupchat {}'.format(muc_jid) message = 'Joined groupchat {}'.format(muc_jid)
Toml.save_file(self.filename_bookmarks, self.data_bookmarks)
else: else:
message = '> {}\nGroupchat JID appears to be invalid.'.format(muc_jid) message = '> {}\nGroupchat JID appears to be invalid.'.format(muc_jid)
else: else:
@ -137,7 +136,8 @@ class XmppCommands:
async def muc_leave(self, room): async def muc_leave(self, room):
XmppMuc.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): async def outcast(self, room, alias, reason):
@ -163,14 +163,11 @@ class XmppCommands:
async def print_bookmarks(self): async def print_bookmarks(self):
conferences = await XmppBookmark.get_bookmarks(self) conferences = self.bookmarks
message = '\nList of groupchats:\n\n```\n' message = '\nList of groupchats:\n\n```\n'
for conference in conferences: for conference in conferences:
message += ('Name: {}\n' message += f'{conference}\n'
'Room: {}\n' message += (f'```\nTotal of {len(conferences)} groupchats.\n')
'\n'
.format(conference['name'], conference['jid']))
message += ('```\nTotal of {} groupchats.\n'.format(len(conferences)))
return message return message

View file

@ -1,63 +1,53 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- 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.bookmark import XmppBookmark
from kaikout.xmpp.message import XmppMessage
from kaikout.xmpp.muc import XmppMuc from kaikout.xmpp.muc import XmppMuc
from kaikout.xmpp.status import XmppStatus from kaikout.xmpp.status import XmppStatus
from kaikout.utilities import Toml
from kaikout.log import Logger, Message from kaikout.log import Logger, Message
import random import random
logger = Logger(__name__) logger = Logger(__name__)
class XmppGroupchat: class XmppGroupchat:
async def autojoin(self, bookmarks): async def join(self, room):
mucs_join_success = [] result = await XmppMuc.join(self, room)
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': if result == 'ban':
await XmppBookmark.remove(self, room) message_body = '{} is banned from {}'.format(self.alias, room)
logger.warning('{} is banned from {}'.format(self.alias, room)) jid_bare = message['from'].bare
logger.warning('Groupchat {} has been removed from bookmarks' XmppMessage.send(self, jid_bare, message_body, 'chat')
.format(room)) logger.warning(message_body)
elif result == 'conflict': elif result == 'conflict':
while result == 'conflict':
number = str(random.randrange(1000, 5000)) number = str(random.randrange(1000, 5000))
await XmppMuc.join(self, room, alias + ' #' + number) print(f'Conflict. Atempting to join to {room} as {self.alias} #{number}')
result = await XmppMuc.join(self, room, f'{self.alias} #{number}')
else: else:
mucs_join_success.append(room) #self.bookmarks.append({'jid' : room, 'lang' : '', 'pass' : ''})
logger.info('Autojoin groupchat\n' if room not in self.bookmarks: self.bookmarks.append(room)
'Name : {}\n' Toml.save_file(self.filename_bookmarks, self.data_bookmarks)
'JID : {}\n' return result
'Alias : {}\n'
.format(bookmark["name"], async def autojoin(self):
bookmark["jid"], mucs_joined = []
bookmark["nick"])) for room in self.bookmarks:
elif not bookmark["jid"]: alias = self.alias
logger.error('JID is missing for bookmark {}' print(f'Joining to MUC {room} ...')
.format(bookmark['name'])) #Message.printer(f'Joining to MUC {room} ...')
return mucs_join_success 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))
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

View file

@ -93,21 +93,18 @@ class XmppMuc:
def is_moderator(self, room, alias): def is_moderator(self, room, alias):
"""Check if given JID is a moderator""" """Check if given JID is a moderator"""
role = self.plugin['xep_0045'].get_jid_property(room, alias, 'role') role = self.plugin['xep_0045'].get_jid_property(room, alias, 'role')
if role == 'moderator': result = True if role == 'moderator' else False
result = True
else:
result = False
return result return result
async def join(self, jid, alias=None, password=None): async def join(self, jid_bare, alias=None, password=None):
logger.info('Joining groupchat\nJID : {}\n'.format(jid)) logger.info('Joining groupchat\nJID : {}\n'.format(jid_bare))
jid_from = str(self.boundjid) if self.is_component else None #jid_from = str(self.boundjid) if self.is_component else None
if not alias: alias = self.alias if not alias: alias = self.alias
try: try:
await self.plugin['xep_0045'].join_muc_wait(jid, await self.plugin['xep_0045'].join_muc_wait(jid_bare,
alias, alias,
presence_options = {"pfrom" : jid_from}, #presence_options = {"pfrom" : jid_from},
password=password, password=password,
maxchars=0, maxchars=0,
maxstanzas=0, maxstanzas=0,
@ -118,24 +115,24 @@ class XmppMuc:
except IqError as e: except IqError as e:
logger.error('Error XmppIQ') logger.error('Error XmppIQ')
logger.error(str(e)) logger.error(str(e))
logger.error(jid) logger.error(jid_bare)
result = 'error' result = 'error'
except IqTimeout as e: except IqTimeout as e:
logger.error('Timeout XmppIQ') logger.error('Timeout XmppIQ')
logger.error(str(e)) logger.error(str(e))
logger.error(jid) logger.error(jid_bare)
result = 'timeout' result = 'timeout'
except TimeoutError as e: except TimeoutError as e:
logger.error('Timeout AsyncIO') logger.error('Timeout AsyncIO')
logger.error(str(e)) logger.error(str(e))
logger.error(jid) logger.error(jid_bare)
result = 'timeout' result = 'timeout'
except PresenceError as e: except PresenceError as e:
logger.error('Error Presence') logger.error('Error Presence')
logger.error(str(e)) logger.error(str(e))
if (e.condition == 'forbidden' and if (e.condition == 'forbidden' and
e.presence['error']['code'] == '403'): 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' result = 'ban'
elif e.condition == 'conflict': elif e.condition == 'conflict':
logger.warning(e.presence['error']['text']) logger.warning(e.presence['error']['text'])
@ -145,7 +142,7 @@ class XmppMuc:
except Exception as e: except Exception as e:
logger.error('Unknown error') logger.error('Unknown error')
logger.error(str(e)) logger.error(str(e))
logger.error(jid) logger.error(jid_bare)
result = 'unknown' result = 'unknown'
return result return result