|
|
import asyncio |
|
|
from datetime import datetime, timedelta |
|
|
from nextcord import ui, Interaction |
|
|
from threading import Timer |
|
|
from typing import Optional |
|
|
|
|
|
|
|
|
|
|
|
MERCY_SECONDS: int = 30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AutoDisableView(ui.View): |
|
|
interaction: Optional[Interaction] = None |
|
|
message_id: Optional[int] = None |
|
|
_timer: Optional[Timer] = None |
|
|
|
|
|
def init_interaction( |
|
|
self, |
|
|
interaction: Interaction, |
|
|
message_id: Optional[int] = None, |
|
|
start_timer: bool = True |
|
|
) -> None: |
|
|
''' |
|
|
Initializes the interaction and message_id attributes. |
|
|
This is required for the view to work properly. |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
interaction: Interaction |
|
|
The interaction that the view is attached to. |
|
|
Needs to be the interaction that currently was received and is being processed. |
|
|
message_id: Optional[int] |
|
|
The message id of the message if the response is a followup. |
|
|
start_timer: bool |
|
|
Whether to start the timer that will disable the view |
|
|
after the timeout has been reached. Defaults to True.''' |
|
|
|
|
|
self.interaction = interaction |
|
|
self.__stopped: asyncio.Future[bool] = interaction.client.loop.create_future() |
|
|
self.message_id = message_id |
|
|
if start_timer: |
|
|
self.disable_message_start_timer() |
|
|
|
|
|
def disable_message_start_timer(self, interaction: Optional[Interaction] = None) -> None: |
|
|
''' |
|
|
Starts the timer that will disable the message after the timeout has been reached. |
|
|
This is called automatically when init_interaction() is called with `start_timer` set to True.''' |
|
|
seconds = ( |
|
|
self.interaction.created_at |
|
|
+ timedelta(minutes=15) |
|
|
- datetime.utcnow().replace(tzinfo=self.interaction.expires_at.tzinfo) |
|
|
).total_seconds() - MERCY_SECONDS |
|
|
if self._timer: |
|
|
self._timer.cancel() |
|
|
self._timer = Timer(seconds, self._disable_message_task) |
|
|
self._timer.start() |
|
|
if interaction: |
|
|
|
|
|
|
|
|
self.init_interaction(interaction, self.message_id, False) |
|
|
|
|
|
|
|
|
def stop(self) -> None: |
|
|
self._disable_message_task() |
|
|
super().stop() |
|
|
|
|
|
|
|
|
def _dispatch_timeout(self) -> None: |
|
|
if self.__stopped.done(): |
|
|
return |
|
|
self.interaction.client.loop.create_task(self._disable_view_on_timeout(), |
|
|
name=f"discord-ui-disable-view-timeout-{self.id}") |
|
|
super()._dispatch_timeout() |
|
|
|
|
|
def _disable_message_task(self) -> None: |
|
|
self.interaction.client.loop.create_task(self._disable_message_buttons()) |
|
|
|
|
|
def _disable_children(self) -> None: |
|
|
for child in self.children: |
|
|
child.disabled = True |
|
|
|
|
|
async def _disable_message_buttons(self) -> None: |
|
|
self._disable_children() |
|
|
if self.message_id: |
|
|
await self.interaction.followup.edit_message(self.message_id, view=self) |
|
|
else: |
|
|
await self.interaction.edit_original_message(view=self) |
|
|
self._timer.cancel() |
|
|
|
|
|
async def _disable_view_on_timeout(self) -> None: |
|
|
await self._disable_message_buttons() |
|
|
|