✨ CustomBot with full reload
This commit is contained in:
parent
3bfad6e0a9
commit
3b9f4aae95
|
@ -1,4 +1,6 @@
|
||||||
import code
|
import code
|
||||||
|
import sys
|
||||||
|
from importlib import reload
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
@ -8,13 +10,20 @@ from discord.ext.commands import Cog
|
||||||
from discord.utils import get
|
from discord.utils import get
|
||||||
|
|
||||||
from src.constants import *
|
from src.constants import *
|
||||||
|
from src.core import CustomBot
|
||||||
|
|
||||||
|
COGS_SHORTCUTS = {
|
||||||
COGS_SHORTCUTS = {"d": "dev", "ts": "teams", "t": "tirages", "m": "misc", "e": "errors"}
|
"d": "tirages",
|
||||||
|
"e": "errors",
|
||||||
|
"m": "misc",
|
||||||
|
"t": "teams",
|
||||||
|
"u": "src.utils",
|
||||||
|
"v": "dev",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class DevCog(Cog, name="Dev tools"):
|
class DevCog(Cog, name="Dev tools"):
|
||||||
def __init__(self, bot: Bot):
|
def __init__(self, bot: CustomBot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
@command(name="interrupt")
|
@command(name="interrupt")
|
||||||
|
@ -69,6 +78,11 @@ class DevCog(Cog, name="Dev tools"):
|
||||||
possibles: `teams`, `tirages`, `dev`.
|
possibles: `teams`, `tirages`, `dev`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if name is None:
|
||||||
|
self.bot.reload()
|
||||||
|
await ctx.send(":tada: The bot was reloaded !")
|
||||||
|
return
|
||||||
|
|
||||||
names = [name] if name else list(COGS_SHORTCUTS.values())
|
names = [name] if name else list(COGS_SHORTCUTS.values())
|
||||||
|
|
||||||
for name in names:
|
for name in names:
|
||||||
|
|
|
@ -5,8 +5,8 @@ import discord
|
||||||
from discord.ext.commands import *
|
from discord.ext.commands import *
|
||||||
from discord.utils import maybe_coroutine
|
from discord.utils import maybe_coroutine
|
||||||
|
|
||||||
from src.errors import UnwantedCommand
|
from src.core import CustomBot
|
||||||
|
from src.errors import UnwantedCommand, TfjmError
|
||||||
|
|
||||||
# Global variable and function because I'm too lazy to make a metaclass
|
# Global variable and function because I'm too lazy to make a metaclass
|
||||||
handlers = {}
|
handlers = {}
|
||||||
|
@ -29,6 +29,9 @@ def handles(error_type):
|
||||||
class ErrorsCog(Cog):
|
class ErrorsCog(Cog):
|
||||||
"""This cog defines all the handles for errors."""
|
"""This cog defines all the handles for errors."""
|
||||||
|
|
||||||
|
def __init__(self, bot: CustomBot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
@Cog.listener()
|
@Cog.listener()
|
||||||
async def on_command_error(self, ctx: Context, error: CommandError):
|
async def on_command_error(self, ctx: Context, error: CommandError):
|
||||||
print(repr(error), file=sys.stderr)
|
print(repr(error), file=sys.stderr)
|
||||||
|
@ -47,10 +50,11 @@ class ErrorsCog(Cog):
|
||||||
msg = await maybe_coroutine(handler, self, ctx, error)
|
msg = await maybe_coroutine(handler, self, ctx, error)
|
||||||
|
|
||||||
if msg:
|
if msg:
|
||||||
await ctx.send(msg)
|
message = await ctx.send(msg)
|
||||||
|
await self.bot.wait_for_bin(ctx.message.author, message)
|
||||||
|
|
||||||
@handles(UnwantedCommand)
|
@handles(UnwantedCommand)
|
||||||
async def on_unwanted_command(self, ctx, error):
|
async def on_unwanted_command(self, ctx, error: UnwantedCommand):
|
||||||
await ctx.message.delete()
|
await ctx.message.delete()
|
||||||
author: discord.Message
|
author: discord.Message
|
||||||
await ctx.author.send(
|
await ctx.author.send(
|
||||||
|
@ -59,14 +63,19 @@ class ErrorsCog(Cog):
|
||||||
+ "\nC'est pas grave, c'est juste pour ne pas encombrer "
|
+ "\nC'est pas grave, c'est juste pour ne pas encombrer "
|
||||||
"le chat lors du tirage."
|
"le chat lors du tirage."
|
||||||
)
|
)
|
||||||
await ctx.author.send("Raison: " + error.original.msg)
|
await ctx.author.send("Raison: " + error.msg)
|
||||||
|
|
||||||
|
@handles(TfjmError)
|
||||||
|
async def on_tfjm_error(self, ctx: Context, error: TfjmError):
|
||||||
|
msg = await ctx.send(error.msg)
|
||||||
|
await self.bot.wait_for_bin(ctx.author, msg)
|
||||||
|
|
||||||
@handles(CommandInvokeError)
|
@handles(CommandInvokeError)
|
||||||
async def on_command_invoke_error(self, ctx, error):
|
async def on_command_invoke_error(self, ctx, error):
|
||||||
specific_handler = handlers.get(type(error.original))
|
specific_handler = handlers.get(type(error.original))
|
||||||
|
|
||||||
if specific_handler:
|
if specific_handler:
|
||||||
return await specific_handler(self, ctx, error)
|
return await specific_handler(self, ctx, error.original)
|
||||||
|
|
||||||
traceback.print_tb(error.original.__traceback__, file=sys.stderr)
|
traceback.print_tb(error.original.__traceback__, file=sys.stderr)
|
||||||
return (
|
return (
|
||||||
|
@ -91,4 +100,4 @@ class ErrorsCog(Cog):
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
bot.add_cog(ErrorsCog())
|
bot.add_cog(ErrorsCog(bot))
|
||||||
|
|
|
@ -17,13 +17,15 @@ from discord.ext.commands import (
|
||||||
Group,
|
Group,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from src import utils
|
||||||
from src.constants import *
|
from src.constants import *
|
||||||
from src.constants import Emoji
|
from src.constants import Emoji
|
||||||
from src.utils import has_role
|
from src.core import CustomBot
|
||||||
|
from src.utils import has_role, start_time
|
||||||
|
|
||||||
|
|
||||||
class MiscCog(Cog, name="Divers"):
|
class MiscCog(Cog, name="Divers"):
|
||||||
def __init__(self, bot: Bot):
|
def __init__(self, bot: CustomBot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.show_hidden = False
|
self.show_hidden = False
|
||||||
self.verify_checks = True
|
self.verify_checks = True
|
||||||
|
@ -55,6 +57,7 @@ class MiscCog(Cog, name="Divers"):
|
||||||
|
|
||||||
await message.add_reaction(Emoji.JOY)
|
await message.add_reaction(Emoji.JOY)
|
||||||
await message.add_reaction(Emoji.SOB)
|
await message.add_reaction(Emoji.SOB)
|
||||||
|
await self.bot.wait_for_bin(ctx.message.author, message)
|
||||||
|
|
||||||
@command(name="status")
|
@command(name="status")
|
||||||
@commands.has_role(Role.CNO)
|
@commands.has_role(Role.CNO)
|
||||||
|
@ -65,7 +68,7 @@ class MiscCog(Cog, name="Divers"):
|
||||||
benevoles = [g for g in guild.members if has_role(g, Role.BENEVOLE)]
|
benevoles = [g for g in guild.members if has_role(g, Role.BENEVOLE)]
|
||||||
participants = [g for g in guild.members if has_role(g, Role.PARTICIPANT)]
|
participants = [g for g in guild.members if has_role(g, Role.PARTICIPANT)]
|
||||||
no_role = [g for g in guild.members if g.top_role == guild.default_role]
|
no_role = [g for g in guild.members if g.top_role == guild.default_role]
|
||||||
uptime = datetime.timedelta(seconds=round(time() - START_TIME))
|
uptime = datetime.timedelta(seconds=round(time() - start_time()))
|
||||||
|
|
||||||
infos = {
|
infos = {
|
||||||
"Bénévoles": len(benevoles),
|
"Bénévoles": len(benevoles),
|
||||||
|
|
|
@ -7,13 +7,14 @@ from discord.ext.commands import Cog, Bot, group, Context
|
||||||
from discord.utils import get, find
|
from discord.utils import get, find
|
||||||
|
|
||||||
from src.constants import *
|
from src.constants import *
|
||||||
|
from src.core import CustomBot
|
||||||
from src.utils import has_role
|
from src.utils import has_role
|
||||||
|
|
||||||
Team = namedtuple("Team", ["name", "trigram", "tournoi", "secret", "status"])
|
Team = namedtuple("Team", ["name", "trigram", "tournoi", "secret", "status"])
|
||||||
|
|
||||||
|
|
||||||
class TeamsCog(Cog, name="Teams"):
|
class TeamsCog(Cog, name="Teams"):
|
||||||
def __init__(self, bot: Bot):
|
def __init__(self, bot: CustomBot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.teams = self.load_teams()
|
self.teams = self.load_teams()
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ from discord.ext.commands import group, Cog, Context
|
||||||
from discord.utils import get
|
from discord.utils import get
|
||||||
|
|
||||||
from src.constants import *
|
from src.constants import *
|
||||||
|
from src.core import CustomBot
|
||||||
from src.errors import TfjmError, UnwantedCommand
|
from src.errors import TfjmError, UnwantedCommand
|
||||||
|
|
||||||
__all__ = ["Tirage", "TirageCog"]
|
__all__ = ["Tirage", "TirageCog"]
|
||||||
|
@ -606,7 +607,7 @@ class TirageOrderPhase(OrderPhase):
|
||||||
|
|
||||||
class TirageCog(Cog, name="Tirages"):
|
class TirageCog(Cog, name="Tirages"):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot: commands.Bot = bot
|
self.bot: CustomBot = bot
|
||||||
|
|
||||||
# We retrieve the global variable.
|
# We retrieve the global variable.
|
||||||
# We don't want tirages to be ust an attribute
|
# We don't want tirages to be ust an attribute
|
||||||
|
@ -627,13 +628,21 @@ class TirageCog(Cog, name="Tirages"):
|
||||||
if channel in self.tirages:
|
if channel in self.tirages:
|
||||||
await self.tirages[channel].dice(ctx, n)
|
await self.tirages[channel].dice(ctx, n)
|
||||||
else:
|
else:
|
||||||
|
if n == 0:
|
||||||
|
raise TfjmError(f"Un dé sans faces ? Le concept m'intéresse...")
|
||||||
if n < 1:
|
if n < 1:
|
||||||
raise TfjmError(f"Je ne peux pas lancer un dé à {n} faces, désolé.")
|
raise TfjmError(
|
||||||
|
f"Je ne peux pas lancer un dé avec un "
|
||||||
|
f"nombre négatif faces, désolé."
|
||||||
|
)
|
||||||
|
if len(str(n)) > 1900:
|
||||||
|
raise TfjmError(
|
||||||
|
"Oulà... Je sais que la taille ça ne compte pas, "
|
||||||
|
"mais là il est vraiment gros ton dé !"
|
||||||
|
)
|
||||||
|
|
||||||
dice = random.randint(1, n)
|
dice = random.randint(1, n)
|
||||||
await ctx.send(
|
await ctx.send(f"{ctx.author.mention} : {Emoji.DICE} {dice}")
|
||||||
f"Le dé à {n} face{'s' * (n > 1)} s'est arrêté sur... **{dice}**"
|
|
||||||
)
|
|
||||||
|
|
||||||
@commands.command(
|
@commands.command(
|
||||||
name="random-problem",
|
name="random-problem",
|
||||||
|
|
|
@ -59,6 +59,8 @@ class Role:
|
||||||
class Emoji:
|
class Emoji:
|
||||||
JOY = "😂"
|
JOY = "😂"
|
||||||
SOB = "😭"
|
SOB = "😭"
|
||||||
|
BIN = "🗑️"
|
||||||
|
DICE = "🎲"
|
||||||
|
|
||||||
|
|
||||||
class File:
|
class File:
|
||||||
|
@ -71,3 +73,8 @@ class File:
|
||||||
with open(File.TOP_LEVEL / "data" / "problems") as f:
|
with open(File.TOP_LEVEL / "data" / "problems") as f:
|
||||||
PROBLEMS = f.read().splitlines()
|
PROBLEMS = f.read().splitlines()
|
||||||
MAX_REFUSE = len(PROBLEMS) - 4 # -5 usually but not in 2020 because of covid-19
|
MAX_REFUSE = len(PROBLEMS) - 4 # -5 usually but not in 2020 because of covid-19
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
# Just so we can reload the constants
|
||||||
|
pass
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import asyncio
|
||||||
|
import sys
|
||||||
|
from importlib import reload
|
||||||
|
|
||||||
|
import psutil
|
||||||
|
from discord import User, Message, Reaction
|
||||||
|
from discord.ext.commands import Bot
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["CustomBot"]
|
||||||
|
|
||||||
|
from discord.utils import get
|
||||||
|
|
||||||
|
from src.constants import Emoji
|
||||||
|
|
||||||
|
|
||||||
|
class CustomBot(Bot):
|
||||||
|
"""
|
||||||
|
This is the same as a discord bot except
|
||||||
|
for class reloading and it provides hints
|
||||||
|
for the type checker about the modules
|
||||||
|
that are added by extensions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.__class__.__name__}:{hex(id(self.__class__))} obj at {hex(id(self))}"
|
||||||
|
|
||||||
|
def reload(self):
|
||||||
|
cls = self.__class__
|
||||||
|
module_name = cls.__module__
|
||||||
|
old_module = sys.modules[module_name]
|
||||||
|
|
||||||
|
print("Trying to reload the bot.")
|
||||||
|
try:
|
||||||
|
# del sys.modules[module_name]
|
||||||
|
module = reload(old_module)
|
||||||
|
self.__class__ = getattr(module, cls.__name__, cls)
|
||||||
|
except:
|
||||||
|
print("Could not reload the bot :/")
|
||||||
|
raise
|
||||||
|
print("The bot has reloaded !")
|
||||||
|
|
||||||
|
async def wait_for_bin(bot: Bot, user: User, *msgs: Message, timeout=300):
|
||||||
|
"""Wait for timeout seconds for `user` to delete the messages."""
|
||||||
|
|
||||||
|
msgs = list(msgs)
|
||||||
|
|
||||||
|
assert msgs, "No messages in wait_for_bin"
|
||||||
|
|
||||||
|
for m in msgs:
|
||||||
|
await m.add_reaction(Emoji.BIN)
|
||||||
|
|
||||||
|
def check(reaction: Reaction, u):
|
||||||
|
return (
|
||||||
|
user == u
|
||||||
|
and any(m.id == reaction.message.id for m in msgs)
|
||||||
|
and str(reaction.emoji) == Emoji.BIN
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
while msgs:
|
||||||
|
reaction, u = await bot.wait_for(
|
||||||
|
"reaction_add", check=check, timeout=timeout
|
||||||
|
)
|
||||||
|
the_msg = get(msgs, id=reaction.message.id)
|
||||||
|
await the_msg.delete()
|
||||||
|
msgs.remove(the_msg)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for m in msgs:
|
||||||
|
await m.clear_reaction(Emoji.BIN)
|
|
@ -1,12 +1,13 @@
|
||||||
#!/bin/python
|
#!/bin/python
|
||||||
|
|
||||||
from discord.ext import commands
|
|
||||||
|
|
||||||
from src.constants import *
|
from src.constants import *
|
||||||
|
|
||||||
|
|
||||||
# We allow "! " to catch people that put a space in their commands.
|
# We allow "! " to catch people that put a space in their commands.
|
||||||
# It must be in first otherwise "!" always match first and the space is not recognised
|
# It must be in first otherwise "!" always match first and the space is not recognised
|
||||||
bot = commands.Bot(("! ", "!"))
|
from src.core import CustomBot
|
||||||
|
|
||||||
|
bot = CustomBot(("! ", "!"))
|
||||||
|
|
||||||
# Global variable to hold the tirages.
|
# Global variable to hold the tirages.
|
||||||
# We *want* it to be global so we can reload the tirages cog without
|
# We *want* it to be global so we can reload the tirages cog without
|
||||||
|
@ -25,6 +26,7 @@ bot.load_extension("src.cogs.errors")
|
||||||
bot.load_extension("src.cogs.misc")
|
bot.load_extension("src.cogs.misc")
|
||||||
bot.load_extension("src.cogs.teams")
|
bot.load_extension("src.cogs.teams")
|
||||||
bot.load_extension("src.cogs.tirages")
|
bot.load_extension("src.cogs.tirages")
|
||||||
|
bot.load_extension("src.utils")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
22
src/utils.py
22
src/utils.py
|
@ -1,6 +1,12 @@
|
||||||
|
import asyncio
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
import psutil
|
import psutil
|
||||||
from discord import Message
|
from discord import Message, Member, User, Reaction
|
||||||
from discord.ext.commands import Context, Bot
|
from discord.ext.commands import Context, Bot
|
||||||
|
from discord.utils import get
|
||||||
|
|
||||||
|
from src.constants import Emoji
|
||||||
|
|
||||||
|
|
||||||
def has_role(member, role: str):
|
def has_role(member, role: str):
|
||||||
|
@ -9,17 +15,9 @@ def has_role(member, role: str):
|
||||||
return any(r.name == role for r in member.roles)
|
return any(r.name == role for r in member.roles)
|
||||||
|
|
||||||
|
|
||||||
async def send_and_bin(bot: Bot, ctx: Context, msg=None, *, embed=None):
|
def start_time(self):
|
||||||
"""Send a message and wait 5min for the author to delete it."""
|
|
||||||
|
|
||||||
message: Message = await ctx.send(msg, embed=embed)
|
|
||||||
|
|
||||||
await msg
|
|
||||||
|
|
||||||
|
|
||||||
def start_time():
|
|
||||||
return psutil.Process().create_time()
|
return psutil.Process().create_time()
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot: Bot):
|
||||||
bot.send_and_bin = send_and_bin
|
pass
|
||||||
|
|
Loading…
Reference in New Issue