public override int GetHashCode() { int hash = Name.GetHashCode(); if (Icon != null) { hash ^= Icon.GetHashCode(); } hash ^= IsRepeatable.GetHashCode(); hash ^= RequiredLevel.GetHashCode(); hash ^= XpLevel.GetHashCode(); hash ^= Difficulty.GetHashCode(); hash ^= CanAbandon.GetHashCode(); hash ^= IsHidden.GetHashCode(); hash ^= IsClassQuest.GetHashCode(); hash ^= IsBonus.GetHashCode(); hash ^= BonusShareable.GetHashCode(); hash ^= Category.GetHashCode(); foreach (var x in Branches) { hash ^= x.GetHashCode(); } foreach (var x in Classes) { hash ^= x.Id.GetHashCode(); } return(hash); }
private async Task UpdateXp(SocketMessage message, IGuildChannel channel) { if (!await _config.XpGainEnabled(channel.Id)) { return; } using IServiceScope scope = _scopeFactory.CreateScope(); var context = scope.ServiceProvider.GetRequiredService <RokiContext>(); GuildConfig guildConfig = await _config.GetGuildConfigAsync(channel.GuildId); UserData data = await context.UserData.AsQueryable().Where(x => x.UserId == message.Author.Id && x.GuildId == channel.GuildId).SingleOrDefaultAsync(); if (data == null) { if (await context.Users.AsNoTracking().AnyAsync(x => x.Id == message.Author.Id)) { data = new UserData(message.Author.Id, channel.GuildId); } else { data = new UserData(message.Author.Id, channel.GuildId) { User = new User(message.Author.Username, message.Author.Discriminator, message.Author.AvatarId) }; } await context.UserData.AddAsync(data); } // temp // these are always false bool doubleXp = data.Xp < 0; bool fastXp = data.Xp < 0; var oldXp = new XpLevel(data.Xp); DateTime now = DateTime.UtcNow; if (fastXp && now - data.LastXpGain >= TimeSpan.FromSeconds(guildConfig.XpFastCooldown)) { data.Xp += doubleXp ? guildConfig.XpPerMessage * 2 : guildConfig.XpPerMessage; } else if (DateTime.UtcNow - data.LastXpGain >= TimeSpan.FromSeconds(guildConfig.XpCooldown)) { data.Xp += doubleXp ? guildConfig.XpPerMessage * 2 : guildConfig.XpPerMessage; } else { return; } data.LastXpGain = now; var newXp = new XpLevel(data.Xp); if (oldXp.Level == newXp.Level) { await context.SaveChangesAsync(); return; } data.LastLevelUp = now; await SendNotification(data, message, newXp.Level).ConfigureAwait(false); List <XpReward> rewards = await context.XpRewards.AsNoTracking().Where(x => x.GuildId == channel.GuildId && x.Level == newXp.Level).ToListAsync(); if (rewards.Count > 0) { var sb = new StringBuilder(); foreach (XpReward reward in rewards) { string[] parsed = reward.Details.Split(';'); if (parsed.Length < 2) { continue; } switch (parsed[0]) { case "currency": { long amount = long.Parse(parsed[1]); await _currency.AddCurrencyAsync(data.UserId, data.GuildId, channel.Id, message.Id, $"#{reward.Id} XP Level {reward.Level} Reward", amount); sb.AppendLine($"+ `{amount:N0}` {guildConfig.CurrencyIcon}"); break; } case "role": { SocketRole role = (channel.Guild as SocketGuild)?.GetRole(ulong.Parse(parsed[1])); if (role == null) { continue; } var guildUser = (IGuildUser)message.Author; await guildUser.AddRoleAsync(role).ConfigureAwait(false); sb.AppendLine(reward.Description); break; } } } IDMChannel dm = await message.Author.GetOrCreateDMChannelAsync().ConfigureAwait(false); try { await dm.EmbedAsync(new EmbedBuilder().WithOkColor() .WithTitle($"Level `{newXp.Level}` Rewards - {channel.Guild.Name}") .WithDescription("Here are your rewards:\n" + sb)) .ConfigureAwait(false); await dm.CloseAsync().ConfigureAwait(false); } catch { // unable to send dm to user // ignored } } await context.SaveChangesAsync(); }
public static MemoryStream GenerateXpBar(Stream avatar, XpLevel xp, string rank, string username, string discriminator, DateTime date, bool doubleXp, bool fastXp) { using Image image = Image.Load("data/xp/roki_xp_half.png"); var transparentGray = new Rgba32(211, 211, 211, 220); var grayBackground = new RectangleF(PfpOutlineX + (PfpLength + 10) / 2, 7.5f, 680, 185); var pfpBackground = new RectangleF(PfpOutlineX, PfpOutlineY, PfpLength + 10, PfpLength + 10); var pfpOutline = new RectangleF(PfpOutlineX, PfpOutlineY, PfpLength + 10, PfpLength + 10); var xpBarOutline = new RectangleF(XpBarX, XpBarY, XpBarLength, XpBarHeight); image.Mutate(x => x .Fill(new Color(transparentGray), grayBackground) // profile pic .Fill(Color.White, pfpBackground) .Draw(Color.WhiteSmoke, 3.5f, pfpOutline) .Fill(Color.HotPink, new RectangleF(37, 25, 150, 150)) // profile pic // xp bar .Draw(Color.HotPink, 2.5f, xpBarOutline)); if (xp.TotalXp != 0) { var xpBarProgress = new RectangleF(XpBarX + 2.5f, XpBarY + 2.5f, (XpBarLength - 5) * ((float)xp.ProgressXp / xp.RequiredXp), XpBarHeight - 5); image.Mutate(x => x.Fill(Color.HotPink, xpBarProgress)); } using (Image pfp = Image.Load(avatar)) { pfp.Mutate(x => x.Resize(new Size(PfpLength, PfpLength))); image.Mutate(x => x.DrawImage(pfp, new Point(PfpOutlineX + 5, PfpOutlineY + 5), 1)); } var fonts = new FontCollection(); fonts.Install("data/xp/DIN_REG.ttf"); fonts.Install("data/xp/DIN_MED.ttf"); Font usernameFont = fonts.CreateFont("DIN", 30); Font discriminatorFont = fonts.CreateFont("DIN", 21); Font xpFont = fonts.CreateFont("DIN", 30); Font headerFont = fonts.CreateFont("DIN Medium", 20); Font bodyFont = fonts.CreateFont("DIN", 25); image.Mutate(x => x .DrawUsername(usernameFont, discriminatorFont, username, $"#{discriminator}", new PointF(XpBarX, 50)) // username + discrim .DrawText(new TextGraphicsOptions { TextOptions = { HorizontalAlignment = HorizontalAlignment.Center } }, $"XP: {xp.TotalXp:N0} / {xp.TotalRequiredXp:N0}", xpFont, Color.DarkSlateGray, new PointF(XpBarX + XpBarLength / 2, 66)) // xp progress .DrawStats(headerFont, bodyFont, $"#{rank}", xp.Level.ToString("N0"), (xp.TotalRequiredXp - xp.TotalXp).ToString("N0"), date) .DrawBoosts(doubleXp, fastXp)); var stream = new MemoryStream(); image.SaveAsPng(stream); stream.Position = 0; return(stream); }
public async Task Experience(IUser user = null) { if (user == null || user.IsBot) { user = Context.User; } await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); long seconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); RedisValue image = await _cache.StringGetAsync($"xp:image:{user.Id}:{Context.Guild.Id}"); if (image.HasValue) { await using var stream = new MemoryStream((byte[])image); await Context.Channel.SendFileAsync(stream, $"xp-{user.Username}-{seconds}.png").ConfigureAwait(false); return; } Stream avatar; using (HttpClient http = _http.CreateClient()) { try { // handle gif avatars in future string avatarUrl = user.GetAvatarUrl(ImageFormat.Png) ?? user.GetDefaultAvatarUrl(); avatar = await http.GetStreamAsync(avatarUrl).ConfigureAwait(false); } catch (Exception) { Log.Error("Error downloading avatar"); return; } } var data = await _context.UserData.AsNoTracking() .AsQueryable() .Where(x => x.UserId == user.Id && x.GuildId == Context.Guild.Id) .Select(x => new { x.Xp, x.LastLevelUp, x.LastXpGain }) .SingleAsync(); var xp = new XpLevel(data.Xp); int rank = await GetUserXpRank(user.Id, Context.Guild.Id); bool doubleXp = await _context.Subscriptions.AsNoTracking() .AnyAsync(x => x.UserId == user.Id && x.GuildId == Context.Guild.Id && x.Item.Details == "doublexp"); bool fastXp = await _context.Subscriptions.AsNoTracking() .AnyAsync(x => x.UserId == user.Id && x.GuildId == Context.Guild.Id && x.Item.Details == "fastxp"); await using MemoryStream xpImage = XpDrawExtensions.GenerateXpBar(avatar, xp, $"{rank}", user.Username, user.Discriminator, data.LastLevelUp, doubleXp, fastXp); await avatar.DisposeAsync(); await _cache.StringSetAsync($"xp:image:{user.Id}:{Context.Guild.Id}", xpImage.ToArray(), TimeSpan.FromMinutes(5), flags : CommandFlags.FireAndForget); await Context.Channel.SendFileAsync(xpImage, $"xp-{user.Username}-{seconds}.png").ConfigureAwait(false); }