From 0a88053e0c13b9ff07a9e98bc58336e25bfc1599 Mon Sep 17 00:00:00 2001 From: Adrien Bourmault Date: Tue, 8 Feb 2022 15:46:34 +0100 Subject: [PATCH] WIP: refactoring --- bot.py | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ data.py | 90 ++++++++++++++++++++++++++++++++++ main.py | 100 +++++++++++++++++++++++++++++++++++++ 3 files changed, 340 insertions(+) create mode 100644 bot.py create mode 100644 data.py create mode 100755 main.py diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..acc5464 --- /dev/null +++ b/bot.py @@ -0,0 +1,150 @@ +import slixmpp +import logging +from systemd.journal import JournalHandler + +from hintTables import annecdotetable, topictable +from commands import commandtable + + +# Logging +log = logging.getLogger(__name__) +log.addHandler(JournalHandler()) +log.setLevel(logging.INFO) + + +class MUCBot(slixmpp.ClientXMPP): + + def __init__(self, jid, password, rooms, nick): + slixmpp.ClientXMPP.__init__(self, jid + "/Isengard", password) + + self.rooms = rooms + self.nick = nick + + # Per channel owners + self.owners = {} + + self.datastore = None + + # The session_start event will be triggered when + # the bot establishes its connection with the server + # and the XML streams are ready for use. We want to + # listen for this event so that we we can initialize + # our roster. + self.add_event_handler("session_start", self.start) + + # The groupchat_message event is triggered whenever a message + # stanza is received from any chat room. If you also also + # register a handler for the 'message' event, MUC messages + # will be processed by both handlers. + self.add_event_handler("groupchat_message", self.muc_message) + + # The groupchat_presence event is triggered whenever a + # presence stanza is received from any chat room, including + # any presences you send yourself. To limit event handling + # to a single room, use the events muc::room@server::presence, + # muc::room@server::got_online, or muc::room@server::got_offline. + self.add_event_handler("muc::%s::got_online" % self.room, + self.muc_online) + + + async def start(self, event): + """ + Requestthe roster and broadcast initial presence stanza. + """ + + await self.get_roster() + self.send_presence() + self.plugin['xep_0045'].join_muc(self.rooms[0], + self.nick, + # password=the_room_password, + ) + + def push(self, destmuc, msg): + self.send_message( mto=destmuc, + mbody=msg, + mtype='groupchat') + print("To: %s\nBody: %s" % (destmuc, msg)) + + + def muc_message(self, msg): + """ + Whenever the bot's nickname is mentioned, respond to + the message from any muc + """ + + if msg['mucnick'] == self.nick: + return + + # Commands + + cmdhints = list(commandtable.keys()) + random.shuffle(cmdhints) + + for opcod in cmdhints: + if msg['body'][0] == "!" and opcod in msg['body']: + self.send_message(mto=msg['from'].bare, + mbody="%s, %s" % (msg['mucnick'], + commandtable[opcod](self.owners[msg['from'].bare], + msg['mucnick'], + msg['body'], + self.datastore)), + mtype='groupchat') + return + + # When people speak to each other without necessarily highlighting Isengard + + anechints = list(annecdotetable.keys()) + random.shuffle(anechints) + + for hint in anechints: + if hint in msg['body']: + self.send_message(mto=msg['from'].bare, + mbody="%s" % (annecdotetable[hint][ + random.randrange(0,len(annecdotetable[hint])) + ]), + mtype='groupchat') + return + + if self.nick not in msg['body']: + return + + # When people talk to Isengard + + topichints = list(topictable.keys()) + random.shuffle(topichints) + + for hint in topichints: + if hint in msg['body']: + self.send_message(mto=msg['from'].bare, + mbody="%s, %s" % (msg['mucnick'], + topictable[hint][ + random.randrange(0,len(topictable[hint])) + ]), + mtype='groupchat') + return + + # And fallback + + self.send_message(mto=msg['from'].bare, + mbody="%s, je n'ai pas compris (je suis un peu bot)" % msg['mucnick'], + mtype='groupchat') + + def muc_online(self, presence): + """ + Send a welcome message that includes + the user's nickname + """ + + if presence['muc']['nick'] in self.datastore.maintainerData: + self.send_message(mto=presence['from'].bare, + mbody="Salut %s, vos services ont produit des " + + "alertes en votre absence !\nUtilisez la commande"+ + " `hist` pour consulter l'historique" + % (presence['muc']['nick']), + mtype='groupchat') + + if presence['muc']['affiliation'] == "owner": + if not presence['from'].bare in self.owners: + self.owners[presence['from'].bare] = [] + + self.owners[presence['from'].bare].append(presence['muc']['nick']) diff --git a/data.py b/data.py new file mode 100644 index 0000000..e7ada02 --- /dev/null +++ b/data.py @@ -0,0 +1,90 @@ +import datetime +import logging + +from systemd.journal import JournalHandler + + +# Logging +log = logging.getLogger(__name__) +log.addHandler(JournalHandler()) +log.setLevel(logging.INFO) + +class HostData: + """ + Data related to notifications related to a given host + """ + + def __init__(self, name): + self.name = name + + # Concerning services + self.serviceLastType = "" + self.serviceLastStatus = "" + self.serviceLastText = "" + + # Concerning host + self.lastType = "" + self.lastStatus = "" + + # Tools + self.linkedMUC = "" + self.notifCount = 0 + self.needUpdate = False + self.maintainer = "Tout le monde" + +class DataStore: + + def __init__(self, linkedBot): + + log.info("Created DataStore") + + self.knownHosts = {} + self.knownMaintainers = {} + self.linkedBot = linkedBot + + def push(self, msg): + + # TYPE|HOST/SERVICE|STATE|OUTPUT|SENDER|COMMENT + + # Get current time + curtime = datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S") + + # Get all params + destmuc, status_type, location, status_state, text, sender, comment = msg.split("|") + print("Dest: %s, Msg: %s" % (destmuc, msg)) + print("Status: %s" % (status_type + " (" + status_state + ")")) + + # check if message is about a service or host + if len(location.split("/")) > 1: + host, service = location.split("/") + else: + host = location + service = False + print("Host: %s, Service: %s" % (host, service)) + + print("Text: %s" % (text)) + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main.py b/main.py new file mode 100755 index 0000000..a1a6c0a --- /dev/null +++ b/main.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 + +from systemd.journal import JournalHandler +from getpass import getpass +from argparse import ArgumentParser +import random +import threading +import socket +import time +import signal +import logging + +from bot import MUCBot +from data import DataStore +from localServer import LocalServer + + +# Logging +log = logging.getLogger(__name__) +log.addHandler(JournalHandler()) +log.setLevel(logging.INFO) + +if __name__ == '__main__': + + def cleanExit(): + global localservthread + localservthread.pleaseStop = True + # stop tcp server + sockfd = socket.socket() + port = 12346 + sockfd.connect(('127.0.0.1', port)) + sockfd.send(b'') + sockfd.close() + + exit(0) + + + def signalHandler(sig, frame): + log.info("Exiting...") + cleanExit() + + # Register SIGINT signal handler + signal.signal(signal.SIGINT, signalHandler) + + # Setup the command line arguments. + parser = ArgumentParser() + + # Output verbosity options. + parser.add_argument("-q", "--quiet", help="set logging to ERROR", + action="store_const", dest="loglevel", + const=logging.ERROR, default=logging.INFO) + parser.add_argument("-d", "--debug", help="set logging to DEBUG", + action="store_const", dest="loglevel", + const=logging.DEBUG, default=logging.INFO) + + # JID and password options. + parser.add_argument("-j", "--jid", dest="jid", + help="JID to use") + parser.add_argument("-p", "--password", dest="password", + help="password to use") + parser.add_argument("-r", "--room", dest="room", + help="MUC room to join") + parser.add_argument("-n", "--nick", dest="nick", + help="MUC nickname") + + args = parser.parse_args() + + # Setup logging. + logging.basicConfig(level=args.loglevel, + format='%(levelname)-8s %(message)s') + + # Setup the MUCBot and register plugins. Note that while plugins may + # have interdependencies, the order in which you register them does + # not matter. + xmpp = MUCBot(args.jid, args.password, args.room, args.nick) + xmpp.register_plugin('xep_0030') # Service Discovery + xmpp.register_plugin('xep_0045') # Multi-User Chat + xmpp.register_plugin('xep_0199') # XMPP Ping + + # Create buffer + store = DataStore(xmpp) + xmpp.datastore = store + + localservthread = LocalServer(store) + localservthread.start() + + while True: + time.sleep(1) + + # Connect to the XMPP server and start processing XMPP stanzas. + xmpp.connect() + + # Check local server status + if not localservthread.is_alive(): + exit(1) + + try: + xmpp.process() + except KeyboardInterrupt: + cleanExit()