aboutsummaryrefslogtreecommitdiffstats
path: root/cogs
diff options
context:
space:
mode:
Diffstat (limited to 'cogs')
-rw-r--r--cogs/ai.py35
-rw-r--r--cogs/assistant.py13
-rw-r--r--cogs/compression.py1
-rw-r--r--cogs/cron.py2
-rw-r--r--cogs/downloader.py2
-rw-r--r--cogs/music/audio_playback.py55
-rw-r--r--cogs/music/play_commands.py4
-rw-r--r--cogs/qr.py8
-rw-r--r--cogs/trigger.py44
-rw-r--r--cogs/tts.py12
-rw-r--r--cogs/utils/settings.py11
-rw-r--r--cogs/utils/temp.py4
-rw-r--r--cogs/vclogs.py15
-rw-r--r--cogs/voice.py4
-rw-r--r--cogs/web.py2
15 files changed, 127 insertions, 85 deletions
diff --git a/cogs/ai.py b/cogs/ai.py
index 4a04693..6f79ac5 100644
--- a/cogs/ai.py
+++ b/cogs/ai.py
@@ -7,7 +7,7 @@ import os
7import discord 7import discord
8from discord.ext import commands 8from discord.ext import commands
9import logging 9import logging
10import requests 10import httpx
11from gtts import gTTS 11from gtts import gTTS
12from cogs.music import audio_playback 12from cogs.music import audio_playback
13import io 13import io
@@ -52,23 +52,24 @@ class AI(commands.Cog):
52 ], 52 ],
53 53
54 } 54 }
55 response = requests.post(url=url, headers=headers, json=payload) 55 async with httpx.AsyncClient() as client:
56 response_data = response.json() 56 response = await client.post(url=url, headers=headers, json=payload)
57 logger.info(response_data) 57 response_data = response.json()
58 if not response_data["choices"]: 58 logger.info(response_data)
59 await ctx.send_followup("bot siem zesroł") 59 if not response_data["choices"]:
60 else: 60 await ctx.send_followup("bot siem zesroł")
61 response_text = response_data["choices"][0]["message"]["content"]
62 if tts:
63 # Generate TTS
64 tts_obj = gTTS(text=response_text, lang='pl')
65 tts_buffer = io.BytesIO()
66 tts_obj.write_to_fp(tts_buffer)
67 tts_buffer.seek(0)
68 await audio_playback.AudioPlaybackManager.play(ctx, discord.FFmpegPCMAudio(tts_buffer), delete_after_play=True)
69
70 else: 61 else:
71 await ctx.send_followup(response_text) 62 response_text = response_data["choices"][0]["message"]["content"]
63 if tts:
64 # Generate TTS
65 tts_obj = gTTS(text=response_text, lang='pl')
66 tts_buffer = io.BytesIO()
67 tts_obj.write_to_fp(tts_buffer)
68 tts_buffer.seek(0)
69 await audio_playback.AudioPlaybackManager.play(ctx, discord.FFmpegPCMAudio(tts_buffer), delete_after_play=True)
70
71 else:
72 await ctx.send_followup(response_text)
72 73
73 74
74 75
diff --git a/cogs/assistant.py b/cogs/assistant.py
index 71f5150..21e4989 100644
--- a/cogs/assistant.py
+++ b/cogs/assistant.py
@@ -6,7 +6,7 @@
6import re 6import re
7import discord 7import discord
8from discord.ext import commands 8from discord.ext import commands
9import requests 9import httpx
10import json 10import json
11import logging 11import logging
12import shutil 12import shutil
@@ -133,11 +133,12 @@ class Jarvis(commands.Cog):
133 133
134 @staticmethod 134 @staticmethod
135 async def _make_api_request(payload, headers): 135 async def _make_api_request(payload, headers):
136 url = "https://openrouter.ai/api/v1/chat/completions" 136 async with httpx.AsyncClient() as client:
137 response = requests.post(url=url, headers=headers, json=payload) 137 url = "https://openrouter.ai/api/v1/chat/completions"
138 response_data = response.json() 138 response = await client.post(url=url, headers=headers, json=payload)
139 logger.info(response_data) 139 response_data = response.json()
140 return response_data 140 logger.info(response_data)
141 return response_data
141 142
142 @staticmethod 143 @staticmethod
143 def _parse_response(response_data): 144 def _parse_response(response_data):
diff --git a/cogs/compression.py b/cogs/compression.py
index 150239f..acc9eda 100644
--- a/cogs/compression.py
+++ b/cogs/compression.py
@@ -275,6 +275,7 @@ class Compression(commands.Cog):
275 if file_type == "Image": 275 if file_type == "Image":
276 command, output_path = command 276 command, output_path = command
277 elif file_type == "Audio": 277 elif file_type == "Audio":
278 # Audio output path remains unchanged - already processed in _process_audio
278 pass 279 pass
279 280
280 success = await self._execute_compression(command) 281 success = await self._execute_compression(command)
diff --git a/cogs/cron.py b/cogs/cron.py
index 6e5020e..da8127b 100644
--- a/cogs/cron.py
+++ b/cogs/cron.py
@@ -92,7 +92,7 @@ class Cron(commands.Cog):
92 92
93 channel = self.bot.get_channel(channel_id) 93 channel = self.bot.get_channel(channel_id)
94 if not channel: 94 if not channel:
95 logger.warning(f"Channel {channel_id} not found for task {task['id']}") 95 logger.warning(f"Channel {channel_id} not found for task ID: {task.get('id')}")
96 return 96 return
97 97
98 logger.info(f"Found channel {channel} (type: {type(channel).__name__})") 98 logger.info(f"Found channel {channel} (type: {type(channel).__name__})")
diff --git a/cogs/downloader.py b/cogs/downloader.py
index d364b3a..36bd723 100644
--- a/cogs/downloader.py
+++ b/cogs/downloader.py
@@ -195,7 +195,7 @@ class Downloader(commands.Cog):
195 } 195 }
196 196
197 @staticmethod 197 @staticmethod
198 def _create_error_response(content: str, details: str, temp_dir: str) -> dict: 198 def _create_error_response(content: str, details: str, temp_dir: str | None) -> dict:
199 return { 199 return {
200 "status": "error", 200 "status": "error",
201 "type": "error", 201 "type": "error",
diff --git a/cogs/music/audio_playback.py b/cogs/music/audio_playback.py
index 1267fa7..28c6118 100644
--- a/cogs/music/audio_playback.py
+++ b/cogs/music/audio_playback.py
@@ -60,17 +60,18 @@ class AudioPlaybackManager:
60 """Connect or move to voice channel. Returns voice client.""" 60 """Connect or move to voice channel. Returns voice client."""
61 try: 61 try:
62 # Check if vc is valid and actually connected 62 # Check if vc is valid and actually connected
63 is_connected = vc is not None and vc.is_connected() 63 if vc is not None and vc.is_connected():
64 # Connect to voice channel if not connected 64 # Already connected - check if need to move
65 if not is_connected: 65 if vc.channel != target_channel:
66 logger.info(f"Connecting to voice channel: {target_channel.name}") 66 logger.info(f"Moving to voice channel: {target_channel.name}")
67 vc = await target_channel.connect() 67 await vc.move_to(target_channel)
68 logger.info(f"Connected to voice channel: {target_channel.name}") 68 logger.info(f"Moved to voice channel: {target_channel.name}")
69 # Move to user's channel if in a different one 69 return vc
70 elif vc.channel != target_channel: 70
71 logger.info(f"Moving to voice channel: {target_channel.name}") 71 # Not connected - connect now
72 await vc.move_to(target_channel) 72 logger.info(f"Connecting to voice channel: {target_channel.name}")
73 logger.info(f"Moved to voice channel: {target_channel.name}") 73 vc = await target_channel.connect()
74 logger.info(f"Connected to voice channel: {target_channel.name}")
74 return vc 75 return vc
75 except Exception as e: 76 except Exception as e:
76 logger.error(f"Error connecting to voice channel: {str(e)}") 77 logger.error(f"Error connecting to voice channel: {str(e)}")
@@ -138,15 +139,35 @@ class AudioPlaybackManager:
138 @staticmethod 139 @staticmethod
139 async def execute_playback(ctx, source, display_name, is_url): 140 async def execute_playback(ctx, source, display_name, is_url):
140 """Execute the actual audio playback.""" 141 """Execute the actual audio playback."""
141 vc = ctx.voice_client
142 target_channel = await AudioPlaybackManager.validate_voice_channel(ctx) 142 target_channel = await AudioPlaybackManager.validate_voice_channel(ctx)
143 if not target_channel: 143 if not target_channel:
144 return False 144 return False
145 145
146 # Get existing voice client from bot or connect
147 guild = ctx.guild
148 vc = discord.utils.get(ctx.bot.voice_clients, guild=guild)
149
146 try: 150 try:
147 vc = await AudioPlaybackManager.connect_to_voice_channel( 151 # Handle various connection states
148 ctx, vc, target_channel 152 if vc is None:
149 ) 153 logger.info("No existing voice client, connecting...")
154 try:
155 vc = await target_channel.connect()
156 except discord.ClientException:
157 vc = discord.utils.get(ctx.bot.voice_clients, guild=guild)
158 elif not vc.is_connected():
159 logger.info("Voice client exists but disconnected, reconnecting...")
160 try:
161 await vc.disconnect()
162 except Exception:
163 pass
164 try:
165 vc = await target_channel.connect()
166 except discord.ClientException:
167 vc = discord.utils.get(ctx.bot.voice_clients, guild=guild)
168 elif vc.channel != target_channel:
169 logger.info(f"Moving to voice channel: {target_channel.name}")
170 await vc.move_to(target_channel)
150 171
151 # Stop any currently playing audio 172 # Stop any currently playing audio
152 if vc.is_playing() or vc.is_paused(): # type: ignore[union-attr] 173 if vc.is_playing() or vc.is_paused(): # type: ignore[union-attr]
@@ -171,7 +192,7 @@ class AudioPlaybackManager:
171 # Play the audio 192 # Play the audio
172 vc.play(audio_source, after=after_playing) 193 vc.play(audio_source, after=after_playing)
173 logger.info(f"Playing: {final_display_name}") 194 logger.info(f"Playing: {final_display_name}")
174 await ctx.respond(f"grom: {final_display_name}") 195 await ctx.followup.send(f"grom: {final_display_name}")
175 196
176 # Wait for FFmpeg to attempt connection - errors usually happen within 5 seconds 197 # Wait for FFmpeg to attempt connection - errors usually happen within 5 seconds
177 await asyncio.sleep(5) 198 await asyncio.sleep(5)
@@ -195,7 +216,7 @@ class AudioPlaybackManager:
195 216
196 except Exception as e: 217 except Exception as e:
197 logger.error(f"Error in play: {str(e)}", exc_info=True) 218 logger.error(f"Error in play: {str(e)}", exc_info=True)
198 await ctx.respond(f"eror: {str(e)}", ephemeral=True) 219 await ctx.followup.send(f"eror: {str(e)}", ephemeral=True)
199 return False 220 return False
200 221
201 @staticmethod 222 @staticmethod
diff --git a/cogs/music/play_commands.py b/cogs/music/play_commands.py
index fd5113a..a82fa9d 100644
--- a/cogs/music/play_commands.py
+++ b/cogs/music/play_commands.py
@@ -6,6 +6,7 @@
6import logging 6import logging
7import os 7import os
8import urllib.parse 8import urllib.parse
9import asyncio
9from os import PathLike 10from os import PathLike
10from typing import Awaitable, Callable, List, Tuple 11from typing import Awaitable, Callable, List, Tuple
11 12
@@ -139,9 +140,10 @@ class PlayCommands(commands.Cog):
139 local_path = os.path.join(self.temp_dir, filename) 140 local_path = os.path.join(self.temp_dir, filename)
140 os.makedirs(self.temp_dir, exist_ok=True) 141 os.makedirs(self.temp_dir, exist_ok=True)
141 142
143 loop = asyncio.get_event_loop()
142 with open(local_path, "wb") as f: 144 with open(local_path, "wb") as f:
143 async for chunk in response.content.iter_chunked(8192): 145 async for chunk in response.content.iter_chunked(8192):
144 f.write(chunk) 146 await loop.run_in_executor(None, f.write, chunk)
145 147
146 compressed_path = await media_processing.AudioCompressor.compress( 148 compressed_path = await media_processing.AudioCompressor.compress(
147 ctx, local_path 149 ctx, local_path
diff --git a/cogs/qr.py b/cogs/qr.py
index c1fd499..32c574e 100644
--- a/cogs/qr.py
+++ b/cogs/qr.py
@@ -22,10 +22,10 @@ _decode_available = False
22 22
23def _ensure_qr(): 23def _ensure_qr():
24 global _pil_image, _decode_func, _decode_available 24 global _pil_image, _decode_func, _decode_available
25 25
26 if _pil_image is not None: 26 if _pil_image is not None:
27 return _decode_available 27 return _decode_available
28 28
29 try: 29 try:
30 from PIL import Image 30 from PIL import Image
31 from pyzbar.pyzbar import decode as _decode_func 31 from pyzbar.pyzbar import decode as _decode_func
@@ -48,7 +48,7 @@ class QR(commands.Cog):
48 if response.status == 200: 48 if response.status == 200:
49 content = await response.read() 49 content = await response.read()
50 with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as f: 50 with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as f:
51 f.write(content) 51 await asyncio.get_event_loop().run_in_executor(None, f.write, content)
52 return f.name 52 return f.name
53 return None 53 return None
54 54
@@ -137,7 +137,7 @@ class QR(commands.Cog):
137 137
138 def _decode_qr(self, image_path): 138 def _decode_qr(self, image_path):
139 """Decode QR code from image (runs in executor)""" 139 """Decode QR code from image (runs in executor)"""
140 if not _ensure_qr(): 140 if not _ensure_qr() or _decode_func is None:
141 return None 141 return None
142 142
143 try: 143 try:
diff --git a/cogs/trigger.py b/cogs/trigger.py
index 0e76cef..02e5573 100644
--- a/cogs/trigger.py
+++ b/cogs/trigger.py
@@ -15,6 +15,12 @@ logger = logging.getLogger(__name__)
15 15
16DB_PATH = 'data/triggers.db' 16DB_PATH = 'data/triggers.db'
17 17
18_SELECT_KEYWORD_SQL = "SELECT keyword FROM trigger_keywords WHERE trigger_id=?"
19_SELECT_RESPONSE_SQL = "SELECT response_text FROM trigger_responses WHERE trigger_id=?"
20_SELECT_TRIGGER_ID_SQL = "SELECT id FROM triggers WHERE name=?"
21_TRIGGER_NOT_FOUND_MSG = "Trigger not found."
22_TRIGGER_NAME_OPTION = "the name of the trigger"
23
18 24
19def get_connection(): 25def get_connection():
20 return sqlite3.connect(DB_PATH) 26 return sqlite3.connect(DB_PATH)
@@ -112,10 +118,10 @@ class Trigger(commands.Cog):
112 c = conn.cursor() 118 c = conn.cursor()
113 c.execute("SELECT id, name FROM triggers") 119 c.execute("SELECT id, name FROM triggers")
114 for trigger_id, name in c.fetchall(): 120 for trigger_id, name in c.fetchall():
115 c.execute("SELECT keyword FROM trigger_keywords WHERE trigger_id=?", (trigger_id,)) 121 c.execute(_SELECT_KEYWORD_SQL, (trigger_id,))
116 keywords = [row[0] for row in c.fetchall()] 122 keywords = [row[0] for row in c.fetchall()]
117 if any(keyword in content for keyword in keywords): 123 if any(keyword in content for keyword in keywords):
118 c.execute("SELECT response_text FROM trigger_responses WHERE trigger_id=?", (trigger_id,)) 124 c.execute(_SELECT_RESPONSE_SQL, (trigger_id,))
119 responses = [row[0] for row in c.fetchall()] 125 responses = [row[0] for row in c.fetchall()]
120 if responses: 126 if responses:
121 await message.channel.send(random.choice(responses).replace('\\n', '\n')) 127 await message.channel.send(random.choice(responses).replace('\\n', '\n'))
@@ -137,7 +143,7 @@ class Trigger(commands.Cog):
137 ctx: discord.ApplicationContext, 143 ctx: discord.ApplicationContext,
138 name = discord.Option( 144 name = discord.Option(
139 str, 145 str,
140 "the name of the trigger", 146 _TRIGGER_NAME_OPTION,
141 autocomplete= discord.utils.basic_autocomplete(get_type), 147 autocomplete= discord.utils.basic_autocomplete(get_type),
142 required=True 148 required=True
143 ) 149 )
@@ -157,7 +163,7 @@ class Trigger(commands.Cog):
157 ctx: discord.ApplicationContext, 163 ctx: discord.ApplicationContext,
158 name = discord.Option( 164 name = discord.Option(
159 str, 165 str,
160 "the name of the trigger", 166 _TRIGGER_NAME_OPTION,
161 autocomplete= discord.utils.basic_autocomplete(get_type), 167 autocomplete= discord.utils.basic_autocomplete(get_type),
162 required=True 168 required=True
163 ), 169 ),
@@ -174,10 +180,10 @@ class Trigger(commands.Cog):
174 ): 180 ):
175 with get_connection() as conn: 181 with get_connection() as conn:
176 c = conn.cursor() 182 c = conn.cursor()
177 c.execute("SELECT id FROM triggers WHERE name=?", (name,)) 183 c.execute(_SELECT_TRIGGER_ID_SQL, (name,))
178 row = c.fetchone() 184 row = c.fetchone()
179 if not row: 185 if not row:
180 await ctx.respond("Trigger not found.") 186 await ctx.respond(_TRIGGER_NOT_FOUND_MSG)
181 logger.warning(f"Trigger not found: {name}") 187 logger.warning(f"Trigger not found: {name}")
182 return 188 return
183 trigger_id = row[0] 189 trigger_id = row[0]
@@ -204,7 +210,7 @@ class Trigger(commands.Cog):
204 ctx: discord.ApplicationContext, 210 ctx: discord.ApplicationContext,
205 name = discord.Option( 211 name = discord.Option(
206 str, 212 str,
207 "the name of the trigger", 213 _TRIGGER_NAME_OPTION,
208 autocomplete= discord.utils.basic_autocomplete(get_type), 214 autocomplete= discord.utils.basic_autocomplete(get_type),
209 required=True 215 required=True
210 ), 216 ),
@@ -221,10 +227,10 @@ class Trigger(commands.Cog):
221 ): 227 ):
222 with get_connection() as conn: 228 with get_connection() as conn:
223 c = conn.cursor() 229 c = conn.cursor()
224 c.execute("SELECT id FROM triggers WHERE name=?", (name,)) 230 c.execute(_SELECT_TRIGGER_ID_SQL, (name,))
225 row = c.fetchone() 231 row = c.fetchone()
226 if not row: 232 if not row:
227 await ctx.respond("Trigger not found.") 233 await ctx.respond(_TRIGGER_NOT_FOUND_MSG)
228 logger.warning(f"Trigger not found: {name}") 234 logger.warning(f"Trigger not found: {name}")
229 return 235 return
230 trigger_id = row[0] 236 trigger_id = row[0]
@@ -245,17 +251,17 @@ class Trigger(commands.Cog):
245 ctx: discord.ApplicationContext, 251 ctx: discord.ApplicationContext,
246 name = discord.Option( 252 name = discord.Option(
247 str, 253 str,
248 "the name of the trigger", 254 _TRIGGER_NAME_OPTION,
249 autocomplete= discord.utils.basic_autocomplete(get_type), 255 autocomplete= discord.utils.basic_autocomplete(get_type),
250 required=True 256 required=True
251 ) 257 )
252 ): 258 ):
253 with get_connection() as conn: 259 with get_connection() as conn:
254 c = conn.cursor() 260 c = conn.cursor()
255 c.execute("SELECT id FROM triggers WHERE name=?", (name,)) 261 c.execute(_SELECT_TRIGGER_ID_SQL, (name,))
256 row = c.fetchone() 262 row = c.fetchone()
257 if not row: 263 if not row:
258 await ctx.respond("Trigger not found.") 264 await ctx.respond(_TRIGGER_NOT_FOUND_MSG)
259 return 265 return
260 trigger_id = row[0] 266 trigger_id = row[0]
261 c.execute("DELETE FROM trigger_keywords WHERE trigger_id=?", (trigger_id,)) 267 c.execute("DELETE FROM trigger_keywords WHERE trigger_id=?", (trigger_id,))
@@ -275,7 +281,7 @@ class Trigger(commands.Cog):
275 ctx: discord.ApplicationContext, 281 ctx: discord.ApplicationContext,
276 name = discord.Option( 282 name = discord.Option(
277 str, 283 str,
278 "the name of the trigger", 284 _TRIGGER_NAME_OPTION,
279 autocomplete=discord.utils.basic_autocomplete(get_type), 285 autocomplete=discord.utils.basic_autocomplete(get_type),
280 required=True 286 required=True
281 ) 287 )
@@ -286,13 +292,13 @@ class Trigger(commands.Cog):
286 c.execute("SELECT id FROM triggers WHERE name=?", (name,)) 292 c.execute("SELECT id FROM triggers WHERE name=?", (name,))
287 row = c.fetchone() 293 row = c.fetchone()
288 if not row: 294 if not row:
289 await ctx.respond("Trigger not found.") 295 await ctx.respond(_TRIGGER_NOT_FOUND_MSG)
290 logger.warning(f"Trigger not found: {name}") 296 logger.warning(f"Trigger not found: {name}")
291 return 297 return
292 trigger_id = row[0] 298 trigger_id = row[0]
293 c.execute("SELECT keyword FROM trigger_keywords WHERE trigger_id=?", (trigger_id,)) 299 c.execute(_SELECT_KEYWORD_SQL, (trigger_id,))
294 triggers = [row[0] for row in c.fetchall()] 300 triggers = [row[0] for row in c.fetchall()]
295 c.execute("SELECT response_text FROM trigger_responses WHERE trigger_id=?", (trigger_id,)) 301 c.execute(_SELECT_RESPONSE_SQL, (trigger_id,))
296 responses = [row[0] for row in c.fetchall()] 302 responses = [row[0] for row in c.fetchall()]
297 trigger_list = "\n".join(triggers) 303 trigger_list = "\n".join(triggers)
298 response_list = "\n".join(responses) 304 response_list = "\n".join(responses)
@@ -303,13 +309,13 @@ class Trigger(commands.Cog):
303 c.execute("SELECT id, name FROM triggers") 309 c.execute("SELECT id, name FROM triggers")
304 messages = [] 310 messages = []
305 for trigger_id, name in c.fetchall(): 311 for trigger_id, name in c.fetchall():
306 c.execute("SELECT keyword FROM trigger_keywords WHERE trigger_id=?", (trigger_id,)) 312 c.execute(_SELECT_KEYWORD_SQL, (trigger_id,))
307 triggers = ", ".join([row[0] for row in c.fetchall()]) 313 triggers = ", ".join([row[0] for row in c.fetchall()])
308 c.execute("SELECT response_text FROM trigger_responses WHERE trigger_id=?", (trigger_id,)) 314 c.execute(_SELECT_RESPONSE_SQL, (trigger_id,))
309 responses = ", ".join([row[0] for row in c.fetchall()]) 315 responses = ", ".join([row[0] for row in c.fetchall()])
310 messages.append(f"**{name}**\nTriggers: {triggers}\nResponses: {responses}") 316 messages.append(f"**{name}**\nTriggers: {triggers}\nResponses: {responses}")
311 await ctx.respond("\n\n".join(messages)) 317 await ctx.respond("\n\n".join(messages))
312 logger.info(f"Triggers listed") 318 logger.info("Triggers listed")
313 319
314 @trigger_group.command( 320 @trigger_group.command(
315 description="how to use trigger group?" 321 description="how to use trigger group?"
diff --git a/cogs/tts.py b/cogs/tts.py
index 3dc4c09..68eb27f 100644
--- a/cogs/tts.py
+++ b/cogs/tts.py
@@ -54,19 +54,17 @@ class TTS(commands.Cog):
54 if not voice_client: 54 if not voice_client:
55 await ctx.respond("Failed to connect to voice channel", ephemeral=True) 55 await ctx.respond("Failed to connect to voice channel", ephemeral=True)
56 return 56 return
57 57
58 # Play the TTS audio with volume adjustment 58 if voice_client.is_playing():
59 await ctx.respond("Audio is already playing, try again later", ephemeral=True)
60 return
61
59 audio_source = discord.FFmpegPCMAudio( 62 audio_source = discord.FFmpegPCMAudio(
60 temp_path, 63 temp_path,
61 options=f'-filter:a "volume={volume}"' 64 options=f'-filter:a "volume={volume}"'
62 ) 65 )
63 voice_client.play(audio_source)
64 logger.info(f"Playing TTS: {text}") 66 logger.info(f"Playing TTS: {text}")
65 67
66 # Wait for the audio to finish playing
67 while voice_client.is_playing():
68 await asyncio.sleep(0.1)
69
70 except Exception as e: 68 except Exception as e:
71 logger.error(f"Error in voice channel handling: {e}") 69 logger.error(f"Error in voice channel handling: {e}")
72 await ctx.respond("eror :c", ephemeral=True) 70 await ctx.respond("eror :c", ephemeral=True)
diff --git a/cogs/utils/settings.py b/cogs/utils/settings.py
index 984ec16..d66ce7f 100644
--- a/cogs/utils/settings.py
+++ b/cogs/utils/settings.py
@@ -12,18 +12,19 @@ from dataclasses import dataclass, field
12logger = logging.getLogger(__name__) 12logger = logging.getLogger(__name__)
13 13
14settings_file = "data/settings.json" 14settings_file = "data/settings.json"
15DEFAULT_BASEURL = "http://localhost:8080"
15 16
16 17
17@dataclass 18@dataclass
18class Settings: 19class Settings:
19 volume: float = 1.0 20 volume: float = 1.0
20 baseurl: str = "http://localhost:8080" 21 baseurl: str = DEFAULT_BASEURL
21 _data: dict = field(default_factory=dict, init=False) 22 _data: dict = field(default_factory=dict, init=False)
22 23
23 async def load(self) -> "Settings": 24 async def load(self) -> "Settings":
24 default_settings = { 25 default_settings = {
25 "volume": 1.0, 26 "volume": 1.0,
26 "baseurl": "http://localhost:8080", 27 "baseurl": DEFAULT_BASEURL,
27 } 28 }
28 29
29 try: 30 try:
@@ -44,7 +45,7 @@ class Settings:
44 self._sync_from_data() 45 self._sync_from_data()
45 await self.save() 46 await self.save()
46 logger.info("Created default settings.json") 47 logger.info("Created default settings.json")
47 except (json.JSONDecodeError, Exception) as e: 48 except json.JSONDecodeError as e:
48 logger.error(f"Error loading settings: {e}") 49 logger.error(f"Error loading settings: {e}")
49 self._data = default_settings.copy() 50 self._data = default_settings.copy()
50 self._sync_from_data() 51 self._sync_from_data()
@@ -53,7 +54,7 @@ class Settings:
53 54
54 def _sync_from_data(self): 55 def _sync_from_data(self):
55 self.volume = self._data.get("volume", 1.0) 56 self.volume = self._data.get("volume", 1.0)
56 self.baseurl = self._data.get("baseurl", "http://localhost:8080") 57 self.baseurl = self._data.get("baseurl", DEFAULT_BASEURL)
57 58
58 def _sync_to_data(self): 59 def _sync_to_data(self):
59 self._data["volume"] = self.volume 60 self._data["volume"] = self.volume
@@ -89,4 +90,4 @@ def get_volume() -> float:
89 90
90 91
91def get_baseurl() -> str: 92def get_baseurl() -> str:
92 return _settings_instance.baseurl.rstrip("/") if _settings_instance else "http://localhost:8080" 93 return _settings_instance.baseurl.rstrip("/") if _settings_instance else DEFAULT_BASEURL
diff --git a/cogs/utils/temp.py b/cogs/utils/temp.py
index 528d6a3..0b2c8d7 100644
--- a/cogs/utils/temp.py
+++ b/cogs/utils/temp.py
@@ -8,11 +8,11 @@ import tempfile
8 8
9def get_temp_dir(subdir: str = "senator_sperma") -> str: 9def get_temp_dir(subdir: str = "senator_sperma") -> str:
10 """Get or create a temp subdirectory. 10 """Get or create a temp subdirectory.
11 11
12 Uses system temp directory (/tmp on Linux, %TEMP% on Windows). 12 Uses system temp directory (/tmp on Linux, %TEMP% on Windows).
13 OS handles cleanup automatically. 13 OS handles cleanup automatically.
14 """ 14 """
15 base_temp = tempfile.gettempdir() 15 base_temp = tempfile.gettempdir()
16 temp_path = os.path.join(base_temp, subdir) 16 temp_path = os.path.join(base_temp, subdir)
17 os.makedirs(temp_path, exist_ok=True) 17 os.makedirs(temp_path, exist_ok=True)
18 return temp_path \ No newline at end of file 18 return temp_path
diff --git a/cogs/vclogs.py b/cogs/vclogs.py
index 397d296..85d19bc 100644
--- a/cogs/vclogs.py
+++ b/cogs/vclogs.py
@@ -14,9 +14,11 @@ from gtts import gTTS
14from cogs.voice import get_voice_manager 14from cogs.voice import get_voice_manager
15from cogs.utils.settings import get_volume, save_settings 15from cogs.utils.settings import get_volume, save_settings
16 16
17import tempfile
18
17logger = logging.getLogger(__name__) 19logger = logging.getLogger(__name__)
18 20
19ACTIVITY_FILE = 'temp/voice_activity.json' 21ACTIVITY_FILE = os.path.join(tempfile.gettempdir(), 'voice_activity.json')
20 22
21 23
22def load_activity_data(): 24def load_activity_data():
@@ -125,7 +127,16 @@ class VcLogs(commands.Cog):
125 127
126 @commands.Cog.listener() 128 @commands.Cog.listener()
127 async def on_voice_state_update(self, member, before, after): 129 async def on_voice_state_update(self, member, before, after):
128 log_channel = self.bot.get_channel(1213843195457183885) 130 log_channel_id = os.getenv("STATUS_CHANNEL_ID")
131 if not log_channel_id:
132 logger.warning("STATUS_CHANNEL_ID not set in environment")
133 return
134
135 log_channel = self.bot.get_channel(int(log_channel_id))
136 if log_channel is None:
137 logger.warning("Log channel not found or not accessible")
138 return
139
129 current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") 140 current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
130 current_ts = datetime.datetime.now().timestamp() 141 current_ts = datetime.datetime.now().timestamp()
131 tts_text = None 142 tts_text = None
diff --git a/cogs/voice.py b/cogs/voice.py
index 07015cc..319bb16 100644
--- a/cogs/voice.py
+++ b/cogs/voice.py
@@ -95,7 +95,7 @@ class VoiceManager:
95 if not voice_client: 95 if not voice_client:
96 return False 96 return False
97 97
98 if volume != 1.0: 98 if abs(volume - 1.0) > 1e-9:
99 audio_source = discord.PCMVolumeTransformer(audio_source) 99 audio_source = discord.PCMVolumeTransformer(audio_source)
100 audio_source.volume = volume 100 audio_source.volume = volume
101 101
@@ -197,4 +197,4 @@ class Voice(commands.Cog):
197 197
198 198
199def setup(bot): 199def setup(bot):
200 bot.add_cog(Voice(bot)) \ No newline at end of file 200 bot.add_cog(Voice(bot))
diff --git a/cogs/web.py b/cogs/web.py
index e003399..b072e99 100644
--- a/cogs/web.py
+++ b/cogs/web.py
@@ -97,4 +97,4 @@ class Web(commands.Cog):
97 97
98 98
99def setup(bot): 99def setup(bot):
100 bot.add_cog(Web(bot)) \ No newline at end of file 100 bot.add_cog(Web(bot))