new feature: add modules of commands instead of list of commands

This commit is contained in:
Shadow15510 2023-06-12 09:50:59 +02:00
parent 7e33a90fb2
commit 8178a602fd
5 changed files with 155 additions and 105 deletions

13
fun_cmnds.py Normal file
View File

@ -0,0 +1,13 @@
from irc_api import api
from irc_api.api import auto_help
@api.channel("#glados")
@api.on(lambda m: "blague" in m.text.lower())
def fun(bot, msg, *args):
bot.send(msg.to, "Mais je suis nuuuullle en blagues euh.")
@api.command("limite")
def history_limit(bot, msg, *args):
bot.send(msg.to, f"limite historique : {bot.history._History__limit}")

View File

@ -1,5 +1,5 @@
from irc_api import api
from random import choice
from irc_api.api import auto_help
@api.on(lambda m: "au revoir glados" in m.text.lower())
@ -14,8 +14,7 @@ def greetings_bye(bot, msg, *args):
"#glados."
)
def greetings_hello(bot, msg, *args):
bot.send(msg.to, args)
bot.send(msg.to, f"Oh un utilisateur sur #glados :o")
bot.send(msg.to, f"Bonjour à toi")
@api.command("test")
@ -26,22 +25,12 @@ def test(bot, msg, *args):
@api.channel("#glados")
def react_on_glados(bot, msg, *args):
"""Répond de manière random à tout les messages posté sur #glados."""
answers = (
f"Yo {msg.author} o/",
"Alors, de quoi on parle ?",
"Ça va ?",
"Bon, tu as fini de spammer ou bien ?",
"Ça roule ?",
"Hey you! Ah, you finally awake…",
)
bot.send(msg.to, choice(answers))
"""Répète le dernier message envoyé sur #glados."""
if bot.history.get()[-1].author == msg.author:
bot.counter += 1
else:
bot.counter = 1
commands = [
api.auto_help,
greetings_bye,
greetings_hello,
test,
# react_on_glados
]
if bot.counter > 3:
bot.send(msg.to, f"Bon, {msg.author}, tu vas arrêter de spammer un jour ou bien ?")
bot.counter = 0

View File

