2025-02-13 14:51:35 -05:00
|
|
|
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-03-24 09:06:56 -04:00
|
|
|
from util.radio_util import get_now_playing, skip
|
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
|
|
|
|
2025-04-17 14:35:56 -04:00
|
|
|
|
2025-02-13 14:51:35 -05:00
|
|
|
class Radio(commands.Cog):
|
|
|
|
"""Radio Cog for Havoc"""
|
2025-04-17 14:35:56 -04:00
|
|
|
|
2025-02-15 08:36:45 -05:00
|
|
|
def __init__(self, bot: Havoc) -> None:
|
|
|
|
self.bot: Havoc = bot
|
|
|
|
self.channels: dict[str, tuple] = {
|
2025-04-17 14:35:56 -04:00
|
|
|
"sfm": (
|
|
|
|
1145182936002482196,
|
|
|
|
1221615558492029050,
|
|
|
|
), # Tuple: Guild Id, Chan Id
|
|
|
|
}
|
2025-03-10 09:59:42 -04:00
|
|
|
self.STREAM_URL: str = "https://stream.codey.lol/sfm.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:
|
2025-04-17 14:35:56 -04:00
|
|
|
logging.debug("Failed to cancel radio_state_loop: %s", str(e))
|
|
|
|
|
2025-02-13 14:51:35 -05:00
|
|
|
@commands.Cog.listener()
|
|
|
|
async def on_ready(self) -> None:
|
|
|
|
"""Run on Bot Ready"""
|
|
|
|
await self.radio_init()
|
2025-04-17 14:35:56 -04:00
|
|
|
|
|
|
|
def is_radio_chan(): # type: ignore
|
2025-02-13 14:51:35 -05:00
|
|
|
"""Check if channel is radio chan"""
|
2025-04-17 14:35:56 -04:00
|
|
|
|
2025-02-13 14:51:35 -05:00
|
|
|
def predicate(ctx):
|
|
|
|
try:
|
|
|
|
return ctx.channel.id == 1221615558492029050
|
|
|
|
except:
|
|
|
|
traceback.print_exc()
|
|
|
|
return False
|
2025-04-17 14:35:56 -04:00
|
|
|
|
|
|
|
return commands.check(predicate)
|
|
|
|
|
2025-02-13 14:51:35 -05:00
|
|
|
@bridge.bridge_command()
|
2025-04-17 14:35:56 -04:00
|
|
|
@commands.is_owner()
|
2025-02-13 14:51:35 -05:00
|
|
|
async def reinitradio(self, ctx) -> None:
|
|
|
|
"""
|
|
|
|
Reinitialize serious.FM
|
|
|
|
"""
|
|
|
|
loop: discord.asyncio.AbstractEventLoop = self.bot.loop
|
|
|
|
loop.create_task(self.radio_init())
|
|
|
|
await ctx.respond("Done!", ephemeral=True)
|
2025-04-17 14:35:56 -04:00
|
|
|
|
2025-02-13 14:51:35 -05:00
|
|
|
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:
|
2025-04-17 14:35:56 -04:00
|
|
|
(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
|
2025-04-17 14:35:56 -04:00
|
|
|
channel = guild.get_channel(radio_chan)
|
2025-02-15 13:57:47 -05:00
|
|
|
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:
|
2025-04-17 14:35:56 -04:00
|
|
|
logging.debug("Failed to cancel radio_state_loop: %s", str(e))
|
2025-02-13 14:51:35 -05:00
|
|
|
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-04-17 14:35:56 -04:00
|
|
|
|
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:
|
2025-04-17 14:35:56 -04:00
|
|
|
(radio_guild, radio_chan) = self.channels["sfm"]
|
2025-02-13 14:51:35 -05:00
|
|
|
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-04-17 14:35:56 -04:00
|
|
|
|
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-04-17 14:35:56 -04:00
|
|
|
source: discord.FFmpegAudio = discord.FFmpegOpusAudio(
|
|
|
|
self.STREAM_URL, before_options="-timeout 3000000"
|
|
|
|
)
|
|
|
|
vc.play(
|
|
|
|
source, # type: ignore
|
|
|
|
after=lambda e: logging.info("Error: %s", e) if e else None,
|
|
|
|
)
|
|
|
|
# Get Now Playing
|
2025-04-10 19:52:29 -04:00
|
|
|
np_track: Optional[str] = await get_now_playing()
|
|
|
|
if np_track and not self.LAST_NP_TRACK == np_track:
|
|
|
|
self.LAST_NP_TRACK = np_track
|
2025-04-17 14:35:56 -04:00
|
|
|
await self.bot.change_presence(
|
|
|
|
activity=discord.Activity(
|
|
|
|
type=discord.ActivityType.listening, name=np_track
|
|
|
|
)
|
|
|
|
)
|
2025-02-13 14:51:35 -05:00
|
|
|
except:
|
|
|
|
traceback.print_exc()
|
2025-04-17 14:35:56 -04:00
|
|
|
|
2025-03-24 09:06:56 -04:00
|
|
|
@bridge.bridge_command()
|
|
|
|
@commands.is_owner()
|
|
|
|
async def skip(self, ctx) -> None:
|
|
|
|
"""
|
|
|
|
Skip - Convenience Command
|
|
|
|
"""
|
2025-04-17 14:35:56 -04:00
|
|
|
|
2025-03-24 09:06:56 -04:00
|
|
|
await skip()
|
|
|
|
return await ctx.respond("OK", ephemeral=True)
|
2025-04-17 14:35:56 -04:00
|
|
|
|
2025-02-13 14:51:35 -05:00
|
|
|
def cog_unload(self) -> None:
|
2025-04-17 14:35:56 -04:00
|
|
|
"""Run on Cog Unload"""
|
2025-02-13 14:51:35 -05:00
|
|
|
self.radio_state_loop.cancel()
|
2025-04-17 14:35:56 -04:00
|
|
|
|
|
|
|
|
2025-02-13 14:51:35 -05:00
|
|
|
def setup(bot) -> None:
|
|
|
|
"""Run on Cog Load"""
|
2025-04-17 14:35:56 -04:00
|
|
|
bot.add_cog(Radio(bot))
|