diff --git a/irc_bot_api/__pycache__/commands.cpython-311.pyc b/irc_bot_api/__pycache__/commands.cpython-311.pyc deleted file mode 100644 index 83ea653..0000000 Binary files a/irc_bot_api/__pycache__/commands.cpython-311.pyc and /dev/null differ diff --git a/irc_bot_api/__pycache__/irc.cpython-311.pyc b/irc_bot_api/__pycache__/irc.cpython-311.pyc deleted file mode 100644 index 4d12e63..0000000 Binary files a/irc_bot_api/__pycache__/irc.cpython-311.pyc and /dev/null differ diff --git a/irc_bot_api/__pycache__/secrets.cpython-311.pyc b/irc_bot_api/__pycache__/secrets.cpython-311.pyc deleted file mode 100644 index 2271db1..0000000 Binary files a/irc_bot_api/__pycache__/secrets.cpython-311.pyc and /dev/null differ diff --git a/irc_bot_api/__pycache__/v5.cpython-311.pyc b/irc_bot_api/__pycache__/v5.cpython-311.pyc deleted file mode 100644 index 026e387..0000000 Binary files a/irc_bot_api/__pycache__/v5.cpython-311.pyc and /dev/null differ diff --git a/irc_bot_api/commands.py b/irc_bot_api/commands.py deleted file mode 100644 index 9cff824..0000000 --- a/irc_bot_api/commands.py +++ /dev/null @@ -1,130 +0,0 @@ -from functools import wraps -import logging -from irc_bot_api.secrets import USER, PASSWORD -from irc_bot_api.irc import IRC -from irc_bot_api.v5 import V5 - - -PREFIX = "" - -def command(name, event=None): - """Decorate a function and return a Command instance.""" - def decorator(func): - desc = name - if func.__doc__: - desc = func.__doc__ - return Command( - name=name, - desc=desc, - func=func, - event=event - ) - return decorator - - -class Command: - def __init__(self, name, desc, func, event=None): - self.name = name - self.desc = desc - self.func = func - if not event: - self.event = lambda m: m.text.startswith(PREFIX + name) - else: - self.event = event - - self.cmnd_pack = None - - def __call__(self, msg): - return self.func(self.cmnd_pack, msg) - - -class CommandsPack: - def __init__(self, bot): - self.bot = bot - - -class Bot: - """Run the connexion between IRC's server and V5 one. - - Attributes - ---------- - irc : IRC, public - IRC wrapper which handle communication with IRC server. - v5 : V5, public - V5 wrapper which handle communication with V5 server. - channels : list, public - The channels the bot will listen. - - Methods - ------- - start : NoneType, public - Runs the bot and connects it to IRC and V5 servers. - """ - def __init__(self, irc_params: tuple, v5_params: tuple, channels: list, prefix: str=""): - """Initialize the Bot instance. - - Parameters - ---------- - irc_params : tuple - Contains the IRC server informations (host, port) - v5_params : tuple - Contains the V5 server informations (host, port) - channels : list - Contains the names of the channels on which the bot will connect. - prefix : str, optionnal - The prefix on which the bot will react. - """ - global PREFIX - PREFIX = prefix - - self.irc = IRC(*irc_params) - self.v5 = V5(v5_params, self.irc) - self.channels = channels - - def start(self): - """Starts the bot and connect it to the given IRC and V5 servers.""" - # Start IRC - self.irc.start(USER, PASSWORD) - - # Join channels - for channel in self.channels: - self.irc.join(channel) - - # Start V5 hadndler - self.v5.start() - - # Run IRC - self.irc.run() - - def add_help(self): - help_callback = Command( - "aide", - "Affiche la liste des commandes disponibles.", - help_cmnd - ) - help_callback.cmnd_pack = self - self.irc.callbacks.append(help_callback) - - def add_commands_pack(self, commands_pack): - """Add a package of commands to the bot. - - Parameters - ---------- - commands_pack : CommandsPack - A commands pack which contains command's instances. - """ - cmnd_pack = commands_pack(self) - - for cmnd_name in dir(commands_pack): - if not cmnd_name.startswith("__") and not cmnd_name.endswith("__"): - cmnd = getattr(commands_pack, cmnd_name) - cmnd.cmnd_pack = cmnd_pack - - self.irc.callbacks.append(cmnd) - - -def help_cmnd(bot, msg): - """Documentation des fonctions disponibles.""" - bot.irc.send(msg.to, f"Aide des commandes") - for cmnd in bot.irc.callbacks: - bot.irc.send(msg.to, f" – {cmnd.name} : {cmnd.desc}") diff --git a/irc_bot_api/irc.py b/irc_bot_api/irc.py deleted file mode 100644 index 79c2087..0000000 --- a/irc_bot_api/irc.py +++ /dev/null @@ -1,234 +0,0 @@ -""" -irc (GLaDOS) -============ - -Description ------------ -Manage the IRC layer of GLaDOS. -""" - -import logging -import re -import socket -import ssl - -from functools import wraps -from queue import Queue -from threading import Thread - - -class IRC: - """Manage connexion to an IRC server, authentication and callbacks. - - Attributes - ---------- - connected : bool, public - If the bot is connected to an IRC server or not. - callbacks : list, public - List of the registred callbacks. - - socket : ssl.SSLSocket, private - The IRC's socket. - inbox : Queue, private - Queue of the incomming messages. - handler : Thread, private - - Methods - ------- - start : NoneType, public - Starts the IRC layer and manage authentication. - run : NoneType, public - Mainloop, allows to handle public messages. - send : NoneType, public - Sends a message to a given channel. - receive : Message, public - Same as ``run`` for private messages. - join : NoneType, public - Allows to join a given channel. - on : function, public - Add a callback on a given message. - - handle : NoneType, private - Handles the ping and store incoming messages into the inbox attribute. - send : NoneType, private - Send message to a target. - recv : str, private - Get the oldest incoming message and returns it. - waitfor : str, private - Wait for a raw message that matches the given condition. - """ - def __init__(self, host: str, port: int): - """Initialize an IRC wrapper. - - Parameters - ---------- - host : str - The adress of the IRC server. - port : int - The port of the IRC server. - """ - - # Public attributes - self.connected = False # Simple lock - self.callbacks = [] - - - # Private attributes - self.__socket = ssl.create_default_context().wrap_socket( - socket.create_connection((host, port)), - server_hostname=host - ) - self.__inbox = Queue() - self.__handler = Thread(target=self.__handle) - - # Public methods - def start(self, nick: str, password: str): - """Start the IRC layer. Manage authentication as well. - - Parameters - ---------- - nick : str - The username for login and nickname once connected. - password : str - The password for authentification. - """ - self.__handler.start() - - self.__send(f"USER {nick} * * :{nick}") - self.__send(f"NICK {nick}") - self.__waitfor(lambda m: "NOTICE" in m and "/AUTH" in m) - self.__send(f"AUTH {nick}:{password}") - self.__waitfor(lambda m: "You are now logged in" in m) - - self.connected = True - - def run(self): - """Handle new messages.""" - while True: - message = self.receive() - logging.info("received %s", message) - if message is not None: - for callback in self.callbacks: - if callback.event(message): - logging.info("matched %s", callback.name) - callback(message) - - def send(self, target: str, message: str): - """Send a message to the specified target (channel or user). - - Parameters - ---------- - target : str - The target of the message. It can be a channel or user (private message). - message : str - The content of the message to send. - """ - self.__send(f"PRIVMSG {target} :{message}") - - def receive(self): - """Receive a private message. - - Returns - ------- - msg : Message - The incoming processed private message. - """ - while True: - message = self.__inbox.get() - if " PRIVMSG " in message: - msg = Message(message) - if msg: - return msg - - def join(self, channel: str): - """Join a channel. - - Parameters - ---------- - channel : str - The name of the channel to join. - """ - self.__send(f"JOIN {channel}") - logging.info("joined %s", channel) - - # Private methods - def __handle(self): - """Handle raw messages from irc and manage ping.""" - while True: - # Get incoming messages - data = self.__socket.recv(4096).decode() - - # Split multiple lines - for msg in data.split('\r\n'): - # Manage ping - if msg.startswith("PING"): - self.__send(msg.replace("PING", "PONG")) - - # Or add a new message to inbox - elif len(msg): - self.__inbox.put(msg) - logging.debug("received %s", msg) - - def __send(self, raw: str): - """Wrap and encode raw message to send. - - Parameters - ---------- - raw : str - The raw message to send. - """ - self.__socket.send(f"{raw}\r\n".encode()) - - def __waitfor(self, condition): - """Wait for a raw message that matches the condition. - - Parameters - ---------- - condition : function - ``condition`` is a function that must taking a raw message in parameter and returns a - boolean. - - Returns - ------- - msg : str - The last message received that doesn't match the condition. - """ - msg = self.__inbox.get() - while not condition(msg): - msg = self.__inbox.get() - return msg - - -class Message: - """Parse the raw message in three fields : author, the channel, and text. - - Attributes - ---------- - pattern : re.Pattern, public - The message parsing pattern. - author : str, public - The message's author. - to : str, public - The message's origin (channel or DM). - text : str, public - The message's content. - """ - pattern = re.compile( - r"^:(?P[\w.~|\-\[\]]+)(?:!(?P\S+))? PRIVMSG (?P\S+) :(?P.+)" - ) - - def __init__(self, raw: str): - match = re.search(Message.pattern, raw) - if match: - self.author = match.group("author") - self.to = match.group("to") - self.text = match.group("text") - logging.debug("sucessfully parsed %s into %s", raw, self.__str__()) - else: - self.author = "" - self.to = "" - self.text = "" - logging.warning("failed to parse %s into valid message", raw) - - def __str__(self): - return f"{self.author} to {self.to}: {self.text}" diff --git a/irc_bot_api/v5.py b/irc_bot_api/v5.py deleted file mode 100644 index 25dfaa7..0000000 --- a/irc_bot_api/v5.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -v5 (GLaDOS) -=========== - -Description ------------ -Manage the V5 layer of GLaDOS. -""" - -import logging -import socket -from threading import Thread -from functools import wraps - - -class V5: - """Manage connexion beetween the bot and the V5 server, and manage callbacks. - - Attributes - ---------- - irc : irc.IRC, public - An IRC instance. - - sock : ssl.SSLSocket, private - The V5 socket. - handler : Thread, private - callbacks : list, private - List of the registred callbacks. - - Methods - ------- - start : NoneType, public - Start v5 handler. - on : function, public - Add a callback to the v5 handler. - - handle : NoneType, private - Handle the incoming messages and callbacks. - """ - - def __init__(self, v5_params: tuple, irc): - """Initialize V5 handle. - - Parameters - ---------- - v5 : tuple - The information on V5 server (host, port). - irc : irc.IRC - An initialized IRC instance. - """ - self.irc = irc - self.__sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - self.__sock.bind(v5_params) - self.__handler = Thread(target=self.__handle) - self.__callbacks = [] - - def start(self): - """Start v5 handler.""" - self.__handler.start() - logging.info("started") - - def on(self, event): - """Adds a callback to the v5 handler. - - Parameters - ---------- - event : function - ``event`` is a function taking in parameter a list of channels and a string, and return - ``True`` if the callback should be executed. - """ - def callback(func): - @wraps(func) - def wrapper(channels, message): - func(channels, message) - self.__callbacks.append((event, wrapper)) - return wrapper - - return callback - - def __handle(self): - """Handle the incoming messages and callbacks.""" - while True: - data, addr = self.__sock.recvfrom(4096) - data = data.decode() - logging.debug("received %s", data) - channels, message = data.split(":", 1) - channels = channels.split(" ") - - for event, callback in self.__callbacks: - if event(channels, message): - logging.info("passed %s", event.__name__) - callback(channels, message)