Ejemplo n.º 1
0
    async def on_red_audio_track_auto_play(
        self,
        guild: discord.Guild,
        track: lavalink.Track,
        requester: discord.Member,
        player: lavalink.Player,
    ):
        notify_channel = self.bot.get_channel(player.fetch("channel"))
        tries = 0
        while not player._is_playing:
            await asyncio.sleep(0.1)
            if tries > 1000:
                return

        if notify_channel and not player.fetch("autoplay_notified", False):
            await self.send_embed_msg(notify_channel, title=_("Auto Play started."))
            player.store("autoplay_notified", True)
Ejemplo n.º 2
0
    async def _eq_check(self, ctx: commands.Context, player: lavalink.Player) -> None:
        eq = player.fetch("eq", Equalizer())

        config_bands = await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands()
        if not config_bands:
            config_bands = eq.bands
            await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands.set(eq.bands)

        if eq.bands != config_bands:
            band_num = list(range(0, eq.band_count))
            band_value = config_bands
            eq_dict = {}
            for k, v in zip(band_num, band_value):
                eq_dict[k] = v
            for band, value in eq_dict.items():
                eq.set_gain(band, value)
            player.store("eq", eq)
            await self._apply_gains(ctx.guild.id, config_bands)
Ejemplo n.º 3
0
    async def on_red_audio_track_auto_play(
        self,
        guild: discord.Guild,
        track: lavalink.Track,
        requester: discord.Member,
        player: lavalink.Player,
    ):
        notify_channel = self.bot.get_channel(player.fetch("channel"))
        tries = 0
        while not player._is_playing:
            await asyncio.sleep(0.1)
            if tries > 1000:
                return

        if notify_channel and not player.fetch("autoplay_notified", False):
            if (len(player.manager.players) < 10 or not player._last_resume
                    and player._last_resume + datetime.timedelta(seconds=60) >
                    datetime.datetime.now(tz=datetime.timezone.utc)):
                await self.send_embed_msg(notify_channel,
                                          title=_("Auto Play started."))
            player.store("autoplay_notified", True)
    async def lavalink_event_handler(self, player: lavalink.Player,
                                     event_type: lavalink.LavalinkEvents,
                                     extra):

        if event_type == lavalink.LavalinkEvents.TRACK_START:
            track = player.current
            if track:
                embed = Embed(
                    description="▶️ **Now playing** [{0.title}]({0.uri}) !".
                    format(track))
                message = await track.channel.send(embed=embed)
                track.start_message = message
                if not len(player.queue) > 1:
                    player.store("m_msg", message)

        if event_type == lavalink.LavalinkEvents.TRACK_END:
            msg = player.fetch("m_msg")
            if (extra == lavalink.TrackEndReason.FINISHED
                    or extra == lavalink.TrackEndReason.REPLACED):
                if msg:
                    await msg.delete()

            if len(player.queue):
                await player.stop()
