Fixed slow .xp and .xpglb
				
					
				
			This commit is contained in:
		
							
								
								
									
										1949
									
								
								src/NadekoBot/Migrations/20170913022654_total-xp.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1949
									
								
								src/NadekoBot/Migrations/20170913022654_total-xp.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										27
									
								
								src/NadekoBot/Migrations/20170913022654_total-xp.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/NadekoBot/Migrations/20170913022654_total-xp.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using Microsoft.EntityFrameworkCore.Migrations; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Migrations | ||||||
|  | { | ||||||
|  |     public partial class totalxp : Migration | ||||||
|  |     { | ||||||
|  |         protected override void Up(MigrationBuilder migrationBuilder) | ||||||
|  |         { | ||||||
|  |             migrationBuilder.AddColumn<int>( | ||||||
|  |                 name: "TotalXp", | ||||||
|  |                 table: "DiscordUser", | ||||||
|  |                 nullable: false, | ||||||
|  |                 defaultValue: 0); | ||||||
|  |  | ||||||
|  |             migrationBuilder.Sql(MigrationQueries.TotalXp); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         protected override void Down(MigrationBuilder migrationBuilder) | ||||||
|  |         { | ||||||
|  |             migrationBuilder.DropColumn( | ||||||
|  |                 name: "TotalXp", | ||||||
|  |                 table: "DiscordUser"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -34,5 +34,9 @@ INSERT INTO DiscordUser | |||||||
|     FROM DiscordUser_tmp; |     FROM DiscordUser_tmp; | ||||||
|  |  | ||||||
| DROP TABLE DiscordUser_tmp;"; | DROP TABLE DiscordUser_tmp;"; | ||||||
|  |         public static string TotalXp { get; } = | ||||||
|  | @"UPDATE DiscordUser | ||||||
|  | SET TotalXp = (SELECT SUM(Xp) FROM UserXpStats WHERE UserId = DiscordUser.UserId)"; | ||||||
|  |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -464,12 +464,14 @@ namespace NadekoBot.Migrations | |||||||
|  |  | ||||||
|                     b.Property<DateTime>("LastLevelUp") |                     b.Property<DateTime>("LastLevelUp") | ||||||
|                         .ValueGeneratedOnAdd() |                         .ValueGeneratedOnAdd() | ||||||
|                         .HasDefaultValue(new DateTime(2017, 9, 11, 22, 0, 31, 236, DateTimeKind.Local)); |                         .HasDefaultValue(new DateTime(2017, 9, 13, 4, 26, 53, 906, DateTimeKind.Local)); | ||||||
|  |  | ||||||
|                     b.Property<DateTime>("LastXpGain"); |                     b.Property<DateTime>("LastXpGain"); | ||||||
|  |  | ||||||
|                     b.Property<int>("NotifyOnLevelUp"); |                     b.Property<int>("NotifyOnLevelUp"); | ||||||
|  |  | ||||||
|  |                     b.Property<int>("TotalXp"); | ||||||
|  |  | ||||||
|                     b.Property<ulong>("UserId"); |                     b.Property<ulong>("UserId"); | ||||||
|  |  | ||||||
|                     b.Property<string>("Username"); |                     b.Property<string>("Username"); | ||||||
| @@ -1362,7 +1364,7 @@ namespace NadekoBot.Migrations | |||||||
|  |  | ||||||
|                     b.Property<DateTime>("LastLevelUp") |                     b.Property<DateTime>("LastLevelUp") | ||||||
|                         .ValueGeneratedOnAdd() |                         .ValueGeneratedOnAdd() | ||||||
|                         .HasDefaultValue(new DateTime(2017, 9, 11, 22, 0, 31, 238, DateTimeKind.Local)); |                         .HasDefaultValue(new DateTime(2017, 9, 13, 4, 26, 53, 910, DateTimeKind.Local)); | ||||||
|  |  | ||||||
|                     b.Property<int>("NotifyOnLevelUp"); |                     b.Property<int>("NotifyOnLevelUp"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -150,6 +150,7 @@ namespace NadekoBot.Modules.Xp.Services | |||||||
|  |  | ||||||
|                             var oldGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp); |                             var oldGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp); | ||||||
|                             usr.Xp += xp; |                             usr.Xp += xp; | ||||||
|  |                             du.TotalXp += xp; | ||||||
|                             if (du.Club != null) |                             if (du.Club != null) | ||||||
|                                 du.Club.Xp += xp; |                                 du.Club.Xp += xp; | ||||||
|                             var newGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp); |                             var newGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp); | ||||||
| @@ -311,7 +312,7 @@ namespace NadekoBot.Modules.Xp.Services | |||||||
|         { |         { | ||||||
|             using (var uow = _db.UnitOfWork) |             using (var uow = _db.UnitOfWork) | ||||||
|             { |             { | ||||||
|                 return uow.Xp.GetUsersFor(page); |                 return uow.DiscordUsers.GetUsersXpLeaderboardFor(page); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -424,8 +425,8 @@ namespace NadekoBot.Modules.Xp.Services | |||||||
|             { |             { | ||||||
|                 du = uow.DiscordUsers.GetOrCreate(user); |                 du = uow.DiscordUsers.GetOrCreate(user); | ||||||
|                 stats = uow.Xp.GetOrCreateUser(user.GuildId, user.Id); |                 stats = uow.Xp.GetOrCreateUser(user.GuildId, user.Id); | ||||||
|                 totalXp = uow.Xp.GetTotalUserXp(user.Id); |                 totalXp = du.TotalXp; | ||||||
|                 globalRank = uow.Xp.GetUserGlobalRanking(user.Id); |                 globalRank = uow.DiscordUsers.GetUserGlobalRanking(user.Id); | ||||||
|                 guildRank = uow.Xp.GetUserGuildRanking(user.Id, user.GuildId); |                 guildRank = uow.Xp.GetUserGuildRanking(user.Id, user.GuildId); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,10 +1,12 @@ | |||||||
| using Discord; | using Discord; | ||||||
| using Discord.Commands; | using Discord.Commands; | ||||||
| using Discord.WebSocket; | using Discord.WebSocket; | ||||||
|  | using NadekoBot.Common; | ||||||
| using NadekoBot.Common.Attributes; | using NadekoBot.Common.Attributes; | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Modules.Xp.Common; | using NadekoBot.Modules.Xp.Common; | ||||||
| using NadekoBot.Modules.Xp.Services; | using NadekoBot.Modules.Xp.Services; | ||||||
|  | using NadekoBot.Services; | ||||||
| using NadekoBot.Services.Database.Models; | using NadekoBot.Services.Database.Models; | ||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
| using System.Linq; | using System.Linq; | ||||||
| @@ -15,14 +17,42 @@ namespace NadekoBot.Modules.Xp | |||||||
|     public partial class Xp : NadekoTopLevelModule<XpService> |     public partial class Xp : NadekoTopLevelModule<XpService> | ||||||
|     { |     { | ||||||
|         private readonly DiscordSocketClient _client; |         private readonly DiscordSocketClient _client; | ||||||
|  |         private readonly DbService _db; | ||||||
|  |  | ||||||
|         public Xp(DiscordSocketClient client) |         public Xp(DiscordSocketClient client,DbService db) | ||||||
|         { |         { | ||||||
|             _client = client; |             _client = client; | ||||||
|  |             _db = db; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         //[NadekoCommand, Usage, Description, Aliases] | ||||||
|  |         //[RequireContext(ContextType.Guild)] | ||||||
|  |         //[OwnerOnly] | ||||||
|  |         //public async Task Populate() | ||||||
|  |         //{ | ||||||
|  |         //    var rng = new NadekoRandom(); | ||||||
|  |         //    using (var uow = _db.UnitOfWork) | ||||||
|  |         //    { | ||||||
|  |         //        for (var i = 0ul; i < 1000000; i++) | ||||||
|  |         //        { | ||||||
|  |         //            uow.DiscordUsers.Add(new DiscordUser() | ||||||
|  |         //            { | ||||||
|  |         //                AvatarId = i.ToString(), | ||||||
|  |         //                Discriminator = "1234", | ||||||
|  |         //                UserId = i, | ||||||
|  |         //                Username = i.ToString(), | ||||||
|  |         //                Club = null, | ||||||
|  |         //            }); | ||||||
|  |         //            var xp = uow.Xp.GetOrCreateUser(Context.Guild.Id, i); | ||||||
|  |         //            xp.Xp = rng.Next(100, 100000); | ||||||
|  |         //        } | ||||||
|  |         //        uow.Complete(); | ||||||
|  |         //    } | ||||||
|  |         //} | ||||||
|  |  | ||||||
|         [NadekoCommand, Usage, Description, Aliases] |         [NadekoCommand, Usage, Description, Aliases] | ||||||
|         [RequireContext(ContextType.Guild)] |         [RequireContext(ContextType.Guild)] | ||||||
|  |         //[Ratelimit(30)] | ||||||
|         public async Task Experience([Remainder]IUser user = null) |         public async Task Experience([Remainder]IUser user = null) | ||||||
|         { |         { | ||||||
|             user = user ?? Context.User; |             user = user ?? Context.User; | ||||||
| @@ -44,7 +74,7 @@ namespace NadekoBot.Modules.Xp | |||||||
|         { |         { | ||||||
|             page--; |             page--; | ||||||
|  |  | ||||||
|             if (page < 0) |             if (page < 0 || page > 100) | ||||||
|                 return Task.CompletedTask; |                 return Task.CompletedTask; | ||||||
|  |  | ||||||
|             var roles = _service.GetRoleRewards(Context.Guild.Id) |             var roles = _service.GetRoleRewards(Context.Guild.Id) | ||||||
| @@ -173,7 +203,7 @@ namespace NadekoBot.Modules.Xp | |||||||
|         [RequireContext(ContextType.Guild)] |         [RequireContext(ContextType.Guild)] | ||||||
|         public Task XpLeaderboard(int page = 1) |         public Task XpLeaderboard(int page = 1) | ||||||
|         { |         { | ||||||
|             if (--page < 0) |             if (--page < 0 || page > 100) | ||||||
|                 return Task.CompletedTask; |                 return Task.CompletedTask; | ||||||
|  |  | ||||||
|             return Context.Channel.SendPaginatedConfirmAsync(_client, page, async (curPage) => |             return Context.Channel.SendPaginatedConfirmAsync(_client, page, async (curPage) => | ||||||
| @@ -214,32 +244,28 @@ namespace NadekoBot.Modules.Xp | |||||||
|         [RequireContext(ContextType.Guild)] |         [RequireContext(ContextType.Guild)] | ||||||
|         public async Task XpGlobalLeaderboard(int page = 1) |         public async Task XpGlobalLeaderboard(int page = 1) | ||||||
|         { |         { | ||||||
|             if (--page < 0) |             if (--page < 0 || page > 100) | ||||||
|                 return; |                 return; | ||||||
|  |             var users = _service.GetUserXps(page); | ||||||
|             await Context.Channel.SendPaginatedConfirmAsync(_client, page, async (curPage) => |  | ||||||
|             { |  | ||||||
|                 var users = _service.GetUserXps(curPage); |  | ||||||
|  |  | ||||||
|             var embed = new EmbedBuilder() |             var embed = new EmbedBuilder() | ||||||
|                 .WithTitle(GetText("global_leaderboard")) |                 .WithTitle(GetText("global_leaderboard")) | ||||||
|                 .WithOkColor(); |                 .WithOkColor(); | ||||||
|  |  | ||||||
|             if (!users.Any()) |             if (!users.Any()) | ||||||
|                     return embed.WithDescription("-"); |                 embed.WithDescription("-"); | ||||||
|             else |             else | ||||||
|             { |             { | ||||||
|                 for (int i = 0; i < users.Length; i++) |                 for (int i = 0; i < users.Length; i++) | ||||||
|                 { |                 { | ||||||
|                     var user = await Context.Guild.GetUserAsync(users[i].UserId).ConfigureAwait(false); |                     var user = await Context.Guild.GetUserAsync(users[i].UserId).ConfigureAwait(false); | ||||||
|                     embed.AddField( |                     embed.AddField( | ||||||
|                             $"#{(i + 1 + curPage * 9)} {(user?.ToString() ?? users[i].UserId.ToString())}",  |                         $"#{(i + 1 + page * 9)} {(user?.ToString() ?? users[i].UserId.ToString())}",  | ||||||
|                         $"{GetText("level_x", LevelStats.FromXp(users[i].TotalXp).Level)} - {users[i].TotalXp}xp"); |                         $"{GetText("level_x", LevelStats.FromXp(users[i].TotalXp).Level)} - {users[i].TotalXp}xp"); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                     return embed; |  | ||||||
|             } |             } | ||||||
|             }, addPaginatedFooter: false); |  | ||||||
|  |             await Context.Channel.EmbedAsync(embed); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         [NadekoCommand, Usage, Description, Aliases] |         [NadekoCommand, Usage, Description, Aliases] | ||||||
|   | |||||||
| @@ -3589,7 +3589,7 @@ | |||||||
|     <value>`{0}xpex Role Excluded-Role` `{0}xpex Server`</value> |     <value>`{0}xpex Role Excluded-Role` `{0}xpex Server`</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="xpexclude_desc" xml:space="preserve"> |   <data name="xpexclude_desc" xml:space="preserve"> | ||||||
|     <value>Exclude a user or a role from the xp system, or whole current server.</value> |     <value>Exclude a channel, role or current server from the xp system.</value> | ||||||
|   </data> |   </data> | ||||||
|   <data name="xpnotify_cmd" xml:space="preserve"> |   <data name="xpnotify_cmd" xml:space="preserve"> | ||||||
|     <value>xpnotify xpn</value> |     <value>xpnotify xpn</value> | ||||||
|   | |||||||
| @@ -10,6 +10,8 @@ namespace NadekoBot.Services.Database.Models | |||||||
|         public string AvatarId { get; set; } |         public string AvatarId { get; set; } | ||||||
|          |          | ||||||
|         public ClubInfo Club { get; set; } |         public ClubInfo Club { get; set; } | ||||||
|  |  | ||||||
|  |         public int TotalXp { get; set; } | ||||||
|         public DateTime LastLevelUp { get; set; } = DateTime.UtcNow; |         public DateTime LastLevelUp { get; set; } = DateTime.UtcNow; | ||||||
|         public DateTime LastXpGain { get; set; } = DateTime.MinValue; |         public DateTime LastXpGain { get; set; } = DateTime.MinValue; | ||||||
|         public XpNotificationType NotifyOnLevelUp { get; set; } |         public XpNotificationType NotifyOnLevelUp { get; set; } | ||||||
|   | |||||||
| @@ -6,5 +6,7 @@ namespace NadekoBot.Services.Database.Repositories | |||||||
|     public interface IDiscordUserRepository : IRepository<DiscordUser> |     public interface IDiscordUserRepository : IRepository<DiscordUser> | ||||||
|     { |     { | ||||||
|         DiscordUser GetOrCreate(IUser original); |         DiscordUser GetOrCreate(IUser original); | ||||||
|  |         int GetUserGlobalRanking(ulong id); | ||||||
|  |         (ulong UserId, int TotalXp)[] GetUsersXpLeaderboardFor(int page); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,9 +6,7 @@ namespace NadekoBot.Services.Database.Repositories | |||||||
|     { |     { | ||||||
|         UserXpStats GetOrCreateUser(ulong guildId, ulong userId); |         UserXpStats GetOrCreateUser(ulong guildId, ulong userId); | ||||||
|         int GetTotalUserXp(ulong userId); |         int GetTotalUserXp(ulong userId); | ||||||
|         UserXpStats[] GetUsersFor(ulong guildId, int page); |  | ||||||
|         (ulong UserId, int TotalXp)[] GetUsersFor(int page); |  | ||||||
|         int GetUserGlobalRanking(ulong userId); |  | ||||||
|         int GetUserGuildRanking(ulong userId, ulong guildId); |         int GetUserGuildRanking(ulong userId, ulong guildId); | ||||||
|  |         UserXpStats[] GetUsersFor(ulong guildId, int page); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -37,5 +37,24 @@ namespace NadekoBot.Services.Database.Repositories.Impl | |||||||
|  |  | ||||||
|             return toReturn; |             return toReturn; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public int GetUserGlobalRanking(ulong id) | ||||||
|  |         { | ||||||
|  |             return _set.Count(x => x.TotalXp >  | ||||||
|  |                 _set.Where(y => y.UserId == id) | ||||||
|  |                     .DefaultIfEmpty() | ||||||
|  |                     .Sum(y => y.TotalXp)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public (ulong UserId, int TotalXp)[] GetUsersXpLeaderboardFor(int page) | ||||||
|  |         { | ||||||
|  |             return _set | ||||||
|  |                 .OrderByDescending(x => x.TotalXp) | ||||||
|  |                 .Skip(page * 9) | ||||||
|  |                 .Take(9) | ||||||
|  |                 .AsEnumerable() | ||||||
|  |                 .Select(y => (y.UserId, y.TotalXp)) | ||||||
|  |                 .ToArray(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -43,15 +43,6 @@ namespace NadekoBot.Services.Database.Repositories.Impl | |||||||
|                 .ToArray(); |                 .ToArray(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public int GetUserGlobalRanking(ulong userId) |  | ||||||
|         { |  | ||||||
|             return _set |  | ||||||
|                 .GroupBy(x => x.UserId) |  | ||||||
|                 .Count(x => x.Sum(y => y.Xp) > _set |  | ||||||
|                     .Where(y => y.UserId == userId) |  | ||||||
|                     .Sum(y => y.Xp)) + 1; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public int GetUserGuildRanking(ulong userId, ulong guildId) |         public int GetUserGuildRanking(ulong userId, ulong guildId) | ||||||
|         { |         { | ||||||
|             return _set |             return _set | ||||||
| @@ -62,16 +53,5 @@ namespace NadekoBot.Services.Database.Repositories.Impl | |||||||
|                     .DefaultIfEmpty() |                     .DefaultIfEmpty() | ||||||
|                     .Sum())) + 1; |                     .Sum())) + 1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public (ulong UserId, int TotalXp)[] GetUsersFor(int page) |  | ||||||
|         { |  | ||||||
|             return _set.GroupBy(x => x.UserId) |  | ||||||
|                 .OrderByDescending(x => x.Sum(y => y.Xp)) |  | ||||||
|                 .Skip(page * 9) |  | ||||||
|                 .Take(9) |  | ||||||
|                 .AsEnumerable() |  | ||||||
|                 .Select(x => (x.Key, x.Sum(y => y.Xp))) |  | ||||||
|                 .ToArray(); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
		Reference in New Issue
	
	Block a user