Cleanup, .clparew can now be ran everyone, modules load appropriate guild configs, IEnumerable<GuildConfig> replaces with NadekoBot.AllGuildConfigs

This commit is contained in:
Master Kwoth 2017-10-13 02:21:39 +02:00
parent db6fa9af1a
commit e32446335e
40 changed files with 336 additions and 180 deletions

View File

@ -0,0 +1,49 @@
using System;
namespace NadekoBot.Core.Common.Caching
{
/// <summary>
/// A caching object which loads its value with a factory method when it expires.
/// </summary>
/// <typeparam name="T">Type of the value which is cached.</typeparam>
public class FactoryCache<T> : IFactoryCache
{
public DateTime LastUpdate { get; set; } = DateTime.MinValue;
private readonly object _locker = new object();
private TimeSpan _expireAfter;
private readonly Func<T> _factory;
private T Value;
/// <summary>
/// Creates a new factory cache object.
/// </summary>
/// <param name="factory">Method which loads the value when it expires or if it's not loaded the first time.</param>
/// <param name="expireAfter">Time after which the value will be reloaded.</param>
/// <param name="loadImmediately">Should the value be loaded right away. If set to false, value will load when it's first retrieved.</param>
public FactoryCache(Func<T> factory, TimeSpan expireAfter,
bool loadImmediately = false)
{
_expireAfter = expireAfter;
_factory = factory;
if (loadImmediately)
{
Value = _factory();
LastUpdate = DateTime.UtcNow;
}
}
public T GetValue()
{
lock (_locker)
{
if (DateTime.UtcNow - LastUpdate > _expireAfter)
{
LastUpdate = DateTime.UtcNow;
return Value = _factory();
}
return Value;
}
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Core.Common.Caching
{
public interface IFactoryCache
{
}
}

View File

@ -0,0 +1,48 @@
using Discord.Commands;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules;
using NadekoBot.Services;
using System;
using System.Threading.Tasks;
#if DEBUG
namespace NadekoBot.Modules.Administration
{
public partial class Administration
{
[Group]
[OwnerOnly]
public class DangerousCommands : NadekoSubmodule
{
private readonly DbService _db;
public DangerousCommands(DbService db)
{
_db = db;
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task ExecSql([Remainder]string sql)
{
try
{
int res;
using (var uow = _db.UnitOfWork)
{
res = uow._context.Database.ExecuteSqlCommand(sql);
}
await Context.Channel.SendConfirmAsync(res.ToString());
}
catch (Exception ex)
{
await Context.Channel.SendErrorAsync(ex.ToString());
}
}
}
}
}
#endif

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Discord;
@ -7,7 +6,6 @@ using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Common.Collections;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NLog;
namespace NadekoBot.Modules.Administration.Services
@ -16,12 +14,14 @@ namespace NadekoBot.Modules.Administration.Services
{
public readonly ConcurrentHashSet<ulong> DeleteMessagesOnCommand;
private readonly Logger _log;
private readonly NadekoBot _bot;
public AdministrationService(IEnumerable<GuildConfig> gcs, CommandHandler cmdHandler)
public AdministrationService(NadekoBot bot, CommandHandler cmdHandler)
{
_log = LogManager.GetCurrentClassLogger();
_bot = bot;
DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(gcs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(bot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
cmdHandler.CommandExecuted += DelMsgOnCmd_Handler;
}

View File

@ -1,11 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Discord.WebSocket;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NLog;
namespace NadekoBot.Modules.Administration.Services
@ -18,13 +16,13 @@ namespace NadekoBot.Modules.Administration.Services
//guildid/roleid
public ConcurrentDictionary<ulong, ulong> AutoAssignedRoles { get; }
public AutoAssignRoleService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs)
public AutoAssignRoleService(DiscordSocketClient client, NadekoBot bot)
{
_log = LogManager.GetCurrentClassLogger();
_client = client;
AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(
gcs.Where(x => x.AutoAssignRoleId != 0)
bot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
.ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
_client.UserJoined += (user) =>

View File

@ -19,14 +19,14 @@ namespace NadekoBot.Modules.Administration.Services
private readonly DbService _db;
private readonly DiscordSocketClient _client;
public GameVoiceChannelService(DiscordSocketClient client, DbService db, IEnumerable<GuildConfig> gcs)
public GameVoiceChannelService(DiscordSocketClient client, DbService db, NadekoBot bot)
{
_log = LogManager.GetCurrentClassLogger();
_db = db;
_client = client;
GameVoiceChannels = new ConcurrentHashSet<ulong>(
gcs.Where(gc => gc.GameVoiceChannel != null)
bot.AllGuildConfigs.Where(gc => gc.GameVoiceChannel != null)
.Select(gc => gc.GameVoiceChannel.Value));
_client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;

View File

@ -16,9 +16,9 @@ namespace NadekoBot.Modules.Administration.Services
private ConcurrentDictionary<ulong, TimeZoneInfo> _timezones;
private readonly DbService _db;
public GuildTimezoneService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs, DbService db)
public GuildTimezoneService(DiscordSocketClient client, NadekoBot bot, DbService db)
{
_timezones = gcs
_timezones = bot.AllGuildConfigs
.Select(x =>
{
TimeZoneInfo tz;
@ -33,10 +33,10 @@ namespace NadekoBot.Modules.Administration.Services
{
tz = null;
}
return (x.GuildId, tz);
return (x.GuildId, Timezone: tz);
})
.Where(x => x.Item2 != null)
.ToDictionary(x => x.Item1, x => x.Item2)
.Where(x => x.Timezone != null)
.ToDictionary(x => x.GuildId, x => x.Timezone)
.ToConcurrent();
var curUser = client.CurrentUser;

View File

@ -50,7 +50,7 @@ namespace NadekoBot.Modules.Administration.Services
private readonly GuildTimezoneService _tz;
public LogCommandService(DiscordSocketClient client, NadekoStrings strings,
IEnumerable<GuildConfig> gcs, DbService db, MuteService mute, ProtectionService prot, GuildTimezoneService tz)
NadekoBot bot, DbService db, MuteService mute, ProtectionService prot, GuildTimezoneService tz)
{
_client = client;
_log = LogManager.GetCurrentClassLogger();
@ -60,7 +60,7 @@ namespace NadekoBot.Modules.Administration.Services
_prot = prot;
_tz = tz;
GuildLogSettings = gcs
GuildLogSettings = bot.AllGuildConfigs
.ToDictionary(g => g.GuildId, g => g.LogSetting)
.ToConcurrent();

View File

@ -38,22 +38,25 @@ namespace NadekoBot.Modules.Administration.Services
private readonly DiscordSocketClient _client;
private readonly DbService _db;
public MuteService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs, DbService db)
public MuteService(DiscordSocketClient client, NadekoBot bot, DbService db)
{
_client = client;
_db = db;
GuildMuteRoles = gcs
.Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
.ToDictionary(c => c.GuildId, c => c.MuteRoleName)
.ToConcurrent();
GuildMuteRoles = bot
.AllGuildConfigs
.Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
.ToDictionary(c => c.GuildId, c => c.MuteRoleName)
.ToConcurrent();
MutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(gcs.ToDictionary(
k => k.GuildId,
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
MutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(bot
.AllGuildConfigs
.ToDictionary(
k => k.GuildId,
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
));
foreach (var conf in gcs)
foreach (var conf in bot.AllGuildConfigs)
{
foreach (var x in conf.UnmuteTimers)
{

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Discord;
@ -26,13 +25,13 @@ namespace NadekoBot.Modules.Administration.Services
private readonly DiscordSocketClient _client;
private readonly MuteService _mute;
public ProtectionService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs, MuteService mute)
public ProtectionService(DiscordSocketClient client, NadekoBot bot, MuteService mute)
{
_log = LogManager.GetCurrentClassLogger();
_client = client;
_mute = mute;
foreach (var gc in gcs)
foreach (var gc in bot.AllGuildConfigs)
{
var raid = gc.AntiRaidSetting;
var spam = gc.AntiSpamSetting;

View File

@ -23,17 +23,17 @@ namespace NadekoBot.Modules.Administration.Services
private readonly Logger _log;
private readonly DiscordSocketClient _client;
public SlowmodeService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs)
public SlowmodeService(DiscordSocketClient client, NadekoBot bot)
{
_log = LogManager.GetCurrentClassLogger();
_client = client;
IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>(
gcs.ToDictionary(x => x.GuildId,
bot.AllGuildConfigs.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,
bot.AllGuildConfigs.ToDictionary(x => x.GuildId,
x => new HashSet<ulong>(x.SlowmodeIgnoredUsers.Select(y => y.UserId))));
}

View File

@ -42,7 +42,7 @@ namespace NadekoBot.Modules.Administration.Services
_creds = creds;
_bc = bc;
var _ = Task.Run(async () =>
Task.Run(async () =>
{
await bot.Ready.Task.ConfigureAwait(false);
@ -57,14 +57,12 @@ namespace NadekoBot.Modules.Administration.Services
}
});
var ___ = Task.Run(async () =>
Task.Run(async () =>
{
await bot.Ready.Task.ConfigureAwait(false);
await Task.Delay(5000);
_client.Guilds.SelectMany(g => g.Users);
if(client.ShardId == 0)
LoadOwnerChannels();
});

View File

@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Administration.Services
public ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>> VcRoles { get; }
public VcRoleService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs, DbService db)
public VcRoleService(DiscordSocketClient client, NadekoBot bot, DbService db)
{
_log = LogManager.GetCurrentClassLogger();
_db = db;
@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Administration.Services
_client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated;
VcRoles = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>>();
var missingRoles = new List<VcRoleInfo>();
foreach (var gconf in gcs)
foreach (var gconf in bot.AllGuildConfigs)
{
var g = _client.GetGuild(gconf.GuildId);
if (g == null)

View File

@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Administration.Services
private readonly DbService _db;
private readonly Logger _log;
public VplusTService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs, NadekoStrings strings,
public VplusTService(DiscordSocketClient client, NadekoBot bot, NadekoStrings strings,
DbService db)
{
_client = client;
@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Administration.Services
_db = db;
_log = LogManager.GetCurrentClassLogger();
VoicePlusTextCache = new ConcurrentHashSet<ulong>(gcs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
VoicePlusTextCache = new ConcurrentHashSet<ulong>(bot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
_client.UserVoiceStateUpdated += UserUpdatedEventHandler;
}

View File

@ -16,10 +16,10 @@ namespace NadekoBot.Modules.Permissions.Services
public ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> CommandCooldowns { get; }
public ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> ActiveCooldowns { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>();
public CmdCdService(IEnumerable<GuildConfig> gcs)
public CmdCdService(NadekoBot bot)
{
CommandCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>>(
gcs.ToDictionary(k => k.GuildId,
bot.AllGuildConfigs.ToDictionary(k => k.GuildId,
v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
}

View File

@ -43,21 +43,21 @@ namespace NadekoBot.Modules.Permissions.Services
return words;
}
public FilterService(DiscordSocketClient _client, IEnumerable<GuildConfig> gcs)
public FilterService(DiscordSocketClient _client, NadekoBot bot)
{
_log = LogManager.GetCurrentClassLogger();
InviteFilteringServers = new ConcurrentHashSet<ulong>(gcs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
InviteFilteringChannels = new ConcurrentHashSet<ulong>(gcs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
InviteFilteringServers = new ConcurrentHashSet<ulong>(bot.AllGuildConfigs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
InviteFilteringChannels = new ConcurrentHashSet<ulong>(bot.AllGuildConfigs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
var dict = gcs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word)));
var dict = bot.AllGuildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word)));
ServerFilteredWords = new ConcurrentDictionary<ulong, ConcurrentHashSet<string>>(dict);
var serverFiltering = gcs.Where(gc => gc.FilterWords);
var serverFiltering = bot.AllGuildConfigs.Where(gc => gc.FilterWords);
WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId));
WordFilteringChannels = new ConcurrentHashSet<ulong>(gcs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId)));
WordFilteringChannels = new ConcurrentHashSet<ulong>(bot.AllGuildConfigs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId)));
_client.MessageUpdated += (oldData, newMsg, channel) =>
{

View File

@ -51,7 +51,9 @@ namespace NadekoBot.Services
public ConcurrentHashSet<ulong> UsersOnShortCooldown { get; } = new ConcurrentHashSet<ulong>();
private readonly Timer _clearUsersOnShortCooldown;
public CommandHandler(DiscordSocketClient client, DbService db, IBotConfigProvider bc, IEnumerable<GuildConfig> gcs, CommandService commandService, IBotCredentials credentials, NadekoBot bot)
public CommandHandler(DiscordSocketClient client, DbService db,
IBotConfigProvider bc, CommandService commandService,
IBotCredentials credentials, NadekoBot bot)
{
_client = client;
_commandService = commandService;
@ -67,7 +69,7 @@ namespace NadekoBot.Services
}, null, GlobalCommandsCooldown, GlobalCommandsCooldown);
DefaultPrefix = bc.BotConfig.DefaultPrefix;
_prefixes = gcs
_prefixes = bot.AllGuildConfigs
.Where(x => x.Prefix != null)
.ToDictionary(x => x.GuildId, x => x.Prefix)
.ToConcurrent();

View File

@ -29,8 +29,6 @@ namespace NadekoBot.Services.Database
public DbSet<Quote> Quotes { get; set; }
public DbSet<Donator> Donators { get; set; }
public DbSet<GuildConfig> GuildConfigs { get; set; }
public DbSet<ClashWar> ClashOfClans { get; set; }
public DbSet<ClashCaller> ClashCallers { get; set; }
public DbSet<Reminder> Reminders { get; set; }
public DbSet<SelfAssignedRole> SelfAssignableRoles { get; set; }
public DbSet<BotConfig> BotConfig { get; set; }

View File

@ -21,13 +21,15 @@ namespace NadekoBot.Services
private readonly DiscordSocketClient _client;
private readonly Logger _log;
public GreetSettingsService(DiscordSocketClient client, IEnumerable<GuildConfig> guildConfigs, DbService db)
public GreetSettingsService(DiscordSocketClient client, NadekoBot bot, DbService db)
{
_db = db;
_client = client;
_log = LogManager.GetCurrentClassLogger();
GuildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(guildConfigs.ToDictionary(g => g.GuildId, GreetSettings.Create));
GuildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(
bot.AllGuildConfigs
.ToDictionary(g => g.GuildId, GreetSettings.Create));
_client.UserJoined += UserJoined;
_client.UserLeft += UserLeft;
@ -180,10 +182,8 @@ namespace NadekoBot.Services
public GreetSettings GetOrAddSettingsForGuild(ulong guildId)
{
GreetSettings settings;
GuildConfigsCache.TryGetValue(guildId, out settings);
if (settings != null)
if(GuildConfigsCache.TryGetValue(guildId, out var settings) &&
settings != null)
return settings;
using (var uow = _db.UnitOfWork)

View File

@ -28,11 +28,11 @@ namespace NadekoBot.Services.Impl
}
private Localization() { }
public Localization(IBotConfigProvider bcp, IEnumerable<GuildConfig> gcs, DbService db)
public Localization(IBotConfigProvider bcp, NadekoBot bot, DbService db)
{
_log = LogManager.GetCurrentClassLogger();
var cultureInfoNames = gcs.ToDictionary(x => x.GuildId, x => x.Locale);
var cultureInfoNames = bot.AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale);
var defaultCulture = bcp.BotConfig.Locale;
_db = db;
@ -123,8 +123,7 @@ namespace NadekoBot.Services.Impl
{
if (guildId == null)
return DefaultCultureInfo;
CultureInfo info = null;
GuildCultureInfos.TryGetValue(guildId.Value, out info);
GuildCultureInfos.TryGetValue(guildId.Value, out CultureInfo info);
return info ?? DefaultCultureInfo;
}

View File

@ -42,7 +42,6 @@ namespace NadekoBot.Services.Impl
private readonly Timer _carbonitexTimer;
private readonly Timer _dataTimer;
private readonly ShardsCoordinator _sc;
private readonly ConnectionMultiplexer _redis;
public StatsService(DiscordSocketClient client, CommandHandler cmdHandler,
@ -51,7 +50,6 @@ namespace NadekoBot.Services.Impl
{
_client = client;
_creds = creds;
_sc = nadeko.ShardCoord;
_redis = cache.Redis;
_started = DateTime.UtcNow;
@ -134,7 +132,7 @@ namespace NadekoBot.Services.Impl
return Task.CompletedTask;
};
if (_sc != null)
if (_client.ShardId == 0)
{
_carbonitexTimer = new Timer(async (state) =>
{

View File

@ -48,8 +48,6 @@ namespace NadekoBot
public INServiceProvider Services { get; private set; }
public ShardsCoordinator ShardCoord { get; private set; }
private readonly BotConfig _botConfig;
public IDataCache Cache { get; private set; }
@ -143,9 +141,6 @@ namespace NadekoBot
.AddManual(Client)
.AddManual(CommandService)
.AddManual(botConfigProvider)
//todo this needs to reload whenever a new service is supposed to be loaded
//except at startup for obvious reasons
.AddManual<IEnumerable<GuildConfig>>(AllGuildConfigs) //todo wrap this
.AddManual<NadekoBot>(this)
.AddManual<IUnitOfWork>(uow)
.AddManual<IDataCache>(Cache);
@ -255,9 +250,6 @@ namespace NadekoBot
public async Task RunAsync(params string[] args)
{
if (Client.ShardId == 0)
_log.Info("Starting NadekoBot v" + StatsService.BotVersion);
var sw = Stopwatch.StartNew();
await LoginAsync(Credentials.Token).ConfigureAwait(false);
@ -450,7 +442,12 @@ namespace NadekoBot
{
if (_packageModules.ContainsKey(name))
return false;
var startingGuildIdList = Client.Guilds.Select(x => (long)x.Id).ToList();
using (var uow = _db.UnitOfWork)
{
AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList).ToImmutableArray();
}
var package = Assembly.LoadFile(Path.Combine(AppContext.BaseDirectory,
"modules",
$"NadekoBot.Modules.{name}",

View File

@ -54,6 +54,16 @@ namespace NadekoBot.Services
return this;
}
public INServiceProvider UpdateManual<T>(T obj)
{
lock (_locker)
{
_services.Remove(typeof(T));
_services.TryAdd(typeof(T), obj);
}
return this;
}
public IEnumerable<Type> LoadFrom(Assembly assembly)
{
List<Type> addedTypes = new List<Type>();

View File

@ -27,7 +27,11 @@ namespace NadekoBot.Services
LogSetup.SetupLogger();
_log = LogManager.GetCurrentClassLogger();
_creds = new BotCredentials();
_log.Info("Starting NadekoBot v" + StatsService.BotVersion);
_key = _creds.RedisKey();
_redis = ConnectionMultiplexer.Connect("127.0.0.1");
//setup initial shard statuses
_defaultShardState = new ShardComMessage()

View File

@ -0,0 +1,12 @@
using NadekoBot.Services;
using System;
using System.Collections.Concurrent;
namespace NadekoBot.Modules.Gambling.Services
{
public class WaifuService : INService
{
public ConcurrentDictionary<ulong, DateTime> DivorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
public ConcurrentDictionary<ulong, DateTime> AffinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
}
}

View File

@ -4,13 +4,17 @@ using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NadekoBot.Common;
using NadekoBot.Common.Attributes;
using NadekoBot.Modules.Gambling.Services;
//todo add .deletewaifus
//todo add .deletecurrency
//todo add .deleteplaylists
//todo add .deletexp
namespace NadekoBot.Modules.Gambling
{
public partial class Gambling
@ -44,13 +48,10 @@ namespace NadekoBot.Modules.Gambling
Depraved,
Harlot
}
//todo unclaimed waifus should lose 3% of their value a day
[Group]
public class WaifuClaimCommands : NadekoSubmodule
{
private static ConcurrentDictionary<ulong, DateTime> _divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
private static ConcurrentDictionary<ulong, DateTime> _affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
[Group]
public class WaifuClaimCommands : NadekoSubmodule<WaifuService>
{
enum WaifuClaimResult
{
Success,
@ -219,7 +220,7 @@ namespace NadekoBot.Modules.Gambling
var now = DateTime.UtcNow;
if (w?.Claimer == null || w.Claimer.UserId != Context.User.Id)
result = DivorceResult.NotYourWife;
else if (_divorceCooldowns.AddOrUpdate(Context.User.Id,
else if (_service.DivorceCooldowns.AddOrUpdate(Context.User.Id,
now,
(key, old) => ((difference = now.Subtract(old)) > _divorceLimit) ? now : old) != now)
{
@ -303,7 +304,7 @@ namespace NadekoBot.Modules.Gambling
if (w?.Affinity?.UserId == u?.Id)
{
}
else if (_affinityCooldowns.AddOrUpdate(Context.User.Id,
else if (_service.AffinityCooldowns.AddOrUpdate(Context.User.Id,
now,
(key, old) => ((difference = now.Subtract(old)) > _affinityLimit) ? now : old) != now)
{

View File

@ -28,8 +28,9 @@ namespace NadekoBot.Modules.Games.Services
public ConcurrentDictionary<ulong, Lazy<IChatterBotSession>> ChatterBotGuilds { get; }
public ChatterBotService(DiscordSocketClient client, PermissionService perms, IEnumerable<GuildConfig> gcs,
CommandHandler cmd, NadekoStrings strings, IBotCredentials creds)
public ChatterBotService(DiscordSocketClient client, PermissionService perms,
NadekoBot bot, CommandHandler cmd, NadekoStrings strings,
IBotCredentials creds)
{
_client = client;
_log = LogManager.GetCurrentClassLogger();
@ -39,7 +40,8 @@ namespace NadekoBot.Modules.Games.Services
_creds = creds;
ChatterBotGuilds = new ConcurrentDictionary<ulong, Lazy<IChatterBotSession>>(
gcs.Where(gc => gc.CleverbotEnabled)
bot.AllGuildConfigs
.Where(gc => gc.CleverbotEnabled)
.ToDictionary(gc => gc.GuildId, gc => new Lazy<IChatterBotSession>(() => CreateSession(), true)));
}

View File

@ -55,7 +55,7 @@ namespace NadekoBot.Modules.Games.Services
public ConcurrentDictionary<ulong, TypingGame> RunningContests { get; } = new ConcurrentDictionary<ulong, TypingGame>();
public ConcurrentDictionary<ulong, Nunchi> NunchiGames { get; } = new ConcurrentDictionary<ulong, Common.Nunchi.Nunchi>();
public GamesService(CommandHandler cmd, IBotConfigProvider bc, IEnumerable<GuildConfig> gcs,
public GamesService(CommandHandler cmd, IBotConfigProvider bc, NadekoBot bot,
NadekoStrings strings, IImagesService images, CommandHandler cmdHandler)
{
_bc = bc;
@ -77,7 +77,8 @@ namespace NadekoBot.Modules.Games.Services
//plantpick
_cmd.OnMessageNoTrigger += PotentialFlowerGeneration;
GenerationChannels = new ConcurrentHashSet<ulong>(gcs
GenerationChannels = new ConcurrentHashSet<ulong>(bot
.AllGuildConfigs
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
try

View File

@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Music.Services
public MusicService(DiscordSocketClient client, IGoogleApiService google,
NadekoStrings strings, ILocalization localization, DbService db,
SoundCloudApiService sc, IBotCredentials creds, IEnumerable<GuildConfig> gcs)
SoundCloudApiService sc, IBotCredentials creds, NadekoBot bot)
{
_client = client;
_google = google;
@ -51,7 +51,9 @@ namespace NadekoBot.Modules.Music.Services
try { Directory.Delete(MusicDataPath, true); } catch { }
_defaultVolumes = new ConcurrentDictionary<ulong, float>(gcs.ToDictionary(x => x.GuildId, x => x.DefaultMusicVolume));
_defaultVolumes = new ConcurrentDictionary<ulong, float>(
bot.AllGuildConfigs
.ToDictionary(x => x.GuildId, x => x.DefaultMusicVolume));
Directory.CreateDirectory(MusicDataPath);
}

View File

@ -24,11 +24,13 @@ namespace NadekoBot.Modules.Searches.Services
private readonly ConcurrentDictionary<string, DateTime> _lastPosts =
new ConcurrentDictionary<string, DateTime>();
public FeedsService(IEnumerable<GuildConfig> gcs, DbService db, DiscordSocketClient client)
public FeedsService(NadekoBot bot, DbService db, DiscordSocketClient client)
{
_db = db;
_subs = gcs.SelectMany(x => x.FeedSubs)
_subs = bot
.AllGuildConfigs
.SelectMany(x => x.FeedSubs)
.GroupBy(x => x.Url)
.ToDictionary(x => x.Key, x => x.ToHashSet())
.ToConcurrent();
@ -54,8 +56,7 @@ namespace NadekoBot.Modules.Searches.Services
if (kvp.Value.Count == 0)
continue;
DateTime lastTime;
if (!_lastPosts.TryGetValue(kvp.Key, out lastTime))
if (!_lastPosts.TryGetValue(kvp.Key, out DateTime lastTime))
lastTime = _lastPosts.AddOrUpdate(kvp.Key, DateTime.UtcNow, (k, old) => DateTime.UtcNow);
var rssUrl = kvp.Key;

View File

@ -49,7 +49,8 @@ namespace NadekoBot.Modules.Searches.Services
private readonly ConcurrentDictionary<ulong, HashSet<string>> _blacklistedTags = new ConcurrentDictionary<ulong, HashSet<string>>();
public SearchesService(DiscordSocketClient client, IGoogleApiService google, DbService db, IEnumerable<GuildConfig> gcs)
public SearchesService(DiscordSocketClient client, IGoogleApiService google,
DbService db, NadekoBot bot)
{
Http = new HttpClient();
Http.AddFakeHeaders();
@ -59,7 +60,7 @@ namespace NadekoBot.Modules.Searches.Services
_log = LogManager.GetCurrentClassLogger();
_blacklistedTags = new ConcurrentDictionary<ulong, HashSet<string>>(
gcs.ToDictionary(
bot.AllGuildConfigs.ToDictionary(
x => x.GuildId,
x => new HashSet<string>(x.NsfwBlacklistedTags.Select(y => y.Tag))));

View File

@ -26,21 +26,19 @@ namespace NadekoBot.Modules.Utility
_db = db;
_currency = currency;
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
[RequireContext(ContextType.DM)]
public async Task PatreonRewardsReload()
{
if (string.IsNullOrWhiteSpace(_creds.PatreonAccessToken))
return;
await _service.RefreshPledges(true).ConfigureAwait(false);
await _service.RefreshPledges().ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("👌").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.DM)]
public async Task ClaimPatreonRewards()
{
if (string.IsNullOrWhiteSpace(_creds.PatreonAccessToken))

View File

@ -17,11 +17,11 @@ namespace NadekoBot.Modules.Utility.Services
public ConcurrentDictionary<ulong, ConcurrentDictionary<string, string>> AliasMaps { get; } = new ConcurrentDictionary<ulong, ConcurrentDictionary<string, string>>();
//commandmap
public CommandMapService(IEnumerable<GuildConfig> gcs)
public CommandMapService(NadekoBot bot)
{
_log = LogManager.GetCurrentClassLogger();
AliasMaps = new ConcurrentDictionary<ulong, ConcurrentDictionary<string, string>>(
gcs.ToDictionary(
bot.AllGuildConfigs.ToDictionary(
x => x.GuildId,
x => new ConcurrentDictionary<string, string>(x.CommandAliases
.Distinct(new CommandAliasEqualityComparer())

View File

@ -13,6 +13,7 @@ using NLog;
namespace NadekoBot.Modules.Utility.Services
{
//todo rewrite
public class ConverterService : INService, IUnloadableService
{
public List<ConvertUnit> Units { get; } = new List<ConvertUnit>();

View File

@ -16,24 +16,25 @@ namespace NadekoBot.Modules.Utility.Services
public ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>> Repeaters { get; set; }
public bool RepeaterReady { get; private set; }
public MessageRepeaterService(NadekoBot bot, DiscordSocketClient client, IEnumerable<GuildConfig> gcs)
public MessageRepeaterService(NadekoBot bot, DiscordSocketClient client)
{
var _ = Task.Run(async () =>
{
await bot.Ready.Task.ConfigureAwait(false);
Repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(gcs
.Select(gc =>
{
var guild = client.GetGuild(gc.GuildId);
if (guild == null)
return (0, null);
return (gc.GuildId, new ConcurrentQueue<RepeatRunner>(gc.GuildRepeaters
.Select(gr => new RepeatRunner(client, guild, gr))
.Where(x => x.Guild != null)));
})
.Where(x => x.Item2 != null)
.ToDictionary(x => x.GuildId, x => x.Item2));
Repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(
bot.AllGuildConfigs
.Select(gc =>
{
var guild = client.GetGuild(gc.GuildId);
if (guild == null)
return (0, null);
return (gc.GuildId, new ConcurrentQueue<RepeatRunner>(gc.GuildRepeaters
.Select(gr => new RepeatRunner(client, guild, gr))
.Where(x => x.Guild != null)));
})
.Where(x => x.Item2 != null)
.ToDictionary(x => x.GuildId, x => x.Item2));
RepeaterReady = true;
});
}

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
@ -12,6 +10,8 @@ using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using Newtonsoft.Json;
using NLog;
using NadekoBot.Extensions;
using NadekoBot.Core.Common.Caching;
namespace NadekoBot.Modules.Utility.Services
{
@ -19,8 +19,8 @@ namespace NadekoBot.Modules.Utility.Services
{
private readonly SemaphoreSlim getPledgesLocker = new SemaphoreSlim(1, 1);
public ImmutableArray<PatreonUserAndReward> Pledges { get; private set; }
public DateTime LastUpdate { get; private set; } = DateTime.UtcNow;
private readonly FactoryCache<PatreonUserAndReward[]> _pledges;
public PatreonUserAndReward[] Pledges => _pledges.GetValue();
public readonly Timer Updater;
private readonly SemaphoreSlim claimLockJustInCase = new SemaphoreSlim(1, 1);
@ -30,79 +30,95 @@ namespace NadekoBot.Modules.Utility.Services
private readonly IBotCredentials _creds;
private readonly DbService _db;
private readonly CurrencyService _currency;
private readonly IDataCache _cache;
private readonly string _key;
private readonly string cacheFileName = "./patreon-rewards.json";
public DateTime LastUpdate { get; private set; } = DateTime.UtcNow;
public PatreonRewardsService(IBotCredentials creds, DbService db,
CurrencyService currency,
DiscordSocketClient client)
DiscordSocketClient client, IDataCache cache)
{
_log = LogManager.GetCurrentClassLogger();
_creds = creds;
_db = db;
_currency = currency;
if (string.IsNullOrWhiteSpace(creds.PatreonAccessToken))
return;
Updater = new Timer(async (load) => await RefreshPledges((bool)load),
client.ShardId == 0, client.ShardId == 0 ? TimeSpan.Zero : TimeSpan.FromMinutes(2), Interval);
_cache = cache;
_key = _creds.RedisKey() + "_patreon_rewards";
_pledges = new FactoryCache<PatreonUserAndReward[]>(() =>
{
var r = _cache.Redis.GetDatabase();
var data = r.StringGet(_key);
if (data.IsNullOrEmpty)
return null;
else
{
_log.Info(data);
return JsonConvert.DeserializeObject<PatreonUserAndReward[]>(data);
}
}, TimeSpan.FromSeconds(20));
if(client.ShardId == 0)
Updater = new Timer(async _ => await RefreshPledges(),
null, TimeSpan.Zero, Interval);
}
public async Task RefreshPledges(bool shouldLoad)
public async Task RefreshPledges()
{
if (shouldLoad)
if (string.IsNullOrWhiteSpace(_creds.PatreonAccessToken))
return;
LastUpdate = DateTime.UtcNow;
await getPledgesLocker.WaitAsync().ConfigureAwait(false);
try
{
LastUpdate = DateTime.UtcNow;
await getPledgesLocker.WaitAsync().ConfigureAwait(false);
try
var rewards = new List<PatreonPledge>();
var users = new List<PatreonUser>();
using (var http = new HttpClient())
{
var rewards = new List<PatreonPledge>();
var users = new List<PatreonUser>();
using (var http = new HttpClient())
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("Authorization", "Bearer " + _creds.PatreonAccessToken);
var data = new PatreonData()
{
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("Authorization", "Bearer " + _creds.PatreonAccessToken);
var data = new PatreonData()
Links = new PatreonDataLinks()
{
Links = new PatreonDataLinks()
{
next = $"https://api.patreon.com/oauth2/api/campaigns/{_creds.PatreonCampaignId}/pledges"
}
};
do
{
var res = await http.GetStringAsync(data.Links.next)
.ConfigureAwait(false);
data = JsonConvert.DeserializeObject<PatreonData>(res);
var pledgers = data.Data.Where(x => x["type"].ToString() == "pledge");
rewards.AddRange(pledgers.Select(x => JsonConvert.DeserializeObject<PatreonPledge>(x.ToString()))
.Where(x => x.attributes.declined_since == null));
users.AddRange(data.Included
.Where(x => x["type"].ToString() == "user")
.Select(x => JsonConvert.DeserializeObject<PatreonUser>(x.ToString())));
} while (!string.IsNullOrWhiteSpace(data.Links.next));
}
Pledges = rewards.Join(users, (r) => r.relationships?.patron?.data?.id, (u) => u.id, (x, y) => new PatreonUserAndReward()
next = $"https://api.patreon.com/oauth2/api/campaigns/{_creds.PatreonCampaignId}/pledges"
}
};
do
{
User = y,
Reward = x,
}).ToImmutableArray();
File.WriteAllText("./patreon_rewards.json", JsonConvert.SerializeObject(Pledges));
var res = await http.GetStringAsync(data.Links.next)
.ConfigureAwait(false);
data = JsonConvert.DeserializeObject<PatreonData>(res);
var pledgers = data.Data.Where(x => x["type"].ToString() == "pledge");
rewards.AddRange(pledgers.Select(x => JsonConvert.DeserializeObject<PatreonPledge>(x.ToString()))
.Where(x => x.attributes.declined_since == null));
users.AddRange(data.Included
.Where(x => x["type"].ToString() == "user")
.Select(x => JsonConvert.DeserializeObject<PatreonUser>(x.ToString())));
} while (!string.IsNullOrWhiteSpace(data.Links.next));
}
catch (Exception ex)
var db = _cache.Redis.GetDatabase();
var toSet = JsonConvert.SerializeObject(rewards.Join(users, (r) => r.relationships?.patron?.data?.id, (u) => u.id, (x, y) => new PatreonUserAndReward()
{
_log.Warn(ex);
}
finally
{
getPledgesLocker.Release();
}
User = y,
Reward = x,
}).ToArray());
_log.Info(toSet);
db.StringSet(_key, toSet);
}
else
catch (Exception ex)
{
if(File.Exists(cacheFileName))
Pledges = JsonConvert.DeserializeObject<PatreonUserAndReward[]>(File.ReadAllText("./patreon_rewards.json"))
.ToImmutableArray();
_log.Warn(ex);
}
finally
{
getPledgesLocker.Release();
}
}
public async Task<int> ClaimReward(ulong userId)
@ -111,7 +127,7 @@ namespace NadekoBot.Modules.Utility.Services
var now = DateTime.UtcNow;
try
{
var data = Pledges.FirstOrDefault(x => x.User.attributes?.social_connections?.discord?.user_id == userId.ToString());
var data = Pledges?.FirstOrDefault(x => x.User.attributes?.social_connections?.discord?.user_id == userId.ToString());
if (data == null)
return 0;
@ -175,7 +191,7 @@ namespace NadekoBot.Modules.Utility.Services
public Task Unload()
{
Updater.Change(Timeout.Infinite, Timeout.Infinite);
Updater?.Change(Timeout.Infinite, Timeout.Infinite);
return Task.CompletedTask;
}
}

View File

@ -24,13 +24,14 @@ namespace NadekoBot.Modules.Utility.Services
private readonly ConcurrentDictionary<ulong, StreamRoleSettings> guildSettings;
private readonly Logger _log;
public StreamRoleService(DiscordSocketClient client, DbService db, IEnumerable<GuildConfig> gcs)
public StreamRoleService(DiscordSocketClient client, DbService db, NadekoBot bot)
{
this._log = LogManager.GetCurrentClassLogger();
this._db = db;
this._client = client;
guildSettings = gcs.ToDictionary(x => x.GuildId, x => x.StreamRole)
guildSettings = bot.AllGuildConfigs
.ToDictionary(x => x.GuildId, x => x.StreamRole)
.Where(x => x.Value != null && x.Value.Enabled)
.ToConcurrent();

View File

@ -18,7 +18,7 @@ namespace NadekoBot.Modules.Utility.Services
private readonly CommandHandler _ch;
private readonly HelpService _hs;
public VerboseErrorsService(IEnumerable<GuildConfig> gcs, DbService db, CommandHandler ch, HelpService hs)
public VerboseErrorsService(NadekoBot bot, DbService db, CommandHandler ch, HelpService hs)
{
_db = db;
_ch = ch;
@ -26,7 +26,10 @@ namespace NadekoBot.Modules.Utility.Services
_ch.CommandErrored += LogVerboseError;
guildsEnabled = new ConcurrentHashSet<ulong>(gcs.Where(x => x.VerboseErrors).Select(x => x.GuildId));
guildsEnabled = new ConcurrentHashSet<ulong>(bot
.AllGuildConfigs
.Where(x => x.VerboseErrors)
.Select(x => x.GuildId));
}
public Task Unload()

View File

@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Xp.Services
private Font _timeFont;
public XpService(CommandHandler cmd, IBotConfigProvider bc,
IEnumerable<GuildConfig> allGuildConfigs, IImagesService images,
NadekoBot bot, IImagesService images,
DbService db, NadekoStrings strings, IDataCache cache)
{
_db = db;
@ -80,7 +80,7 @@ namespace NadekoBot.Modules.Xp.Services
_cache = cache;
//load settings
allGuildConfigs = allGuildConfigs.Where(x => x.XpSettings != null);
var allGuildConfigs = bot.AllGuildConfigs.Where(x => x.XpSettings != null);
_excludedChannels = allGuildConfigs
.ToDictionary(
x => x.GuildId,
@ -685,7 +685,6 @@ namespace NadekoBot.Modules.Xp.Services
}
//club image
if (!string.IsNullOrWhiteSpace(stats.User.Club?.ImageUrl))
{
var imgUrl = stats.User.Club.ImageUrl;
@ -694,6 +693,7 @@ namespace NadekoBot.Modules.Xp.Services
var (succ, data) = await _cache.TryGetImageDataAsync(imgUrl);
if (!succ)
{
//todo make sure it's a picture
using (var temp = await http.GetStreamAsync(imgUrl))
using (var tempDraw = Image.Load(temp).Resize(45, 45))
{

View File

@ -1836,7 +1836,7 @@
},
"antispam": {
"Cmd": "antispam",
"Desc": "Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. Max message count is 10.",
"Desc": "Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. If you're using mute, you can add a number of seconds at the end to use a timed mute. Max message count is 10.",
"Usage": [
"{0}antispam 3 Mute",
"{0}antispam 4 Kick",