Ejemplo n.º 5
0
    async def lavalink_event_handler(self, player: lavalink.Player,
                                     event_type: lavalink.LavalinkEvents,
                                     extra) -> None:
        current_track = player.current
        current_channel = player.channel
        guild = self.rgetattr(current_channel, "guild", None)
        if not (current_channel and guild):
            player.store("autoplay_notified", False)
            await player.stop()
            await player.disconnect()
            return
        if await self.bot.cog_disabled_in_guild(self, guild):
            await player.stop()
            await player.disconnect()
            if guild:
                await self.config.guild_from_id(
                    guild_id=guild.id).currently_auto_playing_in.set([])
            return
        guild_id = self.rgetattr(guild, "id", None)
        if not guild:
            return
        guild_data = await self.config.guild(guild).all()
        disconnect = guild_data["disconnect"]

        if event_type == lavalink.LavalinkEvents.WEBSOCKET_CLOSED:
            deafen = guild_data["auto_deafen"]
            await self._websocket_closed_handler(guild=guild,
                                                 player=player,
                                                 extra=extra,
                                                 deafen=deafen,
                                                 disconnect=disconnect)
            return

        await set_contextual_locales_from_guild(self.bot, guild)
        current_requester = self.rgetattr(current_track, "requester", None)
        current_stream = self.rgetattr(current_track, "is_stream", None)
        current_length = self.rgetattr(current_track, "length", None)
        current_thumbnail = self.rgetattr(current_track, "thumbnail", None)
        current_id = self.rgetattr(current_track, "_info",
                                   {}).get("identifier")

        repeat = guild_data["repeat"]
        notify = guild_data["notify"]
        autoplay = guild_data["auto_play"]
        description = await self.get_track_description(
            current_track, self.local_folder_current_path)
        status = await self.config.status()
        log.debug(
            f"Received a new lavalink event for {guild_id}: {event_type}: {extra}"
        )
        prev_song: lavalink.Track = player.fetch("prev_song")
        await self.maybe_reset_error_counter(player)

        if event_type == lavalink.LavalinkEvents.TRACK_START:
            self.skip_votes[guild] = []
            playing_song = player.fetch("playing_song")
            requester = player.fetch("requester")
            player.store("prev_song", playing_song)
            player.store("prev_requester", requester)
            player.store("playing_song", current_track)
            player.store("requester", current_requester)
            self.bot.dispatch("red_audio_track_start", guild, current_track,
                              current_requester)
            if guild_id and current_track:
                await self.api_interface.persistent_queue_api.played(
                    guild_id=guild_id, track_id=current_track.track_identifier)
            notify_channel = player.fetch("channel")
            if notify_channel:
                await self.config.guild_from_id(
                    guild_id=guild_id).currently_auto_playing_in.set(
                        [notify_channel, player.channel.id])
            else:
                await self.config.guild_from_id(
                    guild_id=guild_id).currently_auto_playing_in.set([])
        if event_type == lavalink.LavalinkEvents.TRACK_END:
            prev_requester = player.fetch("prev_requester")
            self.bot.dispatch("red_audio_track_end", guild, prev_song,
                              prev_requester)
            player.store("resume_attempts", 0)
        if event_type == lavalink.LavalinkEvents.QUEUE_END:
            prev_requester = player.fetch("prev_requester")
            self.bot.dispatch("red_audio_queue_end", guild, prev_song,
                              prev_requester)
            if guild_id:
                await self.api_interface.persistent_queue_api.drop(guild_id)
            if player.is_auto_playing or (autoplay and not player.queue
                                          and player.fetch("playing_song")
                                          is not None
                                          and self.playlist_api is not None
                                          and self.api_interface is not None):
                notify_channel = player.fetch("channel")
                try:
                    await self.api_interface.autoplay(player,
                                                      self.playlist_api)
                except DatabaseError:
                    notify_channel = self.bot.get_channel(notify_channel)
                    if notify_channel:
                        await self.send_embed_msg(
                            notify_channel,
                            title=_("Couldn't get a valid track."))
                    return
                except TrackEnqueueError:
                    notify_channel = self.bot.get_channel(notify_channel)
                    if notify_channel:
                        await self.send_embed_msg(
                            notify_channel,
                            title=_("Unable to Get Track"),
                            description=_(
                                "I'm unable to get a track from Lavalink at the moment, try again in a few "
                                "minutes."),
                        )
                    return
        if event_type == lavalink.LavalinkEvents.TRACK_START and notify:
            notify_channel = player.fetch("channel")
            if notify_channel:
                notify_channel = self.bot.get_channel(notify_channel)
                if player.fetch("notify_message") is not None:
                    with contextlib.suppress(discord.HTTPException):
                        await player.fetch("notify_message").delete()
                if not (description and notify_channel):
                    return
                if current_stream:
                    dur = "LIVE"
                else:
                    dur = self.format_time(current_length)

                thumb = None
                if await self.config.guild(guild
                                           ).thumbnail() and current_thumbnail:
                    thumb = current_thumbnail

                notify_message = await self.send_embed_msg(
                    notify_channel,
                    description=description,
                    footer=_("Track length: {length} | Requested by: {user}").
                    format(length=dur, user=current_requester),
                    thumbnail=thumb,
                    author={
                        "name":
                        _("Now Playing"),
                        "url":
                        "https://cdn.discordapp.com/emojis/572861527049109515.gif",
                    },
                )
                player.store("notify_message", notify_message)
        if event_type == lavalink.LavalinkEvents.TRACK_START and status:
            player_check = await self.get_active_player_count()
            await self.update_bot_presence(*player_check)

        if event_type == lavalink.LavalinkEvents.TRACK_END and status:
            await asyncio.sleep(1)
            if not player.is_playing:
                player_check = await self.get_active_player_count()
                await self.update_bot_presence(*player_check)

        if event_type == lavalink.LavalinkEvents.QUEUE_END:
            if not autoplay:
                notify_channel = player.fetch("channel")
                if notify_channel and notify:
                    notify_channel = self.bot.get_channel(notify_channel)
                    await self.send_embed_msg(notify_channel,
                                              title=_("Queue ended."))
                if disconnect:
                    self.bot.dispatch("red_audio_audio_disconnect", guild)
                    await self.config.guild_from_id(
                        guild_id=guild_id).currently_auto_playing_in.set([])
                    await player.disconnect()
                    self._ll_guild_updates.discard(guild.id)
            if status:
                player_check = await self.get_active_player_count()
                await self.update_bot_presence(*player_check)

        if event_type in [
                lavalink.LavalinkEvents.TRACK_EXCEPTION,
                lavalink.LavalinkEvents.TRACK_STUCK,
        ]:
            message_channel = player.fetch("channel")
            while True:
                if current_track in player.queue:
                    player.queue.remove(current_track)
                else:
                    break
            if repeat:
                player.current = None
            if not guild_id:
                return
            guild_id = int(guild_id)
            self._error_counter.setdefault(guild_id, 0)
            if guild_id not in self._error_counter:
                self._error_counter[guild_id] = 0
            early_exit = await self.increase_error_counter(player)
            if early_exit:
                self._disconnected_players[guild_id] = True
                self.play_lock[guild_id] = False
                eq = player.fetch("eq")
                player.queue = []
                player.store("playing_song", None)
                player.store("autoplay_notified", False)
                if eq:
                    await self.config.custom("EQUALIZER",
                                             guild_id).eq_bands.set(eq.bands)
                await player.stop()
                await player.disconnect()
                await self.config.guild_from_id(
                    guild_id=guild_id).currently_auto_playing_in.set([])
                self._ll_guild_updates.discard(guild_id)
                self.bot.dispatch("red_audio_audio_disconnect", guild)
            if message_channel:
                message_channel = self.bot.get_channel(message_channel)
                if early_exit:
                    embed = discord.Embed(
                        colour=await self.bot.get_embed_color(message_channel),
                        title=_("Multiple Errors Detected"),
                        description=_(
                            "Closing the audio player "
                            "due to multiple errors being detected. "
                            "If this persists, please inform the bot owner "
                            "as the Audio cog may be temporally unavailable."),
                    )
                    await message_channel.send(embed=embed)
                    return
                else:
                    description = description or ""
                    if event_type == lavalink.LavalinkEvents.TRACK_STUCK:
                        embed = discord.Embed(
                            colour=await
                            self.bot.get_embed_color(message_channel),
                            title=_("Track Stuck"),
                            description=
                            _("Playback of the song has stopped due to an unexcepted error.\n{error}"
                              ).format(error=description),
                        )
                    else:
                        embed = discord.Embed(
                            title=_("Track Error"),
                            colour=await
                            self.bot.get_embed_color(message_channel),
                            description="{}\n{}".format(
                                extra.replace("\n", ""), description),
                        )
                        if current_id:
                            asyncio.create_task(
                                self.api_interface.global_cache_api.
                                report_invalid(current_id))
                    await message_channel.send(embed=embed)
            await player.skip()
