🛡️ feat: Система защиты от случайных рассылок

Техническая защита:
- 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 <qwen-coder@alibabacloud.com>
This commit is contained in:
Admin 2026-04-12 18:57:42 +04:00
parent 97c3e5864d
commit 23ee551271
6 changed files with 240 additions and 1 deletions

View file

@ -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

65
AI_AGENT_RULES.md Normal file
View file

@ -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*

View file

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

View file

@ -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()

130
services/safety_guard.py Normal file
View file

@ -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

View file

@ -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 после серии инцидентов с тестовыми рассылками*
---