diff --git a/main b/main new file mode 100644 index 0000000..b41a26c --- /dev/null +++ b/main @@ -0,0 +1,161 @@ +import discord +from discord.ext import commands +import json +import datetime +from apscheduler.schedulers.asyncio import AsyncIOScheduler +import pytz # Импортируем pytz +import os +from dotenv import load_dotenv +from pathlib import Path +dotenv_path = f"{Path(__file__).parent.resolve()}/.env" +load_dotenv(dotenv_path=dotenv_path) + +intents = discord.Intents.default() +intents.voice_states = True # Включаем интенты для отслеживания голосовых состояний +intents.messages = True +intents.message_content = True +intents.members = True + +bot = commands.Bot(command_prefix='!', intents=intents) + +# Путь к файлу для хранения истории +history_file = 'logs/voice_history.json' +# Устанавливаем временную зону для Москвы +moscow_tz = pytz.timezone('Europe/Moscow') +backup_file = f'logs/voice_history_{datetime.datetime.now(moscow_tz).date()}.json' +# Словарь для хранения истории подключений и отключений пользователей по серверам и каналам +voice_history = {} + +#===================[WORK WITH DB]====================================== +def load_voice_history(): + """Загружает историю голосовых подключений из файла.""" + global voice_history + if os.path.exists(history_file): + with open(history_file, 'r', encoding='utf-8') as f: + try: + voice_history = json.load(f) + except: + voice_history = {} + +def save_voice_history(): + """Сохраняет историю голосовых подключений в файл.""" + with open(history_file, 'w', encoding='utf-8') as f: + json.dump(voice_history, f, ensure_ascii=False, indent=4) + +def backup_voice_history(): + """Создает резервную копию истории голосовых подключений.""" + if os.path.exists(history_file): + with open(history_file, 'r', encoding='utf-8') as f: + data = f.read() + with open(backup_file, 'w', encoding='utf-8') as f: + f.write(data) + with open(history_file, 'w', encoding='utf-8') as f: + f.write("{}") +#===================[WORK WITH DB]====================================== + +@bot.event +async def on_ready(): + load_voice_history() # Загружаем историю при запуске бота + print(f'Бот {bot.user} запущен!') + # Настраиваем планировщик + scheduler = AsyncIOScheduler() + scheduler.add_job(backup_voice_history, 'cron', hour=17, minute=47) # Запланировать на 00:00 + scheduler.start() + +@bot.event +async def on_voice_state_update(member, before, after): + guild_id = str(member.guild.id) # Получаем ID сервера как строку + channel_id = str(after.channel.id) if after.channel else str(before.channel.id) if before.channel else None + + # Инициализируем историю для сервера, если её нет + if guild_id not in voice_history: + voice_history[guild_id] = {} + + # Инициализируем историю для канала, если её нет + if channel_id not in voice_history[guild_id]: + voice_history[guild_id][channel_id] = [] + + # Если пользователь подключился к голосовому каналу + if before.channel is None and after.channel is not None: + join_time = datetime.datetime.now(moscow_tz) # Получаем текущее время в Москве + voice_history[guild_id][channel_id].append({ + 'user_id': member.id, + 'join_time': join_time.isoformat(), # Сохраняем время в формате ISO + 'leave_time': None + }) + save_voice_history() # Сохраняем историю после обновления + + # Если пользователь отключился от голосового канала + elif before.channel is not None and after.channel is None: + leave_time = datetime.datetime.now(moscow_tz) # Получаем текущее время в Москве + for record in voice_history[guild_id][channel_id]: + if record['user_id'] == member.id and record['leave_time'] is None: + record['leave_time'] = leave_time.isoformat() # Обновляем последнее подключение + break + save_voice_history() # Сохраняем историю после обновления + + + +@bot.command(name="log") +async def _log(ctx, channel_name: str): + """Команда для вывода логов о подключениях и отключениях пользователей в определённом голосовом канале за день.""" + load_voice_history() + guild_id = str(ctx.guild.id) # Получаем ID сервера как строку + + # Получаем список всех голосовых каналов на сервере + voice_channels = {channel.name: str(channel.id) for channel in ctx.guild.voice_channels} + + # Проверяем, существует ли канал с таким именем + channel_id = voice_channels.get(channel_name) + if not channel_id: + await ctx.send(f"Канал с именем '{channel_name}' не найден.") + return + + # Проверяем, есть ли записи для этого канала в истории + if guild_id not in voice_history or channel_id not in voice_history[guild_id]: + await ctx.send("Нет записей о подключениях и отключениях.") + return + + today = datetime.datetime.now(moscow_tz).date() + log_messages = [] + + for record in voice_history[guild_id][channel_id]: + user_id = record['user_id'] + join_time = datetime.datetime.fromisoformat(record['join_time']) # Преобразуем обратно в datetime + leave_time = record['leave_time'] + + try: + member = await ctx.guild.fetch_member(user_id) # Получаем участника по ID + member_mention = member.mention + except discord.NotFound: + await ctx.send('Пользователь не найден.') + continue # Пропускаем, если пользователь не найден + except discord.Forbidden: + await ctx.send('У меня нет прав для получения информации о пользователе.') + continue # Пропускаем, если нет прав + except discord.HTTPException: + await ctx.send('Произошла ошибка при получении информации о пользователе.') + continue # Пропускаем, если произошла ошибка + + if leave_time is None: # Если пользователь все еще в голосовом канале + duration = datetime.datetime.now(moscow_tz) - join_time + log_messages.append( + f"{member_mention} подключен к каналу с {join_time.strftime('%Y-%m-%d %H:%M:%S')}\n"\ + f"(продолжительность: {duration})\n") + else: + leave_time = datetime.datetime.fromisoformat(leave_time) # Преобразуем обратно в datetime + if leave_time.date() == today: # Проверяем, что отключение произошло сегодня + duration = leave_time - join_time + log_messages.append( + f"{member_mention} подключен к каналу с {join_time.strftime('%Y-%m-%d %H:%M:%S')}\n"\ + f"отключен с {leave_time.strftime('%Y-%m-%d %H:%M:%S')}"\ + f"\n(продолжительность: {duration})\n" + ) + + if log_messages: + await ctx.send("\n".join(log_messages)) + else: + await ctx.send(f"Нет записей о подключениях и отключениях в канале '{channel_name}' за сегодня.") + + +bot.run(os.getenv("token"))