updated vers

This commit is contained in:
Danamir
2025-08-03 10:06:47 +03:00
parent b8a9a244f5
commit 7d697dce9b
2 changed files with 186 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
venv
bot*.py
.env

183
main.py Normal file
View File

@@ -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())