🛡️ 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:
parent
97c3e5864d
commit
23ee551271
6 changed files with 240 additions and 1 deletions
|
|
@ -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
65
AI_AGENT_RULES.md
Normal 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*
|
||||
|
|
@ -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"""
|
||||
|
|
|
|||
8
main.py
8
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()
|
||||
|
|
|
|||
130
services/safety_guard.py
Normal file
130
services/safety_guard.py
Normal 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
|
||||
|
|
@ -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 после серии инцидентов с тестовыми рассылками*
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue