feat: режим чата с ИИ агентом через меню
Новые возможности: - Кнопка '💬 Чат с ИИ агентом' в главном меню - Включение/выключение режима чата - Все сообщения отправляются в Qwen Code когда режим включён - Индикация статуса в кнопке (✅/❌) - Индикация статуса в главном меню Изменения: - UserState.ai_chat_mode — флаг режима - handle_ai_task() — обработка задач для ИИ - Динамическое обновление кнопки в get_keyboard() - Обновление 'О боте' с информацией о чате с ИИ Использование: 1. Меню → 💬 Чат с ИИ агентом (включить) 2. Отправлять сообщения как задачи для ИИ 3. Меню → 💬 Чат с ИИ агентом (выключить) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
cac597688d
commit
b1ccda8a13
98
bot.py
98
bot.py
|
|
@ -298,6 +298,7 @@ class UserState:
|
||||||
working_directory: Optional[str] = None
|
working_directory: Optional[str] = None
|
||||||
current_server: str = "local" # Имя текущего сервера
|
current_server: str = "local" # Имя текущего сервера
|
||||||
editing_server: Optional[str] = None # Имя сервера, который редактируем
|
editing_server: Optional[str] = None # Имя сервера, который редактируем
|
||||||
|
ai_chat_mode: bool = False # Режим чата с ИИ агентом
|
||||||
|
|
||||||
|
|
||||||
class StateManager:
|
class StateManager:
|
||||||
|
|
@ -771,17 +772,33 @@ class MenuBuilder:
|
||||||
def get_menu(self, menu_name: str) -> List[MenuItem]:
|
def get_menu(self, menu_name: str) -> List[MenuItem]:
|
||||||
return self._menus.get(menu_name, [])
|
return self._menus.get(menu_name, [])
|
||||||
|
|
||||||
def get_keyboard(self, menu_name: str) -> InlineKeyboardMarkup:
|
def get_keyboard(self, menu_name: str, user_id: int = None) -> InlineKeyboardMarkup:
|
||||||
"""Создает InlineKeyboard для меню."""
|
"""Создает InlineKeyboard для меню."""
|
||||||
items = self._menus.get(menu_name, [])
|
items = self._menus.get(menu_name, [])
|
||||||
keyboard = []
|
keyboard = []
|
||||||
|
|
||||||
|
# Для главного меню — динамически меняем кнопку ИИ
|
||||||
|
if menu_name == "main" and user_id:
|
||||||
|
from bot import state_manager
|
||||||
|
state = state_manager.get(user_id)
|
||||||
|
ai_status = "✅" if state.ai_chat_mode else "❌"
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
if item.callback == "toggle_ai_chat":
|
||||||
|
# Меняем текст кнопки
|
||||||
|
label = f"{ai_status} Чат с ИИ агентом"
|
||||||
|
button = InlineKeyboardButton(label, callback_data=item.callback)
|
||||||
|
else:
|
||||||
|
button = InlineKeyboardButton(item.label, callback_data=item.callback)
|
||||||
|
keyboard.append([button])
|
||||||
|
else:
|
||||||
for item in items:
|
for item in items:
|
||||||
# Иконка уже есть в label, поэтому не добавляем её отдельно
|
|
||||||
button = InlineKeyboardButton(
|
button = InlineKeyboardButton(
|
||||||
item.label,
|
item.label,
|
||||||
callback_data=item.callback
|
callback_data=item.callback
|
||||||
)
|
)
|
||||||
keyboard.append([button])
|
keyboard.append([button])
|
||||||
|
|
||||||
return InlineKeyboardMarkup(keyboard)
|
return InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -846,6 +863,7 @@ def init_menus():
|
||||||
main_menu = [
|
main_menu = [
|
||||||
MenuItem("🖥️ Выбор сервера", "server_menu", icon="🖥️"),
|
MenuItem("🖥️ Выбор сервера", "server_menu", icon="🖥️"),
|
||||||
MenuItem("📋 Предустановленные команды", "preset_menu", icon="📋"),
|
MenuItem("📋 Предустановленные команды", "preset_menu", icon="📋"),
|
||||||
|
MenuItem("💬 Чат с ИИ агентом", "toggle_ai_chat", icon="💬"),
|
||||||
MenuItem("⚙️ Настройки бота", "settings_menu", icon="⚙️"),
|
MenuItem("⚙️ Настройки бота", "settings_menu", icon="⚙️"),
|
||||||
MenuItem("ℹ️ О боте", "about", icon="ℹ️"),
|
MenuItem("ℹ️ О боте", "about", icon="ℹ️"),
|
||||||
]
|
]
|
||||||
|
|
@ -955,7 +973,7 @@ async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
f"Или выберите сервер в меню.\n"
|
f"Или выберите сервер в меню.\n"
|
||||||
f"Команда /help покажет справку.",
|
f"Команда /help покажет справку.",
|
||||||
parse_mode="Markdown",
|
parse_mode="Markdown",
|
||||||
reply_markup=menu_builder.get_keyboard("main")
|
reply_markup=menu_builder.get_keyboard("main", user_id=query.from_user.id if query else update.effective_user.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -980,7 +998,7 @@ async def menu_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
f"📁 *Директория:* `{working_dir}`\n\n"
|
f"📁 *Директория:* `{working_dir}`\n\n"
|
||||||
f"Выберите действие:",
|
f"Выберите действие:",
|
||||||
parse_mode="Markdown",
|
parse_mode="Markdown",
|
||||||
reply_markup=menu_builder.get_keyboard("main")
|
reply_markup=menu_builder.get_keyboard("main", user_id=query.from_user.id if query else update.effective_user.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1050,10 +1068,14 @@ async def menu_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
# Обработка навигации
|
# Обработка навигации
|
||||||
if callback == "main":
|
if callback == "main":
|
||||||
state.current_menu = "main"
|
state.current_menu = "main"
|
||||||
|
|
||||||
|
# Проверяем режим чата с ИИ для обновления текста кнопки
|
||||||
|
ai_status = "✅ ВКЛ" if state.ai_chat_mode else "❌ ВЫКЛ"
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
"🏠 *Главное меню*",
|
f"🏠 *Главное меню*\n\n"
|
||||||
|
f"💬 *Чат с ИИ:* {ai_status}",
|
||||||
parse_mode="Markdown",
|
parse_mode="Markdown",
|
||||||
reply_markup=menu_builder.get_keyboard("main")
|
reply_markup=menu_builder.get_keyboard("main", user_id=query.from_user.id if query else update.effective_user.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
elif callback == "preset_menu":
|
elif callback == "preset_menu":
|
||||||
|
|
@ -1299,7 +1321,7 @@ async def menu_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
f"📍 `{server.description}`\n\n"
|
f"📍 `{server.description}`\n\n"
|
||||||
f"Теперь команды выполняются на этом сервере.",
|
f"Теперь команды выполняются на этом сервере.",
|
||||||
parse_mode="Markdown",
|
parse_mode="Markdown",
|
||||||
reply_markup=menu_builder.get_keyboard("main")
|
reply_markup=menu_builder.get_keyboard("main", user_id=query.from_user.id if query else update.effective_user.id)
|
||||||
)
|
)
|
||||||
state.current_menu = "main"
|
state.current_menu = "main"
|
||||||
else:
|
else:
|
||||||
|
|
@ -1408,13 +1430,31 @@ async def menu_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
f"• Предустановленные команды\n"
|
f"• Предустановленные команды\n"
|
||||||
f"• Управление серверами\n"
|
f"• Управление серверами\n"
|
||||||
f"• Очистка ANSI-кодов и прогресс-баров\n"
|
f"• Очистка ANSI-кодов и прогресс-баров\n"
|
||||||
f"• Форматирование длинного вывода\n\n"
|
f"• Форматирование длинного вывода\n"
|
||||||
|
f"• 💬 Чат с ИИ агентом (Qwen Code)\n\n"
|
||||||
f"*Рабочая директория:*\n"
|
f"*Рабочая директория:*\n"
|
||||||
f"`{config.working_directory}`\n\n"
|
f"`{config.working_directory}`\n\n"
|
||||||
f"Бот позволяет безопасно выполнять команды\n"
|
f"Бот позволяет безопасно выполнять команды\n"
|
||||||
f"на вашем сервере через интерфейс Telegram.",
|
f"на вашем сервере через интерфейс Telegram.",
|
||||||
parse_mode="Markdown",
|
parse_mode="Markdown",
|
||||||
reply_markup=menu_builder.get_keyboard("main")
|
reply_markup=menu_builder.get_keyboard("main", user_id=query.from_user.id if query else update.effective_user.id)
|
||||||
|
)
|
||||||
|
state.current_menu = "main"
|
||||||
|
|
||||||
|
elif callback == "toggle_ai_chat":
|
||||||
|
# Переключаем режим
|
||||||
|
state.ai_chat_mode = not state.ai_chat_mode
|
||||||
|
|
||||||
|
ai_status = "✅ ВКЛЮЧЕН" if state.ai_chat_mode else "❌ ВЫКЛЮЧЕН"
|
||||||
|
action = "включён" if state.ai_chat_mode else "выключен"
|
||||||
|
|
||||||
|
await query.edit_message_text(
|
||||||
|
f"🏠 *Главное меню*\n\n"
|
||||||
|
f"💬 *Чат с ИИ:* {ai_status}\n\n"
|
||||||
|
f"Режим чата с агентом {action}.\n"
|
||||||
|
f"Теперь все сообщения будут отправляться в Qwen Code.",
|
||||||
|
parse_mode="Markdown",
|
||||||
|
reply_markup=menu_builder.get_keyboard("main", user_id=query.from_user.id if query else update.effective_user.id)
|
||||||
)
|
)
|
||||||
state.current_menu = "main"
|
state.current_menu = "main"
|
||||||
|
|
||||||
|
|
@ -1725,12 +1765,52 @@ async def handle_text_message(update: Update, context: ContextTypes.DEFAULT_TYPE
|
||||||
await handle_local_session_input(update, text, local_session)
|
await handle_local_session_input(update, text, local_session)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# ПРОВЕРКА: режим чата с ИИ агентом
|
||||||
|
if state.ai_chat_mode:
|
||||||
|
logger.info(f"Пользователь {user_id} отправил задачу ИИ: {text}")
|
||||||
|
await handle_ai_task(update, text)
|
||||||
|
return
|
||||||
|
|
||||||
# Любое текстовое сообщение = CLI команда
|
# Любое текстовое сообщение = CLI команда
|
||||||
logger.info(f"Пользователь {user_id} отправил команду: {text}")
|
logger.info(f"Пользователь {user_id} отправил команду: {text}")
|
||||||
|
|
||||||
await execute_cli_command_from_message(update, text)
|
await execute_cli_command_from_message(update, text)
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_ai_task(update: Update, text: str):
|
||||||
|
"""Обработка задачи для ИИ агента."""
|
||||||
|
user_id = update.effective_user.id
|
||||||
|
|
||||||
|
# Отправляем статус
|
||||||
|
status_msg = await update.message.reply_text("⏳ 🤖 Думаю...", parse_mode="Markdown")
|
||||||
|
|
||||||
|
output_buffer = []
|
||||||
|
|
||||||
|
def on_output(text: str):
|
||||||
|
output_buffer.append(text)
|
||||||
|
|
||||||
|
def on_oauth_url(url: str):
|
||||||
|
pass # OAuth обрабатывается автоматически
|
||||||
|
|
||||||
|
# Выполняем задачу
|
||||||
|
result = await qwen_manager.run_task(user_id, text, on_output, on_oauth_url)
|
||||||
|
|
||||||
|
# Показываем результат
|
||||||
|
full_output = "".join(output_buffer).strip()
|
||||||
|
|
||||||
|
if not full_output:
|
||||||
|
full_output = result
|
||||||
|
|
||||||
|
if len(full_output) > 4000:
|
||||||
|
full_output = full_output[:4000] + "\n... (вывод обрезан)"
|
||||||
|
|
||||||
|
await status_msg.edit_text(
|
||||||
|
f"🤖 *Результат:*\n\n"
|
||||||
|
f"```\n{full_output}\n```",
|
||||||
|
parse_mode="Markdown"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def handle_ssh_session_input(update: Update, text: str, session: SSHSession):
|
async def handle_ssh_session_input(update: Update, text: str, session: SSHSession):
|
||||||
"""Обработка ввода пользователя в активную SSH-сессию."""
|
"""Обработка ввода пользователя в активную SSH-сессию."""
|
||||||
user_id = update.effective_user.id
|
user_id = update.effective_user.id
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue