diff --git a/.env.example b/.env.example index 0239bd5..f60cc8b 100644 --- a/.env.example +++ b/.env.example @@ -36,3 +36,9 @@ USE_PROXY=true PROXY_TYPE=socks5 # socks5 или http PROXY_HOST=127.0.0.1 PROXY_PORT=10808 # SOCKS5 порт (или 10809 для HTTP) + +# ===== ВЕБ-ИНТЕРФЕЙС АДМИНА ===== +WEB_ADMIN_LOGIN=admin +WEB_ADMIN_PASSWORD=5nMVkjd5nMVkjd +WEB_ADMIN_HOST=0.0.0.0 +WEB_ADMIN_PORT=8000 diff --git a/.env.web b/.env.web new file mode 100644 index 0000000..5495430 --- /dev/null +++ b/.env.web @@ -0,0 +1,51 @@ +# Telegram Bot Token (получить у @BotFather) +BOT_TOKEN=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz + +# ID чата дома (можно узнать через @userinfobot или переслав сообщение из чата) +ADMIN_CHAT_ID=-1001234567890 + +# Ваш Telegram ID (админ) - узнать через @userinfobot +ADMIN_USER_ID=123456789 + +# Путь к базе данных +DATABASE_PATH=./database/domovoy.db + +# Директория для экспортов +EXPORT_DIR=./exports + +# Расписание экспорта (cron формат: минута час день месяц день_недели) +# Каждое воскресенье в 03:00 +EXPORT_SCHEDULE=0 3 * * 0 + +# Детект шпионов +SPY_DETECTION_ENABLED=true + +# Куда слать уведомления о шпионах (ваш ID) +SPY_ALERT_USER_ID=123456789 + +# Дополнительные админы (через запятую, если будут) +EXTRA_ADMIN_IDS= + +# Логирование +LOG_LEVEL=INFO +LOG_FILE=./logs/bot.log + +# ===== ПРОКСИ ДЛЯ ОБХОДА БЛОКИРОВОК РКН ===== +# xray на хосте: SOCKS5 порт 10808, HTTP порт 10809 +USE_PROXY=true +PROXY_TYPE=socks5 # socks5 или http +PROXY_HOST=127.0.0.1 +PROXY_PORT=10808 # SOCKS5 порт (или 10809 для HTTP) + +# ===== ВЕБ-ИНТЕРФЕЙС АДМИНА ===== +WEB_ADMIN_LOGIN=admin +WEB_ADMIN_PASSWORD=5nMVkjd5nMVkjd +WEB_ADMIN_HOST=0.0.0.0 +WEB_ADMIN_PORT=8000 + +# ===== ВЕБ-ИНТЕРФЕЙС АДМИНА ===== +WEB_ADMIN_LOGIN=admin +WEB_ADMIN_PASSWORD=5nMVkjd5nMVkjd +WEB_ADMIN_HOST=0.0.0.0 +WEB_ADMIN_PORT=8000 + diff --git a/config.py b/config.py index 2b5dfb5..008cf36 100644 --- a/config.py +++ b/config.py @@ -35,6 +35,12 @@ PROXY_TYPE = os.getenv('PROXY_TYPE', 'socks5').lower() PROXY_HOST = os.getenv('PROXY_HOST', '127.0.0.1') PROXY_PORT = int(os.getenv('PROXY_PORT', '10808')) +# ===== ВЕБ-ИНТЕРФЕЙС ===== +WEB_ADMIN_LOGIN = os.getenv('WEB_ADMIN_LOGIN', 'admin') +WEB_ADMIN_PASSWORD = os.getenv('WEB_ADMIN_PASSWORD', 'admin') +WEB_ADMIN_HOST = os.getenv('WEB_ADMIN_HOST', '0.0.0.0') +WEB_ADMIN_PORT = int(os.getenv('WEB_ADMIN_PORT', '8000')) + # Проверка критичных настроек if not BOT_TOKEN: raise ValueError('BOT_TOKEN не настроен! Проверьте .env файл') diff --git a/services/web_server.py b/services/web_server.py index c07e457..f795729 100644 --- a/services/web_server.py +++ b/services/web_server.py @@ -10,10 +10,12 @@ logger = logging.getLogger(__name__) class WebServerService: """Сервис для управления веб-сервером""" - - def __init__(self, host: str = "0.0.0.0", port: int = 8000): - self.host = host - self.port = port + + def __init__(self, host: str = None, port: int = None): + # Импортируем конфиг при инициализации + import config + self.host = host or config.WEB_ADMIN_HOST + self.port = port or config.WEB_ADMIN_PORT self.server = None self.thread: Optional[threading.Thread] = None self.is_running = False @@ -23,22 +25,23 @@ class WebServerService: if self.is_running: logger.warning("Веб-сервер уже запущен") return - + try: import uvicorn from web.app import app - + import config + def run_server(): uvicorn.run(app, host=self.host, port=self.port, log_level="info") - + self.thread = threading.Thread(target=run_server, daemon=True) self.thread.start() self.is_running = True - + logger.info(f"✅ Веб-сервер запущен на http://{self.host}:{self.port}") - logger.info(f" Логин: admin") - logger.info(f" Пароль: {self.port} (ID админа)") - + logger.info(f" Логин: {config.WEB_ADMIN_LOGIN}") + logger.info(f" Пароль: {config.WEB_ADMIN_PASSWORD}") + except Exception as e: logger.error(f"Ошибка запуска веб-сервера: {e}") self.is_running = False @@ -47,7 +50,7 @@ class WebServerService: """Остановить веб-сервер""" if not self.is_running: return - + try: self.is_running = False logger.info("🛑 Веб-сервер остановлен") diff --git a/web/app.py b/web/app.py index 22a9162..6731fda 100644 --- a/web/app.py +++ b/web/app.py @@ -42,16 +42,16 @@ security = HTTPBasic() def get_current_admin(credentials: HTTPBasicCredentials = Depends(security)): """Проверка админа""" - correct_username = secrets.compare_digest(credentials.username, "admin") - correct_password = secrets.compare_digest(credentials.password, str(config.ADMIN_USER_ID)) - + correct_username = secrets.compare_digest(credentials.username, config.WEB_ADMIN_LOGIN) + correct_password = secrets.compare_digest(credentials.password, config.WEB_ADMIN_PASSWORD) + if not (correct_username and correct_password): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Неверное имя пользователя или пароль", headers={"WWW-Authenticate": "Basic"}, ) - + return credentials.username @@ -258,11 +258,99 @@ async def settings_page(request: Request, username: str = Depends(get_current_ad "bot_token": config.BOT_TOKEN[:20] + "...", "admin_chat_id": config.ADMIN_CHAT_ID, "admin_user_id": config.ADMIN_USER_ID, - "use_proxy": config.USE_PROXY + "use_proxy": config.USE_PROXY, + "web_login": config.WEB_ADMIN_LOGIN, + "web_password": config.WEB_ADMIN_PASSWORD } }) +# ============================================================================= +# API - КОМАНДЫ БОТА +# ============================================================================= + +@app.post("/api/command") +async def api_command( + request: Request, + username: str = Depends(get_current_admin) +): + """Выполнить команду бота""" + import json + from aiogram import Bot + + data = await request.json() + command = data.get('command', '') + args = data.get('args', '') + + if not command: + return JSONResponse({"success": False, "error": "Нет команды"}) + + try: + # Создаём бота + bot = Bot(token=config.BOT_TOKEN) + + # Формируем полное сообщение команды + full_command = f"/{command}" + if args: + full_command += f" {args}" + + # Отправляем команду в чат админа + result = await bot.send_message( + config.ADMIN_CHAT_ID, + full_command + ) + + await bot.session.close() + + return JSONResponse({ + "success": True, + "message": f"Команда /{command} отправлена", + "result_id": result.message_id + }) + except Exception as e: + return JSONResponse({ + "success": False, + "error": str(e) + }, status_code=500) + + +@app.get("/api/commands") +async def api_commands_list(username: str = Depends(get_current_admin)): + """Список доступных команд""" + commands = { + "admin": [ + {"cmd": "admin", "desc": "Админ-панель"}, + {"cmd": "stats", "desc": "Статистика чата"}, + {"cmd": "spies", "desc": "Подозрительные пользователи"}, + {"cmd": "users", "desc": "Список жильцов"}, + {"cmd": "verify", "desc": "Заявки на верификацию"}, + {"cmd": "export", "desc": "Экспорт данных"}, + {"cmd": "rating", "desc": "Рейтинг пользователей"}, + ], + "initiative_group": [ + {"cmd": "ig_add", "desc": "Добавить в ИГ"}, + {"cmd": "ig_remove", "desc": "Удалить из ИГ"}, + {"cmd": "ig_list", "desc": "Список ИГ"}, + {"cmd": "ig_stats", "desc": "Статистика ИГ"}, + {"cmd": "ig_check", "desc": "Проверка участника"}, + {"cmd": "ig_broadcast", "desc": "Рассылка для ИГ"}, + ], + "events": [ + {"cmd": "event_add", "desc": "Добавить событие"}, + {"cmd": "events", "desc": "Список событий"}, + {"cmd": "participants", "desc": "Участники события"}, + {"cmd": "event_cancel", "desc": "Отменить событие"}, + ], + "security": [ + {"cmd": "unverify", "desc": "Отозвать верификацию"}, + {"cmd": "unverified_list", "desc": "Список не верифицированных"}, + {"cmd": "ban", "desc": "Забанить пользователя"}, + {"cmd": "unban", "desc": "Разбанить"}, + ] + } + return JSONResponse({"commands": commands}) + + # ============================================================================= # ЗАПУСК # =============================================================================