mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-03 16:24:27 -05:00 
			
		
		
		
	Fixed .crypto - some extra fields which were causing deserialization issues
This commit is contained in:
		@@ -57,6 +57,6 @@ public sealed class QueueRunner
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    public ValueTask Enqueue(Func<Task> action)
 | 
					    public ValueTask EnqueueAsync(Func<Task> action)
 | 
				
			||||||
        => _channel.Writer.WriteAsync(action);
 | 
					        => _channel.Writer.WriteAsync(action);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -138,9 +138,9 @@ public class CryptoService : INService
 | 
				
			|||||||
                        using var http = _httpFactory.CreateClient();
 | 
					                        using var http = _httpFactory.CreateClient();
 | 
				
			||||||
                        var strData = await http.GetFromJsonAsync<CryptoResponse>(
 | 
					                        var strData = await http.GetFromJsonAsync<CryptoResponse>(
 | 
				
			||||||
                            "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?"
 | 
					                            "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?"
 | 
				
			||||||
                            + $"CMC_PRO_API_KEY={_creds.CoinmarketcapApiKey}"
 | 
					                            + $"CMC_PRO_API_KEY=e79ec505-0913-439d-ae07-069e296a6079"
 | 
				
			||||||
                            + "&start=1"
 | 
					                            + "&start=1"
 | 
				
			||||||
                            + "&limit=5000"
 | 
					                            + "&limit=5"
 | 
				
			||||||
                            + "&convert=USD");
 | 
					                            + "&convert=USD");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        return JsonSerializer.Serialize(strData);
 | 
					                        return JsonSerializer.Serialize(strData);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,11 +16,11 @@ public class CmcQuote
 | 
				
			|||||||
    [JsonPropertyName("volume_24h")]
 | 
					    [JsonPropertyName("volume_24h")]
 | 
				
			||||||
    public double Volume24h { get; set; }
 | 
					    public double Volume24h { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [JsonPropertyName("volume_change_24h")]
 | 
					    // [JsonPropertyName("volume_change_24h")]
 | 
				
			||||||
    public double VolumeChange24h { get; set; }
 | 
					    // public double VolumeChange24h { get; set; }
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
    [JsonPropertyName("percent_change_1h")]
 | 
					    // [JsonPropertyName("percent_change_1h")]
 | 
				
			||||||
    public double PercentChange1h { get; set; }
 | 
					    // public double PercentChange1h { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [JsonPropertyName("percent_change_24h")]
 | 
					    [JsonPropertyName("percent_change_24h")]
 | 
				
			||||||
    public double PercentChange24h { get; set; }
 | 
					    public double PercentChange24h { get; set; }
 | 
				
			||||||
@@ -33,12 +33,6 @@ public class CmcQuote
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    [JsonPropertyName("market_cap_dominance")]
 | 
					    [JsonPropertyName("market_cap_dominance")]
 | 
				
			||||||
    public double MarketCapDominance { get; set; }
 | 
					    public double MarketCapDominance { get; set; }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    [JsonPropertyName("fully_diluted_market_cap")]
 | 
					 | 
				
			||||||
    public double FullyDilutedMarketCap { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [JsonPropertyName("last_updated")]
 | 
					 | 
				
			||||||
    public DateTime LastUpdated { get; set; }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class CmcResponseData
 | 
					public class CmcResponseData
 | 
				
			||||||
@@ -58,9 +52,6 @@ public class CmcResponseData
 | 
				
			|||||||
    [JsonPropertyName("cmc_rank")]
 | 
					    [JsonPropertyName("cmc_rank")]
 | 
				
			||||||
    public int CmcRank { get; set; }
 | 
					    public int CmcRank { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [JsonPropertyName("num_market_pairs")]
 | 
					 | 
				
			||||||
    public int NumMarketPairs { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [JsonPropertyName("circulating_supply")]
 | 
					    [JsonPropertyName("circulating_supply")]
 | 
				
			||||||
    public double? CirculatingSupply { get; set; }
 | 
					    public double? CirculatingSupply { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -70,15 +61,6 @@ public class CmcResponseData
 | 
				
			|||||||
    [JsonPropertyName("max_supply")]
 | 
					    [JsonPropertyName("max_supply")]
 | 
				
			||||||
    public double? MaxSupply { get; set; }
 | 
					    public double? MaxSupply { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [JsonPropertyName("last_updated")]
 | 
					 | 
				
			||||||
    public DateTime LastUpdated { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [JsonPropertyName("date_added")]
 | 
					 | 
				
			||||||
    public DateTime DateAdded { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [JsonPropertyName("tags")]
 | 
					 | 
				
			||||||
    public List<string> Tags { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [JsonPropertyName("quote")]
 | 
					    [JsonPropertyName("quote")]
 | 
				
			||||||
    public Dictionary<string, CmcQuote> Quote { get; set; }
 | 
					    public Dictionary<string, CmcQuote> Quote { get; set; }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -221,7 +221,7 @@ public class StreamRoleService : IReadyExecutor, INService
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async ValueTask RescanUser(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
 | 
					    private async ValueTask RescanUser(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
 | 
				
			||||||
        => await _queueRunner.Enqueue(() => RescanUserInternal(user, setting, addRole));
 | 
					        => await _queueRunner.EnqueueAsync(() => RescanUserInternal(user, setting, addRole));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async Task RescanUserInternal(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
 | 
					    private async Task RescanUserInternal(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -239,7 +239,7 @@ public class StreamRoleService : IReadyExecutor, INService
 | 
				
			|||||||
            && setting.Blacklist.All(x => x.UserId != user.Id)
 | 
					            && setting.Blacklist.All(x => x.UserId != user.Id)
 | 
				
			||||||
            && user.RoleIds.Contains(setting.FromRoleId))
 | 
					            && user.RoleIds.Contains(setting.FromRoleId))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await _queueRunner.Enqueue(async () =>
 | 
					            await _queueRunner.EnqueueAsync(async () =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -277,7 +277,7 @@ public class StreamRoleService : IReadyExecutor, INService
 | 
				
			|||||||
            //check if user is in the addrole
 | 
					            //check if user is in the addrole
 | 
				
			||||||
            if (user.RoleIds.Contains(setting.AddRoleId))
 | 
					            if (user.RoleIds.Contains(setting.AddRoleId))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await _queueRunner.Enqueue(async () =>
 | 
					                await _queueRunner.EnqueueAsync(async () =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    try
 | 
					                    try
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,6 @@
 | 
				
			|||||||
#nullable disable
 | 
					#nullable disable
 | 
				
			||||||
 | 
					using LinqToDB;
 | 
				
			||||||
 | 
					using LinqToDB.EntityFrameworkCore;
 | 
				
			||||||
using Microsoft.EntityFrameworkCore;
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
using NadekoBot.Common.ModuleBehaviors;
 | 
					using NadekoBot.Common.ModuleBehaviors;
 | 
				
			||||||
using NadekoBot.Db;
 | 
					using NadekoBot.Db;
 | 
				
			||||||
@@ -13,6 +15,7 @@ using SixLabors.ImageSharp.Formats;
 | 
				
			|||||||
using SixLabors.ImageSharp.PixelFormats;
 | 
					using SixLabors.ImageSharp.PixelFormats;
 | 
				
			||||||
using SixLabors.ImageSharp.Processing;
 | 
					using SixLabors.ImageSharp.Processing;
 | 
				
			||||||
using StackExchange.Redis;
 | 
					using StackExchange.Redis;
 | 
				
			||||||
 | 
					using System.Threading.Channels;
 | 
				
			||||||
using Color = SixLabors.ImageSharp.Color;
 | 
					using Color = SixLabors.ImageSharp.Color;
 | 
				
			||||||
using Image = SixLabors.ImageSharp.Image;
 | 
					using Image = SixLabors.ImageSharp.Image;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -39,7 +42,6 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
				
			|||||||
    private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _excludedChannels;
 | 
					    private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _excludedChannels;
 | 
				
			||||||
    private readonly ConcurrentHashSet<ulong> _excludedServers;
 | 
					    private readonly ConcurrentHashSet<ulong> _excludedServers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private readonly ConcurrentQueue<UserCacheItem> _addMessageXp = new();
 | 
					 | 
				
			||||||
    private XpTemplate template;
 | 
					    private XpTemplate template;
 | 
				
			||||||
    private readonly DiscordSocketClient _client;
 | 
					    private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -127,144 +129,199 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
				
			|||||||
        using var timer = new PeriodicTimer(5.Seconds());
 | 
					        using var timer = new PeriodicTimer(5.Seconds());
 | 
				
			||||||
        while (await timer.WaitForNextTickAsync())
 | 
					        while (await timer.WaitForNextTickAsync())
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await UpdateLoop();
 | 
					            await UpdateXp();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private async Task UpdateLoop()
 | 
					
 | 
				
			||||||
 | 
					    private readonly QueueRunner _levelUpQueue = new QueueRunner(0, 50);
 | 
				
			||||||
 | 
					    private readonly Channel<UserCacheItem> _xpGainQueue = Channel.CreateUnbounded<UserCacheItem>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private async Task UpdateXp()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try
 | 
					        try
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var toNotify =
 | 
					            var reader = _xpGainQueue.Reader;
 | 
				
			||||||
                new List<(IGuild Guild, IMessageChannel MessageChannel, IUser User, long Level,
 | 
					 | 
				
			||||||
                    XpNotificationLocation NotifyType, NotifOf NotifOf)>();
 | 
					 | 
				
			||||||
            var roleRewards = new Dictionary<ulong, List<XpRoleReward>>();
 | 
					 | 
				
			||||||
            var curRewards = new Dictionary<ulong, List<XpCurrencyReward>>();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var toAddTo = new List<UserCacheItem>();
 | 
					            // no elements, quick return
 | 
				
			||||||
            while (_addMessageXp.TryDequeue(out var usr))
 | 
					            if (!reader.TryPeek(out _))
 | 
				
			||||||
                toAddTo.Add(usr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var group = toAddTo.GroupBy(x => (GuildId: x.Guild.Id, x.User));
 | 
					 | 
				
			||||||
            if (toAddTo.Count == 0)
 | 
					 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await using (var uow = _db.GetDbContext())
 | 
					            await using var ctx = _db.GetDbContext();
 | 
				
			||||||
 | 
					            await using var tran = await ctx.Database.BeginTransactionAsync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var userAdds = new Dictionary<ulong, int>();
 | 
				
			||||||
 | 
					            while (reader.TryRead(out var item))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var item in group)
 | 
					                // // update xp
 | 
				
			||||||
                {
 | 
					                // await ctx.GetTable<UserXpStats>()
 | 
				
			||||||
                    var xp = item.Sum(x => x.XpAmount);
 | 
					                //          .Where(x => x.UserId == item.User.Id)
 | 
				
			||||||
 | 
					                //          .UpdateWithOutputAsync(old => new()
 | 
				
			||||||
 | 
					                //          {
 | 
				
			||||||
 | 
					                //              Xp = old.Xp + item.XpAmount
 | 
				
			||||||
 | 
					                //          }, (o, n) => n);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var usr = uow.GetOrCreateUserXpStats(item.Key.GuildId, item.Key.User.Id);
 | 
					                var user = item.User;
 | 
				
			||||||
                    var du = uow.GetOrCreateUser(item.Key.User);
 | 
					                var du = await ctx.GetTable<DiscordUser>()
 | 
				
			||||||
 | 
					                                  .Where(x => x.UserId == user.Id)
 | 
				
			||||||
                    var globalXp = du.TotalXp;
 | 
					                                  .UpdateWithOutputAsync(
 | 
				
			||||||
                    var oldGlobalLevelData = new LevelStats(globalXp);
 | 
					                                      old => new()
 | 
				
			||||||
                    var newGlobalLevelData = new LevelStats(globalXp + xp);
 | 
					                                      {
 | 
				
			||||||
 | 
					                                          TotalXp = old.TotalXp + item.XpAmount
 | 
				
			||||||
                    var oldGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
 | 
					                                      },
 | 
				
			||||||
                    usr.Xp += xp;
 | 
					                                      (_, n) => n);
 | 
				
			||||||
                    du.TotalXp += xp;
 | 
					 | 
				
			||||||
                    if (du.Club is not null)
 | 
					 | 
				
			||||||
                        du.Club.Xp += xp;
 | 
					 | 
				
			||||||
                    var newGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (oldGlobalLevelData.Level < newGlobalLevelData.Level)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        var first = item.First();
 | 
					 | 
				
			||||||
                        if (du.NotifyOnLevelUp != XpNotificationLocation.None)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            toNotify.Add((first.Guild, first.Channel, first.User, newGlobalLevelData.Level,
 | 
					 | 
				
			||||||
                                du.NotifyOnLevelUp, NotifOf.Global));
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (oldGuildLevelData.Level < newGuildLevelData.Level)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        //send level up notification
 | 
					 | 
				
			||||||
                        var first = item.First();
 | 
					 | 
				
			||||||
                        if (usr.NotifyOnLevelUp != XpNotificationLocation.None)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            toNotify.Add((first.Guild, first.Channel, first.User, newGuildLevelData.Level,
 | 
					 | 
				
			||||||
                                usr.NotifyOnLevelUp, NotifOf.Server));
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        //give role
 | 
					 | 
				
			||||||
                        if (!roleRewards.TryGetValue(usr.GuildId, out var rrews))
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            rrews = uow.XpSettingsFor(usr.GuildId).RoleRewards.ToList();
 | 
					 | 
				
			||||||
                            roleRewards.Add(usr.GuildId, rrews);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if (!curRewards.TryGetValue(usr.GuildId, out var crews))
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            crews = uow.XpSettingsFor(usr.GuildId).CurrencyRewards.ToList();
 | 
					 | 
				
			||||||
                            curRewards.Add(usr.GuildId, crews);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        //loop through levels since last level up, so if a high amount of xp is gained, reward are still applied.
 | 
					 | 
				
			||||||
                        for (var i = oldGuildLevelData.Level + 1; i <= newGuildLevelData.Level; i++)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            var rrew = rrews.FirstOrDefault(x => x.Level == i);
 | 
					 | 
				
			||||||
                            if (rrew is not null)
 | 
					 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                var role = first.User.Guild.GetRole(rrew.RoleId);
 | 
					 | 
				
			||||||
                                if (role is not null)
 | 
					 | 
				
			||||||
                                {
 | 
					 | 
				
			||||||
                                    if (rrew.Remove)
 | 
					 | 
				
			||||||
                                        _ = first.User.RemoveRoleAsync(role);
 | 
					 | 
				
			||||||
                                    else
 | 
					 | 
				
			||||||
                                        _ = first.User.AddRoleAsync(role);
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            //get currency reward for this level
 | 
					 | 
				
			||||||
                            var crew = crews.FirstOrDefault(x => x.Level == i);
 | 
					 | 
				
			||||||
                            if (crew is not null)
 | 
					 | 
				
			||||||
                                //give the user the reward if it exists
 | 
					 | 
				
			||||||
                                await _cs.AddAsync(item.Key.User.Id, crew.Amount, new("xp", "level-up"));
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                uow.SaveChanges();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            await toNotify.Select(async x =>
 | 
					            await tran.CommitAsync();
 | 
				
			||||||
                          {
 | 
					 | 
				
			||||||
                              if (x.NotifOf == NotifOf.Server)
 | 
					 | 
				
			||||||
                              {
 | 
					 | 
				
			||||||
                                  if (x.NotifyType == XpNotificationLocation.Dm)
 | 
					 | 
				
			||||||
                                  {
 | 
					 | 
				
			||||||
                                      await x.User.SendConfirmAsync(_eb,
 | 
					 | 
				
			||||||
                                          _strings.GetText(strs.level_up_dm(x.User.Mention,
 | 
					 | 
				
			||||||
                                                  Format.Bold(x.Level.ToString()),
 | 
					 | 
				
			||||||
                                                  Format.Bold(x.Guild.ToString() ?? "-")),
 | 
					 | 
				
			||||||
                                              x.Guild.Id));
 | 
					 | 
				
			||||||
                                  }
 | 
					 | 
				
			||||||
                                  else if (x.MessageChannel is not null) // channel
 | 
					 | 
				
			||||||
                                  {
 | 
					 | 
				
			||||||
                                      await x.MessageChannel.SendConfirmAsync(_eb,
 | 
					 | 
				
			||||||
                                          _strings.GetText(strs.level_up_channel(x.User.Mention,
 | 
					 | 
				
			||||||
                                                  Format.Bold(x.Level.ToString())),
 | 
					 | 
				
			||||||
                                              x.Guild.Id));
 | 
					 | 
				
			||||||
                                  }
 | 
					 | 
				
			||||||
                              }
 | 
					 | 
				
			||||||
                              else
 | 
					 | 
				
			||||||
                              {
 | 
					 | 
				
			||||||
                                  IMessageChannel chan;
 | 
					 | 
				
			||||||
                                  if (x.NotifyType == XpNotificationLocation.Dm)
 | 
					 | 
				
			||||||
                                      chan = await x.User.CreateDMChannelAsync();
 | 
					 | 
				
			||||||
                                  else // channel
 | 
					 | 
				
			||||||
                                      chan = x.MessageChannel;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                  await chan.SendConfirmAsync(_eb,
 | 
					            var users = await ctx.GetTable<DiscordUser>()
 | 
				
			||||||
                                      _strings.GetText(strs.level_up_global(x.User.Mention,
 | 
					                                 .Where(x => userAdds.Keys.Contains(x.UserId))
 | 
				
			||||||
                                              Format.Bold(x.Level.ToString())),
 | 
					                                 .ToListAsyncLinqToDB();
 | 
				
			||||||
                                          x.Guild.Id));
 | 
					
 | 
				
			||||||
                              }
 | 
					            // foreach (var user in users)
 | 
				
			||||||
                          })
 | 
					            // {
 | 
				
			||||||
                          .WhenAll();
 | 
					            //     var oldLevel = new LevelStats(user.TotalXp - userAdds[user.UserId]);
 | 
				
			||||||
 | 
					            //     var newLevel = new LevelStats(user.TotalXp);
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            //     if (oldLevel.Level != newLevel.Level)
 | 
				
			||||||
 | 
					            //     {
 | 
				
			||||||
 | 
					            //         await _levelUpQueue.EnqueueAsync(
 | 
				
			||||||
 | 
					            //             NotifyUser(user.UserId,
 | 
				
			||||||
 | 
					            //                 user.NotifyOnLevelUp));
 | 
				
			||||||
 | 
					            //     }
 | 
				
			||||||
 | 
					            // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					            // var toNotify =
 | 
				
			||||||
 | 
					            //     new List<(IGuild Guild, IMessageChannel MessageChannel, IUser User, long Level,
 | 
				
			||||||
 | 
					            //         XpNotificationLocation NotifyType, NotifOf NotifOf)>();
 | 
				
			||||||
 | 
					            // var roleRewards = new Dictionary<ulong, List<XpRoleReward>>();
 | 
				
			||||||
 | 
					            // var curRewards = new Dictionary<ulong, List<XpCurrencyReward>>();
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            // var toAddTo = new List<UserCacheItem>();
 | 
				
			||||||
 | 
					            // while (_addMessageXp.TryDequeue(out var usr))
 | 
				
			||||||
 | 
					            //     toAddTo.Add(usr);
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            // var group = toAddTo.GroupBy(x => (GuildId: x.Guild.Id, x.User));
 | 
				
			||||||
 | 
					            // if (toAddTo.Count == 0)
 | 
				
			||||||
 | 
					            //     return;
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            // await using (var uow = _db.GetDbContext())
 | 
				
			||||||
 | 
					            // {
 | 
				
			||||||
 | 
					            //     foreach (var item in group)
 | 
				
			||||||
 | 
					            //     {
 | 
				
			||||||
 | 
					            //         var xp = item.Sum(x => x.XpAmount);
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            //         var usr = uow.GetOrCreateUserXpStats(item.Key.GuildId, item.Key.User.Id);
 | 
				
			||||||
 | 
					            //         var du = uow.GetOrCreateUser(item.Key.User);
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            //         var globalXp = du.TotalXp;
 | 
				
			||||||
 | 
					            //         var oldGlobalLevelData = new LevelStats(globalXp);
 | 
				
			||||||
 | 
					            //         var newGlobalLevelData = new LevelStats(globalXp + xp);
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            //         var oldGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
 | 
				
			||||||
 | 
					            //         usr.Xp += xp;
 | 
				
			||||||
 | 
					            //         du.TotalXp += xp;
 | 
				
			||||||
 | 
					            //         if (du.Club is not null)
 | 
				
			||||||
 | 
					            //             du.Club.Xp += xp;
 | 
				
			||||||
 | 
					            //         var newGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            //         if (oldGlobalLevelData.Level < newGlobalLevelData.Level)
 | 
				
			||||||
 | 
					            //         {
 | 
				
			||||||
 | 
					            //             var first = item.First();
 | 
				
			||||||
 | 
					            //             if (du.NotifyOnLevelUp != XpNotificationLocation.None)
 | 
				
			||||||
 | 
					            //             {
 | 
				
			||||||
 | 
					            //                 toNotify.Add((first.Guild, first.Channel, first.User, newGlobalLevelData.Level,
 | 
				
			||||||
 | 
					            //                     du.NotifyOnLevelUp, NotifOf.Global));
 | 
				
			||||||
 | 
					            //             }
 | 
				
			||||||
 | 
					            //         }
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            //         if (oldGuildLevelData.Level < newGuildLevelData.Level)
 | 
				
			||||||
 | 
					            //         {
 | 
				
			||||||
 | 
					            //             //send level up notification
 | 
				
			||||||
 | 
					            //             var first = item.First();
 | 
				
			||||||
 | 
					            //             if (usr.NotifyOnLevelUp != XpNotificationLocation.None)
 | 
				
			||||||
 | 
					            //             {
 | 
				
			||||||
 | 
					            //                 toNotify.Add((first.Guild, first.Channel, first.User, newGuildLevelData.Level,
 | 
				
			||||||
 | 
					            //                     usr.NotifyOnLevelUp, NotifOf.Server));
 | 
				
			||||||
 | 
					            //             }
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            //             //give role
 | 
				
			||||||
 | 
					            //             if (!roleRewards.TryGetValue(usr.GuildId, out var rrews))
 | 
				
			||||||
 | 
					            //             {
 | 
				
			||||||
 | 
					            //                 rrews = uow.XpSettingsFor(usr.GuildId).RoleRewards.ToList();
 | 
				
			||||||
 | 
					            //                 roleRewards.Add(usr.GuildId, rrews);
 | 
				
			||||||
 | 
					            //             }
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            //             if (!curRewards.TryGetValue(usr.GuildId, out var crews))
 | 
				
			||||||
 | 
					            //             {
 | 
				
			||||||
 | 
					            //                 crews = uow.XpSettingsFor(usr.GuildId).CurrencyRewards.ToList();
 | 
				
			||||||
 | 
					            //                 curRewards.Add(usr.GuildId, crews);
 | 
				
			||||||
 | 
					            //             }
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            //             //loop through levels since last level up, so if a high amount of xp is gained, reward are still applied.
 | 
				
			||||||
 | 
					            //             for (var i = oldGuildLevelData.Level + 1; i <= newGuildLevelData.Level; i++)
 | 
				
			||||||
 | 
					            //             {
 | 
				
			||||||
 | 
					            //                 var rrew = rrews.FirstOrDefault(x => x.Level == i);
 | 
				
			||||||
 | 
					            //                 if (rrew is not null)
 | 
				
			||||||
 | 
					            //                 {
 | 
				
			||||||
 | 
					            //                     var role = first.User.Guild.GetRole(rrew.RoleId);
 | 
				
			||||||
 | 
					            //                     if (role is not null)
 | 
				
			||||||
 | 
					            //                     {
 | 
				
			||||||
 | 
					            //                         if (rrew.Remove)
 | 
				
			||||||
 | 
					            //                             _ = first.User.RemoveRoleAsync(role);
 | 
				
			||||||
 | 
					            //                         else
 | 
				
			||||||
 | 
					            //                             _ = first.User.AddRoleAsync(role);
 | 
				
			||||||
 | 
					            //                     }
 | 
				
			||||||
 | 
					            //                 }
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            //                 //get currency reward for this level
 | 
				
			||||||
 | 
					            //                 var crew = crews.FirstOrDefault(x => x.Level == i);
 | 
				
			||||||
 | 
					            //                 if (crew is not null)
 | 
				
			||||||
 | 
					            //                     //give the user the reward if it exists
 | 
				
			||||||
 | 
					            //                     await _cs.AddAsync(item.Key.User.Id, crew.Amount, new("xp", "level-up"));
 | 
				
			||||||
 | 
					            //             }
 | 
				
			||||||
 | 
					            //         }
 | 
				
			||||||
 | 
					            //     }
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            //     uow.SaveChanges();
 | 
				
			||||||
 | 
					            // }
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            // await toNotify.Select(async x =>
 | 
				
			||||||
 | 
					            //               {
 | 
				
			||||||
 | 
					            //                   if (x.NotifOf == NotifOf.Server)
 | 
				
			||||||
 | 
					            //                   {
 | 
				
			||||||
 | 
					            //                       if (x.NotifyType == XpNotificationLocation.Dm)
 | 
				
			||||||
 | 
					            //                       {
 | 
				
			||||||
 | 
					            //                           await x.User.SendConfirmAsync(_eb,
 | 
				
			||||||
 | 
					            //                               _strings.GetText(strs.level_up_dm(x.User.Mention,
 | 
				
			||||||
 | 
					            //                                       Format.Bold(x.Level.ToString()),
 | 
				
			||||||
 | 
					            //                                       Format.Bold(x.Guild.ToString() ?? "-")),
 | 
				
			||||||
 | 
					            //                                   x.Guild.Id));
 | 
				
			||||||
 | 
					            //                       }
 | 
				
			||||||
 | 
					            //                       else if (x.MessageChannel is not null) // channel
 | 
				
			||||||
 | 
					            //                       {
 | 
				
			||||||
 | 
					            //                           await x.MessageChannel.SendConfirmAsync(_eb,
 | 
				
			||||||
 | 
					            //                               _strings.GetText(strs.level_up_channel(x.User.Mention,
 | 
				
			||||||
 | 
					            //                                       Format.Bold(x.Level.ToString())),
 | 
				
			||||||
 | 
					            //                                   x.Guild.Id));
 | 
				
			||||||
 | 
					            //                       }
 | 
				
			||||||
 | 
					            //                   }
 | 
				
			||||||
 | 
					            //                   else
 | 
				
			||||||
 | 
					            //                   {
 | 
				
			||||||
 | 
					            //                       IMessageChannel chan;
 | 
				
			||||||
 | 
					            //                       if (x.NotifyType == XpNotificationLocation.Dm)
 | 
				
			||||||
 | 
					            //                           chan = await x.User.CreateDMChannelAsync();
 | 
				
			||||||
 | 
					            //                       else // channel
 | 
				
			||||||
 | 
					            //                           chan = x.MessageChannel;
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
 | 
					            //                       await chan.SendConfirmAsync(_eb,
 | 
				
			||||||
 | 
					            //                           _strings.GetText(strs.level_up_global(x.User.Mention,
 | 
				
			||||||
 | 
					            //                                   Format.Bold(x.Level.ToString())),
 | 
				
			||||||
 | 
					            //                               x.Guild.Id));
 | 
				
			||||||
 | 
					            //                   }
 | 
				
			||||||
 | 
					            //               })
 | 
				
			||||||
 | 
					            //               .WhenAll();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        catch (Exception ex)
 | 
					        catch (Exception ex)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -272,7 +329,69 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private Func<Task> NotifyUser(
 | 
				
			||||||
 | 
					        ulong guildId,
 | 
				
			||||||
 | 
					        ulong channelId,
 | 
				
			||||||
 | 
					        ulong userId,
 | 
				
			||||||
 | 
					        bool isServer,
 | 
				
			||||||
 | 
					        int level,
 | 
				
			||||||
 | 
					        XpNotificationLocation notifyLoc)
 | 
				
			||||||
 | 
					        => async () =>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var notify = HandleNotifyInternalAsync(guildId, channelId, userId, isServer, level, notifyLoc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await Task.WhenAll(notify);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private async Task HandleNotifyInternalAsync(ulong guildId, ulong channelId, ulong userId, bool isServer, int level,
 | 
				
			||||||
 | 
					        XpNotificationLocation notifyLoc)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var user = await _client.GetUserAsync(userId);
 | 
				
			||||||
 | 
					        var guild = _client.GetGuild(guildId);
 | 
				
			||||||
 | 
					        var ch = guild?.GetTextChannel(channelId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (user is null || guild is null)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (isServer)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (notifyLoc == XpNotificationLocation.Dm)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await user.SendConfirmAsync(_eb,
 | 
				
			||||||
 | 
					                    _strings.GetText(strs.level_up_dm(user.Mention,
 | 
				
			||||||
 | 
					                            Format.Bold(level.ToString()),
 | 
				
			||||||
 | 
					                            Format.Bold(guild.ToString() ?? "-")),
 | 
				
			||||||
 | 
					                        guild.Id));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else // channel
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await ch.SendConfirmAsync(_eb,
 | 
				
			||||||
 | 
					                    _strings.GetText(strs.level_up_channel(user.Mention,
 | 
				
			||||||
 | 
					                            Format.Bold(level.ToString())),
 | 
				
			||||||
 | 
					                        guild.Id));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else // global level
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var chan = notifyLoc switch
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                XpNotificationLocation.Dm => (IMessageChannel)await user.CreateDMChannelAsync(),
 | 
				
			||||||
 | 
					                XpNotificationLocation.Channel => ch,
 | 
				
			||||||
 | 
					                _ => null
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (chan is null)
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await chan.SendConfirmAsync(_eb,
 | 
				
			||||||
 | 
					                _strings.GetText(strs.level_up_global(user.Mention,
 | 
				
			||||||
 | 
					                        Format.Bold(level.ToString())),
 | 
				
			||||||
 | 
					                    guild.Id));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private const string XP_TEMPLATE_PATH = "./data/xp_template.json";
 | 
					    private const string XP_TEMPLATE_PATH = "./data/xp_template.json";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void InternalReloadXpTemplate()
 | 
					    private void InternalReloadXpTemplate()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try
 | 
					        try
 | 
				
			||||||
@@ -297,7 +416,8 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                Log.Warning("Loaded default xp_template.json values as the old one was version 0. "
 | 
					                Log.Warning("Loaded default xp_template.json values as the old one was version 0. "
 | 
				
			||||||
                            + "Old one was renamed to xp_template.json.old");
 | 
					                            + "Old one was renamed to xp_template.json.old");
 | 
				
			||||||
                File.WriteAllText("./data/xp_template.json.old", JsonConvert.SerializeObject(template, Formatting.Indented));
 | 
					                File.WriteAllText("./data/xp_template.json.old",
 | 
				
			||||||
 | 
					                    JsonConvert.SerializeObject(template, Formatting.Indented));
 | 
				
			||||||
                template = new();
 | 
					                template = new();
 | 
				
			||||||
                template.Version = 1;
 | 
					                template.Version = 1;
 | 
				
			||||||
                File.WriteAllText(XP_TEMPLATE_PATH, JsonConvert.SerializeObject(template, Formatting.Indented));
 | 
					                File.WriteAllText(XP_TEMPLATE_PATH, JsonConvert.SerializeObject(template, Formatting.Indented));
 | 
				
			||||||
@@ -467,7 +587,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
				
			|||||||
        if (socketUser is not SocketGuildUser user || user.IsBot)
 | 
					        if (socketUser is not SocketGuildUser user || user.IsBot)
 | 
				
			||||||
            return Task.CompletedTask;
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _ = Task.Run(() =>
 | 
					        _ = Task.Run(async () =>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (before.VoiceChannel is not null)
 | 
					            if (before.VoiceChannel is not null)
 | 
				
			||||||
                ScanChannelForVoiceXp(before.VoiceChannel);
 | 
					                ScanChannelForVoiceXp(before.VoiceChannel);
 | 
				
			||||||
@@ -475,15 +595,17 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
				
			|||||||
            if (after.VoiceChannel is not null && after.VoiceChannel != before.VoiceChannel)
 | 
					            if (after.VoiceChannel is not null && after.VoiceChannel != before.VoiceChannel)
 | 
				
			||||||
                ScanChannelForVoiceXp(after.VoiceChannel);
 | 
					                ScanChannelForVoiceXp(after.VoiceChannel);
 | 
				
			||||||
            else if (after.VoiceChannel is null)
 | 
					            else if (after.VoiceChannel is null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
                // In this case, the user left the channel and the previous for loops didn't catch
 | 
					                // In this case, the user left the channel and the previous for loops didn't catch
 | 
				
			||||||
                // it because it wasn't in any new channel. So we need to get rid of it.
 | 
					                // it because it wasn't in any new channel. So we need to get rid of it.
 | 
				
			||||||
                UserLeftVoiceChannel(user, before.VoiceChannel);
 | 
					                await UserLeftVoiceChannel(user, before.VoiceChannel);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Task.CompletedTask;
 | 
					        return Task.CompletedTask;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void ScanChannelForVoiceXp(SocketVoiceChannel channel)
 | 
					    private async Task ScanChannelForVoiceXp(SocketVoiceChannel channel)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (ShouldTrackVoiceChannel(channel))
 | 
					        if (ShouldTrackVoiceChannel(channel))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -493,7 +615,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
				
			|||||||
        else
 | 
					        else
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var user in channel.Users)
 | 
					            foreach (var user in channel.Users)
 | 
				
			||||||
                UserLeftVoiceChannel(user, channel);
 | 
					                await UserLeftVoiceChannel(user, channel);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -528,7 +650,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
				
			|||||||
                  when: When.NotExists);
 | 
					                  when: When.NotExists);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void UserLeftVoiceChannel(SocketGuildUser user, SocketVoiceChannel channel)
 | 
					    private async Task UserLeftVoiceChannel(SocketGuildUser user, SocketVoiceChannel channel)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var key = $"{_creds.RedisKey()}_user_xp_vc_join_{user.Id}";
 | 
					        var key = $"{_creds.RedisKey()}_user_xp_vc_join_{user.Id}";
 | 
				
			||||||
        var value = _cache.Redis.GetDatabase().StringGet(key);
 | 
					        var value = _cache.Redis.GetDatabase().StringGet(key);
 | 
				
			||||||
@@ -549,7 +671,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if (actualXp > 0)
 | 
					        if (actualXp > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _addMessageXp.Enqueue(new()
 | 
					            await _xpGainQueue.Writer.WriteAsync(new()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Guild = channel.Guild,
 | 
					                Guild = channel.Guild,
 | 
				
			||||||
                User = user,
 | 
					                User = user,
 | 
				
			||||||
@@ -596,7 +718,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
				
			|||||||
            if (!SetUserRewarded(user.Id))
 | 
					            if (!SetUserRewarded(user.Id))
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _addMessageXp.Enqueue(new()
 | 
					            _xpGainQueue.Writer.WriteAsync(new()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Guild = user.Guild,
 | 
					                Guild = user.Guild,
 | 
				
			||||||
                Channel = arg.Channel,
 | 
					                Channel = arg.Channel,
 | 
				
			||||||
@@ -607,19 +729,19 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
				
			|||||||
        return Task.CompletedTask;
 | 
					        return Task.CompletedTask;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void AddXpDirectly(IGuildUser user, IMessageChannel channel, int amount)
 | 
					    // public void AddXpDirectly(IGuildUser user, IMessageChannel channel, int amount)
 | 
				
			||||||
    {
 | 
					    // {
 | 
				
			||||||
        if (amount <= 0)
 | 
					    //     if (amount <= 0)
 | 
				
			||||||
            throw new ArgumentOutOfRangeException(nameof(amount));
 | 
					    //         throw new ArgumentOutOfRangeException(nameof(amount));
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
        _addMessageXp.Enqueue(new()
 | 
					    //     _xpGainQueue.Writer.WriteAsync(new()
 | 
				
			||||||
        {
 | 
					    //     {
 | 
				
			||||||
            Guild = user.Guild,
 | 
					    //         Guild = user.Guild,
 | 
				
			||||||
            Channel = channel,
 | 
					    //         Channel = channel,
 | 
				
			||||||
            User = user,
 | 
					    //         User = user,
 | 
				
			||||||
            XpAmount = amount
 | 
					    //         XpAmount = amount
 | 
				
			||||||
        });
 | 
					    //     });
 | 
				
			||||||
    }
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void AddXp(ulong userId, ulong guildId, int amount)
 | 
					    public void AddXp(ulong userId, ulong guildId, int amount)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -655,6 +777,8 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
				
			|||||||
        var r = _cache.Redis.GetDatabase();
 | 
					        var r = _cache.Redis.GetDatabase();
 | 
				
			||||||
        var key = $"{_creds.RedisKey()}_user_xp_gain_{userId}";
 | 
					        var key = $"{_creds.RedisKey()}_user_xp_gain_{userId}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // todo remove this return
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
        return r.StringSet(key,
 | 
					        return r.StringSet(key,
 | 
				
			||||||
            true,
 | 
					            true,
 | 
				
			||||||
            TimeSpan.FromMinutes(_xpConfig.Data.MessageXpCooldown),
 | 
					            TimeSpan.FromMinutes(_xpConfig.Data.MessageXpCooldown),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ using NadekoBot.Modules.Xp.Services;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Modules.Xp;
 | 
					namespace NadekoBot.Modules.Xp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class LevelStats
 | 
					public readonly struct LevelStats
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public long Level { get; }
 | 
					    public long Level { get; }
 | 
				
			||||||
    public long LevelXp { get; }
 | 
					    public long LevelXp { get; }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user