xp almost fully reimplemented
This commit is contained in:
parent
96c4ea0637
commit
5d9326b65e
5 changed files with 236 additions and 183 deletions
src/EllieBot
Db
Modules/Xp
Services/GrpcApi
|
@ -89,9 +89,9 @@ public sealed class EllieDbService : DbService
|
||||||
var applied = await ctx.Database.GetAppliedMigrationsAsync();
|
var applied = await ctx.Database.GetAppliedMigrationsAsync();
|
||||||
|
|
||||||
// get all .sql file names from the migrations folder
|
// get all .sql file names from the migrations folder
|
||||||
var available = Directory.GetFiles("Migrations/Sqlite", "*_*.sql")
|
var available = Directory.GetFiles("Migrations/" + GetMigrationDirectory(ctx.Database), "*_*.sql")
|
||||||
.Select(x => Path.GetFileNameWithoutExtension(x))
|
.Select(x => Path.GetFileNameWithoutExtension(x))
|
||||||
.OrderBy(x => x);
|
.OrderBy(x => x);
|
||||||
|
|
||||||
var lastApplied = applied.Last();
|
var lastApplied = applied.Last();
|
||||||
Log.Information("Last applied migration: {LastApplied}", lastApplied);
|
Log.Information("Last applied migration: {LastApplied}", lastApplied);
|
||||||
|
@ -112,12 +112,17 @@ public sealed class EllieDbService : DbService
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetMigrationPath(DatabaseFacade ctxDatabase, string runnable)
|
private static string GetMigrationPath(DatabaseFacade ctxDatabase, string runnable)
|
||||||
|
{
|
||||||
|
return $"Migrations/{GetMigrationDirectory(ctxDatabase)}/{runnable}.sql";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetMigrationDirectory(DatabaseFacade ctxDatabase)
|
||||||
{
|
{
|
||||||
if (ctxDatabase.IsSqlite())
|
if (ctxDatabase.IsSqlite())
|
||||||
return $"Migrations/Sqlite/{runnable}.sql";
|
return "Sqlite";
|
||||||
|
|
||||||
if (ctxDatabase.IsNpgsql())
|
if (ctxDatabase.IsNpgsql())
|
||||||
return $"Migrations/PostgreSql/{runnable}.sql";
|
return "PostgreSql";
|
||||||
|
|
||||||
throw new NotSupportedException("This database type is not supported.");
|
throw new NotSupportedException("This database type is not supported.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,8 @@ public static class GuildConfigExtensions
|
||||||
public static async Task<StreamRoleSettings> GetOrCreateStreamRoleSettings(this DbContext ctx, ulong guildId)
|
public static async Task<StreamRoleSettings> GetOrCreateStreamRoleSettings(this DbContext ctx, ulong guildId)
|
||||||
{
|
{
|
||||||
var srs = await ctx.GetTable<StreamRoleSettings>()
|
var srs = await ctx.GetTable<StreamRoleSettings>()
|
||||||
.Where(x => x.GuildId == guildId)
|
.Where(x => x.GuildId == guildId)
|
||||||
.FirstOrDefaultAsyncEF();
|
.FirstOrDefaultAsyncEF();
|
||||||
|
|
||||||
if (srs is not null)
|
if (srs is not null)
|
||||||
return srs;
|
return srs;
|
||||||
|
@ -52,18 +52,18 @@ public static class GuildConfigExtensions
|
||||||
public static LogSetting LogSettingsFor(this DbContext ctx, ulong guildId)
|
public static LogSetting LogSettingsFor(this DbContext ctx, ulong guildId)
|
||||||
{
|
{
|
||||||
var logSetting = ctx.Set<LogSetting>()
|
var logSetting = ctx.Set<LogSetting>()
|
||||||
.AsQueryable()
|
.AsQueryable()
|
||||||
.Include(x => x.LogIgnores)
|
.Include(x => x.LogIgnores)
|
||||||
.Where(x => x.GuildId == guildId)
|
.Where(x => x.GuildId == guildId)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (logSetting is null)
|
if (logSetting is null)
|
||||||
{
|
{
|
||||||
ctx.Set<LogSetting>()
|
ctx.Set<LogSetting>()
|
||||||
.Add(logSetting = new()
|
.Add(logSetting = new()
|
||||||
{
|
{
|
||||||
GuildId = guildId
|
GuildId = guildId
|
||||||
});
|
});
|
||||||
ctx.SaveChanges();
|
ctx.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,6 @@ public static class GuildConfigExtensions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static IEnumerable<GuildConfig> PermissionsForAll(this DbSet<GuildConfig> configs, List<ulong> include)
|
public static IEnumerable<GuildConfig> PermissionsForAll(this DbSet<GuildConfig> configs, List<ulong> include)
|
||||||
{
|
{
|
||||||
var query = configs.AsQueryable().Where(x => include.Contains(x.GuildId)).Include(gc => gc.Permissions);
|
var query = configs.AsQueryable().Where(x => include.Contains(x.GuildId)).Include(gc => gc.Permissions);
|
||||||
|
@ -82,19 +81,19 @@ public static class GuildConfigExtensions
|
||||||
public static GuildConfig GcWithPermissionsFor(this DbContext ctx, ulong guildId)
|
public static GuildConfig GcWithPermissionsFor(this DbContext ctx, ulong guildId)
|
||||||
{
|
{
|
||||||
var config = ctx.Set<GuildConfig>()
|
var config = ctx.Set<GuildConfig>()
|
||||||
.AsQueryable()
|
.AsQueryable()
|
||||||
.Where(gc => gc.GuildId == guildId)
|
.Where(gc => gc.GuildId == guildId)
|
||||||
.Include(gc => gc.Permissions)
|
.Include(gc => gc.Permissions)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (config is null) // if there is no guildconfig, create new one
|
if (config is null) // if there is no guildconfig, create new one
|
||||||
{
|
{
|
||||||
ctx.Set<GuildConfig>()
|
ctx.Set<GuildConfig>()
|
||||||
.Add(config = new()
|
.Add(config = new()
|
||||||
{
|
{
|
||||||
GuildId = guildId,
|
GuildId = guildId,
|
||||||
Permissions = Permissionv2.GetDefaultPermlist
|
Permissions = Permissionv2.GetDefaultPermlist
|
||||||
});
|
});
|
||||||
ctx.SaveChanges();
|
ctx.SaveChanges();
|
||||||
}
|
}
|
||||||
else if (config.Permissions is null || !config.Permissions.Any()) // if no perms, add default ones
|
else if (config.Permissions is null || !config.Permissions.Any()) // if no perms, add default ones
|
||||||
|
@ -106,20 +105,24 @@ public static class GuildConfigExtensions
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<XpSettings> XpSettingsFor(this DbContext ctx, ulong guildId)
|
public static async Task<XpSettings> XpSettingsFor(this DbContext ctx, ulong guildId,
|
||||||
|
Func<IQueryable<XpSettings>, IQueryable<XpSettings>> includes = default)
|
||||||
{
|
{
|
||||||
var srs = await ctx.GetTable<XpSettings>()
|
includes ??= static set => set;
|
||||||
.Where(x => x.GuildId == guildId)
|
|
||||||
.FirstOrDefaultAsyncLinqToDB();
|
var srs = await includes(ctx.GetTable<XpSettings>()
|
||||||
|
.Where(x => x.GuildId == guildId))
|
||||||
|
.FirstOrDefaultAsyncLinqToDB();
|
||||||
|
|
||||||
if (srs is not null)
|
if (srs is not null)
|
||||||
return srs;
|
return srs;
|
||||||
|
|
||||||
srs = await ctx.GetTable<XpSettings>()
|
srs = await ctx.GetTable<XpSettings>()
|
||||||
.InsertWithOutputAsync(() => new()
|
.InsertWithOutputAsync(() => new()
|
||||||
{
|
{
|
||||||
GuildId = guildId,
|
GuildId = guildId,
|
||||||
});
|
ServerExcluded = false,
|
||||||
|
});
|
||||||
|
|
||||||
return srs;
|
return srs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@ public partial class Xp
|
||||||
public async Task XpRewsReset()
|
public async Task XpRewsReset()
|
||||||
{
|
{
|
||||||
var promptEmbed = CreateEmbed()
|
var promptEmbed = CreateEmbed()
|
||||||
.WithPendingColor()
|
.WithPendingColor()
|
||||||
.WithDescription(GetText(strs.xprewsreset_confirm));
|
.WithDescription(GetText(strs.xprewsreset_confirm));
|
||||||
|
|
||||||
var reply = await PromptUserConfirmAsync(promptEmbed);
|
var reply = await PromptUserConfirmAsync(promptEmbed);
|
||||||
|
|
||||||
|
@ -38,57 +38,59 @@ public partial class Xp
|
||||||
if (page is < 0 or > 100)
|
if (page is < 0 or > 100)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var rews = await _service.GetRoleRewardsAsync(ctx.Guild.Id);
|
var xpSettings = await _service.GetFullXpSettingsFor(ctx.Guild.Id);
|
||||||
|
var rews = xpSettings.RoleRewards;
|
||||||
|
|
||||||
var allRewards = rews.OrderBy(x => x.Level)
|
var allRewards = rews.OrderBy(x => x.Level)
|
||||||
.Select(x =>
|
.Select(x =>
|
||||||
{
|
{
|
||||||
var sign = !x.Remove ? "✅ " : "❌ ";
|
var sign = !x.Remove ? "✅ " : "❌ ";
|
||||||
|
|
||||||
var str = ctx.Guild.GetRole(x.RoleId)?.ToString();
|
var str = ctx.Guild.GetRole(x.RoleId)?.ToString();
|
||||||
|
|
||||||
if (str is null)
|
if (str is null)
|
||||||
{
|
{
|
||||||
str = GetText(strs.role_not_found(Format.Code(x.RoleId.ToString())));
|
str = GetText(strs.role_not_found(Format.Code(x.RoleId.ToString())));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!x.Remove)
|
if (!x.Remove)
|
||||||
str = GetText(strs.xp_receive_role(Format.Bold(str)));
|
str = GetText(strs.xp_receive_role(Format.Bold(str)));
|
||||||
else
|
else
|
||||||
str = GetText(strs.xp_lose_role(Format.Bold(str)));
|
str = GetText(strs.xp_lose_role(Format.Bold(str)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (x.Level, Text: sign + str);
|
return (x.Level, Text: sign + str);
|
||||||
})
|
})
|
||||||
.Concat((await _service.GetCurrencyRewardsAsync(ctx.Guild.Id))
|
.Concat(xpSettings.CurrencyRewards
|
||||||
.OrderBy(x => x.Level)
|
.OrderBy(x => x.Level)
|
||||||
.Select(x => (x.Level,
|
.Select(x => (x.Level,
|
||||||
Format.Bold(x.Amount + _cp.GetCurrencySign()))))
|
Format.Bold(x.Amount + _cp.GetCurrencySign()))))
|
||||||
.GroupBy(x => x.Level)
|
.GroupBy(x => x.Level)
|
||||||
.OrderBy(x => x.Key)
|
.OrderBy(x => x.Key)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Paginated()
|
.Paginated()
|
||||||
.Items(allRewards)
|
.Items(allRewards)
|
||||||
.PageSize(9)
|
.PageSize(9)
|
||||||
.CurrentPage(page)
|
.CurrentPage(page)
|
||||||
.Page((items, _) =>
|
.Page((items, _) =>
|
||||||
{
|
{
|
||||||
var embed = CreateEmbed().WithTitle(GetText(strs.level_up_rewards)).WithOkColor();
|
var embed = CreateEmbed().WithTitle(GetText(strs.level_up_rewards)).WithOkColor();
|
||||||
|
|
||||||
if (!items.Any())
|
if (!items.Any())
|
||||||
return embed.WithDescription(GetText(strs.no_level_up_rewards));
|
return embed.WithDescription(GetText(strs.no_level_up_rewards));
|
||||||
|
|
||||||
foreach (var reward in items)
|
foreach (var reward in items)
|
||||||
{
|
{
|
||||||
embed.AddField(GetText(strs.level_x(reward.Key)),
|
embed.AddField(GetText(strs.level_x(reward.Key)),
|
||||||
string.Join("\n", reward.Select(y => y.Item2)));
|
string.Join("\n", reward.Select(y => y.Item2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return embed;
|
return embed;
|
||||||
})
|
})
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
|
@ -120,9 +122,9 @@ public partial class Xp
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await Response()
|
await Response()
|
||||||
.Confirm(strs.xp_role_reward_remove_role(Format.Bold(level.ToString()),
|
.Confirm(strs.xp_role_reward_remove_role(Format.Bold(level.ToString()),
|
||||||
Format.Bold(role.ToString())))
|
Format.Bold(role.ToString())))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,9 +144,9 @@ public partial class Xp
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await Response()
|
await Response()
|
||||||
.Confirm(strs.cur_reward_added(level,
|
.Confirm(strs.cur_reward_added(level,
|
||||||
Format.Bold(amount + _cp.GetCurrencySign())))
|
Format.Bold(amount + _cp.GetCurrencySign())))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,11 +46,12 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
private readonly IPatronageService _ps;
|
private readonly IPatronageService _ps;
|
||||||
private readonly IBotCache _c;
|
private readonly IBotCache _c;
|
||||||
|
|
||||||
|
|
||||||
private readonly Channel<UserXpGainData> _xpGainQueue = Channel.CreateUnbounded<UserXpGainData>();
|
private readonly Channel<UserXpGainData> _xpGainQueue = Channel.CreateUnbounded<UserXpGainData>();
|
||||||
private readonly INotifySubscriber _notifySub;
|
private readonly INotifySubscriber _notifySub;
|
||||||
private readonly ShardData _shardData;
|
private readonly ShardData _shardData;
|
||||||
|
|
||||||
|
private readonly QueueRunner _levelUpQueue = new QueueRunner(0, 100);
|
||||||
|
|
||||||
public XpService(
|
public XpService(
|
||||||
DiscordSocketClient client,
|
DiscordSocketClient client,
|
||||||
DbService db,
|
DbService db,
|
||||||
|
@ -109,7 +110,8 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
public async Task OnReadyAsync()
|
public async Task OnReadyAsync()
|
||||||
{
|
{
|
||||||
// initialize ignored
|
// initialize ignored
|
||||||
ArgumentOutOfRangeException.ThrowIfLessThan(_xpConfig.Data.MessageXpCooldown, 1,
|
ArgumentOutOfRangeException.ThrowIfLessThan(_xpConfig.Data.MessageXpCooldown,
|
||||||
|
1,
|
||||||
nameof(_xpConfig.Data.MessageXpCooldown));
|
nameof(_xpConfig.Data.MessageXpCooldown));
|
||||||
|
|
||||||
await using (var ctx = _db.GetDbContext())
|
await using (var ctx = _db.GetDbContext())
|
||||||
|
@ -134,27 +136,25 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.WhenAll(UpdateTimer(), PeriodClearTimer());
|
await Task.WhenAll(UpdateTimer(), _levelUpQueue.RunAsync());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
async Task PeriodClearTimer()
|
|
||||||
{
|
|
||||||
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(_xpConfig.Data.MessageXpCooldown));
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
await timer.WaitForNextTickAsync();
|
|
||||||
_usersGainedInPeriod.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task UpdateTimer()
|
async Task UpdateTimer()
|
||||||
{
|
{
|
||||||
// todo a bigger loop that runs once every XpTimer
|
// todo a bigger loop that runs once every XpTimer
|
||||||
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(3));
|
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(3));
|
||||||
while (await timer.WaitForNextTickAsync())
|
while (await timer.WaitForNextTickAsync())
|
||||||
{
|
{
|
||||||
await UpdateXp();
|
try
|
||||||
|
{
|
||||||
|
await UpdateXp();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Error updating xp");
|
||||||
|
await Task.Delay(30_000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,33 +170,80 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
var currentBatch = _usersBatch.ToArray();
|
var currentBatch = _usersBatch.ToArray();
|
||||||
_usersBatch.Clear();
|
_usersBatch.Clear();
|
||||||
|
|
||||||
|
if (currentBatch.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ids = currentBatch.Select(x => x.Id).ToArray();
|
||||||
|
|
||||||
await using var ctx = _db.GetDbContext();
|
await using var ctx = _db.GetDbContext();
|
||||||
await using var lctx = ctx.CreateLinqToDBConnection();
|
await using var lctx = ctx.CreateLinqToDBConnection();
|
||||||
|
|
||||||
await using var batchTable = await lctx.CreateTempTableAsync<UserXpBatch>();
|
var tempTableName = "xp_batch_" + _shardData.ShardId;
|
||||||
|
await using var batchTable = await lctx.CreateTempTableAsync<UserXpBatch>(tempTableName);
|
||||||
|
|
||||||
await batchTable.BulkCopyAsync(currentBatch.Select(x => new UserXpBatch()
|
await batchTable.BulkCopyAsync(currentBatch.Select(x => new UserXpBatch()
|
||||||
{
|
{
|
||||||
GuildId = x.GuildId,
|
GuildId = x.GuildId,
|
||||||
UserId = x.Id,
|
UserId = x.Id,
|
||||||
UserName = x.Username,
|
Username = x.Username,
|
||||||
AvatarId = x.DisplayAvatarId,
|
AvatarId = x.DisplayAvatarId
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await lctx.ExecuteAsync(
|
await lctx.ExecuteAsync(
|
||||||
$"""
|
$"""
|
||||||
INSERT INTO ${nameof(DiscordUser)}
|
INSERT INTO UserXpStats (GuildId, UserId, Xp)
|
||||||
SELECT ${nameof(UserXpBatch.GuildId)}, ${nameof(UserXpBatch.UserId)}, ${nameof(UserXpBatch.UserName)}, ${nameof(UserXpBatch.AvatarId)}, ${xpAmount}
|
SELECT "{tempTableName}"."GuildId", "{tempTableName}"."UserId", {xpAmount}
|
||||||
FROM ${nameof(UserXpBatch)}
|
FROM {tempTableName}
|
||||||
ON CONFLICT(${nameof(DiscordUser.UserId)}, ${nameof(DiscordUser.UserId)}) DO UPDATE SET
|
WHERE TRUE
|
||||||
${nameof(DiscordUser.Username)} = ${nameof(UserXpBatch)}.${nameof(UserXpBatch.UserName)}
|
ON CONFLICT (GuildId, UserId) DO UPDATE
|
||||||
${nameof(DiscordUser.AvatarId)} = ${nameof(UserXpBatch)}.${nameof(UserXpBatch.AvatarId)}
|
SET
|
||||||
${nameof(DiscordUser.TotalXp)} = ${nameof(DiscordUser.TotalXp)} + ${xpAmount}
|
Xp = UserXpStats.Xp + EXCLUDED.Xp;
|
||||||
RETURNING *;
|
|
||||||
""");
|
""");
|
||||||
|
|
||||||
// todo send notifications
|
await lctx.ExecuteAsync(
|
||||||
|
$"""
|
||||||
|
INSERT INTO DiscordUser (UserId, AvatarId, Username, TotalXp)
|
||||||
|
SELECT "{tempTableName}"."UserId", "{tempTableName}"."AvatarId", "{tempTableName}"."Username", {xpAmount}
|
||||||
|
FROM {tempTableName}
|
||||||
|
WHERE TRUE
|
||||||
|
ON CONFLICT (UserId) DO UPDATE
|
||||||
|
SET
|
||||||
|
Username = EXCLUDED.Username,
|
||||||
|
AvatarId = EXCLUDED.AvatarId,
|
||||||
|
TotalXp = DiscordUser.TotalXp + {xpAmount};
|
||||||
|
""");
|
||||||
|
|
||||||
|
foreach (var (guildId, users) in currentBatch.GroupBy(x => x.GuildId)
|
||||||
|
.ToDictionary(x => x.Key, x => x.AsEnumerable()))
|
||||||
|
{
|
||||||
|
var userIds = users.Select(x => x.Id).ToArray();
|
||||||
|
|
||||||
|
var dbStats = await ctx.GetTable<UserXpStats>()
|
||||||
|
.Where(x => x.GuildId == guildId && userIds.Contains(x.UserId))
|
||||||
|
.OrderByDescending(x => x.Xp)
|
||||||
|
.ToArrayAsyncLinqToDB();
|
||||||
|
|
||||||
|
for (var i = 0; i < dbStats.Length; i++)
|
||||||
|
{
|
||||||
|
var oldStats = new LevelStats(dbStats[i].Xp - xpAmount);
|
||||||
|
var newStats = new LevelStats(dbStats[i].Xp);
|
||||||
|
|
||||||
|
Log.Information("User {User} xp updated from {OldLevel} to {NewLevel}",
|
||||||
|
dbStats[i].UserId,
|
||||||
|
oldStats.TotalXp,
|
||||||
|
newStats.TotalXp);
|
||||||
|
|
||||||
|
if (oldStats.Level < newStats.Level)
|
||||||
|
{
|
||||||
|
await _levelUpQueue.EnqueueAsync(NotifyUser(guildId,
|
||||||
|
0,
|
||||||
|
dbStats[i].UserId,
|
||||||
|
true,
|
||||||
|
oldStats.Level,
|
||||||
|
newStats.Level));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Func<Task> NotifyUser(
|
private Func<Task> NotifyUser(
|
||||||
|
@ -222,13 +269,9 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
long oldLevel,
|
long oldLevel,
|
||||||
long newLevel)
|
long newLevel)
|
||||||
{
|
{
|
||||||
List<XpRoleReward> rrews;
|
var settings = await GetFullXpSettingsFor(guildId);
|
||||||
List<XpCurrencyReward> crews;
|
var rrews = settings.RoleRewards;
|
||||||
await using (var ctx = _db.GetDbContext())
|
var crews = settings.CurrencyRewards;
|
||||||
{
|
|
||||||
rrews = await ctx.XpSettingsFor(guildId).Fmap(x => x.RoleRewards.ToList());
|
|
||||||
crews = await ctx.XpSettingsFor(guildId).Fmap(x => x.CurrencyRewards.ToList());
|
|
||||||
}
|
|
||||||
|
|
||||||
//loop through levels since last level up, so if a high amount of xp is gained, reward are still applied.
|
//loop through levels since last level up, so if a high amount of xp is gained, reward are still applied.
|
||||||
for (var i = oldLevel + 1; i <= newLevel; i++)
|
for (var i = oldLevel + 1; i <= newLevel; i++)
|
||||||
|
@ -369,7 +412,7 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
public async Task SetCurrencyReward(ulong guildId, int level, int amount)
|
public async Task SetCurrencyReward(ulong guildId, int level, int amount)
|
||||||
{
|
{
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
var settings = await uow.XpSettingsFor(guildId);
|
var settings = await uow.XpSettingsFor(guildId, set => set.LoadWith(x => x.CurrencyRewards));
|
||||||
|
|
||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
{
|
{
|
||||||
|
@ -399,22 +442,19 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
uow.SaveChanges();
|
uow.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<XpCurrencyReward>> GetCurrencyRewardsAsync(ulong id)
|
public async Task<XpSettings> GetFullXpSettingsFor(ulong guildId)
|
||||||
{
|
{
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
return (await uow.XpSettingsFor(id)).CurrencyRewards.ToArray();
|
return await uow.XpSettingsFor(guildId,
|
||||||
}
|
set => set
|
||||||
|
.LoadWith(x => x.CurrencyRewards)
|
||||||
public async Task<IEnumerable<XpRoleReward>> GetRoleRewardsAsync(ulong id)
|
.LoadWith(x => x.RoleRewards));
|
||||||
{
|
|
||||||
await using var uow = _db.GetDbContext();
|
|
||||||
return (await uow.XpSettingsFor(id)).RoleRewards.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ResetRoleRewardAsync(ulong guildId, int level)
|
public async Task ResetRoleRewardAsync(ulong guildId, int level)
|
||||||
{
|
{
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
var settings = await uow.XpSettingsFor(guildId);
|
var settings = await uow.XpSettingsFor(guildId, set => set.LoadWith(x => x.RoleRewards));
|
||||||
|
|
||||||
var toRemove = settings.RoleRewards.FirstOrDefault(x => x.Level == level);
|
var toRemove = settings.RoleRewards.FirstOrDefault(x => x.Level == level);
|
||||||
if (toRemove is not null)
|
if (toRemove is not null)
|
||||||
|
@ -423,7 +463,7 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
settings.RoleRewards.Remove(toRemove);
|
settings.RoleRewards.Remove(toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
uow.SaveChanges();
|
await uow.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetRoleRewardAsync(
|
public async Task SetRoleRewardAsync(
|
||||||
|
@ -433,7 +473,7 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
bool remove)
|
bool remove)
|
||||||
{
|
{
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
var settings = await uow.XpSettingsFor(guildId);
|
var settings = await uow.XpSettingsFor(guildId, set => set.LoadWith(x => x.RoleRewards));
|
||||||
|
|
||||||
var rew = settings.RoleRewards.FirstOrDefault(x => x.Level == level);
|
var rew = settings.RoleRewards.FirstOrDefault(x => x.Level == level);
|
||||||
|
|
||||||
|
@ -806,7 +846,7 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
{
|
{
|
||||||
var roles = _excludedRoles.GetOrAdd(guildId, _ => new());
|
var roles = _excludedRoles.GetOrAdd(guildId, _ => new());
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
var xpSetting = await uow.XpSettingsFor(guildId);
|
var xpSetting = await uow.XpSettingsFor(guildId, set => set.LoadWith(x => x.ExclusionList));
|
||||||
var excludeObj = new ExcludedItem
|
var excludeObj = new ExcludedItem
|
||||||
{
|
{
|
||||||
ItemId = rId,
|
ItemId = rId,
|
||||||
|
@ -837,7 +877,7 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
{
|
{
|
||||||
var channels = _excludedChannels.GetOrAdd(guildId, _ => new());
|
var channels = _excludedChannels.GetOrAdd(guildId, _ => new());
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
var xpSetting = await uow.XpSettingsFor(guildId);
|
var xpSetting = await uow.XpSettingsFor(guildId, set => set.LoadWith(x => x.ExclusionList));
|
||||||
var excludeObj = new ExcludedItem
|
var excludeObj = new ExcludedItem
|
||||||
{
|
{
|
||||||
ItemId = chId,
|
ItemId = chId,
|
||||||
|
@ -1510,8 +1550,10 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
|
|
||||||
public sealed class UserXpBatch
|
public sealed class UserXpBatch
|
||||||
{
|
{
|
||||||
[Key] public ulong UserId { get; set; }
|
[Key]
|
||||||
|
public ulong UserId { get; set; }
|
||||||
|
|
||||||
public ulong GuildId { get; set; }
|
public ulong GuildId { get; set; }
|
||||||
public string UserName { get; set; } = string.Empty;
|
public string Username { get; set; } = string.Empty;
|
||||||
public string AvatarId { get; set; } = string.Empty;
|
public string AvatarId { get; set; } = string.Empty;
|
||||||
}
|
}
|
|
@ -42,22 +42,23 @@ public class XpSvc : GrpcXp.GrpcXpBase, IGrpcSvc, IEService
|
||||||
var reply = new GetXpSettingsReply();
|
var reply = new GetXpSettingsReply();
|
||||||
|
|
||||||
reply.Exclusions.AddRange(excludedChannels
|
reply.Exclusions.AddRange(excludedChannels
|
||||||
.Select(x => new ExclItemReply()
|
.Select(x => new ExclItemReply()
|
||||||
{
|
{
|
||||||
Id = x,
|
Id = x,
|
||||||
Type = "Channel",
|
Type = "Channel",
|
||||||
Name = guild.GetChannel(x)?.Name ?? "????"
|
Name = guild.GetChannel(x)?.Name ?? "????"
|
||||||
})
|
})
|
||||||
.Concat(excludedRoles
|
.Concat(excludedRoles
|
||||||
.Select(x => new ExclItemReply()
|
.Select(x => new ExclItemReply()
|
||||||
{
|
{
|
||||||
Id = x,
|
Id = x,
|
||||||
Type = "Role",
|
Type = "Role",
|
||||||
Name = guild.GetRole(x)?.Name ?? "????"
|
Name = guild.GetRole(x)?.Name ?? "????"
|
||||||
})));
|
})));
|
||||||
|
|
||||||
var curRews = await _xp.GetCurrencyRewardsAsync(request.GuildId);
|
var settings = await _xp.GetFullXpSettingsFor(request.GuildId);
|
||||||
var roleRews = await _xp.GetRoleRewardsAsync(request.GuildId);
|
var curRews = settings.CurrencyRewards;
|
||||||
|
var roleRews = settings.RoleRewards;
|
||||||
|
|
||||||
var rews = curRews.Select(x => new RewItemReply()
|
var rews = curRews.Select(x => new RewItemReply()
|
||||||
{
|
{
|
||||||
|
@ -72,7 +73,7 @@ public class XpSvc : GrpcXp.GrpcXpBase, IGrpcSvc, IEService
|
||||||
Type = x.Remove ? "RemoveRole" : "AddRole",
|
Type = x.Remove ? "RemoveRole" : "AddRole",
|
||||||
Value = guild.GetRole(x.RoleId)?.ToString() ?? x.RoleId.ToString()
|
Value = guild.GetRole(x.RoleId)?.ToString() ?? x.RoleId.ToString()
|
||||||
}))
|
}))
|
||||||
.OrderBy(x => x.Level);
|
.OrderBy(x => x.Level);
|
||||||
|
|
||||||
reply.Rewards.AddRange(rews);
|
reply.Rewards.AddRange(rews);
|
||||||
|
|
||||||
|
@ -224,43 +225,43 @@ public class XpSvc : GrpcXp.GrpcXpBase, IGrpcSvc, IEService
|
||||||
};
|
};
|
||||||
|
|
||||||
var users = await data
|
var users = await data
|
||||||
.Select(async x =>
|
.Select(async x =>
|
||||||
{
|
{
|
||||||
var user = guild.GetUser(x.UserId);
|
var user = guild.GetUser(x.UserId);
|
||||||
|
|
||||||
if (user is null)
|
if (user is null)
|
||||||
{
|
{
|
||||||
var du = await _duSvc.GetUserAsync(x.UserId);
|
var du = await _duSvc.GetUserAsync(x.UserId);
|
||||||
if (du is null)
|
if (du is null)
|
||||||
return new XpLbUserReply
|
return new XpLbUserReply
|
||||||
{
|
{
|
||||||
UserId = x.UserId,
|
UserId = x.UserId,
|
||||||
Avatar = string.Empty,
|
Avatar = string.Empty,
|
||||||
Username = string.Empty,
|
Username = string.Empty,
|
||||||
Xp = x.Xp,
|
Xp = x.Xp,
|
||||||
Level = new LevelStats(x.Xp).Level
|
Level = new LevelStats(x.Xp).Level
|
||||||
};
|
};
|
||||||
|
|
||||||
return new XpLbUserReply()
|
return new XpLbUserReply()
|
||||||
{
|
{
|
||||||
UserId = x.UserId,
|
UserId = x.UserId,
|
||||||
Avatar = du.RealAvatarUrl()?.ToString() ?? string.Empty,
|
Avatar = du.RealAvatarUrl()?.ToString() ?? string.Empty,
|
||||||
Username = du.ToString() ?? string.Empty,
|
Username = du.ToString() ?? string.Empty,
|
||||||
Xp = x.Xp,
|
Xp = x.Xp,
|
||||||
Level = new LevelStats(x.Xp).Level
|
Level = new LevelStats(x.Xp).Level
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return new XpLbUserReply
|
return new XpLbUserReply
|
||||||
{
|
{
|
||||||
UserId = x.UserId,
|
UserId = x.UserId,
|
||||||
Avatar = user?.GetAvatarUrl() ?? string.Empty,
|
Avatar = user?.GetAvatarUrl() ?? string.Empty,
|
||||||
Username = user?.ToString() ?? string.Empty,
|
Username = user?.ToString() ?? string.Empty,
|
||||||
Xp = x.Xp,
|
Xp = x.Xp,
|
||||||
Level = new LevelStats(x.Xp).Level
|
Level = new LevelStats(x.Xp).Level
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.WhenAll();
|
.WhenAll();
|
||||||
|
|
||||||
reply.Users.AddRange(users);
|
reply.Users.AddRange(users);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue