From cf006aa659500b315c91c921fc4d400aff0e7207 Mon Sep 17 00:00:00 2001 From: Tamas Gal <himself@tamasgal.com> Date: Tue, 21 Jan 2025 13:55:19 +0100 Subject: [PATCH] Fix RocketChat bot --- backend/requirements.txt | 1 - backend/scripts/chatbot.py | 161 ++++++++++++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 3 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index e73ba4f..9fd1d97 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -8,7 +8,6 @@ numpy==2.1.3 pandas==2.2.3 requests==2.32.3 rocketchat-API==1.34.0 -pyrocketbot==1.0.5 seaborn==0.13.2 supervisor==4.2.5 toml==0.10.2 diff --git a/backend/scripts/chatbot.py b/backend/scripts/chatbot.py index 7b5f0a8..afa9117 100755 --- a/backend/scripts/chatbot.py +++ b/backend/scripts/chatbot.py @@ -13,13 +13,16 @@ Options: -h --help Show this screen. """ +from datetime import datetime +from pprint import pprint +import random import re import requests import subprocess import time +import threading import toml from rocketchat_API.rocketchat import RocketChat -from RocketChatBot import RocketChatBot import km3pipe as kp @@ -29,6 +32,160 @@ URL = "https://chat.km3net.de" CONFIG = "pipeline.toml" RECONNECT_INTERVAL = 30 + +class RocketChatBot(object): + def __init__(self, botname, passwd, server, command_character=None): + self.botname = botname + self.api = RocketChat(user=botname, password=passwd, server_url=server) + self.commands = [(['echo', ], self.echo)] + self.auto_answers = [] + self.direct_answers = [] + self.unknow_command = ['command not found', ] + self.lastts = {} + self.command_character = command_character + + def echo(self, msg, user, channel_id): + self.send_message('@' + user + ' : ' + msg, channel_id) + + def get_status(self, auser): + return self.api.users_get_presence(username=auser) + + def send_message(self, msg, channel_id): + self.api.chat_post_message(channel=channel_id, text=msg) + + def add_dm_handler(self, command, action): + self.commands.append((command, action)) + + def add_auto_answer(self, triggers, answers): + self.auto_answers.append((triggers, answers)) + + def add_direct_answer(self, triggers, answers): + self.direct_answers.append((triggers, answers)) + + def handle_command_character_message(self, message, channel_id): + msg = message['msg'].lstrip(self.command_character) + + command = msg.split()[0].lower() + arguments = " ".join(msg.split()[1:]) + user = message['u']['username'] + + for cmd_list in self.commands: + if command.lower() in cmd_list[0]: + cmd_list[1](arguments, user, channel_id) + return + + if not self.handle_auto_answer(message, self.direct_answers, channel_id): + self.send_message('@' + user + ' :' + random.choice(self.unknow_command), channel_id) + + def handle_direct_message(self, message, channel_id): + msg = message['msg'].lstrip('@' + self.botname).strip() + if len(msg) > 0: + command = msg.split()[0].lower() + arguments = " ".join(msg.split()[1:]) + user = message['u']['username'] + for cmd_list in self.commands: + if command.lower() in cmd_list[0]: + cmd_list[1](arguments, user, channel_id) + return + + if not self.handle_auto_answer(message, self.direct_answers, channel_id): + self.send_message('@' + user + ' :' + random.choice(self.unknow_command), channel_id) + else: + self.send_message('Here I am', channel_id) + + def handle_auto_answer(self, message, answers, channel_id): + for kind in answers: + for k in kind[0]: + if k in message['msg'].lower(): + self.send_message(random.choice(kind[1]) + ' @' + message['u']['username'], channel_id) + return True + return False + + def handle_messages(self, messages, channel_id): + for message in messages['messages']: + if message['u']['username'] != self.botname: + pprint(message) + if message['u']['username'] == 'rocket.cat': + continue + if message['msg'].startswith('@' + self.botname): + threading.Thread(target=self.handle_direct_message, args=(message, channel_id)).start() + elif self.command_character is not None and message['msg'].startswith(self.command_character): + threading.Thread(target=self.handle_command_character_message, args=(message, channel_id)).start() + elif 'mentions' not in message or message.get('mentions') == []: + threading.Thread(target=self.handle_auto_answer, args=(message, self.auto_answers, channel_id)).start() + + def load_ts(self, channel_id, messages): + if len(messages) > 0: + self.lastts[channel_id] = messages[0]['ts'] + else: + self.lastts[channel_id] = '' + + def load_channel_ts(self, channel_id): + self.load_ts(channel_id, self.api.channels_history(channel_id).json()['messages']) + + def load_group_ts(self, channel_id): + self.load_ts(channel_id, self.api.groups_history(channel_id).json()['messages']) + + def load_im_ts(self, channel_id): + response = self.api.im_history(channel_id).json() + if response.get('success'): + self.load_ts(channel_id, self.api.im_history(channel_id).json()['messages']) + + def process_messages(self, messages, channel_id): + try: + if "success" in messages: + if messages['success'] == False: + raise RuntimeError(messages['error']) + if len(messages['messages']) > 0: + self.lastts[channel_id] = messages['messages'][0]['ts'] + self.handle_messages(messages, channel_id) + except Exception as e: + pprint(e) + + def process_channel(self, channel_id): + if channel_id not in self.lastts: + self.lastts[channel_id] = datetime.now().isoformat() + + self.process_messages(self.api.channels_history(channel_id, oldest=self.lastts[channel_id]).json(), + channel_id) + + def process_group(self, channel_id): + if channel_id not in self.lastts: + self.lastts[channel_id] = '' + + self.process_messages(self.api.groups_history(channel_id, oldest=self.lastts[channel_id]).json(), + channel_id) + + def process_im(self, channel_id): + if channel_id not in self.lastts: + self.lastts[channel_id] = '' + + self.process_messages(self.api.im_history(channel_id, oldest=self.lastts[channel_id]).json(), + channel_id) + + def run(self): + for channel in self.api.channels_list_joined().json().get('channels'): + self.load_channel_ts(channel.get('_id')) + + for group in self.api.groups_list().json().get('groups'): + self.load_group_ts(group.get('_id')) + + for im in self.api.im_list().json().get('ims'): + self.load_im_ts(im.get('_id')) + + while 1: + for channel in self.api.channels_list_joined().json().get('channels'): + threading.Thread(target=self.process_channel, args=(channel.get('_id'),)).start() + +# for group in self.api.groups_list().json().get('groups'): +# threading.Thread(target=self.process_group, args=(group.get('_id'),)).start() +# +# for im in self.api.im_list().json().get('ims'): +# threading.Thread(target=self.process_im, args=(im.get('_id'),)).start() + + time.sleep(1) + + with open(CONFIG, 'r') as fobj: print(f"Reading configuration from {CONFIG}") config = toml.load(fobj) @@ -72,7 +229,7 @@ def run(): print("Running the monitoring bot system") bot = spawn_bot() register_handlers(bot) - bot.send_message("I am up and running!", CHANNEL_ID) + bot.send_message("ChatBot (re)started. I am up and running!", CHANNEL_ID) bot.run() -- GitLab