Init repo

This commit is contained in:
2025-01-25 02:31:27 -05:00
commit fd0231690b
12 changed files with 735 additions and 0 deletions

157
app/bot.py Normal file
View File

@@ -0,0 +1,157 @@
import asyncio
from typing import Optional, Dict
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import discord
from discord.ext import commands
from NoiseGatev2 import NoiseGate
import op25_controller
import pulse
# Define FastAPI app
app = FastAPI()
# Discord Bot Setup
intents = discord.Intents.default()
intents.voice_states = True
intents.guilds = True
# Models for API requests
class BotConfig(BaseModel):
token: str # Discord Bot Token
class VoiceChannelRequest(BaseModel):
guild_id: int
channel_id: int
# Discord Bot Manager
class DiscordBotManager:
def __init__(self):
self.bot: Optional[commands.Bot] = None
self.bot_task: Optional[asyncio.Task] = None
self.voice_clients: Dict[int, discord.VoiceClient] = {}
self.token: Optional[str] = None
self.loop = asyncio.get_event_loop()
self.lock = asyncio.Lock()
async def start_bot(self, token: str):
async with self.lock:
if self.bot and self.bot.is_closed():
raise RuntimeError("Bot is already running.")
if self.bot_task and not self.bot_task.done():
raise RuntimeError("Bot is already running.")
self.token = token
self.bot = commands.Bot(command_prefix="!", intents=intents)
@self.bot.event
async def on_ready():
print(f'Logged in as {self.bot.user}')
# Handle graceful shutdown when all voice connections are closed
@self.bot.event
async def on_voice_state_update(member, before, after):
# Check if all voice clients are disconnected
await asyncio.sleep(1) # Give time for the state to update
if not self.voice_clients:
await self.stop_bot()
# Start the bot in the background
self.bot_task = self.loop.create_task(self.bot.start(token))
async def stop_bot(self):
async with self.lock:
if self.bot:
await self.bot.close()
self.bot = None
if self.bot_task:
await self.bot_task
self.bot_task = None
self.voice_clients.clear()
print("Bot has been stopped.")
async def join_voice_channel(self, guild_id: int, channel_id: int, ng_threshold: int = 50, device_id: int = 4):
if not self.bot:
raise RuntimeError("Bot is not running.")
guild = self.bot.get_guild(guild_id)
if not guild:
raise ValueError("Guild not found.")
channel = guild.get_channel(channel_id)
if not isinstance(channel, discord.VoiceChannel):
raise ValueError("Channel is not a voice channel.")
if guild_id in self.voice_clients:
raise RuntimeError("Already connected to this guild's voice channel.")
voice_client = await channel.connect()
streamHandler = NoiseGate(
_input_device_index=device_id,
_voice_connection=voice_client,
_noise_gate_threshold=ng_threshold)
# Start the audio stream
streamHandler.run()
self.voice_clients[guild_id] = voice_client
print(f"Joined guild {guild_id} voice channel {channel_id}.")
async def leave_voice_channel(self, guild_id: int):
if not self.bot:
raise RuntimeError("Bot is not running.")
voice_client = self.voice_clients.get(guild_id)
if not voice_client:
raise RuntimeError("Not connected to the specified guild's voice channel.")
await voice_client.disconnect()
del self.voice_clients[guild_id]
print(f"Left guild {guild_id} voice channel.")
# Initialize Discord Bot Manager
bot_manager = DiscordBotManager()
# API Endpoints
@app.post("/start_bot")
async def start_bot(config: BotConfig):
try:
await bot_manager.start_bot(config.token)
return {"status": "Bot started successfully."}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.post("/stop_bot")
async def stop_bot():
try:
await bot_manager.stop_bot()
return {"status": "Bot stopped successfully."}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.post("/join_voice")
async def join_voice_channel(request: VoiceChannelRequest):
try:
await bot_manager.join_voice_channel(request.guild_id, request.channel_id)
return {"status": f"Joined guild {request.guild_id} voice channel {request.channel_id}."}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.post("/leave_voice")
async def leave_voice_channel(request: VoiceChannelRequest):
try:
await bot_manager.leave_voice_channel(request.guild_id)
return {"status": f"Left guild {request.guild_id} voice channel."}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/status")
async def get_status():
status = {
"bot_running": bot_manager.bot is not None and not bot_manager.bot.is_closed(),
"connected_guilds": list(bot_manager.voice_clients.keys())
}
return status
app.include_router(op25_controller.router, prefix="/op25")
app.include_router(pulse.router, prefix="/pulse")