Добавлено состояние для обработки текстовых сообщений в дневных заметках и улучшена логика создания заметок в Inbox
All checks were successful
Deploy bot / build-deploy (push) Successful in 32s

This commit is contained in:
Dmitry
2025-12-12 18:55:36 +03:00
parent 7df16e92a4
commit 440fcc0f6a

260
main.py
View File

@@ -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,
)
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"
cancel_keyboard = ReplyKeyboardMarkup(
keyboard=[[KeyboardButton(text="❌ Отмена")]], resize_keyboard=True
)
def save_daily(text: str) -> str:
today = datetime.now()
month_name = (
f"{today.strftime('%m')} - {_translate_month_en_ru(today.strftime('%B'))}"
)
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"
@dp.message(Command("start"))
async def cmd_start(message: Message):
"""Команда /start - показываем главное меню"""
await message.answer(
"Выбери действие:\n"
"📅 Daily - добавить в дневную заметку\n"
"📥 Inbox - создать заметку в Inbox (по умолчанию)",
reply_markup=main_keyboard,
)
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}."
@dp.message(F.text == "📅 Daily")
async def btn_daily(message: Message, state: FSMContext):
"""Кнопка Daily"""
await message.answer(
"Отправь текст для сегодняшней заметки:", reply_markup=cancel_keyboard
)
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}<p><strong>{timestamp}</strong>: {text}</p>"
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"<p><strong>{timestamp}</strong></p><p>{content}</p>",
)
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())