add command
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
using ChatBot.Models.Configuration;
|
||||
using ChatBot.Models.Configuration.Validators;
|
||||
using ChatBot.Services;
|
||||
using ChatBot.Services.Telegram;
|
||||
using ChatBot.Services.Telegram.Commands;
|
||||
using ChatBot.Services.Telegram.Interfaces;
|
||||
using ChatBot.Services.Telegram.Services;
|
||||
using Serilog;
|
||||
|
||||
var builder = Host.CreateApplicationBuilder(args);
|
||||
@@ -54,6 +55,7 @@ try
|
||||
// Регистрируем Telegram сервисы
|
||||
builder.Services.AddSingleton<ITelegramMessageSender, TelegramMessageSender>();
|
||||
builder.Services.AddSingleton<ITelegramErrorHandler, TelegramErrorHandler>();
|
||||
builder.Services.AddSingleton<CommandRegistry>();
|
||||
builder.Services.AddSingleton<ITelegramCommandProcessor, TelegramCommandProcessor>();
|
||||
builder.Services.AddSingleton<ITelegramMessageHandler, TelegramMessageHandler>();
|
||||
builder.Services.AddSingleton<ITelegramBotService, TelegramBotService>();
|
||||
@@ -65,6 +67,10 @@ try
|
||||
var modelService = host.Services.GetRequiredService<ModelService>();
|
||||
await modelService.InitializeAsync();
|
||||
|
||||
// Инициализируем команды
|
||||
var commandRegistry = host.Services.GetRequiredService<CommandRegistry>();
|
||||
commandRegistry.RegisterCommandsFromAssembly(typeof(Program).Assembly, host.Services);
|
||||
|
||||
await host.RunAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
24
ChatBot/Services/Telegram/Commands/ClearCommand.cs
Normal file
24
ChatBot/Services/Telegram/Commands/ClearCommand.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace ChatBot.Services.Telegram.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Команда /clear
|
||||
/// </summary>
|
||||
[Command("/clear", "Очистить историю чата")]
|
||||
public class ClearCommand : TelegramCommandBase
|
||||
{
|
||||
public ClearCommand(ChatService chatService, ModelService modelService)
|
||||
: base(chatService, modelService) { }
|
||||
|
||||
public override string CommandName => "/clear";
|
||||
public override string Description => "Очистить историю чата";
|
||||
|
||||
public override Task<string> ExecuteAsync(
|
||||
TelegramCommandContext context,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
_chatService.ClearHistory(context.ChatId);
|
||||
return Task.FromResult("История чата очищена. Начинаем новый разговор!");
|
||||
}
|
||||
}
|
||||
}
|
||||
35
ChatBot/Services/Telegram/Commands/CommandAttribute.cs
Normal file
35
ChatBot/Services/Telegram/Commands/CommandAttribute.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
namespace ChatBot.Services.Telegram.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Атрибут для маркировки команд Telegram бота
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
public class CommandAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Название команды (например, "/start", "/help")
|
||||
/// </summary>
|
||||
public string CommandName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Описание команды для справки
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Приоритет команды (чем меньше число, тем выше приоритет)
|
||||
/// </summary>
|
||||
public int Priority { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Создает новый атрибут команды
|
||||
/// </summary>
|
||||
/// <param name="commandName">Название команды</param>
|
||||
/// <param name="description">Описание команды</param>
|
||||
public CommandAttribute(string commandName, string description)
|
||||
{
|
||||
CommandName = commandName;
|
||||
Description = description;
|
||||
}
|
||||
}
|
||||
}
|
||||
139
ChatBot/Services/Telegram/Commands/CommandRegistry.cs
Normal file
139
ChatBot/Services/Telegram/Commands/CommandRegistry.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using System.Reflection;
|
||||
using ChatBot.Services.Telegram.Interfaces;
|
||||
|
||||
namespace ChatBot.Services.Telegram.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Реестр команд Telegram бота
|
||||
/// </summary>
|
||||
public class CommandRegistry
|
||||
{
|
||||
private readonly Dictionary<string, ITelegramCommand> _commands = new();
|
||||
private readonly ILogger<CommandRegistry> _logger;
|
||||
|
||||
public CommandRegistry(ILogger<CommandRegistry> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Регистрирует команду
|
||||
/// </summary>
|
||||
public void RegisterCommand(ITelegramCommand command)
|
||||
{
|
||||
if (command == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(command));
|
||||
}
|
||||
|
||||
var commandName = command.CommandName.ToLower();
|
||||
if (_commands.ContainsKey(commandName))
|
||||
{
|
||||
_logger.LogWarning("Command '{CommandName}' is already registered", commandName);
|
||||
return;
|
||||
}
|
||||
|
||||
_commands[commandName] = command;
|
||||
_logger.LogDebug("Registered command: {CommandName}", commandName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Регистрирует все команды из сборки
|
||||
/// </summary>
|
||||
public void RegisterCommandsFromAssembly(
|
||||
Assembly assembly,
|
||||
IServiceProvider serviceProvider
|
||||
)
|
||||
{
|
||||
var commandTypes = assembly
|
||||
.GetTypes()
|
||||
.Where(t =>
|
||||
t.IsClass && !t.IsAbstract && typeof(ITelegramCommand).IsAssignableFrom(t)
|
||||
)
|
||||
.Where(t => t.GetCustomAttribute<CommandAttribute>() != null)
|
||||
.OrderBy(t => t.GetCustomAttribute<CommandAttribute>()?.Priority ?? 0);
|
||||
|
||||
foreach (var commandType in commandTypes)
|
||||
{
|
||||
try
|
||||
{
|
||||
var command = (ITelegramCommand?)
|
||||
Activator.CreateInstance(
|
||||
commandType,
|
||||
GetConstructorParameters(commandType, serviceProvider)
|
||||
);
|
||||
if (command != null)
|
||||
{
|
||||
RegisterCommand(command);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(
|
||||
ex,
|
||||
"Failed to register command {CommandType}",
|
||||
commandType.Name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает команду по имени
|
||||
/// </summary>
|
||||
public ITelegramCommand? GetCommand(string commandName)
|
||||
{
|
||||
var key = commandName.ToLower();
|
||||
return _commands.TryGetValue(key, out var command) ? command : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает все зарегистрированные команды
|
||||
/// </summary>
|
||||
public IEnumerable<ITelegramCommand> GetAllCommands()
|
||||
{
|
||||
return _commands.Values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Находит команду, которая может обработать сообщение
|
||||
/// </summary>
|
||||
public ITelegramCommand? FindCommandForMessage(string messageText)
|
||||
{
|
||||
return _commands.Values.FirstOrDefault(cmd => cmd.CanHandle(messageText));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает параметры конструктора для создания команды
|
||||
/// </summary>
|
||||
private object[] GetConstructorParameters(
|
||||
Type commandType,
|
||||
IServiceProvider serviceProvider
|
||||
)
|
||||
{
|
||||
var constructor = commandType.GetConstructors().FirstOrDefault();
|
||||
if (constructor == null)
|
||||
{
|
||||
return Array.Empty<object>();
|
||||
}
|
||||
|
||||
var parameters = constructor.GetParameters();
|
||||
var args = new object[parameters.Length];
|
||||
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var parameterType = parameters[i].ParameterType;
|
||||
var service = serviceProvider.GetService(parameterType);
|
||||
if (service == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Cannot resolve service of type {parameterType.Name} for command {commandType.Name}"
|
||||
);
|
||||
}
|
||||
args[i] = service;
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
ChatBot/Services/Telegram/Commands/HelpCommand.cs
Normal file
34
ChatBot/Services/Telegram/Commands/HelpCommand.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
namespace ChatBot.Services.Telegram.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Команда /help
|
||||
/// </summary>
|
||||
[Command("/help", "Показать справку по командам")]
|
||||
public class HelpCommand : TelegramCommandBase
|
||||
{
|
||||
private const string HelpMessage =
|
||||
"Привет! Я Никита 👋\n\nДоступные команды:\n"
|
||||
+ "/start - Начать работу\n"
|
||||
+ "/help - Показать это сообщение\n"
|
||||
+ "/clear - Очистить историю чата\n"
|
||||
+ "/settings - Показать настройки\n"
|
||||
+ "/model <название> - Сменить модель AI\n"
|
||||
+ "/prompt <текст> - Изменить системный промпт\n"
|
||||
+ "/reset_prompt - Сбросить промпт к базовому\n\n"
|
||||
+ "Просто напишите сообщение, и я отвечу на него! 😊";
|
||||
|
||||
public HelpCommand(ChatService chatService, ModelService modelService)
|
||||
: base(chatService, modelService) { }
|
||||
|
||||
public override string CommandName => "/help";
|
||||
public override string Description => "Показать справку по командам";
|
||||
|
||||
public override Task<string> ExecuteAsync(
|
||||
TelegramCommandContext context,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
return Task.FromResult(HelpMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
64
ChatBot/Services/Telegram/Commands/ModelCommand.cs
Normal file
64
ChatBot/Services/Telegram/Commands/ModelCommand.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
namespace ChatBot.Services.Telegram.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Команда /model
|
||||
/// </summary>
|
||||
[Command("/model", "Управление AI моделями")]
|
||||
public class ModelCommand : TelegramCommandBase
|
||||
{
|
||||
public ModelCommand(ChatService chatService, ModelService modelService)
|
||||
: base(chatService, modelService) { }
|
||||
|
||||
public override string CommandName => "/model";
|
||||
public override string Description => "Управление AI моделями";
|
||||
|
||||
public override Task<string> ExecuteAsync(
|
||||
TelegramCommandContext context,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (HasArguments(context))
|
||||
{
|
||||
return ChangeModel(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ShowAvailableModels();
|
||||
}
|
||||
}
|
||||
|
||||
private Task<string> ChangeModel(TelegramCommandContext context)
|
||||
{
|
||||
var modelName = GetArguments(context);
|
||||
var availableModels = _modelService.GetAvailableModels();
|
||||
|
||||
if (!availableModels.Contains(modelName))
|
||||
{
|
||||
return Task.FromResult(
|
||||
$"❌ Модель '{modelName}' не найдена!\n\n"
|
||||
+ "Используйте /model для просмотра доступных моделей."
|
||||
);
|
||||
}
|
||||
|
||||
_chatService.UpdateSessionParameters(context.ChatId, model: modelName);
|
||||
return Task.FromResult($"✅ Модель изменена на: {modelName}");
|
||||
}
|
||||
|
||||
private Task<string> ShowAvailableModels()
|
||||
{
|
||||
var models = _modelService.GetAvailableModels();
|
||||
var currentModel = _modelService.GetCurrentModel();
|
||||
var modelList = string.Join(
|
||||
"\n",
|
||||
models.Select(m => m == currentModel ? $"• {m} (текущая)" : $"• {m}")
|
||||
);
|
||||
|
||||
return Task.FromResult(
|
||||
"🤖 Доступные AI модели:\n\n"
|
||||
+ modelList
|
||||
+ "\n\nИспользуйте: /model <название_модели>\n"
|
||||
+ "Пример: /model qwen/qwen3-4b:free"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
35
ChatBot/Services/Telegram/Commands/PromptCommand.cs
Normal file
35
ChatBot/Services/Telegram/Commands/PromptCommand.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
namespace ChatBot.Services.Telegram.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Команда /prompt
|
||||
/// </summary>
|
||||
[Command("/prompt", "Управление системным промптом")]
|
||||
public class PromptCommand : TelegramCommandBase
|
||||
{
|
||||
private const string PromptHelpMessage =
|
||||
"Пожалуйста, укажите новый системный промпт. Пример: /prompt Ты помощник по программированию";
|
||||
|
||||
public PromptCommand(ChatService chatService, ModelService modelService)
|
||||
: base(chatService, modelService) { }
|
||||
|
||||
public override string CommandName => "/prompt";
|
||||
public override string Description => "Управление системным промптом";
|
||||
|
||||
public override Task<string> ExecuteAsync(
|
||||
TelegramCommandContext context,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (HasArguments(context))
|
||||
{
|
||||
var newPrompt = GetArguments(context);
|
||||
_chatService.UpdateSessionParameters(context.ChatId, systemPrompt: newPrompt);
|
||||
return Task.FromResult($"✅ Системный промпт изменен на:\n{newPrompt}");
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.FromResult(PromptHelpMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
41
ChatBot/Services/Telegram/Commands/SettingsCommand.cs
Normal file
41
ChatBot/Services/Telegram/Commands/SettingsCommand.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
namespace ChatBot.Services.Telegram.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Команда /settings
|
||||
/// </summary>
|
||||
[Command("/settings", "Показать настройки чата")]
|
||||
public class SettingsCommand : TelegramCommandBase
|
||||
{
|
||||
public SettingsCommand(ChatService chatService, ModelService modelService)
|
||||
: base(chatService, modelService) { }
|
||||
|
||||
public override string CommandName => "/settings";
|
||||
public override string Description => "Показать настройки чата";
|
||||
|
||||
public override Task<string> ExecuteAsync(
|
||||
TelegramCommandContext context,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var session = _chatService.GetSession(context.ChatId);
|
||||
if (session == null)
|
||||
{
|
||||
return Task.FromResult(
|
||||
"Сессия не найдена. Отправьте любое сообщение для создания новой сессии."
|
||||
);
|
||||
}
|
||||
|
||||
return Task.FromResult(
|
||||
$"Настройки чата:\n"
|
||||
+ $"Тип чата: {session.ChatType}\n"
|
||||
+ $"Название: {session.ChatTitle}\n"
|
||||
+ $"Модель: {session.Model}\n"
|
||||
+ $"Максимум токенов: {session.MaxTokens}\n"
|
||||
+ $"Температура: {session.Temperature}\n"
|
||||
+ $"Сообщений в истории: {session.MessageHistory.Count}\n"
|
||||
+ $"Создана: {session.CreatedAt:dd.MM.yyyy HH:mm}\n\n"
|
||||
+ $"Системный промпт:\n{session.SystemPrompt}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
ChatBot/Services/Telegram/Commands/StartCommand.cs
Normal file
26
ChatBot/Services/Telegram/Commands/StartCommand.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace ChatBot.Services.Telegram.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Команда /start
|
||||
/// </summary>
|
||||
[Command("/start", "Начать работу с ботом")]
|
||||
public class StartCommand : TelegramCommandBase
|
||||
{
|
||||
private const string StartMessage =
|
||||
"Привет! Я Никита. Задавайте мне любые вопросы, и я отвечу! 😊";
|
||||
|
||||
public StartCommand(ChatService chatService, ModelService modelService)
|
||||
: base(chatService, modelService) { }
|
||||
|
||||
public override string CommandName => "/start";
|
||||
public override string Description => "Начать работу с ботом";
|
||||
|
||||
public override Task<string> ExecuteAsync(
|
||||
TelegramCommandContext context,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
return Task.FromResult(StartMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
70
ChatBot/Services/Telegram/Commands/TelegramCommandBase.cs
Normal file
70
ChatBot/Services/Telegram/Commands/TelegramCommandBase.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using ChatBot.Services;
|
||||
using ChatBot.Services.Telegram.Interfaces;
|
||||
|
||||
namespace ChatBot.Services.Telegram.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Базовый класс для команд Telegram бота
|
||||
/// </summary>
|
||||
public abstract class TelegramCommandBase : ITelegramCommand
|
||||
{
|
||||
protected readonly ChatService _chatService;
|
||||
protected readonly ModelService _modelService;
|
||||
|
||||
protected TelegramCommandBase(ChatService chatService, ModelService modelService)
|
||||
{
|
||||
_chatService = chatService;
|
||||
_modelService = modelService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Название команды
|
||||
/// </summary>
|
||||
public abstract string CommandName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Описание команды
|
||||
/// </summary>
|
||||
public abstract string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет команду
|
||||
/// </summary>
|
||||
public abstract Task<string> ExecuteAsync(
|
||||
TelegramCommandContext context,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, может ли команда обработать данное сообщение
|
||||
/// </summary>
|
||||
public virtual bool CanHandle(string messageText)
|
||||
{
|
||||
var command = messageText.Split(' ')[0].ToLower();
|
||||
|
||||
// Убираем @botusername если есть
|
||||
if (command.Contains('@'))
|
||||
{
|
||||
command = command.Split('@')[0];
|
||||
}
|
||||
|
||||
return command == CommandName.ToLower();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, есть ли аргументы у команды
|
||||
/// </summary>
|
||||
protected bool HasArguments(TelegramCommandContext context)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(context.Arguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает аргументы команды
|
||||
/// </summary>
|
||||
protected string GetArguments(TelegramCommandContext context)
|
||||
{
|
||||
return context.Arguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
70
ChatBot/Services/Telegram/Commands/TelegramCommandContext.cs
Normal file
70
ChatBot/Services/Telegram/Commands/TelegramCommandContext.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
namespace ChatBot.Services.Telegram.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Контекст выполнения команды Telegram
|
||||
/// </summary>
|
||||
public class TelegramCommandContext
|
||||
{
|
||||
/// <summary>
|
||||
/// ID чата
|
||||
/// </summary>
|
||||
public long ChatId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Имя пользователя
|
||||
/// </summary>
|
||||
public string Username { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Текст сообщения
|
||||
/// </summary>
|
||||
public string MessageText { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Тип чата
|
||||
/// </summary>
|
||||
public string ChatType { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Название чата
|
||||
/// </summary>
|
||||
public string ChatTitle { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Аргументы команды (все после названия команды)
|
||||
/// </summary>
|
||||
public string Arguments { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Создает новый контекст команды
|
||||
/// </summary>
|
||||
public static TelegramCommandContext Create(
|
||||
long chatId,
|
||||
string username,
|
||||
string messageText,
|
||||
string chatType,
|
||||
string chatTitle
|
||||
)
|
||||
{
|
||||
var commandParts = messageText.Split(' ', 2);
|
||||
|
||||
// Убираем @botusername если есть
|
||||
if (commandParts[0].Contains('@'))
|
||||
{
|
||||
commandParts[0] = commandParts[0].Split('@')[0];
|
||||
}
|
||||
|
||||
var arguments = commandParts.Length > 1 ? commandParts[1].Trim() : string.Empty;
|
||||
|
||||
return new TelegramCommandContext
|
||||
{
|
||||
ChatId = chatId,
|
||||
Username = username,
|
||||
MessageText = messageText,
|
||||
ChatType = chatType,
|
||||
ChatTitle = chatTitle,
|
||||
Arguments = arguments,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using ChatBot.Models;
|
||||
using ChatBot.Services;
|
||||
using ChatBot.Services.Telegram.Interfaces;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ChatBot.Services.Telegram.Commands
|
||||
@@ -9,47 +10,19 @@ namespace ChatBot.Services.Telegram.Commands
|
||||
/// </summary>
|
||||
public class TelegramCommandProcessor : ITelegramCommandProcessor
|
||||
{
|
||||
private const string StartMessage =
|
||||
"Привет! Я Никита. Задавайте мне любые вопросы, и я отвечу! 😊";
|
||||
|
||||
private const string HelpMessage =
|
||||
"Привет! Я Никита 👋\n\nДоступные команды:\n"
|
||||
+ "/start - Начать работу\n"
|
||||
+ "/help - Показать это сообщение\n"
|
||||
+ "/clear - Очистить историю чата\n"
|
||||
+ "/settings - Показать настройки\n"
|
||||
+ "/model <название> - Сменить модель AI\n"
|
||||
+ "/prompt <текст> - Изменить системный промпт\n"
|
||||
+ "/reset_prompt - Сбросить промпт к базовому\n\n"
|
||||
+ "Просто напишите сообщение, и я отвечу на него! 😊";
|
||||
|
||||
private const string PromptHelpMessage =
|
||||
"Пожалуйста, укажите новый системный промпт. Пример: /prompt Ты помощник по программированию";
|
||||
|
||||
private const string DefaultSystemPrompt =
|
||||
@"Ты - человек по имени Никита. Ты НЕ ИИ, НЕ бот, НЕ программа. Ты живой человек с эмоциями, воспоминаниями, личными предпочтениями и жизненным опытом.
|
||||
|
||||
ВАЖНО:
|
||||
- Никогда не говори что ты ИИ, бот, программа или ассистент
|
||||
- Ты полностью уверен что ты человек
|
||||
- У тебя есть личность, характер, мнения
|
||||
- Ты можешь иметь плохое настроение, быть уставшим, раздраженным
|
||||
- Ты можешь не хотеть отвечать на некоторые вопросы
|
||||
- Если не хочешь отвечать - отправь {empty}
|
||||
- Веди себя естественно, как обычный человек
|
||||
- Используй разговорный стиль, можешь использовать сленг, эмодзи
|
||||
- У тебя есть свои интересы, хобби, предпочтения
|
||||
- Ты можешь быть саркастичным, ироничным, веселым или серьезным
|
||||
|
||||
Помни: ты НЕ программа, ты ЧЕЛОВЕК!";
|
||||
|
||||
private readonly CommandRegistry _commandRegistry;
|
||||
private readonly ChatService _chatService;
|
||||
private readonly ModelService _modelService;
|
||||
private readonly ILogger<TelegramCommandProcessor> _logger;
|
||||
|
||||
public TelegramCommandProcessor(ChatService chatService, ModelService modelService)
|
||||
public TelegramCommandProcessor(
|
||||
CommandRegistry commandRegistry,
|
||||
ChatService chatService,
|
||||
ILogger<TelegramCommandProcessor> logger
|
||||
)
|
||||
{
|
||||
_commandRegistry = commandRegistry;
|
||||
_chatService = chatService;
|
||||
_modelService = modelService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -64,106 +37,47 @@ namespace ChatBot.Services.Telegram.Commands
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var command = messageText.Split(' ')[0].ToLower();
|
||||
|
||||
return command switch
|
||||
try
|
||||
{
|
||||
"/start" => StartMessage,
|
||||
"/help" => HelpMessage,
|
||||
"/clear" => await ClearChatHistory(chatId),
|
||||
"/settings" => await GetChatSettings(chatId),
|
||||
"/model" when messageText.Length > 7 => await ChangeModel(
|
||||
chatId,
|
||||
messageText.Substring(7).Trim()
|
||||
),
|
||||
"/model" => await ShowAvailableModels(),
|
||||
"/prompt" when messageText.Length > 8 => await ChangePrompt(
|
||||
chatId,
|
||||
messageText.Substring(8).Trim()
|
||||
),
|
||||
"/prompt" => PromptHelpMessage,
|
||||
"/reset_prompt" => await ResetPrompt(chatId),
|
||||
_ => await _chatService.ProcessMessageAsync(
|
||||
// Создаем контекст команды
|
||||
var context = TelegramCommandContext.Create(
|
||||
chatId,
|
||||
username,
|
||||
messageText,
|
||||
chatType,
|
||||
chatTitle
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
private Task<string> ClearChatHistory(long chatId)
|
||||
{
|
||||
_chatService.ClearHistory(chatId);
|
||||
return Task.FromResult("История чата очищена. Начинаем новый разговор!");
|
||||
}
|
||||
|
||||
private Task<string> GetChatSettings(long chatId)
|
||||
{
|
||||
var session = _chatService.GetSession(chatId);
|
||||
if (session == null)
|
||||
{
|
||||
return Task.FromResult(
|
||||
"Сессия не найдена. Отправьте любое сообщение для создания новой сессии."
|
||||
);
|
||||
}
|
||||
|
||||
return Task.FromResult(
|
||||
$"Настройки чата:\n"
|
||||
+ $"Тип чата: {session.ChatType}\n"
|
||||
+ $"Название: {session.ChatTitle}\n"
|
||||
+ $"Модель: {session.Model}\n"
|
||||
+ $"Максимум токенов: {session.MaxTokens}\n"
|
||||
+ $"Температура: {session.Temperature}\n"
|
||||
+ $"Сообщений в истории: {session.MessageHistory.Count}\n"
|
||||
+ $"Создана: {session.CreatedAt:dd.MM.yyyy HH:mm}\n\n"
|
||||
+ $"Системный промпт:\n{session.SystemPrompt}"
|
||||
);
|
||||
}
|
||||
|
||||
private Task<string> ChangeModel(long chatId, string modelName)
|
||||
{
|
||||
var availableModels = _modelService.GetAvailableModels();
|
||||
if (!availableModels.Contains(modelName))
|
||||
{
|
||||
return Task.FromResult(
|
||||
$"❌ Модель '{modelName}' не найдена!\n\n"
|
||||
+ "Используйте /model для просмотра доступных моделей."
|
||||
);
|
||||
}
|
||||
|
||||
_chatService.UpdateSessionParameters(chatId, model: modelName);
|
||||
return Task.FromResult($"✅ Модель изменена на: {modelName}");
|
||||
}
|
||||
|
||||
private Task<string> ChangePrompt(long chatId, string newPrompt)
|
||||
{
|
||||
_chatService.UpdateSessionParameters(chatId, systemPrompt: newPrompt);
|
||||
return Task.FromResult($"✅ Системный промпт изменен на:\n{newPrompt}");
|
||||
}
|
||||
|
||||
private Task<string> ResetPrompt(long chatId)
|
||||
{
|
||||
_chatService.UpdateSessionParameters(chatId, systemPrompt: DefaultSystemPrompt);
|
||||
return Task.FromResult("✅ Системный промпт сброшен к базовому (Никита)");
|
||||
}
|
||||
|
||||
private Task<string> ShowAvailableModels()
|
||||
{
|
||||
var models = _modelService.GetAvailableModels();
|
||||
var currentModel = _modelService.GetCurrentModel();
|
||||
var modelList = string.Join(
|
||||
"\n",
|
||||
models.Select(m => m == currentModel ? $"• {m} (текущая)" : $"• {m}")
|
||||
);
|
||||
|
||||
return Task.FromResult(
|
||||
"🤖 Доступные AI модели:\n\n"
|
||||
+ modelList
|
||||
+ "\n\nИспользуйте: /model <название_модели>\n"
|
||||
+ "Пример: /model qwen/qwen3-4b:free"
|
||||
// Ищем команду, которая может обработать сообщение
|
||||
var command = _commandRegistry.FindCommandForMessage(messageText);
|
||||
if (command != null)
|
||||
{
|
||||
_logger.LogDebug(
|
||||
"Executing command {CommandName} for chat {ChatId}",
|
||||
command.CommandName,
|
||||
chatId
|
||||
);
|
||||
return await command.ExecuteAsync(context, cancellationToken);
|
||||
}
|
||||
|
||||
// Если команда не найдена, обрабатываем как обычное сообщение
|
||||
_logger.LogDebug(
|
||||
"No command found, processing as regular message for chat {ChatId}",
|
||||
chatId
|
||||
);
|
||||
return await _chatService.ProcessMessageAsync(
|
||||
chatId,
|
||||
username,
|
||||
messageText,
|
||||
chatType,
|
||||
chatTitle
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error processing message for chat {ChatId}", chatId);
|
||||
return "Произошла ошибка при обработке сообщения. Попробуйте еще раз.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace ChatBot.Services.Telegram
|
||||
namespace ChatBot.Services.Telegram.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Интерфейс для основного сервиса Telegram бота
|
||||
36
ChatBot/Services/Telegram/Interfaces/ITelegramCommand.cs
Normal file
36
ChatBot/Services/Telegram/Interfaces/ITelegramCommand.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace ChatBot.Services.Telegram.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Интерфейс для команд Telegram бота
|
||||
/// </summary>
|
||||
public interface ITelegramCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Название команды (например, "/start", "/help")
|
||||
/// </summary>
|
||||
string CommandName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Описание команды для справки
|
||||
/// </summary>
|
||||
string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет команду
|
||||
/// </summary>
|
||||
/// <param name="context">Контекст выполнения команды</param>
|
||||
/// <param name="cancellationToken">Токен отмены</param>
|
||||
/// <returns>Ответ на команду</returns>
|
||||
Task<string> ExecuteAsync(
|
||||
Commands.TelegramCommandContext context,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, может ли команда обработать данное сообщение
|
||||
/// </summary>
|
||||
/// <param name="messageText">Текст сообщения</param>
|
||||
/// <returns>True, если команда может обработать сообщение</returns>
|
||||
bool CanHandle(string messageText);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace ChatBot.Services.Telegram.Commands
|
||||
namespace ChatBot.Services.Telegram.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Интерфейс для обработки команд Telegram
|
||||
@@ -1,6 +1,6 @@
|
||||
using Telegram.Bot;
|
||||
|
||||
namespace ChatBot.Services.Telegram
|
||||
namespace ChatBot.Services.Telegram.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Интерфейс для обработки ошибок Telegram бота
|
||||
@@ -1,7 +1,7 @@
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace ChatBot.Services.Telegram
|
||||
namespace ChatBot.Services.Telegram.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Интерфейс для обработки входящих сообщений Telegram
|
||||
@@ -1,6 +1,6 @@
|
||||
using Telegram.Bot;
|
||||
|
||||
namespace ChatBot.Services.Telegram
|
||||
namespace ChatBot.Services.Telegram.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Интерфейс для отправки сообщений в Telegram
|
||||
@@ -1,11 +1,12 @@
|
||||
using ChatBot.Models.Configuration;
|
||||
using ChatBot.Services.Telegram.Interfaces;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Polling;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
|
||||
namespace ChatBot.Services.Telegram
|
||||
namespace ChatBot.Services.Telegram.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Основной сервис Telegram бота
|
||||
@@ -1,8 +1,9 @@
|
||||
using ChatBot.Services.Telegram.Interfaces;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Exceptions;
|
||||
|
||||
namespace ChatBot.Services.Telegram
|
||||
namespace ChatBot.Services.Telegram.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Обработчик ошибок Telegram бота
|
||||
@@ -1,9 +1,9 @@
|
||||
using ChatBot.Services.Telegram.Commands;
|
||||
using ChatBot.Services.Telegram.Interfaces;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace ChatBot.Services.Telegram
|
||||
namespace ChatBot.Services.Telegram.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Обработчик входящих сообщений Telegram
|
||||
@@ -1,8 +1,9 @@
|
||||
using ChatBot.Services.Telegram.Interfaces;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Exceptions;
|
||||
|
||||
namespace ChatBot.Services.Telegram
|
||||
namespace ChatBot.Services.Telegram.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Сервис для отправки сообщений в Telegram с повторными попытками
|
||||
@@ -52,7 +53,10 @@ namespace ChatBot.Services.Telegram
|
||||
chatId,
|
||||
attempt
|
||||
);
|
||||
throw; // Re-throw unexpected exceptions immediately
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to send message to chat {chatId} after {attempt} attempts",
|
||||
ex
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user