littleshop/TeleBot/TeleBot/TelegramBotService.cs
SilverLabs DevTeam 73e8773ea3 Configure BTCPay with external nodes via Tor
- Set up Tor container for SOCKS proxy (port 9050)
- Configured Monero wallet with remote onion node
- Bitcoin node continues syncing in background (60% complete)
- Created documentation for wallet configuration steps
- All external connections routed through Tor for privacy

BTCPay requires manual wallet configuration through web interface:
- Bitcoin: Need to add xpub/zpub for watch-only wallet
- Monero: Need to add address and view key

System ready for payment acceptance once wallets configured.
2025-09-19 12:14:39 +01:00

225 lines
8.7 KiB
C#

using System;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Telegram.Bot;
using Telegram.Bot.Polling;
using Telegram.Bot.Types;
using Telegram.Bot.Exceptions;
using Telegram.Bot.Types.Enums;
using TeleBot.Handlers;
using TeleBot.Services;
namespace TeleBot
{
public class TelegramBotService : IHostedService
{
private readonly IConfiguration _configuration;
private readonly ILogger<TelegramBotService> _logger;
private readonly IServiceProvider _serviceProvider;
private readonly ICommandHandler _commandHandler;
private readonly ICallbackHandler _callbackHandler;
private readonly IMessageHandler _messageHandler;
private readonly IMessageDeliveryService _messageDeliveryService;
private readonly BotManagerService _botManagerService;
private ITelegramBotClient? _botClient;
private CancellationTokenSource? _cancellationTokenSource;
private string? _currentBotToken;
public TelegramBotService(
IConfiguration configuration,
ILogger<TelegramBotService> logger,
IServiceProvider serviceProvider,
ICommandHandler commandHandler,
ICallbackHandler callbackHandler,
IMessageHandler messageHandler,
IMessageDeliveryService messageDeliveryService,
BotManagerService botManagerService)
{
_configuration = configuration;
_logger = logger;
_serviceProvider = serviceProvider;
_commandHandler = commandHandler;
_callbackHandler = callbackHandler;
_messageHandler = messageHandler;
_messageDeliveryService = messageDeliveryService;
_botManagerService = botManagerService;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
// Try to get bot token from API first via BotManagerService
var botToken = await GetBotTokenAsync();
// Fallback to configuration if API doesn't provide token
if (string.IsNullOrEmpty(botToken))
{
botToken = _configuration["Telegram:BotToken"];
}
if (string.IsNullOrEmpty(botToken) || botToken == "YOUR_BOT_TOKEN_HERE")
{
_logger.LogError("Bot token not configured. Please either register via admin panel or set Telegram:BotToken in appsettings.json");
return;
}
_currentBotToken = botToken;
_botClient = new TelegramBotClient(botToken);
_cancellationTokenSource = new CancellationTokenSource();
var receiverOptions = new ReceiverOptions
{
AllowedUpdates = Array.Empty<UpdateType>(),
ThrowPendingUpdates = true
};
_botClient.StartReceiving(
HandleUpdateAsync,
HandleErrorAsync,
receiverOptions,
cancellationToken: _cancellationTokenSource.Token
);
var me = await _botClient.GetMeAsync(cancellationToken);
_logger.LogInformation("Bot started: @{Username} ({Id})", me.Username, me.Id);
// Set bot client for message delivery service
if (_messageDeliveryService is MessageDeliveryService deliveryService)
{
deliveryService.SetBotClient(_botClient);
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_cancellationTokenSource?.Cancel();
_logger.LogInformation("Bot stopped");
return Task.CompletedTask;
}
private async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
{
try
{
if (update.Type == UpdateType.Message && update.Message != null)
{
var message = update.Message;
// Handle commands
if (message.Text != null && message.Text.StartsWith("/"))
{
var parts = message.Text.Split(' ', 2);
var command = parts[0].ToLower();
var args = parts.Length > 1 ? parts[1] : null;
await _commandHandler.HandleCommandAsync(botClient, message, command, args);
}
else
{
// Handle regular messages (for checkout flow, etc.)
await _messageHandler.HandleMessageAsync(botClient, message);
}
}
else if (update.Type == UpdateType.CallbackQuery && update.CallbackQuery != null)
{
await _callbackHandler.HandleCallbackAsync(botClient, update.CallbackQuery);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error handling update {UpdateId}", update.Id);
}
}
private Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken)
{
var errorMessage = exception switch
{
ApiRequestException apiException => $"Telegram API Error: [{apiException.ErrorCode}] {apiException.Message}",
_ => exception.ToString()
};
_logger.LogError(exception, "Bot error: {ErrorMessage}", errorMessage);
return Task.CompletedTask;
}
private async Task<string?> GetBotTokenAsync()
{
try
{
// Check if we have a bot key stored
var botKey = _configuration["BotManager:ApiKey"];
if (string.IsNullOrEmpty(botKey))
{
_logger.LogInformation("No bot key configured. Bot will need to register first or use local token.");
return null;
}
// Fetch settings from API
var settings = await _botManagerService.GetSettingsAsync();
if (settings != null && settings.ContainsKey("telegram"))
{
if (settings["telegram"] is JsonElement telegramElement)
{
var telegramSettings = telegramElement.EnumerateObject().ToDictionary(p => p.Name, p => p.Value.ToString());
if (telegramSettings.TryGetValue("botToken", out var token))
{
_logger.LogInformation("Bot token fetched from admin panel successfully");
return token;
}
}
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to fetch bot token from API. Will use local configuration.");
}
return null;
}
public async Task UpdateBotTokenAsync(string newToken)
{
if (_botClient != null && _currentBotToken != newToken)
{
_logger.LogInformation("Updating bot token and restarting bot...");
// Stop current bot
_cancellationTokenSource?.Cancel();
// Create new bot client with new token
_currentBotToken = newToken;
_botClient = new TelegramBotClient(newToken);
_cancellationTokenSource = new CancellationTokenSource();
var receiverOptions = new ReceiverOptions
{
AllowedUpdates = Array.Empty<UpdateType>(),
ThrowPendingUpdates = true
};
_botClient.StartReceiving(
HandleUpdateAsync,
HandleErrorAsync,
receiverOptions,
cancellationToken: _cancellationTokenSource.Token
);
var me = await _botClient.GetMeAsync();
_logger.LogInformation("Bot restarted with new token: @{Username} ({Id})", me.Username, me.Id);
// Update message delivery service
if (_messageDeliveryService is MessageDeliveryService deliveryService)
{
deliveryService.SetBotClient(_botClient);
}
}
}
}
}