Add channel system with CLI, Telegram, and Web channel implementations
Define channel abstractions (IChannel, IChannelManager) with event-driven message and connection handling. Implement ChannelManager for multi-channel orchestration, CliChannel for terminal I/O, TelegramChannel with adapter pattern for bot API integration, and WebChannel stub. Includes DI registration and channel type enumeration.
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
using Luna.Channels.Abstractions;
|
||||
using Luna.Configuration;
|
||||
using Luna.Shared;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Polling;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace Luna.Channels.Telegram;
|
||||
|
||||
public class TelegramAdapter(
|
||||
IChannelManager channelManager,
|
||||
ITelegramBotClient botClient,
|
||||
TelegramOptions options,
|
||||
ILogger<TelegramAdapter> adapterLogger,
|
||||
ILogger<TelegramChannel> channelLogger) : IHostedService, IUpdateHandler
|
||||
{
|
||||
private readonly Dictionary<long, TelegramChannel> channels = new();
|
||||
private CancellationTokenSource? cts;
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cts = new CancellationTokenSource();
|
||||
botClient.StartReceiving(this, new ReceiverOptions(), cts.Token);
|
||||
adapterLogger.LogInformation("Telegram adapter started polling");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cts?.Cancel();
|
||||
cts?.Dispose();
|
||||
|
||||
foreach (var (_, channel) in channels)
|
||||
{
|
||||
channelManager.UnregisterChannel(channel.ChannelId);
|
||||
channel.Dispose();
|
||||
}
|
||||
|
||||
channels.Clear();
|
||||
adapterLogger.LogInformation("Telegram adapter stopped");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task HandleUpdateAsync(ITelegramBotClient client, Update update, CancellationToken cancellationToken)
|
||||
{
|
||||
if (update.Message is not { } message)
|
||||
return;
|
||||
|
||||
if (message.Text is null or "")
|
||||
return;
|
||||
|
||||
var chat = message.Chat;
|
||||
var channel = GetOrCreateChannel(chat.Id);
|
||||
|
||||
if (message.Text.StartsWith('/'))
|
||||
{
|
||||
await HandleCommandAsync(channel, message, cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
var channelMessage = new ChannelMessage(
|
||||
MessageId: message.Id.ToString(),
|
||||
ConversationId: chat.Id.ToString(),
|
||||
SenderId: message.From?.Id.ToString() ?? "unknown",
|
||||
SenderName: message.From?.FirstName ?? "Unknown",
|
||||
Content: message.Text,
|
||||
Timestamp: new DateTimeOffset(message.Date, TimeSpan.Zero));
|
||||
|
||||
channel.RaiseMessageReceived(channelMessage);
|
||||
}
|
||||
|
||||
public Task HandleErrorAsync(ITelegramBotClient client, Exception exception, HandleErrorSource source,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
adapterLogger.LogError(exception, "Telegram polling error from {Source}", source);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private TelegramChannel GetOrCreateChannel(long chatId)
|
||||
{
|
||||
if (channels.TryGetValue(chatId, out var existing))
|
||||
return existing;
|
||||
|
||||
var channel = new TelegramChannel(
|
||||
chatId.ToString(),
|
||||
botClient,
|
||||
channelLogger,
|
||||
options);
|
||||
|
||||
channels[chatId] = channel;
|
||||
channelManager.RegisterChannel(channel);
|
||||
adapterLogger.LogInformation("Created TelegramChannel for chat {ChatId}", chatId);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
private async Task HandleCommandAsync(TelegramChannel channel, Message message, CancellationToken cancellationToken)
|
||||
{
|
||||
var command = message.Text!.Split(' ')[0].ToLowerInvariant();
|
||||
|
||||
var responseText = command switch
|
||||
{
|
||||
"/start" => "Welcome to Luna AI Assistant! Send me a message and I'll do my best to help.",
|
||||
"/help" => "Luna AI Assistant\n\nCommands:\n/start - Start the bot\n/help - Show this help message\n\nJust send a message to chat with Luna!",
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (responseText is null)
|
||||
return;
|
||||
|
||||
var responseMessage = new ChannelMessage(
|
||||
MessageId: Guid.NewGuid().ToString(),
|
||||
ConversationId: message.Chat.Id.ToString(),
|
||||
SenderId: "luna",
|
||||
SenderName: "Luna",
|
||||
Content: responseText,
|
||||
Timestamp: DateTimeOffset.UtcNow);
|
||||
|
||||
await channel.SendMessageAsync(responseMessage, cancellationToken);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user