Skip to content

Commit

Permalink
v0.6.2: add select for /search
Browse files Browse the repository at this point in the history
  • Loading branch information
tristiisch committed Dec 18, 2023
1 parent 852a286 commit dddb2ac
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 50 deletions.
24 changes: 12 additions & 12 deletions src/pyramid/connector/discord/bot_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from typing import Callable, List

from discord import AppInfo, ClientUser, Color, Embed, Guild, Interaction
import discord
from discord.app_commands import Command
from discord.ext.commands import Bot
from discord.user import BaseUser
Expand Down Expand Up @@ -233,6 +232,17 @@ async def cmd_queue(ctx: Interaction):

guild_cmd.queue_list(ms, ctx)

# @bot.tree.command(name="search_v1", description="Search tracks (old way)")
# async def cmd_search_v1(ctx: Interaction, input: str, engine: SourceType | None):
# if (await self.__use_on_guild_only(ctx)) is False:
# return
# ms = MessageSenderQueued(ctx)
# await ms.thinking()
# guild: Guild = ctx.guild # type: ignore
# guild_cmd: GuildCmd = self.__get_guild_cmd(guild)

# await guild_cmd.searchV1(ms, input, engine)

@bot.tree.command(name="search", description="Search tracks")
async def cmd_search(ctx: Interaction, input: str, engine: SourceType | None):
if (await self.__use_on_guild_only(ctx)) is False:
Expand Down Expand Up @@ -271,20 +281,10 @@ async def cmd_play_url_next(ctx: Interaction, url: str):

await guild_cmd.play_url(ms, ctx, url, at_end=False)

@bot.tree.command(
name="test",
)
async def cmd_test(ctx: Interaction):
view = discord.ui.View() # Establish an instance of the discord.ui.View class
style = discord.ButtonStyle.gray # The button will be gray in color
item = discord.ui.Button(style=style, label="Read the docs!", url="https://discordpy.readthedocs.io/en/master") # Create an item to pass into the view class.
view.add_item(item=item) # Add that item into the view class
await ctx.response.send_message("This message has buttons!", view=view) # Send your message with a button.

# @bot.tree.command(name="spam", description="Test spam")
# async def cmd_spam(ctx: Interaction):
# ms = MessageSenderQueued(ctx)
# await ms.waiting()
# await ms.thinking()

# for i in range(100):
# ms.add_message(f"Spam n°{i}")
Expand Down
34 changes: 32 additions & 2 deletions src/pyramid/connector/discord/guild_cmd.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from logging import Logger

from discord import Interaction, VoiceChannel
from discord import Interaction, Member, User, VoiceChannel
import discord

import data.tracklist as utils_list_track
from data.guild_data import GuildData
Expand All @@ -11,6 +12,7 @@
from data.functional.engine_source import EngineSource, SourceType
from data.exceptions import DiscordMessageException
from data.a_guild_cmd import AGuildCmd
from data.select_view import SelectView


class GuildCmd(AGuildCmd, GuildCmdTools):
Expand Down Expand Up @@ -193,7 +195,7 @@ def queue_list(self, ms: MessageSenderQueued, ctx: Interaction) -> bool:
ms.add_code_message(queue, prefix="Here's the music in the queue :")
return True

