Add missing async support to ImageSharp for webp rendering in roulette as well as adding as much support for cancellation tokens as I can

This commit is contained in:
barelyprofessional
2026-05-10 21:57:28 -05:00
parent a4b740480f
commit 32bef9f8e0

View File

@@ -6,7 +6,6 @@ using KfChatDotNetBot.Models;
using KfChatDotNetBot.Models.DbModels; using KfChatDotNetBot.Models.DbModels;
using KfChatDotNetBot.Services; using KfChatDotNetBot.Services;
using KfChatDotNetBot.Settings; using KfChatDotNetBot.Settings;
using KfChatDotNetWsClient.Models.Events;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NLog; using NLog;
using SixLabors.Fonts; using SixLabors.Fonts;
@@ -47,6 +46,7 @@ public class RouletteCommand : ICommand
private IDatabase? _redisDb; private IDatabase? _redisDb;
private ApplicationDbContext _dbContext = new(); private ApplicationDbContext _dbContext = new();
private CancellationToken _ct;
// European Roulette wheel configuration // European Roulette wheel configuration
private static readonly HashSet<int> BlackNumbers = new() private static readonly HashSet<int> BlackNumbers = new()
@@ -58,6 +58,7 @@ public class RouletteCommand : ICommand
public async Task RunCommand(ChatBot botInstance, BotCommandMessageModel message, UserDbModel user, GroupCollection arguments, public async Task RunCommand(ChatBot botInstance, BotCommandMessageModel message, UserDbModel user, GroupCollection arguments,
CancellationToken ctx) CancellationToken ctx)
{ {
_ct = ctx;
var settings = await SettingsProvider.GetMultipleValuesAsync([ var settings = await SettingsProvider.GetMultipleValuesAsync([
BuiltIn.Keys.KasinoGameDisabledMessageCleanupDelay, BuiltIn.Keys.KasinoGameDisabledMessageCleanupDelay,
BuiltIn.Keys.KasinoRouletteEnabled, BuiltIn.Keys.KasinoRouletteEnabled,
@@ -271,7 +272,7 @@ public class RouletteCommand : ICommand
// Wait until message is fully sent // Wait until message is fully sent
logger.Debug("Waiting for countdown message to be sent..."); logger.Debug("Waiting for countdown message to be sent...");
var success = await botInstance.WaitForChatMessageAsync(countdownMessage, TimeSpan.FromSeconds(30)); var success = await botInstance.WaitForChatMessageAsync(countdownMessage, TimeSpan.FromSeconds(30), _ct);
if (!success) if (!success)
{ {
@@ -289,7 +290,7 @@ public class RouletteCommand : ICommand
if (remaining.TotalSeconds <= 0) break; if (remaining.TotalSeconds <= 0) break;
// Wait 1 second between updates // Wait 1 second between updates
await Task.Delay(TimeSpan.FromSeconds(1)); await Task.Delay(TimeSpan.FromSeconds(1), _ct);
try try
{ {
@@ -386,7 +387,7 @@ public class RouletteCommand : ICommand
{ {
// Generate winning number using first gambler's seed // Generate winning number using first gambler's seed
var firstGambler = await _dbContext.Gamblers var firstGambler = await _dbContext.Gamblers
.FirstOrDefaultAsync(g => g.Id == round.Bets[0].GamblerId); .FirstOrDefaultAsync(g => g.Id == round.Bets[0].GamblerId, cancellationToken: _ct);
if (firstGambler == null) if (firstGambler == null)
{ {
@@ -398,7 +399,7 @@ public class RouletteCommand : ICommand
// Generate animation // Generate animation
logger.Info($"Generating roulette animation for round {round.RoundId}"); logger.Info($"Generating roulette animation for round {round.RoundId}");
var (animationDuration, animationBytes) = RouletteAnimationGenerator.GenerateAnimation(winningNumber); var (animationDuration, animationBytes) = await RouletteAnimationGenerator.GenerateAnimation(winningNumber, _ct);
logger.Info($"Animation generated: {animationBytes.Length} bytes, duration: {animationDuration}s"); logger.Info($"Animation generated: {animationBytes.Length} bytes, duration: {animationDuration}s");
// Upload animation to Zipline // Upload animation to Zipline
@@ -407,7 +408,7 @@ public class RouletteCommand : ICommand
var animationUrl = await Zipline.Upload( var animationUrl = await Zipline.Upload(
animationStream, animationStream,
new MediaTypeHeaderValue("image/webp"), new MediaTypeHeaderValue("image/webp"),
expiration: "1h"); expiration: "1h", ct: _ct);
if (string.IsNullOrEmpty(animationUrl)) if (string.IsNullOrEmpty(animationUrl))
{ {
@@ -432,7 +433,7 @@ public class RouletteCommand : ICommand
// Wait for animation duration before revealing results // Wait for animation duration before revealing results
logger.Info($"Waiting {animationDuration} seconds for animation to complete"); logger.Info($"Waiting {animationDuration} seconds for animation to complete");
await Task.Delay(TimeSpan.FromSeconds(animationDuration)); await Task.Delay(TimeSpan.FromSeconds(animationDuration), _ct);
// Process all bets and show results // Process all bets and show results
await ProcessBets(botInstance, round, winningNumber); await ProcessBets(botInstance, round, winningNumber);
@@ -459,20 +460,20 @@ public class RouletteCommand : ICommand
{ {
var wager = await _dbContext.Wagers var wager = await _dbContext.Wagers
.Include(w => w.Gambler) .Include(w => w.Gambler)
.FirstOrDefaultAsync(w => w.Id == bet.WagerId); .FirstOrDefaultAsync(w => w.Id == bet.WagerId, cancellationToken: _ct);
if (wager != null) if (wager != null)
{ {
wager.IsComplete = true; wager.IsComplete = true;
wager.WagerEffect = 0; wager.WagerEffect = 0;
wager.Multiplier = 1; wager.Multiplier = 1;
await _dbContext.SaveChangesAsync(); await _dbContext.SaveChangesAsync(_ct);
await Money.ModifyBalanceAsync( await Money.ModifyBalanceAsync(
wager.Gambler.Id, wager.Gambler.Id,
wager.WagerAmount, wager.WagerAmount,
TransactionSourceEventType.Gambling, TransactionSourceEventType.Gambling,
$"Roulette round {round.RoundId} cancelled due to error, wager {wager.Id} refunded"); $"Roulette round {round.RoundId} cancelled due to error, wager {wager.Id} refunded", ct: _ct);
totalRefunded += wager.WagerAmount; totalRefunded += wager.WagerAmount;
} }
@@ -520,7 +521,7 @@ public class RouletteCommand : ICommand
{ {
var wager = await _dbContext.Wagers var wager = await _dbContext.Wagers
.Include(w => w.Gambler) .Include(w => w.Gambler)
.FirstOrDefaultAsync(w => w.Id == bet.WagerId); .FirstOrDefaultAsync(w => w.Id == bet.WagerId, cancellationToken: _ct);
if (wager == null) if (wager == null)
{ {
@@ -537,7 +538,7 @@ public class RouletteCommand : ICommand
wager.WagerEffect = effect; wager.WagerEffect = effect;
wager.Multiplier = payout / bet.Amount; wager.Multiplier = payout / bet.Amount;
await _dbContext.SaveChangesAsync(); await _dbContext.SaveChangesAsync(_ct);
// Update balance // Update balance
var balanceAdjustment = payout; var balanceAdjustment = payout;
@@ -545,7 +546,7 @@ public class RouletteCommand : ICommand
wager.Gambler.Id, wager.Gambler.Id,
balanceAdjustment, balanceAdjustment,
TransactionSourceEventType.Gambling, TransactionSourceEventType.Gambling,
$"Roulette outcome from wager {wager.Id}"); $"Roulette outcome from wager {wager.Id}", ct: _ct);
// Track results by user // Track results by user
if (!winnersByUser.ContainsKey(bet.Username)) if (!winnersByUser.ContainsKey(bet.Username))
@@ -923,7 +924,7 @@ public static class RouletteAnimationGenerator
/// </summary> /// </summary>
/// <param name="winningNumber">The number (0-36) that the ball should land on</param> /// <param name="winningNumber">The number (0-36) that the ball should land on</param>
/// <returns>A tuple containing the animation duration in seconds and the WebP animation bytes</returns> /// <returns>A tuple containing the animation duration in seconds and the WebP animation bytes</returns>
public static (int durationSeconds, byte[] animationBytes) GenerateAnimation(int winningNumber) public static async Task<(int duration, byte[])> GenerateAnimation(int winningNumber, CancellationToken ct = default)
{ {
if (winningNumber < 0 || winningNumber > 36) if (winningNumber < 0 || winningNumber > 36)
{ {
@@ -994,7 +995,7 @@ public static class RouletteAnimationGenerator
animation.Frames.RemoveFrame(0); animation.Frames.RemoveFrame(0);
using var ms = new MemoryStream(); using var ms = new MemoryStream();
animation.SaveAsWebp(ms, new WebpEncoder { FileFormat = WebpFileFormatType.Lossy, Quality = 50 }); await animation.SaveAsWebpAsync(ms, new WebpEncoder { FileFormat = WebpFileFormatType.Lossy, Quality = 50 }, cancellationToken: ct);
return (duration, ms.ToArray()); return (duration, ms.ToArray());
} }