Ejemplo n.º 6
0
    async def _websocket_closed_handler(
        self,
        guild: discord.Guild,
        player: lavalink.Player,
        extra: Dict,
        deafen: bool,
        disconnect: bool,
    ) -> None:
        guild_id = guild.id
        node = player.node
        voice_ws: DiscordWebSocket = node.get_voice_ws(guild_id)
        code = extra.get("code")
        by_remote = extra.get("byRemote", "")
        reason = extra.get("reason", "").strip()
        if self._ws_resume[guild_id].is_set():
            ws_audio_log.debug(
                f"WS EVENT | Discarding WS Closed event for guild {guild_id} -> "
                f"Socket Closed {voice_ws.socket._closing or voice_ws.socket.closed}.  "
                f"Code: {code} -- Remote: {by_remote} -- {reason}")
            return
        self._ws_resume[guild_id].set()
        if player.channel:
            current_perms = player.channel.permissions_for(
                player.channel.guild.me)
            has_perm = current_perms.speak and current_perms.connect
        else:
            has_perm = False
        channel_id = player.channel.id
        if voice_ws.socket._closing or voice_ws.socket.closed or not voice_ws.open:
            if player._con_delay:
                delay = player._con_delay.delay()
            else:
                player._con_delay = ExponentialBackoff(base=1)
                delay = player._con_delay.delay()
            ws_audio_log.warning(
                "YOU CAN IGNORE THIS UNLESS IT'S CONSISTENTLY REPEATING FOR THE SAME GUILD - "
                f"Voice websocket closed for guild {guild_id} -> "
                f"Socket Closed {voice_ws.socket._closing or voice_ws.socket.closed}.  "
                f"Code: {code} -- Remote: {by_remote} -- {reason}")
            ws_audio_log.debug(
                f"Reconnecting to channel {channel_id} in guild: {guild_id} | {delay:.2f}s"
            )
            await asyncio.sleep(delay)
            while voice_ws.socket._closing or voice_ws.socket.closed or not voice_ws.open:
                voice_ws = node.get_voice_ws(guild_id)
                await asyncio.sleep(0.1)

            if has_perm and player.current and player.is_playing:
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.connect(deafen=deafen)
                await player.resume(player.current, start=player.position)
                ws_audio_log.info(
                    "Voice websocket reconnected "
                    f"to channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Currently playing.")
            elif has_perm and player.paused and player.current:
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.connect(deafen=deafen)
                await player.pause(pause=True)
                ws_audio_log.info(
                    "Voice websocket reconnected "
                    f"to channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Currently Paused.")
            elif has_perm and (not disconnect) and (not player.is_playing):
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.connect(deafen=deafen)
                ws_audio_log.info(
                    "Voice websocket reconnected "
                    f"to channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Not playing, but auto disconnect disabled."
                )
                self._ll_guild_updates.discard(guild_id)
            elif not has_perm:
                self.bot.dispatch("red_audio_audio_disconnect", guild)
                ws_audio_log.info(
                    "Voice websocket disconnected "
                    f"from channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Missing permissions.")
                self._ll_guild_updates.discard(guild_id)
                player.store("autoplay_notified", False)
                await player.stop()
                await player.disconnect()
                await self.config.guild_from_id(
                    guild_id=guild_id).currently_auto_playing_in.set([])
            else:
                self.bot.dispatch("red_audio_audio_disconnect", guild)
                ws_audio_log.info(
                    "Voice websocket disconnected "
                    f"from channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Unknown.")
                self._ll_guild_updates.discard(guild_id)
                player.store("autoplay_notified", False)
                await player.stop()
                await player.disconnect()
                await self.config.guild_from_id(
                    guild_id=guild_id).currently_auto_playing_in.set([])
        elif code in (42069, ):
            player.store("resumes", player.fetch("resumes", 0) + 1)
            await player.connect(deafen=deafen)
            await player.resume(player.current, start=player.position)
            ws_audio_log.info(
                f"Player resumed in channel {channel_id} in guild: {guild_id} | "
                f"Reason: Error code {code} & {reason}.")
        elif code in (4015, 4014, 4009, 4006, 1006):
            if (code == 4006 and has_perm and player._last_resume
                    and player._last_resume + datetime.timedelta(seconds=5) >
                    datetime.datetime.now(tz=datetime.timezone.utc)):
                attempts = player.fetch("resume_attempts", 0)
                player.store("resume_attempts", attempts + 1)
                channel = self.bot.get_channel(player.channel.id)
                should_skip = not channel.members or all(
                    m.bot for m in channel.members)
                if should_skip:
                    ws_audio_log.info(
                        "Voice websocket reconnected skipped "
                        f"for channel {channel_id} in guild: {guild_id} | "
                        f"Reason: Error code {code} & "
                        "Player resumed too recently and no human members connected."
                    )
                    return

            if player._con_delay:
                delay = player._con_delay.delay()
            else:
                player._con_delay = ExponentialBackoff(base=1)
                delay = player._con_delay.delay()
            ws_audio_log.debug(
                f"Reconnecting to channel {channel_id} in guild: {guild_id} | {delay:.2f}s"
            )
            await asyncio.sleep(delay)
            if has_perm and player.current and player.is_playing:
                await player.connect(deafen=deafen)
                await player.resume(player.current, start=player.position)
                ws_audio_log.info(
                    "Voice websocket reconnected "
                    f"to channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Player is active.")
            elif has_perm and player.paused and player.current:
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.connect(deafen=deafen)
                await player.pause(pause=True)
                ws_audio_log.info(
                    "Voice websocket reconnected "
                    f"to channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Player is paused.")
            elif has_perm and (not disconnect) and (not player.is_playing):
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.connect(deafen=deafen)
                ws_audio_log.info(
                    "Voice websocket reconnected "
                    f"to channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Not playing.")
                self._ll_guild_updates.discard(guild_id)
            elif not has_perm:
                self.bot.dispatch("red_audio_audio_disconnect", guild)
                ws_audio_log.info(
                    "Voice websocket disconnected "
                    f"from channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Missing permissions.")
                self._ll_guild_updates.discard(guild_id)
                player.store("autoplay_notified", False)
                await player.stop()
                await player.disconnect()
                await self.config.guild_from_id(
                    guild_id=guild_id).currently_auto_playing_in.set([])
        else:
            ws_audio_log.info(
                "WS EVENT - IGNORED (Healthy Socket) | "
                f"Voice websocket closed event for guild {guild_id} -> "
                f"Code: {code} -- Remote: {by_remote} -- {reason}")
        self._ws_resume[guild_id].clear()
