Started work on the xp system
This commit is contained in:
parent
e5609a0708
commit
088d95340f
@ -17,6 +17,8 @@
|
||||
CurrencyDropAmountMax,
|
||||
MinimumBetAmount,
|
||||
TriviaCurrencyReward,
|
||||
XpPerMessage,
|
||||
XpMinutesTimeout,
|
||||
|
||||
//ErrorColor, //after i fix the nadekobot.cs static variables
|
||||
//OkColor
|
||||
|
1825
src/NadekoBot/Migrations/20170821085106_xp-stuff.Designer.cs
generated
Normal file
1825
src/NadekoBot/Migrations/20170821085106_xp-stuff.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
153
src/NadekoBot/Migrations/20170821085106_xp-stuff.cs
Normal file
153
src/NadekoBot/Migrations/20170821085106_xp-stuff.cs
Normal file
@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace NadekoBot.Migrations
|
||||
{
|
||||
public partial class xpstuff : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "XpMinutesTimeout",
|
||||
table: "BotConfig",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "XpPerMessage",
|
||||
table: "BotConfig",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UserXpStats",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
DateAdded = table.Column<DateTime>(nullable: true),
|
||||
GuildId = table.Column<ulong>(nullable: false),
|
||||
NotifyOnLevelUp = table.Column<bool>(nullable: false),
|
||||
UserId = table.Column<ulong>(nullable: false),
|
||||
Xp = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UserXpStats", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "XpSettings",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
DateAdded = table.Column<DateTime>(nullable: true),
|
||||
GuildConfigId = table.Column<int>(nullable: false),
|
||||
NotifyMessage = table.Column<string>(nullable: true),
|
||||
ServerExcluded = table.Column<bool>(nullable: false),
|
||||
XpRoleRewardExclusive = table.Column<bool>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_XpSettings", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_XpSettings_GuildConfigs_GuildConfigId",
|
||||
column: x => x.GuildConfigId,
|
||||
principalTable: "GuildConfigs",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ExcludedItem",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
DateAdded = table.Column<DateTime>(nullable: true),
|
||||
ItemId = table.Column<ulong>(nullable: false),
|
||||
ItemType = table.Column<int>(nullable: false),
|
||||
XpSettingsId = table.Column<int>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ExcludedItem", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ExcludedItem_XpSettings_XpSettingsId",
|
||||
column: x => x.XpSettingsId,
|
||||
principalTable: "XpSettings",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "XpRoleReward",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
DateAdded = table.Column<DateTime>(nullable: true),
|
||||
Level = table.Column<int>(nullable: false),
|
||||
RoleId = table.Column<ulong>(nullable: false),
|
||||
XpSettingsId = table.Column<int>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_XpRoleReward", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_XpRoleReward_XpSettings_XpSettingsId",
|
||||
column: x => x.XpSettingsId,
|
||||
principalTable: "XpSettings",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ExcludedItem_XpSettingsId",
|
||||
table: "ExcludedItem",
|
||||
column: "XpSettingsId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_UserXpStats_UserId_GuildId",
|
||||
table: "UserXpStats",
|
||||
columns: new[] { "UserId", "GuildId" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_XpRoleReward_XpSettingsId",
|
||||
table: "XpRoleReward",
|
||||
column: "XpSettingsId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_XpSettings_GuildConfigId",
|
||||
table: "XpSettings",
|
||||
column: "GuildConfigId",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ExcludedItem");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "UserXpStats");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "XpRoleReward");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "XpSettings");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "XpMinutesTimeout",
|
||||
table: "BotConfig");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "XpPerMessage",
|
||||
table: "BotConfig");
|
||||
}
|
||||
}
|
||||
}
|
@ -183,6 +183,10 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<int>("TriviaCurrencyReward");
|
||||
|
||||
b.Property<int>("XpMinutesTimeout");
|
||||
|
||||
b.Property<int>("XpPerMessage");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("BotConfig");
|
||||
@ -445,6 +449,26 @@ namespace NadekoBot.Migrations
|
||||
b.ToTable("EightBallResponses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<ulong>("ItemId");
|
||||
|
||||
b.Property<int>("ItemType");
|
||||
|
||||
b.Property<int?>("XpSettingsId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("XpSettingsId");
|
||||
|
||||
b.ToTable("ExcludedItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@ -1252,6 +1276,29 @@ namespace NadekoBot.Migrations
|
||||
b.ToTable("PokeGame");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<ulong>("GuildId");
|
||||
|
||||
b.Property<bool>("NotifyOnLevelUp");
|
||||
|
||||
b.Property<ulong>("UserId");
|
||||
|
||||
b.Property<int>("Xp");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId", "GuildId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("UserXpStats");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@ -1393,6 +1440,49 @@ namespace NadekoBot.Migrations
|
||||
b.ToTable("WarningPunishment");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int>("Level");
|
||||
|
||||
b.Property<ulong>("RoleId");
|
||||
|
||||
b.Property<int?>("XpSettingsId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("XpSettingsId");
|
||||
|
||||
b.ToTable("XpRoleReward");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int>("GuildConfigId");
|
||||
|
||||
b.Property<string>("NotifyMessage");
|
||||
|
||||
b.Property<bool>("ServerExcluded");
|
||||
|
||||
b.Property<bool>("XpRoleRewardExclusive");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GuildConfigId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("XpSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
|
||||
{
|
||||
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig")
|
||||
@ -1470,6 +1560,13 @@ namespace NadekoBot.Migrations
|
||||
.HasForeignKey("BotConfigId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
|
||||
{
|
||||
b.HasOne("NadekoBot.Services.Database.Models.XpSettings")
|
||||
.WithMany("ExclusionList")
|
||||
.HasForeignKey("XpSettingsId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
|
||||
{
|
||||
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
||||
@ -1707,6 +1804,21 @@ namespace NadekoBot.Migrations
|
||||
.WithMany("WarnPunishments")
|
||||
.HasForeignKey("GuildConfigId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b =>
|
||||
{
|
||||
b.HasOne("NadekoBot.Services.Database.Models.XpSettings")
|
||||
.WithMany("RoleRewards")
|
||||
.HasForeignKey("XpSettingsId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b =>
|
||||
{
|
||||
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig")
|
||||
.WithOne("XpSettings")
|
||||
.HasForeignKey("NadekoBot.Services.Database.Models.XpSettings", "GuildConfigId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Games.Services
|
||||
public readonly ImmutableArray<string> EightBallResponses;
|
||||
|
||||
private readonly Timer _t;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly CommandHandler _cmd;
|
||||
private readonly NadekoStrings _strings;
|
||||
private readonly IImagesService _images;
|
||||
private readonly Logger _log;
|
||||
@ -38,11 +38,11 @@ namespace NadekoBot.Modules.Games.Services
|
||||
|
||||
public List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
|
||||
|
||||
public GamesService(DiscordSocketClient client, IBotConfigProvider bc, IEnumerable<GuildConfig> gcs,
|
||||
public GamesService(CommandHandler cmd, IBotConfigProvider bc, IEnumerable<GuildConfig> gcs,
|
||||
NadekoStrings strings, IImagesService images, CommandHandler cmdHandler)
|
||||
{
|
||||
_bc = bc;
|
||||
_client = client;
|
||||
_cmd = cmd;
|
||||
_strings = strings;
|
||||
_images = images;
|
||||
_cmdHandler = cmdHandler;
|
||||
@ -59,7 +59,7 @@ namespace NadekoBot.Modules.Games.Services
|
||||
}, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1));
|
||||
|
||||
//plantpick
|
||||
client.MessageReceived += PotentialFlowerGeneration;
|
||||
_cmd.OnMessageNoTrigger += PotentialFlowerGeneration;
|
||||
GenerationChannels = new ConcurrentHashSet<ulong>(gcs
|
||||
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
|
||||
|
||||
@ -102,7 +102,7 @@ namespace NadekoBot.Modules.Games.Services
|
||||
private string GetText(ITextChannel ch, string key, params object[] rep)
|
||||
=> _strings.GetText(key, ch.GuildId, "Games".ToLowerInvariant(), rep);
|
||||
|
||||
private Task PotentialFlowerGeneration(SocketMessage imsg)
|
||||
private Task PotentialFlowerGeneration(IUserMessage imsg)
|
||||
{
|
||||
var msg = imsg as SocketUserMessage;
|
||||
if (msg == null || msg.Author.IsBot)
|
||||
|
34
src/NadekoBot/Modules/Xp/Extensions/Extensions.cs
Normal file
34
src/NadekoBot/Modules/Xp/Extensions/Extensions.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using NadekoBot.Modules.Xp.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Xp.Extensions
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static (int Level, int LevelXp, int LevelRequiredXp) GetLevelData(this UserXpStats stats)
|
||||
{
|
||||
var baseXp = XpService.XP_REQUIRED_LVL_1;
|
||||
|
||||
var required = baseXp;
|
||||
var totalXp = 0;
|
||||
var lvl = 1;
|
||||
while (true)
|
||||
{
|
||||
required = (int)(baseXp + baseXp / 4.0 * (lvl - 1));
|
||||
|
||||
if (required + totalXp > stats.Xp)
|
||||
break;
|
||||
|
||||
totalXp += required;
|
||||
lvl++;
|
||||
}
|
||||
|
||||
return (lvl - 1, stats.Xp - totalXp, required);
|
||||
}
|
||||
}
|
||||
}
|
18
src/NadekoBot/Modules/Xp/Services/UserCacheItem.cs
Normal file
18
src/NadekoBot/Modules/Xp/Services/UserCacheItem.cs
Normal file
@ -0,0 +1,18 @@
|
||||
namespace NadekoBot.Modules.Xp.Services
|
||||
{
|
||||
public class UserCacheItem
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public ulong GuildId { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return UserId.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is UserCacheItem uci && uci.UserId == UserId;
|
||||
}
|
||||
}
|
||||
}
|
229
src/NadekoBot/Modules/Xp/Services/XpService.cs
Normal file
229
src/NadekoBot/Modules/Xp/Services/XpService.cs
Normal file
@ -0,0 +1,229 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common.Collections;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Xp.Services
|
||||
{
|
||||
public class XpService : INService
|
||||
{
|
||||
private readonly DbService _db;
|
||||
private readonly CommandHandler _cmd;
|
||||
private readonly IBotConfigProvider _bc;
|
||||
private readonly Logger _log;
|
||||
public const int XP_REQUIRED_LVL_1 = 36;
|
||||
|
||||
private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _excludedRoles
|
||||
= new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>();
|
||||
private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _excludedChannels
|
||||
= new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>();
|
||||
private readonly ConcurrentHashSet<ulong> _excludedServers = new ConcurrentHashSet<ulong>();
|
||||
|
||||
private readonly ConcurrentHashSet<ulong> _rewardedUsers = new ConcurrentHashSet<ulong>();
|
||||
|
||||
private readonly ConcurrentQueue<UserCacheItem> _addMessageXp = new ConcurrentQueue<UserCacheItem>();
|
||||
|
||||
private readonly Timer updateXpTimer;
|
||||
private readonly Timer clearRewardedUsersTimer;
|
||||
|
||||
public XpService(CommandHandler cmd, IBotConfigProvider bc,
|
||||
IEnumerable<GuildConfig> allGuildConfigs, DbService db)
|
||||
{
|
||||
_db = db;
|
||||
_cmd = cmd;
|
||||
_bc = bc;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
_cmd.OnMessageNoTrigger += _cmd_OnMessageNoTrigger;
|
||||
|
||||
updateXpTimer = new Timer(_ =>
|
||||
{
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
while (_addMessageXp.TryDequeue(out var usr))
|
||||
{
|
||||
var usrObj = uow.Xp.GetOrCreateUser(usr.GuildId, usr.UserId);
|
||||
usrObj.Xp += _bc.BotConfig.XpPerMessage;
|
||||
}
|
||||
uow.Complete();
|
||||
}
|
||||
}, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
|
||||
|
||||
clearRewardedUsersTimer = new Timer(_ =>
|
||||
{
|
||||
_rewardedUsers.Clear();
|
||||
}, null, TimeSpan.FromSeconds(bc.BotConfig.XpMinutesTimeout), TimeSpan.FromSeconds(bc.BotConfig.XpMinutesTimeout));
|
||||
}
|
||||
|
||||
private Task _cmd_OnMessageNoTrigger(IUserMessage arg)
|
||||
{
|
||||
if (!(arg.Author is SocketGuildUser user) || user.IsBot)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var _ = Task.Run(() =>
|
||||
{
|
||||
if (!SetUserRewarded(user.Id))
|
||||
return;
|
||||
|
||||
if (_excludedChannels.TryGetValue(user.Guild.Id, out var chans) &&
|
||||
chans.Contains(arg.Channel.Id))
|
||||
return;
|
||||
|
||||
if (_excludedServers.Contains(user.Guild.Id))
|
||||
return;
|
||||
|
||||
if (_excludedRoles.TryGetValue(user.Guild.Id, out var roles) &&
|
||||
user.Roles.Any(x => roles.Contains(x.Id)))
|
||||
return;
|
||||
|
||||
_log.Info("Adding {0} xp to {1} on {2} server", _bc.BotConfig.XpPerMessage, user.ToString(), user.Guild.Name);
|
||||
_addMessageXp.Enqueue(new UserCacheItem { GuildId = user.Guild.Id, UserId = user.Id });
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public bool IsServerExcluded(ulong id)
|
||||
{
|
||||
return _excludedServers.Contains(id);
|
||||
}
|
||||
|
||||
public IEnumerable<ulong> GetExcludedRoles(ulong id)
|
||||
{
|
||||
if (_excludedRoles.TryGetValue(id, out var val))
|
||||
return val.ToArray();
|
||||
|
||||
return Enumerable.Empty<ulong>();
|
||||
}
|
||||
|
||||
public IEnumerable<ulong> GetExcludedChannels(ulong id)
|
||||
{
|
||||
if (_excludedChannels.TryGetValue(id, out var val))
|
||||
return val.ToArray();
|
||||
|
||||
return Enumerable.Empty<ulong>();
|
||||
}
|
||||
|
||||
private bool SetUserRewarded(ulong userId)
|
||||
{
|
||||
return _rewardedUsers.Add(userId);
|
||||
}
|
||||
|
||||
public UserXpStats GetUserStats(ulong guildId, ulong userId)
|
||||
{
|
||||
UserXpStats user;
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
user = uow.Xp.GetOrCreateUser(guildId, userId);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public string GenerateXpBar(int currentXp, int requiredXp)
|
||||
{
|
||||
//todo
|
||||
return $"{currentXp}/{requiredXp}";
|
||||
}
|
||||
|
||||
|
||||
//todo exclude in database
|
||||
public bool ToggleExcludeServer(ulong id)
|
||||
{
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var xpSetting = uow.GuildConfigs.XpSettingsFor(id);
|
||||
if (_excludedServers.Add(id))
|
||||
{
|
||||
xpSetting.ServerExcluded = true;
|
||||
uow.Complete();
|
||||
return true;
|
||||
}
|
||||
|
||||
_excludedServers.TryRemove(id);
|
||||
xpSetting.ServerExcluded = false;
|
||||
uow.Complete();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ToggleExcludeRole(ulong guildId, ulong rId)
|
||||
{
|
||||
var roles = _excludedRoles.GetOrAdd(guildId, _ => new ConcurrentHashSet<ulong>());
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var xpSetting = uow.GuildConfigs.XpSettingsFor(guildId);
|
||||
var excludeObj = new ExcludedItem
|
||||
{
|
||||
ItemId = rId,
|
||||
ItemType = ExcludedItemType.Role,
|
||||
};
|
||||
|
||||
if (roles.Add(rId))
|
||||
{
|
||||
|
||||
if (xpSetting.ExclusionList.Add(excludeObj))
|
||||
{
|
||||
uow.Complete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
roles.TryRemove(rId);
|
||||
|
||||
if (xpSetting.ExclusionList.Remove(excludeObj))
|
||||
{
|
||||
uow.Complete();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ToggleExcludeChannel(ulong guildId, ulong chId)
|
||||
{
|
||||
var channels = _excludedChannels.GetOrAdd(guildId, _ => new ConcurrentHashSet<ulong>());
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var xpSetting = uow.GuildConfigs.XpSettingsFor(guildId);
|
||||
var excludeObj = new ExcludedItem
|
||||
{
|
||||
ItemId = chId,
|
||||
ItemType = ExcludedItemType.Channel,
|
||||
};
|
||||
|
||||
if (channels.Add(chId))
|
||||
{
|
||||
|
||||
if (xpSetting.ExclusionList.Add(excludeObj))
|
||||
{
|
||||
uow.Complete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
channels.TryRemove(chId);
|
||||
|
||||
if (xpSetting.ExclusionList.Remove(excludeObj))
|
||||
{
|
||||
uow.Complete();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
105
src/NadekoBot/Modules/Xp/Xp.cs
Normal file
105
src/NadekoBot/Modules/Xp/Xp.cs
Normal file
@ -0,0 +1,105 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Xp.Extensions;
|
||||
using NadekoBot.Modules.Xp.Services;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Xp
|
||||
{
|
||||
public partial class Xp : NadekoTopLevelModule<XpService>
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Experience(IUser user = null)
|
||||
{
|
||||
user = user ?? Context.User;
|
||||
await Task.Delay(64).ConfigureAwait(false); // wait a bit in case user got XP with this message
|
||||
|
||||
var stats = _service.GetUserStats(Context.Guild.Id, user.Id);
|
||||
|
||||
var levelData = stats.GetLevelData();
|
||||
var xpBarStr = _service.GenerateXpBar(levelData.LevelXp, levelData.LevelRequiredXp);
|
||||
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder()
|
||||
.WithTitle(user.ToString())
|
||||
//.AddField(GetText("server_level"), stats.ServerLevel.ToString(), true)
|
||||
.AddField(GetText("level"), levelData.Level.ToString(), true)
|
||||
//.AddField(GetText("club"), stats.ClubName ?? "-", true)
|
||||
.AddField(GetText("xp"), xpBarStr, false)
|
||||
.WithOkColor())
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public enum Server { Server };
|
||||
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
//[RequireContext(ContextType.Guild)]
|
||||
//[OwnerOnly]
|
||||
//[Priority(1)]
|
||||
//public async Task XpExclude(Server _, IGuild guild)
|
||||
//{
|
||||
//}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.Administrator)]
|
||||
public async Task XpExclude(Server _)
|
||||
{
|
||||
var ex = _service.ToggleExcludeServer(Context.Guild.Id);
|
||||
|
||||
await ReplyConfirmLocalized((ex ? "excluded" : "not_excluded"), Format.Bold(Context.Guild.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public enum Role { Role };
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task XpExclude(Role _, [Remainder] IRole role)
|
||||
{
|
||||
var ex = _service.ToggleExcludeRole(Context.Guild.Id, role.Id);
|
||||
|
||||
await ReplyConfirmLocalized((ex ? "excluded" : "not_excluded"), Format.Bold(role.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public enum Channel { Channel };
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireUserPermission(GuildPermission.ManageChannels)]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task XpExclude(Channel _, [Remainder] ITextChannel channel)
|
||||
{
|
||||
var ex = _service.ToggleExcludeChannel(Context.Guild.Id, channel.Id);
|
||||
|
||||
await ReplyConfirmLocalized((ex ? "excluded" : "not_excluded"), Format.Bold(channel.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task XpExclusionList()
|
||||
{
|
||||
var serverExcluded = _service.IsServerExcluded(Context.Guild.Id);
|
||||
var roles = _service.GetExcludedRoles(Context.Guild.Id)
|
||||
.Select(x => Context.Guild.GetRole(x)?.Name)
|
||||
.Where(x => x != null);
|
||||
|
||||
var chans = (await Task.WhenAll(_service.GetExcludedChannels(Context.Guild.Id)
|
||||
.Select(x => Context.Guild.GetChannelAsync(x)))
|
||||
.ConfigureAwait(false))
|
||||
.Where(x => x != null)
|
||||
.Select(x => x.Name);
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle(GetText("exclusion_list"))
|
||||
.WithDescription((serverExcluded ? GetText("server_is_excluded") : GetText("server_is_not_excluded")))
|
||||
.AddField(GetText("excluded_roles"), roles.Any() ? string.Join("\n", roles) : "-", false)
|
||||
.AddField(GetText("excluded_channels"), chans.Any() ? string.Join("\n", chans) : "-", false)
|
||||
.WithOkColor();
|
||||
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
@ -91,4 +91,10 @@
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
|
||||
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources\CommandStrings.Designer.cs">
|
||||
<SubType>Designer</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -3573,4 +3573,31 @@
|
||||
<data name="nsfwtagblacklist_desc" xml:space="preserve">
|
||||
<value>Toggles whether the tag is blacklisted or not in nsfw searches. Provide no parameters to see the list of blacklisted tags.</value>
|
||||
</data>
|
||||
<data name="experience_cmd" xml:space="preserve">
|
||||
<value>experience xp</value>
|
||||
</data>
|
||||
<data name="experience_usage" xml:space="preserve">
|
||||
<value>`{0}xp`</value>
|
||||
</data>
|
||||
<data name="experience_desc" xml:space="preserve">
|
||||
<value>Shows your xp stats. Specify the user to show that user's stats instead.</value>
|
||||
</data>
|
||||
<data name="xpexclusionlist_cmd" xml:space="preserve">
|
||||
<value>xpexclusionlist xpexl</value>
|
||||
</data>
|
||||
<data name="xpexclusionlist_usage" xml:space="preserve">
|
||||
<value>`{0}xpexl`</value>
|
||||
</data>
|
||||
<data name="xpexclusionlist_desc" xml:space="preserve">
|
||||
<value>Shows the roles and channels excluded from the XP system on this server, as well as whether the whole server is excluded.</value>
|
||||
</data>
|
||||
<data name="xpexclude_cmd" xml:space="preserve">
|
||||
<value>xpexclude xpex</value>
|
||||
</data>
|
||||
<data name="xpexclude_usage" xml:space="preserve">
|
||||
<value>`{0}xpex User @b1nzy` `{0}xpex Server`</value>
|
||||
</data>
|
||||
<data name="xpexclude_desc" xml:space="preserve">
|
||||
<value>Exclude a user or a role from the xp system, or whole current server.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
@ -24,6 +24,7 @@ namespace NadekoBot.Services.Database
|
||||
IWaifuRepository Waifus { get; }
|
||||
IDiscordUserRepository DiscordUsers { get; }
|
||||
IWarningsRepository Warnings { get; }
|
||||
IXpRepository Xp { get; }
|
||||
|
||||
int Complete();
|
||||
Task<int> CompleteAsync();
|
||||
|
@ -68,6 +68,8 @@ Nadeko Support Server: https://discord.gg/nadekobot";
|
||||
public int PermissionVersion { get; set; }
|
||||
public string DefaultPrefix { get; set; } = ".";
|
||||
public bool CustomReactionsStartWith { get; set; } = false;
|
||||
public int XpPerMessage { get; set; } = 3;
|
||||
public int XpMinutesTimeout { get; set; } = 5;
|
||||
}
|
||||
|
||||
public class BlockedCmdOrMdl : DbEntity
|
||||
|
@ -86,6 +86,8 @@ namespace NadekoBot.Services.Database.Models
|
||||
|
||||
public StreamRoleSettings StreamRole { get; set; }
|
||||
|
||||
public XpSettings XpSettings { get; set; }
|
||||
|
||||
//public List<ProtectionIgnoredChannel> ProtectionIgnoredChannels { get; set; } = new List<ProtectionIgnoredChannel>();
|
||||
}
|
||||
|
||||
|
10
src/NadekoBot/Services/Database/Models/UserXpStats.cs
Normal file
10
src/NadekoBot/Services/Database/Models/UserXpStats.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace NadekoBot.Services.Database.Models
|
||||
{
|
||||
public class UserXpStats : DbEntity
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public ulong GuildId { get; set; }
|
||||
public int Xp { get; set; }
|
||||
public bool NotifyOnLevelUp { get; set; }
|
||||
}
|
||||
}
|
50
src/NadekoBot/Services/Database/Models/XpSettings.cs
Normal file
50
src/NadekoBot/Services/Database/Models/XpSettings.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NadekoBot.Services.Database.Models
|
||||
{
|
||||
public class XpSettings : DbEntity
|
||||
{
|
||||
public int GuildConfigId { get; set; }
|
||||
public GuildConfig GuildConfig { get; set; }
|
||||
|
||||
public HashSet<XpRoleReward> RoleRewards { get; set; } = new HashSet<XpRoleReward>();
|
||||
public bool XpRoleRewardExclusive { get; set; }
|
||||
public string NotifyMessage { get; set; } = "Congratulations {0}! You have reached level {1}!";
|
||||
public HashSet<ExcludedItem> ExclusionList { get; set; } = new HashSet<ExcludedItem>();
|
||||
public bool ServerExcluded { get; set; }
|
||||
}
|
||||
|
||||
public enum ExcludedItemType { Channel, Role }
|
||||
|
||||
public class XpRoleReward : DbEntity
|
||||
{
|
||||
public int Level { get; set; }
|
||||
public ulong RoleId { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Level.GetHashCode() ^ RoleId.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is XpRoleReward xrr && xrr.Level == Level && xrr.RoleId == RoleId;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExcludedItem : DbEntity
|
||||
{
|
||||
public ulong ItemId { get; set; }
|
||||
public ExcludedItemType ItemType { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ItemId.GetHashCode() ^ ItemType.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ExcludedItem ei && ei.ItemId == ItemId && ei.ItemType == ItemType;
|
||||
}
|
||||
}
|
||||
}
|
@ -43,6 +43,7 @@ namespace NadekoBot.Services.Database
|
||||
public DbSet<UserPokeTypes> PokeGame { get; set; }
|
||||
public DbSet<WaifuUpdate> WaifuUpdates { get; set; }
|
||||
public DbSet<Warning> Warnings { get; set; }
|
||||
public DbSet<UserXpStats> UserXpStats { get; set; }
|
||||
|
||||
//logging
|
||||
public DbSet<LogSetting> LogSettings { get; set; }
|
||||
@ -292,6 +293,18 @@ namespace NadekoBot.Services.Database
|
||||
pr.HasIndex(x => x.UserId)
|
||||
.IsUnique();
|
||||
#endregion
|
||||
|
||||
#region XpStatas
|
||||
modelBuilder.Entity<UserXpStats>()
|
||||
.HasIndex(x => new { x.UserId, x.GuildId })
|
||||
.IsUnique();
|
||||
#endregion
|
||||
|
||||
#region XpSettings
|
||||
modelBuilder.Entity<XpSettings>()
|
||||
.HasOne(x => x.GuildConfig)
|
||||
.WithOne(x => x.XpSettings);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,5 +16,6 @@ namespace NadekoBot.Services.Database.Repositories
|
||||
void SetCleverbotEnabled(ulong id, bool cleverbotEnabled);
|
||||
IEnumerable<GuildConfig> Permissionsv2ForAll(List<long> include);
|
||||
GuildConfig GcWithPermissionsv2For(ulong guildId);
|
||||
XpSettings XpSettingsFor(ulong guildId);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Services.Database.Repositories
|
||||
{
|
||||
public interface IXpRepository : IRepository<UserXpStats>
|
||||
{
|
||||
UserXpStats GetOrCreateUser(ulong guildId, ulong userId);
|
||||
}
|
||||
}
|
@ -189,5 +189,19 @@ namespace NadekoBot.Services.Database.Repositories.Impl
|
||||
|
||||
conf.CleverbotEnabled = cleverbotEnabled;
|
||||
}
|
||||
|
||||
public XpSettings XpSettingsFor(ulong guildId)
|
||||
{
|
||||
var gc = For(guildId,
|
||||
set => set.Include(x => x.XpSettings)
|
||||
.ThenInclude(x => x.RoleRewards)
|
||||
.Include(x => x.XpSettings)
|
||||
.ThenInclude(x => x.ExclusionList));
|
||||
|
||||
if (gc.XpSettings == null)
|
||||
gc.XpSettings = new XpSettings();
|
||||
|
||||
return gc.XpSettings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace NadekoBot.Services.Database.Repositories.Impl
|
||||
{
|
||||
public class XpRepository : Repository<UserXpStats>, IXpRepository
|
||||
{
|
||||
public XpRepository(DbContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public UserXpStats GetOrCreateUser(ulong guildId, ulong userId)
|
||||
{
|
||||
var usr = _set.FirstOrDefault(x => x.UserId == userId);
|
||||
|
||||
if (usr == null)
|
||||
{
|
||||
_context.Add(usr = new UserXpStats()
|
||||
{
|
||||
Xp = 0,
|
||||
UserId = userId,
|
||||
NotifyOnLevelUp = false,
|
||||
GuildId = guildId,
|
||||
});
|
||||
}
|
||||
|
||||
return usr;
|
||||
}
|
||||
}
|
||||
}
|
@ -57,6 +57,9 @@ namespace NadekoBot.Services.Database
|
||||
private IWarningsRepository _warnings;
|
||||
public IWarningsRepository Warnings => _warnings ?? (_warnings = new WarningsRepository(_context));
|
||||
|
||||
private IXpRepository _xp;
|
||||
public IXpRepository Xp => _xp ?? (_xp = new XpRepository(_context));
|
||||
|
||||
public UnitOfWork(NadekoContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
@ -122,6 +122,18 @@ namespace NadekoBot.Services.Impl
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case BotConfigEditType.XpPerMessage:
|
||||
if (int.TryParse(newValue, out var xp) && xp > 0)
|
||||
bc.XpPerMessage = xp;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case BotConfigEditType.XpMinutesTimeout:
|
||||
if (int.TryParse(newValue, out var min) && min > 0)
|
||||
bc.XpMinutesTimeout = min;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -822,5 +822,16 @@
|
||||
"administration_prefix_current": "Prefix on this server is {0}",
|
||||
"administration_prefix_new": "Changed prefix on this server from {0} to {1}",
|
||||
"administration_defprefix_current": "Default bot prefix is {0}",
|
||||
"administration_defprefix_new": "Changed Default bot prefix from {0} to {1}"
|
||||
"administration_defprefix_new": "Changed Default bot prefix from {0} to {1}",
|
||||
"xp_server_level": "Server Level",
|
||||
"xp_level": "Level",
|
||||
"xp_club": "Club",
|
||||
"xp_xp": "Experience",
|
||||
"xp_excluded": "{0} has been excluded from the XP system on this server.",
|
||||
"xp_not_excluded": "{0} is no longer excluded from the XP system on this server.",
|
||||
"xp_exclusion_list": "Exclusion List",
|
||||
"xp_server_is_excluded": "This server is excluded.",
|
||||
"xp_server_is_not_excluded": "This server is not excluded.",
|
||||
"xp_excluded_roles": "Excluded Roles",
|
||||
"xp_excluded_channels": "Excluded Channels"
|
||||
}
|
Loading…
Reference in New Issue
Block a user