feat(commands:rule|Mod): feat rule command
This commit is contained in:
parent
4e3fbd7f4d
commit
ad443c9c48
8 changed files with 252 additions and 35 deletions
|
@ -42,6 +42,8 @@
|
|||
<w>pred</w>
|
||||
<w>pydig</w>
|
||||
<w>pylint</w>
|
||||
<w>regle</w>
|
||||
<w>regles</w>
|
||||
<w>releaselevel</w>
|
||||
<w>rprint</w>
|
||||
<w>skipcq</w>
|
||||
|
|
34
tuxbot/cogs/Mod/functions/converters.py
Normal file
34
tuxbot/cogs/Mod/functions/converters.py
Normal 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
|
13
tuxbot/cogs/Mod/functions/exceptions.py
Normal file
13
tuxbot/cogs/Mod/functions/exceptions.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from discord.ext import commands
|
||||
|
||||
|
||||
class ModException(commands.BadArgument):
|
||||
pass
|
||||
|
||||
|
||||
class RuleTooLongException(ModException):
|
||||
pass
|
||||
|
||||
|
||||
class UnknownRuleException(ModException):
|
||||
pass
|
|
@ -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 Config
|
||||
|
||||
|
@ -5,7 +6,29 @@ from tuxbot.core.bot import Tux
|
|||
from tuxbot.core.utils.functions.extra import ContextPlus
|
||||
|
||||
|
||||
async def save_lang(bot: Tux, ctx: ContextPlus, lang: str):
|
||||
set_for_key(
|
||||
bot.config.Servers, ctx.guild.id, Config.Server, locale=lang
|
||||
)
|
||||
async def save_lang(bot: Tux, ctx: ContextPlus, lang: str) -> None:
|
||||
set_for_key(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}"
|
||||
|
|
|
@ -1,9 +1,22 @@
|
|||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
import discord
|
||||
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.bot import Tux
|
||||
|
||||
|
@ -26,6 +39,16 @@ class Mod(commands.Cog):
|
|||
def __init__(self, bot: Tux):
|
||||
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)
|
||||
|
||||
# =========================================================================
|
||||
|
||||
@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)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
from .rules import *
|
||||
from .warns import *
|
||||
|
|
28
tuxbot/cogs/Mod/models/rules.py
Normal file
28
tuxbot/cogs/Mod/models/rules.py
Normal 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__
|
|
@ -15,17 +15,7 @@ IP_REPLACEMENT = "■" * random.randint(3, 15)
|
|||
class ContextPlus(commands.Context):
|
||||
# noinspection PyTypedDict
|
||||
async def send(
|
||||
self,
|
||||
content=None,
|
||||
*,
|
||||
tts=False,
|
||||
embed=None,
|
||||
file=None,
|
||||
files=None,
|
||||
delete_after=None,
|
||||
nonce=None,
|
||||
allowed_mentions=None,
|
||||
deletable=True,
|
||||
self, content=None, *, embed=None, deletable=True, **kwargs
|
||||
): # i know *args and **kwargs but, i prefer work with same values
|
||||
from tuxbot.core.utils.functions.utils import (
|
||||
replace_in_dict,
|
||||
|
@ -45,7 +35,6 @@ class ContextPlus(commands.Context):
|
|||
)
|
||||
|
||||
if len(content) > 1800:
|
||||
file = discord.File(BytesIO(content.encode()), "output.txt")
|
||||
content = "output too long..."
|
||||
if embed:
|
||||
e = embed.to_dict()
|
||||
|
@ -107,14 +96,7 @@ class ContextPlus(commands.Context):
|
|||
hasattr(self.command, "deletable") and self.command.deletable
|
||||
) and deletable:
|
||||
message = await super().send(
|
||||
content=content,
|
||||
tts=tts,
|
||||
embed=embed,
|
||||
file=file,
|
||||
files=files,
|
||||
delete_after=delete_after,
|
||||
nonce=nonce,
|
||||
allowed_mentions=allowed_mentions,
|
||||
content=content, embed=embed, **kwargs
|
||||
)
|
||||
await message.add_reaction("🗑")
|
||||
|
||||
|
@ -135,16 +117,7 @@ class ContextPlus(commands.Context):
|
|||
await message.delete()
|
||||
return message
|
||||
|
||||
return await super().send(
|
||||
content=content,
|
||||
tts=tts,
|
||||
embed=embed,
|
||||
file=file,
|
||||
files=files,
|
||||
delete_after=delete_after,
|
||||
nonce=nonce,
|
||||
allowed_mentions=allowed_mentions,
|
||||
)
|
||||
return await super().send(content=content, embed=embed, **kwargs)
|
||||
|
||||
@property
|
||||
def session(self) -> aiohttp.ClientSession:
|
||||
|
|
Loading…
Reference in a new issue