feat(bot): точка входа, конфиг, главное меню, доступ для ADMIN_IDS, healthcheck
- config: pydantic-settings по подсистемам, ADMIN_IDS через запятую - texts.yaml + валидация при старте (лишний/пропущенный ключ — ошибка сразу) - меню: reply-клавиатура разделов (Участники — только админам), инлайн «Назад»/«Отмена» с редактированием сообщения на месте, /start, /menu, /help - AccessMiddleware (outer) на сообщениях и кнопках: до #25 пускает только ADMIN_IDS, постороннему — одна фраза в сутки, устаревшие кнопки молча гасятся - heartbeat-файл + HEALTHCHECK в Dockerfile; JSON-логи - FSM пока в памяти (фабрика), хранилище в БД — в #5 - тесты на фейковой сессии aiogram через настоящий Dispatcher Refs #4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
"""JSON logs to stdout, one object per line. Never log message contents or secrets (SPEC §5.3)."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from datetime import UTC, datetime
|
||||
|
||||
|
||||
class JsonFormatter(logging.Formatter):
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
entry = {
|
||||
"ts": datetime.fromtimestamp(record.created, UTC).isoformat(timespec="milliseconds"),
|
||||
"level": record.levelname,
|
||||
"logger": record.name,
|
||||
"msg": record.getMessage(),
|
||||
}
|
||||
if record.exc_info:
|
||||
entry["exc"] = self.formatException(record.exc_info)
|
||||
return json.dumps(entry, ensure_ascii=False)
|
||||
|
||||
|
||||
def setup_logging(level: str) -> None:
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(JsonFormatter())
|
||||
logging.basicConfig(level=level.upper(), handlers=[handler], force=True)
|
||||
Reference in New Issue
Block a user