mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 11:18:54 -05:00
[ModLog] Use custom scopes for ModLog Config (#2766)
Modlog was the biggest culprit for seriously large documents in the MongoDB backend, since it stored all cases as nested dicts in the guild scope. So, for example, on the Fortnite server, the guild document for Kowlin's bot had exceeded 8MB. This commit gives each case its own document. It also does the same for casetypes. Not only does it remove the possibility of the document exceeding the maximum size in MongoDB, it's also just more efficient for all backends. Other misc changes: Fixed a bunch of type-hints, and also added more support for when an object related to a case (user, moderator, channel etc.) can't be found (because it was deleted or something rather) Signed-off-by: Toby Harradine <tobyharradine@gmail.com>
This commit is contained in:
parent
52f5d5cd6a
commit
f91d8610ae
@ -117,7 +117,7 @@ def main():
|
|||||||
if cli_flags.dev:
|
if cli_flags.dev:
|
||||||
red.add_cog(Dev())
|
red.add_cog(Dev())
|
||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
modlog._init()
|
loop.run_until_complete(modlog._init())
|
||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
bank._init()
|
bank._init()
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import List, Union
|
from typing import List, Union, Optional, cast
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
@ -30,18 +30,66 @@ __all__ = [
|
|||||||
"reset_cases",
|
"reset_cases",
|
||||||
]
|
]
|
||||||
|
|
||||||
_DEFAULT_GLOBAL = {"casetypes": {}}
|
_conf: Optional[Config] = None
|
||||||
|
|
||||||
_DEFAULT_GUILD = {"mod_log": None, "cases": {}, "casetypes": {}}
|
_CASETYPES = "CASETYPES"
|
||||||
|
_CASES = "CASES"
|
||||||
_conf: Config = None
|
_SCHEMA_VERSION = 2
|
||||||
|
|
||||||
|
|
||||||
def _init():
|
async def _init():
|
||||||
global _conf
|
global _conf
|
||||||
_conf = Config.get_conf(None, 1354799444, cog_name="ModLog")
|
_conf = Config.get_conf(None, 1354799444, cog_name="ModLog")
|
||||||
_conf.register_global(**_DEFAULT_GLOBAL)
|
_conf.register_global(schema_version=1)
|
||||||
_conf.register_guild(**_DEFAULT_GUILD)
|
_conf.register_guild(mod_log=None, casetypes={})
|
||||||
|
_conf.init_custom(_CASETYPES, 1)
|
||||||
|
_conf.init_custom(_CASES, 2)
|
||||||
|
_conf.register_custom(
|
||||||
|
_CASETYPES, default_setting=None, image=None, case_str=None, audit_type=None
|
||||||
|
)
|
||||||
|
_conf.register_custom(
|
||||||
|
_CASES,
|
||||||
|
case_number=None,
|
||||||
|
action_type=None,
|
||||||
|
guild=None,
|
||||||
|
created_at=None,
|
||||||
|
user=None,
|
||||||
|
moderator=None,
|
||||||
|
reason=None,
|
||||||
|
until=None,
|
||||||
|
channel=None,
|
||||||
|
amended_by=None,
|
||||||
|
modified_at=None,
|
||||||
|
message=None,
|
||||||
|
)
|
||||||
|
await _migrate_config(from_version=await _conf.schema_version(), to_version=_SCHEMA_VERSION)
|
||||||
|
|
||||||
|
|
||||||
|
async def _migrate_config(from_version: int, to_version: int):
|
||||||
|
if from_version == to_version:
|
||||||
|
return
|
||||||
|
elif from_version < to_version:
|
||||||
|
# casetypes go from GLOBAL -> casetypes to CASETYPES
|
||||||
|
all_casetypes = await _conf.get_raw("casetypes", default={})
|
||||||
|
if all_casetypes:
|
||||||
|
await _conf.custom(_CASETYPES).set(all_casetypes)
|
||||||
|
|
||||||
|
# cases go from GUILD -> guild_id -> cases to CASES -> guild_id -> cases
|
||||||
|
all_guild_data = await _conf.all_guilds()
|
||||||
|
all_cases = {}
|
||||||
|
for guild_id, guild_data in all_guild_data.items():
|
||||||
|
guild_cases = guild_data.pop("cases", None)
|
||||||
|
if guild_cases:
|
||||||
|
all_cases[str(guild_id)] = guild_cases
|
||||||
|
await _conf.custom(_CASES).set(all_cases)
|
||||||
|
|
||||||
|
# new schema is now in place
|
||||||
|
await _conf.schema_version.set(_SCHEMA_VERSION)
|
||||||
|
|
||||||
|
# migration done, now let's delete all the old stuff
|
||||||
|
await _conf.clear_raw("casetypes")
|
||||||
|
for guild_id in all_guild_data:
|
||||||
|
await _conf.guild(cast(discord.Guild, discord.Object(id=guild_id))).clear_raw("cases")
|
||||||
|
|
||||||
|
|
||||||
class Case:
|
class Case:
|
||||||
@ -53,15 +101,15 @@ class Case:
|
|||||||
guild: discord.Guild,
|
guild: discord.Guild,
|
||||||
created_at: int,
|
created_at: int,
|
||||||
action_type: str,
|
action_type: str,
|
||||||
user: discord.User,
|
user: Union[discord.User, int],
|
||||||
moderator: discord.Member,
|
moderator: discord.User,
|
||||||
case_number: int,
|
case_number: int,
|
||||||
reason: str = None,
|
reason: str = None,
|
||||||
until: int = None,
|
until: int = None,
|
||||||
channel: discord.TextChannel = None,
|
channel: Optional[Union[discord.TextChannel, discord.VoiceChannel, int]] = None,
|
||||||
amended_by: discord.Member = None,
|
amended_by: Optional[discord.User] = None,
|
||||||
modified_at: int = None,
|
modified_at: Optional[int] = None,
|
||||||
message: discord.Message = None,
|
message: Optional[discord.Message] = None,
|
||||||
):
|
):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.guild = guild
|
self.guild = guild
|
||||||
@ -90,7 +138,7 @@ class Case:
|
|||||||
for item in list(data.keys()):
|
for item in list(data.keys()):
|
||||||
setattr(self, item, data[item])
|
setattr(self, item, data[item])
|
||||||
|
|
||||||
await _conf.guild(self.guild).cases.set_raw(str(self.case_number), value=self.to_json())
|
await _conf.custom(_CASES, str(self.guild.id), str(self.case_number)).set(self.to_json())
|
||||||
self.bot.dispatch("modlog_case_edit", self)
|
self.bot.dispatch("modlog_case_edit", self)
|
||||||
|
|
||||||
async def message_content(self, embed: bool = True):
|
async def message_content(self, embed: bool = True):
|
||||||
@ -119,11 +167,7 @@ class Case:
|
|||||||
reason = "**Reason:** Use the `reason` command to add it"
|
reason = "**Reason:** Use the `reason` command to add it"
|
||||||
|
|
||||||
if self.moderator is not None:
|
if self.moderator is not None:
|
||||||
moderator = escape_spoilers(
|
moderator = escape_spoilers(f"{self.moderator} ({self.moderator.id})")
|
||||||
"{}#{} ({})\n".format(
|
|
||||||
self.moderator.name, self.moderator.discriminator, self.moderator.id
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
moderator = "Unknown"
|
moderator = "Unknown"
|
||||||
until = None
|
until = None
|
||||||
@ -151,21 +195,28 @@ class Case:
|
|||||||
datetime.fromtimestamp(self.modified_at).strftime("%Y-%m-%d %H:%M:%S")
|
datetime.fromtimestamp(self.modified_at).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
)
|
)
|
||||||
|
|
||||||
user = escape_spoilers(
|
if isinstance(self.user, int):
|
||||||
filter_invites(
|
user = f"Deleted User#0000 ({self.user})"
|
||||||
"{}#{} ({})\n".format(self.user.name, self.user.discriminator, self.user.id)
|
avatar_url = None
|
||||||
)
|
else:
|
||||||
) # Invites and spoilers get rendered even in embeds.
|
user = escape_spoilers(
|
||||||
|
filter_invites(f"{self.user} ({self.user.id})")
|
||||||
|
) # Invites and spoilers get rendered even in embeds.
|
||||||
|
avatar_url = self.user.avatar_url
|
||||||
|
|
||||||
if embed:
|
if embed:
|
||||||
emb = discord.Embed(title=title, description=reason)
|
emb = discord.Embed(title=title, description=reason)
|
||||||
|
|
||||||
emb.set_author(name=user, icon_url=self.user.avatar_url)
|
if avatar_url is not None:
|
||||||
|
emb.set_author(name=user, icon_url=avatar_url)
|
||||||
emb.add_field(name="Moderator", value=moderator, inline=False)
|
emb.add_field(name="Moderator", value=moderator, inline=False)
|
||||||
if until and duration:
|
if until and duration:
|
||||||
emb.add_field(name="Until", value=until)
|
emb.add_field(name="Until", value=until)
|
||||||
emb.add_field(name="Duration", value=duration)
|
emb.add_field(name="Duration", value=duration)
|
||||||
|
|
||||||
if self.channel:
|
if isinstance(self.channel, int):
|
||||||
|
emb.add_field(name="Channel", value=f"{self.channel} (deleted)", inline=False)
|
||||||
|
elif self.channel is not None:
|
||||||
emb.add_field(name="Channel", value=self.channel.name, inline=False)
|
emb.add_field(name="Channel", value=self.channel.name, inline=False)
|
||||||
if amended_by:
|
if amended_by:
|
||||||
emb.add_field(name="Amended by", value=amended_by)
|
emb.add_field(name="Amended by", value=amended_by)
|
||||||
@ -203,12 +254,15 @@ class Case:
|
|||||||
mod = self.moderator.id
|
mod = self.moderator.id
|
||||||
else:
|
else:
|
||||||
mod = None
|
mod = None
|
||||||
|
if isinstance(self.user, int):
|
||||||
|
user_id = self.user
|
||||||
|
else:
|
||||||
|
user_id = self.user.id
|
||||||
data = {
|
data = {
|
||||||
"case_number": self.case_number,
|
|
||||||
"action_type": self.action_type,
|
"action_type": self.action_type,
|
||||||
"guild": self.guild.id,
|
"guild": self.guild.id,
|
||||||
"created_at": self.created_at,
|
"created_at": self.created_at,
|
||||||
"user": self.user.id,
|
"user": user_id,
|
||||||
"moderator": mod,
|
"moderator": mod,
|
||||||
"reason": self.reason,
|
"reason": self.reason,
|
||||||
"until": self.until,
|
"until": self.until,
|
||||||
@ -220,7 +274,9 @@ class Case:
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def from_json(cls, mod_channel: discord.TextChannel, bot: Red, data: dict):
|
async def from_json(
|
||||||
|
cls, mod_channel: discord.TextChannel, bot: Red, case_number: int, data: dict, **kwargs
|
||||||
|
):
|
||||||
"""Get a Case object from the provided information
|
"""Get a Case object from the provided information
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@ -229,8 +285,14 @@ class Case:
|
|||||||
The mod log channel for the guild
|
The mod log channel for the guild
|
||||||
bot: Red
|
bot: Red
|
||||||
The bot's instance. Needed to get the target user
|
The bot's instance. Needed to get the target user
|
||||||
|
case_number: int
|
||||||
|
The case's number.
|
||||||
data: dict
|
data: dict
|
||||||
The JSON representation of the case to be gotten
|
The JSON representation of the case to be gotten
|
||||||
|
**kwargs
|
||||||
|
Extra attributes for the Case instance which override values
|
||||||
|
in the data dict. These should be complete objects and not
|
||||||
|
IDs, where possible.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -246,31 +308,55 @@ class Case:
|
|||||||
`discord.HTTPException`
|
`discord.HTTPException`
|
||||||
A generic API issue
|
A generic API issue
|
||||||
"""
|
"""
|
||||||
guild = mod_channel.guild
|
guild = kwargs.get("guild") or mod_channel.guild
|
||||||
if data["message"]:
|
|
||||||
try:
|
message = kwargs.get("message")
|
||||||
message = await mod_channel.fetch_message(data["message"])
|
if message is None:
|
||||||
except discord.NotFound:
|
message_id = data.get("message")
|
||||||
|
if message_id is not None:
|
||||||
|
try:
|
||||||
|
message = discord.utils.get(bot.cached_messages, id=message_id)
|
||||||
|
except AttributeError:
|
||||||
|
# bot.cached_messages didn't exist prior to discord.py 1.1.0
|
||||||
|
message = None
|
||||||
|
if message is None:
|
||||||
|
try:
|
||||||
|
message = await mod_channel.fetch_message(message_id)
|
||||||
|
except (discord.NotFound, AttributeError):
|
||||||
|
message = None
|
||||||
|
else:
|
||||||
message = None
|
message = None
|
||||||
user = await bot.fetch_user(data["user"])
|
|
||||||
moderator = guild.get_member(data["moderator"])
|
user_objects = {"user": None, "moderator": None, "amended_by": None}
|
||||||
channel = guild.get_channel(data["channel"])
|
for user_key in tuple(user_objects):
|
||||||
amended_by = guild.get_member(data["amended_by"])
|
user_object = kwargs.get(user_key)
|
||||||
case_guild = bot.get_guild(data["guild"])
|
if user_object is None:
|
||||||
|
user_id = data.get(user_key)
|
||||||
|
if user_id is None:
|
||||||
|
user_object = None
|
||||||
|
else:
|
||||||
|
user_object = bot.get_user(user_id)
|
||||||
|
if user_object is None:
|
||||||
|
try:
|
||||||
|
user_object = await bot.fetch_user(user_id)
|
||||||
|
except discord.NotFound:
|
||||||
|
user_object = user_id
|
||||||
|
user_objects[user_key] = user_object
|
||||||
|
|
||||||
|
channel = kwargs.get("channel") or guild.get_channel(data["channel"]) or data["channel"]
|
||||||
|
case_guild = kwargs.get("guild") or bot.get_guild(data["guild"])
|
||||||
return cls(
|
return cls(
|
||||||
bot=bot,
|
bot=bot,
|
||||||
guild=case_guild,
|
guild=case_guild,
|
||||||
created_at=data["created_at"],
|
created_at=data["created_at"],
|
||||||
action_type=data["action_type"],
|
action_type=data["action_type"],
|
||||||
user=user,
|
case_number=case_number,
|
||||||
moderator=moderator,
|
|
||||||
case_number=data["case_number"],
|
|
||||||
reason=data["reason"],
|
reason=data["reason"],
|
||||||
until=data["until"],
|
until=data["until"],
|
||||||
channel=channel,
|
channel=channel,
|
||||||
amended_by=amended_by,
|
|
||||||
modified_at=data["modified_at"],
|
modified_at=data["modified_at"],
|
||||||
message=message,
|
message=message,
|
||||||
|
**user_objects,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -300,8 +386,8 @@ class CaseType:
|
|||||||
default_setting: bool,
|
default_setting: bool,
|
||||||
image: str,
|
image: str,
|
||||||
case_str: str,
|
case_str: str,
|
||||||
audit_type: str = None,
|
audit_type: Optional[str] = None,
|
||||||
guild: discord.Guild = None,
|
guild: Optional[discord.Guild] = None,
|
||||||
):
|
):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.default_setting = default_setting
|
self.default_setting = default_setting
|
||||||
@ -318,7 +404,7 @@ class CaseType:
|
|||||||
"case_str": self.case_str,
|
"case_str": self.case_str,
|
||||||
"audit_type": self.audit_type,
|
"audit_type": self.audit_type,
|
||||||
}
|
}
|
||||||
await _conf.casetypes.set_raw(self.name, value=data)
|
await _conf.custom(_CASETYPES, self.name).set(data)
|
||||||
|
|
||||||
async def is_enabled(self) -> bool:
|
async def is_enabled(self) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -352,23 +438,27 @@ class CaseType:
|
|||||||
await _conf.guild(self.guild).casetypes.set_raw(self.name, value=enabled)
|
await _conf.guild(self.guild).casetypes.set_raw(self.name, value=enabled)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls, data: dict):
|
def from_json(cls, name: str, data: dict, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
data: dict
|
name : str
|
||||||
The data to create an instance from
|
The casetype's name.
|
||||||
|
data : dict
|
||||||
|
The JSON data to create an instance from
|
||||||
|
**kwargs
|
||||||
|
Values for other attributes of the instance
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
CaseType
|
CaseType
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return cls(**data)
|
return cls(name=name, **data, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
async def get_next_case_number(guild: discord.Guild) -> str:
|
async def get_next_case_number(guild: discord.Guild) -> int:
|
||||||
"""
|
"""
|
||||||
Gets the next case number
|
Gets the next case number
|
||||||
|
|
||||||
@ -379,12 +469,15 @@ async def get_next_case_number(guild: discord.Guild) -> str:
|
|||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
str
|
int
|
||||||
The next case number
|
The next case number
|
||||||
|
|
||||||
"""
|
"""
|
||||||
cases = sorted((await _conf.guild(guild).get_raw("cases")), key=lambda x: int(x), reverse=True)
|
case_numbers = (await _conf.custom(_CASES, guild.id).all()).keys()
|
||||||
return str(int(cases[0]) + 1) if cases else "1"
|
if not case_numbers:
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return max(map(int, case_numbers)) + 1
|
||||||
|
|
||||||
|
|
||||||
async def get_case(case_number: int, guild: discord.Guild, bot: Red) -> Case:
|
async def get_case(case_number: int, guild: discord.Guild, bot: Red) -> Case:
|
||||||
@ -412,11 +505,11 @@ async def get_case(case_number: int, guild: discord.Guild, bot: Red) -> Case:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
case = await _conf.guild(guild).cases.get_raw(str(case_number))
|
case = await _conf.custom(_CASES, str(guild.id), str(case_number)).all()
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise RuntimeError("That case does not exist for guild {}".format(guild.name)) from e
|
raise RuntimeError("That case does not exist for guild {}".format(guild.name)) from e
|
||||||
mod_channel = await get_modlog_channel(guild)
|
mod_channel = await get_modlog_channel(guild)
|
||||||
return await Case.from_json(mod_channel, bot, case)
|
return await Case.from_json(mod_channel, bot, case_number, case)
|
||||||
|
|
||||||
|
|
||||||
async def get_all_cases(guild: discord.Guild, bot: Red) -> List[Case]:
|
async def get_all_cases(guild: discord.Guild, bot: Red) -> List[Case]:
|
||||||
@ -436,12 +529,12 @@ async def get_all_cases(guild: discord.Guild, bot: Red) -> List[Case]:
|
|||||||
A list of all cases for the guild
|
A list of all cases for the guild
|
||||||
|
|
||||||
"""
|
"""
|
||||||
cases = await _conf.guild(guild).get_raw("cases")
|
cases = await _conf.custom(_CASES, str(guild.id)).all()
|
||||||
case_numbers = list(cases.keys())
|
mod_channel = await get_modlog_channel(guild)
|
||||||
case_list = []
|
return [
|
||||||
for case in case_numbers:
|
await Case.from_json(mod_channel, bot, case_number, case_data)
|
||||||
case_list.append(await get_case(case, guild, bot))
|
for case_number, case_data in cases.items()
|
||||||
return case_list
|
]
|
||||||
|
|
||||||
|
|
||||||
async def get_cases_for_member(
|
async def get_cases_for_member(
|
||||||
@ -470,15 +563,13 @@ async def get_cases_for_member(
|
|||||||
------
|
------
|
||||||
ValueError
|
ValueError
|
||||||
If at least one of member or member_id is not provided
|
If at least one of member or member_id is not provided
|
||||||
`discord.NotFound`
|
|
||||||
A user with this ID does not exist.
|
|
||||||
`discord.Forbidden`
|
`discord.Forbidden`
|
||||||
The bot does not have permission to fetch the modlog message which was sent.
|
The bot does not have permission to fetch the modlog message which was sent.
|
||||||
`discord.HTTPException`
|
`discord.HTTPException`
|
||||||
Fetching the user failed.
|
Fetching the user failed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cases = await _conf.guild(guild).get_raw("cases")
|
cases = await _conf.custom(_CASES, str(guild.id)).all()
|
||||||
|
|
||||||
if not (member_id or member):
|
if not (member_id or member):
|
||||||
raise ValueError("Expected a member or a member id to be provided.") from None
|
raise ValueError("Expected a member or a member id to be provided.") from None
|
||||||
@ -487,43 +578,21 @@ async def get_cases_for_member(
|
|||||||
member_id = member.id
|
member_id = member.id
|
||||||
|
|
||||||
if not member:
|
if not member:
|
||||||
member = guild.get_member(member_id)
|
member = bot.get_user(member_id)
|
||||||
if not member:
|
if not member:
|
||||||
member = await bot.fetch_user(member_id)
|
try:
|
||||||
|
member = await bot.fetch_user(member_id)
|
||||||
|
except discord.NotFound:
|
||||||
|
member = member_id
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mod_channel = await get_modlog_channel(guild)
|
modlog_channel = await get_modlog_channel(guild)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
mod_channel = None
|
modlog_channel = None
|
||||||
|
|
||||||
async def make_case(data: dict) -> Case:
|
|
||||||
|
|
||||||
message = None
|
|
||||||
if data["message"] and mod_channel:
|
|
||||||
try:
|
|
||||||
message = await mod_channel.fetch_message(data["message"])
|
|
||||||
except discord.NotFound:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return Case(
|
|
||||||
bot=bot,
|
|
||||||
guild=bot.get_guild(data["guild"]),
|
|
||||||
created_at=data["created_at"],
|
|
||||||
action_type=data["action_type"],
|
|
||||||
user=member,
|
|
||||||
moderator=guild.get_member(data["moderator"]),
|
|
||||||
case_number=data["case_number"],
|
|
||||||
reason=data["reason"],
|
|
||||||
until=data["until"],
|
|
||||||
channel=guild.get_channel(data["channel"]),
|
|
||||||
amended_by=guild.get_member(data["amended_by"]),
|
|
||||||
modified_at=data["modified_at"],
|
|
||||||
message=message,
|
|
||||||
)
|
|
||||||
|
|
||||||
cases = [
|
cases = [
|
||||||
await make_case(case_data)
|
await Case.from_json(modlog_channel, bot, case_number, case_data, user=member, guild=guild)
|
||||||
for case_data in cases.values()
|
for case_number, case_data in cases.items()
|
||||||
if case_data["user"] == member_id
|
if case_data["user"] == member_id
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -536,11 +605,11 @@ async def create_case(
|
|||||||
created_at: datetime,
|
created_at: datetime,
|
||||||
action_type: str,
|
action_type: str,
|
||||||
user: Union[discord.User, discord.Member],
|
user: Union[discord.User, discord.Member],
|
||||||
moderator: discord.Member = None,
|
moderator: Optional[Union[discord.User, discord.Member]] = None,
|
||||||
reason: str = None,
|
reason: Optional[str] = None,
|
||||||
until: datetime = None,
|
until: Optional[datetime] = None,
|
||||||
channel: discord.TextChannel = None,
|
channel: Optional[discord.TextChannel] = None,
|
||||||
) -> Union[Case, None]:
|
) -> Optional[Case]:
|
||||||
"""
|
"""
|
||||||
Creates a new case.
|
Creates a new case.
|
||||||
|
|
||||||
@ -548,36 +617,36 @@ async def create_case(
|
|||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
bot: `Red`
|
bot: Red
|
||||||
The bot object
|
The bot object
|
||||||
guild: `discord.Guild`
|
guild: discord.Guild
|
||||||
The guild the action was taken in
|
The guild the action was taken in
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
The time the action occurred at
|
The time the action occurred at
|
||||||
action_type: str
|
action_type: str
|
||||||
The type of action that was taken
|
The type of action that was taken
|
||||||
user: `discord.User` or `discord.Member`
|
user: Union[discord.User, discord.Member]
|
||||||
The user target by the action
|
The user target by the action
|
||||||
moderator: `discord.Member`
|
moderator: Optional[Union[discord.User, discord.Member]]
|
||||||
The moderator who took the action
|
The moderator who took the action
|
||||||
reason: str
|
reason: Optional[str]
|
||||||
The reason the action was taken
|
The reason the action was taken
|
||||||
until: datetime
|
until: Optional[datetime]
|
||||||
The time the action is in effect until
|
The time the action is in effect until
|
||||||
channel: `discord.TextChannel` or `discord.VoiceChannel`
|
channel: Optional[discord.TextChannel]
|
||||||
The channel the action was taken in
|
The channel the action was taken in
|
||||||
"""
|
"""
|
||||||
case_type = await get_casetype(action_type, guild)
|
case_type = await get_casetype(action_type, guild)
|
||||||
if case_type is None:
|
if case_type is None:
|
||||||
return None
|
return
|
||||||
|
|
||||||
if not await case_type.is_enabled():
|
if not await case_type.is_enabled():
|
||||||
return None
|
return
|
||||||
|
|
||||||
if user == bot.user:
|
if user == bot.user:
|
||||||
return None
|
return
|
||||||
|
|
||||||
next_case_number = int(await get_next_case_number(guild))
|
next_case_number = await get_next_case_number(guild)
|
||||||
|
|
||||||
case = Case(
|
case = Case(
|
||||||
bot,
|
bot,
|
||||||
@ -594,12 +663,12 @@ async def create_case(
|
|||||||
modified_at=None,
|
modified_at=None,
|
||||||
message=None,
|
message=None,
|
||||||
)
|
)
|
||||||
await _conf.guild(guild).cases.set_raw(str(next_case_number), value=case.to_json())
|
await _conf.custom(_CASES, str(guild.id), str(next_case_number)).set(case.to_json())
|
||||||
bot.dispatch("modlog_case_create", case)
|
bot.dispatch("modlog_case_create", case)
|
||||||
return case
|
return case
|
||||||
|
|
||||||
|
|
||||||
async def get_casetype(name: str, guild: discord.Guild = None) -> Union[CaseType, None]:
|
async def get_casetype(name: str, guild: Optional[discord.Guild] = None) -> Optional[CaseType]:
|
||||||
"""
|
"""
|
||||||
Gets the case type
|
Gets the case type
|
||||||
|
|
||||||
@ -607,22 +676,21 @@ async def get_casetype(name: str, guild: discord.Guild = None) -> Union[CaseType
|
|||||||
----------
|
----------
|
||||||
name: str
|
name: str
|
||||||
The name of the case type to get
|
The name of the case type to get
|
||||||
guild: discord.Guild
|
guild: Optional[discord.Guild]
|
||||||
If provided, sets the case type's guild attribute to this guild
|
If provided, sets the case type's guild attribute to this guild
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
CaseType or None
|
Optional[CaseType]
|
||||||
"""
|
"""
|
||||||
casetypes = await _conf.get_raw("casetypes")
|
try:
|
||||||
if name in casetypes:
|
data = await _conf.custom(_CASETYPES, name).all()
|
||||||
data = casetypes[name]
|
except KeyError:
|
||||||
data["name"] = name
|
return
|
||||||
casetype = CaseType.from_json(data)
|
else:
|
||||||
|
casetype = CaseType.from_json(name, data)
|
||||||
casetype.guild = guild
|
casetype.guild = guild
|
||||||
return casetype
|
return casetype
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
async def get_all_casetypes(guild: discord.Guild = None) -> List[CaseType]:
|
async def get_all_casetypes(guild: discord.Guild = None) -> List[CaseType]:
|
||||||
@ -635,15 +703,10 @@ async def get_all_casetypes(guild: discord.Guild = None) -> List[CaseType]:
|
|||||||
A list of case types
|
A list of case types
|
||||||
|
|
||||||
"""
|
"""
|
||||||
casetypes = await _conf.get_raw("casetypes", default={})
|
return [
|
||||||
typelist = []
|
CaseType.from_json(name, data, guild=guild)
|
||||||
for ct in casetypes.keys():
|
for name, data in await _conf.custom(_CASETYPES).all()
|
||||||
data = casetypes[ct]
|
]
|
||||||
data["name"] = ct
|
|
||||||
casetype = CaseType.from_json(data)
|
|
||||||
casetype.guild = guild
|
|
||||||
typelist.append(casetype)
|
|
||||||
return typelist
|
|
||||||
|
|
||||||
|
|
||||||
async def register_casetype(
|
async def register_casetype(
|
||||||
@ -822,7 +885,7 @@ async def set_modlog_channel(
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def reset_cases(guild: discord.Guild) -> bool:
|
async def reset_cases(guild: discord.Guild) -> None:
|
||||||
"""
|
"""
|
||||||
Wipes all modlog cases for the specified guild
|
Wipes all modlog cases for the specified guild
|
||||||
|
|
||||||
@ -831,14 +894,8 @@ async def reset_cases(guild: discord.Guild) -> bool:
|
|||||||
guild: `discord.Guild`
|
guild: `discord.Guild`
|
||||||
The guild to reset cases for
|
The guild to reset cases for
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
bool
|
|
||||||
`True` if successful
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
await _conf.guild(guild).cases.set({})
|
await _conf.custom(_CASES, str(guild.id)).clear()
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _strfdelta(delta):
|
def _strfdelta(delta):
|
||||||
|
|||||||
@ -5,11 +5,11 @@ __all__ = ["mod"]
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mod(config, monkeypatch):
|
async def mod(config, monkeypatch):
|
||||||
from redbot.core import Config
|
from redbot.core import Config
|
||||||
|
|
||||||
with monkeypatch.context() as m:
|
with monkeypatch.context() as m:
|
||||||
m.setattr(Config, "get_conf", lambda *args, **kwargs: config)
|
m.setattr(Config, "get_conf", lambda *args, **kwargs: config)
|
||||||
|
|
||||||
modlog._init()
|
await modlog._init()
|
||||||
return modlog
|
return modlog
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user