Этап 6: Расписание и инициативность
This commit is contained in:
parent
001d273bd1
commit
b50388063a
|
|
@ -9,6 +9,7 @@ from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||||
from config.config import get_settings
|
from config.config import get_settings
|
||||||
from src.tools.orchestrator import Orchestrator
|
from src.tools.orchestrator import Orchestrator
|
||||||
from src.bot.states import chat_state, ChatMode
|
from src.bot.states import chat_state, ChatMode
|
||||||
|
from src.scheduler.scheduler import SchedulerManager
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||||
|
|
@ -18,6 +19,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
settings = get_settings()
|
settings = get_settings()
|
||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
scheduler_manager = None
|
||||||
|
|
||||||
|
|
||||||
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
|
@ -41,7 +43,8 @@ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"/cancel - Отменить текущее действие\n"
|
"/cancel - Отменить текущее действие\n"
|
||||||
"/qwen <текст> - Задать вопрос qwen-code\n"
|
"/qwen <текст> - Задать вопрос qwen-code\n"
|
||||||
"/open <текст> - Задать вопрос opencode\n"
|
"/open <текст> - Задать вопрос opencode\n"
|
||||||
"/forget - Очистить историю чата\n\n"
|
"/forget - Очистить историю чата\n"
|
||||||
|
"/remind <текст> <время> - Создать напоминание\n\n"
|
||||||
f"Текущий режим: {'с подтверждением' if mode == ChatMode.CONFIRM else 'автономный'}\n"
|
f"Текущий режим: {'с подтверждением' if mode == ChatMode.CONFIRM else 'автономный'}\n"
|
||||||
f"Инструмент по умолчанию: {current_tool}"
|
f"Инструмент по умолчанию: {current_tool}"
|
||||||
)
|
)
|
||||||
|
|
@ -203,6 +206,48 @@ async def forget_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
await update.message.reply_text("История чата очищена.")
|
await update.message.reply_text("История чата очищена.")
|
||||||
|
|
||||||
|
|
||||||
|
async def remind_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
if not context.args:
|
||||||
|
await update.message.reply_text(
|
||||||
|
"Использование: /remind <текст> <время>\n"
|
||||||
|
"Пример: /remind Позвонить врачу через 1 час"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
text = " ".join(context.args)
|
||||||
|
chat_id = update.effective_chat.id
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
time_keywords = {
|
||||||
|
"минут": 1,
|
||||||
|
"минуту": 1,
|
||||||
|
"час": 60,
|
||||||
|
"часа": 60,
|
||||||
|
"часов": 60,
|
||||||
|
"день": 1440,
|
||||||
|
"дня": 1440,
|
||||||
|
"дней": 1440
|
||||||
|
}
|
||||||
|
|
||||||
|
minutes = 60
|
||||||
|
for keyword, value in time_keywords.items():
|
||||||
|
if keyword in text.lower():
|
||||||
|
import re
|
||||||
|
match = re.search(r'(\d+)\s*' + keyword, text.lower())
|
||||||
|
if match:
|
||||||
|
minutes = int(match.group(1)) * value
|
||||||
|
break
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
run_at = datetime.now() + timedelta(minutes=minutes)
|
||||||
|
|
||||||
|
scheduler_manager.add_reminder(chat_id, text, run_at)
|
||||||
|
await update.message.reply_text(
|
||||||
|
f"Напоминание установлено на {run_at.strftime('%H:%M %d.%m.%Y')}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
prompt = update.message.text
|
prompt = update.message.text
|
||||||
chat_id = update.effective_chat.id
|
chat_id = update.effective_chat.id
|
||||||
|
|
@ -234,6 +279,8 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
global scheduler_manager
|
||||||
|
|
||||||
builder = Application.builder()
|
builder = Application.builder()
|
||||||
builder.token(settings.telegram_bot_token)
|
builder.token(settings.telegram_bot_token)
|
||||||
|
|
||||||
|
|
@ -243,6 +290,9 @@ def main():
|
||||||
|
|
||||||
application = builder.build()
|
application = builder.build()
|
||||||
|
|
||||||
|
scheduler_manager = SchedulerManager(application.bot, orchestrator)
|
||||||
|
scheduler_manager.start()
|
||||||
|
|
||||||
application.add_handler(CommandHandler("start", start))
|
application.add_handler(CommandHandler("start", start))
|
||||||
application.add_handler(CommandHandler("help", help_command))
|
application.add_handler(CommandHandler("help", help_command))
|
||||||
application.add_handler(CommandHandler("mode", mode_command))
|
application.add_handler(CommandHandler("mode", mode_command))
|
||||||
|
|
@ -251,6 +301,7 @@ def main():
|
||||||
application.add_handler(CommandHandler("qwen", qwen_command))
|
application.add_handler(CommandHandler("qwen", qwen_command))
|
||||||
application.add_handler(CommandHandler("open", open_command))
|
application.add_handler(CommandHandler("open", open_command))
|
||||||
application.add_handler(CommandHandler("forget", forget_command))
|
application.add_handler(CommandHandler("forget", forget_command))
|
||||||
|
application.add_handler(CommandHandler("remind", remind_command))
|
||||||
application.add_handler(CallbackQueryHandler(confirm_callback))
|
application.add_handler(CallbackQueryHandler(confirm_callback))
|
||||||
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
|
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
import logging
|
||||||
|
from typing import Dict, List
|
||||||
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
|
from apscheduler.triggers.interval import IntervalTrigger
|
||||||
|
from apscheduler.triggers.date import DateTrigger
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from config.config import get_settings
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
settings = get_settings()
|
||||||
|
|
||||||
|
|
||||||
|
class SchedulerManager:
|
||||||
|
def __init__(self, bot, orchestrator):
|
||||||
|
self.bot = bot
|
||||||
|
self.orchestrator = orchestrator
|
||||||
|
self.scheduler = AsyncIOScheduler()
|
||||||
|
self.reminders: Dict[int, List[dict]] = {}
|
||||||
|
|
||||||
|
async def generate_idea(self):
|
||||||
|
logger.info("Запуск генерации идей")
|
||||||
|
|
||||||
|
for chat_id in self.orchestrator.memory.collection.get().get("metadatas", []):
|
||||||
|
try:
|
||||||
|
chat_id_int = int(chat_id.get("chat_id", 0))
|
||||||
|
if not chat_id_int:
|
||||||
|
continue
|
||||||
|
|
||||||
|
context = self.orchestrator.memory.get_context_for_prompt(chat_id_int)
|
||||||
|
if not context:
|
||||||
|
continue
|
||||||
|
|
||||||
|
idea_prompt = f"""На основе нашего разговора, придумай полезную идею, совет или вопрос для пользователя.
|
||||||
|
|
||||||
|
История разговора:
|
||||||
|
{context}
|
||||||
|
|
||||||
|
Ответь кратко (1-2 предложения), без лишних объяснений."""
|
||||||
|
|
||||||
|
idea, success = await self.orchestrator.ask(idea_prompt, chat_id_int)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
await self.bot.send_message(
|
||||||
|
chat_id=chat_id_int,
|
||||||
|
text=f"💡 Идея для вас:\n\n{idea}"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Ошибка генерации идеи для чата {chat_id}: {e}")
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
if settings.scheduler_enabled:
|
||||||
|
self.scheduler.add_job(
|
||||||
|
self.generate_idea,
|
||||||
|
trigger=IntervalTrigger(hours=settings.idea_interval_hours),
|
||||||
|
id="idea_generator",
|
||||||
|
name="Генерация идей",
|
||||||
|
replace_existing=True
|
||||||
|
)
|
||||||
|
self.scheduler.start()
|
||||||
|
logger.info("Планировщик запущен")
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.scheduler.shutdown()
|
||||||
|
logger.info("Планировщик остановлен")
|
||||||
|
|
||||||
|
def add_reminder(self, chat_id: int, text: str, run_at: datetime):
|
||||||
|
if chat_id not in self.reminders:
|
||||||
|
self.reminders[chat_id] = []
|
||||||
|
|
||||||
|
job = self.scheduler.add_job(
|
||||||
|
self._send_reminder,
|
||||||
|
trigger=DateTrigger(run_date=run_at),
|
||||||
|
args=[chat_id, text],
|
||||||
|
id=f"reminder_{chat_id}_{len(self.reminders[chat_id])}"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.reminders[chat_id].append({
|
||||||
|
"job_id": job.id,
|
||||||
|
"text": text,
|
||||||
|
"run_at": run_at
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.info(f"Напоминание добавлено для чата {chat_id}: {text} в {run_at}")
|
||||||
|
|
||||||
|
async def _send_reminder(self, chat_id: int, text: str):
|
||||||
|
try:
|
||||||
|
await self.bot.send_message(chat_id=chat_id, text=f"🔔 Напоминание:\n\n{text}")
|
||||||
|
|
||||||
|
if chat_id in self.reminders:
|
||||||
|
self.reminders[chat_id] = [
|
||||||
|
r for r in self.reminders[chat_id]
|
||||||
|
if r["text"] != text
|
||||||
|
]
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Ошибка отправки напоминания: {e}")
|
||||||
|
|
||||||
|
def get_reminders(self, chat_id: int) -> List[dict]:
|
||||||
|
return self.reminders.get(chat_id, [])
|
||||||
Loading…
Reference in New Issue