mirror of
https://github.com/System-End/tts.git
synced 2026-04-19 23:22:51 +00:00
Update bot.py
This commit is contained in:
parent
e4a741db72
commit
80ba304043
1 changed files with 73 additions and 23 deletions
96
bot.py
96
bot.py
|
|
@ -2,6 +2,7 @@ import asyncio
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
|
|
@ -25,15 +26,33 @@ with open("config.json", "r", encoding="utf-8") as f:
|
|||
with open("voices.json", "r", encoding="utf-8") as f:
|
||||
voices_data = json.load(f)
|
||||
|
||||
Path("audio_cache").mkdir(exist_ok=True)
|
||||
data directory (set this in your deployment environment, e.g. Coolify)
|
||||
DATA_DIR = os.getenv("DATA_DIR", "/app/data")
|
||||
Path(DATA_DIR).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
SETTINGS_FILE = "guild_settings.json"
|
||||
SETTINGS_FILE = os.path.join(DATA_DIR, "guild_settings.json")
|
||||
|
||||
AUDIO_CACHE_DIR = os.path.join(DATA_DIR, "audio_cache")
|
||||
Path
|
||||
(AUDIO_CACHE_DIR).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
_image_settings = "/app/guild_settings.json"
|
||||
if not os.path.exists(SETTINGS_FILE) and os.path.exists(_image_settings):
|
||||
try:
|
||||
shutil.copy(_image_settings, SETTINGS_FILE)
|
||||
logger.info("Migrated guild_settings.json into DATA_DIR")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to migrate settings: {e}")
|
||||
|
||||
|
||||
def load_guild_settings() -> dict:
|
||||
if os.path.exists(SETTINGS_FILE):
|
||||
with open(SETTINGS_FILE, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
try:
|
||||
with open(SETTINGS_FILE, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load settings file {SETTINGS_FILE}: {e}")
|
||||
return {}
|
||||
return {}
|
||||
|
||||
|
||||
|
|
@ -44,8 +63,11 @@ def save_guild_settings():
|
|||
"tts_channels": data.get("tts_channels", []),
|
||||
"bound_vc": data.get("bound_vc", None),
|
||||
}
|
||||
with open(SETTINGS_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(saveable, f, indent=2)
|
||||
try:
|
||||
with open(SETTINGS_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(saveable, f, indent=2)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to write settings to {SETTINGS_FILE}: {e}")
|
||||
|
||||
|
||||
guild_settings: dict = load_guild_settings()
|
||||
|
|
@ -92,6 +114,7 @@ def find_voice(name: str):
|
|||
|
||||
|
||||
def ffmpeg_options() -> dict:
|
||||
# Use the config audio settings; ffmpeg filter string can be adjusted as needed.
|
||||
return {
|
||||
"options": (
|
||||
f"-vn -b:a {config['audio']['bitrate']}k "
|
||||
|
|
@ -126,12 +149,20 @@ async def cleanup(path: str, delay: int = 8):
|
|||
async def ensure_connected(
|
||||
guild: discord.Guild, member: discord.Member
|
||||
) -> discord.VoiceClient | None:
|
||||
if not member.voice or not member.voice.channel:
|
||||
return None
|
||||
gs = get_guild(guild.id)
|
||||
bound = gs.get("bound_vc")
|
||||
target = None
|
||||
|
||||
if bound:
|
||||
target = discord.utils.get(guild.voice_channels, id=bound)
|
||||
if target is None and member.voice and member.voice.channel:
|
||||
target = member.voice.channel
|
||||
else:
|
||||
if not member.voice or not member.voice.channel:
|
||||
return None
|
||||
target = member.voice.channel
|
||||
|
||||
target = member.voice.channel
|
||||
vc = voice_clients.get(guild.id)
|
||||
|
||||
if vc and vc.is_connected():
|
||||
if vc.channel.id != target.id:
|
||||
await vc.move_to(target)
|
||||
|
|
@ -150,7 +181,8 @@ async def enqueue(
|
|||
guild: discord.Guild, member: discord.Member, text: str, voice_id: str
|
||||
):
|
||||
q = tts_queues.setdefault(guild.id, asyncio.Queue())
|
||||
await q.put((text, voice_id, member))
|
||||
await q.put((text
|
||||
, voice_id, member))
|
||||
if q.qsize() == 1:
|
||||
asyncio.create_task(queue_worker(guild))
|
||||
|
||||
|
|
@ -165,23 +197,29 @@ async def queue_worker(guild: discord.Guild):
|
|||
|
||||
vc = await ensure_connected(guild, member)
|
||||
if vc is None:
|
||||
await member.send(
|
||||
"Could not join a voice channel. Make sure you are in a voice channel (or the bound VC if one is set)."
|
||||
)
|
||||
try:
|
||||
await member.send(
|
||||
"Could not join a voice channel. Make sure you are in a voice channel (or the bound VC if one is set)."
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
q.task_done()
|
||||
continue
|
||||
|
||||
last_activity[guild.id] = datetime.now()
|
||||
|
||||
ts = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
||||
path = f"audio_cache/{guild.id}_{ts}.mp3"
|
||||
path = os.path.join(AUDIO_CACHE_DIR, f"{guild.id}_{ts}.mp3")
|
||||
|
||||
ok = await generate_tts(text, voice_id, path)
|
||||
|
||||
if not ok:
|
||||
await member.send(
|
||||
"Failed to generate TTS audio. The TTS service may be unavailable."
|
||||
)
|
||||
try:
|
||||
await member.send(
|
||||
"Failed to generate TTS audio. The TTS service may be unavailable."
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
q.task_done()
|
||||
continue
|
||||
|
||||
|
|
@ -194,7 +232,10 @@ async def queue_worker(guild: discord.Guild):
|
|||
asyncio.create_task(cleanup(path, delay=12))
|
||||
except Exception as e:
|
||||
logger.error(f"Playback error: {e}")
|
||||
await member.send("Failed to play audio. Check that ffmpeg is installed.")
|
||||
try:
|
||||
await member.send("Failed to play audio. Check that ffmpeg is installed.")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
q.task_done()
|
||||
|
||||
|
|
@ -229,7 +270,10 @@ async def on_message(message: discord.Message):
|
|||
return
|
||||
|
||||
if not message.author.voice or not message.author.voice.channel:
|
||||
await message.reply("You need to be in a voice channel.", mention_author=False)
|
||||
try:
|
||||
await message.reply("You need to be in a voice channel.", mention_author=False)
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
|
||||
if len(text) > config["tts"]["max_length"]:
|
||||
|
|
@ -254,7 +298,10 @@ async def on_voice_state_update(member: discord.Member, before, after):
|
|||
vc = voice_clients.get(member.guild.id)
|
||||
if vc and vc.is_connected():
|
||||
if all(m.bot for m in vc.channel.members):
|
||||
await vc.disconnect()
|
||||
try:
|
||||
await vc.disconnect()
|
||||
except Exception:
|
||||
pass
|
||||
voice_clients.pop(member.guild.id, None)
|
||||
last_activity.pop(member.guild.id, None)
|
||||
logger.info(f"Auto-disconnected from {member.guild.name}")
|
||||
|
|
@ -344,7 +391,6 @@ async def setup_vc(
|
|||
await interaction.response.send_message(
|
||||
"Provide a voice channel to bind to.", ephemeral=True
|
||||
)
|
||||
|
||||
return
|
||||
gs["bound_vc"] = channel.id
|
||||
save_guild_settings()
|
||||
|
|
@ -454,7 +500,10 @@ async def stop(interaction: discord.Interaction):
|
|||
except Exception:
|
||||
break
|
||||
|
||||
await vc.disconnect()
|
||||
try:
|
||||
await vc.disconnect()
|
||||
except Exception:
|
||||
pass
|
||||
voice_clients.pop(gid, None)
|
||||
last_activity.pop(gid, None)
|
||||
|
||||
|
|
@ -472,6 +521,7 @@ async def tts_enable(interaction: discord.Interaction):
|
|||
)
|
||||
return
|
||||
|
||||
|
||||
enabled = tts_enabled.setdefault(interaction.guild_id, set())
|
||||
if interaction.user.id in enabled:
|
||||
await interaction.response.send_message(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue