You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
8.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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"))