changes
This commit is contained in:
@@ -19,7 +19,7 @@ class RadioBot:
|
||||
f"http://{settings.icecast_host}:{settings.icecast_port}{settings.icecast_mount}"
|
||||
)
|
||||
|
||||
async def join(self, guild_id: int, channel_id: int, token: str) -> bool:
|
||||
async def join(self, guild_id: int, channel_id: int, token: str, call_active: bool = False) -> bool:
|
||||
# (Re)start the bot if the token changed or the bot isn't running
|
||||
if self._current_token != token or not self._is_bot_running():
|
||||
if not await self._start_bot(token):
|
||||
@@ -39,8 +39,10 @@ class RadioBot:
|
||||
if self._voice_client and self._voice_client.is_connected():
|
||||
await self._voice_client.disconnect(force=True)
|
||||
self._voice_client = await channel.connect()
|
||||
self._play_stream()
|
||||
logger.info(f"Streaming to #{channel.name} in {guild.name}")
|
||||
# Only start playing immediately if a call is currently active
|
||||
if call_active:
|
||||
self._play_stream()
|
||||
logger.info(f"Joined #{channel.name} in {guild.name} (streaming={'yes' if call_active else 'waiting for call'})")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to join voice channel: {e}")
|
||||
@@ -49,6 +51,7 @@ class RadioBot:
|
||||
async def leave(self) -> bool:
|
||||
if self._voice_client and self._voice_client.is_connected():
|
||||
try:
|
||||
self._stop_stream()
|
||||
await self._voice_client.disconnect(force=True)
|
||||
self._voice_client = None
|
||||
logger.info("Disconnected from voice channel.")
|
||||
@@ -57,6 +60,19 @@ class RadioBot:
|
||||
logger.error(f"Failed to disconnect: {e}")
|
||||
return False
|
||||
|
||||
def start_stream(self):
|
||||
"""Called when an OP25 call starts — begin transmitting audio and light the ring."""
|
||||
if self._voice_client and self._voice_client.is_connected():
|
||||
if not self._voice_client.is_playing():
|
||||
self._play_stream()
|
||||
logger.debug("Stream started (call active).")
|
||||
|
||||
def stop_stream(self):
|
||||
"""Called when an OP25 call ends — stop transmitting so the ring goes dark."""
|
||||
if self._voice_client and self._voice_client.is_connected():
|
||||
self._stop_stream()
|
||||
logger.debug("Stream stopped (call ended).")
|
||||
|
||||
async def stop(self):
|
||||
await self.leave()
|
||||
if self._task:
|
||||
@@ -80,11 +96,17 @@ class RadioBot:
|
||||
after=lambda e: logger.error(f"Stream ended unexpectedly: {e}") if e else None,
|
||||
)
|
||||
|
||||
def _stop_stream(self):
|
||||
if self._voice_client and self._voice_client.is_playing():
|
||||
self._voice_client.stop()
|
||||
|
||||
async def _start_bot(self, token: str) -> bool:
|
||||
await self.stop() # clean up any previous instance
|
||||
|
||||
intents = discord.Intents.default()
|
||||
intents.voice_states = True
|
||||
intents.message_content = True
|
||||
intents.messages = True
|
||||
self._bot = commands.Bot(command_prefix="!", intents=intents)
|
||||
self._ready_event = asyncio.Event()
|
||||
self._current_token = token
|
||||
@@ -94,6 +116,37 @@ class RadioBot:
|
||||
logger.info(f"Discord bot ready: {self._bot.user} ({self._bot.user.id})")
|
||||
self._ready_event.set()
|
||||
|
||||
@self._bot.event
|
||||
async def on_message(message: discord.Message):
|
||||
if message.author.bot:
|
||||
return
|
||||
if self._bot.user not in message.mentions:
|
||||
return
|
||||
content = message.content.lower()
|
||||
if "leave" in content:
|
||||
await self.leave()
|
||||
try:
|
||||
await message.reply("Disconnected.")
|
||||
except Exception:
|
||||
pass
|
||||
elif "joinme" in content or "join" in content:
|
||||
member = message.guild.get_member(message.author.id) if message.guild else None
|
||||
vc = member.voice.channel if member and member.voice else None
|
||||
if not vc:
|
||||
try:
|
||||
await message.reply("You're not in a voice channel.")
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
try:
|
||||
if self._voice_client and self._voice_client.is_connected():
|
||||
await self._voice_client.move_to(vc)
|
||||
else:
|
||||
self._voice_client = await vc.connect()
|
||||
await message.reply(f"Joined {vc.name}.")
|
||||
except Exception as e:
|
||||
logger.error(f"joinme failed: {e}")
|
||||
|
||||
self._task = asyncio.create_task(self._bot.start(token))
|
||||
|
||||
try:
|
||||
|
||||
@@ -7,8 +7,8 @@ from app.internal.logger import logger
|
||||
|
||||
CallbackFn = Callable[[dict], Awaitable[None]]
|
||||
|
||||
HANG_THRESHOLD = 3 # polls before declaring a call ended (1 poll/sec → 3s hang time)
|
||||
POLL_INTERVAL = 1.0 # seconds
|
||||
HANG_THRESHOLD = 2 # polls before declaring a call ended (0.5s poll → 1s hang time)
|
||||
POLL_INTERVAL = 0.5 # seconds
|
||||
|
||||
|
||||
class MetadataWatcher:
|
||||
|
||||
@@ -53,9 +53,18 @@ class OP25Client:
|
||||
"""Poll the OP25 HTTP terminal for current call metadata."""
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=3) as client:
|
||||
r = await client.get(f"{self.terminal_url}/0/status.json")
|
||||
r = await client.post(
|
||||
self.terminal_url,
|
||||
json=[{"command": "update", "arg1": 0, "arg2": 0}],
|
||||
)
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
messages = r.json()
|
||||
for msg in messages:
|
||||
if msg.get("json_type") == "channel_update":
|
||||
channels = msg.get("channels", [])
|
||||
if channels:
|
||||
return msg.get(str(channels[0]), {})
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user