artemis/utils/views.py
2024-03-01 20:51:07 +01:00

183 lines
6.3 KiB
Python

from typing import Any, List
import discord
from discord.ext import commands
from utils.common import trim
class BaseView(discord.ui.View):
def __init__(self, ctx: commands.Context, timeout: int = 180):
super().__init__(timeout=timeout)
self.ctx = ctx
async def interaction_check(self, interaction):
if interaction.user == self.ctx.author:
return True
await interaction.response.send_message(
"This interaction cannot be controlled by you, sorry!", ephemeral=True
)
class ConfirmView(BaseView):
def __init__(self, ctx: commands.Context):
super().__init__(ctx, timeout=60)
self.result = None
@discord.ui.button(label="Yes", style=discord.ButtonStyle.green)
async def yes(self, interaction, button):
self.result = True
self.stop()
await self.message.delete()
@discord.ui.button(label="No", style=discord.ButtonStyle.red)
async def no(self, interaction, button):
self.result = False
self.stop()
await self.message.delete()
async def prompt(self, message="Are you sure?", timeout_msg="You took too long!"):
self.message = await self.ctx.send(message, view=self)
if await self.wait():
await self.message.edit(content=timeout_msg, view=None)
return None
return self.result
class ViewPages(BaseView):
def __init__(self, ctx: commands.Context, items: List[Any], timeout: int = 180):
super().__init__(ctx=ctx, timeout=timeout)
self.items = items
self.current_page = 0
self.pages = len(self.items)
self.use_last_and_first = self.pages > 2
def get_kwargs(self, item: discord.Embed | str):
if isinstance(item, discord.Embed):
return {"embed": item}
elif isinstance(item, str):
return {"content": item}
async def start(self):
start_page = self.items[0]
kwargs = self.get_kwargs(start_page)
if self.pages == 1:
return await self.ctx.send(**kwargs)
elif not self.use_last_and_first:
self.remove_item(self.first_page)
self.remove_item(self.last_page)
self.update_labels()
self.message = await self.ctx.send(**kwargs, view=self)
async def update_view(self, interaction: discord.Interaction):
item = self.items[self.current_page]
kwargs = self.get_kwargs(item)
self.update_labels()
if interaction.response.is_done():
if self.message:
await self.message.edit(**kwargs, view=self)
else:
await interaction.response.edit_message(**kwargs, view=self)
def update_labels(self):
self.current_page_display.label = f"{self.current_page + 1}/{self.pages}"
if self.use_last_and_first:
self.first_page.disabled = self.current_page == 0
self.last_page.disabled = (self.current_page + 1) >= self.pages
self.next_page.disabled = False
self.previous_page.disabled = False
if (self.current_page + 1) >= self.pages:
self.next_page.disabled = True
elif self.current_page == 0:
self.previous_page.disabled = True
@discord.ui.button(label="◀◀", style=discord.ButtonStyle.blurple)
async def first_page(self, interaction, button):
self.current_page = 0
await self.update_view(interaction)
@discord.ui.button(label="", style=discord.ButtonStyle.blurple)
async def previous_page(self, interaction, button):
if self.current_page != 0:
self.current_page -= 1
await self.update_view(interaction)
@discord.ui.button(label="PH", style=discord.ButtonStyle.gray, disabled=True)
async def current_page_display(self, interaction, button):
pass
@discord.ui.button(label="", style=discord.ButtonStyle.blurple)
async def next_page(self, interaction, button):
if not (self.current_page + 1) >= self.pages:
self.current_page += 1
await self.update_view(interaction)
@discord.ui.button(label="▶▶", style=discord.ButtonStyle.blurple)
async def last_page(self, interaction, button):
self.current_page = self.pages - 1
await self.update_view(interaction)
async def on_timeout(self):
if self.message:
await self.message.edit(view=None)
class BaseDropdown(discord.ui.Select):
def __init__(self, items: list, label_key, description_key, placeholder: str, max_values: int):
self.items = items
options = []
for i, item in enumerate(self.items[:25]):
options.append(
discord.SelectOption(
label=trim(label_key(item), 100),
description=trim(description_key(item), 100) if description_key else None,
value=str(i),
)
)
super().__init__(placeholder=placeholder, options=options, max_values=max_values)
async def callback(self, interaction: discord.Interaction):
if self.max_values > 1:
self.view.result = [self.items[int(i)] for i in self.values]
else:
result_idx = int(self.values[0])
self.view.result = self.items[result_idx]
self.view.stop()
await self.view.message.delete()
class DropdownView(BaseView):
def __init__(
self,
ctx,
items,
label_key,
description_key=None,
placeholder="Choose one...",
max_values=1,
chunk: bool = False,
):
super().__init__(ctx, timeout=60)
if chunk:
items = items[: 5 * 25]
for i in range(0, len(items), 25):
chunk = items[i : i + 25]
self.add_item(
BaseDropdown(chunk, label_key, description_key, placeholder, len(chunk))
)
else:
self.add_item(BaseDropdown(items, label_key, description_key, placeholder, max_values))
self.result = None
async def prompt(self, message="Which one?", timeout_msg="You took too long!"):
self.message = await self.ctx.send(message, view=self)
if await self.wait():
await self.message.edit(content=timeout_msg, view=None)
return None
return self.result