Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions discord/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
from .flags import *
from .guild import *
from .http import *
from .incidents import *
from .integrations import *
from .interactions import *
from .invite import *
Expand Down
64 changes: 63 additions & 1 deletion discord/guild.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from __future__ import annotations

import copy
import datetime
import unicodedata
from typing import (
TYPE_CHECKING,
Expand Down Expand Up @@ -68,6 +69,7 @@
from .errors import ClientException, InvalidArgument, InvalidData
from .file import File
from .flags import SystemChannelFlags
from .incidents import IncidentsData
from .integrations import Integration, _integration_factory
from .invite import Invite
from .iterators import (
Expand Down Expand Up @@ -112,7 +114,13 @@
from .template import Template
from .types.guild import Ban as BanPayload
from .types.guild import Guild as GuildPayload
from .types.guild import GuildFeature, MFALevel
from .types.guild import (
GuildFeature,
)
from .types.guild import IncidentsData as IncidentsDataPayload
from .types.guild import (
MFALevel,
)
from .types.member import Member as MemberPayload
from .types.threads import Thread as ThreadPayload
from .types.voice import GuildVoiceState
Expand Down Expand Up @@ -290,6 +298,7 @@ class Guild(Hashable):
"approximate_member_count",
"approximate_presence_count",
"_sounds",
"incidents",
)

_PREMIUM_GUILD_LIMITS: ClassVar[dict[int | None, _GuildLimit]] = {
Expand Down Expand Up @@ -569,6 +578,13 @@ def _from_data(self, guild: GuildPayload) -> None:
sound = SoundboardSound(state=state, http=state.http, data=sound)
self._add_sound(sound)

incidents_payload = guild.get("incidents_data")
self.incidents_data: IncidentsData | None = (
IncidentsData(data=incidents_payload, guild=self)
if incidents_payload is not None
else None
)

def _add_sound(self, sound: SoundboardSound) -> None:
self._sounds[sound.id] = sound
self._state._add_sound(sound)
Expand Down Expand Up @@ -4405,6 +4421,52 @@ async def edit_onboarding(
new = await self._state.http.edit_onboarding(self.id, fields, reason=reason)
return Onboarding(data=new, guild=self)

async def modify_incident_actions(
self,
*,
invites_disabled_until: datetime.datetime | None = MISSING,
dms_disabled_until: datetime.datetime | None = MISSING,
reason: str | None = MISSING,
) -> IncidentsData:
"""|coro|

Modify the guild's incident actions (invites or DMs disabled until a
given ISO8601 timestamp). Supplying ``None`` disables the respective
action. Requires :attr:`~Permissions.manage_guild`.

Parameters
----------
invites_disabled_until: Optional[:class:`datetime.datetime`]
ISO8601 timestamp indicating when invites will be enabled again, or
``None`` to disable.
dms_disabled_until: Optional[:class:`datetime.datetime`]
ISO8601 timestamp indicating when DMs will be enabled again, or
``None`` to disable.
reason: Optional[:class:`str`]
Audit log reason.

Returns
-------
:class:`IncidentsData`
The updated incidents data for the guild.
"""

fields: IncidentsDataPayload = {}
if invites_disabled_until is not MISSING:
fields["invites_disabled_until"] = (
invites_disabled_until and invites_disabled_until.isoformat()
)

if dms_disabled_until is not MISSING:
fields["dms_disabled_until"] = (
dms_disabled_until and dms_disabled_until.isoformat()
)

new = await self._state.http.modify_guild_incident_actions(
self.id, fields, reason=reason
)
return IncidentsData(data=new, guild=self)

async def delete_auto_moderation_rule(
self,
id: int,
Expand Down
15 changes: 14 additions & 1 deletion discord/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
from .file import VoiceMessage
from .gateway import DiscordClientWebSocketResponse
from .soundboard import PartialSoundboardSound, SoundboardSound
from .utils import MISSING, warn_deprecated
from .utils import MISSING

_log = logging.getLogger(__name__)

Expand Down Expand Up @@ -3135,6 +3135,19 @@ def edit_onboarding(
reason=reason,
)

def modify_guild_incident_actions(
self,
guild_id: Snowflake,
payload: guild.ModifyIncidents,
*,
reason: str | None = None,
) -> Response[guild.IncidentsData]:
return self.request(
Route("PUT", "/guilds/{guild_id}/incident-actions", guild_id=guild_id),
json=payload,
reason=reason,
)

# Polls

def expire_poll(
Expand Down
74 changes: 74 additions & 0 deletions discord/incidents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from __future__ import annotations

import datetime
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from .guild import Guild
from .types.guild import IncidentsData as IncidentsDataPayload

__all__ = ("IncidentsData",)


class IncidentsData:
"""Represents the incidents data object for a guild.
Attributes
----------
invites_disabled_until: Optional[datetime.datetime]
When invites will be enabled again as a :class:`datetime.datetime`, or ``None``.
dms_disabled_until: Optional[datetime.datetime]
When direct messages will be enabled again as a :class:`datetime.datetime`, or ``None``.
dm_spam_detected_at: Optional[datetime.datetime]
When DM spam was detected, or ``None``.
raid_detected_at: Optional[datetime.datetime]
When a raid was detected, or ``None``.
"""

__slots__ = (
"guild",
"invites_disabled_until",
"dms_disabled_until",
"dm_spam_detected_at",
"raid_detected_at",
)

def __init__(self, data: IncidentsDataPayload, guild: Guild | None = None):
self.guild = guild

self.invites_disabled_until: datetime.datetime | None = (
datetime.datetime.fromisoformat(s)
if (s := data.get("invites_disabled_until"))
else None
)

self.dms_disabled_until: datetime.datetime | None = (
datetime.datetime.fromisoformat(s)
if (s := data.get("dms_disabled_until"))
else None
)

self.dm_spam_detected_at: datetime.datetime | None = (
datetime.datetime.fromisoformat(s)
if (s := data.get("dm_spam_detected_at"))
else None
)

self.raid_detected_at: datetime.datetime | None = (
datetime.datetime.fromisoformat(s)
if (s := data.get("raid_detected_at"))
else None
)

def to_dict(self) -> IncidentsDataPayload:
"""Converts this object back to a raw payload suitable for API use."""
return {
"invites_disabled_until": self.invites_disabled_until
and self.invites_disabled_until.isoformat(),
"dms_disabled_until": self.dms_disabled_until
and self.dms_disabled_until.isoformat(),
"dm_spam_detected_at": self.dm_spam_detected_at
and self.dm_spam_detected_at.isoformat(),
"raid_detected_at": self.raid_detected_at
and self.raid_detected_at.isoformat(),
}
13 changes: 13 additions & 0 deletions discord/types/guild.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ class Guild(_BaseGuildPreview):
premium_tier: PremiumTier
preferred_locale: str
public_updates_channel_id: Snowflake | None
incidents_data: IncidentsData | None


class InviteGuild(Guild, total=False):
Expand Down Expand Up @@ -197,3 +198,15 @@ class GuildMFAModify(TypedDict):
class GuildBulkBan(TypedDict):
banned_users: list[Snowflake]
failed_users: list[Snowflake]


class IncidentsData(TypedDict, total=False):
invites_disabled_until: str | None
dms_disabled_until: str | None
dm_spam_detected_at: str | None
raid_detected_at: str | None


class ModifyIncidents(TypedDict, total=False):
invites_disabled_until: str | None
dms_disabled_until: str | None