mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28:27 -04:00
- Improved .curtrs (It will now have a lot more useful data in the database, show Tx ids, and be partially localized)
- Added .curtr command which lets you see full information about one of your own transactions with the specified id - Added .WithAuthor(IUser) extension for embedbuilder
This commit is contained in:
@@ -8,6 +8,7 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
|||||||
## Changes
|
## Changes
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added `.deleteemptyservers` command
|
- Added `.deleteemptyservers` command
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
@@ -19,6 +20,7 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
|||||||
- Fixed reference to non-existent command in bot.yml
|
- Fixed reference to non-existent command in bot.yml
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- CustomReactions module (and customreactions db table) has been renamed to Expressions.
|
- CustomReactions module (and customreactions db table) has been renamed to Expressions.
|
||||||
- This was done to remove confusion about how it relates to discord Reactions (it doesn't, it was created and named before discord reactions existed)
|
- This was done to remove confusion about how it relates to discord Reactions (it doesn't, it was created and named before discord reactions existed)
|
||||||
- Expression command now start with ex/expr and end with the name of the action or setting.
|
- Expression command now start with ex/expr and end with the name of the action or setting.
|
||||||
@@ -35,6 +37,7 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
|||||||
- [dev] Moved FilterWordsChannelId to a separate table
|
- [dev] Moved FilterWordsChannelId to a separate table
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Removed `.bce` - use `.config` or `.config bot` specifically for bot config
|
- Removed `.bce` - use `.config` or `.config bot` specifically for bot config
|
||||||
- Removed obsolete placeholders: %users% %servers% %userfull% %username% %userdiscrim% %useravatar% %id% %uid% %chname% %cid% %sid% %members% %server_time% %shardid% %time% %mention%
|
- Removed obsolete placeholders: %users% %servers% %userfull% %username% %userdiscrim% %useravatar% %id% %uid% %chname% %cid% %sid% %members% %server_time% %shardid% %time% %mention%
|
||||||
- Removed some obsolete commands and strings
|
- Removed some obsolete commands and strings
|
||||||
|
@@ -4,9 +4,9 @@ namespace NadekoBot.Services.Database.Models;
|
|||||||
public class CurrencyTransaction : DbEntity
|
public class CurrencyTransaction : DbEntity
|
||||||
{
|
{
|
||||||
public long Amount { get; set; }
|
public long Amount { get; set; }
|
||||||
public string Reason { get; set; }
|
public string Note { get; set; }
|
||||||
public ulong UserId { get; set; }
|
public ulong UserId { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
public CurrencyTransaction Clone()
|
public string Extra { get; set; }
|
||||||
=> new() { Amount = Amount, Reason = Reason, UserId = UserId };
|
public ulong? OtherId { get; set; }
|
||||||
}
|
}
|
@@ -14,7 +14,7 @@ public class FilterChannelId : DbEntity
|
|||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
=> ChannelId.GetHashCode();
|
=> ChannelId.GetHashCode();
|
||||||
}
|
}
|
||||||
// todo check if filterwords migration works
|
|
||||||
public class FilterWordsChannelId : DbEntity
|
public class FilterWordsChannelId : DbEntity
|
||||||
{
|
{
|
||||||
public ulong ChannelId { get; set; }
|
public ulong ChannelId { get; set; }
|
||||||
|
@@ -146,13 +146,23 @@ public class NadekoContext : DbContext
|
|||||||
|
|
||||||
modelBuilder.Entity<DiscordUser>(du =>
|
modelBuilder.Entity<DiscordUser>(du =>
|
||||||
{
|
{
|
||||||
du.Property(x => x.IsClubAdmin).HasDefaultValue(false);
|
du.Property(x => x.IsClubAdmin)
|
||||||
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
du.Property(x => x.NotifyOnLevelUp).HasDefaultValue(XpNotificationLocation.None);
|
du.Property(x => x.NotifyOnLevelUp)
|
||||||
|
.HasDefaultValue(XpNotificationLocation.None);
|
||||||
|
|
||||||
du.Property(x => x.LastXpGain).HasDefaultValueSql("datetime('now', '-1 years')");
|
du.Property(x => x.LastXpGain)
|
||||||
|
.HasDefaultValueSql("datetime('now', '-1 years')");
|
||||||
|
|
||||||
du.Property(x => x.LastLevelUp).HasDefaultValueSql("datetime('now')");
|
du.Property(x => x.LastLevelUp)
|
||||||
|
.HasDefaultValueSql("datetime('now')");
|
||||||
|
|
||||||
|
du.Property(x => x.TotalXp)
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
|
du.Property(x => x.CurrencyAmount)
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
du.HasAlternateKey(w => w.UserId);
|
du.HasAlternateKey(w => w.UserId);
|
||||||
du.HasOne(x => x.Club).WithMany(x => x.Users).IsRequired(false);
|
du.HasOne(x => x.Club).WithMany(x => x.Users).IsRequired(false);
|
||||||
@@ -244,7 +254,22 @@ public class NadekoContext : DbContext
|
|||||||
|
|
||||||
#region CurrencyTransactions
|
#region CurrencyTransactions
|
||||||
|
|
||||||
modelBuilder.Entity<CurrencyTransaction>().HasIndex(x => x.UserId).IsUnique(false);
|
modelBuilder.Entity<CurrencyTransaction>(e =>
|
||||||
|
{
|
||||||
|
e.HasIndex(x => x.UserId)
|
||||||
|
.IsUnique(false);
|
||||||
|
|
||||||
|
e.Property(x => x.OtherId)
|
||||||
|
.HasDefaultValueSql("NULL");
|
||||||
|
|
||||||
|
e.Property(x => x.Type)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
e.Property(x => x.Extra)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -321,6 +346,7 @@ public class NadekoContext : DbContext
|
|||||||
atch.HasMany(x => x.Users).WithOne(x => x.Channel).OnDelete(DeleteBehavior.Cascade);
|
atch.HasMany(x => x.Users).WithOne(x => x.Channel).OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
modelBuilder.Entity<AutoTranslateUser>(atu => atu.HasAlternateKey(x => new { x.ChannelId, x.UserId }));
|
modelBuilder.Entity<AutoTranslateUser>(atu => atu.HasAlternateKey(x => new { x.ChannelId, x.UserId }));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
2763
src/NadekoBot/Migrations/20220125044401_curtrs-rework-discorduser-defaults.Designer.cs
generated
Normal file
2763
src/NadekoBot/Migrations/20220125044401_curtrs-rework-discorduser-defaults.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
public partial class curtrsreworkdiscorduserdefaults : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.RenameColumn(
|
||||||
|
name: "Reason",
|
||||||
|
table: "CurrencyTransactions",
|
||||||
|
newName: "Note");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<int>(
|
||||||
|
name: "TotalXp",
|
||||||
|
table: "DiscordUser",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0,
|
||||||
|
oldClrType: typeof(int),
|
||||||
|
oldType: "INTEGER");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<long>(
|
||||||
|
name: "CurrencyAmount",
|
||||||
|
table: "DiscordUser",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L,
|
||||||
|
oldClrType: typeof(long),
|
||||||
|
oldType: "INTEGER");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Extra",
|
||||||
|
table: "CurrencyTransactions",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "OtherId",
|
||||||
|
table: "CurrencyTransactions",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: true,
|
||||||
|
defaultValueSql: "NULL");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Type",
|
||||||
|
table: "CurrencyTransactions",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Extra",
|
||||||
|
table: "CurrencyTransactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "OtherId",
|
||||||
|
table: "CurrencyTransactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Type",
|
||||||
|
table: "CurrencyTransactions");
|
||||||
|
|
||||||
|
migrationBuilder.RenameColumn(
|
||||||
|
name: "Note",
|
||||||
|
table: "CurrencyTransactions",
|
||||||
|
newName: "Reason");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<int>(
|
||||||
|
name: "TotalXp",
|
||||||
|
table: "DiscordUser",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(int),
|
||||||
|
oldType: "INTEGER",
|
||||||
|
oldDefaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<long>(
|
||||||
|
name: "CurrencyAmount",
|
||||||
|
table: "DiscordUser",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(long),
|
||||||
|
oldType: "INTEGER",
|
||||||
|
oldDefaultValue: 0L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -102,7 +102,9 @@ namespace NadekoBot.Migrations
|
|||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<long>("CurrencyAmount")
|
b.Property<long>("CurrencyAmount")
|
||||||
.HasColumnType("INTEGER");
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(0L);
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
@@ -131,7 +133,9 @@ namespace NadekoBot.Migrations
|
|||||||
.HasDefaultValue(0);
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
b.Property<int>("TotalXp")
|
b.Property<int>("TotalXp")
|
||||||
.HasColumnType("INTEGER");
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
b.Property<ulong>("UserId")
|
b.Property<ulong>("UserId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
@@ -502,7 +506,20 @@ namespace NadekoBot.Migrations
|
|||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("Reason")
|
b.Property<string>("Extra")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Note")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong?>("OtherId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValueSql("NULL");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<ulong>("UserId")
|
b.Property<ulong>("UserId")
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using NadekoBot.Db;
|
using NadekoBot.Db;
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
using NadekoBot.Modules.Gambling.Common;
|
using NadekoBot.Modules.Gambling.Common;
|
||||||
@@ -7,10 +9,10 @@ using NadekoBot.Services.Currency;
|
|||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Gambling;
|
namespace NadekoBot.Modules.Gambling;
|
||||||
|
|
||||||
// todo leave empty servers
|
|
||||||
public partial class Gambling : GamblingModule<GamblingService>
|
public partial class Gambling : GamblingModule<GamblingService>
|
||||||
{
|
{
|
||||||
public enum RpsPick
|
public enum RpsPick
|
||||||
@@ -66,6 +68,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
{
|
{
|
||||||
var flowersCi = (CultureInfo)Culture.Clone();
|
var flowersCi = (CultureInfo)Culture.Clone();
|
||||||
flowersCi.NumberFormat.CurrencySymbol = CurrencySign;
|
flowersCi.NumberFormat.CurrencySymbol = CurrencySign;
|
||||||
|
flowersCi.NumberFormat.CurrencyNegativePattern = 5;
|
||||||
|
// if (cur < 0)
|
||||||
|
// cur = -cur;
|
||||||
return cur.ToString("C0", flowersCi);
|
return cur.ToString("C0", flowersCi);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,6 +204,8 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
public partial Task CurrencyTransactions(IUser usr, int page)
|
public partial Task CurrencyTransactions(IUser usr, int page)
|
||||||
=> InternalCurrencyTransactions(usr.Id, page);
|
=> InternalCurrencyTransactions(usr.Id, page);
|
||||||
|
|
||||||
|
// todo curtrs max lifetime
|
||||||
|
// todo waifu decay
|
||||||
private async Task InternalCurrencyTransactions(ulong userId, int page)
|
private async Task InternalCurrencyTransactions(ulong userId, int page)
|
||||||
{
|
{
|
||||||
if (--page < 0)
|
if (--page < 0)
|
||||||
@@ -215,19 +222,85 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
?? $"{userId}")))
|
?? $"{userId}")))
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
var desc = string.Empty;
|
var sb = new StringBuilder();
|
||||||
foreach (var tr in trs)
|
foreach (var tr in trs)
|
||||||
{
|
{
|
||||||
var type = tr.Amount > 0 ? "🔵" : "🔴";
|
var change = tr.Amount >= 0 ? "🔵" : "🔴";
|
||||||
var date = Format.Code($"〖{tr.DateAdded:HH:mm yyyy-MM-dd}〗");
|
var kwumId = new kwum(tr.Id).ToString();
|
||||||
desc += $"\\{type} {date} {Format.Bold(n(tr.Amount))}\n\t{tr.Reason?.Trim()}\n";
|
var date = $"#{Format.Code(kwumId)} `〖{GetFormattedCurtrDate(tr)}〗`";
|
||||||
|
|
||||||
|
|
||||||
|
sb.AppendLine($"\\{change} {date} {Format.Bold(n(tr.Amount))}");
|
||||||
|
var transactionString = GetHumanReadableTransaction(tr.Type, tr.Extra, tr.OtherId);
|
||||||
|
if(transactionString is not null)
|
||||||
|
sb.AppendLine(transactionString);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(tr.Note))
|
||||||
|
sb.AppendLine($"\t`Note:` {tr.Note.TrimTo(50)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
embed.WithDescription(desc);
|
embed.WithDescription(sb.ToString());
|
||||||
embed.WithFooter(GetText(strs.page(page + 1)));
|
embed.WithFooter(GetText(strs.page(page + 1)));
|
||||||
await ctx.Channel.EmbedAsync(embed);
|
await ctx.Channel.EmbedAsync(embed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetFormattedCurtrDate(CurrencyTransaction ct)
|
||||||
|
=> $"{ct.DateAdded:HH:mm yyyy-MM-dd}";
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async partial Task CurrencyTransaction(kwum id)
|
||||||
|
{
|
||||||
|
int intId = id;
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
|
||||||
|
var tr = await uow.CurrencyTransactions
|
||||||
|
.ToLinqToDBTable()
|
||||||
|
.Where(x => x.Id == intId && x.UserId == ctx.User.Id)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (tr is null)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var eb = _eb.Create(ctx)
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
eb.WithAuthor(ctx.User);
|
||||||
|
eb.WithTitle(GetText(strs.transaction));
|
||||||
|
eb.WithDescription(new kwum(tr.Id).ToString());
|
||||||
|
eb.AddField("Amount", n(tr.Amount), false);
|
||||||
|
eb.AddField("Type", tr.Type, true);
|
||||||
|
eb.AddField("Extra", tr.Extra, true);
|
||||||
|
|
||||||
|
if (tr.OtherId is ulong other)
|
||||||
|
eb.AddField("From Id", other);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(tr.Note))
|
||||||
|
eb.AddField("Note", tr.Note);
|
||||||
|
|
||||||
|
|
||||||
|
eb.WithFooter(GetFormattedCurtrDate(tr));
|
||||||
|
|
||||||
|
await ctx.Channel.EmbedAsync(eb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetHumanReadableTransaction(string type, string subType, ulong? maybeUserId)
|
||||||
|
=> (type, subType, maybeUserId) switch
|
||||||
|
{
|
||||||
|
("gift", var name, ulong userId) => GetText(strs.curtr_gift(name, userId)),
|
||||||
|
("award", var name, ulong userId) => GetText(strs.curtr_award(name, userId)),
|
||||||
|
("take", var name, ulong userId) => GetText(strs.curtr_take(name, userId)),
|
||||||
|
("blackjack", _, _) => $"Blackjack - {subType}",
|
||||||
|
("wheel", _, _) => $"Wheel Of Fortune - {subType}",
|
||||||
|
("rps", _, _) => $"Rock Paper Scissors - {subType}",
|
||||||
|
(null, _, _) => null,
|
||||||
|
(_, null, _) => null,
|
||||||
|
(_, _, ulong userId) => $"{type.Titleize()} - {subType.Titleize()} | [{userId}]",
|
||||||
|
_ => $"{type.Titleize()} - {subType.Titleize()}"
|
||||||
|
};
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async partial Task Cash(ulong userId)
|
public async partial Task Cash(ulong userId)
|
||||||
@@ -253,7 +326,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
if (amount <= 0 || ctx.User.Id == receiver.Id || receiver.IsBot)
|
if (amount <= 0 || ctx.User.Id == receiver.Id || receiver.IsBot)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!await _cs.TransferAsync(ctx.User.Id, receiver.Id, amount, msg))
|
if (!await _cs.TransferAsync(ctx.User.Id, receiver.Id, amount, ctx.User.ToString(), msg))
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||||
return;
|
return;
|
||||||
@@ -300,7 +373,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
|
|
||||||
await _cs.AddAsync(usr.Id,
|
await _cs.AddAsync(usr.Id,
|
||||||
amount,
|
amount,
|
||||||
new Extra("owner", "award", $"Awarded by bot owner. ({ctx.User.Username}/{ctx.User.Id}) {msg ?? ""}")
|
new TxData("award", ctx.User.ToString()!, msg, ctx.User.Id)
|
||||||
);
|
);
|
||||||
await ReplyConfirmLocalizedAsync(strs.awarded(n(amount), $"<@{usrId}>"));
|
await ReplyConfirmLocalizedAsync(strs.awarded(n(amount), $"<@{usrId}>"));
|
||||||
}
|
}
|
||||||
@@ -315,9 +388,10 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
|
|
||||||
await _cs.AddBulkAsync(users.Select(x => x.Id).ToList(),
|
await _cs.AddBulkAsync(users.Select(x => x.Id).ToList(),
|
||||||
amount,
|
amount,
|
||||||
new("owner",
|
new("award",
|
||||||
"award",
|
ctx.User.ToString()!,
|
||||||
$"Awarded by bot owner to **{role.Name}** role. ({ctx.User.Username}/{ctx.User.Id})"));
|
role.Name,
|
||||||
|
ctx.User.Id));
|
||||||
|
|
||||||
await ReplyConfirmLocalizedAsync(strs.mass_award(n(amount),
|
await ReplyConfirmLocalizedAsync(strs.mass_award(n(amount),
|
||||||
Format.Bold(users.Count.ToString()),
|
Format.Bold(users.Count.ToString()),
|
||||||
@@ -334,7 +408,10 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
|
|
||||||
await _cs.RemoveBulkAsync(users.Select(x => x.Id).ToList(),
|
await _cs.RemoveBulkAsync(users.Select(x => x.Id).ToList(),
|
||||||
amount,
|
amount,
|
||||||
new("owner", "take", $"Taken by bot owner from **{role.Name}** role. ({ctx.User.Username}/{ctx.User.Id})"));
|
new("take",
|
||||||
|
ctx.User.ToString()!,
|
||||||
|
null,
|
||||||
|
ctx.User.Id));
|
||||||
|
|
||||||
await ReplyConfirmLocalizedAsync(strs.mass_take(n(amount),
|
await ReplyConfirmLocalizedAsync(strs.mass_take(n(amount),
|
||||||
Format.Bold(users.Count.ToString()),
|
Format.Bold(users.Count.ToString()),
|
||||||
@@ -350,9 +427,12 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (await _cs.RemoveAsync(user.Id,
|
var extra = new TxData("take",
|
||||||
amount,
|
ctx.User.ToString()!,
|
||||||
new("owner", "take", $"Taken by bot owner. ({ctx.User.Username}/{ctx.User.Id})")))
|
null,
|
||||||
|
ctx.User.Id);
|
||||||
|
|
||||||
|
if (await _cs.RemoveAsync(user.Id, amount, extra))
|
||||||
await ReplyConfirmLocalizedAsync(strs.take(n(amount), Format.Bold(user.ToString())));
|
await ReplyConfirmLocalizedAsync(strs.take(n(amount), Format.Bold(user.ToString())));
|
||||||
else
|
else
|
||||||
await ReplyErrorLocalizedAsync(strs.take_fail(n(amount), Format.Bold(user.ToString()), CurrencySign));
|
await ReplyErrorLocalizedAsync(strs.take_fail(n(amount), Format.Bold(user.ToString()), CurrencySign));
|
||||||
@@ -366,9 +446,12 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (await _cs.RemoveAsync(usrId,
|
var extra = new TxData("take",
|
||||||
amount,
|
ctx.User.ToString()!,
|
||||||
new("owner", "take", $"Taken by bot owner. ({ctx.User.Username}/{ctx.User.Id})")))
|
null,
|
||||||
|
ctx.User.Id);
|
||||||
|
|
||||||
|
if (await _cs.RemoveAsync(usrId, amount, extra))
|
||||||
await ReplyConfirmLocalizedAsync(strs.take(n(amount), $"<@{usrId}>"));
|
await ReplyConfirmLocalizedAsync(strs.take(n(amount), $"<@{usrId}>"));
|
||||||
else
|
else
|
||||||
await ReplyErrorLocalizedAsync(strs.take_fail(n(amount), Format.Code(usrId.ToString()), CurrencySign));
|
await ReplyErrorLocalizedAsync(strs.take_fail(n(amount), Format.Code(usrId.ToString()), CurrencySign));
|
||||||
@@ -383,7 +466,8 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
|
|
||||||
//since the challenge is created by another user, we need to reverse the ids
|
//since the challenge is created by another user, we need to reverse the ids
|
||||||
//if it gets removed, means challenge is accepted
|
//if it gets removed, means challenge is accepted
|
||||||
if (_service.Duels.TryRemove((ctx.User.Id, u.Id), out var game)) await game.StartGame();
|
if (_service.Duels.TryRemove((ctx.User.Id, u.Id), out var game))
|
||||||
|
await game.StartGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
|
@@ -25,7 +25,7 @@ public class CurrencyService : ICurrencyService, INService
|
|||||||
public async Task AddBulkAsync(
|
public async Task AddBulkAsync(
|
||||||
IReadOnlyCollection<ulong> userIds,
|
IReadOnlyCollection<ulong> userIds,
|
||||||
long amount,
|
long amount,
|
||||||
Extra extra,
|
TxData txData,
|
||||||
CurrencyType type = CurrencyType.Default)
|
CurrencyType type = CurrencyType.Default)
|
||||||
{
|
{
|
||||||
if (type == CurrencyType.Default)
|
if (type == CurrencyType.Default)
|
||||||
@@ -34,7 +34,7 @@ public class CurrencyService : ICurrencyService, INService
|
|||||||
foreach (var userId in userIds)
|
foreach (var userId in userIds)
|
||||||
{
|
{
|
||||||
var wallet = new DefaultWallet(userId, ctx);
|
var wallet = new DefaultWallet(userId, ctx);
|
||||||
await wallet.Add(amount, extra);
|
await wallet.Add(amount, txData);
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
@@ -47,7 +47,7 @@ public class CurrencyService : ICurrencyService, INService
|
|||||||
public async Task RemoveBulkAsync(
|
public async Task RemoveBulkAsync(
|
||||||
IReadOnlyCollection<ulong> userIds,
|
IReadOnlyCollection<ulong> userIds,
|
||||||
long amount,
|
long amount,
|
||||||
Extra extra,
|
TxData txData,
|
||||||
CurrencyType type = CurrencyType.Default)
|
CurrencyType type = CurrencyType.Default)
|
||||||
{
|
{
|
||||||
if (type == CurrencyType.Default)
|
if (type == CurrencyType.Default)
|
||||||
@@ -68,54 +68,55 @@ public class CurrencyService : ICurrencyService, INService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CurrencyTransaction GetCurrencyTransaction(ulong userId, string reason, long amount)
|
private CurrencyTransaction GetCurrencyTransaction(ulong userId, string reason, long amount)
|
||||||
=> new() { Amount = amount, UserId = userId, Reason = reason ?? "-" };
|
=> new() { Amount = amount, UserId = userId, Note = reason ?? "-" };
|
||||||
|
|
||||||
public async Task AddAsync(
|
public async Task AddAsync(
|
||||||
ulong userId,
|
ulong userId,
|
||||||
long amount,
|
long amount,
|
||||||
Extra extra)
|
TxData txData)
|
||||||
{
|
{
|
||||||
await using var wallet = await GetWalletAsync(userId);
|
await using var wallet = await GetWalletAsync(userId);
|
||||||
await wallet.Add(amount, extra);
|
await wallet.Add(amount, txData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddAsync(
|
public async Task AddAsync(
|
||||||
IUser user,
|
IUser user,
|
||||||
long amount,
|
long amount,
|
||||||
Extra extra)
|
TxData txData)
|
||||||
{
|
{
|
||||||
await using var wallet = await GetWalletAsync(user.Id);
|
await using var wallet = await GetWalletAsync(user.Id);
|
||||||
await wallet.Add(amount, extra);
|
await wallet.Add(amount, txData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> RemoveAsync(
|
public async Task<bool> RemoveAsync(
|
||||||
ulong userId,
|
ulong userId,
|
||||||
long amount,
|
long amount,
|
||||||
Extra extra)
|
TxData txData)
|
||||||
{
|
{
|
||||||
await using var wallet = await GetWalletAsync(userId);
|
await using var wallet = await GetWalletAsync(userId);
|
||||||
return await wallet.Take(amount, extra);
|
return await wallet.Take(amount, txData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> RemoveAsync(
|
public async Task<bool> RemoveAsync(
|
||||||
IUser user,
|
IUser user,
|
||||||
long amount,
|
long amount,
|
||||||
Extra extra)
|
TxData txData)
|
||||||
{
|
{
|
||||||
await using var wallet = await GetWalletAsync(user.Id);
|
await using var wallet = await GetWalletAsync(user.Id);
|
||||||
return await wallet.Take(amount, extra);
|
return await wallet.Take(amount, txData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> TransferAsync(
|
public async Task<bool> TransferAsync(
|
||||||
ulong from,
|
ulong fromId,
|
||||||
ulong to,
|
ulong toId,
|
||||||
long amount,
|
long amount,
|
||||||
|
string fromName,
|
||||||
string note)
|
string note)
|
||||||
{
|
{
|
||||||
await using var fromWallet = await GetWalletAsync(@from);
|
await using var fromWallet = await GetWalletAsync(fromId);
|
||||||
await using var toWallet = await GetWalletAsync(to);
|
await using var toWallet = await GetWalletAsync(toId);
|
||||||
|
|
||||||
var extra = new Extra("transfer", "gift", note);
|
var extra = new TxData("gift", fromName, note, fromId);
|
||||||
|
|
||||||
return await fromWallet.Transfer(amount, toWallet, extra);
|
return await fromWallet.Transfer(amount, toWallet, extra);
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@ public class DefaultWallet : IWallet
|
|||||||
.Select(x => x.CurrencyAmount)
|
.Select(x => x.CurrencyAmount)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
public async Task<bool> Take(long amount, Extra extra)
|
public async Task<bool> Take(long amount, TxData txData)
|
||||||
{
|
{
|
||||||
if (amount < 0)
|
if (amount < 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(amount), "Amount to take must be non negative.");
|
throw new ArgumentOutOfRangeException(nameof(amount), "Amount to take must be non negative.");
|
||||||
@@ -39,20 +39,21 @@ public class DefaultWallet : IWallet
|
|||||||
if (changed == 0)
|
if (changed == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// todo type, subtype
|
|
||||||
// todo from? by?
|
|
||||||
await _ctx.CreateLinqToDbContext()
|
await _ctx.CreateLinqToDbContext()
|
||||||
.InsertAsync(new CurrencyTransaction()
|
.InsertAsync(new CurrencyTransaction()
|
||||||
{
|
{
|
||||||
Amount = -amount,
|
Amount = -amount,
|
||||||
Reason = extra.Note,
|
Note = txData.Note,
|
||||||
UserId = UserId,
|
UserId = UserId,
|
||||||
|
Type = txData.Type,
|
||||||
|
Extra = txData.Extra,
|
||||||
|
OtherId = txData.OtherId
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Add(long amount, Extra extra)
|
public async Task Add(long amount, TxData txData)
|
||||||
{
|
{
|
||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be greater than 0.");
|
throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be greater than 0.");
|
||||||
@@ -60,6 +61,7 @@ public class DefaultWallet : IWallet
|
|||||||
await using (var tran = await _ctx.Database.BeginTransactionAsync())
|
await using (var tran = await _ctx.Database.BeginTransactionAsync())
|
||||||
{
|
{
|
||||||
var changed = await _ctx.DiscordUser
|
var changed = await _ctx.DiscordUser
|
||||||
|
.Where(x => x.UserId == UserId)
|
||||||
.UpdateAsync(x => new()
|
.UpdateAsync(x => new()
|
||||||
{
|
{
|
||||||
CurrencyAmount = x.CurrencyAmount + amount
|
CurrencyAmount = x.CurrencyAmount + amount
|
||||||
@@ -82,8 +84,11 @@ public class DefaultWallet : IWallet
|
|||||||
var ct = new CurrencyTransaction()
|
var ct = new CurrencyTransaction()
|
||||||
{
|
{
|
||||||
Amount = amount,
|
Amount = amount,
|
||||||
Reason = extra.Note,
|
|
||||||
UserId = UserId,
|
UserId = UserId,
|
||||||
|
Note = txData.Note,
|
||||||
|
Type = txData.Type,
|
||||||
|
Extra = txData.Extra,
|
||||||
|
OtherId = txData.OtherId,
|
||||||
};
|
};
|
||||||
|
|
||||||
await _ctx.CreateLinqToDbContext()
|
await _ctx.CreateLinqToDbContext()
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
namespace NadekoBot.Services.Currency;
|
|
||||||
|
|
||||||
public record class Extra(string Type, string Subtype, string Note = "", ulong OtherId = 0);
|
|
@@ -10,38 +10,39 @@ public interface ICurrencyService
|
|||||||
Task AddBulkAsync(
|
Task AddBulkAsync(
|
||||||
IReadOnlyCollection<ulong> userIds,
|
IReadOnlyCollection<ulong> userIds,
|
||||||
long amount,
|
long amount,
|
||||||
Extra extra,
|
TxData txData,
|
||||||
CurrencyType type = CurrencyType.Default);
|
CurrencyType type = CurrencyType.Default);
|
||||||
|
|
||||||
Task RemoveBulkAsync(
|
Task RemoveBulkAsync(
|
||||||
IReadOnlyCollection<ulong> userIds,
|
IReadOnlyCollection<ulong> userIds,
|
||||||
long amount,
|
long amount,
|
||||||
Extra extra,
|
TxData txData,
|
||||||
CurrencyType type = CurrencyType.Default);
|
CurrencyType type = CurrencyType.Default);
|
||||||
|
|
||||||
Task AddAsync(
|
Task AddAsync(
|
||||||
ulong userId,
|
ulong userId,
|
||||||
long amount,
|
long amount,
|
||||||
Extra extra);
|
TxData txData);
|
||||||
|
|
||||||
Task AddAsync(
|
Task AddAsync(
|
||||||
IUser user,
|
IUser user,
|
||||||
long amount,
|
long amount,
|
||||||
Extra extra);
|
TxData txData);
|
||||||
|
|
||||||
Task<bool> RemoveAsync(
|
Task<bool> RemoveAsync(
|
||||||
ulong userId,
|
ulong userId,
|
||||||
long amount,
|
long amount,
|
||||||
Extra extra);
|
TxData txData);
|
||||||
|
|
||||||
Task<bool> RemoveAsync(
|
Task<bool> RemoveAsync(
|
||||||
IUser user,
|
IUser user,
|
||||||
long amount,
|
long amount,
|
||||||
Extra extra);
|
TxData txData);
|
||||||
|
|
||||||
Task<bool> TransferAsync(
|
Task<bool> TransferAsync(
|
||||||
ulong from,
|
ulong from,
|
||||||
ulong to,
|
ulong to,
|
||||||
long amount,
|
long amount,
|
||||||
|
string fromName,
|
||||||
string note);
|
string note);
|
||||||
}
|
}
|
@@ -5,19 +5,19 @@ public interface IWallet : IDisposable, IAsyncDisposable
|
|||||||
public ulong UserId { get; }
|
public ulong UserId { get; }
|
||||||
|
|
||||||
public Task<long> GetBalance();
|
public Task<long> GetBalance();
|
||||||
public Task<bool> Take(long amount, Extra extra);
|
public Task<bool> Take(long amount, TxData txData);
|
||||||
public Task Add(long amount, Extra extra);
|
public Task Add(long amount, TxData txData);
|
||||||
|
|
||||||
public async Task<bool> Transfer(
|
public async Task<bool> Transfer(
|
||||||
long amount,
|
long amount,
|
||||||
IWallet to,
|
IWallet to,
|
||||||
Extra extra)
|
TxData txData)
|
||||||
{
|
{
|
||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be greater than 0.");
|
throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be greater than 0.");
|
||||||
|
|
||||||
var succ = await Take(amount,
|
var succ = await Take(amount,
|
||||||
extra with
|
txData with
|
||||||
{
|
{
|
||||||
OtherId = to.UserId
|
OtherId = to.UserId
|
||||||
});
|
});
|
||||||
@@ -26,7 +26,7 @@ public interface IWallet : IDisposable, IAsyncDisposable
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
await to.Add(amount,
|
await to.Add(amount,
|
||||||
extra with
|
txData with
|
||||||
{
|
{
|
||||||
OtherId = UserId
|
OtherId = UserId
|
||||||
});
|
});
|
||||||
|
3
src/NadekoBot/Services/Currency/TxData.cs
Normal file
3
src/NadekoBot/Services/Currency/TxData.cs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
namespace NadekoBot.Services.Currency;
|
||||||
|
|
||||||
|
public record class TxData(string Type, string Extra, string? Note = "", ulong? OtherId = null);
|
@@ -11,6 +11,9 @@ public static class Extensions
|
|||||||
private static readonly Regex _urlRegex =
|
private static readonly Regex _urlRegex =
|
||||||
new(@"^(https?|ftp)://(?<path>[^\s/$.?#].[^\s]*)$", RegexOptions.Compiled);
|
new(@"^(https?|ftp)://(?<path>[^\s/$.?#].[^\s]*)$", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
public static IEmbedBuilder WithAuthor(this IEmbedBuilder eb, IUser author)
|
||||||
|
=> eb.WithAuthor(author.ToString(), author.RealAvatarUrl().ToString());
|
||||||
|
|
||||||
public static Task EditAsync(this IUserMessage msg, SmartText text)
|
public static Task EditAsync(this IUserMessage msg, SmartText text)
|
||||||
=> text switch
|
=> text switch
|
||||||
{
|
{
|
||||||
|
@@ -674,6 +674,8 @@ cash:
|
|||||||
- cur
|
- cur
|
||||||
currencytransactions:
|
currencytransactions:
|
||||||
- curtrs
|
- curtrs
|
||||||
|
currencytransaction:
|
||||||
|
- curtr
|
||||||
listperms:
|
listperms:
|
||||||
- listperms
|
- listperms
|
||||||
- lp
|
- lp
|
||||||
|
@@ -1168,6 +1168,10 @@ currencytransactions:
|
|||||||
args:
|
args:
|
||||||
- "2"
|
- "2"
|
||||||
- "@SomeUser 2"
|
- "@SomeUser 2"
|
||||||
|
currencytransaction:
|
||||||
|
desc: "Shows full details about a currency transaction with the specified ID. You can only check your own transactions."
|
||||||
|
args:
|
||||||
|
- "3yvd"
|
||||||
listperms:
|
listperms:
|
||||||
desc: "Lists whole permission chain with their indexes. You can specify an optional page number if there are a lot of permissions."
|
desc: "Lists whole permission chain with their indexes. You can specify an optional page number if there are a lot of permissions."
|
||||||
args:
|
args:
|
||||||
|
@@ -242,6 +242,7 @@
|
|||||||
"take": "successfully took {0} from {1}",
|
"take": "successfully took {0} from {1}",
|
||||||
"take_fail": "was unable to take {0} from {1} because the user doesn't have that much {2}!",
|
"take_fail": "was unable to take {0} from {1} because the user doesn't have that much {2}!",
|
||||||
"transactions": "Transactions of user {0}",
|
"transactions": "Transactions of user {0}",
|
||||||
|
"transaction": "Currency Transaction",
|
||||||
"commandlist_regen": "Commandlist regenerated.",
|
"commandlist_regen": "Commandlist regenerated.",
|
||||||
"commands_instr": "Type `{0}h CommandName` to see the help for that specified command. e.g. `{0}h {0}8ball`",
|
"commands_instr": "Type `{0}h CommandName` to see the help for that specified command. e.g. `{0}h {0}8ball`",
|
||||||
"command_not_found": "I can't find that command. Please verify that the command exists before trying again.",
|
"command_not_found": "I can't find that command. Please verify that the command exists before trying again.",
|
||||||
@@ -967,5 +968,8 @@
|
|||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
"imageonly_enable": "This channel is now image-only.",
|
"imageonly_enable": "This channel is now image-only.",
|
||||||
"imageonly_disable": "This channel is no longer image-only.",
|
"imageonly_disable": "This channel is no longer image-only.",
|
||||||
"deleted_x_servers": "Deleted {0} servers."
|
"deleted_x_servers": "Deleted {0} servers.",
|
||||||
|
"curtr_gift": "Gift from {0} [{1}]",
|
||||||
|
"curtr_award": "Awarded by bot owner {0} [{1}]",
|
||||||
|
"curtr_take": "Taken by bot owner {0} [{1}]"
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user