From 7d697dce9b4bf8f845cdc88904e360a5543b609c Mon Sep 17 00:00:00 2001 From: Danamir Date: Sun, 3 Aug 2025 10:06:47 +0300 Subject: [PATCH] updated vers --- .gitignore | 3 + main.py | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 .gitignore create mode 100644 main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57268be --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +venv +bot*.py +.env diff --git a/main.py b/main.py new file mode 100644 index 0000000..3e115ca --- /dev/null +++ b/main.py @@ -0,0 +1,183 @@ +import os +import logging +from aiogram import Bot, Dispatcher, Router, types, F +from aiogram.filters import CommandStart, StateFilter +from aiogram.fsm.state import StatesGroup, State +from aiogram.fsm.context import FSMContext +from aiogram.types import ReplyKeyboardMarkup, KeyboardButton +from dotenv import load_dotenv +import kanboard + +# Логирование +logging.basicConfig(level=logging.INFO) + +# Загрузка токена из .env +load_dotenv() +BOT_TOKEN = os.getenv("TELEGRAM_TOKEN") +KB_URI = os.getenv("KB_URI") +BOT_LOGIN = os.getenv("BOT_LOGIN") +BOT_PASS = os.getenv("BOT_PASS") + +bot = Bot(token=BOT_TOKEN) +dp = Dispatcher() +router = Router() + +# Kanboard client (укажи свои адрес и логин/пароль) +kb_connect = "http://"+KB_URI+"/jsonrpc.php" +kb = kanboard.Client(kb_connect,BOT_LOGIN, BOT_PASS) + +# FSM состояния +class ProjectStates(StatesGroup): + waiting_project_choice = State() + project_menu = State() + waiting_for_task_title = State() + +# /start — главное меню +@router.message(CommandStart()) +async def start(msg: types.Message, state: FSMContext): + keyboard = ReplyKeyboardMarkup( + keyboard=[[KeyboardButton(text="Список проектов")]], + resize_keyboard=True + ) + await msg.answer( + "Добро пожаловать! Выберите действие:", + reply_markup=keyboard + ) + await state.clear() + +# Кнопка "Список проектов" +@router.message(F.text == "Список проектов") +async def show_projects(msg: types.Message, state: FSMContext): + try: + projects = kb.get_my_projects() + await state.update_data(projects=projects) + except Exception as e: + await msg.answer(f"Ошибка получения проектов: {e}") + return + + # Список проектов с номерами + lines = [f"{idx + 1}. {proj['name']}" for idx, proj in enumerate(projects)] + text = "Выберите проект (отправьте его номер):\n" + "\n".join(lines) + await msg.answer(text) + await state.set_state(ProjectStates.waiting_project_choice) + +# Выбор проекта по номеру +@router.message(StateFilter(ProjectStates.waiting_project_choice), F.text) +async def project_chosen(msg: types.Message, state: FSMContext): + data = await state.get_data() + projects = data.get("projects", []) + + # Проверяем, что введено число + try: + idx = int(msg.text.strip()) - 1 + except ValueError: + await msg.answer("Пожалуйста, введите номер проекта.") + return + + if not (0 <= idx < len(projects)): + await msg.answer("Нет проекта с таким номером. Попробуйте ещё раз.") + return + + selected = projects[idx] + await state.update_data(selected_project=selected) + keyboard = ReplyKeyboardMarkup( + keyboard=[ + [KeyboardButton(text="Список задач")], + [KeyboardButton(text="Добавить задачу")], + [KeyboardButton(text="Назад")], + ], + resize_keyboard=True + ) + await msg.answer( + f"Вы выбрали проект: {selected['name']}\nВыберите действие:", + reply_markup=keyboard + ) + await state.set_state(ProjectStates.project_menu) + +# Кнопка "Список задач" +@router.message(StateFilter(ProjectStates.project_menu), F.text == "Список задач") +async def show_tasks(msg: types.Message, state: FSMContext): + data = await state.get_data() + project = data.get("selected_project") + if not project: + await msg.answer("Ошибка: проект не выбран.") + return + try: + tasks = kb.get_all_tasks(project_id=project['id']) + if not tasks: + await msg.answer("В этом проекте нет задач.") + else: + text = "\n".join([ + f"{t['title']}" + for t in tasks + ]) + await msg.answer(f"Текущие задачи:\n{text}") + except Exception as e: + await msg.answer(f"Ошибка получения задач: {e}") + +# Кнопка "Добавить задачу" +@router.message(StateFilter(ProjectStates.project_menu), F.text == "Добавить задачу") +async def add_task(msg: types.Message, state: FSMContext): + await msg.answer("Напишите название задачи одним сообщением:") + await state.set_state(ProjectStates.waiting_for_task_title) + +# Кнопка "Назад" — возвращает к выбору проекта по номеру +@router.message(StateFilter(ProjectStates.project_menu), F.text == "Назад") +async def go_back_to_projects(msg: types.Message, state: FSMContext): + data = await state.get_data() + projects = data.get("projects", []) + if not projects: + # если вдруг projects нет в state (например, после сброса) + try: + projects = kb.get_my_projects() + await state.update_data(projects=projects) + except Exception as e: + await msg.answer(f"Ошибка получения проектов: {e}") + return + + lines = [f"{idx + 1}. {proj['name']}" for idx, proj in enumerate(projects)] + text = "Выберите проект (отправьте его номер):\n" + "\n".join(lines) + await state.update_data(selected_project=None) + keyboard = ReplyKeyboardMarkup( + keyboard=[[KeyboardButton(text="Список проектов")]], + resize_keyboard=True + ) + await msg.answer( + text, + reply_markup=keyboard + ) + await state.set_state(ProjectStates.waiting_project_choice) + + +# Принимаем название задачи и создаём её +@router.message(StateFilter(ProjectStates.waiting_for_task_title), F.text) +async def task_title_received(msg: types.Message, state: FSMContext): + data = await state.get_data() + project = data.get("selected_project") + title = msg.text + try: + kb.create_task(project_id=project['id'], title=title) + await msg.answer(f"Задача '{title}' добавлена!") + except Exception as e: + await msg.answer(f"Ошибка добавления задачи: {e}") + # Меню проекта после добавления задачи + keyboard = ReplyKeyboardMarkup( + keyboard=[ + [KeyboardButton(text="Список задач")], + [KeyboardButton(text="Добавить задачу")], + [KeyboardButton(text="Назад")], + ], + resize_keyboard=True + ) + await msg.answer("Выберите действие:", reply_markup=keyboard) + await state.set_state(ProjectStates.project_menu) + +# Регистрация роутера +dp.include_router(router) + +async def main(): + await dp.start_polling(bot) + +if __name__ == "__main__": + import asyncio + asyncio.run(main())