clear
This commit is contained in:
@@ -17,8 +17,6 @@
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
|
||||
<PackageReference Include="FluentValidation" Version="11.10.0" />
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.10.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace ChatBot.Common.Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants for retry logic
|
||||
/// </summary>
|
||||
public static class RetryConstants
|
||||
{
|
||||
public const int DefaultMaxRetries = 3;
|
||||
public const int DefaultBaseDelaySeconds = 1;
|
||||
public const int DefaultMaxJitterMs = 2000;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
namespace ChatBot.Common.Results
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the result of an operation that can succeed or fail
|
||||
/// </summary>
|
||||
public class Result
|
||||
{
|
||||
public bool IsSuccess { get; }
|
||||
public string Error { get; }
|
||||
|
||||
protected Result(bool isSuccess, string error)
|
||||
{
|
||||
IsSuccess = isSuccess;
|
||||
Error = error;
|
||||
}
|
||||
|
||||
public static Result Success() => new(true, string.Empty);
|
||||
|
||||
public static Result Failure(string error) => new(false, error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the result of an operation that returns a value
|
||||
/// </summary>
|
||||
public class Result<T> : Result
|
||||
{
|
||||
public T? Value { get; }
|
||||
|
||||
private Result(T? value, bool isSuccess, string error)
|
||||
: base(isSuccess, error)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static Result<T> Success(T value) => new(value, true, string.Empty);
|
||||
|
||||
public static new Result<T> Failure(string error) => new(default, false, error);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
namespace ChatBot.Models.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Основные настройки приложения
|
||||
/// </summary>
|
||||
public class AppSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Настройки Telegram бота
|
||||
/// </summary>
|
||||
public TelegramBotSettings TelegramBot { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Настройки Ollama API
|
||||
/// </summary>
|
||||
public OllamaSettings Ollama { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Настройки логирования Serilog
|
||||
/// </summary>
|
||||
public SerilogSettings Serilog { get; set; } = new();
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
namespace ChatBot.Models.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Настройки логирования Serilog
|
||||
/// </summary>
|
||||
public class SerilogSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Список используемых sink'ов для логирования
|
||||
/// </summary>
|
||||
public List<string> Using { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Настройки минимального уровня логирования
|
||||
/// </summary>
|
||||
public MinimumLevelSettings MinimumLevel { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Настройки получателей логов (куда писать логи)
|
||||
/// </summary>
|
||||
public List<WriteToSettings> WriteTo { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Список обогатителей логов (дополнительная информация)
|
||||
/// </summary>
|
||||
public List<string> Enrich { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Настройки минимального уровня логирования
|
||||
/// </summary>
|
||||
public class MinimumLevelSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Уровень логирования по умолчанию
|
||||
/// </summary>
|
||||
public string Default { get; set; } = "Information";
|
||||
|
||||
/// <summary>
|
||||
/// Переопределения уровня логирования для конкретных пространств имен
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Override { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Настройки получателя логов
|
||||
/// </summary>
|
||||
public class WriteToSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Название sink'а для записи логов
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Аргументы для настройки sink'а
|
||||
/// </summary>
|
||||
public Dictionary<string, object> Args { get; set; } = new();
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
namespace ChatBot.Models.Configuration.Validators
|
||||
{
|
||||
/// <summary>
|
||||
/// Валидатор конфигурации приложения
|
||||
/// </summary>
|
||||
public static class ConfigurationValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Валидирует все настройки приложения
|
||||
/// </summary>
|
||||
/// <param name="settings">Настройки приложения</param>
|
||||
/// <returns>Результат валидации</returns>
|
||||
public static ValidationResult ValidateAppSettings(AppSettings settings)
|
||||
{
|
||||
var errors = new List<string>();
|
||||
|
||||
// Валидация настроек Telegram бота
|
||||
var telegramResult = ValidateTelegramBotSettings(settings.TelegramBot);
|
||||
errors.AddRange(telegramResult.Errors);
|
||||
|
||||
// Валидация настроек Ollama
|
||||
var ollamaResult = ValidateOllamaSettings(settings.Ollama);
|
||||
errors.AddRange(ollamaResult.Errors);
|
||||
|
||||
return new ValidationResult { IsValid = !errors.Any(), Errors = errors };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Валидирует настройки Telegram бота
|
||||
/// </summary>
|
||||
/// <param name="settings">Настройки Telegram бота</param>
|
||||
/// <returns>Результат валидации</returns>
|
||||
public static ValidationResult ValidateTelegramBotSettings(TelegramBotSettings settings)
|
||||
{
|
||||
var errors = new List<string>();
|
||||
|
||||
// Проверка наличия токена бота
|
||||
if (string.IsNullOrWhiteSpace(settings.BotToken))
|
||||
{
|
||||
errors.Add("TelegramBot:BotToken is required");
|
||||
}
|
||||
// Проверка формата токена (должен содержать ':' или начинаться с 'bot')
|
||||
else if (
|
||||
!settings.BotToken.StartsWith("bot", StringComparison.OrdinalIgnoreCase)
|
||||
&& !settings.BotToken.Contains(":")
|
||||
)
|
||||
{
|
||||
errors.Add(
|
||||
"TelegramBot:BotToken appears to be invalid (should contain ':' or start with 'bot')"
|
||||
);
|
||||
}
|
||||
|
||||
return new ValidationResult { IsValid = !errors.Any(), Errors = errors };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Валидирует настройки Ollama
|
||||
/// </summary>
|
||||
/// <param name="settings">Настройки Ollama</param>
|
||||
/// <returns>Результат валидации</returns>
|
||||
public static ValidationResult ValidateOllamaSettings(OllamaSettings settings)
|
||||
{
|
||||
var errors = new List<string>();
|
||||
|
||||
// Валидация основных компонентов настроек Ollama
|
||||
ValidateUrl(settings.Url, errors);
|
||||
ValidateNumericSettings(settings, errors);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(settings.DefaultModel))
|
||||
{
|
||||
errors.Add("Ollama:DefaultModel is required");
|
||||
}
|
||||
|
||||
return new ValidationResult { IsValid = !errors.Any(), Errors = errors };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Валидирует URL Ollama
|
||||
/// </summary>
|
||||
/// <param name="url">URL для проверки</param>
|
||||
/// <param name="errors">Список ошибок валидации</param>
|
||||
private static void ValidateUrl(string url, List<string> errors)
|
||||
{
|
||||
// Проверка наличия URL
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
{
|
||||
errors.Add("Ollama:Url is required");
|
||||
}
|
||||
// Проверка корректности URL (должен быть валидным HTTP/HTTPS URL)
|
||||
else if (
|
||||
!Uri.TryCreate(url, UriKind.Absolute, out var uri)
|
||||
|| (uri.Scheme != "http" && uri.Scheme != "https")
|
||||
)
|
||||
{
|
||||
errors.Add("Ollama:Url must be a valid HTTP/HTTPS URL");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Валидирует числовые параметры настроек Ollama
|
||||
/// </summary>
|
||||
/// <param name="settings">Настройки Ollama</param>
|
||||
/// <param name="errors">Список ошибок валидации</param>
|
||||
private static void ValidateNumericSettings(OllamaSettings settings, List<string> errors)
|
||||
{
|
||||
// Проверка количества повторных попыток (1-10)
|
||||
if (settings.MaxRetries < 1 || settings.MaxRetries > 10)
|
||||
{
|
||||
errors.Add("Ollama:MaxRetries must be between 1 and 10");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Результат валидации конфигурации
|
||||
/// </summary>
|
||||
public class ValidationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Указывает, прошла ли валидация успешно
|
||||
/// </summary>
|
||||
public bool IsValid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Список ошибок валидации
|
||||
/// </summary>
|
||||
public List<string> Errors { get; set; } = new();
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using ChatBot.Common.Constants;
|
||||
using ChatBot.Models.Dto;
|
||||
using FluentValidation;
|
||||
|
||||
namespace ChatBot.Models.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// Validator for ChatMessage
|
||||
/// </summary>
|
||||
public class ChatMessageValidator : AbstractValidator<ChatMessage>
|
||||
{
|
||||
public ChatMessageValidator()
|
||||
{
|
||||
RuleFor(x => x.Content)
|
||||
.NotEmpty()
|
||||
.WithMessage("Message content cannot be empty")
|
||||
.MaximumLength(10000)
|
||||
.WithMessage("Message content is too long (max 10000 characters)");
|
||||
|
||||
RuleFor(x => x.Role)
|
||||
.NotEmpty()
|
||||
.WithMessage("Message role cannot be empty")
|
||||
.Must(role =>
|
||||
role == ChatRoles.System
|
||||
|| role == ChatRoles.User
|
||||
|| role == ChatRoles.Assistant
|
||||
)
|
||||
.WithMessage(
|
||||
$"Invalid message role. Must be one of: {ChatRoles.System}, {ChatRoles.User}, {ChatRoles.Assistant}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,11 @@
|
||||
using ChatBot.Models.Configuration;
|
||||
using ChatBot.Models.Configuration.Validators;
|
||||
using ChatBot.Models.Validation;
|
||||
using ChatBot.Services;
|
||||
using ChatBot.Services.ErrorHandlers;
|
||||
using ChatBot.Services.HealthChecks;
|
||||
using ChatBot.Services.Interfaces;
|
||||
using ChatBot.Services.Telegram.Commands;
|
||||
using ChatBot.Services.Telegram.Interfaces;
|
||||
using ChatBot.Services.Telegram.Services;
|
||||
using FluentValidation;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Serilog;
|
||||
using Telegram.Bot;
|
||||
@@ -26,8 +23,6 @@ try
|
||||
builder.Services.AddSerilog();
|
||||
|
||||
// Конфигурируем настройки с валидацией
|
||||
builder.Services.Configure<AppSettings>(builder.Configuration);
|
||||
|
||||
builder
|
||||
.Services.Configure<TelegramBotSettings>(builder.Configuration.GetSection("TelegramBot"))
|
||||
.AddSingleton<IValidateOptions<TelegramBotSettings>, TelegramBotSettingsValidator>();
|
||||
@@ -36,36 +31,10 @@ try
|
||||
.Services.Configure<OllamaSettings>(builder.Configuration.GetSection("Ollama"))
|
||||
.AddSingleton<IValidateOptions<OllamaSettings>, OllamaSettingsValidator>();
|
||||
|
||||
builder.Services.Configure<SerilogSettings>(builder.Configuration.GetSection("Serilog"));
|
||||
|
||||
// Валидируем конфигурацию при старте
|
||||
builder.Services.AddOptions<TelegramBotSettings>().ValidateOnStart();
|
||||
builder.Services.AddOptions<OllamaSettings>().ValidateOnStart();
|
||||
|
||||
// Валидируем конфигурацию (старый способ для совместимости)
|
||||
var appSettings = builder.Configuration.Get<AppSettings>();
|
||||
if (appSettings == null)
|
||||
{
|
||||
Log.ForContext<Program>().Fatal("Failed to load configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
var validationResult = ConfigurationValidator.ValidateAppSettings(appSettings);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
Log.ForContext<Program>().Fatal("Configuration validation failed:");
|
||||
foreach (var error in validationResult.Errors)
|
||||
{
|
||||
Log.ForContext<Program>().Fatal(" - {Error}", error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Log.ForContext<Program>().Debug("Configuration validation passed");
|
||||
|
||||
// Регистрируем FluentValidation валидаторы
|
||||
builder.Services.AddValidatorsFromAssemblyContaining<ChatMessageValidator>();
|
||||
|
||||
// Регистрируем IOllamaClient
|
||||
builder.Services.AddSingleton<IOllamaClient>(sp =>
|
||||
{
|
||||
@@ -76,13 +45,6 @@ try
|
||||
// Регистрируем интерфейсы и сервисы
|
||||
builder.Services.AddSingleton<ISessionStorage, InMemorySessionStorage>();
|
||||
|
||||
// Регистрируем error handlers
|
||||
builder.Services.AddSingleton<IErrorHandler, RateLimitErrorHandler>();
|
||||
builder.Services.AddSingleton<IErrorHandler, NetworkErrorHandler>();
|
||||
|
||||
// Регистрируем retry policy (использует error handlers)
|
||||
builder.Services.AddSingleton<IRetryPolicy, ExponentialBackoffRetryPolicy>();
|
||||
|
||||
// Регистрируем основные сервисы
|
||||
builder.Services.AddSingleton<ModelService>();
|
||||
builder.Services.AddSingleton<IAIService, AIService>();
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
using ChatBot.Services.Interfaces;
|
||||
|
||||
namespace ChatBot.Services.ErrorHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Error handler for network-related errors
|
||||
/// </summary>
|
||||
public class NetworkErrorHandler : IErrorHandler
|
||||
{
|
||||
private readonly ILogger<NetworkErrorHandler> _logger;
|
||||
|
||||
public NetworkErrorHandler(ILogger<NetworkErrorHandler> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool CanHandle(Exception exception)
|
||||
{
|
||||
return exception is HttpRequestException
|
||||
|| exception is TaskCanceledException
|
||||
|| exception.Message.Contains("timeout", StringComparison.OrdinalIgnoreCase)
|
||||
|| exception.Message.Contains("connection", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public async Task<ErrorHandlingResult> HandleAsync(
|
||||
Exception exception,
|
||||
int attempt,
|
||||
string currentModel,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
exception,
|
||||
"Network error on attempt {Attempt} for model {Model}",
|
||||
attempt,
|
||||
currentModel
|
||||
);
|
||||
|
||||
// Apply exponential backoff for network errors
|
||||
var delay = TimeSpan.FromSeconds(Math.Pow(2, attempt - 1));
|
||||
|
||||
_logger.LogInformation("Waiting {Delay} before retry due to network error", delay);
|
||||
|
||||
await Task.Delay(delay, cancellationToken);
|
||||
|
||||
return ErrorHandlingResult.Retry();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using ChatBot.Services.Interfaces;
|
||||
|
||||
namespace ChatBot.Services.ErrorHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Error handler for rate limit errors (HTTP 429)
|
||||
/// </summary>
|
||||
public class RateLimitErrorHandler : IErrorHandler
|
||||
{
|
||||
private readonly ILogger<RateLimitErrorHandler> _logger;
|
||||
|
||||
public RateLimitErrorHandler(ILogger<RateLimitErrorHandler> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool CanHandle(Exception exception)
|
||||
{
|
||||
return exception.Message.Contains("429")
|
||||
|| exception.Message.Contains("Too Many Requests")
|
||||
|| exception.Message.Contains("rate limit", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public async Task<ErrorHandlingResult> HandleAsync(
|
||||
Exception exception,
|
||||
int attempt,
|
||||
string currentModel,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
exception,
|
||||
"Rate limit exceeded on attempt {Attempt} for model {Model}",
|
||||
attempt,
|
||||
currentModel
|
||||
);
|
||||
|
||||
// Apply exponential backoff for rate limiting
|
||||
var delay = TimeSpan.FromSeconds(Math.Pow(2, attempt - 1));
|
||||
var jitter = TimeSpan.FromMilliseconds(Random.Shared.Next(0, 2000));
|
||||
|
||||
_logger.LogInformation(
|
||||
"Rate limit hit, waiting {Delay} before retry",
|
||||
delay.Add(jitter)
|
||||
);
|
||||
|
||||
await Task.Delay(delay.Add(jitter), cancellationToken);
|
||||
|
||||
return ErrorHandlingResult.Retry();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
using ChatBot.Common.Constants;
|
||||
using ChatBot.Models.Configuration;
|
||||
using ChatBot.Services.Interfaces;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace ChatBot.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Retry policy with exponential backoff and jitter
|
||||
/// </summary>
|
||||
public class ExponentialBackoffRetryPolicy : IRetryPolicy
|
||||
{
|
||||
private readonly int _maxRetries;
|
||||
private readonly ILogger<ExponentialBackoffRetryPolicy> _logger;
|
||||
private readonly IEnumerable<IErrorHandler> _errorHandlers;
|
||||
|
||||
public ExponentialBackoffRetryPolicy(
|
||||
IOptions<OllamaSettings> settings,
|
||||
ILogger<ExponentialBackoffRetryPolicy> logger,
|
||||
IEnumerable<IErrorHandler> errorHandlers
|
||||
)
|
||||
{
|
||||
_maxRetries = settings.Value.MaxRetries;
|
||||
_logger = logger;
|
||||
_errorHandlers = errorHandlers;
|
||||
}
|
||||
|
||||
public async Task<T> ExecuteAsync<T>(
|
||||
Func<Task<T>> action,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
Exception? lastException = null;
|
||||
|
||||
for (int attempt = 1; attempt <= _maxRetries; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await action();
|
||||
}
|
||||
catch (Exception ex) when (attempt < _maxRetries)
|
||||
{
|
||||
lastException = ex;
|
||||
LogAttemptFailure(ex, attempt);
|
||||
|
||||
if (!await HandleErrorAndDecideRetry(ex, attempt, cancellationToken))
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
lastException = ex;
|
||||
_logger.LogError(ex, "All {MaxRetries} attempts failed", _maxRetries);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Failed after {_maxRetries} attempts",
|
||||
lastException
|
||||
);
|
||||
}
|
||||
|
||||
private void LogAttemptFailure(Exception ex, int attempt)
|
||||
{
|
||||
_logger.LogWarning(ex, "Attempt {Attempt}/{MaxRetries} failed", attempt, _maxRetries);
|
||||
}
|
||||
|
||||
private async Task<bool> HandleErrorAndDecideRetry(
|
||||
Exception ex,
|
||||
int attempt,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var handler = _errorHandlers.FirstOrDefault(h => h.CanHandle(ex));
|
||||
if (handler == null)
|
||||
{
|
||||
await DelayWithBackoff(attempt, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
var result = await handler.HandleAsync(ex, attempt, string.Empty, cancellationToken);
|
||||
|
||||
if (result.IsFatal)
|
||||
{
|
||||
_logger.LogError("Fatal error occurred: {ErrorMessage}", result.ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
return result.ShouldRetry;
|
||||
}
|
||||
|
||||
private async Task DelayWithBackoff(int attempt, CancellationToken cancellationToken)
|
||||
{
|
||||
var baseDelay = TimeSpan.FromSeconds(
|
||||
Math.Pow(2, attempt - 1) * RetryConstants.DefaultBaseDelaySeconds
|
||||
);
|
||||
var jitter = TimeSpan.FromMilliseconds(
|
||||
Random.Shared.Next(0, RetryConstants.DefaultMaxJitterMs)
|
||||
);
|
||||
var delay = baseDelay.Add(jitter);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Waiting {Delay} before retry {NextAttempt}/{MaxRetries}",
|
||||
delay,
|
||||
attempt + 1,
|
||||
_maxRetries
|
||||
);
|
||||
|
||||
await Task.Delay(delay, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Concurrent;
|
||||
using ChatBot.Models;
|
||||
using ChatBot.Services.Interfaces;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace ChatBot.Services
|
||||
{
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
namespace ChatBot.Services.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for error handling strategy
|
||||
/// </summary>
|
||||
public interface IErrorHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if this handler can handle the exception
|
||||
/// </summary>
|
||||
bool CanHandle(Exception exception);
|
||||
|
||||
/// <summary>
|
||||
/// Handle the exception and return result
|
||||
/// </summary>
|
||||
Task<ErrorHandlingResult> HandleAsync(
|
||||
Exception exception,
|
||||
int attempt,
|
||||
string currentModel,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of error handling
|
||||
/// </summary>
|
||||
public class ErrorHandlingResult
|
||||
{
|
||||
public bool ShouldRetry { get; set; }
|
||||
public string? NewModel { get; set; }
|
||||
public bool IsFatal { get; set; }
|
||||
public string? ErrorMessage { get; set; }
|
||||
|
||||
public static ErrorHandlingResult Retry(string? newModel = null) =>
|
||||
new() { ShouldRetry = true, NewModel = newModel };
|
||||
|
||||
public static ErrorHandlingResult Fatal(string errorMessage) =>
|
||||
new() { IsFatal = true, ErrorMessage = errorMessage };
|
||||
|
||||
public static ErrorHandlingResult NoRetry() => new() { ShouldRetry = false };
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace ChatBot.Services.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for retry policy
|
||||
/// </summary>
|
||||
public interface IRetryPolicy
|
||||
{
|
||||
/// <summary>
|
||||
/// Execute an action with retry logic
|
||||
/// </summary>
|
||||
Task<T> ExecuteAsync<T>(
|
||||
Func<Task<T>> action,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -15,24 +15,20 @@ namespace ChatBot.Services.Telegram.Services
|
||||
{
|
||||
private readonly ILogger<TelegramBotService> _logger;
|
||||
private readonly ITelegramBotClient _botClient;
|
||||
private readonly TelegramBotSettings _telegramBotSettings;
|
||||
private readonly ITelegramMessageHandler _messageHandler;
|
||||
private readonly ITelegramErrorHandler _errorHandler;
|
||||
|
||||
public TelegramBotService(
|
||||
ILogger<TelegramBotService> logger,
|
||||
IOptions<TelegramBotSettings> telegramBotSettings,
|
||||
ITelegramBotClient botClient,
|
||||
ITelegramMessageHandler messageHandler,
|
||||
ITelegramErrorHandler errorHandler
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
_telegramBotSettings = telegramBotSettings.Value;
|
||||
_botClient = botClient;
|
||||
_messageHandler = messageHandler;
|
||||
_errorHandler = errorHandler;
|
||||
|
||||
ValidateConfiguration();
|
||||
_botClient = new TelegramBotClient(_telegramBotSettings.BotToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -98,15 +94,5 @@ namespace ChatBot.Services.Telegram.Services
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateConfiguration()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_telegramBotSettings.BotToken))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Bot token is not configured. Please set TelegramBot:BotToken in appsettings.json"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user