Ejemplo n.º 7
0
    async def _websocket_closed_handler(
        self,
        guild: discord.Guild,
        player: lavalink.Player,
        extra: Dict,
        deafen: bool,
        disconnect: bool,
    ) -> None:
        guild_id = guild.id
        event_channel_id = extra.get("channelID")
        try:
            if not self._ws_resume[guild_id].is_set():
                await self._ws_resume[guild_id].wait()
            else:
                self._ws_resume[guild_id].clear()
            node = player.node
            voice_ws: DiscordWebSocket = node.get_voice_ws(guild_id)
            code = extra.get("code")
            by_remote = extra.get("byRemote", "")
            reason = extra.get("reason", "No Specified Reason").strip()
            channel_id = player.channel.id
            try:
                event_channel_id, to_handle_code = await self._ws_op_codes[
                    guild_id].get()
            except asyncio.QueueEmpty:
                log.debug("Empty queue - Resuming Processor - Early exit")
                return

            if code != to_handle_code:
                code = to_handle_code
                if player.channel.id != event_channel_id:
                    code = 4014
            if event_channel_id != channel_id:
                ws_audio_log.info(
                    "Received an op code for a channel that is no longer valid; %d "
                    "Reason: Error code %d & %s, %r",
                    event_channel_id,
                    code,
                    reason,
                    player,
                )
                self._ws_op_codes[guild_id]._init(
                    self._ws_op_codes[guild_id]._maxsize)
                return
            if player.channel:
                has_perm = self.can_join_and_speak(player.channel)
            else:
                has_perm = False
            if code in (
                    1000,
            ) and has_perm and player.current and player.is_playing:
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.resume(player.current,
                                    start=player.position,
                                    replace=True)
                ws_audio_log.info(
                    "Player resumed | Reason: Error code %d & %s, %r", code,
                    reason, player)
                self._ws_op_codes[guild_id]._init(
                    self._ws_op_codes[guild_id]._maxsize)
                return

            if voice_ws.socket._closing or voice_ws.socket.closed or not voice_ws.open:
                if player._con_delay:
                    delay = player._con_delay.delay()
                else:
                    player._con_delay = ExponentialBackoff(base=1)
                    delay = player._con_delay.delay()
                ws_audio_log.warning(
                    "YOU CAN IGNORE THIS UNLESS IT'S CONSISTENTLY REPEATING FOR THE SAME GUILD - "
                    "Voice websocket closed for guild %d -> "
                    "Socket Closed %s.  "
                    "Code: %d -- Remote: %s -- %s, %r",
                    guild_id,
                    voice_ws.socket._closing or voice_ws.socket.closed,
                    code,
                    by_remote,
                    reason,
                    player,
                )
                ws_audio_log.debug(
                    "Reconnecting to channel %d in guild: %d | %.2fs",
                    channel_id,
                    guild_id,
                    delay,
                )
                await asyncio.sleep(delay)
                while voice_ws.socket._closing or voice_ws.socket.closed or not voice_ws.open:
                    voice_ws = node.get_voice_ws(guild_id)
                    await asyncio.sleep(0.1)

                if has_perm and player.current and player.is_playing:
                    player.store("resumes", player.fetch("resumes", 0) + 1)
                    await player.connect(deafen=deafen)
                    await player.resume(player.current,
                                        start=player.position,
                                        replace=True)
                    ws_audio_log.info(
                        "Voice websocket reconnected "
                        "Reason: Error code %d & Currently playing, %r",
                        code,
                        player,
                    )
                elif has_perm and player.paused and player.current:
                    player.store("resumes", player.fetch("resumes", 0) + 1)
                    await player.connect(deafen=deafen)
                    await player.resume(player.current,
                                        start=player.position,
                                        replace=True,
                                        pause=True)
                    ws_audio_log.info(
                        "Voice websocket reconnected "
                        "Reason: Error code %d & Currently Paused, %r",
                        code,
                        player,
                    )
                elif has_perm and (not disconnect) and (not player.is_playing):
                    player.store("resumes", player.fetch("resumes", 0) + 1)
                    await player.connect(deafen=deafen)
                    ws_audio_log.info(
                        "Voice websocket reconnected "
                        "Reason: Error code %d & Not playing, but auto disconnect disabled, %r",
                        code,
                        player,
                    )
                    self._ll_guild_updates.discard(guild_id)
                elif not has_perm:
                    self.bot.dispatch("red_audio_audio_disconnect", guild)
                    ws_audio_log.info(
                        "Voice websocket disconnected "
                        "Reason: Error code %d & Missing permissions, %r",
                        code,
                        player,
                    )
                    self._ll_guild_updates.discard(guild_id)
                    player.store("autoplay_notified", False)
                    await player.stop()
                    await player.disconnect()
                    await self.config.guild_from_id(
                        guild_id=guild_id).currently_auto_playing_in.set([])
                else:
                    self.bot.dispatch("red_audio_audio_disconnect", guild)
                    ws_audio_log.info(
                        "Voice websocket disconnected Reason: Error code %d & Unknown, %r",
                        code,
                        player,
                    )
                    self._ll_guild_updates.discard(guild_id)
                    player.store("autoplay_notified", False)
                    await player.stop()
                    await player.disconnect()
                    await self.config.guild_from_id(
                        guild_id=guild_id).currently_auto_playing_in.set([])
            elif code in (
                    42069,
            ) and has_perm and player.current and player.is_playing:
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.connect(deafen=deafen)
                await player.resume(player.current,
                                    start=player.position,
                                    replace=True)
                ws_audio_log.info(
                    "Player resumed - Reason: Error code %d & %s, %r", code,
                    reason, player)
            elif code in (4015, 4009, 4006, 4000, 1006):
                if player._con_delay:
                    delay = player._con_delay.delay()
                else:
                    player._con_delay = ExponentialBackoff(base=1)
                    delay = player._con_delay.delay()
                ws_audio_log.debug(
                    "Reconnecting to channel %d in guild: %d | %.2fs",
                    channel_id, guild_id, delay)
                await asyncio.sleep(delay)
                if has_perm and player.current and player.is_playing:
                    await player.connect(deafen=deafen)
                    await player.resume(player.current,
                                        start=player.position,
                                        replace=True)
                    ws_audio_log.info(
                        "Voice websocket reconnected "
                        "Reason: Error code %d & Player is active, %r",
                        code,
                        player,
                    )
                elif has_perm and player.paused and player.current:
                    player.store("resumes", player.fetch("resumes", 0) + 1)
                    await player.connect(deafen=deafen)
                    await player.resume(player.current,
                                        start=player.position,
                                        replace=True,
                                        pause=True)
                    ws_audio_log.info(
                        "Voice websocket reconnected "
                        "Reason: Error code %d & Player is paused, %r",
                        code,
                        player,
                    )
                elif has_perm and (not disconnect) and (not player.is_playing):
                    player.store("resumes", player.fetch("resumes", 0) + 1)
                    await player.connect(deafen=deafen)
                    ws_audio_log.info(
                        "Voice websocket reconnected "
                        "to channel %d in guild: %d | "
                        "Reason: Error code %d & Not playing, %r",
                        channel_id,
                        guild_id,
                        code,
                        player,
                    )
                    self._ll_guild_updates.discard(guild_id)
                elif not has_perm:
                    self.bot.dispatch("red_audio_audio_disconnect", guild)
                    ws_audio_log.info(
                        "Voice websocket disconnected "
                        "Reason: Error code %d & Missing permissions, %r",
                        code,
                        player,
                    )
                    self._ll_guild_updates.discard(guild_id)
                    player.store("autoplay_notified", False)
                    await player.stop()
                    await player.disconnect()
                    await self.config.guild_from_id(
                        guild_id=guild_id).currently_auto_playing_in.set([])
            else:
                if not player.paused and player.current:
                    player.store("resumes", player.fetch("resumes", 0) + 1)
                    await player.resume(player.current,
                                        start=player.position,
                                        replace=True)
                    ws_audio_log.info(
                        "WS EVENT - SIMPLE RESUME (Healthy Socket) | "
                        "Voice websocket closed event "
                        "Code: %d -- Remote: %s -- %s, %r",
                        code,
                        by_remote,
                        reason,
                        player,
                    )
                else:
                    ws_audio_log.info(
                        "WS EVENT - IGNORED (Healthy Socket) | "
                        "Voice websocket closed event "
                        "Code: %d -- Remote: %s -- %s, %r",
                        code,
                        by_remote,
                        reason,
                        player,
                    )
        except Exception:
            log.exception("Error in task")
        finally:
            self._ws_op_codes[guild_id]._init(
                self._ws_op_codes[guild_id]._maxsize)
            self._ws_resume[guild_id].set()
