Add customer communication system
This commit is contained in:
332
LittleShop/Areas/Admin/Controllers/BotsController.cs
Normal file
332
LittleShop/Areas/Admin/Controllers/BotsController.cs
Normal file
@@ -0,0 +1,332 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using LittleShop.DTOs;
|
||||
using LittleShop.Enums;
|
||||
using LittleShop.Services;
|
||||
|
||||
namespace LittleShop.Areas.Admin.Controllers;
|
||||
|
||||
[Area("Admin")]
|
||||
[Authorize(Policy = "AdminOnly")]
|
||||
public class BotsController : Controller
|
||||
{
|
||||
private readonly IBotService _botService;
|
||||
private readonly IBotMetricsService _metricsService;
|
||||
private readonly ITelegramBotManagerService _telegramManager;
|
||||
private readonly ILogger<BotsController> _logger;
|
||||
|
||||
public BotsController(
|
||||
IBotService botService,
|
||||
IBotMetricsService metricsService,
|
||||
ITelegramBotManagerService telegramManager,
|
||||
ILogger<BotsController> logger)
|
||||
{
|
||||
_botService = botService;
|
||||
_metricsService = metricsService;
|
||||
_telegramManager = telegramManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
// GET: Admin/Bots
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var bots = await _botService.GetAllBotsAsync();
|
||||
return View(bots);
|
||||
}
|
||||
|
||||
// GET: Admin/Bots/Details/5
|
||||
public async Task<IActionResult> Details(Guid id)
|
||||
{
|
||||
var bot = await _botService.GetBotByIdAsync(id);
|
||||
if (bot == null)
|
||||
return NotFound();
|
||||
|
||||
// Get metrics summary for the last 30 days
|
||||
var metricsSummary = await _metricsService.GetMetricsSummaryAsync(id, DateTime.UtcNow.AddDays(-30), DateTime.UtcNow);
|
||||
ViewData["MetricsSummary"] = metricsSummary;
|
||||
|
||||
// Get active sessions
|
||||
var activeSessions = await _metricsService.GetBotSessionsAsync(id, activeOnly: true);
|
||||
ViewData["ActiveSessions"] = activeSessions;
|
||||
|
||||
return View(bot);
|
||||
}
|
||||
|
||||
// GET: Admin/Bots/Create
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View(new BotRegistrationDto());
|
||||
}
|
||||
|
||||
// GET: Admin/Bots/Wizard
|
||||
public IActionResult Wizard()
|
||||
{
|
||||
return View(new BotWizardDto());
|
||||
}
|
||||
|
||||
// POST: Admin/Bots/Wizard
|
||||
[HttpPost]
|
||||
// [ValidateAntiForgeryToken] // Temporarily disabled for testing
|
||||
public async Task<IActionResult> Wizard(BotWizardDto dto)
|
||||
{
|
||||
_logger.LogInformation("Wizard POST received - BotName: '{BotName}', BotUsername: '{BotUsername}'", dto.BotName, dto.BotUsername);
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
_logger.LogWarning("Validation failed");
|
||||
foreach (var error in ModelState)
|
||||
{
|
||||
_logger.LogWarning("Field {Field}: {Errors}", error.Key, string.Join(", ", error.Value.Errors.Select(e => e.ErrorMessage)));
|
||||
}
|
||||
return View(dto);
|
||||
}
|
||||
|
||||
// Generate BotFather commands
|
||||
var commands = GenerateBotFatherCommands(dto);
|
||||
ViewData["BotFatherCommands"] = commands;
|
||||
ViewData["ShowCommands"] = true;
|
||||
|
||||
_logger.LogInformation("Generated BotFather commands successfully for bot '{BotName}'", dto.BotName);
|
||||
|
||||
return View(dto);
|
||||
}
|
||||
|
||||
// POST: Admin/Bots/CompleteWizard
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> CompleteWizard(BotWizardDto dto)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dto.BotToken))
|
||||
{
|
||||
ModelState.AddModelError("BotToken", "Bot token is required");
|
||||
ViewData["BotFatherCommands"] = GenerateBotFatherCommands(dto);
|
||||
ViewData["ShowCommands"] = true;
|
||||
return View("Wizard", dto);
|
||||
}
|
||||
|
||||
// Validate token first
|
||||
if (!await ValidateTelegramToken(dto.BotToken))
|
||||
{
|
||||
ModelState.AddModelError("BotToken", "Invalid bot token");
|
||||
ViewData["BotFatherCommands"] = GenerateBotFatherCommands(dto);
|
||||
ViewData["ShowCommands"] = true;
|
||||
return View("Wizard", dto);
|
||||
}
|
||||
|
||||
// Create the bot
|
||||
var registrationDto = new BotRegistrationDto
|
||||
{
|
||||
Name = dto.BotName,
|
||||
Description = dto.Description,
|
||||
Type = BotType.Telegram,
|
||||
Version = "1.0.0",
|
||||
PersonalityName = dto.PersonalityName,
|
||||
InitialSettings = new Dictionary<string, object>
|
||||
{
|
||||
["telegram"] = new { botToken = dto.BotToken },
|
||||
["personality"] = new { name = dto.PersonalityName }
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _botService.RegisterBotAsync(registrationDto);
|
||||
|
||||
// Add bot to Telegram manager
|
||||
var telegramAdded = await _telegramManager.AddBotAsync(result.BotId, dto.BotToken);
|
||||
|
||||
if (telegramAdded)
|
||||
{
|
||||
TempData["Success"] = $"Bot '{result.Name}' created successfully and is now running on Telegram!";
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["Warning"] = $"Bot '{result.Name}' created but failed to connect to Telegram. Check token.";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Details), new { id = result.BotId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create bot");
|
||||
ModelState.AddModelError("", $"Failed to create bot: {ex.Message}");
|
||||
return View("Wizard", dto);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: Admin/Bots/Create
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create(BotRegistrationDto dto)
|
||||
{
|
||||
_logger.LogInformation("Received bot registration: Name={Name}, Type={Type}, Version={Version}",
|
||||
dto?.Name, dto?.Type, dto?.Version);
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
_logger.LogWarning("Model validation failed for bot registration");
|
||||
foreach (var error in ModelState.Values.SelectMany(v => v.Errors))
|
||||
{
|
||||
_logger.LogWarning("Validation error: {Error}", error.ErrorMessage);
|
||||
}
|
||||
return View(dto);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _botService.RegisterBotAsync(dto);
|
||||
_logger.LogInformation("Bot registered successfully: {BotId}, Key: {KeyPrefix}...",
|
||||
result.BotId, result.BotKey.Substring(0, 8));
|
||||
|
||||
TempData["BotKey"] = result.BotKey;
|
||||
TempData["Success"] = $"Bot '{result.Name}' created successfully. Save the API key securely!";
|
||||
return RedirectToAction(nameof(Details), new { id = result.BotId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create bot");
|
||||
ModelState.AddModelError("", $"Failed to create bot: {ex.Message}");
|
||||
return View(dto);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: Admin/Bots/Edit/5
|
||||
public async Task<IActionResult> Edit(Guid id)
|
||||
{
|
||||
var bot = await _botService.GetBotByIdAsync(id);
|
||||
if (bot == null)
|
||||
return NotFound();
|
||||
|
||||
ViewData["BotSettings"] = JsonSerializer.Serialize(bot.Settings, new JsonSerializerOptions { WriteIndented = true });
|
||||
return View(bot);
|
||||
}
|
||||
|
||||
// POST: Admin/Bots/Edit/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Edit(Guid id, string settingsJson, BotStatus status)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Parse and update settings
|
||||
var settings = JsonSerializer.Deserialize<Dictionary<string, object>>(settingsJson) ?? new Dictionary<string, object>();
|
||||
var updateDto = new UpdateBotSettingsDto { Settings = settings };
|
||||
|
||||
await _botService.UpdateBotSettingsAsync(id, updateDto);
|
||||
await _botService.UpdateBotStatusAsync(id, status);
|
||||
|
||||
TempData["Success"] = "Bot updated successfully";
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to update bot");
|
||||
TempData["Error"] = "Failed to update bot";
|
||||
return RedirectToAction(nameof(Edit), new { id });
|
||||
}
|
||||
}
|
||||
|
||||
// GET: Admin/Bots/Metrics/5
|
||||
public async Task<IActionResult> Metrics(Guid id, DateTime? startDate, DateTime? endDate)
|
||||
{
|
||||
var bot = await _botService.GetBotByIdAsync(id);
|
||||
if (bot == null)
|
||||
return NotFound();
|
||||
|
||||
var start = startDate ?? DateTime.UtcNow.AddDays(-7);
|
||||
var end = endDate ?? DateTime.UtcNow;
|
||||
|
||||
var metricsSummary = await _metricsService.GetMetricsSummaryAsync(id, start, end);
|
||||
var sessionSummary = await _metricsService.GetSessionSummaryAsync(id, start, end);
|
||||
|
||||
ViewData["Bot"] = bot;
|
||||
ViewData["SessionSummary"] = sessionSummary;
|
||||
ViewData["StartDate"] = start;
|
||||
ViewData["EndDate"] = end;
|
||||
|
||||
return View(metricsSummary);
|
||||
}
|
||||
|
||||
// POST: Admin/Bots/Delete/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
{
|
||||
var success = await _botService.DeleteBotAsync(id);
|
||||
if (!success)
|
||||
{
|
||||
TempData["Error"] = "Failed to delete bot";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
TempData["Success"] = "Bot deleted successfully";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
// POST: Admin/Bots/Suspend/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Suspend(Guid id)
|
||||
{
|
||||
await _botService.UpdateBotStatusAsync(id, BotStatus.Suspended);
|
||||
TempData["Success"] = "Bot suspended";
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
|
||||
// POST: Admin/Bots/Activate/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Activate(Guid id)
|
||||
{
|
||||
await _botService.UpdateBotStatusAsync(id, BotStatus.Active);
|
||||
TempData["Success"] = "Bot activated";
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
|
||||
// GET: Admin/Bots/RegenerateKey/5
|
||||
public async Task<IActionResult> RegenerateKey(Guid id)
|
||||
{
|
||||
// This would require updating the bot model to support key regeneration
|
||||
TempData["Error"] = "Key regeneration not yet implemented";
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
|
||||
private string GenerateBotFatherCommands(BotWizardDto dto)
|
||||
{
|
||||
var commands = new List<string>
|
||||
{
|
||||
"1. Open Telegram and find @BotFather",
|
||||
"2. Send: /newbot",
|
||||
$"3. Send bot name: {dto.BotName}",
|
||||
$"4. Send bot username: {dto.BotUsername}",
|
||||
"5. Copy the token from BotFather's response",
|
||||
"6. Paste the token in the field below"
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(dto.Description))
|
||||
{
|
||||
commands.Add($"7. Optional: Send /setdescription and then: {dto.Description}");
|
||||
}
|
||||
|
||||
return string.Join("\n", commands);
|
||||
}
|
||||
|
||||
private async Task<bool> ValidateTelegramToken(string token)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var httpClient = new HttpClient();
|
||||
var response = await httpClient.GetAsync($"https://api.telegram.org/bot{token}/getMe");
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user