Files
trilium-bot/main.py

172 lines
5.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
import asyncio
from datetime import datetime
from zoneinfo import ZoneInfo
from dotenv import load_dotenv
from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode
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")
TIMEZONE = os.getenv("TIMEZONE", "UTC") # Переменная окружения для timezone
# Получаем объект timezone
tz = ZoneInfo(TIMEZONE)
# создаем Trilium API клиент
ea = ETAPI(server_url=TRILIUM_URL, token=TRILIUM_TOKEN)
bot = Bot(token=TELEGRAM_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
storage = MemoryStorage()
dp = Dispatcher(storage=storage)
class DailyStates(StatesGroup):
waiting_for_text = State()
# Клавиатуры
main_keyboard = ReplyKeyboardMarkup(
keyboard=[
[KeyboardButton(text="📅 Daily"), KeyboardButton(text="📥 Inbox")],
[KeyboardButton(text="❌ Отмена")],
],
resize_keyboard=True,
)
cancel_keyboard = ReplyKeyboardMarkup(
keyboard=[[KeyboardButton(text="❌ Отмена")]], resize_keyboard=True
)
@dp.message(Command("start"))
async def cmd_start(message: Message):
"""Команда /start - показываем главное меню"""
await message.answer(
"Выбери действие:\n"
"📅 Daily - добавить в дневную заметку\n"
"📥 Inbox - создать заметку в Inbox (по умолчанию)",
reply_markup=main_keyboard,
)
@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 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(tz).strftime("%Y-%m-%d")
try:
existing_content = ea.get_day_note(today)
timestamp = datetime.now(tz).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(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
# Игнорируем текст кнопок
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(tz).strftime("%Y-%m-%d %H:%M")
# title = text[:50] + "..." if len(text) > 50 else text
try:
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 message.answer(f"❌ Ошибка: {e}", reply_markup=main_keyboard)
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())