diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1f1af2b --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +TELEGRAM_BOT_TOKEN=your_token_here + +# Полное отключение логов и любого хранения (БД, файлы логов, сохранение message_id) +# DISABLE_PERSISTENCE=1 + +# Или по отдельности: +# DISABLE_LOGGING=1 — нет логов в консоль и в файл +# DISABLE_STORAGE=1 — нет SQLite, /del не работает, группы /set не сохраняются + +SCHEDULE_DRIVE_FOLDER_ID=1WhUFHGkS4qC_e84KRArF4ooXHJr8mL5T diff --git a/addons/x_days_to/handlers.py b/addons/x_days_to/handlers.py index 058d73f..9d42482 100644 --- a/addons/x_days_to/handlers.py +++ b/addons/x_days_to/handlers.py @@ -12,6 +12,8 @@ logger = getLogger(__name__) def register_handlers(dp: Dispatcher, state: BotState, bot: Bot) -> int: async def init_db(): + if Config.DISABLE_STORAGE: + return async with aiosqlite.connect(Config.DAYS_TO_DB_PATH) as db: await db.execute(""" CREATE TABLE IF NOT EXISTS days_to_new_year ( @@ -24,6 +26,8 @@ def register_handlers(dp: Dispatcher, state: BotState, bot: Bot) -> int: logger.info("База данных инициализирована") async def save_days_to_db(user_id: int, days: int): + if Config.DISABLE_STORAGE: + return logger.debug(f"Сохраняем user_id={user_id}, days={days}") async with aiosqlite.connect(Config.DAYS_TO_DB_PATH) as db: await db.execute(""" @@ -34,6 +38,8 @@ def register_handlers(dp: Dispatcher, state: BotState, bot: Bot) -> int: logger.info(f"Запись сохранена: user_id={user_id}, days={days}") async def get_last_days(user_id: int) -> int | None: + if Config.DISABLE_STORAGE: + return None async with aiosqlite.connect(Config.DAYS_TO_DB_PATH) as db: async with db.execute( "SELECT days FROM days_to_new_year WHERE user_id = ?", (int(user_id),) diff --git a/config.py b/config.py index e91a597..d5afccc 100644 --- a/config.py +++ b/config.py @@ -1,11 +1,20 @@ import os +from pathlib import Path + from dotenv import load_dotenv from typing import Dict +load_dotenv() + + +def _env_bool(name: str, default: bool = False) -> bool: + raw = os.getenv(name) + if raw is None: + return default + return raw.strip().lower() in ("1", "true", "yes", "on") + class Config: - # Загружаем .env - load_dotenv() # API API_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") @@ -40,9 +49,23 @@ class Config: "SCHEDULE_DRIVE_FOLDER_ID", "1WhUFHGkS4qC_e84KRArF4ooXHJr8mL5T" ) + # Отключение логов и хранения (см. .env.example) + # DISABLE_PERSISTENCE=1 — выключает и логи, и все БД/файлы сразу + _NO_PERSISTENCE = _env_bool("DISABLE_PERSISTENCE") + DISABLE_LOGGING = ( + _env_bool("DISABLE_LOGGING") + if os.getenv("DISABLE_LOGGING") is not None + else _NO_PERSISTENCE + ) + DISABLE_STORAGE = ( + _env_bool("DISABLE_STORAGE") + if os.getenv("DISABLE_STORAGE") is not None + else _NO_PERSISTENCE + ) + # Пути - LOG_FILE = "storage/log/bot.log" - DAYS_TO_DB_PATH = "addons/x_days_to/days_to_new_year.db" + LOG_FILE = Path("storage/log/bot.log") + DAYS_TO_DB_PATH = Path("addons/x_days_to/days_to_new_year.db") if __name__ == "__main__": diff --git a/handlers/admin.py b/handlers/admin.py index 64b4f2c..ae537ad 100644 --- a/handlers/admin.py +++ b/handlers/admin.py @@ -17,8 +17,14 @@ def register_handlers(dp: Dispatcher, state: BotState, bot: Bot): @saving @admin_required(3) async def send_log(message: Message): + if Config.DISABLE_LOGGING: + await message.answer("📝 Логирование отключено (DISABLE_LOGGING=1).") + return + if Config.DISABLE_STORAGE: + await message.answer("📝 Файл логов не ведётся (DISABLE_STORAGE=1).") + return try: - log_file = types.FSInputFile(Config.LOG_FILE) + log_file = types.FSInputFile(str(Config.LOG_FILE)) await message.answer_document(log_file, caption="📑 Логи бота") except FileNotFoundError: await message.answer("Файл логов пока не создан.") @@ -31,7 +37,12 @@ def register_handlers(dp: Dispatcher, state: BotState, bot: Bot): from utils.mac_metrics import get_macbook_battery_level, get_process_usage try: - stats = analyze_bot_logs(Config.LOG_FILE) + if Config.DISABLE_LOGGING or Config.DISABLE_STORAGE: + await message.answer( + "📊 Аналитика по логам недоступна: логирование или хранение отключено в .env" + ) + return + stats = analyze_bot_logs(str(Config.LOG_FILE)) batt = await get_macbook_battery_level() usage = await get_process_usage() status_text = ( @@ -53,7 +64,12 @@ def register_handlers(dp: Dispatcher, state: BotState, bot: Bot): async def stat(message: Message): from utils.analytics import analyze_bot_logs - stats = analyze_bot_logs(Config.LOG_FILE) + if Config.DISABLE_LOGGING or Config.DISABLE_STORAGE: + await message.answer( + "📊 Аналитика по логам недоступна: логирование или хранение отключено в .env" + ) + return + stats = analyze_bot_logs(str(Config.LOG_FILE)) await message.answer( create_statistics_text(stats), reply_to_message_id=message.message_id ) @@ -61,6 +77,12 @@ def register_handlers(dp: Dispatcher, state: BotState, bot: Bot): @dp.message(Command("del")) @admin_required(1) async def delete_all_messages(message: Message): + if Config.DISABLE_STORAGE: + await message.answer( + "📭 Хранение сообщений отключено (DISABLE_STORAGE=1).", + reply_to_message_id=message.message_id, + ) + return messages = load_messages() if not messages: sent = await message.answer( diff --git a/main.py b/main.py index 2a794f1..81fe8e4 100644 --- a/main.py +++ b/main.py @@ -1,17 +1,11 @@ from asyncio import run -from logging import basicConfig, FileHandler, StreamHandler, INFO, getLogger +from logging import getLogger + from bot.core import TelegramBot from config import Config +from utils.logging_config import setup_logging -# Настройка логирования -basicConfig( - level=INFO, - format="%(asctime)s [%(levelname)s] %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", - handlers=[FileHandler(Config.LOG_FILE, encoding="utf-8"), StreamHandler()], - force=True, -) - +setup_logging() logger = getLogger(__name__) diff --git a/storage/DB.py b/storage/DB.py index d98b7d1..f7c13d8 100644 --- a/storage/DB.py +++ b/storage/DB.py @@ -1,6 +1,9 @@ import sqlite3 +from pathlib import Path -DIR = "/Users/mac/myfirstprogramm/storage/message.db" +from config import Config + +DIR = Path(__file__).resolve().parent / "message.db" if __name__ == "__main__": db = sqlite3.connect(DIR) @@ -33,4 +36,6 @@ if __name__ == "__main__": def get_db(): + if Config.DISABLE_STORAGE: + raise RuntimeError("Хранение отключено (DISABLE_STORAGE=1)") return sqlite3.connect(DIR) diff --git a/storage/message_storage.py b/storage/message_storage.py index 0add373..87fc054 100644 --- a/storage/message_storage.py +++ b/storage/message_storage.py @@ -1,7 +1,11 @@ -from .DB import get_db +from config import Config def save_message(chat_id: int, message_id: int): + if Config.DISABLE_STORAGE: + return + from .DB import get_db + db = get_db() cur = db.cursor() cur.execute("INSERT INTO message VALUES (?, ?)", (int(chat_id), int(message_id))) @@ -11,6 +15,10 @@ def save_message(chat_id: int, message_id: int): def load_messages(): + if Config.DISABLE_STORAGE: + return [] + from .DB import get_db + db = get_db() cur = db.cursor() cur.execute("SELECT * FROM message") @@ -21,6 +29,10 @@ def load_messages(): def clear_messages(): + if Config.DISABLE_STORAGE: + return + from .DB import get_db + db = get_db() cur = db.cursor() cur.execute("DELETE FROM message") diff --git a/storage/users_storage.py b/storage/users_storage.py index 457a7cf..7eb158e 100644 --- a/storage/users_storage.py +++ b/storage/users_storage.py @@ -1,6 +1,13 @@ -from .DB import get_db +from config import Config + +_DEFAULT_GROUP = "30тс" + + +def save_user(user_id: int, group: str = _DEFAULT_GROUP): + if Config.DISABLE_STORAGE: + return + from .DB import get_db -def save_user(user_id: int, group: str = "30тс"): db = get_db() cur = db.cursor() cur.execute("INSERT INTO users (user_id, user_group) VALUES (?, ?)", (user_id, group)) @@ -8,19 +15,20 @@ def save_user(user_id: int, group: str = "30тс"): cur.close() db.close() -def set_group(user_id: int, group: str = "30тс"): + +def set_group(user_id: int, group: str = _DEFAULT_GROUP): + if Config.DISABLE_STORAGE: + return + from .DB import get_db + db = get_db() cur = db.cursor() - - # проверяем, есть ли пользователь cur.execute("SELECT 1 FROM users WHERE user_id = ?", (user_id,)) exists = cur.fetchone() if exists: - # если есть — обновляем группу cur.execute("UPDATE users SET user_group = ? WHERE user_id = ?", (group, user_id)) else: - # если нет — регистрируем нового пользователя cur.execute("INSERT INTO users (user_id, user_group) VALUES (?, ?)", (user_id, group)) db.commit() @@ -28,7 +36,11 @@ def set_group(user_id: int, group: str = "30тс"): db.close() -def get_group(user_id: int, default: str = "30тс") -> str: +def get_group(user_id: int, default: str = _DEFAULT_GROUP) -> str: + if Config.DISABLE_STORAGE: + return default + from .DB import get_db + db = get_db() cur = db.cursor() cur.execute("SELECT user_group FROM users WHERE user_id = ?", (user_id,)) @@ -36,7 +48,6 @@ def get_group(user_id: int, default: str = "30тс") -> str: if row: group = row[0] else: - # если пользователя нет — регистрируем с дефолтной группой cur.execute("INSERT INTO users (user_id, user_group) VALUES (?, ?)", (user_id, default)) db.commit() group = default diff --git a/utils/logging_config.py b/utils/logging_config.py new file mode 100644 index 0000000..d0c6dbb --- /dev/null +++ b/utils/logging_config.py @@ -0,0 +1,34 @@ +import logging +from logging import CRITICAL, NullHandler, getLogger + +from config import Config + + +def setup_logging() -> None: + """Настройка логирования. При DISABLE_LOGGING — полное отключение.""" + root = getLogger() + + if Config.DISABLE_LOGGING: + root.handlers.clear() + root.addHandler(NullHandler()) + root.setLevel(CRITICAL) + logging.disable(CRITICAL) + return + + from logging import INFO, StreamHandler, basicConfig + from logging.handlers import FileHandler + + handlers: list[logging.Handler] = [StreamHandler()] + + if not Config.DISABLE_STORAGE: + log_path = Config.LOG_FILE + log_path.parent.mkdir(parents=True, exist_ok=True) + handlers.append(FileHandler(log_path, encoding="utf-8")) + + basicConfig( + level=INFO, + format="%(asctime)s [%(levelname)s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + handlers=handlers, + force=True, + )