Packages can be loaded/unloaded. IUnloadableService interface added whose method Unload, if service implements it, will be called when the module is unloaded.
This commit is contained in:
1 parent
599245b1ca
commit
33ac43e1b5
74 files changed
+866
-520
No files matched your search
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Modules.CustomReactions.Services;
|
||||
|
||||
namespace NadekoBot.Common.TypeReaders
|
||||
{
|
||||
@@ -28,38 +29,38 @@ namespace NadekoBot.Common.TypeReaders
|
||||
}
|
||||
}
|
||||
//todo dependency on the module
|
||||
//public class CommandOrCrTypeReader : CommandTypeReader
|
||||
//{
|
||||
// public override async Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services)
|
||||
// {
|
||||
// input = input.ToUpperInvariant();
|
||||
public class CommandOrCrTypeReader : CommandTypeReader
|
||||
{
|
||||
public override async Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services)
|
||||
{
|
||||
input = input.ToUpperInvariant();
|
||||
|
||||
// var _crs = ((INServiceProvider)services).GetService<CustomReactionsService>();
|
||||
var _crs = ((INServiceProvider)services).GetService<CustomReactionsService>();
|
||||
|
||||
// if (_crs.GlobalReactions.Any(x => x.Trigger.ToUpperInvariant() == input))
|
||||
// {
|
||||
// return TypeReaderResult.FromSuccess(new CommandOrCrInfo(input));
|
||||
// }
|
||||
// var guild = context.Guild;
|
||||
// if (guild != null)
|
||||
// {
|
||||
// if (_crs.GuildReactions.TryGetValue(guild.Id, out var crs))
|
||||
// {
|
||||
// if (crs.Concat(_crs.GlobalReactions).Any(x => x.Trigger.ToUpperInvariant() == input))
|
||||
// {
|
||||
// return TypeReaderResult.FromSuccess(new CommandOrCrInfo(input));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
if (_crs.GlobalReactions.Any(x => x.Trigger.ToUpperInvariant() == input))
|
||||
{
|
||||
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(input));
|
||||
}
|
||||
var guild = context.Guild;
|
||||
if (guild != null)
|
||||
{
|
||||
if (_crs.GuildReactions.TryGetValue(guild.Id, out var crs))
|
||||
{
|
||||
if (crs.Concat(_crs.GlobalReactions).Any(x => x.Trigger.ToUpperInvariant() == input))
|
||||
{
|
||||
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// var cmd = await base.Read(context, input, services);
|
||||
// if (cmd.IsSuccess)
|
||||
// {
|
||||
// return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name));
|
||||
// }
|
||||
// return TypeReaderResult.FromError(CommandError.ParseFailed, "No such command or cr found.");
|
||||
// }
|
||||
//}
|
||||
var cmd = await base.Read(context, input, services);
|
||||
if (cmd.IsSuccess)
|
||||
{
|
||||
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name));
|
||||
}
|
||||
return TypeReaderResult.FromError(CommandError.ParseFailed, "No such command or cr found.");
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandOrCrInfo
|
||||
{
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Core.Modules.Administration.Services;
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class PackagesCommands : NadekoSubmodule<PackagesService>
|
||||
{
|
||||
private readonly NadekoBot _bot;
|
||||
|
||||
public PackagesCommands(NadekoBot bot)
|
||||
{
|
||||
_bot = bot;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task PackageList()
|
||||
{
|
||||
_service.ReloadAvailablePackages();
|
||||
await Context.Channel.SendConfirmAsync(string.Join("\n", _service.Packages));
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[OwnerOnly]
|
||||
public async Task PackageUnload(string name)
|
||||
{
|
||||
if (name.Contains(":") || name.Contains(".") || name.Contains("\\") || name.Contains("/") || name.Contains("~"))
|
||||
return;
|
||||
name = name.ToTitleCase();
|
||||
var package = Assembly.LoadFrom(Path.Combine(AppContext.BaseDirectory,
|
||||
"modules",
|
||||
$"NadekoBot.Modules.{name}",
|
||||
$"NadekoBot.Modules.{name}.dll"));
|
||||
|
||||
await _bot.UnloadPackage(name).ConfigureAwait(false);
|
||||
await ReplyAsync(":ok:");
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[OwnerOnly]
|
||||
public async Task PackageLoad(string name)
|
||||
{
|
||||
if (name.Contains(".") || name.Contains("\\") || name.Contains("/") || name.Contains("~"))
|
||||
return;
|
||||
name = name.ToTitleCase();
|
||||
|
||||
await _bot.LoadPackage(name);
|
||||
await ReplyAsync(":ok:");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task StartupCommandAdd([Remainder] string cmdText)
|
||||
{
|
||||
//todo don't let .die be a startup command
|
||||
var guser = ((IGuildUser)Context.User);
|
||||
var cmd = new StartupCommand()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using NadekoBot.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NadekoBot.Core.Modules.Administration.Services
|
||||
{
|
||||
public class PackagesService : INService
|
||||
{
|
||||
public IEnumerable<string> Packages { get; private set; }
|
||||
|
||||
public PackagesService()
|
||||
{
|
||||
ReloadAvailablePackages();
|
||||
}
|
||||
|
||||
public void ReloadAvailablePackages()
|
||||
{
|
||||
Packages = Directory.GetDirectories(Path.Combine(AppContext.BaseDirectory, "modules\\"), "NadekoBot.Modules.*", SearchOption.AllDirectories)
|
||||
.SelectMany(x => Directory.GetFiles(x, "NadekoBot.Modules.*.dll"))
|
||||
.Select(x => Path.GetFileNameWithoutExtension(x))
|
||||
.Select(x =>
|
||||
{
|
||||
var m = Regex.Match(x, @"NadekoBot\.Modules\.(?<name>.*)");
|
||||
return m.Groups["name"].Value;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -2,9 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace NadekoBot.Services
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// All services must implement this interface in order to be auto-discovered by the DI system
|
||||
@@ -7,4 +9,12 @@
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All services which require cleanup after they are unloaded must implement this interface
|
||||
/// </summary>
|
||||
public interface IUnloadableService
|
||||
{
|
||||
Task Unload();
|
||||
}
|
||||
}
|
||||
@@ -61,17 +61,6 @@ namespace NadekoBot
|
||||
if (shardId < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(shardId));
|
||||
|
||||
//var obj = JsonConvert.DeserializeObject<Dictionary<string, CommandData2>>(File.ReadAllText("./data/command_strings.json"))
|
||||
// .ToDictionary(x => x.Key, x => new CommandData2
|
||||
// {
|
||||
// Cmd = x.Value.Cmd,
|
||||
// Desc = x.Value.Desc,
|
||||
// Usage = x.Value.Usage.Select(y => y.Substring(1, y.Length - 2)).ToArray(),
|
||||
// });
|
||||
|
||||
//File.WriteAllText("./data/command_strings.json", JsonConvert.SerializeObject(obj, Formatting.Indented));
|
||||
|
||||
|
||||
LogSetup.SetupLogger();
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
TerribleElevatedPermissionCheck();
|
||||
@@ -142,19 +131,17 @@ namespace NadekoBot
|
||||
//var localization = new Localization(_botConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), Db);
|
||||
|
||||
//initialize Services
|
||||
Services = new NServiceProvider.ServiceProviderBuilder()
|
||||
Services = new NServiceProvider()
|
||||
.AddManual<IBotCredentials>(Credentials)
|
||||
.AddManual(_db)
|
||||
.AddManual(Client)
|
||||
.AddManual(CommandService)
|
||||
.AddManual(botConfigProvider)
|
||||
//.AddManual<ILocalization>(localization)
|
||||
.AddManual<IEnumerable<GuildConfig>>(AllGuildConfigs) //todo wrap this
|
||||
.AddManual<NadekoBot>(this)
|
||||
.AddManual<IUnitOfWork>(uow)
|
||||
.AddManual<IDataCache>(new RedisCache(Client.CurrentUser.Id))
|
||||
.LoadFrom(Assembly.GetEntryAssembly())
|
||||
.Build();
|
||||
.AddManual<IDataCache>(new RedisCache(Client.CurrentUser.Id));
|
||||
Services.LoadFrom(Assembly.GetAssembly(typeof(CommandHandler)));
|
||||
|
||||
var commandHandler = Services.GetService<CommandHandler>();
|
||||
commandHandler.AddServices(Services);
|
||||
@@ -163,12 +150,13 @@ namespace NadekoBot
|
||||
CommandService.AddTypeReader<PermissionAction>(new PermissionActionTypeReader());
|
||||
CommandService.AddTypeReader<CommandInfo>(new CommandTypeReader());
|
||||
//todo module dependency
|
||||
//CommandService.AddTypeReader<CommandOrCrInfo>(new CommandOrCrTypeReader());
|
||||
CommandService.AddTypeReader<CommandOrCrInfo>(new CommandOrCrTypeReader());
|
||||
CommandService.AddTypeReader<ModuleInfo>(new ModuleTypeReader(CommandService));
|
||||
CommandService.AddTypeReader<ModuleOrCrInfo>(new ModuleOrCrTypeReader(CommandService));
|
||||
CommandService.AddTypeReader<IGuild>(new GuildTypeReader(Client));
|
||||
//CommandService.AddTypeReader<GuildDateTime>(new GuildDateTimeTypeReader());
|
||||
}
|
||||
Services.Unload(typeof(IUnitOfWork)); // unload it after the startup
|
||||
}
|
||||
|
||||
private async Task LoginAsync(string token)
|
||||
@@ -193,7 +181,7 @@ namespace NadekoBot
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
@@ -225,8 +213,8 @@ namespace NadekoBot
|
||||
|
||||
public async Task RunAsync(params string[] args)
|
||||
{
|
||||
if(Client.ShardId == 0)
|
||||
_log.Info("Starting NadekoBot v" + StatsService.BotVersion);
|
||||
if (Client.ShardId == 0)
|
||||
_log.Info("Starting NadekoBot v" + StatsService.BotVersion);
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
@@ -255,7 +243,7 @@ namespace NadekoBot
|
||||
#endif
|
||||
//unload modules which are not available on the public bot
|
||||
|
||||
if(isPublicNadeko)
|
||||
if (isPublicNadeko)
|
||||
CommandService
|
||||
.Modules
|
||||
.ToArray()
|
||||
@@ -372,5 +360,80 @@ namespace NadekoBot
|
||||
var sub = Services.GetService<IDataCache>().Redis.GetSubscriber();
|
||||
return sub.PublishAsync(Client.CurrentUser.Id + "_status.game_set", JsonConvert.SerializeObject(obj));
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, IEnumerable<ModuleInfo>> _packageModules = new Dictionary<string, IEnumerable<ModuleInfo>>();
|
||||
private readonly Dictionary<string, IEnumerable<Type>> _packageTypes = new Dictionary<string, IEnumerable<Type>>();
|
||||
private readonly SemaphoreSlim _packageLocker = new SemaphoreSlim(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Unloads a package
|
||||
/// </summary>
|
||||
/// <param name="name">Package name. Case sensitive.</param>
|
||||
/// <returns>Whether the unload is successful.</returns>
|
||||
public async Task<bool> UnloadPackage(string name)
|
||||
{
|
||||
await _packageLocker.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (!_packageModules.TryGetValue(name, out var modules))
|
||||
return false;
|
||||
|
||||
var i = 0;
|
||||
foreach (var m in modules)
|
||||
{
|
||||
await CommandService.RemoveModuleAsync(m).ConfigureAwait(false);
|
||||
i++;
|
||||
}
|
||||
_log.Info("Unloaded {0} modules.", i);
|
||||
|
||||
if (_packageTypes.TryGetValue(name, out var types))
|
||||
{
|
||||
i = 0;
|
||||
foreach (var t in types)
|
||||
{
|
||||
var obj = Services.Unload(t);
|
||||
if (obj is IUnloadableService s)
|
||||
await s.Unload().ConfigureAwait(false);
|
||||
i++;
|
||||
}
|
||||
|
||||
_log.Info("Unloaded {0} types.", i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_packageLocker.Release();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Loads a package
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the package to load. Case sensitive.</param>
|
||||
/// <returns>Whether the load is successful.</returns>
|
||||
public async Task<bool> LoadPackage(string name)
|
||||
{
|
||||
await _packageLocker.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (_packageModules.ContainsKey(name))
|
||||
return false;
|
||||
|
||||
var package = Assembly.LoadFile(Path.Combine(AppContext.BaseDirectory,
|
||||
"modules",
|
||||
$"NadekoBot.Modules.{name}",
|
||||
$"NadekoBot.Modules.{name}.dll"));
|
||||
var types = Services.LoadFrom(package);
|
||||
var added = await CommandService.AddModulesAsync(package).ConfigureAwait(false);
|
||||
_log.Info("Loaded {0} modules and {1} types.", added.Count(), types.Count());
|
||||
_packageModules.Add(name, added);
|
||||
_packageTypes.Add(name, types);
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_packageLocker.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
@@ -17,57 +16,83 @@ namespace NadekoBot.Services
|
||||
public interface INServiceProvider : IServiceProvider, IEnumerable<object>
|
||||
{
|
||||
T GetService<T>();
|
||||
IEnumerable<Type> LoadFrom(Assembly assembly);
|
||||
INServiceProvider AddManual<T>(T obj);
|
||||
object Unload(Type t);
|
||||
}
|
||||
|
||||
public class NServiceProvider : INServiceProvider
|
||||
{
|
||||
public class ServiceProviderBuilder
|
||||
private readonly object _locker = new object();
|
||||
private readonly Logger _log;
|
||||
|
||||
public readonly Dictionary<Type, object> _services = new Dictionary<Type, object>();
|
||||
public IReadOnlyDictionary<Type, object> Services => _services;
|
||||
|
||||
public NServiceProvider()
|
||||
{
|
||||
private ConcurrentDictionary<Type, object> _dict = new ConcurrentDictionary<Type, object>();
|
||||
private readonly Logger _log;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
}
|
||||
|
||||
public ServiceProviderBuilder()
|
||||
public T GetService<T>()
|
||||
{
|
||||
return (T)((IServiceProvider)(this)).GetService(typeof(T));
|
||||
}
|
||||
|
||||
object IServiceProvider.GetService(Type serviceType)
|
||||
{
|
||||
_services.TryGetValue(serviceType, out var toReturn);
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public INServiceProvider AddManual<T>(T obj)
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_services.TryAdd(typeof(T), obj);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ServiceProviderBuilder AddManual<T>(T obj)
|
||||
public IEnumerable<Type> LoadFrom(Assembly assembly)
|
||||
{
|
||||
List<Type> addedTypes = new List<Type>();
|
||||
|
||||
Type[] allTypes;
|
||||
try
|
||||
{
|
||||
_dict.TryAdd(typeof(T), obj);
|
||||
return this;
|
||||
allTypes = assembly.GetTypes();
|
||||
}
|
||||
|
||||
public NServiceProvider Build()
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
return new NServiceProvider(_dict);
|
||||
Console.WriteLine(ex.LoaderExceptions[0]);
|
||||
return Enumerable.Empty<Type>();
|
||||
}
|
||||
|
||||
public ServiceProviderBuilder LoadFrom(Assembly assembly)
|
||||
{
|
||||
var allTypes = assembly.GetTypes();
|
||||
var services = new Queue<Type>(allTypes
|
||||
.Where(x => x.GetInterfaces().Contains(typeof(INService))
|
||||
&& !x.GetTypeInfo().IsInterface && !x.GetTypeInfo().IsAbstract
|
||||
|
||||
var services = new Queue<Type>(allTypes
|
||||
.Where(x => x.GetInterfaces().Contains(typeof(INService))
|
||||
&& !x.GetTypeInfo().IsInterface && !x.GetTypeInfo().IsAbstract
|
||||
#if GLOBAL_NADEKO
|
||||
&& x.GetTypeInfo().GetCustomAttribute<NoPublicBot>() == null
|
||||
&& x.GetTypeInfo().GetCustomAttribute<NoPublicBot>() == null
|
||||
#endif
|
||||
)
|
||||
.ToArray());
|
||||
.ToArray());
|
||||
|
||||
var interfaces = new HashSet<Type>(allTypes
|
||||
.Where(x => x.GetInterfaces().Contains(typeof(INService))
|
||||
&& x.GetTypeInfo().IsInterface));
|
||||
addedTypes.AddRange(services);
|
||||
|
||||
var alreadyFailed = new Dictionary<Type, int>();
|
||||
var interfaces = new HashSet<Type>(allTypes
|
||||
.Where(x => x.GetInterfaces().Contains(typeof(INService))
|
||||
&& x.GetTypeInfo().IsInterface));
|
||||
|
||||
var alreadyFailed = new Dictionary<Type, int>();
|
||||
lock (_locker)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
var swInstance = new Stopwatch();
|
||||
while (services.Count > 0)
|
||||
{
|
||||
var type = services.Dequeue(); //get a type i need to make an instance of
|
||||
|
||||
if (_dict.TryGetValue(type, out _)) // if that type is already instantiated, skip
|
||||
if (_services.TryGetValue(type, out _)) // if that type is already instantiated, skip
|
||||
continue;
|
||||
|
||||
var ctor = type.GetConstructors()[0];
|
||||
@@ -79,7 +104,7 @@ namespace NadekoBot.Services
|
||||
var args = new List<object>(argTypes.Length);
|
||||
foreach (var arg in argTypes) //get constructor arguments from the dictionary of already instantiated types
|
||||
{
|
||||
if (_dict.TryGetValue(arg, out var argObj)) //if i got current one, add it to the list of instances and move on
|
||||
if (_services.TryGetValue(arg, out var argObj)) //if i got current one, add it to the list of instances and move on
|
||||
args.Add(argObj);
|
||||
else //if i failed getting it, add it to the end, and break
|
||||
{
|
||||
@@ -97,7 +122,7 @@ namespace NadekoBot.Services
|
||||
}
|
||||
if (args.Count != argTypes.Length)
|
||||
continue;
|
||||
// _log.Info("Loading " + type.Name);
|
||||
|
||||
swInstance.Restart();
|
||||
var instance = ctor.Invoke(args.ToArray());
|
||||
swInstance.Stop();
|
||||
@@ -105,38 +130,34 @@ namespace NadekoBot.Services
|
||||
_log.Info($"{type.Name} took {swInstance.Elapsed.TotalSeconds:F2}s to load.");
|
||||
var interfaceType = interfaces.FirstOrDefault(x => instance.GetType().GetInterfaces().Contains(x));
|
||||
if (interfaceType != null)
|
||||
_dict.TryAdd(interfaceType, instance);
|
||||
{
|
||||
addedTypes.Add(interfaceType);
|
||||
_services.TryAdd(interfaceType, instance);
|
||||
}
|
||||
|
||||
_dict.TryAdd(type, instance);
|
||||
_services.TryAdd(type, instance);
|
||||
}
|
||||
sw.Stop();
|
||||
_log.Info($"All services loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||
|
||||
return this;
|
||||
}
|
||||
return addedTypes;
|
||||
}
|
||||
|
||||
private readonly ImmutableDictionary<Type, object> _services;
|
||||
|
||||
private NServiceProvider() { }
|
||||
public NServiceProvider(IDictionary<Type, object> services)
|
||||
public object Unload(Type t)
|
||||
{
|
||||
this._services = services.ToImmutableDictionary();
|
||||
}
|
||||
|
||||
public T GetService<T>()
|
||||
{
|
||||
return (T)((IServiceProvider)(this)).GetService(typeof(T));
|
||||
}
|
||||
|
||||
object IServiceProvider.GetService(Type serviceType)
|
||||
{
|
||||
_services.TryGetValue(serviceType, out var toReturn);
|
||||
return toReturn;
|
||||
lock (_locker)
|
||||
{
|
||||
if (_services.TryGetValue(t, out var obj))
|
||||
{
|
||||
_services.Remove(t);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _services.Values.GetEnumerator();
|
||||
|
||||
public IEnumerator<object> GetEnumerator() => _services.Values.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\src\NadekoBot\bin\$(Configuration)\netcoreapp2.0\modules\$(AssemblyName)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\src\NadekoBot\bin\$(Configuration)\netcoreapp2.0\modules\$(AssemblyName)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,27 +4,24 @@ using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Modules.Gambling.Common.AnimalRacing.Exceptions;
|
||||
using NadekoBot.Modules.Gambling.Common.AnimalRacing;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling
|
||||
{
|
||||
public partial class Gambling
|
||||
{
|
||||
[Group]
|
||||
public class AnimalRacingCommands : NadekoSubmodule
|
||||
public class AnimalRacingCommands : NadekoSubmodule<AnimalRaceService>
|
||||
{
|
||||
private readonly IBotConfigProvider _bc;
|
||||
private readonly CurrencyService _cs;
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
|
||||
public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
|
||||
|
||||
public AnimalRacingCommands(IBotConfigProvider bc, CurrencyService cs, DiscordSocketClient client)
|
||||
{
|
||||
_bc = bc;
|
||||
@@ -39,7 +36,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
public Task Race()
|
||||
{
|
||||
var ar = new AnimalRace(_cs, _bc.BotConfig.RaceAnimals.Shuffle().ToArray());
|
||||
if (!AnimalRaces.TryAdd(Context.Guild.Id, ar))
|
||||
if (!_service.AnimalRaces.TryAdd(Context.Guild.Id, ar))
|
||||
return Context.Channel.SendErrorAsync(GetText("animal_race"), GetText("animal_race_already_started"));
|
||||
|
||||
ar.Initialize();
|
||||
@@ -66,7 +63,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
Task Ar_OnEnded(AnimalRace race)
|
||||
{
|
||||
_client.MessageReceived -= _client_MessageReceived;
|
||||
AnimalRaces.TryRemove(Context.Guild.Id, out _);
|
||||
_service.AnimalRaces.TryRemove(Context.Guild.Id, out _);
|
||||
var winner = race.FinishedUsers[0];
|
||||
if (race.FinishedUsers[0].Bet > 0)
|
||||
{
|
||||
@@ -126,7 +123,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
private Task Ar_OnStartingFailed(AnimalRace race)
|
||||
{
|
||||
AnimalRaces.TryRemove(Context.Guild.Id, out _);
|
||||
_service.AnimalRaces.TryRemove(Context.Guild.Id, out _);
|
||||
return ReplyErrorLocalized("animal_race_failed");
|
||||
}
|
||||
|
||||
@@ -134,7 +131,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task JoinRace(int amount = 0)
|
||||
{
|
||||
if (!AnimalRaces.TryGetValue(Context.Guild.Id, out var ar))
|
||||
if (!_service.AnimalRaces.TryGetValue(Context.Guild.Id, out var ar))
|
||||
{
|
||||
await ReplyErrorLocalized("race_not_exist").ConfigureAwait(false);
|
||||
return;
|
||||
|
||||
@@ -17,6 +17,7 @@ using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling
|
||||
{
|
||||
//todo mess, needs unload thing too - refactor
|
||||
public partial class Gambling
|
||||
{
|
||||
[Group]
|
||||
@@ -274,7 +275,6 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
_log.Warn("Stopping flower reaction event because it expired.");
|
||||
await End();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\src\NadekoBot\bin\$(Configuration)\netcoreapp2.0\modules\$(AssemblyName)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Services;
|
||||
using System.Collections.Concurrent;
|
||||
using NadekoBot.Modules.Gambling.Common.AnimalRacing;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services
|
||||
{
|
||||
public class AnimalRaceService : INService, IUnloadableService
|
||||
{
|
||||
public ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
|
||||
|
||||
public Task Unload()
|
||||
{
|
||||
foreach (var kvp in AnimalRaces)
|
||||
{
|
||||
try { kvp.Value.Dispose(); } catch { }
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,7 +176,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
using (var file = _images.SlotEmojis[numbers[i]].ToStream())
|
||||
using (var randomImage = ImageSharp.Image.Load(file))
|
||||
{
|
||||
bgImage.DrawImage(randomImage, 100, default(Size), new Point(95 + 142 * i, 330));
|
||||
bgImage.DrawImage(randomImage, 100, default, new Point(95 + 142 * i, 330));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
using (var fs = _images.SlotNumbers[digit].ToStream())
|
||||
using (var img = ImageSharp.Image.Load(fs))
|
||||
{
|
||||
bgImage.DrawImage(img, 100, default(Size), new Point(230 - n * 16, 462));
|
||||
bgImage.DrawImage(img, 100, default, new Point(230 - n * 16, 462));
|
||||
}
|
||||
n++;
|
||||
} while ((printWon /= 10) != 0);
|
||||
@@ -202,7 +202,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
using (var fs = _images.SlotNumbers[digit].ToStream())
|
||||
using (var img = ImageSharp.Image.Load(fs))
|
||||
{
|
||||
bgImage.DrawImage(img, 100, default(Size), new Point(395 - n * 16, 462));
|
||||
bgImage.DrawImage(img, 100, default, new Point(395 - n * 16, 462));
|
||||
}
|
||||
n++;
|
||||
} while ((printAmount /= 10) != 0);
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
Depraved,
|
||||
Harlot
|
||||
}
|
||||
|
||||
//todo unclaimed waifus should lose 5% of their value a day
|
||||
[Group]
|
||||
public class WaifuClaimCommands : NadekoSubmodule
|
||||
{
|
||||
|
||||
@@ -2,26 +2,23 @@
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Modules.Games.Common.Acrophobia;
|
||||
using NadekoBot.Modules.Games.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
public partial class Games
|
||||
{
|
||||
[Group]
|
||||
public class AcropobiaCommands : NadekoSubmodule
|
||||
public class AcropobiaCommands : NadekoSubmodule<GamesService>
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
//channelId, game
|
||||
public static ConcurrentDictionary<ulong, Acrophobia> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, Acrophobia>();
|
||||
|
||||
public AcropobiaCommands(DiscordSocketClient client)
|
||||
{
|
||||
_client = client;
|
||||
@@ -36,7 +33,7 @@ namespace NadekoBot.Modules.Games
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
var game = new Acrophobia(submissionTime);
|
||||
if (AcrophobiaGames.TryAdd(channel.Id, game))
|
||||
if (_service.AcrophobiaGames.TryAdd(channel.Id, game))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -50,7 +47,7 @@ namespace NadekoBot.Modules.Games
|
||||
finally
|
||||
{
|
||||
_client.MessageReceived -= _client_MessageReceived;
|
||||
AcrophobiaGames.TryRemove(channel.Id, out game);
|
||||
_service.AcrophobiaGames.TryRemove(channel.Id, out game);
|
||||
game.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services.Impl;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Games.Common
|
||||
{
|
||||
public class TicTacToe
|
||||
{
|
||||
enum Phase
|
||||
{
|
||||
Starting,
|
||||
Started,
|
||||
Ended
|
||||
}
|
||||
|
||||
private readonly ITextChannel _channel;
|
||||
private readonly IGuildUser[] _users;
|
||||
private readonly int?[,] _state;
|
||||
private Phase _phase;
|
||||
private int _curUserIndex;
|
||||
private readonly SemaphoreSlim _moveLock;
|
||||
|
||||
private IGuildUser _winner;
|
||||
|
||||
private readonly string[] _numbers = { ":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:" };
|
||||
|
||||
public Action<TicTacToe> OnEnded;
|
||||
|
||||
private IUserMessage _previousMessage;
|
||||
private Timer _timeoutTimer;
|
||||
private readonly NadekoStrings _strings;
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
public TicTacToe(NadekoStrings strings, DiscordSocketClient client, ITextChannel channel, IGuildUser firstUser)
|
||||
{
|
||||
_channel = channel;
|
||||
_strings = strings;
|
||||
_client = client;
|
||||
|
||||
_users = new[] { firstUser, null };
|
||||
_state = new int?[,] {
|
||||
{ null, null, null },
|
||||
{ null, null, null },
|
||||
{ null, null, null },
|
||||
};
|
||||
|
||||
_phase = Phase.Starting;
|
||||
_moveLock = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
|
||||
private string GetText(string key, params object[] replacements) =>
|
||||
_strings.GetText(key,
|
||||
_channel.GuildId,
|
||||
typeof(Games).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
|
||||
public string GetState()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < _state.GetLength(0); i++)
|
||||
{
|
||||
for (var j = 0; j < _state.GetLength(1); j++)
|
||||
{
|
||||
sb.Append(_state[i, j] == null ? _numbers[i * 3 + j] : GetIcon(_state[i, j]));
|
||||
if (j < _state.GetLength(1) - 1)
|
||||
sb.Append("┃");
|
||||
}
|
||||
if (i < _state.GetLength(0) - 1)
|
||||
sb.AppendLine("\n──────────");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public EmbedBuilder GetEmbed(string title = null)
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithDescription(Environment.NewLine + GetState())
|
||||
.WithAuthor(eab => eab.WithName(GetText("vs", _users[0], _users[1])));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(title))
|
||||
embed.WithTitle(title);
|
||||
|
||||
if (_winner == null)
|
||||
{
|
||||
if (_phase == Phase.Ended)
|
||||
embed.WithFooter(efb => efb.WithText(GetText("ttt_no_moves")));
|
||||
else
|
||||
embed.WithFooter(efb => efb.WithText(GetText("ttt_users_move", _users[_curUserIndex])));
|
||||
}
|
||||
else
|
||||
embed.WithFooter(efb => efb.WithText(GetText("ttt_has_won", _winner)));
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
private static string GetIcon(int? val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case 0:
|
||||
return "❌";
|
||||
case 1:
|
||||
return "⭕";
|
||||
case 2:
|
||||
return "❎";
|
||||
case 3:
|
||||
return "🅾";
|
||||
default:
|
||||
return "⬛";
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Start(IGuildUser user)
|
||||
{
|
||||
if (_phase == Phase.Started || _phase == Phase.Ended)
|
||||
{
|
||||
await _channel.SendErrorAsync(user.Mention + GetText("ttt_already_running")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else if (_users[0] == user)
|
||||
{
|
||||
await _channel.SendErrorAsync(user.Mention + GetText("ttt_against_yourself")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
_users[1] = user;
|
||||
|
||||
_phase = Phase.Started;
|
||||
|
||||
_timeoutTimer = new Timer(async (_) =>
|
||||
{
|
||||
await _moveLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (_phase == Phase.Ended)
|
||||
return;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
if (_users[1] != null)
|
||||
{
|
||||
_winner = _users[_curUserIndex ^= 1];
|
||||
var del = _previousMessage?.DeleteAsync();
|
||||
try
|
||||
{
|
||||
await _channel.EmbedAsync(GetEmbed(GetText("ttt_time_expired"))).ConfigureAwait(false);
|
||||
if (del != null)
|
||||
await del.ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
OnEnded?.Invoke(this);
|
||||
}
|
||||
catch { }
|
||||
finally
|
||||
{
|
||||
_moveLock.Release();
|
||||
}
|
||||
}, null, 15000, Timeout.Infinite);
|
||||
|
||||
_client.MessageReceived += Client_MessageReceived;
|
||||
|
||||
|
||||
_previousMessage = await _channel.EmbedAsync(GetEmbed(GetText("game_started"))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private bool IsDraw()
|
||||
{
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
for (var j = 0; j < 3; j++)
|
||||
{
|
||||
if (_state[i, j] == null)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Task Client_MessageReceived(SocketMessage msg)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
await _moveLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var curUser = _users[_curUserIndex];
|
||||
if (_phase == Phase.Ended || msg.Author?.Id != curUser.Id)
|
||||
return;
|
||||
|
||||
if (int.TryParse(msg.Content, out var index) &&
|
||||
--index >= 0 &&
|
||||
index <= 9 &&
|
||||
_state[index / 3, index % 3] == null)
|
||||
{
|
||||
_state[index / 3, index % 3] = _curUserIndex;
|
||||
|
||||
// i'm lazy
|
||||
if (_state[index / 3, 0] == _state[index / 3, 1] && _state[index / 3, 1] == _state[index / 3, 2])
|
||||
{
|
||||
_state[index / 3, 0] = _curUserIndex + 2;
|
||||
_state[index / 3, 1] = _curUserIndex + 2;
|
||||
_state[index / 3, 2] = _curUserIndex + 2;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
}
|
||||
else if (_state[0, index % 3] == _state[1, index % 3] && _state[1, index % 3] == _state[2, index % 3])
|
||||
{
|
||||
_state[0, index % 3] = _curUserIndex + 2;
|
||||
_state[1, index % 3] = _curUserIndex + 2;
|
||||
_state[2, index % 3] = _curUserIndex + 2;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
}
|
||||
else if (_curUserIndex == _state[0, 0] && _state[0, 0] == _state[1, 1] && _state[1, 1] == _state[2, 2])
|
||||
{
|
||||
_state[0, 0] = _curUserIndex + 2;
|
||||
_state[1, 1] = _curUserIndex + 2;
|
||||
_state[2, 2] = _curUserIndex + 2;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
}
|
||||
else if (_curUserIndex == _state[0, 2] && _state[0, 2] == _state[1, 1] && _state[1, 1] == _state[2, 0])
|
||||
{
|
||||
_state[0, 2] = _curUserIndex + 2;
|
||||
_state[1, 1] = _curUserIndex + 2;
|
||||
_state[2, 0] = _curUserIndex + 2;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
}
|
||||
var reason = "";
|
||||
|
||||
if (_phase == Phase.Ended) // if user won, stop receiving moves
|
||||
{
|
||||
reason = GetText("ttt_matched_three");
|
||||
_winner = _users[_curUserIndex];
|
||||
_client.MessageReceived -= Client_MessageReceived;
|
||||
OnEnded?.Invoke(this);
|
||||
}
|
||||
else if (IsDraw())
|
||||
{
|
||||
reason = GetText("ttt_a_draw");
|
||||
_phase = Phase.Ended;
|
||||
_client.MessageReceived -= Client_MessageReceived;
|
||||
OnEnded?.Invoke(this);
|
||||
}
|
||||
|
||||
var sendstate = Task.Run(async () =>
|
||||
{
|
||||
var del1 = msg.DeleteAsync();
|
||||
var del2 = _previousMessage?.DeleteAsync();
|
||||
try { _previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { }
|
||||
try { await del1; } catch { }
|
||||
try { if (del2 != null) await del2; } catch { }
|
||||
});
|
||||
_curUserIndex ^= 1;
|
||||
|
||||
_timeoutTimer.Change(15000, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_moveLock.Release();
|
||||
}
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using Discord;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Games.Common.Connect4;
|
||||
using System.Collections.Concurrent;
|
||||
using NadekoBot.Modules.Games.Services;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -13,12 +13,11 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
public partial class Games
|
||||
{
|
||||
public class Connect4Commands : NadekoSubmodule
|
||||
[Group]
|
||||
public class Connect4Commands : NadekoSubmodule<GamesService>
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, Connect4Game> Games = new ConcurrentDictionary<ulong, Connect4Game>();
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
//private readonly string[] numbers = new string[] { "⓪", " ①", "②", "③", "④", "⑤", "⑥", "⑦", "⑧", "⑨" };
|
||||
|
||||
private readonly string[] numbers = new string[] { ":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:"};
|
||||
|
||||
public Connect4Commands(DiscordSocketClient client)
|
||||
@@ -32,7 +31,7 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
var newGame = new Connect4Game(Context.User.Id, Context.User.ToString());
|
||||
Connect4Game game;
|
||||
if ((game = Games.GetOrAdd(Context.Channel.Id, newGame)) != newGame)
|
||||
if ((game = _service.Connect4Games.GetOrAdd(Context.Channel.Id, newGame)) != newGame)
|
||||
{
|
||||
if (game.CurrentPhase != Connect4Game.Phase.Joining)
|
||||
return;
|
||||
@@ -84,7 +83,7 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
Task Game_OnGameFailedToStart(Connect4Game arg)
|
||||
{
|
||||
if (Games.TryRemove(Context.Channel.Id, out var toDispose))
|
||||
if (_service.Connect4Games.TryRemove(Context.Channel.Id, out var toDispose))
|
||||
{
|
||||
_client.MessageReceived -= _client_MessageReceived;
|
||||
toDispose.Dispose();
|
||||
@@ -94,7 +93,7 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
Task Game_OnGameEnded(Connect4Game arg, Connect4Game.Result result)
|
||||
{
|
||||
if (Games.TryRemove(Context.Channel.Id, out var toDispose))
|
||||
if (_service.Connect4Games.TryRemove(Context.Channel.Id, out var toDispose))
|
||||
{
|
||||
_client.MessageReceived -= _client_MessageReceived;
|
||||
toDispose.Dispose();
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Modules.Games.Common.Hangman;
|
||||
using NadekoBot.Modules.Games.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
public partial class Games
|
||||
{
|
||||
[Group]
|
||||
public class HangmanCommands : NadekoSubmodule
|
||||
public class HangmanCommands : NadekoSubmodule<GamesService>
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
@@ -22,9 +22,6 @@ namespace NadekoBot.Modules.Games
|
||||
_client = client;
|
||||
}
|
||||
|
||||
//channelId, game
|
||||
public static ConcurrentDictionary<ulong, Hangman> HangmanGames { get; } = new ConcurrentDictionary<ulong, Hangman>();
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Hangmanlist()
|
||||
@@ -38,7 +35,7 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
var hm = new Hangman(type);
|
||||
|
||||
if (!HangmanGames.TryAdd(Context.Channel.Id, hm))
|
||||
if (!_service.HangmanGames.TryAdd(Context.Channel.Id, hm))
|
||||
{
|
||||
hm.Dispose();
|
||||
await ReplyErrorLocalized("hangman_running").ConfigureAwait(false);
|
||||
@@ -61,7 +58,7 @@ namespace NadekoBot.Modules.Games
|
||||
await hm.EndedTask.ConfigureAwait(false);
|
||||
|
||||
_client.MessageReceived -= _client_MessageReceived;
|
||||
HangmanGames.TryRemove(Context.Channel.Id, out _);
|
||||
_service.HangmanGames.TryRemove(Context.Channel.Id, out _);
|
||||
hm.Dispose();
|
||||
|
||||
Task _client_MessageReceived(SocketMessage msg)
|
||||
@@ -127,7 +124,7 @@ namespace NadekoBot.Modules.Games
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task HangmanStop()
|
||||
{
|
||||
if (HangmanGames.TryRemove(Context.Channel.Id, out var removed))
|
||||
if (_service.HangmanGames.TryRemove(Context.Channel.Id, out var removed))
|
||||
{
|
||||
await removed.Stop().ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("hangman_stopped").ConfigureAwait(false);
|
||||
|
||||
@@ -2,8 +2,15 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\src\NadekoBot\bin\$(Configuration)\netcoreapp2.0\modules\$(AssemblyName)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Discord.Net" Version="2.0.0-alpha-build-00832" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -3,8 +3,8 @@ using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Modules.Games.Common.Nunchi;
|
||||
using NadekoBot.Modules.Games.Services;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
public partial class Games
|
||||
{
|
||||
public class NunchiCommands : NadekoSubmodule
|
||||
[Group]
|
||||
public class NunchiCommands : NadekoSubmodule<GamesService>
|
||||
{
|
||||
public static readonly ConcurrentDictionary<ulong, Nunchi> Games = new ConcurrentDictionary<ulong, Common.Nunchi.Nunchi>();
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
public NunchiCommands(DiscordSocketClient client)
|
||||
@@ -30,7 +30,7 @@ namespace NadekoBot.Modules.Games
|
||||
Nunchi nunchi;
|
||||
|
||||
//if a game was already active
|
||||
if ((nunchi = Games.GetOrAdd(Context.Guild.Id, newNunchi)) != newNunchi)
|
||||
if ((nunchi = _service.NunchiGames.GetOrAdd(Context.Guild.Id, newNunchi)) != newNunchi)
|
||||
{
|
||||
// join it
|
||||
if (!await nunchi.Join(Context.User.Id, Context.User.ToString()))
|
||||
@@ -57,7 +57,7 @@ namespace NadekoBot.Modules.Games
|
||||
var success = await nunchi.Initialize().ConfigureAwait(false);
|
||||
if (!success)
|
||||
{
|
||||
if (Games.TryRemove(Context.Guild.Id, out var game))
|
||||
if (_service.NunchiGames.TryRemove(Context.Guild.Id, out var game))
|
||||
game.Dispose();
|
||||
await ConfirmLocalized("nunchi_failed_to_start").ConfigureAwait(false);
|
||||
}
|
||||
@@ -85,7 +85,7 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
Task Nunchi_OnGameEnded(Nunchi arg1, string arg2)
|
||||
{
|
||||
if (Games.TryRemove(Context.Guild.Id, out var game))
|
||||
if (_service.NunchiGames.TryRemove(Context.Guild.Id, out var game))
|
||||
{
|
||||
_client.MessageReceived -= _client_MessageReceived;
|
||||
game.Dispose();
|
||||
|
||||
@@ -22,19 +22,17 @@ namespace NadekoBot.Modules.Games
|
||||
/// https://discord.gg/0TYNJfCU4De7YIk8
|
||||
/// </summary>
|
||||
[Group]
|
||||
public class PlantPickCommands : NadekoSubmodule
|
||||
public class PlantPickCommands : NadekoSubmodule<GamesService>
|
||||
{
|
||||
private readonly CurrencyService _cs;
|
||||
private readonly IBotConfigProvider _bc;
|
||||
private readonly GamesService _games;
|
||||
private readonly DbService _db;
|
||||
|
||||
public PlantPickCommands(IBotConfigProvider bc, CurrencyService cs, GamesService games,
|
||||
public PlantPickCommands(IBotConfigProvider bc, CurrencyService cs,
|
||||
DbService db)
|
||||
{
|
||||
_bc = bc;
|
||||
_cs = cs;
|
||||
_games = games;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
@@ -49,7 +47,7 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
|
||||
try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
if (!_games.PlantedFlowers.TryRemove(channel.Id, out List<IUserMessage> msgs))
|
||||
if (!_service.PlantedFlowers.TryRemove(channel.Id, out List<IUserMessage> msgs))
|
||||
return;
|
||||
|
||||
await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
|
||||
@@ -74,7 +72,7 @@ namespace NadekoBot.Modules.Games
|
||||
return;
|
||||
}
|
||||
|
||||
var imgData = _games.GetRandomCurrencyImage();
|
||||
var imgData = _service.GetRandomCurrencyImage();
|
||||
|
||||
var msgToSend = GetText("planted",
|
||||
Format.Bold(Context.User.ToString()),
|
||||
@@ -95,7 +93,7 @@ namespace NadekoBot.Modules.Games
|
||||
var msgs = new IUserMessage[amount];
|
||||
msgs[0] = msg;
|
||||
|
||||
_games.PlantedFlowers.AddOrUpdate(Context.Channel.Id, msgs.ToList(), (id, old) =>
|
||||
_service.PlantedFlowers.AddOrUpdate(Context.Channel.Id, msgs.ToList(), (id, old) =>
|
||||
{
|
||||
old.AddRange(msgs);
|
||||
return old;
|
||||
@@ -121,13 +119,13 @@ namespace NadekoBot.Modules.Games
|
||||
if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd))
|
||||
{
|
||||
guildConfig.GenerateCurrencyChannelIds.Add(toAdd);
|
||||
_games.GenerationChannels.Add(channel.Id);
|
||||
_service.GenerationChannels.Add(channel.Id);
|
||||
enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
guildConfig.GenerateCurrencyChannelIds.Remove(toAdd);
|
||||
_games.GenerationChannels.TryRemove(channel.Id);
|
||||
_service.GenerationChannels.TryRemove(channel.Id);
|
||||
enabled = false;
|
||||
}
|
||||
await uow.CompleteAsync();
|
||||
|
||||
@@ -17,10 +17,15 @@ using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Services.Impl;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NadekoBot.Modules.Games.Common.Acrophobia;
|
||||
using NadekoBot.Modules.Games.Common.Connect4;
|
||||
using NadekoBot.Modules.Games.Common.Hangman;
|
||||
using NadekoBot.Modules.Games.Common.Trivia;
|
||||
using NadekoBot.Modules.Games.Common.Nunchi;
|
||||
|
||||
namespace NadekoBot.Modules.Games.Services
|
||||
{
|
||||
public class GamesService : INService
|
||||
public class GamesService : INService, IUnloadableService
|
||||
{
|
||||
private readonly IBotConfigProvider _bc;
|
||||
|
||||
@@ -38,7 +43,16 @@ namespace NadekoBot.Modules.Games.Services
|
||||
|
||||
public List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
|
||||
|
||||
public GamesService(CommandHandler cmd, IBotConfigProvider bc, IEnumerable<GuildConfig> gcs,
|
||||
//channelId, game
|
||||
public ConcurrentDictionary<ulong, Acrophobia> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, Acrophobia>();
|
||||
public ConcurrentDictionary<ulong, Connect4Game> Connect4Games { get; } = new ConcurrentDictionary<ulong, Connect4Game>();
|
||||
public ConcurrentDictionary<ulong, Hangman> HangmanGames { get; } = new ConcurrentDictionary<ulong, Hangman>();
|
||||
public ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>();
|
||||
public Dictionary<ulong, TicTacToe> TicTacToeGames { get; } = new Dictionary<ulong, TicTacToe>();
|
||||
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,
|
||||
NadekoStrings strings, IImagesService images, CommandHandler cmdHandler)
|
||||
{
|
||||
_bc = bc;
|
||||
@@ -74,6 +88,34 @@ namespace NadekoBot.Modules.Games.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Unload()
|
||||
{
|
||||
_t.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
_cmd.OnMessageNoTrigger -= PotentialFlowerGeneration;
|
||||
|
||||
AcrophobiaGames.ForEach(x => x.Value.Dispose());
|
||||
AcrophobiaGames.Clear();
|
||||
Connect4Games.ForEach(x => x.Value.Dispose());
|
||||
Connect4Games.Clear();
|
||||
HangmanGames.ForEach(x => x.Value.Dispose());
|
||||
HangmanGames.Clear();
|
||||
await Task.WhenAll(RunningTrivias.Select(x => x.Value.StopGame()));
|
||||
RunningTrivias.Clear();
|
||||
|
||||
TicTacToeGames.Clear();
|
||||
|
||||
await Task.WhenAll(RunningContests.Select(x => x.Value.Stop()))
|
||||
.ConfigureAwait(false);
|
||||
RunningContests.Clear();
|
||||
NunchiGames.ForEach(x => x.Value.Dispose());
|
||||
NunchiGames.Clear();
|
||||
}
|
||||
|
||||
private void DisposeElems(IEnumerable<IDisposable> xs)
|
||||
{
|
||||
xs.ForEach(x => x.Dispose());
|
||||
}
|
||||
|
||||
public void AddTypingArticle(IUser user, string text)
|
||||
{
|
||||
TypingArticles.Add(new TypingArticle
|
||||
|
||||
@@ -3,7 +3,6 @@ using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -16,9 +15,8 @@ namespace NadekoBot.Modules.Games
|
||||
public partial class Games
|
||||
{
|
||||
[Group]
|
||||
public class SpeedTypingCommands : NadekoSubmodule
|
||||
public class SpeedTypingCommands : NadekoSubmodule<GamesService>
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, TypingGame> RunningContests = new ConcurrentDictionary<ulong, TypingGame>();
|
||||
private readonly GamesService _games;
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
@@ -34,7 +32,7 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
var game = RunningContests.GetOrAdd(channel.Guild.Id, id => new TypingGame(_games, _client, channel, Prefix));
|
||||
var game = _service.RunningContests.GetOrAdd(channel.Guild.Id, id => new TypingGame(_games, _client, channel, Prefix));
|
||||
|
||||
if (game.IsActive)
|
||||
{
|
||||
@@ -54,8 +52,7 @@ namespace NadekoBot.Modules.Games
|
||||
public async Task TypeStop()
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
TypingGame game;
|
||||
if (RunningContests.TryRemove(channel.Guild.Id, out game))
|
||||
if (_service.RunningContests.TryRemove(channel.Guild.Id, out TypingGame game))
|
||||
{
|
||||
await game.Stop().ConfigureAwait(false);
|
||||
return;
|
||||
|
||||
@@ -3,23 +3,21 @@ using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Services.Impl;
|
||||
using NadekoBot.Modules.Games.Services;
|
||||
using NadekoBot.Modules.Games.Common;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
public partial class Games
|
||||
{
|
||||
[Group]
|
||||
public class TicTacToeCommands : NadekoSubmodule
|
||||
public class TicTacToeCommands : NadekoSubmodule<GamesService>
|
||||
{
|
||||
//channelId/game
|
||||
private static readonly Dictionary<ulong, TicTacToe> _games = new Dictionary<ulong, TicTacToe>();
|
||||
|
||||
private readonly SemaphoreSlim _sem = new SemaphoreSlim(1, 1);
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
@@ -37,7 +35,7 @@ namespace NadekoBot.Modules.Games
|
||||
await _sem.WaitAsync(1000);
|
||||
try
|
||||
{
|
||||
if (_games.TryGetValue(channel.Id, out TicTacToe game))
|
||||
if (_service.TicTacToeGames.TryGetValue(channel.Id, out TicTacToe game))
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
@@ -45,13 +43,13 @@ namespace NadekoBot.Modules.Games
|
||||
});
|
||||
return;
|
||||
}
|
||||
game = new TicTacToe(base._strings, (DiscordSocketClient)this._client, channel, (IGuildUser)Context.User);
|
||||
_games.Add(channel.Id, game);
|
||||
game = new TicTacToe(base._strings, this._client, channel, (IGuildUser)Context.User);
|
||||
_service.TicTacToeGames.Add(channel.Id, game);
|
||||
await ReplyConfirmLocalized("ttt_created").ConfigureAwait(false);
|
||||
|
||||
game.OnEnded += (g) =>
|
||||
{
|
||||
_games.Remove(channel.Id);
|
||||
_service.TicTacToeGames.Remove(channel.Id);
|
||||
};
|
||||
}
|
||||
finally
|
||||
@@ -60,271 +58,5 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TicTacToe
|
||||
{
|
||||
enum Phase
|
||||
{
|
||||
Starting,
|
||||
Started,
|
||||
Ended
|
||||
}
|
||||
|
||||
private readonly ITextChannel _channel;
|
||||
private readonly IGuildUser[] _users;
|
||||
private readonly int?[,] _state;
|
||||
private Phase _phase;
|
||||
private int _curUserIndex;
|
||||
private readonly SemaphoreSlim _moveLock;
|
||||
|
||||
private IGuildUser _winner;
|
||||
|
||||
private readonly string[] _numbers = { ":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:" };
|
||||
|
||||
public Action<TicTacToe> OnEnded;
|
||||
|
||||
private IUserMessage _previousMessage;
|
||||
private Timer _timeoutTimer;
|
||||
private readonly NadekoStrings _strings;
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
public TicTacToe(NadekoStrings strings, DiscordSocketClient client, ITextChannel channel, IGuildUser firstUser)
|
||||
{
|
||||
_channel = channel;
|
||||
_strings = strings;
|
||||
_client = client;
|
||||
|
||||
_users = new[] { firstUser, null };
|
||||
_state = new int?[,] {
|
||||
{ null, null, null },
|
||||
{ null, null, null },
|
||||
{ null, null, null },
|
||||
};
|
||||
|
||||
_phase = Phase.Starting;
|
||||
_moveLock = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
|
||||
private string GetText(string key, params object[] replacements) =>
|
||||
_strings.GetText(key,
|
||||
_channel.GuildId,
|
||||
typeof(Games).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
|
||||
public string GetState()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < _state.GetLength(0); i++)
|
||||
{
|
||||
for (var j = 0; j < _state.GetLength(1); j++)
|
||||
{
|
||||
sb.Append(_state[i, j] == null ? _numbers[i * 3 + j] : GetIcon(_state[i, j]));
|
||||
if (j < _state.GetLength(1) - 1)
|
||||
sb.Append("┃");
|
||||
}
|
||||
if (i < _state.GetLength(0) - 1)
|
||||
sb.AppendLine("\n──────────");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public EmbedBuilder GetEmbed(string title = null)
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithDescription(Environment.NewLine + GetState())
|
||||
.WithAuthor(eab => eab.WithName(GetText("vs", _users[0], _users[1])));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(title))
|
||||
embed.WithTitle(title);
|
||||
|
||||
if (_winner == null)
|
||||
{
|
||||
if (_phase == Phase.Ended)
|
||||
embed.WithFooter(efb => efb.WithText(GetText("ttt_no_moves")));
|
||||
else
|
||||
embed.WithFooter(efb => efb.WithText(GetText("ttt_users_move", _users[_curUserIndex])));
|
||||
}
|
||||
else
|
||||
embed.WithFooter(efb => efb.WithText(GetText("ttt_has_won", _winner)));
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
private static string GetIcon(int? val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case 0:
|
||||
return "❌";
|
||||
case 1:
|
||||
return "⭕";
|
||||
case 2:
|
||||
return "❎";
|
||||
case 3:
|
||||
return "🅾";
|
||||
default:
|
||||
return "⬛";
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Start(IGuildUser user)
|
||||
{
|
||||
if (_phase == Phase.Started || _phase == Phase.Ended)
|
||||
{
|
||||
await _channel.SendErrorAsync(user.Mention + GetText("ttt_already_running")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else if (_users[0] == user)
|
||||
{
|
||||
await _channel.SendErrorAsync(user.Mention + GetText("ttt_against_yourself")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
_users[1] = user;
|
||||
|
||||
_phase = Phase.Started;
|
||||
|
||||
_timeoutTimer = new Timer(async (_) =>
|
||||
{
|
||||
await _moveLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (_phase == Phase.Ended)
|
||||
return;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
if (_users[1] != null)
|
||||
{
|
||||
_winner = _users[_curUserIndex ^= 1];
|
||||
var del = _previousMessage?.DeleteAsync();
|
||||
try
|
||||
{
|
||||
await _channel.EmbedAsync(GetEmbed(GetText("ttt_time_expired"))).ConfigureAwait(false);
|
||||
if (del != null)
|
||||
await del.ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
OnEnded?.Invoke(this);
|
||||
}
|
||||
catch { }
|
||||
finally
|
||||
{
|
||||
_moveLock.Release();
|
||||
}
|
||||
}, null, 15000, Timeout.Infinite);
|
||||
|
||||
_client.MessageReceived += Client_MessageReceived;
|
||||
|
||||
|
||||
_previousMessage = await _channel.EmbedAsync(GetEmbed(GetText("game_started"))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private bool IsDraw()
|
||||
{
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
for (var j = 0; j < 3; j++)
|
||||
{
|
||||
if (_state[i, j] == null)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Task Client_MessageReceived(SocketMessage msg)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
await _moveLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var curUser = _users[_curUserIndex];
|
||||
if (_phase == Phase.Ended || msg.Author?.Id != curUser.Id)
|
||||
return;
|
||||
|
||||
if (int.TryParse(msg.Content, out var index) &&
|
||||
--index >= 0 &&
|
||||
index <= 9 &&
|
||||
_state[index / 3, index % 3] == null)
|
||||
{
|
||||
_state[index / 3, index % 3] = _curUserIndex;
|
||||
|
||||
// i'm lazy
|
||||
if (_state[index / 3, 0] == _state[index / 3, 1] && _state[index / 3, 1] == _state[index / 3, 2])
|
||||
{
|
||||
_state[index / 3, 0] = _curUserIndex + 2;
|
||||
_state[index / 3, 1] = _curUserIndex + 2;
|
||||
_state[index / 3, 2] = _curUserIndex + 2;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
}
|
||||
else if (_state[0, index % 3] == _state[1, index % 3] && _state[1, index % 3] == _state[2, index % 3])
|
||||
{
|
||||
_state[0, index % 3] = _curUserIndex + 2;
|
||||
_state[1, index % 3] = _curUserIndex + 2;
|
||||
_state[2, index % 3] = _curUserIndex + 2;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
}
|
||||
else if (_curUserIndex == _state[0, 0] && _state[0, 0] == _state[1, 1] && _state[1, 1] == _state[2, 2])
|
||||
{
|
||||
_state[0, 0] = _curUserIndex + 2;
|
||||
_state[1, 1] = _curUserIndex + 2;
|
||||
_state[2, 2] = _curUserIndex + 2;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
}
|
||||
else if (_curUserIndex == _state[0, 2] && _state[0, 2] == _state[1, 1] && _state[1, 1] == _state[2, 0])
|
||||
{
|
||||
_state[0, 2] = _curUserIndex + 2;
|
||||
_state[1, 1] = _curUserIndex + 2;
|
||||
_state[2, 0] = _curUserIndex + 2;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
}
|
||||
var reason = "";
|
||||
|
||||
if (_phase == Phase.Ended) // if user won, stop receiving moves
|
||||
{
|
||||
reason = GetText("ttt_matched_three");
|
||||
_winner = _users[_curUserIndex];
|
||||
_client.MessageReceived -= Client_MessageReceived;
|
||||
OnEnded?.Invoke(this);
|
||||
}
|
||||
else if (IsDraw())
|
||||
{
|
||||
reason = GetText("ttt_a_draw");
|
||||
_phase = Phase.Ended;
|
||||
_client.MessageReceived -= Client_MessageReceived;
|
||||
OnEnded?.Invoke(this);
|
||||
}
|
||||
|
||||
var sendstate = Task.Run(async () =>
|
||||
{
|
||||
var del1 = msg.DeleteAsync();
|
||||
var del2 = _previousMessage?.DeleteAsync();
|
||||
try { _previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { }
|
||||
try { await del1; } catch { }
|
||||
try { if (del2 != null) await del2; } catch { }
|
||||
});
|
||||
_curUserIndex ^= 1;
|
||||
|
||||
_timeoutTimer.Change(15000, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_moveLock.Release();
|
||||
}
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,24 +3,23 @@ using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Modules.Games.Common.Trivia;
|
||||
using NadekoBot.Modules.Games.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
public partial class Games
|
||||
{
|
||||
//todo move games to service, unload
|
||||
[Group]
|
||||
public class TriviaCommands : NadekoSubmodule
|
||||
public class TriviaCommands : NadekoSubmodule<GamesService>
|
||||
{
|
||||
private readonly CurrencyService _cs;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IBotConfigProvider _bc;
|
||||
|
||||
public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>();
|
||||
|
||||
public TriviaCommands(DiscordSocketClient client, IBotConfigProvider bc, CurrencyService cs)
|
||||
{
|
||||
_cs = cs;
|
||||
@@ -48,7 +47,7 @@ namespace NadekoBot.Modules.Games
|
||||
var isPokemon = additionalArgs.Contains("pokemon");
|
||||
|
||||
var trivia = new TriviaGame(_strings, _client, _bc, _cs, channel.Guild, channel, showHints, winReq, isPokemon);
|
||||
if (RunningTrivias.TryAdd(channel.Guild.Id, trivia))
|
||||
if (_service.RunningTrivias.TryAdd(channel.Guild.Id, trivia))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -56,7 +55,7 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
finally
|
||||
{
|
||||
RunningTrivias.TryRemove(channel.Guild.Id, out trivia);
|
||||
_service.RunningTrivias.TryRemove(channel.Guild.Id, out trivia);
|
||||
await trivia.EnsureStopped().ConfigureAwait(false);
|
||||
}
|
||||
return;
|
||||
@@ -72,8 +71,7 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
TriviaGame trivia;
|
||||
if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
|
||||
if (_service.RunningTrivias.TryGetValue(channel.Guild.Id, out TriviaGame trivia))
|
||||
{
|
||||
await channel.SendConfirmAsync(GetText("leaderboard"), trivia.GetLeaderboard()).ConfigureAwait(false);
|
||||
return;
|
||||
@@ -88,8 +86,7 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
TriviaGame trivia;
|
||||
if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
|
||||
if (_service.RunningTrivias.TryGetValue(channel.Guild.Id, out TriviaGame trivia))
|
||||
{
|
||||
await trivia.StopGame().ConfigureAwait(false);
|
||||
return;
|
||||
|
||||
@@ -30,22 +30,15 @@ namespace NadekoBot.Modules.Music
|
||||
private readonly IGoogleApiService _google;
|
||||
private readonly DbService _db;
|
||||
|
||||
public Music(DiscordSocketClient client, IBotCredentials creds, IGoogleApiService google,
|
||||
public Music(DiscordSocketClient client,
|
||||
IBotCredentials creds,
|
||||
IGoogleApiService google,
|
||||
DbService db)
|
||||
{
|
||||
_client = client;
|
||||
_creds = creds;
|
||||
_google = google;
|
||||
_db = db;
|
||||
|
||||
//_client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
|
||||
_client.LeftGuild += _client_LeftGuild;
|
||||
}
|
||||
|
||||
private Task _client_LeftGuild(SocketGuild arg)
|
||||
{
|
||||
var t = _service.DestroyPlayer(arg.Id);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
//todo changing server region is bugged again
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\src\NadekoBot\bin\$(Configuration)\netcoreapp2.0\modules\$(AssemblyName)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -18,7 +18,7 @@ using NadekoBot.Modules.Music.Common.SongResolver;
|
||||
|
||||
namespace NadekoBot.Modules.Music.Services
|
||||
{
|
||||
public class MusicService : INService
|
||||
public class MusicService : INService, IUnloadableService
|
||||
{
|
||||
public const string MusicDataPath = "data/musicdata";
|
||||
|
||||
@@ -47,13 +47,25 @@ namespace NadekoBot.Modules.Music.Services
|
||||
_creds = creds;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
_client.LeftGuild += _client_LeftGuild;
|
||||
|
||||
try { Directory.Delete(MusicDataPath, true); } catch { }
|
||||
|
||||
_defaultVolumes = new ConcurrentDictionary<ulong, float>(gcs.ToDictionary(x => x.GuildId, x => x.DefaultMusicVolume));
|
||||
|
||||
Directory.CreateDirectory(MusicDataPath);
|
||||
}
|
||||
|
||||
//_t = new Timer(_ => _log.Info(MusicPlayers.Count(x => x.Value.Current.Current != null)), null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
|
||||
public Task Unload()
|
||||
{
|
||||
_client.LeftGuild -= _client_LeftGuild;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task _client_LeftGuild(SocketGuild arg)
|
||||
{
|
||||
var t = DestroyPlayer(arg.Id);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public float GetDefaultVolume(ulong guildId)
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace NadekoBot.Modules.NSFW
|
||||
// thanks to halitalf for adding autoboob and autobutt features :D
|
||||
public class NSFW : NadekoTopLevelModule<SearchesService>
|
||||
{
|
||||
//todo clear when module unloaded
|
||||
private static readonly ConcurrentDictionary<ulong, Timer> _autoHentaiTimers = new ConcurrentDictionary<ulong, Timer>();
|
||||
private static readonly ConcurrentDictionary<ulong, Timer> _autoBoobTimers = new ConcurrentDictionary<ulong, Timer>();
|
||||
private static readonly ConcurrentDictionary<ulong, Timer> _autoButtTimers = new ConcurrentDictionary<ulong, Timer>();
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\src\NadekoBot\bin\$(Configuration)\netcoreapp2.0\modules\$(AssemblyName)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\src\NadekoBot\bin\$(Configuration)\netcoreapp2.0\modules\$(AssemblyName)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\src\NadekoBot\bin\$(Configuration)\netcoreapp2.0\modules\$(AssemblyName)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -13,7 +13,7 @@ using NLog;
|
||||
|
||||
namespace NadekoBot.Modules.Utility.Services
|
||||
{
|
||||
public class ConverterService : INService
|
||||
public class ConverterService : INService, IUnloadableService
|
||||
{
|
||||
public List<ConvertUnit> Units { get; } = new List<ConvertUnit>();
|
||||
private readonly Logger _log;
|
||||
@@ -122,6 +122,12 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
_log.Warn("Failed updating currency. Ignore this.");
|
||||
}
|
||||
}
|
||||
|
||||
public Task Unload()
|
||||
{
|
||||
_currencyUpdater.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
public class MeasurementUnit
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
.Where(x => x.Guild != null)));
|
||||
})
|
||||
.Where(x => x.Item2 != null)
|
||||
.ToDictionary(x => x.Item1, x => x.Item2));
|
||||
.ToDictionary(x => x.GuildId, x => x.Item2));
|
||||
RepeaterReady = true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ using NLog;
|
||||
|
||||
namespace NadekoBot.Modules.Utility.Services
|
||||
{
|
||||
public class PatreonRewardsService : INService
|
||||
public class PatreonRewardsService : INService, IUnloadableService
|
||||
{
|
||||
private readonly SemaphoreSlim getPledgesLocker = new SemaphoreSlim(1, 1);
|
||||
|
||||
@@ -33,15 +33,16 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
|
||||
private readonly string cacheFileName = "./patreon-rewards.json";
|
||||
|
||||
public PatreonRewardsService(IBotCredentials creds, DbService db, CurrencyService currency,
|
||||
public PatreonRewardsService(IBotCredentials creds, DbService db,
|
||||
CurrencyService currency,
|
||||
DiscordSocketClient client)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_creds = creds;
|
||||
_db = db;
|
||||
_currency = currency;
|
||||
if (string.IsNullOrWhiteSpace(creds.PatreonAccessToken))
|
||||
return;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
Updater = new Timer(async (load) => await RefreshPledges((bool)load),
|
||||
client.ShardId == 0, client.ShardId == 0 ? TimeSpan.Zero : TimeSpan.FromMinutes(2), Interval);
|
||||
}
|
||||
@@ -171,5 +172,11 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
claimLockJustInCase.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public Task Unload()
|
||||
{
|
||||
Updater.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,10 @@ using Discord.WebSocket;
|
||||
using NadekoBot.Common.Replacements;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Services.Impl;
|
||||
using NLog;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NadekoBot.Modules.Utility.Services
|
||||
{
|
||||
@@ -29,8 +29,10 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly DbService _db;
|
||||
|
||||
public RemindService(DiscordSocketClient client, IBotConfigProvider config, DbService db,
|
||||
StartingGuildsService guilds, IUnitOfWork uow)
|
||||
public RemindService(DiscordSocketClient client,
|
||||
IBotConfigProvider config,
|
||||
DbService db,
|
||||
StartingGuildsService guilds)
|
||||
{
|
||||
_config = config;
|
||||
_client = client;
|
||||
@@ -39,8 +41,12 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
|
||||
cancelSource = new CancellationTokenSource();
|
||||
cancelAllToken = cancelSource.Token;
|
||||
|
||||
var reminders = uow.Reminders.GetIncludedReminders(guilds).ToList();
|
||||
|
||||
List<Reminder> reminders;
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
reminders = uow.Reminders.GetIncludedReminders(guilds).ToList();
|
||||
}
|
||||
RemindMessageFormat = _config.BotConfig.RemindMessageFormat;
|
||||
|
||||
foreach (var r in reminders)
|
||||
|
||||
@@ -17,22 +17,24 @@ using Discord.Net;
|
||||
|
||||
namespace NadekoBot.Modules.Utility.Services
|
||||
{
|
||||
public class StreamRoleService : INService
|
||||
public class StreamRoleService : INService, IUnloadableService
|
||||
{
|
||||
private readonly DbService _db;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly ConcurrentDictionary<ulong, StreamRoleSettings> guildSettings;
|
||||
private readonly Logger _log;
|
||||
|
||||
public StreamRoleService(DiscordSocketClient client, DbService db, IEnumerable<GuildConfig> gcs)
|
||||
{
|
||||
this._db = db;
|
||||
this._log = LogManager.GetCurrentClassLogger();
|
||||
this._db = db;
|
||||
this._client = client;
|
||||
|
||||
guildSettings = gcs.ToDictionary(x => x.GuildId, x => x.StreamRole)
|
||||
.Where(x => x.Value != null && x.Value.Enabled)
|
||||
.ToConcurrent();
|
||||
|
||||
client.GuildMemberUpdated += Client_GuildMemberUpdated;
|
||||
_client.GuildMemberUpdated += Client_GuildMemberUpdated;
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
@@ -47,6 +49,12 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
});
|
||||
}
|
||||
|
||||
public Task Unload()
|
||||
{
|
||||
_client.GuildMemberUpdated -= Client_GuildMemberUpdated;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task Client_GuildMemberUpdated(SocketGuildUser before, SocketGuildUser after)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
|
||||
@@ -11,7 +11,7 @@ using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Utility.Services
|
||||
{
|
||||
public class VerboseErrorsService : INService
|
||||
public class VerboseErrorsService : INService, IUnloadableService
|
||||
{
|
||||
private readonly ConcurrentHashSet<ulong> guildsEnabled;
|
||||
private readonly DbService _db;
|
||||
@@ -24,11 +24,17 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
_ch = ch;
|
||||
_hs = hs;
|
||||
|
||||
ch.CommandErrored += LogVerboseError;
|
||||
_ch.CommandErrored += LogVerboseError;
|
||||
|
||||
guildsEnabled = new ConcurrentHashSet<ulong>(gcs.Where(x => x.VerboseErrors).Select(x => x.GuildId));
|
||||
}
|
||||
|
||||
public Task Unload()
|
||||
{
|
||||
_ch.CommandErrored -= LogVerboseError;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task LogVerboseError(CommandInfo cmd, ITextChannel channel, string reason)
|
||||
{
|
||||
if (channel == null || !guildsEnabled.Contains(channel.GuildId))
|
||||
@@ -73,6 +79,5 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -99,8 +99,7 @@ namespace NadekoBot.Modules.Xp
|
||||
return;
|
||||
}
|
||||
|
||||
ClubInfo club;
|
||||
if (!_service.GetClubByName(clubName, out club))
|
||||
if (!_service.GetClubByName(clubName, out ClubInfo club))
|
||||
{
|
||||
await ReplyErrorLocalized("club_not_exists").ConfigureAwait(false);
|
||||
return;
|
||||
|
||||
@@ -2,12 +2,19 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Discord.Net" Version="2.0.0-alpha-build-00832" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\src\NadekoBot\bin\$(Configuration)\netcoreapp2.0\modules\$(AssemblyName)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NadekoBot.Core\NadekoBot.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -4,8 +4,6 @@ using NadekoBot.Services.Database.Models;
|
||||
using Discord;
|
||||
using NadekoBot.Modules.Xp.Common;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NadekoBot.Modules.Xp.Services
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ using ImageSharp.Drawing.Brushes;
|
||||
|
||||
namespace NadekoBot.Modules.Xp.Services
|
||||
{
|
||||
public class XpService : INService
|
||||
public class XpService : INService, IUnloadableService
|
||||
{
|
||||
private enum NotifOf { Server, Global } // is it a server level-up or global level-up notification
|
||||
|
||||
@@ -55,7 +55,9 @@ namespace NadekoBot.Modules.Xp.Services
|
||||
private readonly ConcurrentQueue<UserCacheItem> _addMessageXp
|
||||
= new ConcurrentQueue<UserCacheItem>();
|
||||
|
||||
private readonly Timer updateXpTimer;
|
||||
private readonly Timer _updateXpTimer;
|
||||
private readonly CancellationTokenSource _clearRewardTimerTokenSource;
|
||||
private readonly Task _clearRewardTimer;
|
||||
private readonly HttpClient http = new HttpClient();
|
||||
private FontFamily _usernameFontFamily;
|
||||
private FontFamily _clubFontFamily;
|
||||
@@ -115,7 +117,7 @@ namespace NadekoBot.Modules.Xp.Services
|
||||
|
||||
_cmd.OnMessageNoTrigger += _cmd_OnMessageNoTrigger;
|
||||
|
||||
updateXpTimer = new Timer(async _ =>
|
||||
_updateXpTimer = new Timer(async _ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -241,19 +243,20 @@ namespace NadekoBot.Modules.Xp.Services
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
|
||||
|
||||
|
||||
|
||||
_clearRewardTimerTokenSource = new CancellationTokenSource();
|
||||
var token = _clearRewardTimerTokenSource.Token;
|
||||
//just a first line, in order to prevent queries. But since other shards can try to do this too,
|
||||
//i'll check in the db too.
|
||||
var clearRewardTimer = Task.Run(async () =>
|
||||
_clearRewardTimer = Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
_rewardedUsers.Clear();
|
||||
|
||||
await Task.Delay(TimeSpan.FromMinutes(_bc.BotConfig.XpMinutesTimeout));
|
||||
}
|
||||
});
|
||||
}, token);
|
||||
}
|
||||
|
||||
public IEnumerable<XpRoleReward> GetRoleRewards(ulong id)
|
||||
@@ -751,5 +754,17 @@ namespace NadekoBot.Modules.Xp.Services
|
||||
|
||||
return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight);
|
||||
}
|
||||
|
||||
public Task Unload()
|
||||
{
|
||||
_cmd.OnMessageNoTrigger -= _cmd_OnMessageNoTrigger;
|
||||
|
||||
if (!_clearRewardTimerTokenSource.IsCancellationRequested)
|
||||
_clearRewardTimerTokenSource.Cancel();
|
||||
|
||||
_updateXpTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
_clearRewardTimerTokenSource.Dispose();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Xp.Common;
|
||||
|
||||
+9
-18
@@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.3
|
||||
VisualStudioVersion = 15.0.26730.16
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}"
|
||||
EndProject
|
||||
@@ -13,25 +13,23 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NadekoBot", "src\NadekoBot\NadekoBot.csproj", "{45EC1473-C678-4857-A544-07DFE0D0B478}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NadekoBot.Modules.CustomReactions", "NadekoBot.Modules.CustomReactions\NadekoBot.Modules.CustomReactions.csproj", "{60F1217E-5E01-42DC-9B86-0FE910EDE081}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NadekoBot.Core", "NadekoBot.Core\NadekoBot.Core.csproj", "{A6CCEFBD-DCF2-482C-9643-47664683548F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Modules.Xp", "NadekoBot.Modules.Xp\NadekoBot.Modules.Xp.csproj", "{41A2DEBA-E8AE-4EC8-A58F-01C4B6E599E5}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NadekoBot.Modules.Xp", "NadekoBot.Modules.Xp\NadekoBot.Modules.Xp.csproj", "{41A2DEBA-E8AE-4EC8-A58F-01C4B6E599E5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Modules.Utility", "NadekoBot.Modules.Utility\NadekoBot.Modules.Utility.csproj", "{07606931-CB55-4D20-8369-4E086B00EC52}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NadekoBot.Modules.Utility", "NadekoBot.Modules.Utility\NadekoBot.Modules.Utility.csproj", "{07606931-CB55-4D20-8369-4E086B00EC52}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Modules.Searches", "NadekoBot.Module.Searches\NadekoBot.Modules.Searches.csproj", "{8BEE9984-3EB3-45BE-A5CF-0DB912626B81}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NadekoBot.Modules.Searches", "NadekoBot.Module.Searches\NadekoBot.Modules.Searches.csproj", "{8BEE9984-3EB3-45BE-A5CF-0DB912626B81}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Modules.Nsfw", "NadekoBot.Modules.Nsfw\NadekoBot.Modules.Nsfw.csproj", "{75ED72EC-7AB3-4B12-A2DA-3655C740B356}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NadekoBot.Modules.Nsfw", "NadekoBot.Modules.Nsfw\NadekoBot.Modules.Nsfw.csproj", "{75ED72EC-7AB3-4B12-A2DA-3655C740B356}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Modules.Games", "NadekoBot.Modules.Games\NadekoBot.Modules.Games.csproj", "{FF6BDE61-24B4-4DC2-99EE-409EA1650180}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NadekoBot.Modules.Games", "NadekoBot.Modules.Games\NadekoBot.Modules.Games.csproj", "{FF6BDE61-24B4-4DC2-99EE-409EA1650180}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Modules.Gambling", "NadekoBot.Modules.Gambling\NadekoBot.Modules.Gambling.csproj", "{6436A700-694E-412C-92AE-B793FCD44E84}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NadekoBot.Modules.Gambling", "NadekoBot.Modules.Gambling\NadekoBot.Modules.Gambling.csproj", "{6436A700-694E-412C-92AE-B793FCD44E84}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Modules.Pokemon", "NadekoBot.Modules.Pokemon\NadekoBot.Modules.Pokemon.csproj", "{30463C26-555B-4760-9459-A16DFA015DFA}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NadekoBot.Modules.Pokemon", "NadekoBot.Modules.Pokemon\NadekoBot.Modules.Pokemon.csproj", "{30463C26-555B-4760-9459-A16DFA015DFA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Modules.Music", "NadekoBot.Modules.Music\NadekoBot.Modules.Music.csproj", "{674E28A6-30B1-413D-BBD3-E5F71614A00F}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NadekoBot.Modules.Music", "NadekoBot.Modules.Music\NadekoBot.Modules.Music.csproj", "{674E28A6-30B1-413D-BBD3-E5F71614A00F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -46,12 +44,6 @@ Global
|
||||
{45EC1473-C678-4857-A544-07DFE0D0B478}.GlobalNadeko|Any CPU.Build.0 = Release|Any CPU
|
||||
{45EC1473-C678-4857-A544-07DFE0D0B478}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{45EC1473-C678-4857-A544-07DFE0D0B478}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{60F1217E-5E01-42DC-9B86-0FE910EDE081}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{60F1217E-5E01-42DC-9B86-0FE910EDE081}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{60F1217E-5E01-42DC-9B86-0FE910EDE081}.GlobalNadeko|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{60F1217E-5E01-42DC-9B86-0FE910EDE081}.GlobalNadeko|Any CPU.Build.0 = Debug|Any CPU
|
||||
{60F1217E-5E01-42DC-9B86-0FE910EDE081}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{60F1217E-5E01-42DC-9B86-0FE910EDE081}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A6CCEFBD-DCF2-482C-9643-47664683548F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A6CCEFBD-DCF2-482C-9643-47664683548F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A6CCEFBD-DCF2-482C-9643-47664683548F}.GlobalNadeko|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
@@ -112,7 +104,6 @@ Global
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{45EC1473-C678-4857-A544-07DFE0D0B478} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}
|
||||
{60F1217E-5E01-42DC-9B86-0FE910EDE081} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}
|
||||
{A6CCEFBD-DCF2-482C-9643-47664683548F} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}
|
||||
{41A2DEBA-E8AE-4EC8-A58F-01C4B6E599E5} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}
|
||||
{07606931-CB55-4D20-8369-4E086B00EC52} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}
|
||||
|
||||
@@ -4,16 +4,13 @@
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
|
||||
<OutputType>exe</OutputType>
|
||||
<AssetTargetFallback>$(AssetTargetFallback);dnxcore50;portable-net45+win8+wpa81</AssetTargetFallback>
|
||||
<ApplicationIcon>nadeko_icon.ico</ApplicationIcon>
|
||||
<RuntimeIdentifiers>win7-x64<!--;ubuntu.14.04-x64;osx.10.10-x64 --></RuntimeIdentifiers>
|
||||
<Configurations>Debug;Release;global_nadeko</Configurations>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Version)' == '' ">
|
||||
<VersionPrefix Condition=" '$(VersionPrefix)' == '' ">1.9.1</VersionPrefix>
|
||||
<VersionPrefix Condition=" '$(VersionPrefix)' == '' ">2.0.0</VersionPrefix>
|
||||
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionPrefix).$(VersionSuffix)</Version>
|
||||
<Version Condition=" '$(Version)' == '' ">$(VersionPrefix)</Version>
|
||||
</PropertyGroup>
|
||||
@@ -23,6 +20,12 @@
|
||||
<None Update="credentials_example.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="data\**\*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="_strings\*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="libsodium.dll;opus.dll;libsodium.so;libopus.so">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
@@ -66,6 +69,5 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\NadekoBot.Core\NadekoBot.Core.csproj" />
|
||||
<ProjectReference Include="..\..\NadekoBot.Modules.CustomReactions\NadekoBot.Modules.CustomReactions.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,18 +1,20 @@
|
||||
namespace NadekoBot
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
public static Task Main(string[] args)
|
||||
{
|
||||
if (args.Length == 3 && int.TryParse(args[0], out int shardId) && int.TryParse(args[1], out int parentProcessId))
|
||||
{
|
||||
int? port = null;
|
||||
if (int.TryParse(args[2], out var outPort))
|
||||
port = outPort;
|
||||
new NadekoBot(shardId, parentProcessId, outPort).RunAndBlockAsync(args).GetAwaiter().GetResult();
|
||||
return new NadekoBot(shardId, parentProcessId, outPort).RunAndBlockAsync(args);
|
||||
}
|
||||
else
|
||||
new NadekoBot(0, 0).RunAndBlockAsync(args).GetAwaiter().GetResult();
|
||||
return new NadekoBot(0, 0).RunAndBlockAsync(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Reference in new issue
Block a user