Currency level up rewards (.xpcr) Bot owner only.

This commit is contained in:
Master Kwoth 2017-11-15 14:01:40 +01:00
parent 461dfd553f
commit 43ed332f84
7 changed files with 2170 additions and 37 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace NadekoBot.Migrations
{
public partial class currencylevelupreward : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "XpCurrencyReward",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Amount = table.Column<int>(type: "INTEGER", nullable: false),
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true),
Level = table.Column<int>(type: "INTEGER", nullable: false),
XpSettingsId = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_XpCurrencyReward", x => x.Id);
table.ForeignKey(
name: "FK_XpCurrencyReward_XpSettings_XpSettingsId",
column: x => x.XpSettingsId,
principalTable: "XpSettings",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_XpCurrencyReward_XpSettingsId",
table: "XpCurrencyReward",
column: "XpSettingsId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "XpCurrencyReward");
}
}
}

View File

@ -1551,6 +1551,26 @@ namespace NadekoBot.Migrations
b.ToTable("WarningPunishment"); b.ToTable("WarningPunishment");
}); });
modelBuilder.Entity("NadekoBot.Core.Services.Database.Models.XpCurrencyReward", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("Amount");
b.Property<DateTime?>("DateAdded");
b.Property<int>("Level");
b.Property<int>("XpSettingsId");
b.HasKey("Id");
b.HasIndex("XpSettingsId");
b.ToTable("XpCurrencyReward");
});
modelBuilder.Entity("NadekoBot.Core.Services.Database.Models.XpRoleReward", b => modelBuilder.Entity("NadekoBot.Core.Services.Database.Models.XpRoleReward", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -1972,6 +1992,14 @@ namespace NadekoBot.Migrations
.HasForeignKey("GuildConfigId"); .HasForeignKey("GuildConfigId");
}); });
modelBuilder.Entity("NadekoBot.Core.Services.Database.Models.XpCurrencyReward", b =>
{
b.HasOne("NadekoBot.Core.Services.Database.Models.XpSettings", "XpSettings")
.WithMany("CurrencyRewards")
.HasForeignKey("XpSettingsId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Core.Services.Database.Models.XpRoleReward", b => modelBuilder.Entity("NadekoBot.Core.Services.Database.Models.XpRoleReward", b =>
{ {
b.HasOne("NadekoBot.Core.Services.Database.Models.XpSettings", "XpSettings") b.HasOne("NadekoBot.Core.Services.Database.Models.XpSettings", "XpSettings")

View File

@ -36,6 +36,8 @@ namespace NadekoBot.Modules.Xp.Services
private readonly NadekoStrings _strings; private readonly NadekoStrings _strings;
private readonly IDataCache _cache; private readonly IDataCache _cache;
private readonly FontProvider _fonts; private readonly FontProvider _fonts;
private readonly IBotCredentials _creds;
private readonly CurrencyService _cs;
public const int XP_REQUIRED_LVL_1 = 36; public const int XP_REQUIRED_LVL_1 = 36;
private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _excludedRoles private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _excludedRoles
@ -60,7 +62,7 @@ namespace NadekoBot.Modules.Xp.Services
public XpService(CommandHandler cmd, IBotConfigProvider bc, public XpService(CommandHandler cmd, IBotConfigProvider bc,
NadekoBot bot, DbService db, NadekoStrings strings, IDataCache cache, NadekoBot bot, DbService db, NadekoStrings strings, IDataCache cache,
FontProvider fonts) FontProvider fonts, IBotCredentials creds, CurrencyService cs)
{ {
_db = db; _db = db;
_cmd = cmd; _cmd = cmd;
@ -70,6 +72,8 @@ namespace NadekoBot.Modules.Xp.Services
_strings = strings; _strings = strings;
_cache = cache; _cache = cache;
_fonts = fonts; _fonts = fonts;
_creds = creds;
_cs = cs;
//load settings //load settings
var allGuildConfigs = bot.AllGuildConfigs.Where(x => x.XpSettings != null); var allGuildConfigs = bot.AllGuildConfigs.Where(x => x.XpSettings != null);
@ -105,6 +109,7 @@ namespace NadekoBot.Modules.Xp.Services
{ {
var toNotify = new List<(IMessageChannel MessageChannel, IUser User, int Level, XpNotificationType NotifyType, NotifOf NotifOf)>(); var toNotify = new List<(IMessageChannel MessageChannel, IUser User, int Level, XpNotificationType NotifyType, NotifOf NotifOf)>();
var roleRewards = new Dictionary<ulong, List<XpRoleReward>>(); var roleRewards = new Dictionary<ulong, List<XpRoleReward>>();
var curRewards = new Dictionary<ulong, List<XpCurrencyReward>>();
var toAddTo = new List<UserCacheItem>(); var toAddTo = new List<UserCacheItem>();
while (_addMessageXp.TryDequeue(out var usr)) while (_addMessageXp.TryDequeue(out var usr))
@ -120,14 +125,13 @@ namespace NadekoBot.Modules.Xp.Services
{ {
var xp = item.Select(x => bc.BotConfig.XpPerMessage).Sum(); var xp = item.Select(x => bc.BotConfig.XpPerMessage).Sum();
//1. Mass query discord users and userxpstats and get them from local dict
//2. (better but much harder) Move everything to the database, and get old and new xp
// amounts for every user (in order to give rewards)
var usr = uow.Xp.GetOrCreateUser(item.Key.GuildId, item.Key.User.Id); var usr = uow.Xp.GetOrCreateUser(item.Key.GuildId, item.Key.User.Id);
var du = uow.DiscordUsers.GetOrCreate(item.Key.User); var du = uow.DiscordUsers.GetOrCreate(item.Key.User);
if (du.LastXpGain + TimeSpan.FromMinutes(_bc.BotConfig.XpMinutesTimeout) > DateTime.UtcNow)
continue;
du.LastXpGain = DateTime.UtcNow;
var globalXp = du.TotalXp; var globalXp = du.TotalXp;
var oldGlobalLevelData = new LevelStats(globalXp); var oldGlobalLevelData = new LevelStats(globalXp);
var newGlobalLevelData = new LevelStats(globalXp + xp); var newGlobalLevelData = new LevelStats(globalXp + xp);
@ -156,21 +160,34 @@ namespace NadekoBot.Modules.Xp.Services
toNotify.Add((first.Channel, first.User, newGuildLevelData.Level, usr.NotifyOnLevelUp, NotifOf.Server)); toNotify.Add((first.Channel, first.User, newGuildLevelData.Level, usr.NotifyOnLevelUp, NotifOf.Server));
//give role //give role
if (!roleRewards.TryGetValue(usr.GuildId, out var rewards)) if (!roleRewards.TryGetValue(usr.GuildId, out var rrews))
{ {
rewards = uow.GuildConfigs.XpSettingsFor(usr.GuildId).RoleRewards.ToList(); rrews = uow.GuildConfigs.XpSettingsFor(usr.GuildId).RoleRewards.ToList();
roleRewards.Add(usr.GuildId, rewards); roleRewards.Add(usr.GuildId, rrews);
} }
var rew = rewards.FirstOrDefault(x => x.Level == newGuildLevelData.Level); if (!curRewards.TryGetValue(usr.GuildId, out var crews))
if (rew != null)
{ {
var role = first.User.Guild.GetRole(rew.RoleId); crews = uow.GuildConfigs.XpSettingsFor(usr.GuildId).CurrencyRewards.ToList();
curRewards.Add(usr.GuildId, crews);
}
var rrew = rrews.FirstOrDefault(x => x.Level == newGuildLevelData.Level);
if (rrew != null)
{
var role = first.User.Guild.GetRole(rrew.RoleId);
if (role != null) if (role != null)
{ {
var __ = first.User.AddRoleAsync(role); var __ = first.User.AddRoleAsync(role);
} }
} }
//get currency reward for this level
var crew = crews.FirstOrDefault(x => x.Level == newGuildLevelData.Level);
if (crew != null)
{
//give the user the reward if it exists
await _cs.AddAsync(item.Key.User.Id, "Level-up Reward", crew.Amount, uow);
}
} }
} }
@ -429,7 +446,13 @@ namespace NadekoBot.Modules.Xp.Services
private bool SetUserRewarded(ulong userId) private bool SetUserRewarded(ulong userId)
{ {
return _rewardedUsers.Add(userId); var r = _cache.Redis.GetDatabase();
var key = $"{_creds.RedisKey()}_user_xp_gain_{userId}";
return r.StringSet(key,
true,
TimeSpan.FromMinutes(_bc.BotConfig.XpMinutesTimeout),
StackExchange.Redis.When.NotExists);
} }
public FullUserStats GetUserStats(IGuildUser user) public FullUserStats GetUserStats(IGuildUser user)

View File

@ -17,11 +17,13 @@ namespace NadekoBot.Modules.Xp
{ {
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly DbService _db; private readonly DbService _db;
private readonly IBotConfigProvider _bc;
public Xp(DiscordSocketClient client,DbService db) public Xp(DiscordSocketClient client,DbService db, IBotConfigProvider bc)
{ {
_client = client; _client = client;
_db = db; _db = db;
_bc = bc;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -45,34 +47,39 @@ namespace NadekoBot.Modules.Xp
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public Task XpRoleRewards(int page = 1) public Task XpLevelUpRewards(int page = 1)
{ {
page--; page--;
if (page < 0 || page > 100) if (page < 0 || page > 100)
return Task.CompletedTask; return Task.CompletedTask;
var roles = _service.GetRoleRewards(Context.Guild.Id)
.OrderBy(x => x.Level)
.Skip(page * 9)
.Take(9);
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithTitle(GetText("level_up_rewards")) .WithTitle(GetText("level_up_rewards"))
.WithOkColor(); .WithOkColor();
if (!roles.Any()) var rewards = _service.GetRoleRewards(Context.Guild.Id)
return Context.Channel.EmbedAsync(embed.WithDescription(GetText("no_role_rewards"))); .OrderBy(x => x.Level)
.Select(x =>
{
var str = Context.Guild.GetRole(x.RoleId)?.ToString();
if (str != null)
str = GetText("role_reward", Format.Bold(str));
return (x.Level, RoleStr: str);
})
.Where(x => x.RoleStr != null)
.Concat(_service.GetCurrencyRewards(Context.Guild.Id)
.OrderBy(x => x.Level)
.Select(x => (x.Level, Format.Bold(x.Amount + _bc.BotConfig.CurrencySign))))
.GroupBy(x => x.Level)
.OrderBy(x => x.Key)
.Skip(page * 9)
.Take(9)
.ForEach(x => embed.AddField(GetText("level_x", x.Key), string.Join("\n", x.Select(y => y.Item2))));
foreach (var rolerew in roles) if (!rewards.Any())
{ return Context.Channel.EmbedAsync(embed.WithDescription(GetText("no_level_up_rewards")));
var role = Context.Guild.GetRole(rolerew.RoleId);
if (role == null)
continue;
embed.AddField(GetText("level_x", Format.Bold(rolerew.Level.ToString())), role.ToString());
}
return Context.Channel.EmbedAsync(embed); return Context.Channel.EmbedAsync(embed);
} }
@ -104,7 +111,7 @@ namespace NadekoBot.Modules.Xp
if (amount == 0) if (amount == 0)
await ReplyConfirmLocalized("cur_reward_cleared", level).ConfigureAwait(false); await ReplyConfirmLocalized("cur_reward_cleared", level).ConfigureAwait(false);
else else
await ReplyConfirmLocalized("cur_reward_added", level, Format.Bold(amount.ToString())).ConfigureAwait(false); await ReplyConfirmLocalized("cur_reward_added", level, Format.Bold(amount + _bc.BotConfig.CurrencySign)).ConfigureAwait(false);
} }
public enum NotifyPlace public enum NotifyPlace

View File

@ -845,9 +845,10 @@
"xp_role_reward_added": "Users who reach level {0} will receive {1} role.", "xp_role_reward_added": "Users who reach level {0} will receive {1} role.",
"xp_cur_reward_cleared": "Reaching level {0} will no longer reward any {1}.", "xp_cur_reward_cleared": "Reaching level {0} will no longer reward any {1}.",
"xp_cur_reward_added": "Users who reach level {0} will receive {1}.", "xp_cur_reward_added": "Users who reach level {0} will receive {1}.",
"xp_role_rewards": "Role Rewards", "xp_level_up_rewards": "Level Up Rewards",
"xp_level_x": "Level {0}", "xp_level_x": "Level {0}",
"xp_no_role_rewards": "No role reward on this page.", "xp_role_reward": "{0} role",
"xp_no_level_up_rewards": "No level up reward on this page.",
"xp_server_leaderboard": "Server XP Leaderboard", "xp_server_leaderboard": "Server XP Leaderboard",
"xp_global_leaderboard": "Global XP Leaderboard", "xp_global_leaderboard": "Global XP Leaderboard",
"xp_modified": "Modified server XP of the user {0} by {1}", "xp_modified": "Modified server XP of the user {0} by {1}",

View File

@ -2815,11 +2815,11 @@
"{0}xpn server channel" "{0}xpn server channel"
] ]
}, },
"xprolerewards": { "xpleveluprewards": {
"Cmd": "xprolerewards xprrs", "Cmd": "xplvluprewards xprews xpcrs xprrs xprolerewards xpcurrewards",
"Desc": "Shows currently set role rewards.", "Desc": "Shows currently set level up rewards.",
"Usage": [ "Usage": [
"{0}xprrs" "{0}xprews"
] ]
}, },
"xprolereward": { "xprolereward": {
@ -2829,6 +2829,13 @@
"{0}xprr 3 Social" "{0}xprr 3 Social"
] ]
}, },
"xpcurrencyreward": {
"Cmd": "xpcurreward xpcr",
"Desc": "Sets a currency reward on a specified level. Provide no amount in order to remove the reward.",
"Usage": [
"{0}xpcr 3 50"
]
},
"xpleaderboard": { "xpleaderboard": {
"Cmd": "xpleaderboard xplb", "Cmd": "xpleaderboard xplb",
"Desc": "Shows current server's xp leaderboard.", "Desc": "Shows current server's xp leaderboard.",