Wavelink icon indicating copy to clipboard operation
Wavelink copied to clipboard

Buttons not working, It's a bug for me or something I did wrong

Open perhapsradio opened this issue 4 years ago • 0 comments

Well I basically copied this code from here https://github.com/PythonistaGuild/Wavelink/blob/master/examples/advanced.py and changed some things. In the beggining the buttons worked, but now they doesnt work anymore. They are there, they appear, but when I click nothing happens, and was suposed to pause, resume, stop, skip the music. I didn't changed anything in the buttons code, so I don't know. Please help.

Here's my code in .txt: vroom.txt

Here's my code:

import asyncio import async_timeout import copy import datetime import discord import math import random import re import typing import wavelink from discord.ext import commands, menus from discord import Member from discord.ext.commands import MissingPermissions, has_permissions

URL matching REGEX...

URL_REG = re.compile(r'https?://(?:www.)?.+')

class NoChannelProvided(commands.CommandError): """Error raised when no suitable voice channel was supplied.""" pass

class IncorrectChannelError(commands.CommandError): """Error raised when commands are issued outside of the players session channel.""" pass

class MissingPermissions(commands.CommandError): """Error raised when you are not allowed to delete messages.""" pass

class Track(wavelink.Track): """Wavelink Track object with a requester attribute."""

__slots__ = ('requester', )

def __init__(self, *args, **kwargs):
    super().__init__(*args)

    self.requester = kwargs.get('requester')

class Player(wavelink.Player): """Custom wavelink Player class."""

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)

    self.context: commands.Context = kwargs.get('context', None)
    if self.context:
        self.dj: discord.Member = self.context.author

    self.queue = asyncio.Queue()
    self.controller = None

    self.waiting = False
    self.updating = False

    self.pause_votes = set()
    self.resume_votes = set()
    self.skip_votes = set()
    self.shuffle_votes = set()
    self.stop_votes = set()

async def do_next(self) -> None:
    if self.is_playing or self.waiting:
        return

    # Clear the votes for a new song...
    self.pause_votes.clear()
    self.resume_votes.clear()
    self.skip_votes.clear()
    self.shuffle_votes.clear()
    self.stop_votes.clear()

    try:
        self.waiting = True
        with async_timeout.timeout(1800):
            track = await self.queue.get()
    except asyncio.TimeoutError:
        # No music has been played for 30 minutes, cleanup and disconnect...
        return await self.teardown()

    await self.play(track)
    self.waiting = False

    # Invoke our players controller...
    await self.invoke_controller()

async def invoke_controller(self) -> None:
    """Method which updates or sends a new player controller."""
    if self.updating:
        return

    self.updating = True

    if not self.controller:
        self.controller = InteractiveController(embed=self.build_embed(), player=self)
        await self.controller.start(self.context)

    elif not await self.is_position_fresh():
        try:
            await self.controller.message.delete()
        except discord.HTTPException:
            pass

        self.controller.stop()

        self.controller = InteractiveController(embed=self.build_embed(), player=self)
        await self.controller.start(self.context)

    else:
        embed = self.build_embed()
        await self.controller.message.edit(content=None, embed=embed)

    self.updating = False

def build_embed(self) -> typing.Optional[discord.Embed]:
    """Method which builds our players controller embed."""
    track = self.current
    if not track:
        return

    channel = self.bot.get_channel(int(self.channel_id))
    qsize = self.queue.qsize()

    embed = discord.Embed(title=f'Music controller | {channel.name}', colour=0x003dff)
    embed.description = f'Now Playing:\n**`{track.title}`**\n\n'
    embed.set_thumbnail(url=track.thumb)

    embed.add_field(name='Duration', value=str(datetime.timedelta(milliseconds=int(track.length))))
    embed.add_field(name='Queue length', value=str(qsize))
    embed.add_field(name='Requested by', value=track.requester.display_name)
    embed.add_field(name='Video URL', value=f'{track.uri}')

    return embed

async def is_position_fresh(self) -> bool:
    """Method which checks whether the player controller should be remade or updated."""
    try:
        async for message in self.context.channel.history(limit=5):
            if message.id == self.controller.message.id:
                return True
    except (discord.HTTPException, AttributeError):
        return False

    return False

async def teardown(self):
    """Clear internal states, remove player controller and disconnect."""
    try:
        await self.controller.message.delete()
    except discord.HTTPException:
        pass

    self.controller.stop()

    try:
        await self.destroy()
    except KeyError:
        pass

