172 lines
5.6 KiB
Python
172 lines
5.6 KiB
Python
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())
|