Administration almost done, logcommands left
This commit is contained in:
parent
355425bf80
commit
a4973ffbb3
@ -3,9 +3,7 @@ using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using NadekoBot.Services.Administration;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -16,31 +14,13 @@ namespace NadekoBot.Modules.Administration
|
||||
[Group]
|
||||
public class AutoAssignRoleCommands : NadekoSubmodule
|
||||
{
|
||||
//guildid/roleid
|
||||
private static ConcurrentDictionary<ulong, ulong> AutoAssignedRoles { get; }
|
||||
private readonly DbHandler _db;
|
||||
private readonly AutoAssignRoleService _service;
|
||||
|
||||
static AutoAssignRoleCommands()
|
||||
public AutoAssignRoleCommands(AutoAssignRoleService service, DbHandler db)
|
||||
{
|
||||
var log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
|
||||
.ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
|
||||
NadekoBot.Client.UserJoined += async (user) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
AutoAssignedRoles.TryGetValue(user.Guild.Id, out ulong roleId);
|
||||
|
||||
if (roleId == 0)
|
||||
return;
|
||||
|
||||
var role = user.Guild.Roles.FirstOrDefault(r => r.Id == roleId);
|
||||
|
||||
if (role != null)
|
||||
await user.AddRoleAsync(role).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { log.Warn(ex); }
|
||||
};
|
||||
_db = db;
|
||||
_service = service;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -53,18 +33,18 @@ namespace NadekoBot.Modules.Administration
|
||||
if (Context.User.Id != guser.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= role.Position)
|
||||
return;
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||
if (role == null)
|
||||
{
|
||||
conf.AutoAssignRoleId = 0;
|
||||
AutoAssignedRoles.TryRemove(Context.Guild.Id, out ulong throwaway);
|
||||
_service.AutoAssignedRoles.TryRemove(Context.Guild.Id, out ulong throwaway);
|
||||
}
|
||||
else
|
||||
{
|
||||
conf.AutoAssignRoleId = role.Id;
|
||||
AutoAssignedRoles.AddOrUpdate(Context.Guild.Id, role.Id, (key, val) => role.Id);
|
||||
_service.AutoAssignedRoles.AddOrUpdate(Context.Guild.Id, role.Id, (key, val) => role.Id);
|
||||
}
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
|
@ -1,19 +1,9 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.WebSocket;
|
||||
using NLog;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services.Administration;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
@ -22,64 +12,13 @@ namespace NadekoBot.Modules.Administration
|
||||
[Group]
|
||||
public class GameChannelCommands : NadekoSubmodule
|
||||
{
|
||||
//private static readonly Timer _t;
|
||||
private readonly DbHandler _db;
|
||||
private readonly GameVoiceChannelService _service;
|
||||
|
||||
private static readonly ConcurrentHashSet<ulong> gameVoiceChannels = new ConcurrentHashSet<ulong>();
|
||||
|
||||
private static new readonly Logger _log;
|
||||
|
||||
static GameChannelCommands()
|
||||
public GameChannelCommands(GameVoiceChannelService service, DbHandler db)
|
||||
{
|
||||
//_t = new Timer(_ => {
|
||||
|
||||
//}, null, );
|
||||
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
gameVoiceChannels = new ConcurrentHashSet<ulong>(
|
||||
NadekoBot.AllGuildConfigs.Where(gc => gc.GameVoiceChannel != null)
|
||||
.Select(gc => gc.GameVoiceChannel.Value));
|
||||
|
||||
NadekoBot.Client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
|
||||
|
||||
}
|
||||
|
||||
private static Task Client_UserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState, SocketVoiceState newState)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var gUser = usr as SocketGuildUser;
|
||||
if (gUser == null)
|
||||
return;
|
||||
|
||||
var game = gUser.Game?.Name.TrimTo(50).ToLowerInvariant();
|
||||
|
||||
if (oldState.VoiceChannel == newState.VoiceChannel ||
|
||||
newState.VoiceChannel == null)
|
||||
return;
|
||||
|
||||
if (!gameVoiceChannels.Contains(newState.VoiceChannel.Id) ||
|
||||
string.IsNullOrWhiteSpace(game))
|
||||
return;
|
||||
|
||||
var vch = gUser.Guild.VoiceChannels
|
||||
.FirstOrDefault(x => x.Name.ToLowerInvariant() == game);
|
||||
|
||||
if (vch == null)
|
||||
return;
|
||||
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
await gUser.ModifyAsync(gu => gu.Channel = vch).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
_db = db;
|
||||
_service = service;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -96,20 +35,20 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
}
|
||||
ulong? id;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||
|
||||
if (gc.GameVoiceChannel == vch.Id)
|
||||
{
|
||||
gameVoiceChannels.TryRemove(vch.Id);
|
||||
_service.GameVoiceChannels.TryRemove(vch.Id);
|
||||
id = gc.GameVoiceChannel = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(gc.GameVoiceChannel != null)
|
||||
gameVoiceChannels.TryRemove(gc.GameVoiceChannel.Value);
|
||||
gameVoiceChannels.Add(vch.Id);
|
||||
_service.GameVoiceChannels.TryRemove(gc.GameVoiceChannel.Value);
|
||||
_service.GameVoiceChannels.Add(vch.Id);
|
||||
id = gc.GameVoiceChannel = vch.Id;
|
||||
}
|
||||
|
||||
@ -122,7 +61,7 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
else
|
||||
{
|
||||
gameVoiceChannels.Add(vch.Id);
|
||||
_service.GameVoiceChannels.Add(vch.Id);
|
||||
await ReplyConfirmLocalized("gvc_enabled", Format.Bold(vch.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
//Română, România
|
||||
//Bahasa Indonesia, Indonesia
|
||||
private ImmutableDictionary<string, string> supportedLocales { get; } = new Dictionary<string, string>()
|
||||
private static ImmutableDictionary<string, string> supportedLocales { get; } = new Dictionary<string, string>()
|
||||
{
|
||||
//{"ar", "العربية" },
|
||||
{"zh-TW", "繁體中文, 台灣" },
|
||||
@ -46,7 +46,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task LanguageSet()
|
||||
{
|
||||
var cul = NadekoBot.Localization.GetCultureInfo(Context.Guild);
|
||||
var cul = _localization.GetCultureInfo(Context.Guild);
|
||||
await ReplyConfirmLocalized("lang_set_show", Format.Bold(cul.ToString()), Format.Bold(cul.NativeName))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
@ -61,13 +61,13 @@ namespace NadekoBot.Modules.Administration
|
||||
CultureInfo ci;
|
||||
if (name.Trim().ToLowerInvariant() == "default")
|
||||
{
|
||||
NadekoBot.Localization.RemoveGuildCulture(Context.Guild);
|
||||
ci = NadekoBot.Localization.DefaultCultureInfo;
|
||||
_localization.RemoveGuildCulture(Context.Guild);
|
||||
ci = _localization.DefaultCultureInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
ci = new CultureInfo(name);
|
||||
NadekoBot.Localization.SetGuildCulture(Context.Guild, ci);
|
||||
_localization.SetGuildCulture(Context.Guild, ci);
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalized("lang_set", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)).ConfigureAwait(false);
|
||||
@ -81,7 +81,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task LanguageSetDefault()
|
||||
{
|
||||
var cul = NadekoBot.Localization.DefaultCultureInfo;
|
||||
var cul = _localization.DefaultCultureInfo;
|
||||
await ReplyConfirmLocalized("lang_set_bot_show", cul, cul.NativeName).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -94,13 +94,13 @@ namespace NadekoBot.Modules.Administration
|
||||
CultureInfo ci;
|
||||
if (name.Trim().ToLowerInvariant() == "default")
|
||||
{
|
||||
NadekoBot.Localization.ResetDefaultCulture();
|
||||
ci = NadekoBot.Localization.DefaultCultureInfo;
|
||||
_localization.ResetDefaultCulture();
|
||||
ci = _localization.DefaultCultureInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
ci = new CultureInfo(name);
|
||||
NadekoBot.Localization.SetDefaultCulture(ci);
|
||||
_localization.SetDefaultCulture(ci);
|
||||
}
|
||||
await ReplyConfirmLocalized("lang_set_bot", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)).ConfigureAwait(false);
|
||||
}
|
||||
|
@ -35,11 +35,11 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
static LogCommands()
|
||||
{
|
||||
Client = NadekoBot.Client;
|
||||
Client = _client;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
GuildLogSettings = new ConcurrentDictionary<ulong, LogSetting>(NadekoBot.AllGuildConfigs
|
||||
GuildLogSettings = new ConcurrentDictionary<ulong, LogSetting>(gcs
|
||||
.ToDictionary(g => g.GuildId, g => g.LogSetting));
|
||||
|
||||
_timerReference = new Timer(async (state) =>
|
||||
@ -136,7 +136,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
await logChannel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
|
||||
//var guildsMemberOf = NadekoBot.Client.GetGuilds().Where(g => g.Users.Select(u => u.Id).Contains(before.Id)).ToList();
|
||||
//var guildsMemberOf = _client.GetGuilds().Where(g => g.Users.Select(u => u.Id).Contains(before.Id)).ToList();
|
||||
//foreach (var g in guildsMemberOf)
|
||||
//{
|
||||
// LogSetting logSetting;
|
||||
@ -840,7 +840,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
private static void UnsetLogSetting(ulong guildId, LogType logChannelType)
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var newLogSetting = uow.GuildConfigs.LogSettingsFor(guildId).LogSetting;
|
||||
switch (logChannelType)
|
||||
@ -910,7 +910,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
LogSetting logSetting;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
logSetting = uow.GuildConfigs.LogSettingsFor(channel.Guild.Id).LogSetting;
|
||||
GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting);
|
||||
@ -946,7 +946,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
int removed;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.GuildConfigs.LogSettingsFor(channel.Guild.Id);
|
||||
LogSetting logSetting = GuildLogSettings.GetOrAdd(channel.Guild.Id, (id) => config.LogSetting);
|
||||
@ -986,7 +986,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
ulong? channelId = null;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var logSetting = uow.GuildConfigs.LogSettingsFor(channel.Guild.Id).LogSetting;
|
||||
GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting);
|
||||
|
@ -8,7 +8,6 @@ using NadekoBot.Attributes;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NadekoBot.Modules.Administration.Commands.Migration;
|
||||
using System.Collections.Concurrent;
|
||||
using NadekoBot.Extensions;
|
||||
@ -23,12 +22,11 @@ namespace NadekoBot.Modules.Administration
|
||||
public class Migration : NadekoSubmodule
|
||||
{
|
||||
private const int CURRENT_VERSION = 1;
|
||||
private readonly DbHandler _db;
|
||||
|
||||
private new static readonly Logger _log;
|
||||
|
||||
static Migration()
|
||||
public Migration(DbHandler db)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -36,7 +34,7 @@ namespace NadekoBot.Modules.Administration
|
||||
public async Task MigrateData()
|
||||
{
|
||||
var version = 0;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
version = uow.BotConfig.GetOrCreate().MigrationVersion;
|
||||
}
|
||||
@ -62,7 +60,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
private void Migrate0_9To1_0()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var botConfig = uow.BotConfig.GetOrCreate();
|
||||
MigrateConfig0_9(uow, botConfig);
|
||||
|
@ -1,14 +1,9 @@
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Administration;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
@ -18,101 +13,31 @@ namespace NadekoBot.Modules.Administration
|
||||
[Group]
|
||||
public class PlayingRotateCommands : NadekoSubmodule
|
||||
{
|
||||
public static List<PlayingStatus> RotatingStatusMessages { get; }
|
||||
public static volatile bool RotatingStatuses;
|
||||
private readonly object _locker = new object();
|
||||
private new static Logger _log { get; }
|
||||
private static readonly Timer _t;
|
||||
private static readonly object _locker = new object();
|
||||
private readonly DbHandler _db;
|
||||
private readonly PlayingRotateService _service;
|
||||
|
||||
private class TimerState
|
||||
public PlayingRotateCommands(PlayingRotateService service, DbHandler db)
|
||||
{
|
||||
public int Index { get; set; }
|
||||
_db = db;
|
||||
_service = service;
|
||||
}
|
||||
|
||||
static PlayingRotateCommands()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages;
|
||||
RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses;
|
||||
|
||||
_t = new Timer(async (objState) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var state = (TimerState)objState;
|
||||
if (!RotatingStatuses)
|
||||
return;
|
||||
if (state.Index >= RotatingStatusMessages.Count)
|
||||
state.Index = 0;
|
||||
|
||||
if (!RotatingStatusMessages.Any())
|
||||
return;
|
||||
var status = RotatingStatusMessages[state.Index++].Status;
|
||||
if (string.IsNullOrWhiteSpace(status))
|
||||
return;
|
||||
PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value()));
|
||||
var shards = NadekoBot.Client.Shards;
|
||||
for (int i = 0; i < shards.Count; i++)
|
||||
{
|
||||
var curShard = shards.ElementAt(i);
|
||||
ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(curShard)));
|
||||
try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn("Rotating playing status errored.\n" + ex);
|
||||
}
|
||||
}, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
||||
}
|
||||
|
||||
public static Dictionary<string, Func<string>> PlayingPlaceholders { get; } =
|
||||
new Dictionary<string, Func<string>> {
|
||||
{ "%servers%", () => NadekoBot.Client.Guilds.Count.ToString()},
|
||||
{ "%users%", () => NadekoBot.Client.Guilds.Sum(s => s.Users.Count).ToString()},
|
||||
{ "%playing%", () => {
|
||||
var cnt = NadekoBot.MusicService.MusicPlayers.Count(kvp => kvp.Value.CurrentSong != null);
|
||||
if (cnt != 1) return cnt.ToString();
|
||||
try {
|
||||
var mp = NadekoBot.MusicService.MusicPlayers.FirstOrDefault();
|
||||
return mp.Value.CurrentSong.SongInfo.Title;
|
||||
}
|
||||
catch {
|
||||
return "No songs";
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "%queued%", () => NadekoBot.MusicService.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString()},
|
||||
{ "%time%", () => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()) },
|
||||
{ "%shardcount%", () => NadekoBot.Client.Shards.Count.ToString() },
|
||||
};
|
||||
|
||||
public static Dictionary<string, Func<DiscordSocketClient, string>> ShardSpecificPlaceholders { get; } =
|
||||
new Dictionary<string, Func<DiscordSocketClient, string>> {
|
||||
{ "%shardid%", (client) => client.ShardId.ToString()},
|
||||
{ "%shardguilds%", (client) => client.Guilds.Count.ToString()},
|
||||
};
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task RotatePlaying()
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
|
||||
RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses;
|
||||
_service.RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses;
|
||||
uow.Complete();
|
||||
}
|
||||
}
|
||||
if (RotatingStatuses)
|
||||
if (_service.RotatingStatuses)
|
||||
await ReplyConfirmLocalized("ropl_enabled").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalized("ropl_disabled").ConfigureAwait(false);
|
||||
@ -122,12 +47,12 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task AddPlaying([Remainder] string status)
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
var toAdd = new PlayingStatus { Status = status };
|
||||
config.RotatingStatusMessages.Add(toAdd);
|
||||
RotatingStatusMessages.Add(toAdd);
|
||||
_service.RotatingStatusMessages.Add(toAdd);
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
|
||||
@ -138,13 +63,13 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task ListPlaying()
|
||||
{
|
||||
if (!RotatingStatusMessages.Any())
|
||||
if (!_service.RotatingStatusMessages.Any())
|
||||
await ReplyErrorLocalized("ropl_not_set").ConfigureAwait(false);
|
||||
else
|
||||
{
|
||||
var i = 1;
|
||||
await ReplyConfirmLocalized("ropl_list",
|
||||
string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")))
|
||||
string.Join("\n\t", _service.RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -157,7 +82,7 @@ namespace NadekoBot.Modules.Administration
|
||||
index -= 1;
|
||||
|
||||
string msg;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
|
||||
@ -165,7 +90,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
msg = config.RotatingStatusMessages[index].Status;
|
||||
config.RotatingStatusMessages.RemoveAt(index);
|
||||
RotatingStatusMessages.RemoveAt(index);
|
||||
_service.RotatingStatusMessages.RemoveAt(index);
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
await ReplyConfirmLocalized("reprm", msg).ConfigureAwait(false);
|
||||
|
@ -5,246 +5,27 @@ using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using NadekoBot.Services.Administration;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
public enum ProtectionType
|
||||
{
|
||||
Raiding,
|
||||
Spamming,
|
||||
}
|
||||
|
||||
public class AntiRaidStats
|
||||
{
|
||||
public AntiRaidSetting AntiRaidSettings { get; set; }
|
||||
public int UsersCount { get; set; }
|
||||
public ConcurrentHashSet<IGuildUser> RaidUsers { get; set; } = new ConcurrentHashSet<IGuildUser>();
|
||||
}
|
||||
|
||||
public class AntiSpamStats
|
||||
{
|
||||
public AntiSpamSetting AntiSpamSettings { get; set; }
|
||||
public ConcurrentDictionary<ulong, UserSpamStats> UserStats { get; set; }
|
||||
= new ConcurrentDictionary<ulong, UserSpamStats>();
|
||||
}
|
||||
|
||||
public class UserSpamStats : IDisposable
|
||||
{
|
||||
public int Count => timers.Count;
|
||||
public string LastMessage { get; set; }
|
||||
|
||||
private ConcurrentQueue<Timer> timers { get; }
|
||||
|
||||
public UserSpamStats(IUserMessage msg)
|
||||
{
|
||||
LastMessage = msg.Content.ToUpperInvariant();
|
||||
timers = new ConcurrentQueue<Timer>();
|
||||
|
||||
ApplyNextMessage(msg);
|
||||
}
|
||||
|
||||
private readonly object applyLock = new object();
|
||||
public void ApplyNextMessage(IUserMessage message)
|
||||
{
|
||||
lock(applyLock){
|
||||
var upperMsg = message.Content.ToUpperInvariant();
|
||||
if (upperMsg != LastMessage || (string.IsNullOrWhiteSpace(upperMsg) && message.Attachments.Any()))
|
||||
{
|
||||
LastMessage = upperMsg;
|
||||
//todo c#7
|
||||
Timer old;
|
||||
while(timers.TryDequeue(out old))
|
||||
old.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
var t = new Timer((_) => {
|
||||
//todo c#7
|
||||
Timer __;
|
||||
if(timers.TryDequeue(out __))
|
||||
__.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}, null, TimeSpan.FromMinutes(30), TimeSpan.FromMinutes(30));
|
||||
timers.Enqueue(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
//todo c#7
|
||||
Timer old;
|
||||
while(timers.TryDequeue(out old))
|
||||
old.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
[Group]
|
||||
public class ProtectionCommands : NadekoSubmodule
|
||||
{
|
||||
private static readonly ConcurrentDictionary<ulong, AntiRaidStats> _antiRaidGuilds =
|
||||
new ConcurrentDictionary<ulong, AntiRaidStats>();
|
||||
// guildId | (userId|messages)
|
||||
private static readonly ConcurrentDictionary<ulong, AntiSpamStats> _antiSpamGuilds =
|
||||
new ConcurrentDictionary<ulong, AntiSpamStats>();
|
||||
private readonly ProtectionService _service;
|
||||
private readonly MuteService _mute;
|
||||
private readonly DbHandler _db;
|
||||
|
||||
private new static readonly Logger _log;
|
||||
|
||||
static ProtectionCommands()
|
||||
public ProtectionCommands(ProtectionService service, MuteService mute, DbHandler db)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
foreach (var gc in NadekoBot.AllGuildConfigs)
|
||||
{
|
||||
var raid = gc.AntiRaidSetting;
|
||||
var spam = gc.AntiSpamSetting;
|
||||
|
||||
if (raid != null)
|
||||
{
|
||||
var raidStats = new AntiRaidStats() { AntiRaidSettings = raid };
|
||||
_antiRaidGuilds.TryAdd(gc.GuildId, raidStats);
|
||||
}
|
||||
|
||||
if (spam != null)
|
||||
_antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam });
|
||||
}
|
||||
|
||||
NadekoBot.Client.MessageReceived += (imsg) =>
|
||||
{
|
||||
var msg = imsg as IUserMessage;
|
||||
if (msg == null || msg.Author.IsBot)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var channel = msg.Channel as ITextChannel;
|
||||
if (channel == null)
|
||||
return Task.CompletedTask;
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
AntiSpamStats spamSettings;
|
||||
if (!_antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) ||
|
||||
spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore()
|
||||
{
|
||||
ChannelId = channel.Id
|
||||
}))
|
||||
return;
|
||||
|
||||
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, (id) => new UserSpamStats(msg),
|
||||
(id, old) =>
|
||||
{
|
||||
old.ApplyNextMessage(msg); return old;
|
||||
});
|
||||
|
||||
if (stats.Count >= spamSettings.AntiSpamSettings.MessageThreshold)
|
||||
{
|
||||
if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
|
||||
{
|
||||
stats.Dispose();
|
||||
await PunishUsers(spamSettings.AntiSpamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
NadekoBot.Client.UserJoined += (usr) =>
|
||||
{
|
||||
if (usr.IsBot)
|
||||
return Task.CompletedTask;
|
||||
AntiRaidStats settings;
|
||||
if (!_antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
|
||||
return Task.CompletedTask;
|
||||
if (!settings.RaidUsers.Add(usr))
|
||||
return Task.CompletedTask;
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
++settings.UsersCount;
|
||||
|
||||
if (settings.UsersCount >= settings.AntiRaidSettings.UserThreshold)
|
||||
{
|
||||
var users = settings.RaidUsers.ToArray();
|
||||
settings.RaidUsers.Clear();
|
||||
|
||||
await PunishUsers(settings.AntiRaidSettings.Action, ProtectionType.Raiding, users).ConfigureAwait(false);
|
||||
}
|
||||
await Task.Delay(1000 * settings.AntiRaidSettings.Seconds).ConfigureAwait(false);
|
||||
|
||||
settings.RaidUsers.TryRemove(usr);
|
||||
--settings.UsersCount;
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
private static async Task PunishUsers(PunishmentAction action, ProtectionType pt, params IGuildUser[] gus)
|
||||
{
|
||||
_log.Info($"[{pt}] - Punishing [{gus.Length}] users with [{action}] in {gus[0].Guild.Name} guild");
|
||||
foreach (var gu in gus)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case PunishmentAction.Mute:
|
||||
try
|
||||
{
|
||||
await MuteCommands.MuteUser(gu).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); }
|
||||
break;
|
||||
case PunishmentAction.Kick:
|
||||
try
|
||||
{
|
||||
await gu.KickAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); }
|
||||
break;
|
||||
case PunishmentAction.Softban:
|
||||
try
|
||||
{
|
||||
await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false);
|
||||
// try it twice, really don't want to ban user if
|
||||
// only kick has been specified as the punishement
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
|
||||
break;
|
||||
case PunishmentAction.Ban:
|
||||
try
|
||||
{
|
||||
await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
await LogCommands.TriggeredAntiProtection(gus, action, pt).ConfigureAwait(false);
|
||||
_service = service;
|
||||
_mute = mute;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
private string GetAntiSpamString(AntiSpamStats stats)
|
||||
@ -282,9 +63,9 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
|
||||
AntiRaidStats throwaway;
|
||||
if (_antiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway))
|
||||
if (_service.AntiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway))
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiRaidSetting));
|
||||
|
||||
@ -297,7 +78,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
try
|
||||
{
|
||||
await MuteCommands.GetMuteRole(Context.Guild).ConfigureAwait(false);
|
||||
await _mute.GetMuteRole(Context.Guild).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -316,9 +97,9 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
};
|
||||
|
||||
_antiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
|
||||
_service.AntiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiRaidSetting));
|
||||
|
||||
@ -339,10 +120,10 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
|
||||
AntiSpamStats throwaway;
|
||||
if (_antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway))
|
||||
if (_service.AntiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway))
|
||||
{
|
||||
throwaway.UserStats.ForEach(x => x.Value.Dispose());
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiSpamSetting)
|
||||
.ThenInclude(x => x.IgnoredChannels));
|
||||
@ -356,7 +137,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
try
|
||||
{
|
||||
await MuteCommands.GetMuteRole(Context.Guild).ConfigureAwait(false);
|
||||
await _mute.GetMuteRole(Context.Guild).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -374,9 +155,9 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
};
|
||||
|
||||
_antiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
|
||||
_service.AntiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiSpamSetting));
|
||||
|
||||
@ -398,7 +179,7 @@ namespace NadekoBot.Modules.Administration
|
||||
ChannelId = channel.Id
|
||||
};
|
||||
bool added;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiSpamSetting).ThenInclude(x => x.IgnoredChannels));
|
||||
var spam = gc.AntiSpamSetting;
|
||||
@ -410,7 +191,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (spam.IgnoredChannels.Add(obj))
|
||||
{
|
||||
AntiSpamStats temp;
|
||||
if (_antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
|
||||
if (_service.AntiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
|
||||
temp.AntiSpamSettings.IgnoredChannels.Add(obj);
|
||||
added = true;
|
||||
}
|
||||
@ -418,7 +199,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
spam.IgnoredChannels.Remove(obj);
|
||||
AntiSpamStats temp;
|
||||
if (_antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
|
||||
if (_service.AntiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
|
||||
temp.AntiSpamSettings.IgnoredChannels.Remove(obj);
|
||||
added = false;
|
||||
}
|
||||
@ -437,10 +218,10 @@ namespace NadekoBot.Modules.Administration
|
||||
public async Task AntiList()
|
||||
{
|
||||
AntiSpamStats spam;
|
||||
_antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam);
|
||||
_service.AntiSpamGuilds.TryGetValue(Context.Guild.Id, out spam);
|
||||
|
||||
AntiRaidStats raid;
|
||||
_antiRaidGuilds.TryGetValue(Context.Guild.Id, out raid);
|
||||
_service.AntiRaidGuilds.TryGetValue(Context.Guild.Id, out raid);
|
||||
|
||||
if (spam == null && raid == null)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Administration;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
@ -21,85 +22,13 @@ namespace NadekoBot.Modules.Administration
|
||||
[Group]
|
||||
public class RatelimitCommands : NadekoSubmodule
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>();
|
||||
public static ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>();
|
||||
public static ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>();
|
||||
private readonly RatelimitService _service;
|
||||
private readonly DbHandler _db;
|
||||
|
||||
private new static readonly Logger _log;
|
||||
|
||||
public class Ratelimiter
|
||||
public RatelimitCommands(RatelimitService service, DbHandler db)
|
||||
{
|
||||
public class RatelimitedUser
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public int MessageCount { get; set; } = 0;
|
||||
}
|
||||
|
||||
public ulong ChannelId { get; set; }
|
||||
|
||||
public int MaxMessages { get; set; }
|
||||
public int PerSeconds { get; set; }
|
||||
|
||||
public CancellationTokenSource CancelSource { get; set; } = new CancellationTokenSource();
|
||||
|
||||
public ConcurrentDictionary<ulong, RatelimitedUser> Users { get; set; } = new ConcurrentDictionary<ulong, RatelimitedUser>();
|
||||
|
||||
public bool CheckUserRatelimit(ulong id, ulong guildId, SocketGuildUser optUser)
|
||||
{
|
||||
if ((IgnoredUsers.TryGetValue(guildId, out HashSet<ulong> ignoreUsers) && ignoreUsers.Contains(id)) ||
|
||||
(optUser != null && IgnoredRoles.TryGetValue(guildId, out HashSet<ulong> ignoreRoles) && optUser.Roles.Any(x => ignoreRoles.Contains(x.Id))))
|
||||
return false;
|
||||
|
||||
var usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id });
|
||||
if (usr.MessageCount >= MaxMessages)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
usr.MessageCount++;
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(PerSeconds * 1000, CancelSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
usr.MessageCount--;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static RatelimitCommands()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>(
|
||||
NadekoBot.AllGuildConfigs
|
||||
.ToDictionary(x => x.GuildId,
|
||||
x => new HashSet<ulong>(x.SlowmodeIgnoredRoles.Select(y => y.RoleId))));
|
||||
|
||||
IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>(
|
||||
NadekoBot.AllGuildConfigs
|
||||
.ToDictionary(x => x.GuildId,
|
||||
x => new HashSet<ulong>(x.SlowmodeIgnoredUsers.Select(y => y.UserId))));
|
||||
|
||||
NadekoBot.Client.MessageReceived += async (umsg) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var usrMsg = umsg as SocketUserMessage;
|
||||
var channel = usrMsg?.Channel as SocketTextChannel;
|
||||
|
||||
if (channel == null || usrMsg.IsAuthor())
|
||||
return;
|
||||
if (!RatelimitingChannels.TryGetValue(channel.Id, out Ratelimiter limiter))
|
||||
return;
|
||||
|
||||
if (limiter.CheckUserRatelimit(usrMsg.Author.Id, channel.Guild.Id, usrMsg.Author as SocketGuildUser))
|
||||
await usrMsg.DeleteAsync();
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
};
|
||||
_service = service;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -107,7 +36,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[RequireUserPermission(GuildPermission.ManageMessages)]
|
||||
public async Task Slowmode()
|
||||
{
|
||||
if (RatelimitingChannels.TryRemove(Context.Channel.Id, out Ratelimiter throwaway))
|
||||
if (_service.RatelimitingChannels.TryRemove(Context.Channel.Id, out Ratelimiter throwaway))
|
||||
{
|
||||
throwaway.CancelSource.Cancel();
|
||||
await ReplyConfirmLocalized("slowmode_disabled").ConfigureAwait(false);
|
||||
@ -126,13 +55,13 @@ namespace NadekoBot.Modules.Administration
|
||||
await ReplyErrorLocalized("invalid_params").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var toAdd = new Ratelimiter()
|
||||
var toAdd = new Ratelimiter(_service)
|
||||
{
|
||||
ChannelId = Context.Channel.Id,
|
||||
MaxMessages = msg,
|
||||
PerSeconds = perSec,
|
||||
};
|
||||
if(RatelimitingChannels.TryAdd(Context.Channel.Id, toAdd))
|
||||
if(_service.RatelimitingChannels.TryAdd(Context.Channel.Id, toAdd))
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync(GetText("slowmode_init"),
|
||||
GetText("slowmode_desc", Format.Bold(toAdd.MaxMessages.ToString()), Format.Bold(toAdd.PerSeconds.ToString())))
|
||||
@ -153,7 +82,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
HashSet<SlowmodeIgnoredUser> usrs;
|
||||
bool removed;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
usrs = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.SlowmodeIgnoredUsers))
|
||||
.SlowmodeIgnoredUsers;
|
||||
@ -164,7 +93,7 @@ namespace NadekoBot.Modules.Administration
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
IgnoredUsers.AddOrUpdate(Context.Guild.Id, new HashSet<ulong>(usrs.Select(x => x.UserId)), (key, old) => new HashSet<ulong>(usrs.Select(x => x.UserId)));
|
||||
_service.IgnoredUsers.AddOrUpdate(Context.Guild.Id, new HashSet<ulong>(usrs.Select(x => x.UserId)), (key, old) => new HashSet<ulong>(usrs.Select(x => x.UserId)));
|
||||
|
||||
if(removed)
|
||||
await ReplyConfirmLocalized("slowmodewl_user_stop", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
@ -185,7 +114,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
HashSet<SlowmodeIgnoredRole> roles;
|
||||
bool removed;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
roles = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.SlowmodeIgnoredRoles))
|
||||
.SlowmodeIgnoredRoles;
|
||||
@ -196,7 +125,7 @@ namespace NadekoBot.Modules.Administration
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
IgnoredRoles.AddOrUpdate(Context.Guild.Id, new HashSet<ulong>(roles.Select(x => x.RoleId)), (key, old) => new HashSet<ulong>(roles.Select(x => x.RoleId)));
|
||||
_service.IgnoredRoles.AddOrUpdate(Context.Guild.Id, new HashSet<ulong>(roles.Select(x => x.RoleId)), (key, old) => new HashSet<ulong>(roles.Select(x => x.RoleId)));
|
||||
|
||||
if (removed)
|
||||
await ReplyConfirmLocalized("slowmodewl_role_stop", Format.Bold(role.ToString())).ConfigureAwait(false);
|
||||
|
@ -18,6 +18,12 @@ namespace NadekoBot.Modules.Administration
|
||||
[Group]
|
||||
public class SelfAssignedRolesCommands : NadekoSubmodule
|
||||
{
|
||||
private readonly DbHandler _db;
|
||||
|
||||
public SelfAssignedRolesCommands(DbHandler db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@ -25,7 +31,7 @@ namespace NadekoBot.Modules.Administration
|
||||
public async Task AdSarm()
|
||||
{
|
||||
bool newval;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||
newval = config.AutoDeleteSelfAssignedRoleMessages = !config.AutoDeleteSelfAssignedRoleMessages;
|
||||
@ -49,7 +55,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
string msg;
|
||||
var error = false;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
|
||||
if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id))
|
||||
@ -84,7 +90,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
|
||||
bool success;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
success = uow.SelfAssignedRoles.DeleteByGuildAndRoleId(role.Guild.Id, role.Id);
|
||||
await uow.CompleteAsync();
|
||||
@ -105,7 +111,7 @@ namespace NadekoBot.Modules.Administration
|
||||
var removeMsg = new StringBuilder();
|
||||
var msg = new StringBuilder();
|
||||
var roleCnt = 0;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var roleModels = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id).ToList();
|
||||
msg.AppendLine();
|
||||
@ -139,7 +145,7 @@ namespace NadekoBot.Modules.Administration
|
||||
public async Task Tesar()
|
||||
{
|
||||
bool areExclusive;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||
|
||||
@ -160,7 +166,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
GuildConfig conf;
|
||||
SelfAssignedRole[] roles;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||
roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id).ToArray();
|
||||
@ -220,7 +226,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
bool autoDeleteSelfAssignedRoleMessages;
|
||||
IEnumerable<SelfAssignedRole> roles;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
autoDeleteSelfAssignedRoleMessages = uow.GuildConfigs.For(Context.Guild.Id, set => set).AutoDeleteSelfAssignedRoleMessages;
|
||||
roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
|
||||
|
@ -25,7 +25,6 @@ namespace NadekoBot.Modules.Administration
|
||||
public UserPunishCommands(DbHandler db, MuteService muteService)
|
||||
{
|
||||
_db = db;
|
||||
|
||||
_muteService = muteService;
|
||||
}
|
||||
|
||||
|
@ -7,37 +7,25 @@ using NadekoBot.Services.Database.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using NLog;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
using System.Collections.Concurrent;
|
||||
using NadekoBot.Services.Pokemon;
|
||||
|
||||
namespace NadekoBot.Modules.Pokemon
|
||||
{
|
||||
[NadekoModule("Pokemon", ">")]
|
||||
public class Pokemon : NadekoTopLevelModule
|
||||
{
|
||||
private static readonly List<PokemonType> _pokemonTypes = new List<PokemonType>();
|
||||
private static readonly ConcurrentDictionary<ulong, PokeStats> _stats = new ConcurrentDictionary<ulong, PokeStats>();
|
||||
private readonly PokemonService _service;
|
||||
private readonly DbHandler _db;
|
||||
private readonly BotConfig _bc;
|
||||
private readonly CurrencyHandler _ch;
|
||||
|
||||
public const string PokemonTypesFile = "data/pokemon_types.json";
|
||||
|
||||
private new static Logger _log { get; }
|
||||
|
||||
static Pokemon()
|
||||
public Pokemon(PokemonService pokemonService, DbHandler db, BotConfig bc, CurrencyHandler ch)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
if (File.Exists(PokemonTypesFile))
|
||||
{
|
||||
_pokemonTypes = JsonConvert.DeserializeObject<List<PokemonType>>(File.ReadAllText(PokemonTypesFile));
|
||||
_service = pokemonService;
|
||||
_db = db;
|
||||
_bc = bc;
|
||||
_ch = ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.Warn(PokemonTypesFile + " is missing. Pokemon types not loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int GetDamage(PokemonType usertype, PokemonType targetType)
|
||||
{
|
||||
@ -52,12 +40,11 @@ namespace NadekoBot.Modules.Pokemon
|
||||
return damage;
|
||||
}
|
||||
|
||||
|
||||
private static PokemonType GetPokeType(ulong id)
|
||||
private PokemonType GetPokeType(ulong id)
|
||||
{
|
||||
|
||||
Dictionary<ulong, string> setTypes;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
setTypes = uow.PokeGame.GetAll().ToDictionary(x => x.UserId, y => y.type);
|
||||
}
|
||||
@ -66,17 +53,17 @@ namespace NadekoBot.Modules.Pokemon
|
||||
{
|
||||
return StringToPokemonType(setTypes[id]);
|
||||
}
|
||||
var count = _pokemonTypes.Count;
|
||||
var count = _service.PokemonTypes.Count;
|
||||
|
||||
var remainder = Math.Abs((int)(id % (ulong)count));
|
||||
|
||||
return _pokemonTypes[remainder];
|
||||
return _service.PokemonTypes[remainder];
|
||||
}
|
||||
|
||||
private static PokemonType StringToPokemonType(string v)
|
||||
private PokemonType StringToPokemonType(string v)
|
||||
{
|
||||
var str = v?.ToUpperInvariant();
|
||||
var list = _pokemonTypes;
|
||||
var list = _service.PokemonTypes;
|
||||
foreach (var p in list)
|
||||
{
|
||||
if (str == p.Name)
|
||||
@ -111,7 +98,7 @@ namespace NadekoBot.Modules.Pokemon
|
||||
|
||||
// Checking stats first, then move
|
||||
//Set up the userstats
|
||||
var userStats = _stats.GetOrAdd(user.Id, new PokeStats());
|
||||
var userStats = _service.Stats.GetOrAdd(user.Id, new PokeStats());
|
||||
|
||||
//Check if able to move
|
||||
//User not able if HP < 0, has made more than 4 attacks
|
||||
@ -131,7 +118,7 @@ namespace NadekoBot.Modules.Pokemon
|
||||
return;
|
||||
}
|
||||
//get target stats
|
||||
var targetStats = _stats.GetOrAdd(targetUser.Id, new PokeStats());
|
||||
var targetStats = _service.Stats.GetOrAdd(targetUser.Id, new PokeStats());
|
||||
|
||||
//If target's HP is below 0, no use attacking
|
||||
if (targetStats.Hp <= 0)
|
||||
@ -195,8 +182,8 @@ namespace NadekoBot.Modules.Pokemon
|
||||
|
||||
//update dictionary
|
||||
//This can stay the same right?
|
||||
_stats[user.Id] = userStats;
|
||||
_stats[targetUser.Id] = targetStats;
|
||||
_service.Stats[user.Id] = userStats;
|
||||
_service.Stats[targetUser.Id] = targetStats;
|
||||
|
||||
await Context.Channel.SendConfirmAsync(Context.User.Mention + " " + response).ConfigureAwait(false);
|
||||
}
|
||||
@ -228,9 +215,9 @@ namespace NadekoBot.Modules.Pokemon
|
||||
return;
|
||||
}
|
||||
|
||||
if (_stats.ContainsKey(targetUser.Id))
|
||||
if (_service.Stats.ContainsKey(targetUser.Id))
|
||||
{
|
||||
var targetStats = _stats[targetUser.Id];
|
||||
var targetStats = _service.Stats[targetUser.Id];
|
||||
if (targetStats.Hp == targetStats.MaxHp)
|
||||
{
|
||||
await ReplyErrorLocalized("already_full", Format.Bold(targetUser.ToString())).ConfigureAwait(false);
|
||||
@ -242,9 +229,9 @@ namespace NadekoBot.Modules.Pokemon
|
||||
var target = (targetUser.Id == user.Id) ? "yourself" : targetUser.Mention;
|
||||
if (amount > 0)
|
||||
{
|
||||
if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false))
|
||||
if (!await _ch.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false))
|
||||
{
|
||||
await ReplyErrorLocalized("no_currency", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_currency", _bc.CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -254,16 +241,16 @@ namespace NadekoBot.Modules.Pokemon
|
||||
if (targetStats.Hp < 0)
|
||||
{
|
||||
//Could heal only for half HP?
|
||||
_stats[targetUser.Id].Hp = (targetStats.MaxHp / 2);
|
||||
_service.Stats[targetUser.Id].Hp = (targetStats.MaxHp / 2);
|
||||
if (target == "yourself")
|
||||
{
|
||||
await ReplyConfirmLocalized("revive_yourself", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("revive_yourself", _bc.CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalized("revive_other", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("revive_other", Format.Bold(targetUser.ToString()), _bc.CurrencySign).ConfigureAwait(false);
|
||||
}
|
||||
await ReplyConfirmLocalized("healed", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("healed", Format.Bold(targetUser.ToString()), _bc.CurrencySign).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -291,7 +278,7 @@ namespace NadekoBot.Modules.Pokemon
|
||||
var targetType = StringToPokemonType(typeTargeted);
|
||||
if (targetType == null)
|
||||
{
|
||||
await Context.Channel.EmbedAsync(_pokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"),
|
||||
await Context.Channel.EmbedAsync(_service.PokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"),
|
||||
(eb, pt) => eb.AddField(efb => efb.WithName(pt.Name)
|
||||
.WithValue(pt.Icon)
|
||||
.WithIsInline(true)))
|
||||
@ -308,16 +295,16 @@ namespace NadekoBot.Modules.Pokemon
|
||||
var amount = 1;
|
||||
if (amount > 0)
|
||||
{
|
||||
if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"{user} change type to {typeTargeted}", amount, true).ConfigureAwait(false))
|
||||
if (!await _ch.RemoveCurrencyAsync(user, $"{user} change type to {typeTargeted}", amount, true).ConfigureAwait(false))
|
||||
{
|
||||
await ReplyErrorLocalized("no_currency", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_currency", _bc.CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Actually changing the type here
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var pokeUsers = uow.PokeGame.GetAll().ToArray();
|
||||
var setTypes = pokeUsers.ToDictionary(x => x.UserId, y => y.type);
|
||||
@ -344,7 +331,7 @@ namespace NadekoBot.Modules.Pokemon
|
||||
//Now for the response
|
||||
await ReplyConfirmLocalized("settype_success",
|
||||
targetType,
|
||||
NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
_bc.CurrencySign).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
@ -97,8 +97,12 @@ namespace NadekoBot
|
||||
var soundcloud = new SoundCloudApiService(credentials);
|
||||
|
||||
//module services
|
||||
//todo 90 - Make this automatic
|
||||
var utilityService = new UtilityService(AllGuildConfigs, Client, BotConfig, db);
|
||||
#region Searches
|
||||
var searchesService = new SearchesService(Client, google, db);
|
||||
var streamNotificationService = new StreamNotificationService(db, Client, strings);
|
||||
#endregion
|
||||
var clashService = new ClashOfClansService(Client, db, localization, strings);
|
||||
var musicService = new MusicService(google, strings, localization, db, soundcloud, credentials);
|
||||
var crService = new CustomReactionsService(db, Client);
|
||||
@ -108,11 +112,16 @@ namespace NadekoBot
|
||||
var selfService = new SelfService(this, commandHandler, db, BotConfig);
|
||||
var vcRoleService = new VcRoleService(Client, AllGuildConfigs);
|
||||
var vPlusTService = new VplusTService(Client, AllGuildConfigs, strings, db);
|
||||
var muteService = new MuteService(Client, AllGuildConfigs, db);
|
||||
var ratelimitService = new RatelimitService(Client, AllGuildConfigs);
|
||||
var protectionService = new ProtectionService(Client, AllGuildConfigs, muteService);
|
||||
var playingRotateService = new PlayingRotateService(Client, BotConfig, musicService);
|
||||
var gameVcService = new GameVoiceChannelService(Client, db, AllGuildConfigs);
|
||||
var autoAssignRoleService = new AutoAssignRoleService(Client, AllGuildConfigs);
|
||||
#endregion
|
||||
|
||||
|
||||
//initialize Services
|
||||
Services = new NServiceProvider.ServiceProviderBuilder() //todo all Adds should be interfaces
|
||||
Services = new NServiceProvider.ServiceProviderBuilder()
|
||||
.Add<ILocalization>(localization)
|
||||
.Add<IStatsService>(stats)
|
||||
.Add<IImagesService>(images)
|
||||
@ -129,14 +138,22 @@ namespace NadekoBot
|
||||
//modules
|
||||
.Add<UtilityService>(utilityService)
|
||||
.Add<SearchesService>(searchesService)
|
||||
.Add(streamNotificationService)
|
||||
.Add<ClashOfClansService>(clashService)
|
||||
.Add<MusicService>(musicService)
|
||||
.Add<GreetSettingsService>(greetSettingsService)
|
||||
.Add<CustomReactionsService>(crService)
|
||||
.Add<GamesService>(gamesService)
|
||||
.Add<AdministrationService>(administrationService)
|
||||
.Add(selfService)
|
||||
.Add(vcRoleService)
|
||||
.Add(vPlusTService)
|
||||
.Add(muteService)
|
||||
.Add(ratelimitService)
|
||||
.Add(playingRotateService)
|
||||
.Add(gameVcService)
|
||||
.Add(autoAssignRoleService)
|
||||
.Add(protectionService)
|
||||
.Build();
|
||||
|
||||
commandHandler.AddServices(Services);
|
||||
|
@ -39,7 +39,17 @@
|
||||
<None Remove="Modules\Permissions\**" />
|
||||
<Compile Remove="Modules\Gambling\Commands\Lucky7Commands.cs" />
|
||||
<Compile Include="Modules\Administration\Administration.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\AutoAssignRoleCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\GameChannelCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\LocalizationCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\Migration.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\Migration\0_9..cs" />
|
||||
<Compile Include="Modules\Administration\Commands\Migration\MigrationException.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\MuteCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\PlayingRotateCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\ProtectionCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\RatelimitCommand.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\SelfAssignedRolesCommand.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\SelfCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\ServerGreetCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\UserPunishCommands.cs" />
|
||||
@ -61,7 +71,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="0.9.9" />
|
||||
<PackageReference Include="Discord.Net" Version="1.0.0-rc3-00743" />
|
||||
<PackageReference Include="Discord.Net" Version="1.0.0-rc3-00746" />
|
||||
<PackageReference Include="libvideo" Version="1.0.1" />
|
||||
<PackageReference Include="CoreCLR-NCalc" Version="2.1.2" />
|
||||
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.19.0.138" />
|
||||
|
@ -0,0 +1,46 @@
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
public class AutoAssignRoleService
|
||||
{
|
||||
private readonly Logger _log;
|
||||
private readonly DiscordShardedClient _client;
|
||||
|
||||
//guildid/roleid
|
||||
public ConcurrentDictionary<ulong, ulong> AutoAssignedRoles { get; }
|
||||
|
||||
public AutoAssignRoleService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_client = client;
|
||||
|
||||
AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(
|
||||
gcs.Where(x => x.AutoAssignRoleId != 0)
|
||||
.ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
|
||||
|
||||
_client.UserJoined += async (user) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
AutoAssignedRoles.TryGetValue(user.Guild.Id, out ulong roleId);
|
||||
|
||||
if (roleId == 0)
|
||||
return;
|
||||
|
||||
var role = user.Guild.Roles.FirstOrDefault(r => r.Id == roleId);
|
||||
|
||||
if (role != null)
|
||||
await user.AddRoleAsync(role).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
public class GameVoiceChannelService
|
||||
{
|
||||
public readonly ConcurrentHashSet<ulong> GameVoiceChannels = new ConcurrentHashSet<ulong>();
|
||||
|
||||
private readonly Logger _log;
|
||||
private readonly DbHandler _db;
|
||||
private readonly DiscordShardedClient _client;
|
||||
|
||||
public GameVoiceChannelService(DiscordShardedClient client, DbHandler db, IEnumerable<GuildConfig> gcs)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_db = db;
|
||||
_client = client;
|
||||
|
||||
GameVoiceChannels = new ConcurrentHashSet<ulong>(
|
||||
gcs.Where(gc => gc.GameVoiceChannel != null)
|
||||
.Select(gc => gc.GameVoiceChannel.Value));
|
||||
|
||||
_client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
|
||||
|
||||
}
|
||||
|
||||
private Task Client_UserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState, SocketVoiceState newState)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var gUser = usr as SocketGuildUser;
|
||||
if (gUser == null)
|
||||
return;
|
||||
|
||||
var game = gUser.Game?.Name.TrimTo(50).ToLowerInvariant();
|
||||
|
||||
if (oldState.VoiceChannel == newState.VoiceChannel ||
|
||||
newState.VoiceChannel == null)
|
||||
return;
|
||||
|
||||
if (!GameVoiceChannels.Contains(newState.VoiceChannel.Id) ||
|
||||
string.IsNullOrWhiteSpace(game))
|
||||
return;
|
||||
|
||||
var vch = gUser.Guild.VoiceChannels
|
||||
.FirstOrDefault(x => x.Name.ToLowerInvariant() == game);
|
||||
|
||||
if (vch == null)
|
||||
return;
|
||||
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
await gUser.ModifyAsync(gu => gu.Channel = vch).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
104
src/NadekoBot/Services/Administration/PlayingRotateService.cs
Normal file
104
src/NadekoBot/Services/Administration/PlayingRotateService.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Services.Music;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
//todo 99 - Could make a placeholder service, which can work for any module
|
||||
//and have replacements which are dependent on the types provided in the constructor
|
||||
public class PlayingRotateService
|
||||
{
|
||||
public List<PlayingStatus> RotatingStatusMessages { get; }
|
||||
public volatile bool RotatingStatuses;
|
||||
private readonly Timer _t;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly BotConfig _bc;
|
||||
private readonly MusicService _music;
|
||||
private readonly Logger _log;
|
||||
|
||||
private class TimerState
|
||||
{
|
||||
public int Index { get; set; }
|
||||
}
|
||||
|
||||
public PlayingRotateService(DiscordShardedClient client, BotConfig bc, MusicService music)
|
||||
{
|
||||
_client = client;
|
||||
_bc = bc;
|
||||
_music = music;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
RotatingStatusMessages = _bc.RotatingStatusMessages;
|
||||
RotatingStatuses = _bc.RotatingStatuses;
|
||||
|
||||
_t = new Timer(async (objState) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var state = (TimerState)objState;
|
||||
if (!RotatingStatuses)
|
||||
return;
|
||||
if (state.Index >= RotatingStatusMessages.Count)
|
||||
state.Index = 0;
|
||||
|
||||
if (!RotatingStatusMessages.Any())
|
||||
return;
|
||||
var status = RotatingStatusMessages[state.Index++].Status;
|
||||
if (string.IsNullOrWhiteSpace(status))
|
||||
return;
|
||||
PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(_client,_music)));
|
||||
var shards = _client.Shards;
|
||||
for (int i = 0; i < shards.Count; i++)
|
||||
{
|
||||
var curShard = shards.ElementAt(i);
|
||||
ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(curShard)));
|
||||
try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn("Rotating playing status errored.\n" + ex);
|
||||
}
|
||||
}, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
||||
}
|
||||
|
||||
public Dictionary<string, Func<DiscordShardedClient, MusicService, string>> PlayingPlaceholders { get; } =
|
||||
new Dictionary<string, Func<DiscordShardedClient, MusicService, string>> {
|
||||
{ "%servers%", (c, ms) => c.Guilds.Count.ToString()},
|
||||
{ "%users%", (c, ms) => c.Guilds.Sum(s => s.Users.Count).ToString()},
|
||||
{ "%playing%", (c, ms) => {
|
||||
var cnt = ms.MusicPlayers.Count(kvp => kvp.Value.CurrentSong != null);
|
||||
if (cnt != 1) return cnt.ToString();
|
||||
try {
|
||||
var mp = ms.MusicPlayers.FirstOrDefault();
|
||||
return mp.Value.CurrentSong.SongInfo.Title;
|
||||
}
|
||||
catch {
|
||||
return "No songs";
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "%queued%", (c, ms) => ms.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString()},
|
||||
{ "%time%", (c, ms) => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()) },
|
||||
{ "%shardcount%", (c, ms) => c.Shards.Count.ToString() },
|
||||
};
|
||||
|
||||
public Dictionary<string, Func<DiscordSocketClient, string>> ShardSpecificPlaceholders { get; } =
|
||||
new Dictionary<string, Func<DiscordSocketClient, string>> {
|
||||
{ "%shardid%", (client) => client.ShardId.ToString()},
|
||||
{ "%shardguilds%", (client) => client.Guilds.Count.ToString()},
|
||||
};
|
||||
}
|
||||
}
|
182
src/NadekoBot/Services/Administration/ProtectionService.cs
Normal file
182
src/NadekoBot/Services/Administration/ProtectionService.cs
Normal file
@ -0,0 +1,182 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
public class ProtectionService
|
||||
{
|
||||
public readonly ConcurrentDictionary<ulong, AntiRaidStats> AntiRaidGuilds =
|
||||
new ConcurrentDictionary<ulong, AntiRaidStats>();
|
||||
// guildId | (userId|messages)
|
||||
public readonly ConcurrentDictionary<ulong, AntiSpamStats> AntiSpamGuilds =
|
||||
new ConcurrentDictionary<ulong, AntiSpamStats>();
|
||||
|
||||
//todo sub LogCommands to this
|
||||
public event Func<IEnumerable<IGuildUser>, PunishmentAction, ProtectionType, Task> OnAntiProtectionTriggered = delegate { return Task.CompletedTask; };
|
||||
|
||||
private readonly Logger _log;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly MuteService _mute;
|
||||
|
||||
public ProtectionService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, MuteService mute)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_client = client;
|
||||
_mute = mute;
|
||||
|
||||
foreach (var gc in gcs)
|
||||
{
|
||||
var raid = gc.AntiRaidSetting;
|
||||
var spam = gc.AntiSpamSetting;
|
||||
|
||||
if (raid != null)
|
||||
{
|
||||
var raidStats = new AntiRaidStats() { AntiRaidSettings = raid };
|
||||
AntiRaidGuilds.TryAdd(gc.GuildId, raidStats);
|
||||
}
|
||||
|
||||
if (spam != null)
|
||||
AntiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam });
|
||||
}
|
||||
|
||||
_client.MessageReceived += (imsg) =>
|
||||
{
|
||||
var msg = imsg as IUserMessage;
|
||||
if (msg == null || msg.Author.IsBot)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var channel = msg.Channel as ITextChannel;
|
||||
if (channel == null)
|
||||
return Task.CompletedTask;
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!AntiSpamGuilds.TryGetValue(channel.Guild.Id, out var spamSettings) ||
|
||||
spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore()
|
||||
{
|
||||
ChannelId = channel.Id
|
||||
}))
|
||||
return;
|
||||
|
||||
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, (id) => new UserSpamStats(msg),
|
||||
(id, old) =>
|
||||
{
|
||||
old.ApplyNextMessage(msg); return old;
|
||||
});
|
||||
|
||||
if (stats.Count >= spamSettings.AntiSpamSettings.MessageThreshold)
|
||||
{
|
||||
if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
|
||||
{
|
||||
stats.Dispose();
|
||||
await PunishUsers(spamSettings.AntiSpamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_client.UserJoined += (usr) =>
|
||||
{
|
||||
if (usr.IsBot)
|
||||
return Task.CompletedTask;
|
||||
if (!AntiRaidGuilds.TryGetValue(usr.Guild.Id, out var settings))
|
||||
return Task.CompletedTask;
|
||||
if (!settings.RaidUsers.Add(usr))
|
||||
return Task.CompletedTask;
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
++settings.UsersCount;
|
||||
|
||||
if (settings.UsersCount >= settings.AntiRaidSettings.UserThreshold)
|
||||
{
|
||||
var users = settings.RaidUsers.ToArray();
|
||||
settings.RaidUsers.Clear();
|
||||
|
||||
await PunishUsers(settings.AntiRaidSettings.Action, ProtectionType.Raiding, users).ConfigureAwait(false);
|
||||
}
|
||||
await Task.Delay(1000 * settings.AntiRaidSettings.Seconds).ConfigureAwait(false);
|
||||
|
||||
settings.RaidUsers.TryRemove(usr);
|
||||
--settings.UsersCount;
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private async Task PunishUsers(PunishmentAction action, ProtectionType pt, params IGuildUser[] gus)
|
||||
{
|
||||
_log.Info($"[{pt}] - Punishing [{gus.Length}] users with [{action}] in {gus[0].Guild.Name} guild");
|
||||
foreach (var gu in gus)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case PunishmentAction.Mute:
|
||||
try
|
||||
{
|
||||
await _mute.MuteUser(gu).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); }
|
||||
break;
|
||||
case PunishmentAction.Kick:
|
||||
try
|
||||
{
|
||||
await gu.KickAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); }
|
||||
break;
|
||||
case PunishmentAction.Softban:
|
||||
try
|
||||
{
|
||||
await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false);
|
||||
// try it twice, really don't want to ban user if
|
||||
// only kick has been specified as the punishement
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
|
||||
break;
|
||||
case PunishmentAction.Ban:
|
||||
try
|
||||
{
|
||||
await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
await OnAntiProtectionTriggered(gus, action, pt).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
26
src/NadekoBot/Services/Administration/ProtectionStats.cs
Normal file
26
src/NadekoBot/Services/Administration/ProtectionStats.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using Discord;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
public enum ProtectionType
|
||||
{
|
||||
Raiding,
|
||||
Spamming,
|
||||
}
|
||||
|
||||
public class AntiRaidStats
|
||||
{
|
||||
public AntiRaidSetting AntiRaidSettings { get; set; }
|
||||
public int UsersCount { get; set; }
|
||||
public ConcurrentHashSet<IGuildUser> RaidUsers { get; set; } = new ConcurrentHashSet<IGuildUser>();
|
||||
}
|
||||
|
||||
public class AntiSpamStats
|
||||
{
|
||||
public AntiSpamSetting AntiSpamSettings { get; set; }
|
||||
public ConcurrentDictionary<ulong, UserSpamStats> UserStats { get; set; }
|
||||
= new ConcurrentDictionary<ulong, UserSpamStats>();
|
||||
}
|
||||
}
|
53
src/NadekoBot/Services/Administration/RatelimitService.cs
Normal file
53
src/NadekoBot/Services/Administration/RatelimitService.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
public class RatelimitService
|
||||
{
|
||||
public ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>();
|
||||
public ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>();
|
||||
public ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>();
|
||||
|
||||
private readonly Logger _log;
|
||||
private readonly DiscordShardedClient _client;
|
||||
|
||||
public RatelimitService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_client = client;
|
||||
|
||||
IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>(
|
||||
gcs.ToDictionary(x => x.GuildId,
|
||||
x => new HashSet<ulong>(x.SlowmodeIgnoredRoles.Select(y => y.RoleId))));
|
||||
|
||||
IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>(
|
||||
gcs.ToDictionary(x => x.GuildId,
|
||||
x => new HashSet<ulong>(x.SlowmodeIgnoredUsers.Select(y => y.UserId))));
|
||||
|
||||
_client.MessageReceived += async (umsg) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var usrMsg = umsg as SocketUserMessage;
|
||||
var channel = usrMsg?.Channel as SocketTextChannel;
|
||||
|
||||
if (channel == null || usrMsg == null || usrMsg.IsAuthor(client))
|
||||
return;
|
||||
if (!RatelimitingChannels.TryGetValue(channel.Id, out Ratelimiter limiter))
|
||||
return;
|
||||
|
||||
if (limiter.CheckUserRatelimit(usrMsg.Author.Id, channel.Guild.Id, usrMsg.Author as SocketGuildUser))
|
||||
await usrMsg.DeleteAsync();
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
59
src/NadekoBot/Services/Administration/Ratelimiter.cs
Normal file
59
src/NadekoBot/Services/Administration/Ratelimiter.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using Discord.WebSocket;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
public class Ratelimiter
|
||||
{
|
||||
private readonly RatelimitService _svc;
|
||||
|
||||
public class RatelimitedUser
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public int MessageCount { get; set; } = 0;
|
||||
}
|
||||
|
||||
public ulong ChannelId { get; set; }
|
||||
public int MaxMessages { get; set; }
|
||||
public int PerSeconds { get; set; }
|
||||
|
||||
public Ratelimiter(RatelimitService svc)
|
||||
{
|
||||
_svc = svc;
|
||||
}
|
||||
|
||||
public CancellationTokenSource CancelSource { get; set; } = new CancellationTokenSource();
|
||||
|
||||
public ConcurrentDictionary<ulong, RatelimitedUser> Users { get; set; } = new ConcurrentDictionary<ulong, RatelimitedUser>();
|
||||
|
||||
public bool CheckUserRatelimit(ulong id, ulong guildId, SocketGuildUser optUser)
|
||||
{
|
||||
if ((_svc.IgnoredUsers.TryGetValue(guildId, out HashSet<ulong> ignoreUsers) && ignoreUsers.Contains(id)) ||
|
||||
(optUser != null && _svc.IgnoredRoles.TryGetValue(guildId, out HashSet<ulong> ignoreRoles) && optUser.Roles.Any(x => ignoreRoles.Contains(x.Id))))
|
||||
return false;
|
||||
|
||||
var usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id });
|
||||
if (usr.MessageCount >= MaxMessages)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
usr.MessageCount++;
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(PerSeconds * 1000, CancelSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
usr.MessageCount--;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,6 @@ namespace NadekoBot.Services.Administration
|
||||
public volatile bool ForwardDMs;
|
||||
public volatile bool ForwardDMsToAllOwners;
|
||||
|
||||
private readonly Logger _log;
|
||||
private readonly NadekoBot _bot;
|
||||
private readonly CommandHandler _cmdHandler;
|
||||
private readonly DbHandler _db;
|
||||
|
53
src/NadekoBot/Services/Administration/UserSpamStats.cs
Normal file
53
src/NadekoBot/Services/Administration/UserSpamStats.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using Discord;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
public class UserSpamStats : IDisposable
|
||||
{
|
||||
public int Count => timers.Count;
|
||||
public string LastMessage { get; set; }
|
||||
|
||||
private ConcurrentQueue<Timer> timers { get; }
|
||||
|
||||
public UserSpamStats(IUserMessage msg)
|
||||
{
|
||||
LastMessage = msg.Content.ToUpperInvariant();
|
||||
timers = new ConcurrentQueue<Timer>();
|
||||
|
||||
ApplyNextMessage(msg);
|
||||
}
|
||||
|
||||
private readonly object applyLock = new object();
|
||||
public void ApplyNextMessage(IUserMessage message)
|
||||
{
|
||||
lock (applyLock)
|
||||
{
|
||||
var upperMsg = message.Content.ToUpperInvariant();
|
||||
if (upperMsg != LastMessage || (string.IsNullOrWhiteSpace(upperMsg) && message.Attachments.Any()))
|
||||
{
|
||||
LastMessage = upperMsg;
|
||||
while (timers.TryDequeue(out var old))
|
||||
old.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
var t = new Timer((_) => {
|
||||
if (timers.TryDequeue(out var old))
|
||||
old.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}, null, TimeSpan.FromMinutes(30), TimeSpan.FromMinutes(30));
|
||||
timers.Enqueue(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
while (timers.TryDequeue(out var old))
|
||||
old.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NadekoBot.Modules.Pokemon
|
||||
namespace NadekoBot.Services.Pokemon
|
||||
{
|
||||
class PokeStats
|
||||
public class PokeStats
|
||||
{
|
||||
//Health left
|
||||
public int Hp { get; set; } = 500;
|
32
src/NadekoBot/Services/Pokemon/PokemonService.cs
Normal file
32
src/NadekoBot/Services/Pokemon/PokemonService.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace NadekoBot.Services.Pokemon
|
||||
{
|
||||
public class PokemonService
|
||||
{
|
||||
public readonly List<PokemonType> PokemonTypes = new List<PokemonType>();
|
||||
public readonly ConcurrentDictionary<ulong, PokeStats> Stats = new ConcurrentDictionary<ulong, PokeStats>();
|
||||
|
||||
public const string PokemonTypesFile = "data/pokemon_types.json";
|
||||
|
||||
private Logger _log { get; }
|
||||
|
||||
public PokemonService()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
if (File.Exists(PokemonTypesFile))
|
||||
{
|
||||
PokemonTypes = JsonConvert.DeserializeObject<List<PokemonType>>(File.ReadAllText(PokemonTypesFile));
|
||||
}
|
||||
else
|
||||
{
|
||||
PokemonTypes = new List<PokemonType>();
|
||||
_log.Warn(PokemonTypesFile + " is missing. Pokemon types not loaded.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NadekoBot.Modules.Pokemon
|
||||
namespace NadekoBot.Services.Pokemon
|
||||
{
|
||||
public class PokemonType
|
||||
{
|
@ -18,6 +18,9 @@ namespace NadekoBot.Extensions
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static bool IsAuthor(this IMessage msg, IDiscordClient client) =>
|
||||
msg.Author?.Id == client.CurrentUser.Id;
|
||||
|
||||
private static readonly IEmote arrow_left = Emote.Parse("⬅");
|
||||
private static readonly IEmote arrow_right = Emote.Parse("➡");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user