class InteractiveController(menus.Menu): """The Players interactive controller menu class.""" async def send_initial_message(self, ctx, channel): return await channel.send(f'Hello {ctx.author}')

def __init__(self, *, embed: discord.Embed, player: Player):
    super().__init__(timeout=None)

    self.embed = embed
    self.player = player

def update_context(self, payload: discord.RawReactionActionEvent):
    """Update our context with the user who reacted."""
    ctx = copy.copy(self.ctx)
    ctx.author = payload.member

    return ctx

def reaction_check(self, payload: discord.RawReactionActionEvent):
    if payload.event_type == 'REACTION_REMOVE':
        return False

    if not payload.member:
        return False
    if payload.member.bot:
        return False
    if payload.message_id != self.message.id:
        return False
    if payload.member not in self.bot.get_channel(int(self.player.channel_id)).members:
        return False

    return payload.emoji in self.buttons

async def send_initial_message(self, ctx: commands.Context, channel: discord.TextChannel) -> discord.Message:
    return await channel.send(embed=self.embed)

@menus.button(emoji='\u25B6')
async def resume_command(self, payload: discord.RawReactionActionEvent):
    """Resume button."""
    ctx = self.update_context(payload)

    command = self.bot.get_command('resume')
    ctx.command = command

    await self.bot.invoke(ctx)

@menus.button(emoji='\u23F8')
async def pause_command(self, payload: discord.RawReactionActionEvent):
    """Pause button"""
    ctx = self.update_context(payload)

    command = self.bot.get_command('pause')
    ctx.command = command

    await self.bot.invoke(ctx)

@menus.button(emoji='\u23F9')
async def stop_command(self, payload: discord.RawReactionActionEvent):
    """Stop button."""
    ctx = self.update_context(payload)

    command = self.bot.get_command('stop')
    ctx.command = command

    await self.bot.invoke(ctx)

@menus.button(emoji='\u23ED')
async def skip_command(self, payload: discord.RawReactionActionEvent):
    """Skip button."""
    ctx = self.update_context(payload)

    command = self.bot.get_command('skip')
    ctx.command = command

    await self.bot.invoke(ctx)

@menus.button(emoji='\U0001F500')
async def shuffle_command(self, payload: discord.RawReactionActionEvent):
    """Shuffle button."""
    ctx = self.update_context(payload)

    command = self.bot.get_command('shuffle')
    ctx.command = command

    await self.bot.invoke(ctx)

@menus.button(emoji='\u2795')
async def volup_command(self, payload: discord.RawReactionActionEvent):
    """Volume up button"""
    ctx = self.update_context(payload)

    command = self.bot.get_command('vol_up')
    ctx.command = command

    await self.bot.invoke(ctx)

@menus.button(emoji='\u2796')
async def voldown_command(self, payload: discord.RawReactionActionEvent):
    """Volume down button."""
    ctx = self.update_context(payload)

    command = self.bot.get_command('vol_down')
    ctx.command = command

    await self.bot.invoke(ctx)

@menus.button(emoji='\U0001F1F6')
async def queue_command(self, payload: discord.RawReactionActionEvent):
    """Player queue button."""
    ctx = self.update_context(payload)

    command = self.bot.get_command('queue')
    ctx.command = command

    await self.bot.invoke(ctx)

class PaginatorSource(menus.ListPageSource): """Player queue paginator class."""

def __init__(self, entries, *, per_page=10):
    super().__init__(entries, per_page=per_page)

async def format_page(self, menu: menus.Menu, page):
    embed = discord.Embed(title='Coming up...', colour=0x003dff)
    embed.description = '\n'.join(f'`{index}. {title}`' for index, title in enumerate(page, 1))
    return embed

def is_paginating(self):
    # We always want to embed even on 1 page of results...
    return True

class Music(commands.Cog, wavelink.WavelinkMixin): """Music Cog."""

def __init__(self, bot: commands.Bot):
    self.bot = bot

    if not hasattr(bot, 'wavelink'):
        bot.wavelink = wavelink.Client(bot=bot)

    bot.loop.create_task(self.start_nodes())

async def start_nodes(self) -> None:
    """Connect and intiate nodes."""
    await self.bot.wait_until_ready()

    if self.bot.wavelink.nodes:
        previous = self.bot.wavelink.nodes.copy()

        for node in previous.values():
            await node.destroy()

    nodes = {
        'MAIN': {
            'host': '127.0.0.1', 
            'port': 2333,
            'rest_uri': 'http://127.0.0.1:2333/',
            'password': 'youshallnotpass',
            'identifier': 'MAIN',
            'region': 'europe',
        }
    }

    for n in nodes.values():
        await self.bot.wavelink.initiate_node(**n)

