Add bot activity tracking system

This commit is contained in:
SysAdmin 2025-09-25 01:28:56 +01:00
parent d6f8a5e697
commit ac4fe688d9
3 changed files with 306 additions and 2 deletions

View File

@ -0,0 +1,132 @@
using System;
using System.Threading.Tasks;
using LittleShop.Data;
using LittleShop.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace LittleShop.Controllers
{
[ApiController]
[Route("api/bot")]
public class BotController : ControllerBase
{
private readonly LittleShopContext _context;
private readonly ILogger<BotController> _logger;
public BotController(LittleShopContext context, ILogger<BotController> logger)
{
_context = context;
_logger = logger;
}
[HttpPost("activity")]
public async Task<IActionResult> TrackActivity([FromBody] BotActivityDto activity)
{
try
{
// Get the default bot ID (TeleBot) - we can enhance this to support multiple bots later
var bot = await _context.Bots.FirstOrDefaultAsync(b => b.Type == Enums.BotType.Telegram);
if (bot == null)
{
// Create a default TeleBot entry if not exists
bot = new Bot
{
Id = Guid.NewGuid(),
BotKey = "telebot-default",
Name = "TeleBot",
Type = Enums.BotType.Telegram,
Status = Enums.BotStatus.Active,
IsActive = true,
LastSeenAt = DateTime.UtcNow,
CreatedAt = DateTime.UtcNow,
PlatformUsername = "telebot",
PlatformDisplayName = "TeleBot",
Version = "1.0.0"
};
_context.Bots.Add(bot);
await _context.SaveChangesAsync();
}
var botActivity = new BotActivity
{
Id = Guid.NewGuid(),
BotId = bot.Id,
SessionIdentifier = activity.SessionIdentifier,
UserDisplayName = activity.UserDisplayName,
ActivityType = activity.ActivityType,
ActivityDescription = activity.ActivityDescription,
ProductId = activity.ProductId,
ProductName = activity.ProductName ?? string.Empty,
CategoryName = activity.CategoryName ?? string.Empty,
Value = activity.Value,
Quantity = activity.Quantity,
Platform = activity.Platform,
Location = activity.Location,
Timestamp = activity.Timestamp ?? DateTime.UtcNow
};
_context.BotActivities.Add(botActivity);
await _context.SaveChangesAsync();
_logger.LogInformation("Tracked bot activity: {Type} by {User} on {Platform}",
activity.ActivityType, activity.UserDisplayName, activity.Platform);
return Ok(new { success = true, activityId = botActivity.Id });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error tracking bot activity");
return StatusCode(500, new { success = false, error = "Failed to track activity" });
}
}
[HttpGet("activity/stats")]
public async Task<IActionResult> GetActivityStats()
{
var now = DateTime.UtcNow;
var oneDayAgo = now.AddDays(-1);
var oneHourAgo = now.AddHours(-1);
var fiveMinutesAgo = now.AddMinutes(-5);
var stats = new
{
TotalActivities = await _context.BotActivities.CountAsync(),
Last24Hours = await _context.BotActivities.CountAsync(a => a.Timestamp >= oneDayAgo),
LastHour = await _context.BotActivities.CountAsync(a => a.Timestamp >= oneHourAgo),
ActiveNow = await _context.BotActivities.CountAsync(a => a.Timestamp >= fiveMinutesAgo),
UniqueUsersToday = await _context.BotActivities
.Where(a => a.Timestamp >= oneDayAgo)
.Select(a => a.SessionIdentifier)
.Distinct()
.CountAsync(),
TopActivities = await _context.BotActivities
.Where(a => a.Timestamp >= oneDayAgo)
.GroupBy(a => a.ActivityType)
.Select(g => new { Type = g.Key, Count = g.Count() })
.OrderByDescending(x => x.Count)
.Take(5)
.ToListAsync()
};
return Ok(stats);
}
}
public class BotActivityDto
{
public string SessionIdentifier { get; set; } = string.Empty;
public string UserDisplayName { get; set; } = string.Empty;
public string ActivityType { get; set; } = string.Empty;
public string ActivityDescription { get; set; } = string.Empty;
public Guid? ProductId { get; set; }
public string? ProductName { get; set; }
public string? CategoryName { get; set; }
public decimal? Value { get; set; }
public int? Quantity { get; set; }
public string Platform { get; set; } = "Unknown";
public string Location { get; set; } = "Unknown";
public DateTime? Timestamp { get; set; }
}
}

View File

