feat(commands:rule|Mod): feat rule command

This commit is contained in:
Romain J 2021-05-13 22:19:27 +02:00
parent 4e3fbd7f4d
commit ad443c9c48
8 changed files with 252 additions and 35 deletions

View file

@ -42,6 +42,8 @@
<w>pred</w> <w>pred</w>
<w>pydig</w> <w>pydig</w>
<w>pylint</w> <w>pylint</w>
<w>regle</w>
<w>regles</w>
<w>releaselevel</w> <w>releaselevel</w>
<w>rprint</w> <w>rprint</w>
<w>skipcq</w> <w>skipcq</w>

View file

@ -0,0 +1,34 @@
from discord.ext import commands
from discord.ext.commands import Context
from tuxbot.cogs.Mod.functions.exceptions import (
RuleTooLongException,
UnknownRuleException,
)
from tuxbot.cogs.Mod.models import Rule
def _(x):
return x
class RuleIDConverter(commands.Converter):
async def convert(self, ctx: Context, argument: str): # skipcq: PYL-W0613
arg = int(argument)
rule_row = await Rule.get_or_none(server_id=ctx.guild.id, rule_id=arg)
if not rule_row:
raise UnknownRuleException(_("Unknown rule"))
return arg
class RuleConverter(commands.Converter):
async def convert(self, ctx: Context, argument: str): # skipcq: PYL-W0613
if len(argument) > 300:
raise RuleTooLongException(
_("Rule length must be 300 characters or lower.")
)
return argument

View file

@ -0,0 +1,13 @@
from discord.ext import commands
class ModException(commands.BadArgument):
pass
class RuleTooLongException(ModException):
pass
class UnknownRuleException(ModException):
pass

View file

@ -1,3 +1,4 @@
from tuxbot.cogs.Mod.models.rules import Rule
from tuxbot.core.config import set_for_key from tuxbot.core.config import set_for_key
from tuxbot.core.config import Config from tuxbot.core.config import Config
@ -5,7 +6,29 @@ from tuxbot.core.bot import Tux
from tuxbot.core.utils.functions.extra import ContextPlus from tuxbot.core.utils.functions.extra import ContextPlus
async def save_lang(bot: Tux, ctx: ContextPlus, lang: str): async def save_lang(bot: Tux, ctx: ContextPlus, lang: str) -> None:
set_for_key( set_for_key(bot.config.Servers, ctx.guild.id, Config.Server, locale=lang)
bot.config.Servers, ctx.guild.id, Config.Server, locale=lang
)
async def get_server_rules(guild_id: int) -> list[Rule]:
return await Rule.filter(server_id=guild_id).all()
def get_most_recent_server_rules(rules: list[Rule]) -> Rule:
return sorted(rules, key=lambda r: r.updated_at)[0]
def paginate_server_rules(rules: list[Rule]) -> list[str]:
body = [""]
for rule in rules:
if len(body[-1] + format_rule(rule)) > 2000:
body.append(format_rule(rule) + "\n")
else:
body[-1] += format_rule(rule) + "\n"
return body
def format_rule(rule: Rule) -> str:
return f"**{rule.rule_id}** - {rule.content}"

View file