@wavelink.WavelinkMixin.listener()
async def on_node_ready(self, node: wavelink.Node):
    print(f'Node {node.identifier} is ready!')

@wavelink.WavelinkMixin.listener('on_track_stuck')
@wavelink.WavelinkMixin.listener('on_track_end')
@wavelink.WavelinkMixin.listener('on_track_exception')
async def on_player_stop(self, node: wavelink.Node, payload):
    await payload.player.do_next()

@commands.Cog.listener()
async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState):
    if member.bot:
        return

    player: Player = self.bot.wavelink.get_player(member.guild.id, cls=Player)

    if not player.channel_id or not player.context:
        player.node.players.pop(member.guild.id)
        return

    channel = self.bot.get_channel(int(player.channel_id))

    if member == player.dj and after.channel is None:
        for m in channel.members:
            if m.bot:
                continue
            else:
                player.dj = m
                return

    elif after.channel == channel and player.dj not in channel.members:
        player.dj = member

async def cog_command_error(self, ctx: commands.Context, error: Exception):
    """Cog wide error handler."""
    if isinstance(error, IncorrectChannelError):
        return

    if isinstance(error, NoChannelProvided):
        return await ctx.send('You must be connected to a voice channel!')

async def cog_check(self, ctx: commands.Context):
    """Cog wide check, which disallows commands in DMs."""
    if not ctx.guild:
        await ctx.send('Music commands are not available in DMs.')
        return False

    return True

async def cog_before_invoke(self, ctx: commands.Context):
    """Coroutine called before command invocation.
    We mainly just want to check whether the user is in the players controller channel.
    """
    player: Player = self.bot.wavelink.get_player(ctx.guild.id, cls=Player, context=ctx)

    if player.context:
        if player.context.channel != ctx.channel:
            pass

    if ctx.command.name == 'connect' and not player.context:
        return
    elif self.is_privileged(ctx):
        return

    if not player.channel_id:
        return

    channel = self.bot.get_channel(int(player.channel_id))
    if not channel:
        return

    if player.is_connected:
        if ctx.author not in channel.members:
            pass

def required(self, ctx: commands.Context):
    """Method which returns required votes based on amount of members in a channel."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)
    channel = self.bot.get_channel(int(player.channel_id))
    required = math.ceil((len(channel.members) - 1) / 2.5)

    if ctx.command.name == 'stop':
        if len(channel.members) == 3:
            required = 2

    return required

def is_privileged(self, ctx: commands.Context):
    """Check whether the user is an Admin or DJ."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    return player.dj == ctx.author or ctx.author.guild_permissions.kick_members

@commands.command(aliases=['leave'])
async def disconnect(self, ctx: commands.Context, *, channel: discord.VoiceChannel = None):
    """Disconnect from the channel."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)
    await ctx.send('Disconnecting...', delete_after=0.1)
    await player.teardown()
    await ctx.send('Disconnected!', delete_after=10)

@commands.command(aliases=['join'])
async def connect(self, ctx: commands.Context, *, channel: discord.VoiceChannel = None):
    """Connect to a voice channel."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)
    
    if player.is_connected:
        return

    channel = getattr(ctx.author.voice, 'channel', channel)
    if channel is None:
        raise NoChannelProvided

    await player.connect(channel.id)
    await ctx.send('Connecting...', delete_after=0.1)
    await ctx.send('Connected!', delete_after=10)

