discord-havoc/cogs/radio.py

130 lines
4.7 KiB
Python
Raw Normal View History

2025-02-13 14:51:35 -05:00
#!/usr/bin/env python3.12
# pylint: disable=bare-except, broad-exception-caught, invalid-name
import logging
import traceback
2025-02-14 07:10:09 -05:00
from typing import Optional
2025-02-13 14:51:35 -05:00
from discord.ext import bridge, commands, tasks
2025-02-14 07:10:09 -05:00
from util.radio_util import get_now_playing
2025-02-13 14:51:35 -05:00
import discord
2025-02-15 08:36:45 -05:00
from disc_havoc import Havoc
2025-02-13 14:51:35 -05:00
class Radio(commands.Cog):
"""Radio Cog for Havoc"""
2025-02-15 08:36:45 -05:00
def __init__(self, bot: Havoc) -> None:
self.bot: Havoc = bot
self.channels: dict[str, tuple] = {
2025-02-13 14:51:35 -05:00
'sfm': (1145182936002482196, 1221615558492029050), # Tuple: Guild Id, Chan Id
}
self.STREAM_URL: str = "https://relay.sfm.codey.lol/aces.ogg"
2025-02-14 07:10:09 -05:00
self.LAST_NP_TRACK: Optional[str] = None
2025-02-13 14:51:35 -05:00
try:
self.radio_state_loop.cancel()
except Exception as e:
logging.debug("Failed to cancel radio_state_loop: %s",
str(e))
@commands.Cog.listener()
async def on_ready(self) -> None:
"""Run on Bot Ready"""
await self.radio_init()
2025-02-15 13:57:47 -05:00
def is_radio_chan(): # type: ignore
2025-02-13 14:51:35 -05:00
"""Check if channel is radio chan"""
def predicate(ctx):
try:
return ctx.channel.id == 1221615558492029050
except:
traceback.print_exc()
return False
return commands.check(predicate)
@bridge.bridge_command()
@commands.is_owner()
async def reinitradio(self, ctx) -> None:
"""
Reinitialize serious.FM
2025-02-16 20:07:02 -05:00
2025-02-13 14:51:35 -05:00
Args:
ctx (Any): Discord context
Returns:
None
"""
loop: discord.asyncio.AbstractEventLoop = self.bot.loop
loop.create_task(self.radio_init())
await ctx.respond("Done!", ephemeral=True)
async def radio_init(self) -> None:
2025-02-16 20:07:02 -05:00
"""Init Radio"""
2025-02-13 14:51:35 -05:00
try:
(radio_guild, radio_chan) = self.channels['sfm']
2025-02-15 13:57:47 -05:00
guild: Optional[discord.Guild] = self.bot.get_guild(radio_guild)
if not guild:
return
channel = guild.get_channel(radio_chan)
if not isinstance(channel, discord.VoiceChannel):
return
2025-02-13 14:51:35 -05:00
if not self.bot.voice_clients:
await channel.connect()
try:
try:
self.radio_state_loop.cancel()
except Exception as e:
logging.debug("Failed to cancel radio_state_loop: %s",
str(e))
self.radio_state_loop.start()
logging.info("radio_state_loop task started!")
except:
logging.critical("Could not start task...")
traceback.print_exc()
except:
traceback.print_exc()
return
2025-02-14 07:10:09 -05:00
@tasks.loop(seconds=5.0)
2025-02-13 14:51:35 -05:00
async def radio_state_loop(self) -> None:
"""Radio State Loop"""
try:
(radio_guild, radio_chan) = self.channels['sfm']
try:
2025-02-15 13:57:47 -05:00
vc: discord.VoiceProtocol = self.bot.voice_clients[-1]
2025-02-13 14:51:35 -05:00
except:
logging.debug("No voice client, establishing new VC connection...")
2025-02-15 13:57:47 -05:00
guild: Optional[discord.Guild] = self.bot.get_guild(radio_guild)
if not guild:
return
channel = guild.get_channel(radio_chan)
if not isinstance(channel, discord.VoiceChannel):
return
2025-02-13 14:51:35 -05:00
await channel.connect()
vc = self.bot.voice_clients[-1]
2025-02-15 13:57:47 -05:00
if not vc.is_playing() or vc.is_paused(): # type: ignore
"""
Mypy does not seem aware of the is_playing, play, and is_paused methods,
but they exist.
"""
2025-02-13 14:51:35 -05:00
logging.info("Detected VC not playing... playing!")
2025-02-16 13:16:53 -05:00
source: discord.FFmpegAudio = discord.FFmpegOpusAudio(self.STREAM_URL,
2025-02-13 14:51:35 -05:00
before_options="-timeout 3000000")
2025-02-16 13:16:53 -05:00
vc.play(source, # type: ignore
after=lambda e: logging.info("Error: %s", e) if e\
else None)
2025-02-14 07:10:09 -05:00
# Get Now Playing
2025-02-16 13:16:53 -05:00
np_track: Optional[str] = await get_now_playing()
2025-02-14 07:10:09 -05:00
if np_track and not self.LAST_NP_TRACK == np_track:
2025-02-15 13:57:47 -05:00
self.LAST_NP_TRACK = np_track
if isinstance(vc.channel, discord.VoiceChannel):
await vc.channel.set_status(f"Now playing: {np_track}")
2025-02-13 14:51:35 -05:00
except:
traceback.print_exc()
# pylint: enable=superfluous-parens
def cog_unload(self) -> None:
"""Run on Cog Unload"""
self.radio_state_loop.cancel()
def setup(bot) -> None:
"""Run on Cog Load"""
bot.add_cog(Radio(bot))