diff --git a/bridge.py b/bridge.py index e6ac94b..3b8e4d4 100644 --- a/bridge.py +++ b/bridge.py @@ -7,16 +7,16 @@ from cookies import cookies from sasl import nick, password from users import users +import time LOG_FORMAT = "%(asctime)s [%(levelname)s] <%(filename)s> %(funcName)s: %(message)s" -logging.basicConfig(format=LOG_FORMAT, level=logging.INFO) +logging.basicConfig(format=LOG_FORMAT, level=logging.DEBUG) channels = ["hs", "projets", "annonces"] irc = IRC('irc.planet-casio.com', 6697) shoutbox = Shoutbox(cookies) - @irc.on(lambda m: m.to[1:] in channels) def handle_irc(m): shoutbox.post(m.author, m.text, m.to[1:], users) @@ -24,10 +24,11 @@ def handle_irc(m): @shoutbox.on(lambda m: m.channel in channels and m.author != "IRC" and not m.text.endswith("[IRC]")) def handle_shoutbox(m): - irc.send(f"#{m.channel}", f"{m.author}: {m.text}") + author = Shoutbox.normalize(m.author) + shoutbox.irc_clients[author][0].send(f"#{m.channel}", f"{m.text}") -irc.start("Shoutbox", password, nick) +irc.start("Shoutbox[darks]", password, nick) for c in channels: irc.join(f"#{c}") diff --git a/irc.py b/irc.py index 606b6a6..dc66b7f 100644 --- a/irc.py +++ b/irc.py @@ -10,6 +10,7 @@ class IRC(object): """ Initialize an IRC wrapper """ # Public attributes self.connected = False # Simple lock + self.run = False # Private attributes self._socket = ssl.create_default_context().wrap_socket( @@ -24,6 +25,7 @@ class IRC(object): def start(self, nick, password, sasl_nick=None): """ Start the IRC layer. Manage authentication as well """ sasl_nick = sasl_nick or nick + self._run = True self._handler.start() self._send(f"USER {nick} * * :{nick}") @@ -36,7 +38,13 @@ class IRC(object): def stop(self): """ Stop the IRC layer """ - self._handler.terminate() + self._send("QUIT") + logging.debug("STOP: sent QUIT message") + self._run = False + self._handler.join() + logging.debug("STOP: thread has terminated") + self._socket.close() + logging.debug("STOP: socket close") def run(self): """ Handle new messages """ @@ -87,7 +95,7 @@ class IRC(object): def _handle(self, store=True): """ Handle raw messages from irc and manage ping """ - while True: + while self._run: # Get incoming messages data = self._socket.recv(4096).decode() diff --git a/shoutbox.py b/shoutbox.py index 0c0000f..9338190 100644 --- a/shoutbox.py +++ b/shoutbox.py @@ -2,8 +2,12 @@ import json import requests as r import logging import time +import datetime from functools import wraps from threading import Thread +from irc import IRC + +from sasl import nick, password class Shoutbox(object): @@ -11,6 +15,7 @@ class Shoutbox(object): self.channels = {'annonces': 0, 'projets': 0, 'hs': 0} self.cookies = cookies self._callbacks = [] + self.irc_clients = {} # pseudo: [IRC(), date] for channel, last_id in self.channels.items(): messages = json.loads(r.get(f"https://www.planet-casio.com/Fr/shoutbox/api/read?since={last_id}&channel={channel}&format=text").text)['messages'] @@ -27,9 +32,28 @@ class Shoutbox(object): self.channels[channel] = m['id'] message = SBMessage(m, channel) for event, callback in self._callbacks: + # client is not known or is disconnected + author = Shoutbox.normalize(message.author) + if author not in self.irc_clients \ + or self.irc_clients[author][0].run == False: + self.irc_clients[author] = [ + IRC('irc.planet-casio.com', 6697), + datetime.datetime.now() + ] + self.irc_clients[author][0].start(f"{author}[s]", password, nick) + logging.debug(f"{author} has joined IRC") + # client is known but AFK + else: + self.irc_clients[author][1] = datetime.datetime.now() + logging.debug(f"{author} has updated IRC") + if event(message): logging.info(f"matched {event.__name__}") callback(message) + # kill afk clients + for c in self.irc_clients.values(): + if datetime.datetime.now() - c[1] > datetime.timedelta(hours=1): + c[0].stop() time.sleep(1) Thread(target=handler).start() @@ -60,6 +84,8 @@ class Shoutbox(object): data={"user": "IRC", "message": f"{user} : {msg}", "channel": channel}, cookies=self.cookies) + def normalize(pseudo): + return pseudo.replace(' ', '_') class SBMessage(object): def __init__(self, raw, channel):