mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28:27 -04: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)
|
||||
{
|
||||
@@ -32,7 +36,7 @@ public class CurrencyService : ICurrencyService, INService
|
||||
var wallet = await GetWalletAsync(userId);
|
||||
await wallet.Add(amount, txData);
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -49,13 +53,13 @@ public class CurrencyService : ICurrencyService, INService
|
||||
{
|
||||
await using var ctx = _db.GetDbContext();
|
||||
await ctx.DiscordUser
|
||||
.Where(x => userIds.Contains(x.UserId))
|
||||
.UpdateAsync(du => new()
|
||||
{
|
||||
CurrencyAmount = du.CurrencyAmount >= amount
|
||||
? du.CurrencyAmount - amount
|
||||
: 0
|
||||
});
|
||||
.Where(x => userIds.Contains(x.UserId))
|
||||
.UpdateAsync(du => new()
|
||||
{
|
||||
CurrencyAmount = du.CurrencyAmount >= amount
|
||||
? du.CurrencyAmount - amount
|
||||
: 0
|
||||
});
|
||||
await ctx.SaveChangesAsync();
|
||||
return;
|
||||
}
|
||||
@@ -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