from typing import TYPE_CHECKING, Self, Any
from . import utils
from .channel import PartialChannel
from .enums import (
AutoModRulePresetType, AutoModRuleTriggerType,
AutoModRuleActionType, AutoModRuleEventType
)
from .object import PartialBase, Snowflake
from .role import PartialRole
from .user import PartialUser
if TYPE_CHECKING:
from .guild import PartialGuild, Guild
from .http import DiscordAPI
MISSING = utils.MISSING
__all__ = (
"AutoModRuleTriggers",
"AutoModRuleAction",
"PartialAutoModRule",
"AutoModRule",
)
[docs]
class AutoModRuleTriggers:
def __init__(
self,
*,
keyword_filter: list[str] | None = None,
regex_patterns: list[str] | None = None,
presets: list[AutoModRulePresetType] | None = None,
allow_list: list[str] | None = None,
mention_total_limit: int | None = None,
mention_raid_protection_enabled: bool = False
):
self.keyword_filter: list[str] | None = keyword_filter
self.regex_patterns: list[str] | None = regex_patterns
self.presets: list[AutoModRulePresetType] | None = presets
self.allow_list: list[str] | None = allow_list
self.mention_total_limit: int | None = mention_total_limit
self.mention_raid_protection_enabled: bool = mention_raid_protection_enabled
def __repr__(self) -> str:
output = "<AutoModTriggers "
if self.keyword_filter:
output += f"keyword_filter={self.keyword_filter}"
if self.regex_patterns:
output += f"regex_patterns={self.regex_patterns}"
if self.presets:
output += f"presets={self.presets}"
if self.allow_list:
output += f"allow_list={self.allow_list}"
if self.mention_total_limit:
output += f"mention_total_limit={self.mention_total_limit}"
if self.mention_raid_protection_enabled:
output += "mention_raid_protection_enabled=True"
return f"{output}>"
[docs]
def to_dict(self) -> dict:
payload = {}
if self.keyword_filter is not None:
payload["keyword_filter"] = [str(g) for g in self.keyword_filter]
if self.regex_patterns is not None:
payload["regex_patterns"] = [str(g) for g in self.regex_patterns]
if self.presets is not None:
payload["presets"] = [int(g) for g in self.presets]
if self.allow_list is not None:
payload["allow_list"] = [str(g) for g in self.allow_list]
if self.mention_total_limit is not None:
payload["mention_total_limit"] = int(self.mention_total_limit)
if self.mention_raid_protection_enabled is True:
payload["mention_raid_protection_enabled"] = True
return payload
[docs]
@classmethod
def from_dict(cls, data: dict) -> Self:
return cls(
keyword_filter=data.get("keyword_filter", None),
regex_patterns=data.get("regex_patterns", None),
presets=data.get("presets", None),
allow_list=data.get("allow_list", None),
mention_total_limit=data.get("mention_total_limit", None),
mention_raid_protection_enabled=data.get("mention_raid_protection_enabled", False)
)
[docs]
class AutoModRuleAction:
def __init__(
self,
*,
type: AutoModRuleActionType,
channel_id: Snowflake | int | None = None,
duration_seconds: int | None = None,
custom_message: str | None = None,
):
self.type: AutoModRuleActionType = type
self.channel_id = channel_id
self.duration_seconds: int | None = duration_seconds
self.custom_message: str | None = custom_message
if self.duration_seconds is not None:
# 4 Week limit, let's just handle it
self.duration_seconds = min(self.duration_seconds, 2419200)
def __repr__(self) -> str:
output = f"<AutoModAction type={self.type}"
if self.channel_id:
output += f" channel_id={self.channel_id}"
if self.duration_seconds:
output += f" duration_seconds={self.duration_seconds}"
if self.custom_message:
output += f" custom_message={self.custom_message}"
return f"{output}>"
[docs]
def to_dict(self) -> dict:
payload = {
"type": int(self.type),
"metadata": {}
}
if self.channel_id is not None:
payload["metadata"]["channel_id"] = str(self.channel_id)
if self.duration_seconds is not None:
payload["metadata"]["duration_seconds"] = int(self.duration_seconds)
if self.custom_message is not None:
payload["metadata"]["custom_message"] = self.custom_message
return payload
[docs]
@classmethod
def from_dict(cls, data: dict) -> Self:
_metadata = data.get("metadata", {})
return cls(
type=AutoModRuleActionType(data["type"]),
channel_id=utils.get_int(_metadata, "channel_id"),
duration_seconds=_metadata.get("duration_seconds", None),
custom_message=_metadata.get("custom_message", None)
)
[docs]
@classmethod
def create_message(cls, message: str) -> Self:
return cls(
type=AutoModRuleActionType.block_message,
custom_message=str(message)
)
[docs]
@classmethod
def create_alert_location(cls, channel: Snowflake | int) -> Self:
return cls(
type=AutoModRuleActionType.send_alert_message,
channel_id=int(channel)
)
[docs]
@classmethod
def create_timeout(cls, seconds: int) -> Self:
return cls(
type=AutoModRuleActionType.timeout,
duration_seconds=int(seconds)
)
[docs]
class PartialAutoModRule(PartialBase):
def __init__(
self,
*,
state: "DiscordAPI",
id: int,
guild_id: int
):
super().__init__(id=int(id))
self._state = state
self.guild_id: int = guild_id
def __repr__(self) -> str:
return f"<PartialAutoModRule id={self.id}>"
@property
def guild(self) -> "Guild | PartialGuild | None":
""" `PartialGuild`: The guild object this event is in """
if not self.guild_id:
return None
cache = self._state.cache.get_guild(self.guild_id)
if cache:
return cache
from .guild import PartialGuild
return PartialGuild(state=self._state, id=self.guild_id)
[docs]
async def fetch(self) -> "AutoModRule":
""" `AutoModRule`: Fetches more information about the automod rule """
r = await self._state.query(
"GET",
f"/guilds/{self.guild_id}/auto-moderation/rules/{self.id}"
)
return AutoModRule(
state=self._state,
data=r.response
)
[docs]
async def delete(self, *, reason: str | None = None) -> None:
"""
Delete the automod rule
Parameters
----------
reason: `str | None`
Reason for deleting the automod
"""
await self._state.query(
"DELETE",
f"/guilds/{self.guild_id}/auto-moderation/rules/{self.id}",
reason=reason,
res_method="text"
)
[docs]
async def edit(
self,
*,
name: str | None = MISSING,
event_type: AutoModRuleEventType | int | None = MISSING,
keyword_filter: list[str] | None = MISSING,
regex_patterns: list[str] | None = MISSING,
presets: list[AutoModRulePresetType] | None = MISSING,
allow_list: list[str] | None = MISSING,
mention_total_limit: int | None = MISSING,
mention_raid_protection_enabled: bool = MISSING,
alert_channel: Snowflake | int | None = MISSING,
timeout_seconds: int | None = MISSING,
message: str | None = MISSING,
enabled: bool = MISSING,
exempt_roles: list[Snowflake | int] | None = MISSING,
exempt_channels: list[Snowflake | int] | None = MISSING,
reason: str | None = None
) -> "AutoModRule":
"""
Create an automod rule
Parameters
----------
name: `str`
Name of the automod
event_type: `AutoModRuleEventType | int`
What type of event
keyword_filter: `list[str] | None`
Keywords to filter
regex_patterns: `list[str] | None`
Keywords in regex pattern to filter
presets: `list[AutoModRulePresetType] | None`
Automod presets to include
allow_list: `list[str] | None`
List of keywords that are allowed
mention_total_limit: `int | None`
How many unique mentions allowed before trigger
mention_raid_protection_enabled: `bool`
If this should apply for raids
alert_channel: `Snowflake | int | None`
Where the action should be logged
timeout_seconds: `int | None`
How many seconds the user in question should be timed out
message: `str | None`
What message the user gets when action is taken
enabled: `bool`
If the automod should be enabled or not
exempt_roles: `list[Snowflake | int] | None`
Which roles are allowed to bypass
exempt_channels: `list[Snowflake | int] | None`
Which channels are allowed to bypass
reason: `str | None`
Reason for editing the automod
Returns
-------
`AutoModRule`
The automod that was just edited
"""
payload = {}
if name is not MISSING:
payload["name"] = str(name)
if event_type is not MISSING:
payload["event_type"] = int(event_type or -1)
if any([
g is not MISSING
for g in [alert_channel, timeout_seconds, message]
]):
payload["actions"] = []
if alert_channel is not MISSING:
payload["actions"].append(
AutoModRuleAction.create_alert_location(
int(alert_channel or -1)
).to_dict()
)
if timeout_seconds is not MISSING:
payload["actions"].append(
AutoModRuleAction.create_timeout(
int(timeout_seconds or -1)
).to_dict()
)
if message is not MISSING:
payload["actions"].append(
AutoModRuleAction.create_message(
str(message)
).to_dict()
)
if enabled is not MISSING:
payload["enabled"] = bool(enabled)
if isinstance(exempt_roles, list):
payload["exempt_roles"] = [str(int(g)) for g in exempt_roles]
if isinstance(exempt_channels, list):
payload["exempt_channels"] = [str(int(g)) for g in exempt_channels]
_trigger_payload: dict[str, Any] = {
k: v for k, v in {
"keyword_filter": keyword_filter,
"regex_patterns": regex_patterns,
"presets": presets,
"allow_list": allow_list,
"mention_total_limit": mention_total_limit,
"mention_raid_protection_enabled": mention_raid_protection_enabled
}.items()
if v is not MISSING
}
if _trigger_payload:
payload["trigger_metadata"] = AutoModRuleTriggers(
**_trigger_payload
).to_dict()
r = await self._state.query(
"PATCH",
f"/guilds/{self.guild_id}/auto-moderation/rules/{self.id}",
json=payload,
reason=reason
)
return AutoModRule(
state=self._state,
data=r.response
)
[docs]
class AutoModRule(PartialAutoModRule):
def __init__(
self,
*,
state: "DiscordAPI",
data: dict
):
super().__init__(
state=state,
id=int(data["id"]),
guild_id=int(data["guild_id"])
)
self.name: str = data["name"]
self.creator_id: int = int(data["creator_id"])
self.event_type: AutoModRuleEventType = AutoModRuleEventType(data["event_type"])
self.trigger_type: AutoModRuleTriggerType = AutoModRuleTriggerType(data["trigger_type"])
self.actions: list[AutoModRuleAction] = [
AutoModRuleAction.from_dict(g)
for g in data["actions"]
]
self.trigger_metadata: AutoModRuleTriggers | None = None
self.enabled: bool = data.get("enabled", False)
self.exempt_roles: list[PartialRole] = [
PartialRole(state=state, id=int(g), guild_id=self.guild_id)
for g in data.get("exempt_roles", [])
]
self.exempt_channels: list[PartialChannel] = [
PartialChannel(state=state, id=int(g), guild_id=self.guild_id)
for g in data.get("exempt_channels", [])
]
self._from_data(data)
def __repr__(self) -> str:
return f"<AutoModRule id={self.id} name={self.name}>"
def __str__(self) -> str:
return self.name
def _from_data(self, data: dict) -> None:
if data.get("trigger_metadata", None):
self.trigger_metadata = AutoModRuleTriggers.from_dict(
data["trigger_metadata"]
)
@property
def creator(self) -> PartialUser:
""" `PartialUser`: The user that created the automod rule in User object form """
return PartialUser(
state=self._state,
id=self.creator_id
)