* Added Logging abstraction for base library
* Version incremented
This commit is contained in:
@@ -12,6 +12,8 @@ using Telegrator.Configuration;
|
||||
using Telegrator.Hosting.Components;
|
||||
using Telegrator.Hosting.Providers;
|
||||
using Telegrator.Hosting.Web.Components;
|
||||
using Telegrator.Hosting.Logging;
|
||||
using Telegrator.Logging;
|
||||
using Telegrator.MadiatorCore;
|
||||
using Telegrator.MadiatorCore.Descriptors;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<Version>1.0.9</Version>
|
||||
<Version>1.0.10</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
using Telegrator.Logging;
|
||||
|
||||
namespace Telegrator.Hosting.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Adapter for Microsoft.Extensions.Logging to work with Telegrator logging system.
|
||||
/// This allows seamless integration with ASP.NET Core logging infrastructure.
|
||||
/// </summary>
|
||||
public class MicrosoftLoggingAdapter : ITelegratorLogger
|
||||
{
|
||||
private readonly Microsoft.Extensions.Logging.ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of MicrosoftLoggingAdapter.
|
||||
/// </summary>
|
||||
/// <param name="logger">The Microsoft.Extensions.Logging logger instance.</param>
|
||||
public MicrosoftLoggingAdapter(Microsoft.Extensions.Logging.ILogger logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Log(LogLevel level, string message, Exception? exception = null)
|
||||
{
|
||||
var msLogLevel = level switch
|
||||
{
|
||||
LogLevel.Trace => Microsoft.Extensions.Logging.LogLevel.Trace,
|
||||
LogLevel.Debug => Microsoft.Extensions.Logging.LogLevel.Debug,
|
||||
LogLevel.Information => Microsoft.Extensions.Logging.LogLevel.Information,
|
||||
LogLevel.Warning => Microsoft.Extensions.Logging.LogLevel.Warning,
|
||||
LogLevel.Error => Microsoft.Extensions.Logging.LogLevel.Error,
|
||||
_ => Microsoft.Extensions.Logging.LogLevel.Information
|
||||
};
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
_logger.Log(msLogLevel, default, exception, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(msLogLevel, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ using System.Text;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegrator.Configuration;
|
||||
using Telegrator.Hosting.Components;
|
||||
using Telegrator.Hosting.Logging;
|
||||
using Telegrator.Logging;
|
||||
using Telegrator.Hosting.Providers;
|
||||
using Telegrator.MadiatorCore;
|
||||
using Telegrator.MadiatorCore.Descriptors;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<Version>1.0.9</Version>
|
||||
<Version>1.0.10</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
@@ -9,8 +10,10 @@ using Telegram.Bot.Types;
|
||||
using Telegrator.Configuration;
|
||||
using Telegrator.Hosting.Components;
|
||||
using Telegrator.Hosting.Configuration;
|
||||
using Telegrator.Hosting.Logging;
|
||||
using Telegrator.Hosting.Polling;
|
||||
using Telegrator.Hosting.Providers;
|
||||
using Telegrator.Logging;
|
||||
using Telegrator.MadiatorCore;
|
||||
|
||||
namespace Telegrator.Hosting
|
||||
@@ -92,6 +95,18 @@ namespace Telegrator.Hosting
|
||||
client.SetMyCommands(aliases).Wait();
|
||||
return botHost;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Microsoft.Extensions.Logging adapter to Alligator using a logger factory.
|
||||
/// </summary>
|
||||
/// <param name="host"></param>
|
||||
public static void AddLoggingAdapter(this ITelegramBotHost host)
|
||||
{
|
||||
ILoggerFactory loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();
|
||||
ILogger logger = loggerFactory.CreateLogger("Telegrator");
|
||||
MicrosoftLoggingAdapter adapter = new MicrosoftLoggingAdapter(logger);
|
||||
Alligator.AddAdapter(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Telegrator
|
||||
{
|
||||
/// <summary>
|
||||
/// Telegrator's FUNNY debug logger helper
|
||||
/// </summary>
|
||||
public static class Alligator
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets flags of what trace messages to write
|
||||
/// </summary>
|
||||
public static DebugLevel Allowed { get; set; } = DebugLevel.None;
|
||||
|
||||
/// <summary>
|
||||
/// Writes trace message if Indent level has Router flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void RouterWriteLine(string message)
|
||||
{
|
||||
if (Allowed.HasFlag(DebugLevel.Router))
|
||||
Trace.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes debug message if Indent level has Router flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void RouterWriteLine(string message, params object[] args)
|
||||
{
|
||||
if (Allowed.HasFlag(DebugLevel.Router))
|
||||
Trace.WriteLine(string.Format(message, args));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes trace message if Indent level has Providers flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void ProviderWriteLine(string message)
|
||||
{
|
||||
if (Allowed.HasFlag(DebugLevel.Providers))
|
||||
Trace.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes trace message if Indent level has Providers flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void ProviderWriteLine(string message, params object[] args)
|
||||
{
|
||||
if (Allowed.HasFlag(DebugLevel.Providers))
|
||||
Trace.WriteLine(string.Format(message, args));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes trace message if Indent level has Filters flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void FilterWriteLine(string message)
|
||||
{
|
||||
if (Allowed.HasFlag(DebugLevel.Filters))
|
||||
Trace.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes trace message if Indent level has Filters flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void FilterWriteLine(string message, params object[] args)
|
||||
{
|
||||
if (Allowed.HasFlag(DebugLevel.Filters))
|
||||
Trace.WriteLine(string.Format(message, args));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes trace message if Indent level has Pool flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void PoolWriteLine(string message)
|
||||
{
|
||||
if (Allowed.HasFlag(DebugLevel.HandlersPool))
|
||||
Trace.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes trace message if Indent level has Pool flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void PoolWriteLine(string message, params object[] args)
|
||||
{
|
||||
if (Allowed.HasFlag(DebugLevel.HandlersPool))
|
||||
Trace.WriteLine(string.Format(message, args));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes trace message if flag was set
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <param name="message"></param>
|
||||
public static void WriteLine(DebugLevel level, string message)
|
||||
{
|
||||
if (Allowed.HasFlag(level))
|
||||
Trace.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes trace message if flag was set
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void WriteLine(DebugLevel level, string message, params object[] args)
|
||||
{
|
||||
if (Allowed.HasFlag(level))
|
||||
Trace.WriteLine(string.Format(message, args));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
namespace Telegrator.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Centralized logging system for Telegrator.
|
||||
/// Provides static access to logging functionality with adapter support.
|
||||
/// </summary>
|
||||
public static class Alligator
|
||||
{
|
||||
private static readonly List<ITelegratorLogger> _adapters = new();
|
||||
private static readonly object _lock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current adapters count.
|
||||
/// </summary>
|
||||
public static int AdaptersCount => _adapters.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a logger adapter to the centralized logging system.
|
||||
/// </summary>
|
||||
/// <param name="adapter">The logger adapter to add.</param>
|
||||
public static void AddAdapter(ITelegratorLogger adapter)
|
||||
{
|
||||
if (adapter == null)
|
||||
throw new ArgumentNullException(nameof(adapter));
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_adapters.Contains(adapter))
|
||||
{
|
||||
_adapters.Add(adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a logger adapter from the centralized logging system.
|
||||
/// </summary>
|
||||
/// <param name="adapter">The logger adapter to remove.</param>
|
||||
public static void RemoveAdapter(ITelegratorLogger adapter)
|
||||
{
|
||||
if (adapter == null)
|
||||
return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_adapters.Remove(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all logger adapters.
|
||||
/// </summary>
|
||||
public static void ClearAdapters()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_adapters.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a message to all registered adapters.
|
||||
/// </summary>
|
||||
/// <param name="level">The log level.</param>
|
||||
/// <param name="message">The message to log.</param>
|
||||
/// <param name="exception">Optional exception.</param>
|
||||
public static void Log(LogLevel level, string message, Exception? exception = null)
|
||||
{
|
||||
// Fast path: if no adapters, do nothing
|
||||
if (_adapters.Count == 0)
|
||||
return;
|
||||
|
||||
// Lock only during enumeration to prevent collection modification during iteration
|
||||
lock (_lock)
|
||||
{
|
||||
foreach (var adapter in _adapters)
|
||||
{
|
||||
try
|
||||
{
|
||||
adapter.Log(level, message, exception);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_ = 0xBAD + 0xC0DE; // Ignore adapter errors to prevent logging failures
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a trace message to all registered adapters.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log.</param>
|
||||
public static void LogTrace(string message)
|
||||
{
|
||||
Log(LogLevel.Trace, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a debug message to all registered adapters.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log.</param>
|
||||
public static void LogDebug(string message)
|
||||
{
|
||||
Log(LogLevel.Debug, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an information message to all registered adapters.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log.</param>
|
||||
public static void LogInformation(string message)
|
||||
{
|
||||
Log(LogLevel.Information, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message to all registered adapters.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log.</param>
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
Log(LogLevel.Warning, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message to all registered adapters.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log.</param>
|
||||
/// <param name="exception">Optional exception.</param>
|
||||
public static void LogError(string message, Exception? exception = null)
|
||||
{
|
||||
Log(LogLevel.Error, message, exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message with exception only to all registered adapters.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to log.</param>
|
||||
public static void LogError(Exception exception)
|
||||
{
|
||||
Log(LogLevel.Error, exception.Message, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
|
||||
namespace Telegrator.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Console logger implementation that writes to System.Console.
|
||||
/// This logger is optional and can be used for simple console output.
|
||||
/// </summary>
|
||||
public class ConsoleLogger : ITelegratorLogger
|
||||
{
|
||||
private readonly LogLevel _minimumLevel;
|
||||
private readonly bool _includeTimestamp;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of ConsoleLogger.
|
||||
/// </summary>
|
||||
/// <param name="minimumLevel">Minimum log level to output. Default is Information.</param>
|
||||
/// <param name="includeTimestamp">Whether to include timestamp in log messages. Default is true.</param>
|
||||
public ConsoleLogger(LogLevel minimumLevel = LogLevel.Information, bool includeTimestamp = true)
|
||||
{
|
||||
_minimumLevel = minimumLevel;
|
||||
_includeTimestamp = includeTimestamp;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Log(LogLevel level, string message, Exception? exception = null)
|
||||
{
|
||||
if (level < _minimumLevel)
|
||||
return;
|
||||
|
||||
var timestamp = _includeTimestamp ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] " : "";
|
||||
var levelStr = $"[{level.ToString().ToUpper()}] ";
|
||||
var logMessage = $"{timestamp}{levelStr}{message}";
|
||||
|
||||
// Add exception if present
|
||||
if (exception != null)
|
||||
{
|
||||
logMessage += $" | Exception: {exception.Message}";
|
||||
}
|
||||
|
||||
// Write to console with appropriate color
|
||||
var originalColor = Console.ForegroundColor;
|
||||
try
|
||||
{
|
||||
Console.ForegroundColor = level switch
|
||||
{
|
||||
LogLevel.Trace => ConsoleColor.Gray,
|
||||
LogLevel.Debug => ConsoleColor.Cyan,
|
||||
LogLevel.Information => ConsoleColor.White,
|
||||
LogLevel.Warning => ConsoleColor.Yellow,
|
||||
LogLevel.Error => ConsoleColor.Red,
|
||||
_ => ConsoleColor.White
|
||||
};
|
||||
|
||||
Console.WriteLine(logMessage);
|
||||
|
||||
// Write exception details if present
|
||||
if (exception != null)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"Exception Details: {exception}");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.ForegroundColor = originalColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace Telegrator.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for Telegrator logging system.
|
||||
/// Provides abstraction for logging without external dependencies.
|
||||
/// </summary>
|
||||
public interface ITelegratorLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Logs a message with specified level.
|
||||
/// </summary>
|
||||
/// <param name="level">The log level.</param>
|
||||
/// <param name="message">The message to log.</param>
|
||||
/// <param name="exception">Optional exception.</param>
|
||||
void Log(LogLevel level, string message, Exception? exception = null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log levels for Telegrator logging system.
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Trace level - most detailed logging.
|
||||
/// </summary>
|
||||
Trace = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Debug level - detailed debugging information.
|
||||
/// </summary>
|
||||
Debug = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Information level - general information.
|
||||
/// </summary>
|
||||
Information = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Warning level - warning messages.
|
||||
/// </summary>
|
||||
Warning = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Error level - error messages.
|
||||
/// </summary>
|
||||
Error = 4
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace Telegrator.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Null logger implementation that does nothing.
|
||||
/// Used when logging is not required or disabled.
|
||||
/// </summary>
|
||||
public class NullLogger : ITelegratorLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Singleton instance of NullLogger.
|
||||
/// </summary>
|
||||
public static readonly NullLogger Instance = new();
|
||||
|
||||
private NullLogger() { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Log(LogLevel level, string message, Exception? exception = null) { }
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<Version>1.0.9</Version>
|
||||
<Version>1.0.10</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Polling;
|
||||
using Telegrator.Configuration;
|
||||
using Telegrator.Logging;
|
||||
using Telegrator.MadiatorCore;
|
||||
using Telegrator.Polling;
|
||||
using Telegrator.Providers;
|
||||
@@ -37,7 +38,7 @@ namespace Telegrator
|
||||
/// <param name="httpClient">Optional HTTP client for making requests.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public TelegratorClient(string token, HttpClient? httpClient = null, CancellationToken cancellationToken = default)
|
||||
: this(new TelegramBotClientOptions(token), httpClient, cancellationToken) { }
|
||||
: this(new TelegramBotClientOptions(token), null, httpClient, cancellationToken) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TelegratorClient"/> class with bot options.
|
||||
@@ -45,9 +46,19 @@ namespace Telegrator
|
||||
/// <param name="options">The Telegram bot client options.</param>
|
||||
/// <param name="httpClient">Optional HTTP client for making requests.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public TelegratorClient(TelegramBotClientOptions options, HttpClient? httpClient = null, CancellationToken cancellationToken = default) : base(options, httpClient, cancellationToken)
|
||||
public TelegratorClient(TelegramBotClientOptions options, HttpClient? httpClient = null, CancellationToken cancellationToken = default)
|
||||
: this(options, null, httpClient, cancellationToken) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TelegratorClient"/> class with bot options and Telegrator options.
|
||||
/// </summary>
|
||||
/// <param name="options">The Telegram bot client options.</param>
|
||||
/// <param name="telegratorOptions">The Telegrator options.</param>
|
||||
/// <param name="httpClient">Optional HTTP client for making requests.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public TelegratorClient(TelegramBotClientOptions options, TelegratorOptions? telegratorOptions, HttpClient? httpClient = null, CancellationToken cancellationToken = default) : base(options, httpClient, cancellationToken)
|
||||
{
|
||||
Options = new TelegratorOptions();
|
||||
Options = telegratorOptions ?? new TelegratorOptions();
|
||||
Handlers = new HandlersCollection(default);
|
||||
BotInfo = new TelegramBotInfo(this.GetMe(cancellationToken).Result);
|
||||
}
|
||||
@@ -67,6 +78,10 @@ namespace Telegrator
|
||||
AwaitingProvider awaitingProvider = new AwaitingProvider(Options);
|
||||
|
||||
updateRouter = new UpdateRouter(handlerProvider, awaitingProvider, Options, BotInfo);
|
||||
|
||||
// Log startup
|
||||
Alligator.LogInformation($"Telegrator bot starting up - BotId: {BotInfo.Id}, Username: {BotInfo.Username}, MaxParallelHandlers: {Options.MaximumParallelWorkingHandlers ?? -1}");
|
||||
|
||||
StartReceivingInternal(receiverOptions, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -96,7 +111,10 @@ namespace Telegrator
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Cancelled
|
||||
Alligator.LogInformation("Telegrator bot stopped (cancelled)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user