diff --git a/administrator/utils.py b/administrator/utils.py new file mode 100644 index 0000000..6908e4e --- /dev/null +++ b/administrator/utils.py @@ -0,0 +1,25 @@ +import re +from datetime import timedelta + +from discord.ext.commands import BadArgument + + +def time_pars(s: str) -> timedelta: + match = re.fullmatch(r"(?:([0-9]+)W)*(?:([0-9]+)D)*(?:([0-9]+)H)*(?:([0-9]+)M)*(?:([0-9]+)S)*", + s.upper().replace(" ", "").strip()) + if match: + w, d, h, m, s = match.groups() + if any([w, d, h, m, s]): + w, d, h, m, s = [i if i else 0 for i in [w, d, h, m, s]] + return timedelta(weeks=int(w), days=int(d), hours=int(h), minutes=int(m), seconds=int(s)) + raise BadArgument() + + +def seconds_to_time_string(seconds: float) -> str: + days, seconds = divmod(seconds, 86400) + hours, seconds = divmod(seconds, 3600) + minutes, seconds = divmod(seconds, 60) + return f"""{f"{days}d {hours}h {minutes}m {seconds}s" + if days > 0 else f"{hours}h {minutes}m {seconds}s" + if hours > 0 else f"{minutes}m {seconds}s" + if minutes > 0 else f"{seconds}s"}""" diff --git a/db/Warn.py b/db/Warn.py new file mode 100644 index 0000000..cf0c1f8 --- /dev/null +++ b/db/Warn.py @@ -0,0 +1,22 @@ +from datetime import datetime + +from discord import Embed + +from db import Base +from sqlalchemy import Column, Integer, BigInteger, Text, DateTime + + +class Warn(Base): + __tablename__ = "warns" + id = Column(Integer, primary_key=True) + user = Column(BigInteger, nullable=False) + author = Column(BigInteger, nullable=False) + guild = Column(BigInteger, nullable=False) + description = Column(Text, nullable=False) + date = Column(DateTime, nullable=False, default=datetime.now()) + + def __init__(self, user: int, author: int, guild: int, description: str): + self.user = user + self.author = author + self.guild = guild + self.description = description diff --git a/db/WarnAction.py b/db/WarnAction.py new file mode 100644 index 0000000..2f6cfe1 --- /dev/null +++ b/db/WarnAction.py @@ -0,0 +1,19 @@ +from datetime import timedelta + +from db import Base +from sqlalchemy import Column, Integer, BigInteger, String + + +class WarnAction(Base): + __tablename__ = "warn_actions" + id = Column(Integer, primary_key=True) + guild = Column(BigInteger, nullable=False) + count = Column(Integer, nullable=False, unique=True) + action = Column(String, nullable=False) + duration = Column(BigInteger) + + def __init__(self, guild: int, count: int, action: str, duration: float = None): + self.guild = guild + self.count = count + self.action = action + self.duration = duration diff --git a/db/__init__.py b/db/__init__.py index 50ebb32..904b1f0 100644 --- a/db/__init__.py +++ b/db/__init__.py @@ -10,4 +10,6 @@ from db.Greetings import Greetings from db.Presentation import Presentation from db.RoRec import RoRec from db.Polls import Polls +from db.Warn import Warn +from db.WarnAction import WarnAction Base.metadata.create_all(engine) diff --git a/extensions/__init__.py b/extensions/__init__.py index 0b1a35f..44fdf9d 100644 --- a/extensions/__init__.py +++ b/extensions/__init__.py @@ -8,3 +8,4 @@ bot.load_extension("extensions.reminder") bot.load_extension("extensions.greetings") bot.load_extension("extensions.presentation") bot.load_extension("extensions.rorec") +bot.load_extension("extensions.warn") diff --git a/extensions/reminder.py b/extensions/reminder.py index aef4310..1003f30 100644 --- a/extensions/reminder.py +++ b/extensions/reminder.py @@ -8,23 +8,12 @@ from discord.ext import tasks from administrator.logger import logger from administrator import db - +from administrator.utils import time_pars, seconds_to_time_string extension_name = "reminders" logger = logger.getChild(extension_name) -def time_pars(s: str) -> timedelta: - match = re.fullmatch(r"(?:([0-9]+)W)*(?:([0-9]+)D)*(?:([0-9]+)H)*(?:([0-9]+)M)*(?:([0-9]+)S)*", - s.upper().replace(" ", "").strip()) - if match: - w, d, h, m, s = match.groups() - if any([w, d, h, m, s]): - w, d, h, m, s = [i if i else 0 for i in [w, d, h, m, s]] - return timedelta(weeks=int(w), days=int(d), hours=int(h), minutes=int(m), seconds=int(s)) - raise BadArgument() - - class Reminders(commands.Cog, name="Reminder"): def __init__(self, bot: commands.Bot): self.bot = bot @@ -56,12 +45,7 @@ class Reminders(commands.Cog, name="Reminder"): s.commit() s.close() - hours, seconds = divmod(time.seconds, 3600) - minutes, seconds = divmod(seconds, 60) - await ctx.send(f"""Remind you in {f"{time.days}d {hours}h {minutes}m {seconds}s" - if time.days > 0 else f"{hours}h {minutes}m {seconds}s" - if hours > 0 else f"{minutes}m {seconds}s" - if minutes > 0 else f"{seconds}s"} !""") + await ctx.send(f"""Remind you in {seconds_to_time_string(time.total_seconds())} !""") @reminder.group("list", pass_context=True) async def reminder_list(self, ctx: commands.Context): diff --git a/extensions/warn.py b/extensions/warn.py new file mode 100644 index 0000000..17a0ade --- /dev/null +++ b/extensions/warn.py @@ -0,0 +1,198 @@ +from discord import Embed, Forbidden, Member, Guild +from discord.ext import commands +from discord.ext.commands import BadArgument + +from administrator import db +from administrator.logger import logger +from administrator.utils import time_pars, seconds_to_time_string + +extension_name = "warn" +logger = logger.getChild(extension_name) + + +class Warn(commands.Cog): + def __init__(self, bot: commands.Bot): + self.bot = bot + + def description(self): + return "Send warning to user and make custom action after a number of warn" + + @staticmethod + async def check_warn(ctx: commands.Context, target: Member): + s = db.Session() + c = s.query(db.Warn).filter(db.Warn.guild == ctx.guild.id, db.Warn.user == target.id).count() + a = s.query(db.WarnAction).filter(db.WarnAction.guild == ctx.guild.id, db.WarnAction.count == c).first() + if a: + reason = f"Action after {c} warns" + if a.action == "kick": + await target.kick(reason=reason) + elif a.action == "ban": + await target.ban(reason=reason) + elif a.action == "mute": + pass # Integration with upcoming ban & mute extension + + @staticmethod + def get_target(ctx: commands.Context, user: str) -> Member: + users = {str(m): m for m in ctx.guild.members} + if user not in users: + raise BadArgument() + return users[user] + + @commands.group("warn", pass_context=True) + @commands.guild_only() + #@commands.has_permissions(manage_roles=True, kick_members=True, ban_members=True, mute_members=True) + async def warn(self, ctx: commands.Context): + if ctx.invoked_subcommand is None: + await ctx.invoke(self.warn_help) + + @warn.group("help", pass_context=True) + async def warn_help(self, ctx: commands.Context): + embed = Embed(title="Warn help") + embed.add_field(name="add ", value="Send a warn to a user", inline=False) + embed.add_field(name="remove ", value="Remove a number of warn to a user", inline=False) + embed.add_field(name="purge ", value="Remove all warn of a user", inline=False) + embed.add_field(name="list [user#discriminator|actions]", value="List warn of the guild or a specified user\n" + "If you specify `actions` instead of a user, " + "all the actions of the guild will be listed", + inline=False) + embed.add_field(name="action ", value="Set an action for a count of warn\n" + "Actions: `mute