mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-03 16:24:27 -05:00 
			
		
		
		
	Added .betstats command, shows statistics for multiple gambling commands, .slotstats removed as it is obsolete
This commit is contained in:
		
							
								
								
									
										9
									
								
								src/NadekoBot/Db/Models/GamblingStats.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/NadekoBot/Db/Models/GamblingStats.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class GamblingStats : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string Feature { get; set; }
 | 
			
		||||
    public decimal Bet { get; set; }
 | 
			
		||||
    public decimal PaidOut { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -472,6 +472,14 @@ public abstract class NadekoContext : DbContext
 | 
			
		||||
            .IsUnique());
 | 
			
		||||
 | 
			
		||||
        #endregion
 | 
			
		||||
        
 | 
			
		||||
        #region GamblingStats
 | 
			
		||||
 | 
			
		||||
        modelBuilder.Entity<GamblingStats>(gs => gs
 | 
			
		||||
            .HasIndex(x => x.Feature)
 | 
			
		||||
            .IsUnique());
 | 
			
		||||
 | 
			
		||||
        #endregion
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2898
									
								
								src/NadekoBot/Migrations/Sqlite/20221003111019_gambling-stats.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										2898
									
								
								src/NadekoBot/Migrations/Sqlite/20221003111019_gambling-stats.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Migrations;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Migrations
 | 
			
		||||
{
 | 
			
		||||
    public partial class gamblingstats : Migration
 | 
			
		||||
    {
 | 
			
		||||
        protected override void Up(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.CreateTable(
 | 
			
		||||
                name: "GamblingStats",
 | 
			
		||||
                columns: table => new
 | 
			
		||||
                {
 | 
			
		||||
                    Id = table.Column<int>(type: "INTEGER", nullable: false)
 | 
			
		||||
                        .Annotation("Sqlite:Autoincrement", true),
 | 
			
		||||
                    Feature = table.Column<string>(type: "TEXT", nullable: true),
 | 
			
		||||
                    Bet = table.Column<decimal>(type: "TEXT", nullable: false),
 | 
			
		||||
                    PaidOut = table.Column<decimal>(type: "TEXT", nullable: false),
 | 
			
		||||
                    DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
 | 
			
		||||
                },
 | 
			
		||||
                constraints: table =>
 | 
			
		||||
                {
 | 
			
		||||
                    table.PrimaryKey("PK_GamblingStats", x => x.Id);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
                name: "IX_GamblingStats_Feature",
 | 
			
		||||
                table: "GamblingStats",
 | 
			
		||||
                column: "Feature",
 | 
			
		||||
                unique: true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void Down(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DropTable(
 | 
			
		||||
                name: "GamblingStats");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -864,6 +864,32 @@ namespace NadekoBot.Migrations
 | 
			
		||||
                    b.ToTable("FilterWordsChannelId");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.GamblingStats", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<int>("Id")
 | 
			
		||||
                        .ValueGeneratedOnAdd()
 | 
			
		||||
                        .HasColumnType("INTEGER");
 | 
			
		||||
 | 
			
		||||
                    b.Property<decimal>("Bet")
 | 
			
		||||
                        .HasColumnType("TEXT");
 | 
			
		||||
 | 
			
		||||
                    b.Property<DateTime?>("DateAdded")
 | 
			
		||||
                        .HasColumnType("TEXT");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Feature")
 | 
			
		||||
                        .HasColumnType("TEXT");
 | 
			
		||||
 | 
			
		||||
                    b.Property<decimal>("PaidOut")
 | 
			
		||||
                        .HasColumnType("TEXT");
 | 
			
		||||
 | 
			
		||||
                    b.HasKey("Id");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("Feature")
 | 
			
		||||
                        .IsUnique();
 | 
			
		||||
 | 
			
		||||
                    b.ToTable("GamblingStats");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<int>("Id")
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
    private readonly IBankService _bank;
 | 
			
		||||
    private readonly IPatronageService _ps;
 | 
			
		||||
    private readonly RemindService _remind;
 | 
			
		||||
    private readonly GamblingTxTracker _gamblingTxTracker;
 | 
			
		||||
 | 
			
		||||
    private IUserMessage rdMsg;
 | 
			
		||||
 | 
			
		||||
@@ -41,7 +42,8 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
        GamblingConfigService configService,
 | 
			
		||||
        IBankService bank,
 | 
			
		||||
        IPatronageService ps,
 | 
			
		||||
        RemindService remind)
 | 
			
		||||
        RemindService remind,
 | 
			
		||||
        GamblingTxTracker gamblingTxTracker)
 | 
			
		||||
        : base(configService)
 | 
			
		||||
    {
 | 
			
		||||
        _gs = gs;
 | 
			
		||||
@@ -51,6 +53,7 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
        _bank = bank;
 | 
			
		||||
        _ps = ps;
 | 
			
		||||
        _remind = remind;
 | 
			
		||||
        _gamblingTxTracker = gamblingTxTracker;
 | 
			
		||||
 | 
			
		||||
        _enUsCulture = new CultureInfo("en-US", false).NumberFormat;
 | 
			
		||||
        _enUsCulture.NumberDecimalDigits = 0;
 | 
			
		||||
@@ -65,6 +68,26 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
        return N(bal);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public async Task BetStats()
 | 
			
		||||
    {
 | 
			
		||||
        var stats = await _gamblingTxTracker.GetAllAsync();
 | 
			
		||||
 | 
			
		||||
        var eb = _eb.Create(ctx)
 | 
			
		||||
            .WithOkColor();
 | 
			
		||||
 | 
			
		||||
        var str = "**Feature |      Bet      |   Paid  Out   | Payout %** \n";
 | 
			
		||||
        foreach (var stat in stats)
 | 
			
		||||
        {
 | 
			
		||||
            var perc = (stat.PaidOut / stat.Bet).ToString("P2", Culture);
 | 
			
		||||
            str += $"{stat.Feature, 8} | {stat.Bet, 15} | {stat.PaidOut, 15} | {perc}\n";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        eb.WithDescription(str);
 | 
			
		||||
 | 
			
		||||
        await ctx.Channel.EmbedAsync(eb);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public async Task Economy()
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -47,27 +47,6 @@ public partial class Gambling
 | 
			
		||||
        public Task Test()
 | 
			
		||||
            => Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        [OwnerOnly]
 | 
			
		||||
        public async Task SlotStats()
 | 
			
		||||
        {
 | 
			
		||||
            //i remembered to not be a moron
 | 
			
		||||
            var paid = totalPaidOut;
 | 
			
		||||
            var bet = totalBet;
 | 
			
		||||
 | 
			
		||||
            if (bet <= 0)
 | 
			
		||||
                bet = 1;
 | 
			
		||||
 | 
			
		||||
            var embed = _eb.Create()
 | 
			
		||||
                           .WithOkColor()
 | 
			
		||||
                           .WithTitle("Slot Stats")
 | 
			
		||||
                           .AddField("Total Bet", N(bet), true)
 | 
			
		||||
                           .AddField("Paid Out", N(paid), true)
 | 
			
		||||
                           .WithFooter($"Payout Rate: {paid * 1.0M / bet * 100:f4}%");
 | 
			
		||||
 | 
			
		||||
            await ctx.Channel.EmbedAsync(embed);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        public async Task Slot(ShmartNumber amount)
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -85,10 +85,6 @@
 | 
			
		||||
        <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.5" />
 | 
			
		||||
        <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1" />
 | 
			
		||||
        
 | 
			
		||||
<!--        <PackageReference Include="System.Runtime.Experimental" Version="6.0.2" />-->
 | 
			
		||||
        
 | 
			
		||||
        <!-- Used by .crypto command -->
 | 
			
		||||
        
 | 
			
		||||
        <!-- Used by stream notifications -->
 | 
			
		||||
        <PackageReference Include="TwitchLib.Api" Version="3.4.1" />
 | 
			
		||||
        
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,16 @@ using NadekoBot.Services.Currency;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services;
 | 
			
		||||
 | 
			
		||||
public class CurrencyService : ICurrencyService, INService
 | 
			
		||||
public sealed class CurrencyService : ICurrencyService, INService
 | 
			
		||||
{
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
    private readonly ITxTracker _txTracker;
 | 
			
		||||
 | 
			
		||||
    public CurrencyService(DbService db)
 | 
			
		||||
        => _db = db;
 | 
			
		||||
    public CurrencyService(DbService db, ITxTracker txTracker)
 | 
			
		||||
    {
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _txTracker = txTracker;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task<IWallet> GetWalletAsync(ulong userId, CurrencyType type = CurrencyType.Default)
 | 
			
		||||
    {
 | 
			
		||||
@@ -70,16 +74,14 @@ public class CurrencyService : ICurrencyService, INService
 | 
			
		||||
    {
 | 
			
		||||
        var wallet = await GetWalletAsync(userId);
 | 
			
		||||
        await wallet.Add(amount, txData);
 | 
			
		||||
        await _txTracker.TrackAdd(amount, txData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task AddAsync(
 | 
			
		||||
        IUser user,
 | 
			
		||||
        long amount,
 | 
			
		||||
        TxData txData)
 | 
			
		||||
    {
 | 
			
		||||
        var wallet = await GetWalletAsync(user.Id);
 | 
			
		||||
        await wallet.Add(amount, txData);
 | 
			
		||||
    }
 | 
			
		||||
        => await AddAsync(user.Id, amount, txData);
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> RemoveAsync(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
@@ -87,15 +89,14 @@ public class CurrencyService : ICurrencyService, INService
 | 
			
		||||
        TxData txData)
 | 
			
		||||
    {
 | 
			
		||||
        var wallet = await GetWalletAsync(userId);
 | 
			
		||||
        return await wallet.Take(amount, txData);
 | 
			
		||||
        var result = await wallet.Take(amount, txData);
 | 
			
		||||
        await _txTracker.TrackRemove(amount, txData);
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> RemoveAsync(
 | 
			
		||||
        IUser user,
 | 
			
		||||
        long amount,
 | 
			
		||||
        TxData txData)
 | 
			
		||||
    {
 | 
			
		||||
        var wallet = await GetWalletAsync(user.Id);
 | 
			
		||||
        return await wallet.Take(amount, txData);
 | 
			
		||||
    }
 | 
			
		||||
        => await RemoveAsync(user.Id, amount, txData);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										105
									
								
								src/NadekoBot/Services/Currency/GamblingTxTracker.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/NadekoBot/Services/Currency/GamblingTxTracker.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
using LinqToDB;
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
using NadekoBot.Services.Currency;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services;
 | 
			
		||||
 | 
			
		||||
public sealed class GamblingTxTracker : ITxTracker, INService, IReadyExecutor
 | 
			
		||||
{
 | 
			
		||||
    private static readonly IReadOnlySet<string> _gamblingTypes = new HashSet<string>(new[]
 | 
			
		||||
    {
 | 
			
		||||
        "lula",
 | 
			
		||||
        "betroll",
 | 
			
		||||
        "betflip",
 | 
			
		||||
        "blackjack",
 | 
			
		||||
        "betdraw",
 | 
			
		||||
        "slot",
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    private ConcurrentDictionary<string, (decimal Bet, decimal PaidOut)> _stats = new();
 | 
			
		||||
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
 | 
			
		||||
    public GamblingTxTracker(DbService db)
 | 
			
		||||
    {
 | 
			
		||||
        _db = db;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task OnReadyAsync()
 | 
			
		||||
    {
 | 
			
		||||
        using var timer = new PeriodicTimer(TimeSpan.FromHours(1));
 | 
			
		||||
        while (await timer.WaitForNextTickAsync())
 | 
			
		||||
        {
 | 
			
		||||
            await using var ctx = _db.GetDbContext();
 | 
			
		||||
            await using var trans = await ctx.Database.BeginTransactionAsync();
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var keys = _stats.Keys;
 | 
			
		||||
                foreach (var key in keys)
 | 
			
		||||
                {
 | 
			
		||||
                    if (_stats.TryRemove(key, out var stat))
 | 
			
		||||
                    {
 | 
			
		||||
                        await ctx.GetTable<GamblingStats>()
 | 
			
		||||
                            .InsertOrUpdateAsync(() => new()
 | 
			
		||||
                            {
 | 
			
		||||
                                Feature = key,
 | 
			
		||||
                                Bet = stat.Bet,
 | 
			
		||||
                                PaidOut = stat.PaidOut,
 | 
			
		||||
                                DateAdded = DateTime.UtcNow
 | 
			
		||||
                            }, old => new()
 | 
			
		||||
                            {
 | 
			
		||||
                                Bet = old.Bet + stat.Bet,
 | 
			
		||||
                                PaidOut = old.PaidOut + stat.PaidOut,
 | 
			
		||||
                            }, () => new()
 | 
			
		||||
                            {
 | 
			
		||||
                                Feature = key
 | 
			
		||||
                            });
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Log.Error(ex, "An error occurred in gambling tx tracker");
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                await trans.CommitAsync();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task TrackAdd(long amount, TxData txData)
 | 
			
		||||
    {
 | 
			
		||||
        if (_gamblingTypes.Contains(txData.Type))
 | 
			
		||||
        {
 | 
			
		||||
            _stats.AddOrUpdate(txData.Type,
 | 
			
		||||
                _ => (0, amount),
 | 
			
		||||
                (_, old) => (old.Bet, old.PaidOut + amount));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task TrackRemove(long amount, TxData txData)
 | 
			
		||||
    {
 | 
			
		||||
        if (_gamblingTypes.Contains(txData.Type))
 | 
			
		||||
        {
 | 
			
		||||
            _stats.AddOrUpdate(txData.Type,
 | 
			
		||||
                _ => (amount, 0),
 | 
			
		||||
                (_, old) => (old.Bet + amount, old.PaidOut));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<IReadOnlyCollection<GamblingStats>> GetAllAsync()
 | 
			
		||||
    {
 | 
			
		||||
        await using var ctx = _db.GetDbContext();
 | 
			
		||||
        return await ctx
 | 
			
		||||
            .GetTable<GamblingStats>()
 | 
			
		||||
            .ToListAsync();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/NadekoBot/Services/Currency/ITxTracker.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/NadekoBot/Services/Currency/ITxTracker.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
using NadekoBot.Services.Currency;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services;
 | 
			
		||||
 | 
			
		||||
public interface ITxTracker
 | 
			
		||||
{
 | 
			
		||||
    Task TrackAdd(long amount, TxData txData);
 | 
			
		||||
    Task TrackRemove(long amount, TxData txData);
 | 
			
		||||
}
 | 
			
		||||
@@ -852,8 +852,8 @@ antispamignore:
 | 
			
		||||
  - antispamignore
 | 
			
		||||
eventstart:
 | 
			
		||||
  - eventstart
 | 
			
		||||
slotstats:
 | 
			
		||||
  - slotstats
 | 
			
		||||
betstats:
 | 
			
		||||
  - betstats
 | 
			
		||||
bettest:
 | 
			
		||||
  - bettest
 | 
			
		||||
slot:
 | 
			
		||||
 
 | 
			
		||||
@@ -1458,8 +1458,8 @@ eventstart:
 | 
			
		||||
  args:
 | 
			
		||||
    - "reaction"
 | 
			
		||||
    - "reaction -d 1 -a 50 --pot-size 1500"
 | 
			
		||||
slotstats:
 | 
			
		||||
  desc: "Shows the total stats of the slot command for this bot's session."
 | 
			
		||||
betstats:
 | 
			
		||||
  desc: "Shows the total stats of several gambling features. Updates once an hour."
 | 
			
		||||
  args:
 | 
			
		||||
    - ""
 | 
			
		||||
slottest:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user