@commands.command()
async def play(self, ctx: commands.Context, *, query: str):
    """Play or queue a song with the given query."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if not player.is_connected:
        await ctx.invoke(self.connect)

    query = query.strip('<>')
    if not URL_REG.match(query):
        query = f'ytsearch:{query}'

    tracks = await self.bot.wavelink.get_tracks(query)
    if not tracks:
        return await ctx.send('No songs were found with that query. Please try again.', delete_after=15)

    if isinstance(tracks, wavelink.TrackPlaylist):
        for track in tracks.tracks:
            track = Track(track.id, track.info, requester=ctx.author)
            await player.queue.put(track)

        await ctx.send(f'Added the playlist **{tracks.data["playlistInfo"]["name"]}**'
                       f' with **{len(tracks.tracks)}** songs to the queue.')
    else:
        track = Track(tracks[0].id, tracks[0].info, requester=ctx.author)
        await ctx.send(f'Added **{track.title}** to the queue.')
        await player.queue.put(track)

    if not player.is_playing:
        await player.do_next()

@commands.command(name='pause', help='Pause the currently playing song.')
async def pause(self, ctx: commands.Context):
    await ctx.message.add_reaction(u"\u23F8")
    #"""Pause the currently playing song."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if player.is_paused or not player.is_connected:
        return

    if self.is_privileged(ctx):
        #await ctx.send('Music paused!', delete_after=10)
        player.pause_votes.clear()

        return await player.set_pause(True)

    required = self.required(ctx)
    player.pause_votes.add(ctx.author)

    if len(player.pause_votes) >= required:
        await ctx.send('Vote to pause passed. Pausing player.', delete_after=10)
        player.pause_votes.clear()
        await player.set_pause(True)
    else:
        await ctx.send(f'{ctx.author.mention} has voted to pause the player.', delete_after=15)


  

@commands.command(aliases=['unpause'], help='Resume a currently paused player.')
async def resume(self, ctx: commands.Context):
    await ctx.message.add_reaction(u"\u25B6")
    #"""Resume a currently paused player."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if not player.is_paused or not player.is_connected:
        return

    if self.is_privileged(ctx):
        #await ctx.send('Music unpaused!', delete_after=10)
        player.resume_votes.clear()

        return await player.set_pause(False)

    required = self.required(ctx)
    player.resume_votes.add(ctx.author)

    if len(player.resume_votes) >= required:
        await ctx.send('Vote to unpause passed. Unpausing player.', delete_after=10)
        player.resume_votes.clear()
        await player.set_pause(False)
    else:
        await ctx.send(f'{ctx.author.mention} has voted to resume the player.', delete_after=15)

@commands.command(aliases=['next'], help='Skip the currently playing song.')
async def skip(self, ctx: commands.Context):
    await ctx.message.add_reaction(u"\u23ED")
    #"""Skip the currently playing song."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if not player.is_connected:
        return

    if self.is_privileged(ctx):
        #await ctx.send('Skipped!', delete_after=10)
        player.skip_votes.clear()

        return await player.stop()

    if ctx.author == player.current.requester:
        await ctx.send('The song requester has skipped the song.', delete_after=10)
        player.skip_votes.clear()

        return await player.stop()

    required = self.required(ctx)
    player.skip_votes.add(ctx.author)

    if len(player.skip_votes) >= required:
        await ctx.send('Vote to skip passed. Skipping song.', delete_after=10)
        player.skip_votes.clear()
        await player.stop()
    else:
        await ctx.send(f'{ctx.author.mention} has voted to skip the song.', delete_after=15)

@commands.command(name='stop', help='Stop the player and clear the queue.')
async def stop(self, ctx: commands.Context):
    await ctx.message.add_reaction(u"\u23F9")
    #"""Stop the player and clear the queue."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if not player.is_connected:
        return

    if self.is_privileged(ctx):
        #await ctx.send('Stopped!!!', delete_after=10)
        return await player.teardown()
        
    required = self.required(ctx)
    player.stop_votes.add(ctx.author)

    if len(player.stop_votes) >= required:
        #await ctx.send('Vote to stop passed. Stopping the player.', delete_after=10)
        pass
    else:
        #await ctx.send(f'{ctx.author.mention} has voted to stop the player.', delete_after=15)
        pass

@commands.command(aliases=['v', 'vol'])
async def volume(self, ctx: commands.Context, *, vol: int):
    """Change volume, between 1 and 100."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if not player.is_connected:
        return

    if not self.is_privileged(ctx):
        return await ctx.send('Only the DJ or admins may change the volume.')

    if not 0 < vol < 101:
        return await ctx.send('Please enter a value between 1 and 100.')

    await player.set_volume(vol)
    await ctx.send(f'Set the volume to **{vol}**%', delete_after=7)

@commands.command(aliases=['mix', 'random'], help='Shuffle the players queue.')
async def shuffle(self, ctx: commands.Context):
    await ctx.message.add_reaction(u"\U0001F500")
    #"""Shuffle the players queue."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if not player.is_connected:
        return

    #if player.queue.qsize() < 3:
    #   return await ctx.send('Add more songs to the queue before shuffling.', delete_after=15)

    if self.is_privileged(ctx):
        #await ctx.send('Queue shuffled!!!')
        player.shuffle_votes.clear()
        return random.shuffle(player.queue._queue)

    required = self.required(ctx)
    player.shuffle_votes.add(ctx.author)

    if len(player.shuffle_votes) >= required:
        await ctx.send('Vote to shuffle passed. Shuffling the playlist.', delete_after=10)
        player.shuffle_votes.clear()
        random.shuffle(player.queue._queue)
    else:
        await ctx.send(f'{ctx.author.mention} has voted to shuffle the playlist.', delete_after=15)

