import re import typing as t import discord import wavelink from discord.ext import commands URL_REGEX = r"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))" class AlreadyConnectedToChannel(commands.CommandError): pass class NoVoiceChannel(commands.CommandError): pass class QueueIsEmpty(commands.CommandError): pass class NoTracksFound(commands.CommandError): pass class Queue: def __init__(self): self._queue = [] self.position = 0 def add(self, *args): self._queue.extend(args) @property def first_track(self): if not self._queue: raise QueueIsEmpty return self._queue[0] def get_next_track(self): if not self._queue: raise QueueIsEmpty self.position += 1 if self.position > len(self._queue) - 1: return None return self._queue[self.position] class Player(wavelink.Player): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.queue = Queue() async def connect(self, ctx, channel=None): if self.is_connected: raise AlreadyConnectedToChannel if (channel := getattr(ctx.author.voice, "channel", channel)) is None: raise NoVoiceChannel await super().connect(channel.id) return channel async def teardown(self): try: await self.destroy() except KeyError: pass async def add_tracks(self, ctx, tracks): if not tracks: raise NoTracksFound if isinstance(tracks, wavelink.TrackPlaylist): self.queue.add(*tracks.tracks) elif len(tracks) == 1: self.queue.add(tracks[0]) await ctx.send(f"Added {Tracks[0].title} to the queue.") else: pass if not self.is_playing: await self.start_playback() async def start_playback(self): await self.play(self.queue.first_track) async def advance(self): try: if (track := self.queue.get_next_track()) is not None: await self.play(track) except QueueIsEmpty: pass class Music(commands.Cog, wavelink.WavelinkMixin): def __init__(self, bot): self.bot = bot self.wavelink = wavelink.Client(bot=bot) self.bot.loop.create_task(self.start_nodes()) @commands.Cog.listener() async def on_voice_state_update(self, member, before, after): if not member.bot and after.channel is None: if not [m for m in before.channel.members if not m.bot]: await self.get_player(member.guild).teardown() @wavelink.WavelinkMixin.listener() async def on_node_ready(self, node): print(f" Wavelink node `{node.identifier}` 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, payload): await payload.player.advance() async def cog_check(self, ctx): if isinstance(ctx.channel, discord.DMChannel): await ctx.send("Music commands are not available in DMs.") return False return True async def start_nodes(self): await self.bot.wait_until_ready() nodes = { "MAIN": { "host": "127.0.0.1", "port": 2333, "rest_uri": "http://127.0.0.1:2333", "password": "youshallnotpass", "identifier": "MAIN", "region": "europe", } } for node in nodes.values(): await self.wavelink.initiate_node(**node) def get_player(self, obj): if isinstance(obj, commands.Context): return self.wavelink.get_player(obj.guild.id, cls=Player, context=obj) elif isinstance(obj, discord.Guild): return self.wavelink.get_player(obj.id, cls=Player) @commands.command(name="connect", aliases=["join"]) async def connect_command(self, ctx, *, channel: t.Optional[discord.VoiceChannel]): player = self.get_player(ctx) channel = await player.connect(ctx, channel) await ctx.send(f"{channel.name} **Adlı Ses Kanalına Bağlandı.**") @connect_command.error async def connect_command_error(self, ctx, exc): if isinstance(exc, AlreadyConnectedToChannel): await ctx.send("**Bot zaten bir ses kanalında.**") elif isinstance(exc, NoVoiceChannel): await ctx.send("No suitable voice channel was provided.") @commands.command(name="disconnect", aliases=["leave"]) async def disconnect_command(self, ctx): player = self.get_player(ctx) await player.teardown() await ctx.send("**Bağlantı Kesildi.**") @commands.command(name="play") async def play_command(self, ctx, *, query: t.Optional[str]): player = self.get_player(ctx) if not player.is_connected: await player.connect(ctx) if query is None: pass else: qury = query.strip("<>") if not re.match(URL_REGEX, query): query = f"ytsearch:{query}" await player.add_tracks(ctx, await self.wavelink.get_tracks(query)) def setup(bot): bot.add_cog(Music(bot))