@ -1,5 +1,6 @@
import logging
from irc_api.irc import IRC
import re
from irc_api.irc import IRC, History
PREFIX = ""
@ -83,7 +84,14 @@ class Bot:
start : NoneType, public
Runs the bot and connects it to IRC and V5 servers.
"""
def __init__(self, auth: tuple, irc_params: tuple, channels: list, prefix: str=""):
def __init__(
self,
auth: tuple,
irc_params: tuple,
channels: list=["#general"],
*commands_modules,
**kwargs
):
"""Initialize the Bot instance.
Parameters
@ -96,50 +104,97 @@ class Bot:
The prefix on which the bot will react.
"""
global PREFIX
PREFIX = prefix
if kwargs.get('prefix'):
PREFIX = kwargs.get('prefix')
self.auth = auth
self.irc = IRC(*irc_params)
self.history = History(kwargs.get('limit'))
self.channels = channels
self.auth = auth
self.callbacks = {}
self.send = self.irc.send
if commands_modules:
self.add_commands_modules(*commands_modules)
def start(self):
"""Starts the bot and connect it to the given IRC and V5 servers."""
# Start IRC
self.irc.start(self.auth[0], self.auth[1])
self.irc.connexion(self.auth[0], self.auth[1])
# Join channels
for channel in self.channels:
self.irc.join(channel)
# Run IRC
self.irc.run()
# mainloop
while True:
message = self.irc.receive()
self.history.add(message)
logging.info("received %s", message)
if message is not None:
for callback in self.callbacks.values():
if not False in [event(message) for event in callback.events]:
logging.info("matched %s", callback.name)
callback(message, *parse(message.text)[callback.cmnd_type:])
def add_command(self, command):
command.bot = self
self.irc.callbacks.append(command)
def add_commands(self, commands):
"""Add a package of commands to the bot.
def send(self, target: str, message: str):
"""Send a message to the specified target (channel or user).
Parameters
----------
commands_pack : CommandsPack
A commands pack which contains command's instances.
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.irc.send(f"PRIVMSG {target} :{message}")
def add_command(self, command):
command.bot = self
self.callbacks[command.name] = command
def add_commands(self, *commands):
"""Add a list of commands to the bot.
Parameters
----------
commands : list
A list of command's instances.
"""
for command in commands:
self.add_command(command)
def add_commands_modules(self, *commands_modules):
for commands_module in commands_modules:
for cmnd_name in dir(commands_module):
cmnd = getattr(commands_module, cmnd_name)
if isinstance(cmnd, Command):
self.add_command(cmnd)
def remove_command(self, command_name: str):
if command_name in self.callbacks.keys():
self.callbacks.pop(command_name)
@command("aide")
def auto_help(bot, msg, *args):
"""Aide des commandes disponibles."""
known_cmnds = {cmnd.name: cmnd for cmnd in bot.irc.callbacks}
if args and args[0] in known_cmnds.keys():
if args and args[0] in bot.callbacks.keys():
bot.send(msg.to, f"Aide sur la commande : {args[0]}")
bot.send(msg.to, f" {known_cmnds[args[0]].desc}")
bot.send(msg.to, f" {bot.callbacks[args[0]].desc}")
else:
bot.irc.send(msg.to, f"Liste des commandes ({PREFIX}aide <cmnd> pour plus d'info)")
for cmnd in bot.irc.callbacks:
bot.irc.send(msg.to, f" {cmnd.name}")
bot.send(msg.to, f"Liste des commandes ({PREFIX}aide <cmnd> pour plus d'info)")
for cmnd_name in bot.callbacks.keys():
bot.send(msg.to, f" {cmnd_name}")
def parse(message):
pattern = re.compile(r"((\"[\w\ \-\']+\"\ *)|(\'[\w\ \-\"]+\'\ *)|([\w\'\-]+\ *))")
args_to_return = []
for match in re.findall(pattern, message):
match = match[0].strip().rstrip()
if (match.startswith("\"") and match.endswith("\"")) \
or (match.startswith("'") and match.endswith("'")):
args_to_return.append(match[1: -1])
else:
args_to_return.append(match)
return args_to_return

View File

@ -70,8 +70,6 @@ class IRC:
# Public attributes
self.connected = False # Simple lock
self.callbacks = []
# Private attributes
self.__socket = ssl.create_default_context().wrap_socket(
@ -82,7 +80,7 @@ class IRC:
self.__handler = Thread(target=self.__handle)
# Public methods
def start(self, nick: str, password: str):
def connexion(self, nick: str, password: str):
"""Start the IRC layer. Manage authentication as well.
Parameters
@ -94,37 +92,14 @@ class IRC:
"""
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.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 not False in [event(message) for event in callback.events]:
logging.info("matched %s", callback.name)
callback(message, *parse(message.text)[callback.cmnd_type:])
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.
@ -148,28 +123,10 @@ class IRC:
channel : str
The name of the channel to join.
"""
self.__send(f"JOIN {channel}")
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):
def send(self, raw: str):
"""Wrap and encode raw message to send.
Parameters
@ -179,7 +136,7 @@ class IRC:
"""
self.__socket.send(f"{raw}\r\n".encode())
def __waitfor(self, condition):
def waitfor(self, condition):
"""Wait for a raw message that matches the condition.
Parameters
@ -198,6 +155,44 @@ class IRC:
msg = self.__inbox.get()
return msg
# 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)
class History:
def __init__(self, limit: int=100):
self.__content = []
if limit:
self.__limit = limit
else:
self.__limit = 100
def __len__(self):
return len(self.__content)
def add(self, elmnt):
if len(self.__content) == self.__limit:
self.__content.pop(0)
self.__content.append(elmnt)
def get(self):
return self.__content
class Message:
"""Parse the raw message in three fields : author, the channel, and text.
@ -232,8 +227,3 @@ class Message:
def __str__(self):
return f"{self.author} to {self.to}: {self.text}"
def parse(message):
pattern = re.compile(r"(((\"|\')[\w\ \-]+(\"|\')\ *)|([\w\'\-]+\ *))")
return [match[0].strip("\"' ").rstrip("\"' ") for match in re.findall(pattern, message)]

13
main.py
View File

@ -13,7 +13,8 @@ import re
from irc_api import api
from glados_cmnds import commands
import glados_cmnds as gcmnds
import fun_cmnds as fcmnds
from secrets import USER, PASSWORD
@ -21,14 +22,16 @@ LOG_FORMAT = "%(asctime)s [%(levelname)s] <%(filename)s> %(funcName)s: %(message
logging.basicConfig(format=LOG_FORMAT, level=logging.DEBUG)
glados = api.Bot(
class MyBot(api.Bot):
counter = 1
glados = MyBot(
(USER, PASSWORD),
('irc.planet-casio.com', 6697),
["#general", "#glados"],
"!"
gcmnds, fcmnds,
prefix="!",
)
glados.add_commands(commands)
glados.start()