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,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,
|
||||||
|
|||||||
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