rac-bot / better_view.py
Bohaska
add rac bot files
cb6590e
import asyncio
from datetime import datetime, timedelta
from nextcord import ui, Interaction
from threading import Timer
from typing import Optional
# the amount of seconds before the interaction becomes invalid
# made for securing the edit of that message, should be higher than 0.
MERCY_SECONDS: int = 30
# this is an abstract class that can be inherited to make a view
# that disables itself after a certain amount of time
# before you cannot respond to an interaction anymore or when timeout has been reached.
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 # dont rely on expires_at because it may not have responded
+ timedelta(minutes=15) # interaction is valid for 15 minutes in case of message being sent
- 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:
# if you responded with defer or edit_message, you can pass the interaction to this method
# to extend the timer to the new interaction
self.init_interaction(interaction, self.message_id, False)
# override
def stop(self) -> None:
self._disable_message_task()
super().stop()
# override
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()