Initial commit: Telegram CLI Bot with AI memory, tools and multi-provider support

This commit is contained in:
mirivlad
2026-03-16 03:47:15 +08:00
commit 6b8bd8d7a6
69 changed files with 20857 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env python3
"""Клавиатуры бота."""
from bot.keyboards.menus import MenuItem, MenuBuilder, CommandRegistry, init_menus
__all__ = [
"MenuItem",
"MenuBuilder",
"CommandRegistry",
"init_menus",
]
+242
View File
@@ -0,0 +1,242 @@
#!/usr/bin/env python3
"""Построитель многоуровневого меню."""
import logging
from typing import Dict, List, Optional, Callable
from dataclasses import dataclass, field
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
# Импортируем модели и утилиты
from bot.models.user_state import UserState
logger = logging.getLogger(__name__)
@dataclass
class MenuItem:
"""Элемент меню."""
label: str
callback: str # callback_data для кнопки
description: str = ""
icon: str = ""
children: List["MenuItem"] = field(default_factory=list)
command: Optional[str] = None # CLI команда для выполнения
is_command: bool = False
class MenuBuilder:
"""Построитель InlineKeyboard для меню."""
def __init__(self):
self._menus: Dict[str, List[MenuItem]] = {}
def add_menu(self, menu_name: str, items: List[MenuItem]):
self._menus[menu_name] = items
def get_menu(self, menu_name: str) -> List[MenuItem]:
return self._menus.get(menu_name, [])
def get_keyboard(self, menu_name: str, user_id: int = None, state: UserState = None) -> InlineKeyboardMarkup:
"""Создает InlineKeyboard для меню."""
items = self._menus.get(menu_name, [])
keyboard = []
# Для главного меню — динамически меняем кнопку ИИ
if menu_name == "main" and state:
# Используем переданное состояние
logger.info(f"get_keyboard: user_id={user_id}, ai_chat_mode={state.ai_chat_mode}")
for item in items:
# Проверяем базовый callback и его варианты с _on/_off
is_ai_toggle = item.callback in ["toggle_ai_chat", "toggle_ai_chat_on", "toggle_ai_chat_off"]
if is_ai_toggle:
# Меняем текст кнопки и callback_data в зависимости от статуса
if state.ai_chat_mode:
label = f"✅ Выключить чат с ИИ"
callback = "toggle_ai_chat_off"
else:
label = f"❌ Включить чат с ИИ"
callback = "toggle_ai_chat_on"
logger.info(f"get_keyboard: label={label}, callback={callback}")
button = InlineKeyboardButton(label, callback_data=callback)
else:
button = InlineKeyboardButton(item.label, callback_data=item.callback)
keyboard.append([button])
else:
for item in items:
button = InlineKeyboardButton(
item.label,
callback_data=item.callback
)
keyboard.append([button])
return InlineKeyboardMarkup(keyboard)
class CommandRegistry:
"""Реестр команд для легкого добавления."""
def __init__(self):
self._commands: Dict[str, Callable] = {}
def register(self, name: str):
"""Декоратор для регистрации команды."""
def decorator(func: Callable):
self._commands[name] = func
return func
return decorator
def get(self, name: str) -> Optional[Callable]:
return self._commands.get(name)
def list_commands(self) -> List[str]:
return list(self._commands.keys())
def init_menus(menu_builder: MenuBuilder):
"""Инициализация структуры меню."""
# Главное меню
main_menu = [
MenuItem("🖥️ Выбор сервера", "server_menu", icon="🖥️"),
MenuItem("📋 Предустановленные команды", "preset_menu", icon="📋"),
MenuItem("🎛️ AI-пресеты", "ai_presets", icon="🎛️"),
MenuItem("💬 Чат с ИИ агентом", "toggle_ai_chat", icon="💬"),
MenuItem("⚙️ Настройки бота", "settings_menu", icon="⚙️"),
MenuItem("ℹ️ О боте", "about", icon="ℹ️"),
]
menu_builder.add_menu("main", main_menu)
# Меню серверов
server_menu = [
MenuItem("💻 local (localhost)", "server_select_local", icon="💻"),
MenuItem("➕ Добавить сервер", "server_add", icon="➕"),
MenuItem("⬅️ Назад", "main", icon="⬅️"),
]
menu_builder.add_menu("server", server_menu)
# Меню предустановленных команд
preset_menu = [
MenuItem("📁 Файловая система", "fs_menu", icon="📁"),
MenuItem("🔍 Поиск", "search_menu", icon="🔍"),
MenuItem("📊 Система", "system_menu", icon="📊"),
MenuItem("🌐 Сеть", "network_menu", icon="🌐"),
MenuItem("🤖 AI модели Opencode", "opencode_models_menu", icon="🤖"),
MenuItem("⬅️ Назад", "main", icon="⬅️"),
]
menu_builder.add_menu("preset", preset_menu)
# Меню моделей Opencode
opencode_models_menu = [
MenuItem("⚡ minimax (по умолчанию)", "opencode_model_minimax", icon="⚡"),
MenuItem("🗃️ big-pickle", "opencode_model_big_pickle", icon="🗃️"),
MenuItem("🔬 gpt-5-nano", "opencode_model_gpt5", icon="🔬"),
MenuItem("⬅️ Назад", "preset", icon="⬅️"),
]
menu_builder.add_menu("opencode_models", opencode_models_menu)
# Файловая система
fs_menu = [
MenuItem("ls -la", "cmd_ls_la", command="ls -la", icon="📄"),
MenuItem("pwd", "cmd_pwd", command="pwd", icon="📍"),
MenuItem("df -h", "cmd_df", command="df -h", icon="💾"),
MenuItem("du -sh *", "cmd_du", command="du -sh * 2>/dev/null | sort -hr | head -20", icon="📊"),
MenuItem("⬅️ Назад", "preset", icon="⬅️"),
]
menu_builder.add_menu("fs", fs_menu)
# Поиск
search_menu = [
MenuItem("find . -name", "cmd_find_name", command="find . -maxdepth 3 -name '*.txt' 2>/dev/null", icon="🔎"),
MenuItem("grep пример", "cmd_grep", command="grep -r 'example' . 2>/dev/null | head -20", icon="🔍"),
MenuItem("which command", "cmd_which", command="which python3 bash git", icon="📍"),
MenuItem("⬅️ Назад", "preset", icon="⬅️"),
]
menu_builder.add_menu("search", search_menu)
# Система
system_menu = [
MenuItem("top -n 1", "cmd_top", command="top -bn1 | head -20", icon="📈"),
MenuItem("ps aux", "cmd_ps", command="ps aux | head -20", icon="🔄"),
MenuItem("free -h", "cmd_free", command="free -h", icon="💾"),
MenuItem("uname -a", "cmd_uname", command="uname -a", icon="ℹ️"),
MenuItem("uptime", "cmd_uptime", command="uptime", icon="⏱️"),
MenuItem("⬅️ Назад", "preset", icon="⬅️"),
]
menu_builder.add_menu("system", system_menu)
# Сеть
network_menu = [
MenuItem("ip addr", "cmd_ip", command="ip addr 2>/dev/null || ifconfig 2>/dev/null", icon="🌐"),
MenuItem("ping google", "cmd_ping", command="ping -c 4 google.com 2>&1 | head -10", icon="📡"),
MenuItem("netstat", "cmd_netstat", command="ss -tuln 2>/dev/null || netstat -tuln 2>/dev/null | head -20", icon="🔌"),
MenuItem("curl ifconfig.me", "cmd_curl_ip", command="curl -s ifconfig.me 2>&1", icon="📍"),
MenuItem("⬅️ Назад", "preset", icon="⬅️"),
]
menu_builder.add_menu("network", network_menu)
# Настройки
settings_menu = [
MenuItem("📝 Изменить имя бота", "set_name", icon="📝"),
MenuItem("📄 Изменить описание", "set_description", icon="📄"),
MenuItem("🎨 Изменить иконку", "set_icon", icon="🎨"),
MenuItem("🧠 Память ИИ", "memory_menu", icon="🧠"),
MenuItem("🤖 Выбор AI-провайдера", "ai_provider_selection_menu", icon="🤖"),
MenuItem("⬅️ Назад", "main", icon="⬅️"),
]
menu_builder.add_menu("settings", settings_menu)
# Меню выбора AI-провайдера
ai_provider_selection_menu = [
MenuItem("🔄 Qwen Code", "ai_provider_qwen", icon="🔄"),
MenuItem("📡 Opencode ▶", "opencode_model_menu", icon="📡"),
MenuItem("🧠 GigaChat ▶", "gigachat_model_menu", icon="🧠"),
MenuItem("⬅️ Назад", "settings", icon="⬅️"),
]
menu_builder.add_menu("ai_provider_selection", ai_provider_selection_menu)
# Модели Opencode
opencode_model_menu = [
MenuItem("⚡ minimax (по умолчанию)", "opencode_model_minimax", icon="⚡"),
MenuItem("🗃️ big-pickle", "opencode_model_big_pickle", icon="🗃️"),
MenuItem("🔬 gpt-5-nano", "opencode_model_gpt5", icon="🔬"),
MenuItem("⬅️ Назад", "ai_provider_selection", icon="⬅️"),
]
menu_builder.add_menu("opencode_model", opencode_model_menu)
# Модели GigaChat
gigachat_model_menu = [
MenuItem("📱 GigaChat Lite (по умолчанию)", "gigachat_model_lite", icon="📱"),
MenuItem("🚀 GigaChat Pro", "gigachat_model_pro", icon="🚀"),
MenuItem("💎 GigaChat Max", "gigachat_model_max", icon="💎"),
MenuItem("⬅️ Назад", "ai_provider_selection", icon="⬅️"),
]
menu_builder.add_menu("gigachat_model", gigachat_model_menu)
# Меню AI-провайдера
ai_provider_menu = [
MenuItem("🔄 Переключить AI-провайдер", "ai_provider_toggle", icon="🔄"),
MenuItem("ℹ️ Информация о провайдерах", "ai_provider_info", icon="ℹ️"),
MenuItem("⬅️ Назад", "settings", icon="⬅️"),
]
menu_builder.add_menu("ai_provider", ai_provider_menu)
# Память ИИ
memory_menu = [
MenuItem("📋 Мой профиль", "memory_profile", icon="📋"),
MenuItem("📊 Статистика", "memory_stats", icon="📊"),
MenuItem("🗑️ Очистить историю", "memory_clear", icon="🗑️"),
MenuItem("🔄 Компактификация", "memory_compact", icon="🔄"),
MenuItem("⬅️ Назад", "settings", icon="⬅️"),
]
menu_builder.add_menu("memory", memory_menu)
# Доступ
access_menu = [
MenuItem("📋 Показать разрешённых", "show_access", icon="📋"),
MenuItem("➕ Добавить пользователя", "add_access", icon="➕"),
MenuItem("➖ Удалить пользователя", "remove_access", icon="➖"),
MenuItem("⬅️ Назад", "settings", icon="⬅️"),
]
menu_builder.add_menu("access", access_menu)