Ejemplo n.º 8
0
    async def _eq_interact(
        self,
        ctx: commands.Context,
        player: lavalink.Player,
        eq: Equalizer,
        message: discord.Message,
        selected: int,
    ) -> None:
        player.store("eq", eq)
        emoji = {
            "far_left": "\N{BLACK LEFT-POINTING TRIANGLE}",
            "one_left": "\N{LEFTWARDS BLACK ARROW}",
            "max_output": "\N{BLACK UP-POINTING DOUBLE TRIANGLE}",
            "output_up": "\N{UP-POINTING SMALL RED TRIANGLE}",
            "output_down": "\N{DOWN-POINTING SMALL RED TRIANGLE}",
            "min_output": "\N{BLACK DOWN-POINTING DOUBLE TRIANGLE}",
            "one_right": "\N{BLACK RIGHTWARDS ARROW}",
            "far_right": "\N{BLACK RIGHT-POINTING TRIANGLE}",
            "reset": "\N{BLACK CIRCLE FOR RECORD}",
            "info": "\N{INFORMATION SOURCE}",
        }
        selector = f'{" " * 8}{"   " * selected}^^'
        try:
            await message.edit(
                content=box(f"{eq.visualise()}\n{selector}", lang="ini"))
        except discord.errors.NotFound:
            return
        try:
            (react_emoji,
             react_user) = await self._get_eq_reaction(ctx, message, emoji)
        except TypeError:
            return

        if not react_emoji:
            await self.config.custom("EQUALIZER",
                                     ctx.guild.id).eq_bands.set(eq.bands)
            await self._clear_react(message, emoji)

        if react_emoji == "\N{LEFTWARDS BLACK ARROW}":
            await self.remove_react(message, react_emoji, react_user)
            await self._eq_interact(ctx, player, eq, message,
                                    max(selected - 1, 0))

        if react_emoji == "\N{BLACK RIGHTWARDS ARROW}":
            await self.remove_react(message, react_emoji, react_user)
            await self._eq_interact(ctx, player, eq, message,
                                    min(selected + 1, 14))

        if react_emoji == "\N{UP-POINTING SMALL RED TRIANGLE}":
            await self.remove_react(message, react_emoji, react_user)
            _max = float("{:.2f}".format(min(eq.get_gain(selected) + 0.1,
                                             1.0)))
            eq.set_gain(selected, _max)
            await self._apply_gain(ctx.guild.id, selected, _max)
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{DOWN-POINTING SMALL RED TRIANGLE}":
            await self.remove_react(message, react_emoji, react_user)
            _min = float("{:.2f}".format(
                max(eq.get_gain(selected) - 0.1, -0.25)))
            eq.set_gain(selected, _min)
            await self._apply_gain(ctx.guild.id, selected, _min)
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{BLACK UP-POINTING DOUBLE TRIANGLE}":
            await self.remove_react(message, react_emoji, react_user)
            _max = 1.0
            eq.set_gain(selected, _max)
            await self._apply_gain(ctx.guild.id, selected, _max)
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{BLACK DOWN-POINTING DOUBLE TRIANGLE}":
            await self.remove_react(message, react_emoji, react_user)
            _min = -0.25
            eq.set_gain(selected, _min)
            await self._apply_gain(ctx.guild.id, selected, _min)
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{BLACK LEFT-POINTING TRIANGLE}":
            await self.remove_react(message, react_emoji, react_user)
            selected = 0
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{BLACK RIGHT-POINTING TRIANGLE}":
            await self.remove_react(message, react_emoji, react_user)
            selected = 14
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{BLACK CIRCLE FOR RECORD}":
            await self.remove_react(message, react_emoji, react_user)
            for band in range(eq.band_count):
                eq.set_gain(band, 0.0)
            await self._apply_gains(ctx.guild.id, eq.bands)
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{INFORMATION SOURCE}":
            await self.remove_react(message, react_emoji, react_user)
            await ctx.send_help(self.command_equalizer)
            await self._eq_interact(ctx, player, eq, message, selected)
Ejemplo n.º 9
0
    async def lavalink_event_handler(self, player: lavalink.Player,
                                     event_type: lavalink.LavalinkEvents,
                                     extra) -> None:
        current_track = player.current
        current_channel = player.channel
        guild = self.rgetattr(current_channel, "guild", None)
        guild_id = self.rgetattr(guild, "id", None)
        current_requester = self.rgetattr(current_track, "requester", None)
        current_stream = self.rgetattr(current_track, "is_stream", None)
        current_length = self.rgetattr(current_track, "length", None)
        current_thumbnail = self.rgetattr(current_track, "thumbnail", None)
        current_extras = self.rgetattr(current_track, "extras", {})
        guild_data = await self.config.guild(guild).all()
        repeat = guild_data["repeat"]
        notify = guild_data["notify"]
        disconnect = guild_data["disconnect"]
        autoplay = guild_data["auto_play"]
        description = self.get_track_description(
            current_track, self.local_folder_current_path)
        status = await self.config.status()
        log.debug(
            f"Received a new lavalink event for {guild_id}: {event_type}: {extra}"
        )
        prev_song: lavalink.Track = player.fetch("prev_song")
        await self.maybe_reset_error_counter(player)

        if event_type == lavalink.LavalinkEvents.TRACK_START:
            self.skip_votes[guild] = []
            playing_song = player.fetch("playing_song")
            requester = player.fetch("requester")
            player.store("prev_song", playing_song)
            player.store("prev_requester", requester)
            player.store("playing_song", current_track)
            player.store("requester", current_requester)
            self.bot.dispatch("red_audio_track_start", guild, current_track,
                              current_requester)
        if event_type == lavalink.LavalinkEvents.TRACK_END:
            prev_requester = player.fetch("prev_requester")
            self.bot.dispatch("red_audio_track_end", guild, prev_song,
                              prev_requester)
        if event_type == lavalink.LavalinkEvents.QUEUE_END:
            prev_requester = player.fetch("prev_requester")
            self.bot.dispatch("red_audio_queue_end", guild, prev_song,
                              prev_requester)
            if (autoplay and not player.queue
                    and player.fetch("playing_song") is not None
                    and self.playlist_api is not None
                    and self.api_interface is not None):
                try:
                    await self.api_interface.autoplay(player,
                                                      self.playlist_api)
                except DatabaseError:
                    notify_channel = player.fetch("channel")
                    if notify_channel:
                        notify_channel = self.bot.get_channel(notify_channel)
                        await self.send_embed_msg(
                            notify_channel,
                            title=_("Couldn't get a valid track."))
                    return
        if event_type == lavalink.LavalinkEvents.TRACK_START and notify:
            notify_channel = player.fetch("channel")
            if notify_channel:
                notify_channel = self.bot.get_channel(notify_channel)
                if player.fetch("notify_message") is not None:
                    with contextlib.suppress(discord.HTTPException):
                        await player.fetch("notify_message").delete()

                if (autoplay and current_extras.get("autoplay")
                        and (prev_song is None or
                             (hasattr(prev_song, "extras")
                              and not prev_song.extras.get("autoplay")))):
                    await self.send_embed_msg(notify_channel,
                                              title=_("Auto Play started."))

                if not description:
                    return
                if current_stream:
                    dur = "LIVE"
                else:
                    dur = self.format_time(current_length)

                thumb = None
                if await self.config.guild(guild
                                           ).thumbnail() and current_thumbnail:
                    thumb = current_thumbnail

                notify_message = await self.send_embed_msg(
                    notify_channel,
                    title=_("Now Playing"),
                    description=description,
                    footer=_("Track length: {length} | Requested by: {user}").
                    format(length=dur, user=current_requester),
                    thumbnail=thumb,
                )
                player.store("notify_message", notify_message)
        if event_type == lavalink.LavalinkEvents.TRACK_START and status:
            player_check = self.get_active_player_count()
            await self.update_bot_presence(*player_check)

        if event_type == lavalink.LavalinkEvents.TRACK_END and status:
            await asyncio.sleep(1)
            if not player.is_playing:
                player_check = self.get_active_player_count()
                await self.update_bot_presence(*player_check)

        if event_type == lavalink.LavalinkEvents.QUEUE_END:
            if not autoplay:
                notify_channel = player.fetch("channel")
                if notify_channel and notify:
                    notify_channel = self.bot.get_channel(notify_channel)
                    await self.send_embed_msg(notify_channel,
                                              title=_("Queue ended."))
                if disconnect:
                    self.bot.dispatch("red_audio_audio_disconnect", guild)
                    await player.disconnect()
            if status:
                player_check = self.get_active_player_count()
                await self.update_bot_presence(*player_check)

        if event_type in [
                lavalink.LavalinkEvents.TRACK_EXCEPTION,
                lavalink.LavalinkEvents.TRACK_STUCK,
        ]:
            message_channel = player.fetch("channel")
            while True:
                if current_track in player.queue:
                    player.queue.remove(current_track)
                else:
                    break
            if repeat:
                player.current = None
            if not guild_id:
                return
            self._error_counter.setdefault(guild_id, 0)
            if guild_id not in self._error_counter:
                self._error_counter[guild_id] = 0
            early_exit = await self.increase_error_counter(player)
            if early_exit:
                self._disconnected_players[guild_id] = True
                self.play_lock[guild_id] = False
                eq = player.fetch("eq")
                player.queue = []
                player.store("playing_song", None)
                if eq:
                    await self.config.custom("EQUALIZER",
                                             guild_id).eq_bands.set(eq.bands)
                await player.stop()
                await player.disconnect()
                self.bot.dispatch("red_audio_audio_disconnect", guild)
            if message_channel:
                message_channel = self.bot.get_channel(message_channel)
                if early_exit:
                    embed = discord.Embed(
                        colour=await self.bot.get_embed_color(message_channel),
                        title=_("Multiple Errors Detected"),
                        description=_(
                            "Closing the audio player "
                            "due to multiple errors being detected. "
                            "If this persists, please inform the bot owner "
                            "as the Audio cog may be temporally unavailable."),
                    )
                    await message_channel.send(embed=embed)
                    return
                else:
                    description = description or ""
                    if event_type == lavalink.LavalinkEvents.TRACK_STUCK:
                        embed = discord.Embed(
                            colour=await
                            self.bot.get_embed_color(message_channel),
                            title=_("Track Stuck"),
                            description="{}".format(description),
                        )
                    else:
                        embed = discord.Embed(
                            title=_("Track Error"),
                            colour=await
                            self.bot.get_embed_color(message_channel),
                            description="{}\n{}".format(
                                extra.replace("\n", ""), description),
                        )
                    await message_channel.send(embed=embed)
            await player.skip()