Add bot activity tracking system
This commit is contained in:
parent
d6f8a5e697
commit
ac4fe688d9
132
LittleShop/Controllers/BotController.cs
Normal file
132
LittleShop/Controllers/BotController.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@ -26,14 +26,16 @@ namespace TeleBot.Handlers
|
||||
private readonly ILittleShopService _shopService;
|
||||
private readonly IPrivacyService _privacyService;
|
||||
private readonly IProductCarouselService _carouselService;
|
||||
private readonly IBotActivityTracker _activityTracker;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<CallbackHandler> _logger;
|
||||
|
||||
|
||||
public CallbackHandler(
|
||||
ISessionManager sessionManager,
|
||||
ILittleShopService shopService,
|
||||
IPrivacyService privacyService,
|
||||
IProductCarouselService carouselService,
|
||||
IBotActivityTracker activityTracker,
|
||||
IConfiguration configuration,
|
||||
ILogger<CallbackHandler> logger)
|
||||
{
|
||||
@ -41,6 +43,7 @@ namespace TeleBot.Handlers
|
||||
_shopService = shopService;
|
||||
_privacyService = privacyService;
|
||||
_carouselService = carouselService;
|
||||
_activityTracker = activityTracker;
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
}
|
||||
@ -232,6 +235,13 @@ namespace TeleBot.Handlers
|
||||
|
||||
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();
|
||||
|
||||
await bot.EditMessageTextAsync(
|
||||
@ -249,6 +259,13 @@ namespace TeleBot.Handlers
|
||||
var categories = await _shopService.GetCategoriesAsync();
|
||||
var category = categories.FirstOrDefault(c => c.Id == categoryId);
|
||||
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)
|
||||
var headerText = $"**{category?.Name ?? "Unknown Category"} PRODUCTS**\n\nBrowse individual products below:";
|
||||
@ -334,7 +351,15 @@ namespace TeleBot.Handlers
|
||||
await bot.AnswerCallbackQueryAsync("", "Product not found", showAlert: true);
|
||||
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
|
||||
session.TempData["current_product"] = productId;
|
||||
session.TempData["current_quantity"] = 1;
|
||||
@ -407,6 +432,16 @@ namespace TeleBot.Handlers
|
||||
|
||||
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(
|
||||
callbackQuery.Id,
|
||||
$"✅ Added {quantity}x {itemName} to cart",
|
||||
@ -419,6 +454,15 @@ namespace TeleBot.Handlers
|
||||
|
||||
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(
|
||||
message.Chat.Id,
|
||||
message.MessageId,
|
||||
@ -499,6 +543,16 @@ namespace TeleBot.Handlers
|
||||
// Add to cart with base product
|
||||
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(
|
||||
callbackQuery.Id,
|
||||
$"✅ Added {quantity}x {product.Name} to cart",
|
||||
@ -623,6 +677,15 @@ namespace TeleBot.Handlers
|
||||
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
|
||||
session.OrderFlow = new OrderFlowData
|
||||
{
|
||||
@ -876,6 +939,13 @@ namespace TeleBot.Handlers
|
||||
|
||||
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
|
||||
var orders = await _shopService.GetCustomerOrdersAsync(
|
||||
telegramUser.Id,
|
||||
|
||||
102
TeleBot/TeleBot/Services/BotActivityTracker.cs
Normal file
102
TeleBot/TeleBot/Services/BotActivityTracker.cs
Normal 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";
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user