mirror of
https://github.com/UiO-Gaming/ShiteMusicBot.git
synced 2026-11-05 13:56:51 +01:00
Bug fixes for selector and scroller
This commit is contained in:
1 parent
df1745c2a8
commit
6b955b8f5f
6 files changed
+206
-133
No files matched your search
@@ -1,9 +1,7 @@
|
||||
# Discord Packages
|
||||
from discord.ext import commands
|
||||
|
||||
from musicbot.utils.userinteraction.scroller import ScrollClearSettings
|
||||
|
||||
from ...utils.userinteraction import Scroller
|
||||
from ...utils.userinteraction.scroller import Scroller, ScrollClear
|
||||
from .helpformatter import coghelper, commandhelper, helper, prefix_cleaner
|
||||
|
||||
|
||||
@@ -34,6 +32,5 @@ class Help(commands.Cog):
|
||||
else:
|
||||
paginator = await coghelper(ctx, thing)
|
||||
|
||||
scroller = Scroller(ctx, paginator,
|
||||
ScrollClearSettings.OnTimeout | ScrollClearSettings.OnInteractionExit)
|
||||
await scroller.start_scrolling()
|
||||
scroller = Scroller(ctx, paginator)
|
||||
await scroller.start_scrolling(ScrollClear.OnTimeout | ScrollClear.OnInteractionExit)
|
||||
+142
-85
@@ -1,4 +1,5 @@
|
||||
# Discord Packages
|
||||
from os import remove
|
||||
import discord
|
||||
import lavalink
|
||||
import asyncio
|
||||
@@ -7,8 +8,9 @@ from discord.ext import commands, tasks
|
||||
import re
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from musicbot.utils.mixplayer.player import MixPlayer
|
||||
|
||||
from musicbot.utils.userinteraction.scroller import ScrollClearSettings
|
||||
from musicbot.utils.userinteraction.scroller import ScrollClear
|
||||
|
||||
from ...utils import checks, thumbnailer, timeformatter
|
||||
from ...utils.userinteraction import QueuePaginator, Scroller, Selector, TextPaginator
|
||||
@@ -164,8 +166,7 @@ class Music(commands.Cog):
|
||||
|
||||
if not player.is_playing:
|
||||
await player.play()
|
||||
|
||||
|
||||
|
||||
@commands.command(name='seek')
|
||||
@checks.dj_or(alone=True, track_requester=True)
|
||||
@require_voice_connection()
|
||||
@@ -229,33 +230,29 @@ class Music(commands.Cog):
|
||||
|
||||
embed = discord.Embed(color=ctx.me.color, title='{stop}')
|
||||
await ctx.send(embed=ctx.localizer.format_embed(embed))
|
||||
|
||||
|
||||
|
||||
@commands.command(name='now')
|
||||
@require_playing()
|
||||
async def _now(self, ctx):
|
||||
embed = self.get_current_song_embed(ctx, include_time=True)
|
||||
embed = ctx.localizer.format_embed(embed)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
|
||||
|
||||
@commands.command(name='queue')
|
||||
@require_queue(require_member_queue=True)
|
||||
async def _queue(self, ctx, *, member: discord.Member = None):
|
||||
""" Shows the player's queue. """
|
||||
player = self.bot.lavalink.player_manager.get(ctx.guild.id)
|
||||
pagified_queue = QueuePaginator(ctx.localizer, player, color=ctx.me.color, member=member)
|
||||
scroller = Scroller(ctx, pagified_queue, ScrollClearSettings.OnInteractionExit | ScrollClearSettings.OnTimeout)
|
||||
await scroller.start_scrolling()
|
||||
|
||||
|
||||
scroller = Scroller(ctx, pagified_queue)
|
||||
await scroller.start_scrolling(ScrollClear.OnInteractionExit | ScrollClear.OnTimeout)
|
||||
|
||||
@commands.command(name='myqueue')
|
||||
@require_queue(require_author_queue=True)
|
||||
async def _myqueue(self, ctx):
|
||||
""" Shows your queue. """
|
||||
await self._queue(ctx, member=ctx.author)
|
||||
|
||||
|
||||
|
||||
@commands.command(name='pause')
|
||||
@checks.dj_or(alone=True)
|
||||
@require_voice_connection()
|
||||
@@ -340,26 +337,55 @@ class Music(commands.Cog):
|
||||
@require_voice_connection()
|
||||
@require_queue(require_author_queue=True)
|
||||
async def _remove(self, ctx):
|
||||
player = self.bot.lavalink.player_manager.get(ctx.guild.id)
|
||||
player: MixPlayer = self.bot.lavalink.player_manager.get(ctx.guild.id)
|
||||
|
||||
user_queue = player.user_queue(ctx.author.id, dual=True)
|
||||
functions = [player.remove_user_track]*len(user_queue)
|
||||
arguments = [(ctx.author.id, i) for i in range(len(user_queue))]
|
||||
identifiers = []
|
||||
|
||||
for index, temp in enumerate(user_queue):
|
||||
track, globpos = temp
|
||||
identifiers.append(ctx.localizer.format_str("{queue.usertrack}", _index=index+1,
|
||||
_globalindex=globpos+1, _title=track.title,
|
||||
_uri=track.uri))
|
||||
tracks_to_remove = []
|
||||
async def update_remove_list(tracks_list, track):
|
||||
if track in tracks_list:
|
||||
tracks_list.remove(track)
|
||||
else:
|
||||
tracks_list.append(track)
|
||||
|
||||
selector = Selector(ctx, identifiers, functions, arguments, num_selections=5, color=0xFF0000,
|
||||
title='Remove song plz')
|
||||
message, _, removed = await selector.start_scrolling()
|
||||
if removed:
|
||||
await message.edit(content=ctx.localizer.format_str("{remove}", _title=removed.title), embed=None)
|
||||
else:
|
||||
await message.delete()
|
||||
# The callback from the selector takes a discord interaction and
|
||||
# the button class. We wrap the track remove callback to handle those
|
||||
# arguments and update button color
|
||||
def add_change_button_color(func, *args):
|
||||
async def inner(_, button: SelectorButton):
|
||||
if button.style == discord.ButtonStyle.red:
|
||||
button.style = discord.ButtonStyle.gray
|
||||
else:
|
||||
button.style = discord.ButtonStyle.red
|
||||
return await func(*args)
|
||||
return inner
|
||||
|
||||
selector_buttons = []
|
||||
for index, (track, _) in enumerate(user_queue, start=1):
|
||||
selector_buttons.append(
|
||||
SelectorItem(f'`{index}` [{track.title}]({track.uri})`', str(index),
|
||||
add_change_button_color(update_remove_list, tracks_to_remove, track)))
|
||||
|
||||
remove_selector = Selector2(ctx, selector_buttons, terminate_on_select=False, check_for_stop=True,
|
||||
color=ctx.me.color, title='Select songs to remove')
|
||||
await remove_selector.start_scrolling(ScrollClear.OnTimeout | ScrollClear.OnInteractionExit)
|
||||
|
||||
# If any tracks were removed create a scroller for navigating them
|
||||
if tracks_to_remove:
|
||||
paginator = TextPaginator(color=ctx.me.color, title="Removed")
|
||||
|
||||
# If the user spent a long time selecting songs the queue might have changed
|
||||
user_queue = player.user_queue(ctx.author.id, dual=False)
|
||||
track_indexes_to_remove = [i for i, track in enumerate(user_queue) if track in tracks_to_remove]
|
||||
|
||||
# Sort in reverse order since songs shift index when deleting
|
||||
for track in sorted(track_indexes_to_remove, reverse=True):
|
||||
if removed_track := player.remove_user_track(ctx.author.id, track):
|
||||
paginator.add_line(f"{removed_track.title}")
|
||||
|
||||
paginator.close_page()
|
||||
scroller = Scroller(ctx, paginator)
|
||||
await scroller.start_scrolling(ScrollClear.OnInteractionExit | ScrollClear.OnTimeout)
|
||||
|
||||
@commands.command(name="DJremove")
|
||||
@checks.dj_or()
|
||||
@@ -398,39 +424,6 @@ class Music(commands.Cog):
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name='search2')
|
||||
@require_voice_connection(should_connect=True)
|
||||
async def _search2(self, ctx, *, query):
|
||||
player = self.bot.lavalink.player_manager.get(ctx.guild.id)
|
||||
|
||||
if not query.startswith('ytsearch:') and not query.startswith('scsearch:'):
|
||||
query = 'ytsearch:' + query
|
||||
|
||||
results = await player.node.get_tracks(query)
|
||||
|
||||
embed = discord.Embed(description='{nothing_found}', color=0x36393F)
|
||||
if not results or not results['tracks']:
|
||||
embed = ctx.localizer.format_embed(embed)
|
||||
return await ctx.send(embed=embed)
|
||||
|
||||
buttons = []
|
||||
for track in results['tracks']:
|
||||
duration = timeformatter.format_ms(int(track.duration))
|
||||
interaction = SelectorItem(f'[{track.title}]({track.uri}) `{duration}`',
|
||||
wrap_in_button_callback(self.enqueue, ctx, track, discord.Embed(color=ctx.me.color)))
|
||||
buttons.append(interaction)
|
||||
|
||||
search_selector = Selector2(ctx, buttons, color=ctx.me.color, title=ctx.localizer.format_str('{results}'))
|
||||
message, callback_results = await search_selector.start_scrolling()
|
||||
|
||||
song_embed, song_added = callback_results[0]
|
||||
if song_added:
|
||||
embed = ctx.localizer.format_embed(song_embed)
|
||||
await message.edit(embed=embed, view=None)
|
||||
|
||||
if not player.is_playing:
|
||||
await player.play()
|
||||
|
||||
@commands.command(name='search')
|
||||
@require_voice_connection(should_connect=True)
|
||||
async def _search(self, ctx, *, query):
|
||||
@@ -446,34 +439,27 @@ class Music(commands.Cog):
|
||||
embed = ctx.localizer.format_embed(embed)
|
||||
return await ctx.send(embed=embed)
|
||||
|
||||
tracks = results['tracks']
|
||||
result_count = min(len(tracks), 15)
|
||||
|
||||
# Setup for the selector
|
||||
identifiers = []
|
||||
functions = [self.enqueue]*result_count
|
||||
arguments = [(ctx, tracks[i], discord.Embed(color=ctx.me.color)) for i in range(result_count)]
|
||||
|
||||
for index, track in enumerate(tracks, start=1):
|
||||
buttons = []
|
||||
for i, track in enumerate(results['tracks'], start=1):
|
||||
duration = timeformatter.format_ms(int(track.duration))
|
||||
identifiers.append(f'`{index}.` [{track.title}]({track.uri}) `{duration}`')
|
||||
if index == result_count:
|
||||
break
|
||||
interaction = SelectorItem(f'`{i}` [{track.title}]({track.uri}) `{duration}`', str(i),
|
||||
wrap_in_button_callback(
|
||||
self.enqueue, ctx, track, discord.Embed(color=ctx.me.color)))
|
||||
buttons.append(interaction)
|
||||
|
||||
search_selector = Selector2(ctx, buttons, color=ctx.me.color, title=ctx.localizer.format_str('{results}'))
|
||||
message, callback_results = await search_selector.start_scrolling()
|
||||
|
||||
search_selector = Selector(ctx, identifiers, functions, arguments, num_selections=5,
|
||||
color=ctx.me.color, title=ctx.localizer.format_str('{results}'))
|
||||
# Let the user scroll through results
|
||||
message, current_page, result = await search_selector.start_scrolling()
|
||||
if len(callback_results) == 0:
|
||||
return await message.delete()
|
||||
|
||||
if result:
|
||||
(embed, added) = result
|
||||
embed = ctx.localizer.format_embed(embed)
|
||||
await message.edit(embed=embed)
|
||||
song_embed, song_added = callback_results[0]
|
||||
if song_added:
|
||||
embed = ctx.localizer.format_embed(song_embed)
|
||||
await message.edit(embed=embed, view=None)
|
||||
|
||||
if not player.is_playing:
|
||||
await player.play()
|
||||
else:
|
||||
await message.delete()
|
||||
|
||||
@commands.command(name='disconnect')
|
||||
@require_voice_connection()
|
||||
@@ -703,7 +689,78 @@ class Music(commands.Cog):
|
||||
await ctx.send(embed=page)
|
||||
else:
|
||||
paginator.add_page_indicator(ctx.localizer)
|
||||
await Scroller(ctx, paginator).start_scrolling()
|
||||
await Scroller(ctx, paginator).start_scrolling(ScrollClear.OnTimeout | ScrollClear.OnInteractionExit)
|
||||
|
||||
@commands.command(name='scrub2')
|
||||
@checks.dj_or(alone=True)
|
||||
@require_voice_connection()
|
||||
@require_playing(require_user_listening=True)
|
||||
async def _scrub2(self, ctx):
|
||||
""" Lists the first 10 search results from a given query. """
|
||||
player: MixPlayer = self.bot.lavalink.player_manager.get(ctx.guild.id)
|
||||
|
||||
controls = '{scrub.controls}'
|
||||
|
||||
scrubber = [
|
||||
('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', player.seek, -1000),
|
||||
('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE}', player.seek, -15),
|
||||
('\N{DOUBLE VERTICAL BAR}', player.set_pause, True),
|
||||
('\N{BLACK RIGHT-POINTING TRIANGLE}', player.set_pause, False),
|
||||
('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE}', player.seek, 15),
|
||||
('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', player.seek, 1000),
|
||||
]
|
||||
|
||||
selection = None
|
||||
arg = None
|
||||
|
||||
def check(reaction, user):
|
||||
if user is None or user.id != ctx.author.id:
|
||||
return False
|
||||
|
||||
if reaction.message.id != msg.id:
|
||||
return False
|
||||
|
||||
for (emoji, func, _arg) in scrubber:
|
||||
if emoji == reaction.emoji:
|
||||
nonlocal selection
|
||||
nonlocal arg
|
||||
selection = func
|
||||
arg = _arg
|
||||
return True
|
||||
return False
|
||||
|
||||
embed = discord.Embed(description='{scrub.add}', color=ctx.me.color)
|
||||
embed = ctx.localizer.format_embed(embed)
|
||||
msg = await ctx.send(embed=embed)
|
||||
|
||||
for (emoji, _, _) in scrubber:
|
||||
await msg.add_reaction(emoji)
|
||||
|
||||
embed.description = controls
|
||||
embed = ctx.localizer.format_embed(embed)
|
||||
await msg.edit(embed=embed)
|
||||
scrubbing = True
|
||||
while scrubbing:
|
||||
try:
|
||||
reaction, user = await self.bot.wait_for('reaction_add', timeout=15.0, check=check)
|
||||
except asyncio.TimeoutError:
|
||||
# PEP8EDIT scrolling = False
|
||||
try:
|
||||
await msg.delete()
|
||||
await ctx.message.delete()
|
||||
except (discord.Forbidden, discord.HTTPException):
|
||||
pass
|
||||
finally:
|
||||
break
|
||||
|
||||
try:
|
||||
await msg.remove_reaction(reaction, user)
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
if selection is not None:
|
||||
if not isinstance(arg, bool):
|
||||
arg = player.position + arg * 1000
|
||||
await selection(arg)
|
||||
|
||||
@commands.command(name='scrub')
|
||||
@checks.dj_or(alone=True)
|
||||
|
||||
@@ -4,6 +4,7 @@ from lavalink import AudioTrack
|
||||
from collections import OrderedDict, deque
|
||||
from itertools import chain, cycle, islice
|
||||
from random import shuffle
|
||||
from typing import Union, List
|
||||
|
||||
|
||||
def roundrobin(*iterables):
|
||||
@@ -22,7 +23,7 @@ def roundrobin(*iterables):
|
||||
|
||||
class MixQueue:
|
||||
def __init__(self):
|
||||
self.queues = OrderedDict()
|
||||
self.queues: OrderedDict[int, List[AudioTrack]] = OrderedDict()
|
||||
self.priority_queue = []
|
||||
self._history = deque(maxlen=11) # 10 + current
|
||||
self.looping = False
|
||||
@@ -131,7 +132,7 @@ class MixQueue:
|
||||
else:
|
||||
self.queues.pop(requester)
|
||||
|
||||
def remove_user_track(self, requester: int, pos: int):
|
||||
def remove_user_track(self, requester: int, pos: int) -> Union[None, AudioTrack]:
|
||||
user_queue = self.queues.get(requester)
|
||||
if user_queue is not None:
|
||||
if pos < len(user_queue):
|
||||
|
||||
@@ -5,6 +5,7 @@ from lavalink import AudioTrack, DefaultPlayer, Node
|
||||
from lavalink.events import QueueEndEvent, TrackEndEvent, TrackExceptionEvent, TrackStartEvent, TrackStuckEvent
|
||||
|
||||
import typing
|
||||
from typing import Union
|
||||
|
||||
from .mixqueue import MixQueue
|
||||
|
||||
@@ -13,7 +14,7 @@ class MixPlayer(DefaultPlayer):
|
||||
def __init__(self, guild_id: int, node: Node):
|
||||
super().__init__(guild_id, node)
|
||||
|
||||
self.queue = MixQueue()
|
||||
self.queue: MixQueue = MixQueue()
|
||||
|
||||
self.listeners = set()
|
||||
self.voteables = {}
|
||||
@@ -35,7 +36,7 @@ class MixPlayer(DefaultPlayer):
|
||||
def remove_user_queue(self, requester: int):
|
||||
self.queue.remove_user_queue(requester)
|
||||
|
||||
def remove_user_track(self, requester: int, pos: int):
|
||||
def remove_user_track(self, requester: int, pos: int) -> Union[None, AudioTrack]:
|
||||
""" Removes the song at <pos> from the queue of requester """
|
||||
return self.queue.remove_user_track(requester, pos)
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@ from enum import Flag, auto
|
||||
from lavalink.client import asyncio
|
||||
from .paginators import BasePaginator, CantScrollException
|
||||
|
||||
from typing import Union
|
||||
|
||||
class ScrollClearSettings(Flag):
|
||||
class ScrollClear(Flag):
|
||||
OnTimeout = auto()
|
||||
OnInteractionExit = auto()
|
||||
|
||||
@@ -23,14 +24,11 @@ class ScrollerNav(discord.ui.Select):
|
||||
|
||||
|
||||
class Scroller:
|
||||
def __init__(self, ctx, paginator, clear_mode: ScrollClearSettings, timeout=20.0,
|
||||
with_nav_bar: bool = True):
|
||||
def __init__(self, ctx, paginator, timeout=20.0, check_for_stop: bool = False):
|
||||
|
||||
if not isinstance(paginator, BasePaginator):
|
||||
raise TypeError('Paginator needs to be a subclass of BasePaginator.')
|
||||
|
||||
self.clear_mode = clear_mode
|
||||
self.use_nav_bar = with_nav_bar
|
||||
self.paginator = paginator
|
||||
self.ctx = ctx
|
||||
|
||||
@@ -40,21 +38,23 @@ class Scroller:
|
||||
|
||||
self.bot = ctx.bot
|
||||
self.channel = ctx.channel
|
||||
self.message = None
|
||||
self.message: Union[discord.Message, None] = None
|
||||
|
||||
# Initialize the view we'll be using for scrolling
|
||||
self.view = discord.ui.View(timeout=timeout)
|
||||
self.view.on_timeout = self.on_timeout
|
||||
|
||||
self.is_scrolling_paginator = len(self.paginator.pages) > 1
|
||||
self.use_nav_bar = len(self.paginator.pages) > 3
|
||||
self.scrolling_done = asyncio.Event()
|
||||
|
||||
stop_emoji = '✔️' if check_for_stop else '❌'
|
||||
self.interaction_mapping = [
|
||||
('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', self.first_page),
|
||||
('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page),
|
||||
('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page),
|
||||
('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', self.last_page),
|
||||
('❌', self.stop),
|
||||
(stop_emoji, self.stop),
|
||||
]
|
||||
|
||||
bot_user = ctx.guild if ctx.guild.me is not None else ctx.bot.user
|
||||
@@ -66,22 +66,22 @@ class Scroller:
|
||||
if not self.permissions.send_messages:
|
||||
raise CantScrollException('Bot cannot send messages.')
|
||||
|
||||
async def start_scrolling(self) -> discord.Message:
|
||||
async def start_scrolling(self, clear_mode: ScrollClear) -> discord.Message:
|
||||
self.clear_mode = clear_mode
|
||||
self.current_page_number = 0
|
||||
self.build_view()
|
||||
|
||||
if not self.is_scrolling_paginator:
|
||||
return await self.channel.send(embed=self.paginator.pages[0])
|
||||
|
||||
self.message = await self.channel.send(embed=self.paginator.pages[0], view=self.view)
|
||||
await self.scrolling_done.wait()
|
||||
|
||||
return self.message
|
||||
|
||||
def update_view(self, interaction: discord.Interaction):
|
||||
self.navigator.placeholder = f"Page: {self.current_page_number + 1}/{len(self.paginator.pages)}"
|
||||
if self.is_scrolling_paginator and self.use_nav_bar:
|
||||
self.navigator.placeholder = f"Page: {self.current_page_number + 1}/{len(self.paginator.pages)}"
|
||||
|
||||
def build_view(self):
|
||||
if not self.is_scrolling_paginator:
|
||||
return
|
||||
|
||||
for (reaction, callback) in self.interaction_mapping:
|
||||
self.view.add_item(item=ScrollerButton(callback=callback, label=reaction, row=3))
|
||||
|
||||
@@ -95,13 +95,19 @@ class Scroller:
|
||||
self.is_scrolling_paginator = False
|
||||
self.view.stop()
|
||||
self.view.clear_items()
|
||||
if (user_stopped and self.clear_mode & ScrollClearSettings.OnInteractionExit or
|
||||
not user_stopped and self.clear_mode & ScrollClearSettings.OnTimeout):
|
||||
await self.message.delete()
|
||||
self.message = None
|
||||
await self.ctx.message.delete()
|
||||
if (user_stopped and self.clear_mode & ScrollClear.OnInteractionExit or
|
||||
not user_stopped and self.clear_mode & ScrollClear.OnTimeout):
|
||||
if self.message:
|
||||
await self.message.delete()
|
||||
self.message = None
|
||||
# If this fails the message is not accessable either way, which
|
||||
# means it is probably deleted
|
||||
try:
|
||||
await self.ctx.message.delete()
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
await self.message.edit(embed=self.paginator.pages[self.current_page_number])
|
||||
await self.update_message()
|
||||
|
||||
# Notify the start_scrolling function that we're done
|
||||
self.scrolling_done.set()
|
||||
@@ -117,9 +123,13 @@ class Scroller:
|
||||
if interaction.message:
|
||||
# Update the view before we edit the message
|
||||
self.update_view(interaction)
|
||||
await interaction.message.edit(embed=self.paginator.pages[page], view=self.view)
|
||||
await self.update_message()
|
||||
await interaction.response.defer()
|
||||
|
||||
async def update_message(self):
|
||||
if self.message:
|
||||
await self.message.edit(embed=self.paginator.pages[self.current_page_number], view=self.view)
|
||||
|
||||
async def first_page(self, interaction: discord.Interaction):
|
||||
await self._scroll(0, interaction)
|
||||
|
||||
@@ -135,7 +145,8 @@ class Scroller:
|
||||
async def navigate(self, interaction: discord.Interaction):
|
||||
await self._scroll(int(self.navigator.values[0]), interaction)
|
||||
|
||||
async def stop_scrolling(self, _: discord.Interaction):
|
||||
async def stop_scrolling(self, interaction: discord.Interaction):
|
||||
await interaction.response.defer()
|
||||
await self.stop(user_stopped=True)
|
||||
|
||||
async def on_timeout(self):
|
||||
|
||||
@@ -6,8 +6,10 @@ import asyncio
|
||||
import inspect
|
||||
from typing import List, Callable, Coroutine
|
||||
|
||||
from discord.ext.commands import check
|
||||
|
||||
from .paginators import TextPaginator
|
||||
from .scroller import ScrollClearSettings, Scroller
|
||||
from .scroller import ScrollClear, Scroller
|
||||
|
||||
|
||||
class SelectorButton(discord.ui.Button):
|
||||
@@ -16,8 +18,8 @@ class SelectorButton(discord.ui.Button):
|
||||
super().__init__(label=label, style=style, **kwargs)
|
||||
|
||||
# The callback we need to supply to disord.py takes only a discord.Interaction
|
||||
# we want to have access to the button that caused that was clicked
|
||||
# so we can update it, i.e. change color
|
||||
# we want to have access to the button that was clicked (self)
|
||||
# so we can update or modify it upon it being pressed i.e. change color.
|
||||
def add_button_to_callback(func):
|
||||
async def wrapped_callback(interaction: discord.Interaction):
|
||||
await func(interaction, self)
|
||||
@@ -27,13 +29,14 @@ class SelectorButton(discord.ui.Button):
|
||||
|
||||
|
||||
class SelectorItem:
|
||||
def __init__(self, identifier, callback: Callable[[discord.Interaction, SelectorButton], Coroutine]):
|
||||
def __init__(self, identifier, button_label, callback: Callable[[discord.Interaction, SelectorButton], Coroutine]):
|
||||
self.button_label = button_label
|
||||
self.identifier = identifier
|
||||
self.callback = callback
|
||||
|
||||
|
||||
class Selector2(TextPaginator, Scroller):
|
||||
def __init__(self, ctx, choices: List[SelectorItem], round_titles=None,
|
||||
def __init__(self, ctx, choices: List[SelectorItem], round_titles=None, check_for_stop: bool = False,
|
||||
terminate_on_select: bool = True, max_size=2000, **embed_base):
|
||||
self.match = None
|
||||
if round_titles is None:
|
||||
@@ -42,16 +45,15 @@ class Selector2(TextPaginator, Scroller):
|
||||
self.terminate_on_select = terminate_on_select
|
||||
self.round_titles = round_titles
|
||||
|
||||
self.max_selections_per_page = 5
|
||||
self.selections_per_page = 5
|
||||
self.selections = choices
|
||||
self.max_selections = min(len(self.selections), self.max_selections_per_page)
|
||||
|
||||
TextPaginator.__init__(self, max_size=max_size, max_lines=self.max_selections_per_page, **embed_base)
|
||||
for index, selection in enumerate(self.selections, start=1):
|
||||
self.add_line(f"`{index}.` {selection.identifier}")
|
||||
TextPaginator.__init__(self, max_size=max_size, max_lines=self.selections_per_page, **embed_base)
|
||||
for selection in self.selections:
|
||||
self.add_line(selection.identifier)
|
||||
self.close_page()
|
||||
|
||||
Scroller.__init__(self, ctx, self, ScrollClearSettings.OnTimeout)
|
||||
Scroller.__init__(self, ctx, self, check_for_stop=check_for_stop)
|
||||
|
||||
# Contains a list of all buttons that can be interacted with, both
|
||||
# currently visible and not.
|
||||
@@ -64,6 +66,8 @@ class Selector2(TextPaginator, Scroller):
|
||||
# after we have finished interacting with the scroller
|
||||
self.callback_results = []
|
||||
|
||||
# Wrap the provided so that we can both update the view if needed
|
||||
# or terminate the selection process upon interaction
|
||||
def with_update_view(func):
|
||||
async def wrapped_callback(interaction: discord.Interaction, button: SelectorButton):
|
||||
result = await func(interaction, button)
|
||||
@@ -72,10 +76,12 @@ class Selector2(TextPaginator, Scroller):
|
||||
await interaction.response.defer()
|
||||
if terminate_on_select:
|
||||
await self.stop(True)
|
||||
else:
|
||||
await self.update_message()
|
||||
return wrapped_callback
|
||||
|
||||
for i, choice in enumerate(self.selections):
|
||||
self.buttons.append(SelectorButton(label=i+1, callback=with_update_view(choice.callback), row=0))
|
||||
self.buttons.append(SelectorButton(label=choice.button_label, callback=with_update_view(choice.callback), row=0))
|
||||
|
||||
def build_view(self):
|
||||
super().build_view()
|
||||
@@ -91,15 +97,15 @@ class Selector2(TextPaginator, Scroller):
|
||||
|
||||
self.visible_buttons = []
|
||||
self.current_page_number
|
||||
start = self.current_page_number * self.max_selections_per_page
|
||||
end = min((self.current_page_number + 1) * self.max_selections_per_page, len(self.selections))
|
||||
start = self.current_page_number * self.selections_per_page
|
||||
end = min((self.current_page_number + 1) * self.selections_per_page, len(self.selections))
|
||||
|
||||
for button in self.buttons[start:end]:
|
||||
self.view.add_item(item=button)
|
||||
self.visible_buttons.append(button)
|
||||
|
||||
async def start_scrolling(self):
|
||||
message = await super().start_scrolling()
|
||||
async def start_scrolling(self, clear_mode: ScrollClear=ScrollClear.OnTimeout):
|
||||
message = await super().start_scrolling(clear_mode)
|
||||
return message, self.callback_results
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user