1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
# This file is part of Senator Sperma
# Licensed under the GPL-3.0 License
# Copyright (C) 2026 Salmonik @ salmonik@tuta.io
# https://codeberg.org/salmonik/senator-sperma
import datetime
import json
import logging
import os
import asyncio
import discord
from discord.ext import commands
from gtts import gTTS
import aiofiles
from cogs.voice import get_voice_manager
logger = logging.getLogger(__name__)
class VcLogs(commands.Cog):
def __init__(self, bot):
self.bot = bot
self._voice_manager = None
def get_voice_manager(self):
if self._voice_manager is None:
self._voice_manager = get_voice_manager(self.bot)
return self._voice_manager
settings_file = 'settings.json'
@staticmethod
async def handle_state_change(member, channel, state_type, is_enabled, current_time, log_channel):
"""Helper method to handle voice state changes"""
state_messages = {
'deaf': {
True: (
f"[{current_time}] {member.name} ogłuchnął się na {channel.name}",
f"{member.display_name} jest bity przez ojca"
),
False: (
f"[{current_time}] {member.name} odgłuchał się na {channel.name}",
f"{member.display_name} uciek od ojca"
)
},
'mute': {
True: (
f"[{current_time}] {member.name} wyciszył się na {channel.name}",
f"{member.display_name} idzie robić jajecznice"
),
False: (
f"[{current_time}] {member.name} odciszył się na {channel.name}",
f"{member.display_name} zrobił jajecznice"
)
}
}
message, tts_text = state_messages[state_type][is_enabled]
await log_channel.send(message)
logger.info(f"Voice state change: {message}")
return tts_text, channel
async def play_tts(self, channel, text):
voice_mgr = self.get_voice_manager()
temp_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'temp')
os.makedirs(temp_dir, exist_ok=True)
tts_file = os.path.join(temp_dir, f"temp_tts_{os.getpid()}.mp3")
tts = gTTS(text=text, lang='pl')
logger.info("saving file")
tts.save(tts_file)
if os.path.exists(VcLogs.settings_file):
async with aiofiles.open(VcLogs.settings_file) as f:
content = await f.read()
data = json.loads(content)
volume = data.get('volume', 1.0)
voice_client = await voice_mgr.get_voice_client(channel.guild)
if voice_client is None:
voice_client = await voice_mgr.join_channel(channel)
logger.info(f"connected: {voice_client}")
if voice_client and not voice_client.is_playing():
logger.info("preparing to play audio")
audio_source = discord.FFmpegPCMAudio(tts_file, options=f'-filter:a "volume={volume}"')
voice_client.play(audio_source)
while voice_client.is_playing():
await asyncio.sleep(0.5)
if os.path.exists(tts_file):
os.remove(tts_file)
@commands.slash_command(
name="log_volume"
)
async def volume(self, ctx: discord.ApplicationContext, volume: int):
try:
async with aiofiles.open(VcLogs.settings_file, 'w') as f:
json.dump({"volume": volume}, f, indent=4)
await ctx.followup.send(f"Volume has been set to: {volume}")
logger.info(f"Volume changed to: {volume}")
except Exception as e:
await ctx.respond(e, ephemeral=True)
logger.error(f"Error in volume command: {e}", exc_info=True)
@commands.Cog.listener()
async def on_voice_state_update(self, member, before, after):
log_channel = self.bot.get_channel(1213843195457183885)
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
tts_text = None
tts_channel = None
# Ignore if the user is a bot
if member.bot:
return
if before.channel is None and after.channel is not None:
message = f"[{current_time}] {member.name} dołączył na {after.channel.name}"
tts_text = f"{member.display_name} sie zespawnił"
tts_channel = after.channel
await log_channel.send(message)
logger.info(f"User joined voice channel: {message}")
elif before.channel is not None and after.channel is None:
message = f"[{current_time}] {member.name} rozłączył się z {before.channel.name}"
tts_text = f"{member.display_name} jest chujem"
tts_channel = before.channel
await log_channel.send(message)
logger.info(f"User left voice channel: {message}")
if before.self_deaf != after.self_deaf:
tts_text, tts_channel = await self.handle_state_change(
member, after.channel, 'deaf', after.self_deaf, current_time, log_channel
)
if tts_text and tts_channel:
await self.play_tts(tts_channel, tts_text)
return
if before.self_mute != after.self_mute:
tts_text, tts_channel = await self.handle_state_change(
member, after.channel, 'mute', after.self_mute, current_time, log_channel
)
if tts_text and tts_channel:
await self.play_tts(tts_channel, tts_text)
def setup(bot):
bot.add_cog(VcLogs(bot))
|