diff --git a/main.py b/main.py index d473e32..79e3389 100644 --- a/main.py +++ b/main.py @@ -1,183 +1,161 @@ import os +import asyncio from datetime import datetime from dotenv import load_dotenv -from aiogram import Bot, Dispatcher from aiogram.client.default import DefaultBotProperties from aiogram.enums import ParseMode -from aiogram.filters import Command, CommandStart -from aiogram.types import Message +from aiogram.filters import CommandStart, Command +from aiogram.types import ( + Message, + ReplyKeyboardMarkup, + KeyboardButton, + ReplyKeyboardRemove, +) from trilium_py.client import ETAPI +from aiogram import Bot, Dispatcher, F +from aiogram.fsm.context import FSMContext +from aiogram.fsm.state import State, StatesGroup +from aiogram.fsm.storage.memory import MemoryStorage load_dotenv() - TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN") TRILIUM_URL = os.getenv("TRILIUM_URL") TRILIUM_TOKEN = os.getenv("TRILIUM_TOKEN") INBOX_NOTE_ID = os.getenv("INBOX_NOTE_ID") -DAILY_NOTE_ID = os.getenv("DAILY_NOTE_ID") # ID папки для ежедневных заметок # создаем Trilium API клиент ea = ETAPI(server_url=TRILIUM_URL, token=TRILIUM_TOKEN) bot = Bot(token=TELEGRAM_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML)) -dp = Dispatcher() -pending_daily_chats = set() # chat_ids ожидающих текст для /daily +storage = MemoryStorage() + +dp = Dispatcher(storage=storage) -@dp.message(CommandStart()) -async def start(msg: Message): - await msg.answer("Отправь мне текст — я создам заметку в Trilium.") +class DailyStates(StatesGroup): + waiting_for_text = State() -def _get_id(obj): - if isinstance(obj, dict): - return obj.get("noteId") or obj.get("id") - return getattr(obj, "noteId", None) or getattr(obj, "id", None) +# Клавиатуры +main_keyboard = ReplyKeyboardMarkup( + keyboard=[ + [KeyboardButton(text="📅 Daily"), KeyboardButton(text="📥 Inbox")], + [KeyboardButton(text="❌ Отмена")], + ], + resize_keyboard=True, +) + +cancel_keyboard = ReplyKeyboardMarkup( + keyboard=[[KeyboardButton(text="❌ Отмена")]], resize_keyboard=True +) -def _get_title(obj): - return obj.get("title") if isinstance(obj, dict) else getattr(obj, "title", "") - - -def _get_content(obj): - return obj.get("content") if isinstance(obj, dict) else getattr(obj, "content", "") - - -def _children(obj): - if isinstance(obj, dict): - return obj.get("children") or [] - return getattr(obj, "children", []) or [] - - -def _translate_month_en_ru(name: str) -> str: - mapping = { - "January": "Январь", - "February": "Февраль", - "March": "Март", - "April": "Апрель", - "May": "Май", - "June": "Июнь", - "July": "Июль", - "August": "Август", - "September": "Сентябрь", - "October": "Октябрь", - "November": "Ноябрь", - "December": "Декабрь", - } - return mapping.get(name, name) - - -def _translate_weekday_en_ru(name: str) -> str: - mapping = { - "Monday": "Понедельник", - "Tuesday": "Вторник", - "Wednesday": "Среда", - "Thursday": "Четверг", - "Friday": "Пятница", - "Saturday": "Суббота", - "Sunday": "Воскресенье", - } - return mapping.get(name, name) - - -def save_inbox(text: str): - # разделяем на заголовок и тело - lines = text.split("\n", 1) - title = lines[0][:100] - content = lines[1] if len(lines) > 1 else "" - ea.create_note( - parentNoteId=INBOX_NOTE_ID, title=title, content=content, type="text" +@dp.message(Command("start")) +async def cmd_start(message: Message): + """Команда /start - показываем главное меню""" + await message.answer( + "Выбери действие:\n" + "📅 Daily - добавить в дневную заметку\n" + "📥 Inbox - создать заметку в Inbox (по умолчанию)", + reply_markup=main_keyboard, ) -def save_daily(text: str) -> str: - today = datetime.now() - month_name = ( - f"{today.strftime('%m')} - {_translate_month_en_ru(today.strftime('%B'))}" +@dp.message(F.text == "📅 Daily") +async def btn_daily(message: Message, state: FSMContext): + """Кнопка Daily""" + await message.answer( + "Отправь текст для сегодняшней заметки:", reply_markup=cancel_keyboard ) - day_name = ( - f"{today.strftime('%d')} - {_translate_weekday_en_ru(today.strftime('%A'))}" - ) - today_date = today.strftime("%Y-%m-%d") - - # Корень daily - daily_root = ea.get_note(DAILY_NOTE_ID) - - # Ищем/создаём месяц - month_folder = None - for child in _children(daily_root): - if _get_title(child) == month_name: - month_folder = child - break - - if not month_folder: - month_folder = ea.create_note( - parentNoteId=DAILY_NOTE_ID, title=month_name, type="text" - ) - - month_id = _get_id(month_folder) - month_folder = ea.get_note(month_id) - - # Ищем/создаём день - day_folder = None - for child in _children(month_folder): - if _get_title(child) == day_name: - day_folder = child - break - - if not day_folder: - day_folder = ea.create_note(parentNoteId=month_id, title=day_name, type="text") - - day_id = _get_id(day_folder) - day_folder = ea.get_note(day_id) - - # Ищем заметку по дате - existing_note = None - for child in _children(day_folder): - if _get_title(child) == today_date: - existing_note = child - break - - if existing_note: - existing_content = _get_content(existing_note) - new_content = f"{existing_content}\n\n---\n{text}" if existing_content else text - ea.update_note(noteId=_get_id(existing_note), content=new_content) - return f"Добавлено в ежедневную заметку за {today_date}." - - ea.create_note(parentNoteId=day_id, title=today_date, content=text, type="text") - return f"Создана ежедневная заметка за {today_date}." + await state.set_state(DailyStates.waiting_for_text) @dp.message(Command("daily")) -async def daily_command(msg: Message): - pending_daily_chats.add(msg.chat.id) - await msg.answer( - "Жду текст для ежедневной заметки. Следующее сообщение запишу в текущий день." +async def cmd_daily(message: Message, state: FSMContext): + """Команда /daily - альтернативный способ""" + await message.answer( + "Отправь текст для сегодняшней заметки:", reply_markup=cancel_keyboard + ) + await state.set_state(DailyStates.waiting_for_text) + + +@dp.message(DailyStates.waiting_for_text, F.text != "❌ Отмена") +async def process_daily_text(message: Message, state: FSMContext): + """Обработка текста для дневной заметки""" + text = message.text + today = datetime.now().strftime("%Y-%m-%d") + + try: + existing_content = ea.get_day_note(today) + timestamp = datetime.now().strftime("%H:%M") + new_content = f"{existing_content}

{timestamp}: {text}

" + ea.set_day_note(today, new_content) + + await message.answer( + f"✅ Добавлено в дневную заметку", reply_markup=main_keyboard + ) + + except Exception as e: + await message.answer(f"❌ Ошибка: {e}", reply_markup=main_keyboard) + + await state.clear() + + +@dp.message(F.text == "❌ Отмена") +async def btn_cancel(message: Message, state: FSMContext): + """Кнопка отмены""" + await state.clear() + await message.answer("Отменено", reply_markup=main_keyboard) + + +@dp.message(Command("cancel")) +async def cmd_cancel(message: Message, state: FSMContext): + """Команда отмены""" + await state.clear() + await message.answer("Отменено", reply_markup=main_keyboard) + + +@dp.message(F.text == "📥 Inbox") +async def btn_inbox(message: Message): + """Информация о режиме Inbox""" + await message.answer( + "Просто отправь любой текст - он автоматически создаст заметку в Inbox", + reply_markup=main_keyboard, ) -@dp.message() -async def handler(msg: Message): - text = (msg.text or "").strip() - - # Если ожидаем daily — пишем туда - if msg.chat.id in pending_daily_chats: - pending_daily_chats.discard(msg.chat.id) - try: - result = save_daily(text) - await msg.answer(result) - except Exception as e: - await msg.answer(f"Ошибка при работе с ежедневной заметкой: {str(e)}") +@dp.message(F.text & ~F.text.startswith("/")) +async def handle_text_to_inbox(message: Message, state: FSMContext): + """Обработка обычного текста -> создаём новую заметку в Inbox""" + current_state = await state.get_state() + if current_state is not None: + # Если в состоянии daily - пропускаем return - # Обычная заметка в Inbox + # Игнорируем текст кнопок + if message.text in ["📅 Daily", "📥 Inbox", "❌ Отмена"]: + return + text = message.text + lines = text.split("\n", 1) + title = lines[0][:100] # заголовок = первая строка + content = lines[1] if len(lines) > 1 else "" # остальное — тело заметки + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M") + # title = text[:50] + "..." if len(text) > 50 else text + try: - save_inbox(text) - await msg.answer("Заметка сохранена в Trilium.") + res = ea.create_note( + parentNoteId=INBOX_NOTE_ID, + title=title, + type="text", + content=f"

{timestamp}

{content}

", + ) + + await message.answer(f"✅ Создана заметка в Inbox", reply_markup=main_keyboard) + except Exception as e: - await msg.answer(f"Ошибка при сохранении в Inbox: {str(e)}") + await message.answer(f"❌ Ошибка: {e}", reply_markup=main_keyboard) async def main(): @@ -185,6 +163,4 @@ async def main(): if __name__ == "__main__": - import asyncio - asyncio.run(main())