async def search(
async def searchV1(
self, ms: MessageSenderQueued, input: str, engine: SourceType | None = None
) -> bool:
try:
Expand All @@ -203,10 +205,38 @@ async def search(
return False

hsa = utils_list_track.to_str(tracks)
if tracks_unfindable:
hsa = utils_list_track.to_str(tracks_unfindable)
ms.add_code_message(hsa, prefix=":warning: Can't find the audio for these tracks :")
ms.add_code_message(hsa, prefix="Here are the results of your search :")
return True

async def search(
self, ms: MessageSenderQueued, input: str, engine: SourceType | None = None
) -> bool:
try:
tracks, tracks_unfindable = await self.data.search_engine.search_tracks(
input, engine, 25
)
except DiscordMessageException as err:
ms.add_message(err.msg)
return False

view = SelectView({
track: discord.SelectOption(label=track.name, description=f"{track.author_name} - {track.album_title}")
for track in tracks
})
async def callback(user: User | Member, ms: MessageSenderQueued, t: TrackMinimal):
voice_channel: VoiceChannel | None = await self._verify_voice_channel(ms, user)
if not voice_channel:
return
await self._execute_play(ms, voice_channel, t)
view.on_select = callback

if tracks_unfindable:
hsa = utils_list_track.to_str(tracks_unfindable)
ms.add_code_message(hsa, prefix=":warning: Can't find the audio for these tracks :")
await ms.ctx.followup.send(content="Choose a title from the provided list :", view=view)
return True

async def play_url(
Expand Down
2 changes: 1 addition & 1 deletion src/pyramid/connector/discord/guild_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ async def play(self, msg_sender: MessageSenderQueued) -> bool:
)

# Message in channel with player
await self.mpi.send_player(msg_sender.txt_channel, track)
await self.mpi.send_player(msg_sender.txt_channel, track, msg_sender.ctx.locale)
return True

def stop(self) -> bool:
Expand Down
61 changes: 32 additions & 29 deletions src/pyramid/connector/discord/music_player_interface.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,18 @@
import discord
from discord import Button, ButtonStyle, Embed, Interaction, Locale, Message
from discord import ButtonStyle, Embed, Interaction, Locale, Message
from discord.abc import Messageable
from discord.ui import Button

from data.track import Track
from data.tracklist import TrackList
from data.a_guid_queue import AGuildQueue
from data.a_guild_cmd import AGuildCmd
from data.functional.messages.message_sender_queued import MessageSenderQueued


class MusicPlayerInterface(discord.ui.View):

def __init__(self, locale: Locale, track_list: TrackList):
super().__init__(timeout=180)
self.locale = locale
self.last_message: Message | None = None
self.track_list = track_list

def set_queue_action(self, queue_action: AGuildCmd):
class PlayerButton(discord.ui.View):
def __init__(self, queue_action: AGuildCmd, timeout: float | None = 180):
super().__init__(timeout=timeout)
self.queue_action = queue_action

async def send_player(self, txt_channel: Messageable, track: Track):
embed = self.__embed_track(track)

last_channel_message = None
history = txt_channel.history(limit=1)
async for message in history:
last_channel_message = message

if last_channel_message and self.last_message is not None:
if last_channel_message.id == self.last_message.id:
self.last_message = await last_channel_message.edit(content="", embed=embed)
return
else:
await self.last_message.delete()
self.last_message = await txt_channel.send(content="", embed=embed, view=self)

@discord.ui.button(emoji="⏯️", style=ButtonStyle.primary)
async def next_play_or_pause(self, ctx: Interaction, button: Button):
Expand All @@ -61,7 +38,33 @@ async def stop_queue(self, ctx: Interaction, button: Button):
await ms.thinking()
await self.queue_action.stop(ms, ctx)

def __embed_track(self, track: Track) -> Embed:
class MusicPlayerInterface():

def __init__(self, locale: Locale, track_list: TrackList):
self.locale = locale
self.last_message: Message | None = None
self.track_list = track_list

def set_queue_action(self, queue_action: AGuildCmd):
self.queue_action = queue_action

async def send_player(self, txt_channel: Messageable, track: Track, locale: Locale):
embed = self.__embed_track(track, locale)

last_channel_message = None
history = txt_channel.history(limit=1)
async for message in history:
last_channel_message = message

if last_channel_message and self.last_message is not None:
if last_channel_message.id == self.last_message.id:
self.last_message = await last_channel_message.edit(content="", embed=embed)
return
else:
await self.last_message.delete()
self.last_message = await txt_channel.send(content="", embed=embed, view=PlayerButton(self.queue_action))

def __embed_track(self, track: Track, locale: Locale) -> Embed:
# track.actual_seconds = round(track.duration_seconds * 0.75)
track.actual_seconds = int(0)
progress_bar = f"{track.format_duration(track.actual_seconds)} {self.__generate_color_sequence(track.actual_seconds / track.duration_seconds * 100)} {track.duration}"
Expand All @@ -73,7 +76,7 @@ def __embed_track(self, track: Track) -> Embed:
color=discord.Color.blue(),
)
embed.add_field(name="Album", value=track.album_title)
embed.add_field(name="Release", value=track.get_date(self.locale.value))
embed.add_field(name="Release", value=track.get_date(locale.value))

embed.set_author(name=", ".join(track.authors), icon_url=track.author_picture)
embed.set_thumbnail(url=track.album_picture)
Expand Down
2 changes: 1 addition & 1 deletion src/pyramid/data/a_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ async def search_track(self, search) -> TrackMinimal | None:
pass

@abc.abstractmethod
async def search_tracks(self, search, limit=10) -> list[TrackMinimal] | None:
async def search_tracks(self, search, limit: int | None = None) -> list[TrackMinimal] | None:
pass

@abc.abstractmethod
Expand Down
2 changes: 1 addition & 1 deletion src/pyramid/data/functional/application_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class ApplicationInfo:
def __init__(self):
self.name = "pyramid"
self.os = get_os().lower()
self.version = "0.6.1"
self.version = "0.6.2"
self.git_info = GitInfo()

def load_git_info(self):
Expand Down
8 changes: 5 additions & 3 deletions src/pyramid/data/functional/engine_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,13 @@ async def search_track(self, input: str, engine: SourceType | None) -> TrackMini
return await self._equivalent_for_download(track)
return track

async def search_tracks(self, input: str, engine: SourceType | None):
async def search_tracks(
self, input: str, engine: SourceType | None, limit: int | None = None
) -> tuple[list[TrackMinimal], list[TrackMinimal]]:
search_engine = self._resolve_engine(engine)
search_engine_name = self._get_engine_name(search_engine)

tracks = await search_engine.search_tracks(input)
tracks = await search_engine.search_tracks(input, limit)
if not tracks:
raise TrackNotFoundException(
"Search **%s** not found on %s.", input, search_engine_name
Expand All @@ -98,7 +100,7 @@ async def search_tracks(self, input: str, engine: SourceType | None):

if not all(isinstance(t, TrackMinimalDeezer) for t in tracks):
tracks = await self._equivalents_for_download(tracks, tracks_unfindable)
return tracks, tracks_unfindable
return tracks, tracks_unfindable # type: ignore

def _get_engine(self, engine: SourceType):
return self.__sources.get(engine)
Expand Down
1 change: 0 additions & 1 deletion src/pyramid/data/guild_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ def __init__(self, guild: Guild, engine_source: EngineSource):
self.guild: Guild = guild
self.track_list: TrackList = TrackList()
self.voice_client: VoiceClient = None # type: ignore
self.search_engines = engine_source
self.search_engine = engine_source
58 changes: 58 additions & 0 deletions src/pyramid/data/select_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import uuid
from abc import abstractmethod
from typing import Generic, TypeVar

import discord
from data.functional.messages.message_sender_queued import MessageSenderQueued
from discord import Interaction, Member, User

T = TypeVar("T")


class SelectView(discord.ui.View, Generic[T]):
def __init__(self, select_options: dict[T, discord.SelectOption], multiple: bool = False):
super().__init__()

options_len = len(select_options)

self.options_defined: dict[str, T] = {}
options: list[discord.SelectOption] = [] * options_len

for t, option in select_options.items():
option.value = str(uuid.uuid4())

label = self.concat(option.label)
if label:
option.label = label
if option.description:
description = self.concat(option.description)
if description:
option.description = description

self.options_defined[option.value] = t
options.append(option)

async def callback(interaction: Interaction):
ms = MessageSenderQueued(interaction)
await ms.thinking()

value_selected: str = self.children[0].values[0] # type: ignore
t = self.options_defined[value_selected]
await self.on_select(interaction.user, ms, t)
if not multiple:
self.stop()

select = discord.ui.Select(options=options)
select.callback = callback
self.add_item(select)

@abstractmethod
async def on_select(cls, user: User | Member, ms: MessageSenderQueued, t: T):
pass

@staticmethod
def concat(str: str):
if 100 >= len(str):
return None
suffix = "..."
return str[: 100 - len(suffix)] + suffix

0 comments on commit dddb2ac

Please sign in to comment.