@ -26,6 +26,7 @@ namespace TeleBot.Handlers
private readonly ILittleShopService _shopService; private readonly ILittleShopService _shopService;
private readonly IPrivacyService _privacyService; private readonly IPrivacyService _privacyService;
private readonly IProductCarouselService _carouselService; private readonly IProductCarouselService _carouselService;
private readonly IBotActivityTracker _activityTracker;
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly ILogger<CallbackHandler> _logger; private readonly ILogger<CallbackHandler> _logger;
@ -34,6 +35,7 @@ namespace TeleBot.Handlers
ILittleShopService shopService, ILittleShopService shopService,
IPrivacyService privacyService, IPrivacyService privacyService,
IProductCarouselService carouselService, IProductCarouselService carouselService,
IBotActivityTracker activityTracker,
IConfiguration configuration, IConfiguration configuration,
ILogger<CallbackHandler> logger) ILogger<CallbackHandler> logger)
{ {
@ -41,6 +43,7 @@ namespace TeleBot.Handlers
_shopService = shopService; _shopService = shopService;
_privacyService = privacyService; _privacyService = privacyService;
_carouselService = carouselService; _carouselService = carouselService;
_activityTracker = activityTracker;
_configuration = configuration; _configuration = configuration;
_logger = logger; _logger = logger;
} }
@ -232,6 +235,13 @@ namespace TeleBot.Handlers
private async Task HandleBrowse(ITelegramBotClient bot, Message message, UserSession session) private async Task HandleBrowse(ITelegramBotClient bot, Message message, UserSession session)
{ {
// Track browsing activity
await _activityTracker.TrackActivityAsync(
message.Chat,
ActivityTypes.Browse,
"Browsing categories"
);
var categories = await _shopService.GetCategoriesAsync(); var categories = await _shopService.GetCategoriesAsync();
await bot.EditMessageTextAsync( await bot.EditMessageTextAsync(
@ -250,6 +260,13 @@ namespace TeleBot.Handlers
var category = categories.FirstOrDefault(c => c.Id == categoryId); var category = categories.FirstOrDefault(c => c.Id == categoryId);
var products = await _shopService.GetProductsAsync(categoryId, 1); var products = await _shopService.GetProductsAsync(categoryId, 1);
// Track category view
await _activityTracker.TrackActivityAsync(
message.Chat,
ActivityTypes.ViewProduct,
$"Viewing category: {category?.Name ?? "Unknown"}"
);
// Edit the original message to show category header (bigger, more prominent) // Edit the original message to show category header (bigger, more prominent)
var headerText = $"**{category?.Name ?? "Unknown Category"} PRODUCTS**\n\nBrowse individual products below:"; var headerText = $"**{category?.Name ?? "Unknown Category"} PRODUCTS**\n\nBrowse individual products below:";
@ -335,6 +352,14 @@ namespace TeleBot.Handlers
return; return;
} }
// Track product view
await _activityTracker.TrackActivityAsync(
message.Chat,
ActivityTypes.ViewProduct,
$"Viewing product: {product.Name}",
product
);
// Store current product in temp data for quantity selection // Store current product in temp data for quantity selection
session.TempData["current_product"] = productId; session.TempData["current_product"] = productId;
session.TempData["current_quantity"] = 1; session.TempData["current_quantity"] = 1;
@ -407,6 +432,16 @@ namespace TeleBot.Handlers
session.Cart.AddItem(productId, itemName, price, quantity, multiBuyId, selectedVariant); session.Cart.AddItem(productId, itemName, price, quantity, multiBuyId, selectedVariant);
// Track add to cart action
await _activityTracker.TrackActivityAsync(
callbackQuery.Message!.Chat,
ActivityTypes.AddToCart,
$"Added to cart: {itemName}",
product,
price * quantity,
quantity
);
await bot.AnswerCallbackQueryAsync( await bot.AnswerCallbackQueryAsync(
callbackQuery.Id, callbackQuery.Id,
$"✅ Added {quantity}x {itemName} to cart", $"✅ Added {quantity}x {itemName} to cart",
@ -419,6 +454,15 @@ namespace TeleBot.Handlers
private async Task HandleViewCart(ITelegramBotClient bot, Message message, UserSession session) private async Task HandleViewCart(ITelegramBotClient bot, Message message, UserSession session)
{ {
// Track cart view
await _activityTracker.TrackActivityAsync(
message.Chat,
ActivityTypes.ViewCart,
$"Viewing cart with {session.Cart.Items.Count} items",
null,
session.Cart.GetTotalAmount()
);
await bot.EditMessageTextAsync( await bot.EditMessageTextAsync(
message.Chat.Id, message.Chat.Id,
message.MessageId, message.MessageId,
@ -499,6 +543,16 @@ namespace TeleBot.Handlers
// Add to cart with base product // Add to cart with base product
session.Cart.AddItem(productId, product.Name, product.Price, quantity, null, null); session.Cart.AddItem(productId, product.Name, product.Price, quantity, null, null);
// Track quick buy action
await _activityTracker.TrackActivityAsync(
callbackQuery.Message!.Chat,
ActivityTypes.AddToCart,
$"Quick buy: {product.Name}",
product,
product.Price * quantity,
quantity
);
await bot.AnswerCallbackQueryAsync( await bot.AnswerCallbackQueryAsync(
callbackQuery.Id, callbackQuery.Id,
$"✅ Added {quantity}x {product.Name} to cart", $"✅ Added {quantity}x {product.Name} to cart",
@ -623,6 +677,15 @@ namespace TeleBot.Handlers
return; return;
} }
// Track checkout initiation
await _activityTracker.TrackActivityAsync(
message.Chat,
ActivityTypes.Checkout,
$"Starting checkout with {session.Cart.Items.Count} items",
null,
session.Cart.GetTotalAmount()
);
// Initialize order flow // Initialize order flow
session.OrderFlow = new OrderFlowData session.OrderFlow = new OrderFlowData
{ {
@ -876,6 +939,13 @@ namespace TeleBot.Handlers
private async Task HandleViewOrders(ITelegramBotClient bot, Message message, UserSession session, User telegramUser) private async Task HandleViewOrders(ITelegramBotClient bot, Message message, UserSession session, User telegramUser)
{ {
// Track view orders action
await _activityTracker.TrackActivityAsync(
message.Chat,
ActivityTypes.ViewOrders,
"Viewing order history"
);
// Use new customer-based order lookup // Use new customer-based order lookup
var orders = await _shopService.GetCustomerOrdersAsync( var orders = await _shopService.GetCustomerOrdersAsync(
telegramUser.Id, telegramUser.Id,

View File

@ -0,0 +1,102 @@
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Telegram.Bot.Types;
using LittleShop.Client.Models;
namespace TeleBot.Services
{
public interface IBotActivityTracker
{
Task TrackActivityAsync(
Chat chat,
string activityType,
string description,
Product? product = null,
decimal? value = null,
int? quantity = null);
}
public class BotActivityTracker : IBotActivityTracker
{
private readonly HttpClient _httpClient;
private readonly IConfiguration _configuration;
private readonly ILogger<BotActivityTracker> _logger;
private readonly string _littleShopUrl;
public BotActivityTracker(
HttpClient httpClient,
IConfiguration configuration,
ILogger<BotActivityTracker> logger)
{
_httpClient = httpClient;
_configuration = configuration;
_logger = logger;
_littleShopUrl = configuration["LittleShop:BaseUrl"] ?? "http://littleshop-admin:8080";
}
public async Task TrackActivityAsync(
Chat chat,
string activityType,
string description,
Product? product = null,
decimal? value = null,
int? quantity = null)
{
try
{
var activity = new
{
SessionIdentifier = $"telegram_{chat.Id}",
UserDisplayName = chat.Username ?? $"{chat.FirstName} {chat.LastName}".Trim(),
ActivityType = activityType,
ActivityDescription = description,
ProductId = product?.Id,
ProductName = product?.Name,
CategoryName = product?.CategoryName,
Value = value,
Quantity = quantity,
Platform = "Telegram",
Location = "Unknown",
Timestamp = DateTime.UtcNow
};
var json = JsonSerializer.Serialize(activity);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(
$"{_littleShopUrl}/api/bot/activity",
content);
if (!response.IsSuccessStatusCode)
{
_logger.LogWarning("Failed to track activity: {StatusCode}", response.StatusCode);
}
}
catch (Exception ex)
{
// Don't let tracking errors break the main flow
_logger.LogError(ex, "Error tracking bot activity");
}
}
}
public class ActivityTypes
{
public const string Browse = "Browse";
public const string ViewProduct = "ViewProduct";
public const string AddToCart = "AddToCart";
public const string RemoveFromCart = "RemoveFromCart";
public const string ViewCart = "ViewCart";
public const string Checkout = "Checkout";
public const string SelectVariant = "SelectVariant";
public const string ViewOrders = "ViewOrders";
public const string Search = "Search";
public const string Help = "Help";
public const string Start = "Start";
}
}