@ -1,9 +1,22 @@
import logging import logging
from datetime import datetime
import discord import discord
from discord.ext import commands from discord.ext import commands
from tuxbot.cogs.Mod.functions.utils import save_lang from tuxbot.cogs.Mod.functions.converters import RuleConverter, RuleIDConverter
from tuxbot.cogs.Mod.functions.exceptions import (
RuleTooLongException,
UnknownRuleException,
)
from tuxbot.cogs.Mod.functions.utils import (
save_lang,
get_server_rules,
format_rule,
get_most_recent_server_rules,
paginate_server_rules,
)
from tuxbot.cogs.Mod.models.rules import Rule
from tuxbot.core.utils import checks from tuxbot.core.utils import checks
from tuxbot.core.bot import Tux from tuxbot.core.bot import Tux
@ -26,6 +39,16 @@ class Mod(commands.Cog):
def __init__(self, bot: Tux): def __init__(self, bot: Tux):
self.bot = bot self.bot = bot
async def cog_command_error(self, ctx: ContextPlus, error):
if isinstance(
error,
(
RuleTooLongException,
UnknownRuleException,
),
):
await ctx.send(_(str(error), ctx, self.bot.config))
# ========================================================================= # =========================================================================
# ========================================================================= # =========================================================================
@ -58,3 +81,123 @@ class Mod(commands.Cog):
) )
await ctx.send(embed=e) await ctx.send(embed=e)
# =========================================================================
@group_extra(
name="rule",
aliases=["rules", "regle", "regles"],
deletable=False,
invoke_without_command=True,
)
@commands.guild_only()
async def _rule(
self,
ctx: ContextPlus,
rule: RuleIDConverter,
members: commands.Greedy[discord.Member],
):
rule_row = await Rule.get(server_id=ctx.guild.id, rule_id=rule)
message = _(
"{}please read the following rule: \n{}", ctx, self.bot.config
)
authors = ""
for member in members:
if member in ctx.message.mentions:
authors += f"{member.name}#{member.discriminator}, "
else:
authors += f"{member.mention}, "
await ctx.send(message.format(authors, format_rule(rule_row)))
@_rule.command(name="list", aliases=["show", "all"])
async def _rule_list(self, ctx: ContextPlus):
rules = await get_server_rules(ctx.guild.id)
embed = discord.Embed(
title=_("Rules for {}", ctx, self.bot.config).format(
ctx.guild.name
),
color=discord.Color.blue(),
)
embed.set_footer(
text=_("Latest change: {}", ctx, self.bot.config).format(
get_most_recent_server_rules(rules).created_at
)
)
pages = paginate_server_rules(rules)
if len(pages) == 1:
embed.description = pages[0]
await ctx.send(embed=embed)
else:
for i, page in enumerate(pages):
embed.title = _(
"Rules for {} ({}/{})", ctx, self.bot.config
).format(ctx.guild.name, str(i + 1), str(len(pages)))
embed.description = page
await ctx.send(embed=embed)
@checks.is_admin()
@_rule.command(name="add")
async def _rule_add(self, ctx: ContextPlus, *, rule: RuleConverter):
rule_row = await Rule()
rule_row.server_id = ctx.guild.id
rule_row.author_id = ctx.message.author.id
rule_row.rule_id = len(await get_server_rules(ctx.guild.id)) + 1 # type: ignore
rule_row.content = str(rule) # type: ignore
await rule_row.save()
await ctx.send(
_("Following rule added: \n{}", ctx, self.bot.config).format(
format_rule(rule_row)
)
)
@checks.is_admin()
@_rule.command(name="edit")
async def _rule_edit(
self,
ctx: ContextPlus,
rule: RuleIDConverter,
*,
content: RuleConverter,
):
# noinspection PyTypeChecker
rule_row = await Rule.get(server_id=ctx.guild.id, rule_id=rule)
rule_row.content = str(content) # type: ignore
rule_row.updated_at = datetime.now() # type: ignore
await rule_row.save()
await ctx.send(
_("Following rule updated: \n{}", ctx, self.bot.config).format(
format_rule(rule_row)
)
)
@checks.is_admin()
@_rule.command(name="delete")
async def _rule_delete(
self,
ctx: ContextPlus,
rule: RuleIDConverter,
):
# noinspection PyTypeChecker
rule_row = await Rule.get(server_id=ctx.guild.id, rule_id=rule)
await rule_row.delete()
await ctx.send(
_("Following rule deleted: \n{}", ctx, self.bot.config).format(
format_rule(rule_row)
)
)

View file

@ -1 +1,2 @@
from .rules import *
from .warns import * from .warns import *

View file

@ -0,0 +1,28 @@
import tortoise
from tortoise import fields
class Rule(tortoise.Model):
id = fields.BigIntField(pk=True)
server_id = fields.BigIntField()
author_id = fields.BigIntField()
rule_id = fields.IntField()
content = fields.TextField(max_length=300)
created_at = fields.DatetimeField(auto_now_add=True)
updated_at = fields.DatetimeField(auto_now_add=True)
class Meta:
table = "rules"
def __str__(self):
return (
f"<Rule id={self.id} "
f"server_id={self.server_id} "
f"author_id={self.author_id} "
f"rule_id={self.rule_id} "
f"content='{self.content}' "
f"created_at={self.created_at} "
f"updated_at={self.updated_at}>"
)
__repr__ = __str__

View file

@ -15,17 +15,7 @@ IP_REPLACEMENT = "■" * random.randint(3, 15)
class ContextPlus(commands.Context): class ContextPlus(commands.Context):
# noinspection PyTypedDict # noinspection PyTypedDict
async def send( async def send(
self, self, content=None, *, embed=None, deletable=True, **kwargs
content=None,
*,
tts=False,
embed=None,
file=None,
files=None,
delete_after=None,
nonce=None,
allowed_mentions=None,
deletable=True,
): # i know *args and **kwargs but, i prefer work with same values ): # i know *args and **kwargs but, i prefer work with same values
from tuxbot.core.utils.functions.utils import ( from tuxbot.core.utils.functions.utils import (
replace_in_dict, replace_in_dict,
@ -45,7 +35,6 @@ class ContextPlus(commands.Context):
) )
if len(content) > 1800: if len(content) > 1800:
file = discord.File(BytesIO(content.encode()), "output.txt")
content = "output too long..." content = "output too long..."
if embed: if embed:
e = embed.to_dict() e = embed.to_dict()
@ -107,14 +96,7 @@ class ContextPlus(commands.Context):
hasattr(self.command, "deletable") and self.command.deletable hasattr(self.command, "deletable") and self.command.deletable
) and deletable: ) and deletable:
message = await super().send( message = await super().send(
content=content, content=content, embed=embed, **kwargs
tts=tts,
embed=embed,
file=file,
files=files,
delete_after=delete_after,
nonce=nonce,
allowed_mentions=allowed_mentions,
) )
await message.add_reaction("🗑") await message.add_reaction("🗑")
@ -135,16 +117,7 @@ class ContextPlus(commands.Context):
await message.delete() await message.delete()
return message return message
return await super().send( return await super().send(content=content, embed=embed, **kwargs)
content=content,
tts=tts,
embed=embed,
file=file,
files=files,
delete_after=delete_after,
nonce=nonce,
allowed_mentions=allowed_mentions,
)
@property @property
def session(self) -> aiohttp.ClientSession: def session(self) -> aiohttp.ClientSession: