shoutbridge/shoutbox.py

137 lines
5.7 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
import requests as r
import logging
import time
import datetime
import re
from functools import wraps
from threading import Thread
from irc import IRC
from sasl import nick, password
from users import USERS
class Shoutbox(object):
def __init__(self, cookies):
self.channels = {'annonces': 0, 'projets': 0, 'hs': 0}
self.cookies = cookies
self._callbacks = []
self.irc_clients = {} # pseudo: [IRC(), date]
self.running = False
self._handler = Thread(target=self._handle)
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=irc").text)['messages']
for m in messages:
self.channels[channel] = m['id']
def run(self):
self.running = True
logging.debug("Thread start")
self._handler.start()
def stop(self):
logging.debug("STOP: Stop requests to planet-casio.com")
self.running = False
logging.debug("STOP: Halt all irc user threads")
for client in self.irc_clients:
self.irc_clients[client][0].stop()
self.irc_clients.pop(client)
self._handler.join()
logging.debug("STOP: Shoutbox thread closed")
def on(self, event):
""" Adds a callback to the IRC handler
Event is a function taking in parameter a SBMessage and returning
True if the callback should be executed on the message """
def callback(func):
@wraps(func)
def wrapper(message):
func(message)
self._callbacks.append((event, wrapper))
logging.info(f"added callback {func.__name__}")
return wrapper
return callback
def post(self, user, msg, channel, users):
if msg.startswith("ACTION"):
msg = msg.replace("ACTION", "/me")
# Look for pseudo v43-v5 translation
for v43_name, v5_name in users:
if v5_name.lower() == user.lower():
r.post("https://www.planet-casio.com/Fr/shoutbox/api/post-as",
data={"user": v43_name, "message": msg, "channel": channel},
cookies=self.cookies)
return
# No translation found
r.post("https://www.planet-casio.com/Fr/shoutbox/api/post-as",
data={"user": "IRC", "message": f"{user} : {msg}", "channel": channel},
cookies=self.cookies)
def normalize(pseudo):
if pseudo.lower() in [u[0].lower() for u in USERS]:
return [u[1] for u in USERS if u[0].lower() == pseudo.lower()][0]
return re.sub(r'[^.A-Za-z0-9_]', '_', pseudo)
def _handle(self):
while self.running:
try:
for channel, last_id in self.channels.items():
# Do not spam with logs
logging.getLogger().setLevel(logging.INFO)
messages = json.loads(r.get(f"https://www.planet-casio.com/Fr/shoutbox/api/read?since={last_id}&channel={channel}&format=irc").text)['messages']
logging.getLogger().setLevel(logging.DEBUG)
for m in messages:
logging.debug(m)
# If message comes from IRC, drop it (no loops allowed)
if m['source'] == "IRC":
continue
# Get channel id, parse SBMessage
self.channels[channel] = m['id']
message = SBMessage(m, channel)
# If handler needs to be killed
if not self.running:
logging.debug("going to stop")
break
# For each callback defined with @decorator
for event, callback in self._callbacks:
author = Shoutbox.normalize(message.author)
# client is not known or is disconnected
if author not in self.irc_clients.keys() \
or self.irc_clients[author][0].running == False:
self.irc_clients[author] = [
IRC('irc.planet-casio.com', 6697),
datetime.datetime.now()
]
# Start a thread for new client
if 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 k, c in self.irc_clients.items():
if datetime.datetime.now() - c[1] > datetime.timedelta(hours=1):
logging.info(f"killing {c[0].nick}")
c[0].stop()
self.irc_clients.pop(k)
except Exception as e:
logging.error(f"Faillure in Shoutbox thread {e}")
finally:
time.sleep(3)
class SBMessage(object):
def __init__(self, raw, channel):
self.author = raw['author']
self.channel = channel
self.text = raw['content']