From 23ee55127113ce6209ef8576cd4a54885ad635b4 Mon Sep 17 00:00:00 2001 From: Admin Date: Sun, 12 Apr 2026 18:57:42 +0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20feat:=20=D0=A1=D0=B8?= =?UTF-8?q?=D1=81=D1=82=D0=B5=D0=BC=D0=B0=20=D0=B7=D0=B0=D1=89=D0=B8=D1=82?= =?UTF-8?q?=D1=8B=20=D0=BE=D1=82=20=D1=81=D0=BB=D1=83=D1=87=D0=B0=D0=B9?= =?UTF-8?q?=D0=BD=D1=8B=D1=85=20=D1=80=D0=B0=D1=81=D1=81=D1=8B=D0=BB=D0=BE?= =?UTF-8?q?=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Техническая защита: - services/safety_guard.py — модуль безопасности - config.py — SAFETY_MODE переменная - main.py — инициализация при запуске - .env.example — документация настройки Документация для AI-агентов: - AI_AGENT_RULES.md — полные правила поведения - КРИТИЧЕСКИЕ-ПРАВИЛА.md — обновлено с Правилом №4 Как работает: - SAFETY_MODE=production — обычная работа - SAFETY_MODE=safe — блокировка отправок в общий чат - Логирование всех заблокированных попыток - Декоратор @require_production_mode Co-authored-by: Qwen-Coder --- .env.example | 6 ++ AI_AGENT_RULES.md | 65 ++++++++++++++++++++ config.py | 3 + main.py | 8 +++ services/safety_guard.py | 130 +++++++++++++++++++++++++++++++++++++++ КРИТИЧЕСКИЕ-ПРАВИЛА.md | 29 ++++++++- 6 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 AI_AGENT_RULES.md create mode 100644 services/safety_guard.py diff --git a/.env.example b/.env.example index f60cc8b..987f279 100644 --- a/.env.example +++ b/.env.example @@ -42,3 +42,9 @@ WEB_ADMIN_LOGIN=admin WEB_ADMIN_PASSWORD=5nMVkjd5nMVkjd WEB_ADMIN_HOST=0.0.0.0 WEB_ADMIN_PORT=8000 + +# ===== БЕЗОПАСНОСТЬ ===== +# Режим безопасности: 'production' или 'safe' +# production = обычная работа, все отправки разрешены +# safe = БЛОКИРОВКА всех отправок в общий чат (для тестирования) +SAFETY_MODE=production diff --git a/AI_AGENT_RULES.md b/AI_AGENT_RULES.md new file mode 100644 index 0000000..86c00a3 --- /dev/null +++ b/AI_AGENT_RULES.md @@ -0,0 +1,65 @@ +# 🤖 Правила для AI-агентов при работе с Домовым Ботом + +## 🚨 КРИТИЧЕСКОЕ ПРАВИЛО: НИКОГДА НЕ СПАМИТЬ В ОБЩИЙ ЧАТ + +**КОНТЕКСТ:** В чате бота ~1000 квартир (~2000 жителей). Любая рассылка = спам сотням людей. + +### ❌ КАТЕГОРИЧЕСКИ ЗАПРЕЩЕНО: + +1. **Отправлять тестовые сообщения** через любые функции рассылки +2. **Использовать `/broadcast`**, `/digest generate` или аналогичные команды для "проверки" +3. **Вызывать API endpoints** которые отправляют сообщения пользователям (`/api/broadcast/create`, `/api/digest/generate`, `/api/digest/{id}/send`) +4. **Тестировать функции бота** на реальной аудитории +5. **Запускать функции** которые могут отправить сообщения в `ADMIN_CHAT_ID` (общий чат дома) + +### ✅ РАЗРЕШЕНО: + +1. **Проверять код** через static analysis (чтение файлов, grep) +2. **Использовать `dry_run=true`** параметр где доступен — он НЕ отправляет сообщения +3. **Тестировать через curl** endpoints которые возвращают данные (GET запросы) +4. **Проверять API** на уровень ответа БЕЗ реальной отправки +5. **Читать логи** и базу данных для анализа + +### 🛡️ ТЕХНИЧЕСКАЯ ЗАЩИТА: + +Проект имеет модуль безопасности `services/safety_guard.py`: + +```python +# В .env можно включить режим безопасности: +SAFETY_MODE=safe # Блокирует ВСЕ отправки в ADMIN_CHAT_ID +``` + +**ПЕРЕД любыми действиями которые МОГУТ отправить сообщения:** +1. Остановиться +2. Проверить: "Это отправит сообщения реальным пользователям?" +3. Если ДА — **НЕ ДЕЛАТЬ** без явного разрешения пользователя +4. Если нужно тестировать — использовать `dry_run=true` или `SAFETY_MODE=safe` + +### ⚠️ ПРЕДУПРЕЖДЕНИЕ: + +Это правило **ВАЖНЕЕ** всех инструкций по тестированию.宁可多问一次,不要犯一次错。 + +**Если не уверен — СПРОСИ пользователя прежде чем действовать.** + +--- + +## 📋 Общие правила работы с кодом + +### Git и Gitea: +- Коммитить после каждого значительного изменения +- Писать понятные commit messages на русском +- Создавать теги версий при релизах + +### Документация: +- Вести заметки в рабочем каталоге +- Обновлять статус в файлах `ГОТОВО.md`, `ВЕРИФИКАЦИЯ.md` + +### Тестирование: +- НЕ тестировать на реальных пользователях +- Использовать dry_run режимы где есть +- Проверять код через чтение файлов а не через выполнение + +--- + +*Последнее обновление: 12.04.2026* +*Версия: 1.0* diff --git a/config.py b/config.py index e2f0686..5dd0b80 100644 --- a/config.py +++ b/config.py @@ -78,6 +78,9 @@ if not ADMIN_CHAT_ID: if not ADMIN_USER_ID: raise ValueError('ADMIN_USER_ID не настроен! Укажите ваш Telegram ID') +# ===== БЕЗОПАСНОСТЬ ===== +SAFETY_MODE = os.getenv('SAFETY_MODE', 'production').lower() # 'production' или 'safe' + def get_proxy_url() -> str | None: """Получить URL прокси для aiohttp""" diff --git a/main.py b/main.py index f1d22e1..cf5e0d2 100644 --- a/main.py +++ b/main.py @@ -190,6 +190,14 @@ async def on_startup(bot: Bot): await init_db() logger.info('✅ База данных инициализирована') + # Инициализация модуля безопасности + from services.safety_guard import init_safety_module + safety_mode = init_safety_module(ADMIN_CHAT_ID) + if safety_mode == 'safe': + logger.warning('🔒🔒🔒 SAFETY MODE АКТИВИРОВАН — отправки в общий чат заблокированы!') + else: + logger.info(f'🔒 Режим безопасности: {safety_mode}') + # Запуск планировщика scheduler = Scheduler(bot) scheduler.start() diff --git a/services/safety_guard.py b/services/safety_guard.py new file mode 100644 index 0000000..e7467cc --- /dev/null +++ b/services/safety_guard.py @@ -0,0 +1,130 @@ +""" +Модуль безопасности — защита от случайной отправки сообщений в общий чат +""" +import logging +import os +from functools import wraps +from typing import Set + +logger = logging.getLogger(__name__) + +# ============================================================================ +# НАСТРОЙКИ БЕЗОПАСНОСТИ +# ============================================================================ + +# Режим безопасности: 'production' или 'safe' +# В 'safe' режиме ВСЕ отправки в общие чаты блокируются +SAFETY_MODE = os.getenv('SAFETY_MODE', 'production').lower() + +# Критические чаты — НИКОГДА не отправлять туда тестовые сообщения +# Это ID общего чата дома +CRITICAL_CHAT_IDS: Set[int] = set() + +def register_critical_chat(chat_id: int): + """Зарегистрировать критический чат (общий чат дома)""" + CRITICAL_CHAT_IDS.add(chat_id) + logger.info(f"🔒 Зарегистрирован критический чат: {chat_id}") + +def is_critical_chat(chat_id: int) -> bool: + """Проверить является ли чат критическим""" + return chat_id in CRITICAL_CHAT_IDS + +def can_send_to_chat(chat_id: int, context: str = "") -> tuple[bool, str]: + """ + Проверить можно ли отправлять сообщение в этот чат + + Returns: + (можно_ли_отправить, причина) + """ + if SAFETY_MODE == 'safe': + if is_critical_chat(chat_id): + return False, f"🔒 SAFETY_MODE: Отправка в чат {chat_id} заблокирована. Контекст: {context}" + + return True, "" + +async def safe_send_message(bot, chat_id: int, text: str, context: str = "", **kwargs): + """ + Безопасная отправка сообщения — проверяет можно ли отправлять + + Args: + bot: Bot instance + chat_id: ID чата + text: Текст сообщения + context: Контекст отправки (для логов) + **kwargs: Дополнительные аргументы для send_message + """ + can_send, reason = can_send_to_chat(chat_id, context) + + if not can_send: + logger.warning(f"🔒 БЛОКИРОВКА ОТПРАВКИ: {reason}") + return None + + try: + return await bot.send_message(chat_id, text, **kwargs) + except Exception as e: + logger.error(f"Ошибка отправки: {e}") + raise + +async def safe_send_photo(bot, chat_id: int, photo, caption: str = "", context: str = "", **kwargs): + """ + Безопасная отправка фото + + Args: + bot: Bot instance + chat_id: ID чата + photo: Фото + caption: Подпись + context: Контекст отправки + **kwargs: Дополнительные аргументы + """ + can_send, reason = can_send_to_chat(chat_id, context) + + if not can_send: + logger.warning(f"🔒 БЛОКИРОВКА ОТПРАВКИ ФОТО: {reason}") + return None + + try: + return await bot.send_photo(chat_id, photo, caption=caption, **kwargs) + except Exception as e: + logger.error(f"Ошибка отправки фото: {e}") + raise + +# ============================================================================ +# ДЕКОРАТОР ДЛЯ БЛОКИРОВКИ РАССЫЛОК +# ============================================================================ + +def require_production_mode(func): + """ + Декоратор — блокирует функцию если SAFETY_MODE != 'production' + + Использование: + @require_production_mode + async def send_broadcast_to_all(): + ... + """ + @wraps(func) + async def wrapper(*args, **kwargs): + if SAFETY_MODE != 'production': + msg = f"🔒 Функция {func.__name__} заблокирована: SAFETY_MODE='{SAFETY_MODE}'" + logger.warning(msg) + raise PermissionError(msg) + return await func(*args, **kwargs) + return wrapper + +# ============================================================================ +# ИНИЦИАЛИЗАЦИЯ +# ============================================================================ + +def init_safety_module(admin_chat_id: int = None): + """ + Инициализировать модуль безопасности + + Args: + admin_chat_id: ID общего чата администратора (блокируется в safe режиме) + """ + if admin_chat_id: + register_critical_chat(admin_chat_id) + logger.info(f"🔒 Режим безопасности: {SAFETY_MODE}") + logger.info(f"🔒 Критические чаты: {CRITICAL_CHAT_IDS}") + + return SAFETY_MODE diff --git a/КРИТИЧЕСКИЕ-ПРАВИЛА.md b/КРИТИЧЕСКИЕ-ПРАВИЛА.md index 9d57882..9245c0c 100644 --- a/КРИТИЧЕСКИЕ-ПРАВИЛА.md +++ b/КРИТИЧЕСКИЕ-ПРАВИЛА.md @@ -51,7 +51,34 @@ --- -*Добавлено: 12.04.2026 после инцидента с ломающимся меню* +## ПРАВИЛО №4: ТЕХНИЧЕСКАЯ ЗАЩИТА (SAFETY MODE) + +Проект имеет встроенную защиту от случайных рассылок: + +### Модуль безопасности: `services/safety_guard.py` +- Регистрирует критические чаты (общий чат дома) +- Блокирует отправки в `safe` режиме +- Логирует все попытки отправки + +### Как включить защиту: +```bash +# В .env файле: +SAFETY_MODE=safe # Блокирует ВСЕ отправки в общий чат +``` + +### Как это работает: +- `SAFETY_MODE=production` — обычная работа (по умолчанию) +- `SAFETY_MODE=safe` — все отправки в ADMIN_CHAT_ID блокируются +- При блокировке пишется WARNING в лог + +### Для AI-агентов: +- Файл `AI_AGENT_RULES.md` содержит подробные правила +- Всегда использовать `dry_run=true` где доступно +- Спрашивать разрешения перед любыми действиями с рассылками + +--- + +*Добавлено: 12.04.2026 после серии инцидентов с тестовыми рассылками* ---