@commands.command(hidden=True)
async def vol_up(self, ctx: commands.Context):
    """Command used for volume up button."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if not player.is_connected or not self.is_privileged(ctx):
        return

    vol = int(math.ceil((player.volume + 10) / 10)) * 10

    if vol > 100:
        vol = 100
        await ctx.send('Maximum volume reached', delete_after=7)

    await player.set_volume(vol)

@commands.command(hidden=True)
async def vol_down(self, ctx: commands.Context):
    """Command used for volume down button."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if not player.is_connected or not self.is_privileged(ctx):
        return

    vol = int(math.ceil((player.volume - 10) / 10)) * 10

    if vol < 0:
        vol = 0
        await ctx.send('Player is currently muted', delete_after=10)

    await player.set_volume(vol)

@commands.command(aliases=['eq'])
async def equalizer(self, ctx: commands.Context, *, equalizer: str):
    """Change the players equalizer."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if not player.is_connected:
        return

    if not self.is_privileged(ctx):
        return await ctx.send('Only admins may change the equalizer.')

    eqs = {'flat': wavelink.Equalizer.flat(),
           'boost': wavelink.Equalizer.boost(),
           'metal': wavelink.Equalizer.metal(),
           'piano': wavelink.Equalizer.piano()}

    eq = eqs.get(equalizer.lower(), None)

    if not eq:
        joined = "\n".join(eqs.keys())
        return await ctx.send(f'Invalid EQ provided. Valid EQs:\n\n{joined}')

    await ctx.send(f'Successfully changed equalizer to {equalizer}', delete_after=15)
    await player.set_eq(eq)

@commands.command(aliases=['q', 'que', 'list'])
async def queue(self, ctx: commands.Context):
    """Display the players queued songs."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if not player.is_connected:
        return

    if player.queue.qsize() == 0:
        return await ctx.send('There are no more songs in the queue.', delete_after=15)

    entries = [track.title for track in player.queue._queue]
    pages = PaginatorSource(entries=entries)
    paginator = menus.MenuPages(source=pages, timeout=None, delete_message_after=True)

    await paginator.start(ctx)

@commands.command(aliases=['np', 'now_playing', 'current'])
async def nowplaying(self, ctx: commands.Context):
    """Update the player controller."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if not player.is_connected:
        return

    await player.invoke_controller()

@commands.command(aliases=['swap'])
async def swap_dj(self, ctx: commands.Context, *, member: discord.Member = None):
    """Swap the current DJ to another member."""
    player: Player = self.bot.wavelink.get_player(guild_id=ctx.guild.id, cls=Player, context=ctx)

    if not player.is_connected:
        return

    if not self.is_privileged(ctx):
        return await ctx.send('Only admins and the DJ may use this command.', delete_after=15)

    members = self.bot.get_channel(int(player.channel_id)).members

    if member and member not in members:
        return await ctx.send(f'{member} is not currently in voice, so can not be a DJ.', delete_after=15)

    if member and member == player.dj:
        return await ctx.send('Cannot swap DJ to the current DJ... :)', delete_after=15)

    if len(members) <= 2:
        return await ctx.send('No more members to swap to.', delete_after=15)

    if member:
        player.dj = member
        return await ctx.send(f'{member.mention} is now the DJ.')

    for m in members:
        if m == player.dj or m.bot:
            continue
        else:
            player.dj = m
            return await ctx.send(f'{member.mention} is now the DJ.')

def setup(bot: commands.Bot): bot.add_cog(Music(bot))

bot = commands.Bot('?', description='Yet another music bot.') bot.add_cog(Music(bot))

@bot.event async def on_ready(): activity = discord.Game(name="League of Legends", type=3) await bot.change_presence(status=discord.Status.online, activity=activity) print('Logged in as:\n{0.user.name}\n{0.user.id}'.format(bot))

bot.run()

perhapsradio avatar Feb 26 '21 19:02 perhapsradio