mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28:27 -04:00
Applied codestyle to all .cs files
This commit is contained in:
@@ -339,7 +339,7 @@ resharper_keep_existing_linebreaks = false
|
||||
resharper_max_formal_parameters_on_line = 3
|
||||
resharper_wrap_chained_binary_expressions = chop_if_long
|
||||
resharper_wrap_chained_binary_patterns = chop_if_long
|
||||
resharper_wrap_chained_method_calls = wrap_if_long
|
||||
resharper_wrap_chained_method_calls = chop_if_long
|
||||
|
||||
resharper_csharp_wrap_before_first_type_parameter_constraint = true
|
||||
resharper_csharp_place_type_constraints_on_same_line = false
|
||||
|
@@ -1,25 +1,20 @@
|
||||
#nullable disable
|
||||
using Discord.Interactions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NadekoBot.Common.Configs;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Common.Configs;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using Discord.Interactions;
|
||||
using RunMode = Discord.Commands.RunMode;
|
||||
|
||||
namespace NadekoBot;
|
||||
|
||||
public sealed class Bot
|
||||
{
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly CommandService _commandService;
|
||||
private readonly DbService _db;
|
||||
private readonly IBotCredsProvider _credsProvider;
|
||||
private readonly InteractionService _interactionService;
|
||||
|
||||
public event Func<GuildConfig, Task> JoinedGuild = delegate { return Task.CompletedTask; };
|
||||
|
||||
public DiscordSocketClient Client { get; }
|
||||
@@ -29,6 +24,11 @@ public sealed class Bot
|
||||
|
||||
public string Mention { get; private set; }
|
||||
public bool IsReady { get; private set; }
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly CommandService _commandService;
|
||||
private readonly DbService _db;
|
||||
private readonly IBotCredsProvider _credsProvider;
|
||||
private readonly InteractionService _interactionService;
|
||||
|
||||
public Bot(int shardId, int? totalShards)
|
||||
{
|
||||
@@ -40,10 +40,7 @@ public sealed class Bot
|
||||
|
||||
_db = new(_creds);
|
||||
|
||||
if (shardId == 0)
|
||||
{
|
||||
_db.Setup();
|
||||
}
|
||||
if (shardId == 0) _db.Setup();
|
||||
|
||||
Client = new(new()
|
||||
{
|
||||
@@ -55,14 +52,10 @@ public sealed class Bot
|
||||
AlwaysDownloadUsers = false,
|
||||
AlwaysResolveStickers = false,
|
||||
AlwaysDownloadDefaultStickers = false,
|
||||
GatewayIntents = GatewayIntents.All,
|
||||
GatewayIntents = GatewayIntents.All
|
||||
});
|
||||
|
||||
_commandService = new(new()
|
||||
{
|
||||
CaseSensitiveCommands = false,
|
||||
DefaultRunMode = Discord.Commands.RunMode.Sync,
|
||||
});
|
||||
_commandService = new(new() { CaseSensitiveCommands = false, DefaultRunMode = RunMode.Sync });
|
||||
|
||||
_interactionService = new(Client.Rest);
|
||||
|
||||
@@ -86,9 +79,8 @@ public sealed class Bot
|
||||
AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList).ToImmutableArray();
|
||||
}
|
||||
|
||||
var svcs = new ServiceCollection()
|
||||
.AddTransient<IBotCredentials>(_ => _credsProvider.GetCreds()) // bot creds
|
||||
.AddSingleton<IBotCredsProvider>(_credsProvider)
|
||||
var svcs = new ServiceCollection().AddTransient(_ => _credsProvider.GetCreds()) // bot creds
|
||||
.AddSingleton(_credsProvider)
|
||||
.AddSingleton(_db) // database
|
||||
.AddRedis(_creds.RedisOptions) // redis
|
||||
.AddSingleton(Client) // discord socket client
|
||||
@@ -103,31 +95,24 @@ public sealed class Bot
|
||||
.AddConfigMigrators()
|
||||
.AddMemoryCache()
|
||||
// music
|
||||
.AddMusic()
|
||||
.AddMusic();
|
||||
// admin
|
||||
#if GLOBAL_NADEKO
|
||||
.AddSingleton<ILogCommandService, DummyLogCommandService>()
|
||||
svcs.AddSingleton<ILogCommandService, DummyLogCommandService>();
|
||||
#else
|
||||
.AddSingleton<ILogCommandService, LogCommandService>()
|
||||
svcs.AddSingleton<ILogCommandService, LogCommandService>();
|
||||
#endif
|
||||
;
|
||||
|
||||
svcs.AddHttpClient();
|
||||
svcs.AddHttpClient("memelist").ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
|
||||
{
|
||||
AllowAutoRedirect = false
|
||||
});
|
||||
svcs.AddHttpClient("memelist")
|
||||
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { AllowAutoRedirect = false });
|
||||
|
||||
if (Environment.GetEnvironmentVariable("NADEKOBOT_IS_COORDINATED") != "1")
|
||||
{
|
||||
svcs.AddSingleton<ICoordinator, SingleProcessCoordinator>();
|
||||
}
|
||||
else
|
||||
{
|
||||
svcs.AddSingleton<RemoteGrpcCoordinator>()
|
||||
.AddSingleton<ICoordinator>(x => x.GetRequiredService<RemoteGrpcCoordinator>())
|
||||
.AddSingleton<IReadyExecutor>(x => x.GetRequiredService<RemoteGrpcCoordinator>());
|
||||
}
|
||||
|
||||
svcs.AddSingleton<RedisLocalDataCache>()
|
||||
.AddSingleton<ILocalDataCache>(x => x.GetRequiredService<RedisLocalDataCache>())
|
||||
@@ -136,10 +121,8 @@ public sealed class Bot
|
||||
.AddSingleton<IReadyExecutor>(x => x.GetRequiredService<RedisImagesCache>())
|
||||
.AddSingleton<IDataCache, RedisCache>();
|
||||
|
||||
svcs.Scan(scan => scan
|
||||
.FromAssemblyOf<IReadyExecutor>()
|
||||
.AddClasses(classes => classes
|
||||
.AssignableToAny(
|
||||
svcs.Scan(scan => scan.FromAssemblyOf<IReadyExecutor>()
|
||||
.AddClasses(classes => classes.AssignableToAny(
|
||||
// services
|
||||
typeof(INService),
|
||||
|
||||
@@ -153,18 +136,14 @@ public sealed class Bot
|
||||
#endif
|
||||
)
|
||||
.AsSelfWithInterfaces()
|
||||
.WithSingletonLifetime()
|
||||
);
|
||||
.WithSingletonLifetime());
|
||||
|
||||
//initialize Services
|
||||
Services = svcs.BuildServiceProvider();
|
||||
var exec = Services.GetRequiredService<IBehaviourExecutor>();
|
||||
exec.Initialize();
|
||||
|
||||
if (Client.ShardId == 0)
|
||||
{
|
||||
ApplyConfigMigrations();
|
||||
}
|
||||
if (Client.ShardId == 0) ApplyConfigMigrations();
|
||||
|
||||
_ = LoadTypeReaders(typeof(Bot).Assembly);
|
||||
|
||||
@@ -176,10 +155,7 @@ public sealed class Bot
|
||||
{
|
||||
// execute all migrators
|
||||
var migrators = Services.GetServices<IConfigMigrator>();
|
||||
foreach (var migrator in migrators)
|
||||
{
|
||||
migrator.EnsureMigrated();
|
||||
}
|
||||
foreach (var migrator in migrators) migrator.EnsureMigrated();
|
||||
}
|
||||
|
||||
private IEnumerable<object> LoadTypeReaders(Assembly assembly)
|
||||
@@ -225,10 +201,7 @@ public sealed class Bot
|
||||
clientReady.TrySetResult(true);
|
||||
try
|
||||
{
|
||||
foreach (var chan in await Client.GetDMChannelsAsync())
|
||||
{
|
||||
await chan.CloseAsync();
|
||||
}
|
||||
foreach (var chan in await Client.GetDMChannelsAsync()) await chan.CloseAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -282,6 +255,7 @@ public sealed class Bot
|
||||
{
|
||||
gc = uow.GuildConfigsForId(arg.Id, null);
|
||||
}
|
||||
|
||||
await JoinedGuild.Invoke(gc);
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
|
@@ -6,5 +6,5 @@ public enum AddRemove
|
||||
Add = int.MinValue,
|
||||
Remove = int.MinValue + 1,
|
||||
Rem = int.MinValue + 1,
|
||||
Rm = int.MinValue + 1,
|
||||
Rm = int.MinValue + 1
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
|
@@ -1,9 +1,10 @@
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace NadekoBot.Common.Attributes;
|
||||
|
||||
public static class CommandNameLoadHelper
|
||||
{
|
||||
private static readonly YamlDotNet.Serialization.IDeserializer _deserializer =
|
||||
new YamlDotNet.Serialization.Deserializer();
|
||||
private static readonly IDeserializer _deserializer = new Deserializer();
|
||||
|
||||
public static Lazy<Dictionary<string, string[]>> LazyCommandAliases = new(() => LoadCommandNames());
|
||||
|
||||
|
@@ -5,9 +5,9 @@ namespace NadekoBot.Common.Attributes;
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class NadekoCommandAttribute : CommandAttribute
|
||||
{
|
||||
public string MethodName { get; }
|
||||
|
||||
public NadekoCommandAttribute([CallerMemberName] string memberName = "")
|
||||
: base(CommandNameLoadHelper.GetCommandNameFor(memberName))
|
||||
=> this.MethodName = memberName.ToLowerInvariant();
|
||||
|
||||
public string MethodName { get; }
|
||||
=> MethodName = memberName.ToLowerInvariant();
|
||||
}
|
@@ -6,5 +6,5 @@ public sealed class NadekoOptionsAttribute : Attribute
|
||||
public Type OptionType { get; set; }
|
||||
|
||||
public NadekoOptionsAttribute(Type t)
|
||||
=> this.OptionType = t;
|
||||
=> OptionType = t;
|
||||
}
|
@@ -14,7 +14,6 @@ public sealed class OwnerOnlyAttribute : PreconditionAttribute
|
||||
|
||||
return Task.FromResult(creds.IsOwner(context.User) || context.Client.CurrentUser.Id == context.User.Id
|
||||
? PreconditionResult.FromSuccess()
|
||||
: PreconditionResult.FromError("Not owner")
|
||||
);
|
||||
: PreconditionResult.FromError("Not owner"));
|
||||
}
|
||||
}
|
@@ -6,6 +6,16 @@ namespace Discord;
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class UserPermAttribute : RequireUserPermissionAttribute
|
||||
{
|
||||
public UserPermAttribute(GuildPerm permission)
|
||||
: base(permission)
|
||||
{
|
||||
}
|
||||
|
||||
public UserPermAttribute(ChannelPerm permission)
|
||||
: base(permission)
|
||||
{
|
||||
}
|
||||
|
||||
public override Task<PreconditionResult> CheckPermissionsAsync(
|
||||
ICommandContext context,
|
||||
CommandInfo command,
|
||||
@@ -17,14 +27,4 @@ public class UserPermAttribute : RequireUserPermissionAttribute
|
||||
|
||||
return base.CheckPermissionsAsync(context, command, services);
|
||||
}
|
||||
|
||||
public UserPermAttribute(GuildPerm permission)
|
||||
: base(permission)
|
||||
{
|
||||
}
|
||||
|
||||
public UserPermAttribute(ChannelPerm permission)
|
||||
: base(permission)
|
||||
{
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
// License MIT
|
||||
// Source: https://github.com/i3arnon/ConcurrentHashSet
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace System.Collections.Generic;
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the items in the collection.</typeparam>
|
||||
/// <remarks>
|
||||
/// All public members of <see cref="ConcurrentHashSet{T}"/> are thread-safe and may be used
|
||||
/// All public members of <see cref="ConcurrentHashSet{T}" /> are thread-safe and may be used
|
||||
/// concurrently from multiple threads.
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
@@ -20,53 +20,16 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
private const int DefaultCapacity = 31;
|
||||
private const int MaxLockNumber = 1024;
|
||||
|
||||
private readonly IEqualityComparer<T> _comparer;
|
||||
private readonly bool _growLockArray;
|
||||
|
||||
private int _budget;
|
||||
private volatile Tables _tables;
|
||||
|
||||
private static int DefaultConcurrencyLevel
|
||||
=> PlatformHelper.ProcessorCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of items contained in the <see
|
||||
/// cref="ConcurrentHashSet{T}"/>.
|
||||
/// Gets a value that indicates whether the <see cref="ConcurrentHashSet{T}" /> is empty.
|
||||
/// </summary>
|
||||
/// <value>The number of items contained in the <see
|
||||
/// cref="ConcurrentHashSet{T}"/>.</value>
|
||||
/// <remarks>Count has snapshot semantics and represents the number of items in the <see
|
||||
/// cref="ConcurrentHashSet{T}"/>
|
||||
/// at the moment when Count was accessed.</remarks>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = 0;
|
||||
var acquiredLocks = 0;
|
||||
try
|
||||
{
|
||||
AcquireAllLocks(ref acquiredLocks);
|
||||
|
||||
for (var i = 0; i < _tables.CountPerLock.Length; i++)
|
||||
{
|
||||
count += _tables.CountPerLock[i];
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReleaseLocks(0, acquiredLocks);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that indicates whether the <see cref="ConcurrentHashSet{T}"/> is empty.
|
||||
/// </summary>
|
||||
/// <value>true if the <see cref="ConcurrentHashSet{T}"/> is empty; otherwise,
|
||||
/// false.</value>
|
||||
/// <value>
|
||||
/// true if the <see cref="ConcurrentHashSet{T}" /> is empty; otherwise,
|
||||
/// false.
|
||||
/// </value>
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
@@ -77,13 +40,9 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
AcquireAllLocks(ref acquiredLocks);
|
||||
|
||||
for (var i = 0; i < _tables.CountPerLock.Length; i++)
|
||||
{
|
||||
if (_tables.CountPerLock[i] != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReleaseLocks(0, acquiredLocks);
|
||||
@@ -93,91 +52,158 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
}
|
||||
}
|
||||
|
||||
bool ICollection<T>.IsReadOnly
|
||||
=> false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see
|
||||
/// cref="ConcurrentHashSet{T}"/>
|
||||
/// Gets the number of items contained in the
|
||||
/// <see
|
||||
/// cref="ConcurrentHashSet{T}" />
|
||||
/// .
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The number of items contained in the
|
||||
/// <see
|
||||
/// cref="ConcurrentHashSet{T}" />
|
||||
/// .
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Count has snapshot semantics and represents the number of items in the
|
||||
/// <see
|
||||
/// cref="ConcurrentHashSet{T}" />
|
||||
/// at the moment when Count was accessed.
|
||||
/// </remarks>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = 0;
|
||||
var acquiredLocks = 0;
|
||||
try
|
||||
{
|
||||
AcquireAllLocks(ref acquiredLocks);
|
||||
|
||||
for (var i = 0; i < _tables.CountPerLock.Length; i++) count += _tables.CountPerLock[i];
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReleaseLocks(0, acquiredLocks);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IEqualityComparer<T> _comparer;
|
||||
private readonly bool _growLockArray;
|
||||
|
||||
private int _budget;
|
||||
private volatile Tables _tables;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the
|
||||
/// <see
|
||||
/// cref="ConcurrentHashSet{T}" />
|
||||
/// class that is empty, has the default concurrency level, has the default initial capacity, and
|
||||
/// uses the default comparer for the item type.
|
||||
/// </summary>
|
||||
public ConcurrentHashSet()
|
||||
: this(DefaultConcurrencyLevel,
|
||||
DefaultCapacity,
|
||||
true,
|
||||
EqualityComparer<T>.Default)
|
||||
: this(DefaultConcurrencyLevel, DefaultCapacity, true, EqualityComparer<T>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see
|
||||
/// cref="ConcurrentHashSet{T}"/>
|
||||
/// Initializes a new instance of the
|
||||
/// <see
|
||||
/// cref="ConcurrentHashSet{T}" />
|
||||
/// class that is empty, has the specified concurrency level and capacity, and uses the default
|
||||
/// comparer for the item type.
|
||||
/// </summary>
|
||||
/// <param name="concurrencyLevel">The estimated number of threads that will update the
|
||||
/// <see cref="ConcurrentHashSet{T}"/> concurrently.</param>
|
||||
/// <param name="capacity">The initial number of elements that the <see
|
||||
/// cref="ConcurrentHashSet{T}"/>
|
||||
/// can contain.</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="concurrencyLevel"/> is
|
||||
/// less than 1.</exception>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException"> <paramref name="capacity"/> is less than
|
||||
/// 0.</exception>
|
||||
/// <param name="concurrencyLevel">
|
||||
/// The estimated number of threads that will update the
|
||||
/// <see cref="ConcurrentHashSet{T}" /> concurrently.
|
||||
/// </param>
|
||||
/// <param name="capacity">
|
||||
/// The initial number of elements that the
|
||||
/// <see
|
||||
/// cref="ConcurrentHashSet{T}" />
|
||||
/// can contain.
|
||||
/// </param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">
|
||||
/// <paramref name="concurrencyLevel" /> is
|
||||
/// less than 1.
|
||||
/// </exception>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">
|
||||
/// <paramref name="capacity" /> is less than
|
||||
/// 0.
|
||||
/// </exception>
|
||||
public ConcurrentHashSet(int concurrencyLevel, int capacity)
|
||||
: this(concurrencyLevel,
|
||||
capacity,
|
||||
false,
|
||||
EqualityComparer<T>.Default)
|
||||
: this(concurrencyLevel, capacity, false, EqualityComparer<T>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}"/>
|
||||
/// class that contains elements copied from the specified <see
|
||||
/// cref="T:System.Collections.IEnumerable{T}"/>, has the default concurrency
|
||||
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}" />
|
||||
/// class that contains elements copied from the specified
|
||||
/// <see
|
||||
/// cref="T:System.Collections.IEnumerable{T}" />
|
||||
/// , has the default concurrency
|
||||
/// level, has the default initial capacity, and uses the default comparer for the item type.
|
||||
/// </summary>
|
||||
/// <param name="collection">The <see
|
||||
/// cref="T:System.Collections.IEnumerable{T}"/> whose elements are copied to
|
||||
/// <param name="collection">
|
||||
/// The
|
||||
/// <see
|
||||
/// cref="T:System.Collections.IEnumerable{T}" />
|
||||
/// whose elements are copied to
|
||||
/// the new
|
||||
/// <see cref="ConcurrentHashSet{T}"/>.</param>
|
||||
/// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference.</exception>
|
||||
/// <see cref="ConcurrentHashSet{T}" />.
|
||||
/// </param>
|
||||
/// <exception cref="T:System.ArgumentNullException"><paramref name="collection" /> is a null reference.</exception>
|
||||
public ConcurrentHashSet(IEnumerable<T> collection)
|
||||
: this(collection, EqualityComparer<T>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}"/>
|
||||
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}" />
|
||||
/// class that is empty, has the specified concurrency level and capacity, and uses the specified
|
||||
/// <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>.
|
||||
/// <see cref="T:System.Collections.Generic.IEqualityComparer{T}" />.
|
||||
/// </summary>
|
||||
/// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>
|
||||
/// implementation to use when comparing items.</param>
|
||||
/// <exception cref="T:System.ArgumentNullException"><paramref name="comparer"/> is a null reference.</exception>
|
||||
/// <param name="comparer">
|
||||
/// The <see cref="T:System.Collections.Generic.IEqualityComparer{T}" />
|
||||
/// implementation to use when comparing items.
|
||||
/// </param>
|
||||
/// <exception cref="T:System.ArgumentNullException"><paramref name="comparer" /> is a null reference.</exception>
|
||||
public ConcurrentHashSet(IEqualityComparer<T> comparer)
|
||||
: this(DefaultConcurrencyLevel,
|
||||
DefaultCapacity,
|
||||
true,
|
||||
comparer)
|
||||
: this(DefaultConcurrencyLevel, DefaultCapacity, true, comparer)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}"/>
|
||||
/// class that contains elements copied from the specified <see
|
||||
/// cref="T:System.Collections.IEnumerable"/>, has the default concurrency level, has the default
|
||||
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}" />
|
||||
/// class that contains elements copied from the specified
|
||||
/// <see
|
||||
/// cref="T:System.Collections.IEnumerable" />
|
||||
/// , has the default concurrency level, has the default
|
||||
/// initial capacity, and uses the specified
|
||||
/// <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>.
|
||||
/// <see cref="T:System.Collections.Generic.IEqualityComparer{T}" />.
|
||||
/// </summary>
|
||||
/// <param name="collection">The <see
|
||||
/// cref="T:System.Collections.IEnumerable{T}"/> whose elements are copied to
|
||||
/// <param name="collection">
|
||||
/// The
|
||||
/// <see
|
||||
/// cref="T:System.Collections.IEnumerable{T}" />
|
||||
/// whose elements are copied to
|
||||
/// the new
|
||||
/// <see cref="ConcurrentHashSet{T}"/>.</param>
|
||||
/// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>
|
||||
/// implementation to use when comparing items.</param>
|
||||
/// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference
|
||||
/// <see cref="ConcurrentHashSet{T}" />.
|
||||
/// </param>
|
||||
/// <param name="comparer">
|
||||
/// The <see cref="T:System.Collections.Generic.IEqualityComparer{T}" />
|
||||
/// implementation to use when comparing items.
|
||||
/// </param>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="collection" /> is a null reference
|
||||
/// (Nothing in Visual Basic). -or-
|
||||
/// <paramref name="comparer"/> is a null reference (Nothing in Visual Basic).
|
||||
/// <paramref name="comparer" /> is a null reference (Nothing in Visual Basic).
|
||||
/// </exception>
|
||||
public ConcurrentHashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer)
|
||||
: this(comparer)
|
||||
@@ -189,30 +215,33 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}"/>
|
||||
/// class that contains elements copied from the specified <see cref="T:System.Collections.IEnumerable"/>,
|
||||
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}" />
|
||||
/// class that contains elements copied from the specified <see cref="T:System.Collections.IEnumerable" />,
|
||||
/// has the specified concurrency level, has the specified initial capacity, and uses the specified
|
||||
/// <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>.
|
||||
/// <see cref="T:System.Collections.Generic.IEqualityComparer{T}" />.
|
||||
/// </summary>
|
||||
/// <param name="concurrencyLevel">The estimated number of threads that will update the
|
||||
/// <see cref="ConcurrentHashSet{T}"/> concurrently.</param>
|
||||
/// <param name="collection">The <see cref="T:System.Collections.IEnumerable{T}"/> whose elements are copied to the new
|
||||
/// <see cref="ConcurrentHashSet{T}"/>.</param>
|
||||
/// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/> implementation to use
|
||||
/// when comparing items.</param>
|
||||
/// <param name="concurrencyLevel">
|
||||
/// The estimated number of threads that will update the
|
||||
/// <see cref="ConcurrentHashSet{T}" /> concurrently.
|
||||
/// </param>
|
||||
/// <param name="collection">
|
||||
/// The <see cref="T:System.Collections.IEnumerable{T}" /> whose elements are copied to the new
|
||||
/// <see cref="ConcurrentHashSet{T}" />.
|
||||
/// </param>
|
||||
/// <param name="comparer">
|
||||
/// The <see cref="T:System.Collections.Generic.IEqualityComparer{T}" /> implementation to use
|
||||
/// when comparing items.
|
||||
/// </param>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="collection"/> is a null reference.
|
||||
/// <paramref name="collection" /> is a null reference.
|
||||
/// -or-
|
||||
/// <paramref name="comparer"/> is a null reference.
|
||||
/// <paramref name="comparer" /> is a null reference.
|
||||
/// </exception>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">
|
||||
/// <paramref name="concurrencyLevel"/> is less than 1.
|
||||
/// <paramref name="concurrencyLevel" /> is less than 1.
|
||||
/// </exception>
|
||||
public ConcurrentHashSet(int concurrencyLevel, IEnumerable<T> collection, IEqualityComparer<T> comparer)
|
||||
: this(concurrencyLevel,
|
||||
DefaultCapacity,
|
||||
false,
|
||||
comparer)
|
||||
: this(concurrencyLevel, DefaultCapacity, false, comparer)
|
||||
{
|
||||
if (collection is null) throw new ArgumentNullException(nameof(collection));
|
||||
if (comparer is null) throw new ArgumentNullException(nameof(comparer));
|
||||
@@ -221,47 +250,49 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}"/>
|
||||
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}" />
|
||||
/// class that is empty, has the specified concurrency level, has the specified initial capacity, and
|
||||
/// uses the specified <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>.
|
||||
/// uses the specified <see cref="T:System.Collections.Generic.IEqualityComparer{T}" />.
|
||||
/// </summary>
|
||||
/// <param name="concurrencyLevel">The estimated number of threads that will update the
|
||||
/// <see cref="ConcurrentHashSet{T}"/> concurrently.</param>
|
||||
/// <param name="capacity">The initial number of elements that the <see
|
||||
/// cref="ConcurrentHashSet{T}"/>
|
||||
/// can contain.</param>
|
||||
/// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{T}"/>
|
||||
/// implementation to use when comparing items.</param>
|
||||
/// <param name="concurrencyLevel">
|
||||
/// The estimated number of threads that will update the
|
||||
/// <see cref="ConcurrentHashSet{T}" /> concurrently.
|
||||
/// </param>
|
||||
/// <param name="capacity">
|
||||
/// The initial number of elements that the
|
||||
/// <see
|
||||
/// cref="ConcurrentHashSet{T}" />
|
||||
/// can contain.
|
||||
/// </param>
|
||||
/// <param name="comparer">
|
||||
/// The <see cref="T:System.Collections.Generic.IEqualityComparer{T}" />
|
||||
/// implementation to use when comparing items.
|
||||
/// </param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">
|
||||
/// <paramref name="concurrencyLevel"/> is less than 1. -or-
|
||||
/// <paramref name="capacity"/> is less than 0.
|
||||
/// <paramref name="concurrencyLevel" /> is less than 1. -or-
|
||||
/// <paramref name="capacity" /> is less than 0.
|
||||
/// </exception>
|
||||
/// <exception cref="T:System.ArgumentNullException"><paramref name="comparer"/> is a null reference.</exception>
|
||||
/// <exception cref="T:System.ArgumentNullException"><paramref name="comparer" /> is a null reference.</exception>
|
||||
public ConcurrentHashSet(int concurrencyLevel, int capacity, IEqualityComparer<T> comparer)
|
||||
: this(concurrencyLevel,
|
||||
capacity,
|
||||
false,
|
||||
comparer)
|
||||
: this(concurrencyLevel, capacity, false, comparer)
|
||||
{
|
||||
}
|
||||
|
||||
private ConcurrentHashSet(int concurrencyLevel, int capacity, bool growLockArray, IEqualityComparer<T> comparer)
|
||||
private ConcurrentHashSet(
|
||||
int concurrencyLevel,
|
||||
int capacity,
|
||||
bool growLockArray,
|
||||
IEqualityComparer<T> comparer)
|
||||
{
|
||||
if (concurrencyLevel < 1) throw new ArgumentOutOfRangeException(nameof(concurrencyLevel));
|
||||
if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
|
||||
// The capacity should be at least as large as the concurrency level. Otherwise, we would have locks that don't guard
|
||||
// any buckets.
|
||||
if (capacity < concurrencyLevel)
|
||||
{
|
||||
capacity = concurrencyLevel;
|
||||
}
|
||||
if (capacity < concurrencyLevel) capacity = concurrencyLevel;
|
||||
|
||||
var locks = new object[concurrencyLevel];
|
||||
for (var i = 0; i < locks.Length; i++)
|
||||
{
|
||||
locks[i] = new();
|
||||
}
|
||||
for (var i = 0; i < locks.Length; i++) locks[i] = new();
|
||||
|
||||
var countPerLock = new int[locks.Length];
|
||||
var buckets = new Node[capacity];
|
||||
@@ -273,18 +304,7 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified item to the <see cref="ConcurrentHashSet{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to add.</param>
|
||||
/// <returns>true if the items was added to the <see cref="ConcurrentHashSet{T}"/>
|
||||
/// successfully; false if it already exists.</returns>
|
||||
/// <exception cref="T:System.OverflowException">The <see cref="ConcurrentHashSet{T}"/>
|
||||
/// contains too many items.</exception>
|
||||
public bool Add(T item)
|
||||
=> AddInternal(item, _comparer.GetHashCode(item), true);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all items from the <see cref="ConcurrentHashSet{T}"/>.
|
||||
/// Removes all items from the <see cref="ConcurrentHashSet{T}" />.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
@@ -304,11 +324,11 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the <see cref="ConcurrentHashSet{T}"/> contains the specified
|
||||
/// Determines whether the <see cref="ConcurrentHashSet{T}" /> contains the specified
|
||||
/// item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to locate in the <see cref="ConcurrentHashSet{T}"/>.</param>
|
||||
/// <returns>true if the <see cref="ConcurrentHashSet{T}"/> contains the item; otherwise, false.</returns>
|
||||
/// <param name="item">The item to locate in the <see cref="ConcurrentHashSet{T}" />.</param>
|
||||
/// <returns>true if the <see cref="ConcurrentHashSet{T}" /> contains the item; otherwise, false.</returns>
|
||||
public bool Contains(T item)
|
||||
{
|
||||
var hashcode = _comparer.GetHashCode(item);
|
||||
@@ -324,11 +344,8 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
if (hashcode == current.Hashcode &&
|
||||
_comparer.Equals(current.Item, item))
|
||||
{
|
||||
if (hashcode == current.Hashcode && _comparer.Equals(current.Item, item))
|
||||
return true;
|
||||
}
|
||||
|
||||
current = current.Next;
|
||||
}
|
||||
@@ -336,73 +353,53 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove the item from the <see cref="ConcurrentHashSet{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to remove.</param>
|
||||
/// <returns>true if an item was removed successfully; otherwise, false.</returns>
|
||||
public bool TryRemove(T item)
|
||||
{
|
||||
var hashcode = _comparer.GetHashCode(item);
|
||||
while (true)
|
||||
{
|
||||
var tables = _tables;
|
||||
void ICollection<T>.Add(T item)
|
||||
=> Add(item);
|
||||
|
||||
GetBucketAndLockNo(hashcode,
|
||||
out var bucketNo,
|
||||
out var lockNo,
|
||||
tables.Buckets.Length,
|
||||
tables.Locks.Length);
|
||||
void ICollection<T>.CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
if (array is null) throw new ArgumentNullException(nameof(array));
|
||||
if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex));
|
||||
|
||||
lock (tables.Locks[lockNo])
|
||||
var locksAcquired = 0;
|
||||
try
|
||||
{
|
||||
// If the table just got resized, we may not be holding the right lock, and must retry.
|
||||
// This should be a rare occurrence.
|
||||
if (tables != _tables)
|
||||
{
|
||||
continue;
|
||||
AcquireAllLocks(ref locksAcquired);
|
||||
|
||||
var count = 0;
|
||||
|
||||
for (var i = 0; i < _tables.Locks.Length && count >= 0; i++) count += _tables.CountPerLock[i];
|
||||
|
||||
if (array.Length - count < arrayIndex || count < 0) //"count" itself or "count + arrayIndex" can overflow
|
||||
throw new ArgumentException(
|
||||
"The index is equal to or greater than the length of the array, or the number of elements in the set is greater than the available space from index to the end of the destination array.");
|
||||
|
||||
CopyToItems(array, arrayIndex);
|
||||
}
|
||||
|
||||
Node previous = null;
|
||||
for (var current = tables.Buckets[bucketNo]; current != null; current = current.Next)
|
||||
finally
|
||||
{
|
||||
Debug.Assert((previous is null && current == tables.Buckets[bucketNo]) || previous.Next == current);
|
||||
|
||||
if (hashcode == current.Hashcode &&
|
||||
_comparer.Equals(current.Item, item))
|
||||
{
|
||||
if (previous is null)
|
||||
{
|
||||
Volatile.Write(ref tables.Buckets[bucketNo], current.Next);
|
||||
}
|
||||
else
|
||||
{
|
||||
previous.Next = current.Next;
|
||||
}
|
||||
|
||||
tables.CountPerLock[lockNo]--;
|
||||
return true;
|
||||
}
|
||||
|
||||
previous = current;
|
||||
ReleaseLocks(0, locksAcquired);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ICollection<T>.Remove(T item)
|
||||
=> TryRemove(item);
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
/// <summary>Returns an enumerator that iterates through the <see
|
||||
/// cref="ConcurrentHashSet{T}"/>.</summary>
|
||||
/// <returns>An enumerator for the <see cref="ConcurrentHashSet{T}"/>.</returns>
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the
|
||||
/// <see
|
||||
/// cref="ConcurrentHashSet{T}" />
|
||||
/// .
|
||||
/// </summary>
|
||||
/// <returns>An enumerator for the <see cref="ConcurrentHashSet{T}" />.</returns>
|
||||
/// <remarks>
|
||||
/// The enumerator returned from the collection is safe to use concurrently with
|
||||
/// reads and writes to the collection, however it does not represent a moment-in-time snapshot
|
||||
/// of the collection. The contents exposed through the enumerator may contain modifications
|
||||
/// made to the collection after <see cref="GetEnumerator"/> was called.
|
||||
/// made to the collection after <see cref="GetEnumerator" /> was called.
|
||||
/// </remarks>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
@@ -421,58 +418,70 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
}
|
||||
}
|
||||
|
||||
void ICollection<T>.Add(T item)
|
||||
=> Add(item);
|
||||
/// <summary>
|
||||
/// Adds the specified item to the <see cref="ConcurrentHashSet{T}" />.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to add.</param>
|
||||
/// <returns>
|
||||
/// true if the items was added to the <see cref="ConcurrentHashSet{T}" />
|
||||
/// successfully; false if it already exists.
|
||||
/// </returns>
|
||||
/// <exception cref="T:System.OverflowException">
|
||||
/// The <see cref="ConcurrentHashSet{T}" />
|
||||
/// contains too many items.
|
||||
/// </exception>
|
||||
public bool Add(T item)
|
||||
=> AddInternal(item, _comparer.GetHashCode(item), true);
|
||||
|
||||
bool ICollection<T>.IsReadOnly
|
||||
=> false;
|
||||
|
||||
void ICollection<T>.CopyTo(T[] array, int arrayIndex)
|
||||
/// <summary>
|
||||
/// Attempts to remove the item from the <see cref="ConcurrentHashSet{T}" />.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to remove.</param>
|
||||
/// <returns>true if an item was removed successfully; otherwise, false.</returns>
|
||||
public bool TryRemove(T item)
|
||||
{
|
||||
if (array is null) throw new ArgumentNullException(nameof(array));
|
||||
if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex));
|
||||
|
||||
var locksAcquired = 0;
|
||||
try
|
||||
var hashcode = _comparer.GetHashCode(item);
|
||||
while (true)
|
||||
{
|
||||
AcquireAllLocks(ref locksAcquired);
|
||||
var tables = _tables;
|
||||
|
||||
var count = 0;
|
||||
GetBucketAndLockNo(hashcode, out var bucketNo, out var lockNo, tables.Buckets.Length, tables.Locks.Length);
|
||||
|
||||
for (var i = 0; i < _tables.Locks.Length && count >= 0; i++)
|
||||
lock (tables.Locks[lockNo])
|
||||
{
|
||||
count += _tables.CountPerLock[i];
|
||||
// If the table just got resized, we may not be holding the right lock, and must retry.
|
||||
// This should be a rare occurrence.
|
||||
if (tables != _tables) continue;
|
||||
|
||||
Node previous = null;
|
||||
for (var current = tables.Buckets[bucketNo]; current != null; current = current.Next)
|
||||
{
|
||||
Debug.Assert((previous is null && current == tables.Buckets[bucketNo]) || previous.Next == current);
|
||||
|
||||
if (hashcode == current.Hashcode && _comparer.Equals(current.Item, item))
|
||||
{
|
||||
if (previous is null)
|
||||
Volatile.Write(ref tables.Buckets[bucketNo], current.Next);
|
||||
else
|
||||
previous.Next = current.Next;
|
||||
|
||||
tables.CountPerLock[lockNo]--;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (array.Length - count < arrayIndex ||
|
||||
count < 0) //"count" itself or "count + arrayIndex" can overflow
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"The index is equal to or greater than the length of the array, or the number of elements in the set is greater than the available space from index to the end of the destination array.");
|
||||
}
|
||||
|
||||
CopyToItems(array, arrayIndex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReleaseLocks(0, locksAcquired);
|
||||
previous = current;
|
||||
}
|
||||
}
|
||||
|
||||
bool ICollection<T>.Remove(T item)
|
||||
=> TryRemove(item);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeFromCollection(IEnumerable<T> collection)
|
||||
{
|
||||
foreach (var item in collection)
|
||||
{
|
||||
AddInternal(item, _comparer.GetHashCode(item), false);
|
||||
}
|
||||
foreach (var item in collection) AddInternal(item, _comparer.GetHashCode(item), false);
|
||||
|
||||
if (_budget == 0)
|
||||
{
|
||||
_budget = _tables.Buckets.Length / _tables.Locks.Length;
|
||||
}
|
||||
if (_budget == 0) _budget = _tables.Buckets.Length / _tables.Locks.Length;
|
||||
}
|
||||
|
||||
private bool AddInternal(T item, int hashcode, bool acquireLock)
|
||||
@@ -480,11 +489,7 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
while (true)
|
||||
{
|
||||
var tables = _tables;
|
||||
GetBucketAndLockNo(hashcode,
|
||||
out var bucketNo,
|
||||
out var lockNo,
|
||||
tables.Buckets.Length,
|
||||
tables.Locks.Length);
|
||||
GetBucketAndLockNo(hashcode, out var bucketNo, out var lockNo, tables.Buckets.Length, tables.Locks.Length);
|
||||
|
||||
var resizeDesired = false;
|
||||
var lockTaken = false;
|
||||
@@ -495,21 +500,15 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
|
||||
// If the table just got resized, we may not be holding the right lock, and must retry.
|
||||
// This should be a rare occurrence.
|
||||
if (tables != _tables)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (tables != _tables) continue;
|
||||
|
||||
// Try to find this item in the bucket
|
||||
Node previous = null;
|
||||
for (var current = tables.Buckets[bucketNo]; current != null; current = current.Next)
|
||||
{
|
||||
Debug.Assert((previous is null && current == tables.Buckets[bucketNo]) || previous.Next == current);
|
||||
if (hashcode == current.Hashcode &&
|
||||
_comparer.Equals(current.Item, item))
|
||||
{
|
||||
if (hashcode == current.Hashcode && _comparer.Equals(current.Item, item))
|
||||
return false;
|
||||
}
|
||||
|
||||
previous = current;
|
||||
}
|
||||
@@ -526,10 +525,7 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
// It is also possible that GrowTable will increase the budget but won't resize the bucket table.
|
||||
// That happens if the bucket table is found to be poorly utilized due to a bad hash function.
|
||||
//
|
||||
if (tables.CountPerLock[lockNo] > _budget)
|
||||
{
|
||||
resizeDesired = true;
|
||||
}
|
||||
if (tables.CountPerLock[lockNo] > _budget) resizeDesired = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -545,10 +541,7 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
// - As a result, it is possible that GrowTable will be called unnecessarily. But, GrowTable will obtain lock 0
|
||||
// and then verify that the table we passed to it as the argument is still the current table.
|
||||
//
|
||||
if (resizeDesired)
|
||||
{
|
||||
GrowTable(tables);
|
||||
}
|
||||
if (resizeDesired) GrowTable(tables);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -561,7 +554,11 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
return bucketNo;
|
||||
}
|
||||
|
||||
private static void GetBucketAndLockNo(int hashcode, out int bucketNo, out int lockNo, int bucketCount,
|
||||
private static void GetBucketAndLockNo(
|
||||
int hashcode,
|
||||
out int bucketNo,
|
||||
out int lockNo,
|
||||
int bucketCount,
|
||||
int lockCount)
|
||||
{
|
||||
bucketNo = (hashcode & 0x7fffffff) % bucketCount;
|
||||
@@ -582,19 +579,14 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
|
||||
// Make sure nobody resized the table while we were waiting for lock 0:
|
||||
if (tables != _tables)
|
||||
{
|
||||
// We assume that since the table reference is different, it was already resized (or the budget
|
||||
// was adjusted). If we ever decide to do table shrinking, or replace the table for other reasons,
|
||||
// we will have to revisit this logic.
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute the (approx.) total size. Use an Int64 accumulation variable to avoid an overflow.
|
||||
long approxCount = 0;
|
||||
for (var i = 0; i < tables.CountPerLock.Length; i++)
|
||||
{
|
||||
approxCount += tables.CountPerLock[i];
|
||||
}
|
||||
for (var i = 0; i < tables.CountPerLock.Length; i++) approxCount += tables.CountPerLock[i];
|
||||
|
||||
//
|
||||
// If the bucket array is too empty, double the budget instead of resizing the table
|
||||
@@ -602,10 +594,7 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
if (approxCount < tables.Buckets.Length / 4)
|
||||
{
|
||||
_budget = 2 * _budget;
|
||||
if (_budget < 0)
|
||||
{
|
||||
_budget = int.MaxValue;
|
||||
}
|
||||
if (_budget < 0) _budget = int.MaxValue;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -623,17 +612,11 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
|
||||
// Now, we only need to check odd integers, and find the first that is not divisible
|
||||
// by 3, 5 or 7.
|
||||
while (newLength % 3 == 0 || newLength % 5 == 0 || newLength % 7 == 0)
|
||||
{
|
||||
newLength += 2;
|
||||
}
|
||||
while (newLength % 3 == 0 || newLength % 5 == 0 || newLength % 7 == 0) newLength += 2;
|
||||
|
||||
Debug.Assert(newLength % 2 != 0);
|
||||
|
||||
if (newLength > maxArrayLength)
|
||||
{
|
||||
maximizeTableSize = true;
|
||||
}
|
||||
if (newLength > maxArrayLength) maximizeTableSize = true;
|
||||
}
|
||||
}
|
||||
catch (OverflowException)
|
||||
@@ -662,15 +645,8 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
if (_growLockArray && tables.Locks.Length < MaxLockNumber)
|
||||
{
|
||||
newLocks = new object[tables.Locks.Length * 2];
|
||||
Array.Copy(tables.Locks,
|
||||
0,
|
||||
newLocks,
|
||||
0,
|
||||
tables.Locks.Length);
|
||||
for (var i = tables.Locks.Length; i < newLocks.Length; i++)
|
||||
{
|
||||
newLocks[i] = new();
|
||||
}
|
||||
Array.Copy(tables.Locks, 0, newLocks, 0, tables.Locks.Length);
|
||||
for (var i = tables.Locks.Length; i < newLocks.Length; i++) newLocks[i] = new();
|
||||
}
|
||||
|
||||
var newBuckets = new Node[newLength];
|
||||
@@ -718,10 +694,8 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
var elems = this.Where(predicate);
|
||||
var removed = 0;
|
||||
foreach (var elem in elems)
|
||||
{
|
||||
if (this.TryRemove(elem))
|
||||
if (TryRemove(elem))
|
||||
removed++;
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
@@ -751,10 +725,7 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
locksAcquired++;
|
||||
}
|
||||
if (lockTaken) locksAcquired++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -763,24 +734,19 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
{
|
||||
Debug.Assert(fromInclusive <= toExclusive);
|
||||
|
||||
for (var i = fromInclusive; i < toExclusive; i++)
|
||||
{
|
||||
Monitor.Exit(_tables.Locks[i]);
|
||||
}
|
||||
for (var i = fromInclusive; i < toExclusive; i++) Monitor.Exit(_tables.Locks[i]);
|
||||
}
|
||||
|
||||
private void CopyToItems(T[] array, int index)
|
||||
{
|
||||
var buckets = _tables.Buckets;
|
||||
for (var i = 0; i < buckets.Length; i++)
|
||||
{
|
||||
for (var current = buckets[i]; current != null; current = current.Next)
|
||||
{
|
||||
array[index] = current.Item;
|
||||
index++; //this should never flow, CopyToItems is only called when there's no overflow risk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class Tables
|
||||
{
|
||||
@@ -799,8 +765,8 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
|
||||
private sealed class Node
|
||||
{
|
||||
public readonly T Item;
|
||||
public readonly int Hashcode;
|
||||
public readonly T Item;
|
||||
|
||||
public volatile Node Next;
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
using System.Collections;
|
||||
#nullable disable
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Collections;
|
||||
|
||||
namespace NadekoBot.Common.Collections;
|
||||
|
||||
@@ -8,7 +8,6 @@ public class IndexedCollection<T> : IList<T>
|
||||
where T : class, IIndexed
|
||||
{
|
||||
public List<T> Source { get; }
|
||||
private readonly object _locker = new();
|
||||
|
||||
public int Count
|
||||
=> Source.Count;
|
||||
@@ -16,8 +15,20 @@ public class IndexedCollection<T> : IList<T>
|
||||
public bool IsReadOnly
|
||||
=> false;
|
||||
|
||||
public int IndexOf([NotNull] T item)
|
||||
=> item.Index;
|
||||
public virtual T this[int index]
|
||||
{
|
||||
get => Source[index];
|
||||
set
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
value.Index = index;
|
||||
Source[index] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly object _locker = new();
|
||||
|
||||
public IndexedCollection()
|
||||
=> Source = new();
|
||||
@@ -31,23 +42,8 @@ public class IndexedCollection<T> : IList<T>
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateIndexes()
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
for (var i = 0; i < Source.Count; i++)
|
||||
{
|
||||
if (Source[i].Index != i)
|
||||
Source[i].Index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator List<T>(IndexedCollection<T> x)
|
||||
=> x.Source;
|
||||
|
||||
public List<T> ToList()
|
||||
=> Source.ToList();
|
||||
public int IndexOf([NotNull] T item)
|
||||
=> item.Index;
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
=> Source.GetEnumerator();
|
||||
@@ -95,10 +91,8 @@ public class IndexedCollection<T> : IList<T>
|
||||
if (Source.Remove(item))
|
||||
{
|
||||
for (var i = 0; i < Source.Count; i++)
|
||||
{
|
||||
if (Source[i].Index != i)
|
||||
Source[i].Index = i;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -112,10 +106,7 @@ public class IndexedCollection<T> : IList<T>
|
||||
lock (_locker)
|
||||
{
|
||||
Source.Insert(index, item);
|
||||
for (var i = index; i < Source.Count; i++)
|
||||
{
|
||||
Source[i].Index = i;
|
||||
}
|
||||
for (var i = index; i < Source.Count; i++) Source[i].Index = i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,23 +115,23 @@ public class IndexedCollection<T> : IList<T>
|
||||
lock (_locker)
|
||||
{
|
||||
Source.RemoveAt(index);
|
||||
for (var i = index; i < Source.Count; i++)
|
||||
{
|
||||
Source[i].Index = i;
|
||||
}
|
||||
for (var i = index; i < Source.Count; i++) Source[i].Index = i;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual T this[int index]
|
||||
{
|
||||
get => Source[index];
|
||||
set
|
||||
public void UpdateIndexes()
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
value.Index = index;
|
||||
Source[index] = value;
|
||||
}
|
||||
for (var i = 0; i < Source.Count; i++)
|
||||
if (Source[i].Index != i)
|
||||
Source[i].Index = i;
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator List<T>(IndexedCollection<T> x)
|
||||
=> x.Source;
|
||||
|
||||
public List<T> ToList()
|
||||
=> Source.ToList();
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public class CommandData
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#nullable disable
|
||||
using System.Globalization;
|
||||
using Cloneable;
|
||||
using NadekoBot.Common.Yml;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using System.Globalization;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
@@ -19,16 +19,14 @@ next to the response. The color depends whether the command
|
||||
is completed, errored or in progress (pending)
|
||||
Color settings below are for the color of those lines.
|
||||
To get color's hex, you can go here https://htmlcolorcodes.com/
|
||||
and copy the hex code fo your selected color (marked as #)"
|
||||
)]
|
||||
and copy the hex code fo your selected color (marked as #)")]
|
||||
public ColorConfig Color { get; set; }
|
||||
|
||||
[Comment("Default bot language. It has to be in the list of supported languages (.langli)")]
|
||||
public CultureInfo DefaultLocale { get; set; }
|
||||
|
||||
[Comment(@"Style in which executed commands will show up in the console.
|
||||
Allowed values: Simple, Normal, None"
|
||||
)]
|
||||
Allowed values: Simple, Normal, None")]
|
||||
public ConsoleOutputType ConsoleOutputType { get; set; }
|
||||
|
||||
// [Comment(@"For what kind of updates will the bot check.
|
||||
@@ -43,21 +41,18 @@ Allowed values: Simple, Normal, None"
|
||||
|
||||
[Comment(
|
||||
@"Do you want the message to be forwarded only to the first owner specified in the list of owners (in creds.yml),
|
||||
or all owners? (this might cause the bot to lag if there's a lot of owners specified)"
|
||||
)]
|
||||
or all owners? (this might cause the bot to lag if there's a lot of owners specified)")]
|
||||
public bool ForwardToAllOwners { get; set; }
|
||||
|
||||
[Comment(@"When a user DMs the bot with a message which is not a command
|
||||
they will receive this message. Leave empty for no response. The string which will be sent whenever someone DMs the bot.
|
||||
Supports embeds. How it looks: https://puu.sh/B0BLV.png"
|
||||
)]
|
||||
Supports embeds. How it looks: https://puu.sh/B0BLV.png")]
|
||||
[YamlMember(ScalarStyle = ScalarStyle.Literal)]
|
||||
public string DmHelpText { get; set; }
|
||||
|
||||
[Comment(@"Only users who send a DM to the bot containing one of the specified words will get a DmHelpText response.
|
||||
Case insensitive.
|
||||
Leave empty to reply with DmHelpText to every DM."
|
||||
)]
|
||||
Leave empty to reply with DmHelpText to every DM.")]
|
||||
public List<string> DmHelpTextKeywords { get; set; }
|
||||
|
||||
[Comment(@"This is the response for the .h command")]
|
||||
@@ -78,29 +73,14 @@ Keep in mind this might break some of your embeds - for example if you have %use
|
||||
it will become invalid, as it will resolve to a list of avatars of grouped users.
|
||||
note: This setting is primarily used if you're afraid of raids, or you're running medium/large bots where some
|
||||
servers might get hundreds of people join at once. This is used to prevent the bot from getting ratelimited,
|
||||
and (slightly) reduce the greet spam in those servers."
|
||||
)]
|
||||
and (slightly) reduce the greet spam in those servers.")]
|
||||
public bool GroupGreets { get; set; }
|
||||
|
||||
[Comment(@"Whether the bot will rotate through all specified statuses.
|
||||
This setting can be changed via .rots command.
|
||||
See RotatingStatuses submodule in Administration."
|
||||
)]
|
||||
See RotatingStatuses submodule in Administration.")]
|
||||
public bool RotateStatuses { get; set; }
|
||||
|
||||
// [Comment(@"Whether the prefix will be a suffix, or prefix.
|
||||
// For example, if your prefix is ! you will run a command called 'cash' by typing either
|
||||
// '!cash @Someone' if your prefixIsSuffix: false or
|
||||
// 'cash @Someone!' if your prefixIsSuffix: true")]
|
||||
// public bool PrefixIsSuffix { get; set; }
|
||||
|
||||
// public string Prefixed(string text) => PrefixIsSuffix
|
||||
// ? text + Prefix
|
||||
// : Prefix + text;
|
||||
|
||||
public string Prefixed(string text)
|
||||
=> Prefix + text;
|
||||
|
||||
public BotConfig()
|
||||
{
|
||||
var color = new ColorConfig();
|
||||
@@ -149,6 +129,19 @@ See RotatingStatuses submodule in Administration."
|
||||
"can you do"
|
||||
};
|
||||
}
|
||||
|
||||
// [Comment(@"Whether the prefix will be a suffix, or prefix.
|
||||
// For example, if your prefix is ! you will run a command called 'cash' by typing either
|
||||
// '!cash @Someone' if your prefixIsSuffix: false or
|
||||
// 'cash @Someone!' if your prefixIsSuffix: true")]
|
||||
// public bool PrefixIsSuffix { get; set; }
|
||||
|
||||
// public string Prefixed(string text) => PrefixIsSuffix
|
||||
// ? text + Prefix
|
||||
// : Prefix + text;
|
||||
|
||||
public string Prefixed(string text)
|
||||
=> Prefix + text;
|
||||
}
|
||||
|
||||
[Cloneable]
|
||||
@@ -188,5 +181,5 @@ public enum ConsoleOutputType
|
||||
{
|
||||
Normal = 0,
|
||||
Simple = 1,
|
||||
None = 2,
|
||||
None = 2
|
||||
}
|
@@ -9,7 +9,7 @@ public interface IConfigSeria
|
||||
/// Serialize the object to string
|
||||
/// </summary>
|
||||
public string Serialize<T>(T obj)
|
||||
where T: notnull;
|
||||
where T : notnull;
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize string data into an object of the specified type
|
||||
|
@@ -5,33 +5,6 @@ namespace NadekoBot.Common;
|
||||
|
||||
public sealed class Creds : IBotCredentials
|
||||
{
|
||||
public Creds()
|
||||
{
|
||||
Version = 1;
|
||||
Token = string.Empty;
|
||||
OwnerIds = new List<ulong>();
|
||||
TotalShards = 1;
|
||||
GoogleApiKey = string.Empty;
|
||||
Votes = new(string.Empty,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
string.Empty
|
||||
);
|
||||
Patreon = new(string.Empty,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
string.Empty
|
||||
);
|
||||
BotListToken = string.Empty;
|
||||
CleverbotApiKey = string.Empty;
|
||||
RedisOptions = "localhost:6379,syncTimeout=30000,responseTimeout=30000,allowAdmin=true,password=";
|
||||
Db = new() { Type = "sqlite", ConnectionString = "Data Source=data/NadekoBot.db" };
|
||||
|
||||
CoordinatorUrl = "http://localhost:3442";
|
||||
|
||||
RestartCommand = new();
|
||||
}
|
||||
|
||||
[Comment(@"DO NOT CHANGE")]
|
||||
public int Version { get; set; }
|
||||
|
||||
@@ -39,28 +12,24 @@ public sealed class Creds : IBotCredentials
|
||||
public string Token { get; set; }
|
||||
|
||||
[Comment(@"List of Ids of the users who have bot owner permissions
|
||||
**DO NOT ADD PEOPLE YOU DON'T TRUST**"
|
||||
)]
|
||||
**DO NOT ADD PEOPLE YOU DON'T TRUST**")]
|
||||
public ICollection<ulong> OwnerIds { get; set; }
|
||||
|
||||
[Comment(@"The number of shards that the bot will running on.
|
||||
Leave at 1 if you don't know what you're doing."
|
||||
)]
|
||||
Leave at 1 if you don't know what you're doing.")]
|
||||
public int TotalShards { get; set; }
|
||||
|
||||
[Comment(
|
||||
@"Login to https://console.cloud.google.com, create a new project, go to APIs & Services -> Library -> YouTube Data API and enable it.
|
||||
Then, go to APIs and Services -> Credentials and click Create credentials -> API key.
|
||||
Used only for Youtube Data Api (at the moment)."
|
||||
)]
|
||||
Used only for Youtube Data Api (at the moment).")]
|
||||
public string GoogleApiKey { get; set; }
|
||||
|
||||
[Comment(@"Settings for voting system for discordbots. Meant for use on global Nadeko.")]
|
||||
public VotesSettings Votes { get; set; }
|
||||
|
||||
[Comment(@"Patreon auto reward system settings.
|
||||
go to https://www.patreon.com/portal -> my clients -> create client"
|
||||
)]
|
||||
go to https://www.patreon.com/portal -> my clients -> create client")]
|
||||
public PatreonSettings Patreon { get; set; }
|
||||
|
||||
[Comment(@"Api key for sending stats to DiscordBotList.")]
|
||||
@@ -76,27 +45,23 @@ go to https://www.patreon.com/portal -> my clients -> create client"
|
||||
public DbOptions Db { get; set; }
|
||||
|
||||
[Comment(@"Address and port of the coordinator endpoint. Leave empty for default.
|
||||
Change only if you've changed the coordinator address or port."
|
||||
)]
|
||||
Change only if you've changed the coordinator address or port.")]
|
||||
public string CoordinatorUrl { get; set; }
|
||||
|
||||
[Comment(@"Api key obtained on https://rapidapi.com (go to MyApps -> Add New App -> Enter Name -> Application key)"
|
||||
)]
|
||||
[Comment(
|
||||
@"Api key obtained on https://rapidapi.com (go to MyApps -> Add New App -> Enter Name -> Application key)")]
|
||||
public string RapidApiKey { get; set; }
|
||||
|
||||
[Comment(@"https://locationiq.com api key (register and you will receive the token in the email).
|
||||
Used only for .time command."
|
||||
)]
|
||||
Used only for .time command.")]
|
||||
public string LocationIqApiKey { get; set; }
|
||||
|
||||
[Comment(@"https://timezonedb.com api key (register and you will receive the token in the email).
|
||||
Used only for .time command"
|
||||
)]
|
||||
Used only for .time command")]
|
||||
public string TimezoneDbApiKey { get; set; }
|
||||
|
||||
[Comment(@"https://pro.coinmarketcap.com/account/ api key. There is a free plan for personal use.
|
||||
Used for cryptocurrency related commands."
|
||||
)]
|
||||
Used for cryptocurrency related commands.")]
|
||||
public string CoinmarketcapApiKey { get; set; }
|
||||
|
||||
[Comment(@"Api key used for Osu related commands. Obtain this key at https://osu.ppy.sh/p/api")]
|
||||
@@ -112,10 +77,28 @@ Linux default
|
||||
args: ""NadekoBot.dll -- {0}""
|
||||
Windows default
|
||||
cmd: NadekoBot.exe
|
||||
args: {0}"
|
||||
)]
|
||||
args: {0}")]
|
||||
public RestartConfig RestartCommand { get; set; }
|
||||
|
||||
public Creds()
|
||||
{
|
||||
Version = 1;
|
||||
Token = string.Empty;
|
||||
OwnerIds = new List<ulong>();
|
||||
TotalShards = 1;
|
||||
GoogleApiKey = string.Empty;
|
||||
Votes = new(string.Empty, string.Empty, string.Empty, string.Empty);
|
||||
Patreon = new(string.Empty, string.Empty, string.Empty, string.Empty);
|
||||
BotListToken = string.Empty;
|
||||
CleverbotApiKey = string.Empty;
|
||||
RedisOptions = "localhost:6379,syncTimeout=30000,responseTimeout=30000,allowAdmin=true,password=";
|
||||
Db = new() { Type = "sqlite", ConnectionString = "Data Source=data/NadekoBot.db" };
|
||||
|
||||
CoordinatorUrl = "http://localhost:3442";
|
||||
|
||||
RestartCommand = new();
|
||||
}
|
||||
|
||||
|
||||
public class DbOptions
|
||||
{
|
||||
@@ -134,8 +117,7 @@ Windows default
|
||||
public string ClientSecret { get; set; }
|
||||
|
||||
[Comment(
|
||||
@"Campaign ID of your patreon page. Go to your patreon page (make sure you're logged in) and type ""prompt('Campaign ID', window.patreon.bootstrap.creator.data.id);"" in the console. (ctrl + shift + i)"
|
||||
)]
|
||||
@"Campaign ID of your patreon page. Go to your patreon page (make sure you're logged in) and type ""prompt('Campaign ID', window.patreon.bootstrap.creator.data.id);"" in the console. (ctrl + shift + i)")]
|
||||
public string CampaignId { get; set; }
|
||||
|
||||
public PatreonSettings(
|
||||
@@ -159,24 +141,20 @@ Windows default
|
||||
{
|
||||
[Comment(@"top.gg votes service url
|
||||
This is the url of your instance of the NadekoBot.Votes api
|
||||
Example: https://votes.my.cool.bot.com"
|
||||
)]
|
||||
Example: https://votes.my.cool.bot.com")]
|
||||
public string TopggServiceUrl { get; set; }
|
||||
|
||||
[Comment(@"Authorization header value sent to the TopGG service url with each request
|
||||
This should be equivalent to the TopggKey in your NadekoBot.Votes api appsettings.json file"
|
||||
)]
|
||||
This should be equivalent to the TopggKey in your NadekoBot.Votes api appsettings.json file")]
|
||||
public string TopggKey { get; set; }
|
||||
|
||||
[Comment(@"discords.com votes service url
|
||||
This is the url of your instance of the NadekoBot.Votes api
|
||||
Example: https://votes.my.cool.bot.com"
|
||||
)]
|
||||
Example: https://votes.my.cool.bot.com")]
|
||||
public string DiscordsServiceUrl { get; set; }
|
||||
|
||||
[Comment(@"Authorization header value sent to the Discords service url with each request
|
||||
This should be equivalent to the DiscordsKey in your NadekoBot.Votes api appsettings.json file"
|
||||
)]
|
||||
This should be equivalent to the DiscordsKey in your NadekoBot.Votes api appsettings.json file")]
|
||||
public string DiscordsKey { get; set; }
|
||||
|
||||
public VotesSettings()
|
||||
@@ -229,14 +207,14 @@ This should be equivalent to the DiscordsKey in your NadekoBot.Votes api appsett
|
||||
|
||||
public class RestartConfig
|
||||
{
|
||||
public RestartConfig(string cmd, string args)
|
||||
{
|
||||
this.Cmd = cmd;
|
||||
this.Args = args;
|
||||
}
|
||||
|
||||
public string Cmd { get; set; }
|
||||
public string Args { get; set; }
|
||||
|
||||
public RestartConfig(string cmd, string args)
|
||||
{
|
||||
Cmd = cmd;
|
||||
Args = args;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -21,8 +21,7 @@ public class DownloadTracker : INService
|
||||
// download once per hour at most
|
||||
var added = LastDownloads.AddOrUpdate(guild.Id,
|
||||
now,
|
||||
(_, old) => now - old > TimeSpan.FromHours(1) ? now : old
|
||||
);
|
||||
(_, old) => now - old > TimeSpan.FromHours(1) ? now : old);
|
||||
|
||||
// means that this entry was just added - download the users
|
||||
if (added == now)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public static class Helpers
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot;
|
||||
|
||||
public interface IBotCredentials
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public interface ICloneable<T>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot;
|
||||
|
||||
public interface IEmbedBuilder
|
||||
@@ -19,5 +19,5 @@ public enum EmbedColor
|
||||
{
|
||||
Ok,
|
||||
Pending,
|
||||
Error,
|
||||
Error
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public interface INadekoCommandOptions
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using NadekoBot.Common.Yml;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
@@ -10,8 +10,8 @@ namespace NadekoBot.Common;
|
||||
public readonly struct kwum : IEquatable<kwum>
|
||||
#pragma warning restore IDE1006
|
||||
{
|
||||
private readonly int _value;
|
||||
private const string VALID_CHARACTERS = "23456789abcdefghijkmnpqrstuvwxyz";
|
||||
private readonly int _value;
|
||||
|
||||
public kwum(int num)
|
||||
=> _value = num;
|
||||
@@ -24,10 +24,6 @@ public readonly struct kwum : IEquatable<kwum>
|
||||
_value = InternalCharToValue(c);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int InternalCharToValue(in char c)
|
||||
=> VALID_CHARACTERS.IndexOf(c);
|
||||
|
||||
public kwum(in ReadOnlySpan<char> input)
|
||||
{
|
||||
_value = 0;
|
||||
@@ -41,6 +37,10 @@ public readonly struct kwum : IEquatable<kwum>
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int InternalCharToValue(in char c)
|
||||
=> VALID_CHARACTERS.IndexOf(c);
|
||||
|
||||
public static bool TryParse(in ReadOnlySpan<char> input, out kwum value)
|
||||
{
|
||||
value = default;
|
||||
|
@@ -1,15 +1,11 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using CommandLine;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public class LbOpts : INadekoCommandOptions
|
||||
{
|
||||
[Option('c',
|
||||
"clean",
|
||||
Default = false,
|
||||
HelpText = "Only show users who are on the server."
|
||||
)]
|
||||
[Option('c', "clean", Default = false, HelpText = "Only show users who are on the server.")]
|
||||
public bool Clean { get; set; }
|
||||
|
||||
public void NormalizeOptions()
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
@@ -16,23 +16,20 @@ public class LoginErrorHandler
|
||||
switch (ex.HttpCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
Log.Error("Your bot token is wrong.\n" +
|
||||
"You can find the bot token under the Bot tab in the developer page.\n" +
|
||||
"Fix your token in the credentials file and restart the bot"
|
||||
);
|
||||
Log.Error("Your bot token is wrong.\n"
|
||||
+ "You can find the bot token under the Bot tab in the developer page.\n"
|
||||
+ "Fix your token in the credentials file and restart the bot");
|
||||
break;
|
||||
|
||||
case HttpStatusCode.BadRequest:
|
||||
Log.Error("Something has been incorrectly formatted in your credentials file.\n" +
|
||||
"Use the JSON Guide as reference to fix it and restart the bot"
|
||||
);
|
||||
Log.Error("Something has been incorrectly formatted in your credentials file.\n"
|
||||
+ "Use the JSON Guide as reference to fix it and restart the bot");
|
||||
Log.Error("If you are on Linux, make sure Redis is installed and running");
|
||||
break;
|
||||
|
||||
case HttpStatusCode.RequestTimeout:
|
||||
Log.Error("The request timed out. Make sure you have no external program blocking the bot " +
|
||||
"from connecting to the internet"
|
||||
);
|
||||
Log.Error("The request timed out. Make sure you have no external program blocking the bot "
|
||||
+ "from connecting to the internet");
|
||||
break;
|
||||
|
||||
case HttpStatusCode.ServiceUnavailable:
|
||||
@@ -41,9 +38,8 @@ public class LoginErrorHandler
|
||||
break;
|
||||
|
||||
case HttpStatusCode.TooManyRequests:
|
||||
Log.Error("Your bot has been ratelimited by Discord. Please, try again later.\n" +
|
||||
"Global ratelimits usually last for an hour"
|
||||
);
|
||||
Log.Error("Your bot has been ratelimited by Discord. Please, try again later.\n"
|
||||
+ "Global ratelimits usually last for an hour");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@@ -1,13 +1,13 @@
|
||||
#nullable disable
|
||||
using System.Globalization;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace NadekoBot.Modules;
|
||||
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.Default |
|
||||
ImplicitUseTargetFlags.WithInheritors |
|
||||
ImplicitUseTargetFlags.WithMembers
|
||||
)]
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.Default
|
||||
| ImplicitUseTargetFlags.WithInheritors
|
||||
| ImplicitUseTargetFlags.WithMembers)]
|
||||
public abstract class NadekoModule : ModuleBase
|
||||
{
|
||||
protected CultureInfo Culture { get; set; }
|
||||
@@ -36,12 +36,7 @@ public abstract class NadekoModule : ModuleBase
|
||||
string error,
|
||||
string url = null,
|
||||
string footer = null)
|
||||
=> ctx.Channel.SendErrorAsync(_eb,
|
||||
title,
|
||||
error,
|
||||
url,
|
||||
footer
|
||||
);
|
||||
=> ctx.Channel.SendErrorAsync(_eb, title, error, url, footer);
|
||||
|
||||
public Task<IUserMessage> SendConfirmAsync(string text)
|
||||
=> ctx.Channel.SendConfirmAsync(_eb, text);
|
||||
@@ -51,12 +46,7 @@ public abstract class NadekoModule : ModuleBase
|
||||
string text,
|
||||
string url = null,
|
||||
string footer = null)
|
||||
=> ctx.Channel.SendConfirmAsync(_eb,
|
||||
title,
|
||||
text,
|
||||
url,
|
||||
footer
|
||||
);
|
||||
=> ctx.Channel.SendConfirmAsync(_eb, title, text, url, footer);
|
||||
|
||||
public Task<IUserMessage> SendPendingAsync(string text)
|
||||
=> ctx.Channel.SendPendingAsync(_eb, text);
|
||||
@@ -81,8 +71,7 @@ public abstract class NadekoModule : ModuleBase
|
||||
|
||||
public async Task<bool> PromptUserConfirmAsync(IEmbedBuilder embed)
|
||||
{
|
||||
embed.WithPendingColor()
|
||||
.WithFooter("yes/no");
|
||||
embed.WithPendingColor().WithFooter("yes/no");
|
||||
|
||||
var msg = await ctx.Channel.EmbedAsync(embed);
|
||||
try
|
||||
@@ -90,11 +79,8 @@ public abstract class NadekoModule : ModuleBase
|
||||
var input = await GetUserInputAsync(ctx.User.Id, ctx.Channel.Id);
|
||||
input = input?.ToUpperInvariant();
|
||||
|
||||
if (input != "YES" &&
|
||||
input != "Y")
|
||||
{
|
||||
if (input != "YES" && input != "Y")
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -113,11 +99,8 @@ public abstract class NadekoModule : ModuleBase
|
||||
{
|
||||
dsc.MessageReceived += MessageReceived;
|
||||
|
||||
if (await Task.WhenAny(userInputTask.Task, Task.Delay(10000)) !=
|
||||
userInputTask.Task)
|
||||
{
|
||||
if (await Task.WhenAny(userInputTask.Task, Task.Delay(10000)) != userInputTask.Task)
|
||||
return null;
|
||||
}
|
||||
|
||||
return await userInputTask.Task;
|
||||
}
|
||||
@@ -130,22 +113,16 @@ public abstract class NadekoModule : ModuleBase
|
||||
{
|
||||
var _ = Task.Run(() =>
|
||||
{
|
||||
if (arg is not SocketUserMessage userMsg ||
|
||||
userMsg.Channel is not ITextChannel ||
|
||||
userMsg.Author.Id != userId ||
|
||||
userMsg.Channel.Id != channelId)
|
||||
{
|
||||
if (arg is not SocketUserMessage userMsg
|
||||
|| userMsg.Channel is not ITextChannel
|
||||
|| userMsg.Author.Id != userId
|
||||
|| userMsg.Channel.Id != channelId)
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (userInputTask.TrySetResult(arg.Content))
|
||||
{
|
||||
userMsg.DeleteAfter(1);
|
||||
}
|
||||
if (userInputTask.TrySetResult(arg.Content)) userMsg.DeleteAfter(1);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
);
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
@@ -8,7 +8,6 @@ public class NadekoRandom : Random
|
||||
private readonly RandomNumberGenerator _rng;
|
||||
|
||||
public NadekoRandom()
|
||||
: base()
|
||||
=> _rng = RandomNumberGenerator.Create();
|
||||
|
||||
public override int Next()
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
@@ -7,7 +7,10 @@ namespace NadekoBot.Common;
|
||||
[SuppressMessage("Style", "IDE0022:Use expression body for methods")]
|
||||
public sealed class NoPublicBotAttribute : PreconditionAttribute
|
||||
{
|
||||
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
|
||||
public override Task<PreconditionResult> CheckPermissionsAsync(
|
||||
ICommandContext context,
|
||||
CommandInfo command,
|
||||
IServiceProvider services)
|
||||
{
|
||||
#if GLOBAL_NADEKO
|
||||
return Task.FromResult(PreconditionResult.FromError("Not available on the public bot. To learn how to selfhost a private bot, click [here](https://nadekobot.readthedocs.io/en/latest/)."));
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using CommandLine;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
@@ -15,8 +15,7 @@ public static class OptionsParser
|
||||
using var p = new Parser(x =>
|
||||
{
|
||||
x.HelpWriter = null;
|
||||
}
|
||||
);
|
||||
});
|
||||
var res = p.ParseArguments<T>(args);
|
||||
options = res.MapResult(x => x, x => options);
|
||||
options.NormalizeOptions();
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public class OsuMapData
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public static class PlatformHelper
|
||||
@@ -13,8 +13,7 @@ public static class PlatformHelper
|
||||
get
|
||||
{
|
||||
var now = Environment.TickCount;
|
||||
if (processorCount == 0 ||
|
||||
now - lastProcessorCountRefreshTicks >= PROCESSOR_COUNT_REFRESH_INTERVAL_MS)
|
||||
if (processorCount == 0 || now - lastProcessorCountRefreshTicks >= PROCESSOR_COUNT_REFRESH_INTERVAL_MS)
|
||||
{
|
||||
processorCount = Environment.ProcessorCount;
|
||||
lastProcessorCountRefreshTicks = now;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common.Pokemon;
|
||||
|
||||
public class PokemonNameId
|
||||
|
@@ -1,10 +1,24 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NadekoBot.Common.Pokemon;
|
||||
|
||||
public class SearchPokemon
|
||||
{
|
||||
[JsonProperty("num")]
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Species { get; set; }
|
||||
public string[] Types { get; set; }
|
||||
public GenderRatioClass GenderRatio { get; set; }
|
||||
public BaseStatsClass BaseStats { get; set; }
|
||||
public Dictionary<string, string> Abilities { get; set; }
|
||||
public float HeightM { get; set; }
|
||||
public float WeightKg { get; set; }
|
||||
public string Color { get; set; }
|
||||
public string[] Evos { get; set; }
|
||||
public string[] EggGroups { get; set; }
|
||||
|
||||
public class GenderRatioClass
|
||||
{
|
||||
public float M { get; set; }
|
||||
@@ -24,18 +38,4 @@ public class SearchPokemon
|
||||
=> $@"💚**HP:** {Hp,-4} ⚔**ATK:** {Atk,-4} 🛡**DEF:** {Def,-4}
|
||||
✨**SPA:** {Spa,-4} 🎇**SPD:** {Spd,-4} 💨**SPE:** {Spe,-4}";
|
||||
}
|
||||
|
||||
[JsonProperty("num")]
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Species { get; set; }
|
||||
public string[] Types { get; set; }
|
||||
public GenderRatioClass GenderRatio { get; set; }
|
||||
public BaseStatsClass BaseStats { get; set; }
|
||||
public Dictionary<string, string> Abilities { get; set; }
|
||||
public float HeightM { get; set; }
|
||||
public float WeightKg { get; set; }
|
||||
public string Color { get; set; }
|
||||
public string[] Evos { get; set; }
|
||||
public string[] EggGroups { get; set; }
|
||||
}
|
@@ -33,15 +33,10 @@ public class EventPubSub : IPubSub
|
||||
lock (_locker)
|
||||
{
|
||||
if (_actions.TryGetValue(key.Key, out var actions))
|
||||
{
|
||||
// if this class ever gets used, this needs to be properly implemented
|
||||
// 1. ignore all valuetasks which are completed
|
||||
// 2. run all other tasks in parallel
|
||||
return actions
|
||||
.SelectMany(kvp => kvp.Value)
|
||||
.Select(action => action(data).AsTask())
|
||||
.WhenAll();
|
||||
}
|
||||
return actions.SelectMany(kvp => kvp.Value).Select(action => action(data).AsTask()).WhenAll();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -53,7 +48,6 @@ public class EventPubSub : IPubSub
|
||||
{
|
||||
// get subscriptions for this action
|
||||
if (_actions.TryGetValue(key.Key, out var actions))
|
||||
{
|
||||
// get subscriptions which have the same action hash code
|
||||
// note: having this as a list allows for multiple subscriptions of
|
||||
// the same insance's/static method
|
||||
@@ -71,11 +65,7 @@ public class EventPubSub : IPubSub
|
||||
// if our dictionary has no more elements after
|
||||
// removing the entry
|
||||
// it's safe to remove it from the key's subscriptions
|
||||
if (actions.Count == 0)
|
||||
{
|
||||
_actions.Remove(key.Key);
|
||||
}
|
||||
}
|
||||
if (actions.Count == 0) _actions.Remove(key.Key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
using System.Text.Json;
|
||||
using NadekoBot.Common.JsonConverters;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
@@ -7,7 +7,7 @@ public class JsonSeria : ISeria
|
||||
{
|
||||
private readonly JsonSerializerOptions _serializerOptions = new()
|
||||
{
|
||||
Converters = { new Rgba32Converter(), new CultureInfoConverter(), }
|
||||
Converters = { new Rgba32Converter(), new CultureInfoConverter() }
|
||||
};
|
||||
|
||||
public byte[] Serialize<T>(T data)
|
||||
|
@@ -4,9 +4,9 @@ namespace NadekoBot.Common;
|
||||
|
||||
public sealed class RedisPubSub : IPubSub
|
||||
{
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly ConnectionMultiplexer _multi;
|
||||
private readonly ISeria _serializer;
|
||||
private readonly IBotCredentials _creds;
|
||||
|
||||
public RedisPubSub(ConnectionMultiplexer multi, ISeria serializer, IBotCredentials creds)
|
||||
{
|
||||
|
@@ -1,19 +1,18 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using NadekoBot.Common.Yml;
|
||||
using NadekoBot.Common.Configs;
|
||||
using NadekoBot.Common.Yml;
|
||||
using System.Text.RegularExpressions;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public class YamlSeria : IConfigSeria
|
||||
{
|
||||
private readonly ISerializer _serializer;
|
||||
private readonly IDeserializer _deserializer;
|
||||
|
||||
private static readonly Regex _codePointRegex =
|
||||
new(@"(\\U(?<code>[a-zA-Z0-9]{8})|\\u(?<code>[a-zA-Z0-9]{4})|\\x(?<code>[a-zA-Z0-9]{2}))",
|
||||
RegexOptions.Compiled
|
||||
);
|
||||
RegexOptions.Compiled);
|
||||
|
||||
private readonly IDeserializer _deserializer;
|
||||
private readonly ISerializer _serializer;
|
||||
|
||||
public YamlSeria()
|
||||
{
|
||||
@@ -22,7 +21,7 @@ public class YamlSeria : IConfigSeria
|
||||
}
|
||||
|
||||
public string Serialize<T>(T obj)
|
||||
where T: notnull
|
||||
where T : notnull
|
||||
{
|
||||
var escapedOutput = _serializer.Serialize(obj);
|
||||
var output = _codePointRegex.Replace(escapedOutput,
|
||||
@@ -31,8 +30,7 @@ public class YamlSeria : IConfigSeria
|
||||
var str = me.Groups["code"].Value;
|
||||
var newString = YamlHelper.UnescapeUnicodeCodePoint(str);
|
||||
return newString;
|
||||
}
|
||||
);
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@@ -7,11 +7,11 @@ namespace NadekoBot.Common;
|
||||
public class ReplacementBuilder
|
||||
{
|
||||
private static readonly Regex _rngRegex = new("%rng(?:(?<from>(?:-)?\\d+)-(?<to>(?:-)?\\d+))?%",
|
||||
RegexOptions.Compiled
|
||||
);
|
||||
RegexOptions.Compiled);
|
||||
|
||||
private readonly ConcurrentDictionary<Regex, Func<Match, string>> _regex = new();
|
||||
|
||||
private readonly ConcurrentDictionary<string, Func<string>> _reps = new();
|
||||
private readonly ConcurrentDictionary<Regex, Func<Match, string>> _regex = new();
|
||||
|
||||
public ReplacementBuilder()
|
||||
=> WithRngRegex();
|
||||
@@ -21,14 +21,10 @@ public class ReplacementBuilder
|
||||
IMessageChannel ch,
|
||||
SocketGuild g,
|
||||
DiscordSocketClient client)
|
||||
=> this.WithUser(usr).WithChannel(ch).WithServer(client, g).WithClient(client);
|
||||
=> WithUser(usr).WithChannel(ch).WithServer(client, g).WithClient(client);
|
||||
|
||||
public ReplacementBuilder WithDefault(ICommandContext ctx)
|
||||
=> WithDefault(ctx.User,
|
||||
ctx.Channel,
|
||||
ctx.Guild as SocketGuild,
|
||||
(DiscordSocketClient)ctx.Client
|
||||
);
|
||||
=> WithDefault(ctx.User, ctx.Channel, ctx.Guild as SocketGuild, (DiscordSocketClient)ctx.Client);
|
||||
|
||||
public ReplacementBuilder WithMention(DiscordSocketClient client)
|
||||
{
|
||||
@@ -45,8 +41,7 @@ public class ReplacementBuilder
|
||||
_reps.TryAdd("%bot.name%", () => client.CurrentUser.Username);
|
||||
_reps.TryAdd("%bot.fullname%", () => client.CurrentUser.ToString());
|
||||
_reps.TryAdd("%bot.time%",
|
||||
() => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials())
|
||||
);
|
||||
() => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()));
|
||||
_reps.TryAdd("%bot.discrim%", () => client.CurrentUser.Discriminator);
|
||||
_reps.TryAdd("%bot.id%", () => client.CurrentUser.Id.ToString());
|
||||
_reps.TryAdd("%bot.avatar%", () => client.CurrentUser.RealAvatarUrl()?.ToString());
|
||||
@@ -68,15 +63,12 @@ public class ReplacementBuilder
|
||||
{
|
||||
var to = TimeZoneInfo.Local;
|
||||
if (g != null)
|
||||
{
|
||||
if (GuildTimezoneService.AllServices.TryGetValue(client.CurrentUser.Id, out var tz))
|
||||
to = tz.GetTimeZoneOrDefault(g.Id) ?? TimeZoneInfo.Local;
|
||||
}
|
||||
|
||||
return TimeZoneInfo.ConvertTime(DateTime.UtcNow, TimeZoneInfo.Utc, to).ToString("HH:mm ") +
|
||||
to.StandardName.GetInitials();
|
||||
}
|
||||
);
|
||||
return TimeZoneInfo.ConvertTime(DateTime.UtcNow, TimeZoneInfo.Utc, to).ToString("HH:mm ")
|
||||
+ to.StandardName.GetInitials();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -107,17 +99,14 @@ public class ReplacementBuilder
|
||||
_reps.TryAdd("%user.avatar%", () => string.Join(" ", users.Select(user => user.RealAvatarUrl()?.ToString())));
|
||||
_reps.TryAdd("%user.id%", () => string.Join(" ", users.Select(user => user.Id.ToString())));
|
||||
_reps.TryAdd("%user.created_time%",
|
||||
() => string.Join(" ", users.Select(user => user.CreatedAt.ToString("HH:mm")))
|
||||
);
|
||||
() => string.Join(" ", users.Select(user => user.CreatedAt.ToString("HH:mm"))));
|
||||
_reps.TryAdd("%user.created_date%",
|
||||
() => string.Join(" ", users.Select(user => user.CreatedAt.ToString("dd.MM.yyyy")))
|
||||
);
|
||||
() => string.Join(" ", users.Select(user => user.CreatedAt.ToString("dd.MM.yyyy"))));
|
||||
_reps.TryAdd("%user.joined_time%",
|
||||
() => string.Join(" ", users.Select(user => (user as IGuildUser)?.JoinedAt?.ToString("HH:mm") ?? "-"))
|
||||
);
|
||||
() => string.Join(" ", users.Select(user => (user as IGuildUser)?.JoinedAt?.ToString("HH:mm") ?? "-")));
|
||||
_reps.TryAdd("%user.joined_date%",
|
||||
() => string.Join(" ", users.Select(user => (user as IGuildUser)?.JoinedAt?.ToString("dd.MM.yyyy") ?? "-"))
|
||||
);
|
||||
() => string.Join(" ",
|
||||
users.Select(user => (user as IGuildUser)?.JoinedAt?.ToString("dd.MM.yyyy") ?? "-")));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -140,16 +129,14 @@ public class ReplacementBuilder
|
||||
if (!int.TryParse(match.Groups["to"].ToString(), out var to))
|
||||
to = 0;
|
||||
|
||||
if (from == 0 &&
|
||||
to == 0)
|
||||
if (from == 0 && to == 0)
|
||||
return rng.Next(0, 11).ToString();
|
||||
|
||||
if (from >= to)
|
||||
return string.Empty;
|
||||
|
||||
return rng.Next(from, to + 1).ToString();
|
||||
}
|
||||
);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -165,12 +152,8 @@ public class ReplacementBuilder
|
||||
public ReplacementBuilder WithProviders(IEnumerable<IPlaceholderProvider> phProviders)
|
||||
{
|
||||
foreach (var provider in phProviders)
|
||||
{
|
||||
foreach (var ovr in provider.GetPlaceholders())
|
||||
{
|
||||
_reps.TryAdd(ovr.Name, ovr.Func);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@@ -1,12 +1,12 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public class Replacer
|
||||
{
|
||||
private readonly IEnumerable<(string Key, Func<string> Text)> _replacements;
|
||||
private readonly IEnumerable<(Regex Regex, Func<Match, string> Replacement)> _regex;
|
||||
private readonly IEnumerable<(string Key, Func<string> Text)> _replacements;
|
||||
|
||||
public Replacer(IEnumerable<(string, Func<string>)> replacements, IEnumerable<(Regex, Func<Match, string>)> regex)
|
||||
{
|
||||
@@ -20,15 +20,10 @@ public class Replacer
|
||||
return input;
|
||||
|
||||
foreach (var (key, text) in _replacements)
|
||||
{
|
||||
if (input.Contains(key))
|
||||
input = input.Replace(key, text(), StringComparison.InvariantCulture);
|
||||
}
|
||||
|
||||
foreach (var item in _regex)
|
||||
{
|
||||
input = item.Regex.Replace(input, m => item.Replacement(m));
|
||||
}
|
||||
foreach (var item in _regex) input = item.Regex.Replace(input, m => item.Replacement(m));
|
||||
|
||||
return input;
|
||||
}
|
||||
@@ -56,12 +51,10 @@ public class Replacer
|
||||
Url = Replace(embedData.Url)
|
||||
};
|
||||
if (embedData.Author != null)
|
||||
{
|
||||
newEmbedData.Author = new()
|
||||
{
|
||||
Name = Replace(embedData.Author.Name), IconUrl = Replace(embedData.Author.IconUrl)
|
||||
};
|
||||
}
|
||||
|
||||
if (embedData.Fields != null)
|
||||
{
|
||||
@@ -79,12 +72,10 @@ public class Replacer
|
||||
}
|
||||
|
||||
if (embedData.Footer != null)
|
||||
{
|
||||
newEmbedData.Footer = new()
|
||||
{
|
||||
Text = Replace(embedData.Footer.Text), IconUrl = Replace(embedData.Footer.IconUrl)
|
||||
};
|
||||
}
|
||||
|
||||
newEmbedData.Color = embedData.Color;
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public struct ShmartNumber : IEquatable<ShmartNumber>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot;
|
||||
|
||||
public sealed record SmartEmbedText : SmartText
|
||||
@@ -17,12 +17,14 @@ public sealed record SmartEmbedText : SmartText
|
||||
public uint Color { get; set; } = 7458112;
|
||||
|
||||
public bool IsValid
|
||||
=> !string.IsNullOrWhiteSpace(Title) || !string.IsNullOrWhiteSpace(Description) ||
|
||||
!string.IsNullOrWhiteSpace(Url) || !string.IsNullOrWhiteSpace(Thumbnail) ||
|
||||
!string.IsNullOrWhiteSpace(Image) ||
|
||||
(Footer != null &&
|
||||
(!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl))) ||
|
||||
Fields is { Length: > 0 };
|
||||
=> !string.IsNullOrWhiteSpace(Title)
|
||||
|| !string.IsNullOrWhiteSpace(Description)
|
||||
|| !string.IsNullOrWhiteSpace(Url)
|
||||
|| !string.IsNullOrWhiteSpace(Thumbnail)
|
||||
|| !string.IsNullOrWhiteSpace(Image)
|
||||
|| (Footer != null
|
||||
&& (!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl)))
|
||||
|| Fields is { Length: > 0 };
|
||||
|
||||
public static SmartEmbedText FromEmbed(IEmbed eb, string plainText = null)
|
||||
{
|
||||
@@ -40,8 +42,10 @@ public sealed record SmartEmbedText : SmartText
|
||||
|
||||
if (eb.Fields.Length > 0)
|
||||
set.Fields = eb.Fields.Select(field
|
||||
=> new SmartTextEmbedField() { Inline = field.Inline, Name = field.Name, Value = field.Value, }
|
||||
)
|
||||
=> new SmartTextEmbedField
|
||||
{
|
||||
Inline = field.Inline, Name = field.Name, Value = field.Value
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
set.Color = eb.Color?.RawValue ?? 0;
|
||||
@@ -58,31 +62,24 @@ public sealed record SmartEmbedText : SmartText
|
||||
if (!string.IsNullOrWhiteSpace(Description))
|
||||
embed.WithDescription(Description);
|
||||
|
||||
if (Url != null &&
|
||||
Uri.IsWellFormedUriString(Url, UriKind.Absolute))
|
||||
if (Url != null && Uri.IsWellFormedUriString(Url, UriKind.Absolute))
|
||||
embed.WithUrl(Url);
|
||||
|
||||
if (Footer != null)
|
||||
{
|
||||
embed.WithFooter(efb =>
|
||||
{
|
||||
efb.WithText(Footer.Text);
|
||||
if (Uri.IsWellFormedUriString(Footer.IconUrl, UriKind.Absolute))
|
||||
efb.WithIconUrl(Footer.IconUrl);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (Thumbnail != null &&
|
||||
Uri.IsWellFormedUriString(Thumbnail, UriKind.Absolute))
|
||||
if (Thumbnail != null && Uri.IsWellFormedUriString(Thumbnail, UriKind.Absolute))
|
||||
embed.WithThumbnailUrl(Thumbnail);
|
||||
|
||||
if (Image != null &&
|
||||
Uri.IsWellFormedUriString(Image, UriKind.Absolute))
|
||||
if (Image != null && Uri.IsWellFormedUriString(Image, UriKind.Absolute))
|
||||
embed.WithImageUrl(Image);
|
||||
|
||||
if (Author != null &&
|
||||
!string.IsNullOrWhiteSpace(Author.Name))
|
||||
if (Author != null && !string.IsNullOrWhiteSpace(Author.Name))
|
||||
{
|
||||
if (!Uri.IsWellFormedUriString(Author.IconUrl, UriKind.Absolute))
|
||||
Author.IconUrl = null;
|
||||
@@ -93,14 +90,9 @@ public sealed record SmartEmbedText : SmartText
|
||||
}
|
||||
|
||||
if (Fields != null)
|
||||
{
|
||||
foreach (var f in Fields)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(f.Name) &&
|
||||
!string.IsNullOrWhiteSpace(f.Value))
|
||||
if (!string.IsNullOrWhiteSpace(f.Name) && !string.IsNullOrWhiteSpace(f.Value))
|
||||
embed.AddField(f.Name, f.Value, f.Inline);
|
||||
}
|
||||
}
|
||||
|
||||
return embed;
|
||||
}
|
||||
@@ -108,12 +100,10 @@ public sealed record SmartEmbedText : SmartText
|
||||
public void NormalizeFields()
|
||||
{
|
||||
if (Fields is { Length: > 0 })
|
||||
{
|
||||
foreach (var f in Fields)
|
||||
{
|
||||
f.Name = f.Name.TrimTo(256);
|
||||
f.Value = f.Value.TrimTo(1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot;
|
||||
|
||||
public sealed record SmartPlainText : SmartText
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NadekoBot;
|
||||
@@ -29,11 +29,8 @@ public abstract record SmartText
|
||||
|
||||
public static SmartText CreateFrom(string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input) ||
|
||||
!input.TrimStart().StartsWith("{"))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input) || !input.TrimStart().StartsWith("{"))
|
||||
return new SmartPlainText(input);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -44,10 +41,7 @@ public abstract record SmartText
|
||||
|
||||
smartEmbedText.NormalizeFields();
|
||||
|
||||
if (!smartEmbedText.IsValid)
|
||||
{
|
||||
return new SmartPlainText(input);
|
||||
}
|
||||
if (!smartEmbedText.IsValid) return new SmartPlainText(input);
|
||||
|
||||
return smartEmbedText;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NadekoBot;
|
||||
@@ -6,7 +6,9 @@ namespace NadekoBot;
|
||||
public class SmartTextEmbedAuthor
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("icon_url")]
|
||||
public string IconUrl { get; set; }
|
||||
|
||||
public string Url { get; set; }
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot;
|
||||
|
||||
public class SmartTextEmbedField
|
||||
|
@@ -8,6 +8,7 @@ namespace NadekoBot;
|
||||
public class SmartTextEmbedFooter
|
||||
{
|
||||
public string Text { get; set; }
|
||||
|
||||
[JsonProperty("icon_url")]
|
||||
public string IconUrl { get; set; }
|
||||
}
|
@@ -1,13 +1,17 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public sealed class ReactionEventWrapper : IDisposable
|
||||
{
|
||||
public IUserMessage Message { get; }
|
||||
public event Action<SocketReaction> OnReactionAdded = delegate { };
|
||||
public event Action<SocketReaction> OnReactionRemoved = delegate { };
|
||||
public event Action OnReactionsCleared = delegate { };
|
||||
|
||||
public IUserMessage Message { get; }
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
private bool disposing;
|
||||
|
||||
public ReactionEventWrapper(DiscordSocketClient client, IUserMessage msg)
|
||||
{
|
||||
Message = msg ?? throw new ArgumentNullException(nameof(msg));
|
||||
@@ -18,6 +22,14 @@ public sealed class ReactionEventWrapper : IDisposable
|
||||
_client.ReactionsCleared += Discord_ReactionsCleared;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposing)
|
||||
return;
|
||||
disposing = true;
|
||||
UnsubAll();
|
||||
}
|
||||
|
||||
private Task Discord_ReactionsCleared(Cacheable<IUserMessage, ulong> msg, Cacheable<IMessageChannel, ulong> channel)
|
||||
{
|
||||
Task.Run(() =>
|
||||
@@ -28,8 +40,7 @@ public sealed class ReactionEventWrapper : IDisposable
|
||||
OnReactionsCleared?.Invoke();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -47,8 +58,7 @@ public sealed class ReactionEventWrapper : IDisposable
|
||||
OnReactionRemoved?.Invoke(reaction);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -68,8 +78,7 @@ public sealed class ReactionEventWrapper : IDisposable
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -83,15 +92,4 @@ public sealed class ReactionEventWrapper : IDisposable
|
||||
OnReactionRemoved = null;
|
||||
OnReactionsCleared = null;
|
||||
}
|
||||
|
||||
private bool disposing = false;
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposing)
|
||||
return;
|
||||
disposing = true;
|
||||
UnsubAll();
|
||||
}
|
||||
}
|
@@ -5,8 +5,8 @@ namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
public sealed class CommandTypeReader : NadekoTypeReader<CommandInfo>
|
||||
{
|
||||
private readonly CommandHandler _handler;
|
||||
private readonly CommandService _cmds;
|
||||
private readonly CommandHandler _handler;
|
||||
|
||||
public CommandTypeReader(CommandHandler handler, CommandService cmds)
|
||||
{
|
||||
@@ -34,8 +34,8 @@ public sealed class CommandTypeReader : NadekoTypeReader<CommandInfo>
|
||||
public sealed class CommandOrCrTypeReader : NadekoTypeReader<CommandOrCrInfo>
|
||||
{
|
||||
private readonly CommandService _cmds;
|
||||
private readonly CustomReactionsService _crs;
|
||||
private readonly CommandHandler _commandHandler;
|
||||
private readonly CustomReactionsService _crs;
|
||||
|
||||
public CommandOrCrTypeReader(CommandService cmds, CustomReactionsService crs, CommandHandler commandHandler)
|
||||
{
|
||||
@@ -49,18 +49,12 @@ public sealed class CommandOrCrTypeReader : NadekoTypeReader<CommandOrCrInfo>
|
||||
input = input.ToUpperInvariant();
|
||||
|
||||
if (_crs.ReactionExists(context.Guild?.Id, input))
|
||||
{
|
||||
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(input, CommandOrCrInfo.Type.Custom));
|
||||
}
|
||||
|
||||
var cmd = await new CommandTypeReader(_commandHandler, _cmds).ReadAsync(context, input);
|
||||
if (cmd.IsSuccess)
|
||||
{
|
||||
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name,
|
||||
CommandOrCrInfo.Type.Normal
|
||||
)
|
||||
);
|
||||
}
|
||||
CommandOrCrInfo.Type.Normal));
|
||||
|
||||
return TypeReaderResult.FromError(CommandError.ParseFailed, "No such command or cr found.");
|
||||
}
|
||||
@@ -71,7 +65,7 @@ public class CommandOrCrInfo
|
||||
public enum Type
|
||||
{
|
||||
Normal,
|
||||
Custom,
|
||||
Custom
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
@@ -82,7 +76,7 @@ public class CommandOrCrInfo
|
||||
|
||||
public CommandOrCrInfo(string input, Type type)
|
||||
{
|
||||
this.Name = input;
|
||||
this.CmdType = type;
|
||||
Name = input;
|
||||
CmdType = type;
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
public sealed class EmoteTypeReader : NadekoTypeReader<Emote>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
@@ -15,9 +15,7 @@ public sealed class GuildDateTimeTypeReader : NadekoTypeReader<GuildDateTime>
|
||||
var gdt = Parse(context.Guild.Id, input);
|
||||
if (gdt is null)
|
||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed,
|
||||
"Input string is in an incorrect format."
|
||||
)
|
||||
);
|
||||
"Input string is in an incorrect format."));
|
||||
|
||||
return Task.FromResult(TypeReaderResult.FromSuccess(gdt));
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
public sealed class GuildTypeReader : NadekoTypeReader<IGuild>
|
||||
@@ -12,12 +12,14 @@ public sealed class GuildTypeReader : NadekoTypeReader<IGuild>
|
||||
{
|
||||
input = input.Trim().ToUpperInvariant();
|
||||
var guilds = _client.Guilds;
|
||||
var guild = guilds.FirstOrDefault(g => g.Id.ToString().Trim().ToUpperInvariant() == input) ?? //by id
|
||||
var guild = guilds.FirstOrDefault(g => g.Id.ToString().Trim().ToUpperInvariant() == input)
|
||||
?? //by id
|
||||
guilds.FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == input); //by name
|
||||
|
||||
if (guild != null)
|
||||
return Task.FromResult(TypeReaderResult.FromSuccess(guild));
|
||||
|
||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No guild by that name or Id found"));
|
||||
return Task.FromResult(
|
||||
TypeReaderResult.FromError(CommandError.ParseFailed, "No guild by that name or Id found"));
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
public sealed class KwumTypeReader : NadekoTypeReader<kwum>
|
||||
|
@@ -1,25 +1,26 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common.TypeReaders.Models;
|
||||
|
||||
public class PermissionAction
|
||||
{
|
||||
public static PermissionAction Enable => new(true);
|
||||
public static PermissionAction Disable => new(false);
|
||||
public static PermissionAction Enable
|
||||
=> new(true);
|
||||
|
||||
public static PermissionAction Disable
|
||||
=> new(false);
|
||||
|
||||
public bool Value { get; }
|
||||
|
||||
public PermissionAction(bool value)
|
||||
=> this.Value = value;
|
||||
=> Value = value;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null || GetType() != obj.GetType())
|
||||
{
|
||||
return false;
|
||||
if (obj is null || GetType() != obj.GetType()) return false;
|
||||
|
||||
return Value == ((PermissionAction)obj).Value;
|
||||
}
|
||||
|
||||
return this.Value == ((PermissionAction)obj).Value;
|
||||
}
|
||||
|
||||
public override int GetHashCode() => Value.GetHashCode();
|
||||
public override int GetHashCode()
|
||||
=> Value.GetHashCode();
|
||||
}
|
@@ -1,27 +1,24 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NadekoBot.Common.TypeReaders.Models;
|
||||
|
||||
public class StoopidTime
|
||||
{
|
||||
public string Input { get; set; }
|
||||
public TimeSpan Time { get; set; }
|
||||
|
||||
private static readonly Regex _regex = new(
|
||||
@"^(?:(?<months>\d)mo)?(?:(?<weeks>\d{1,2})w)?(?:(?<days>\d{1,2})d)?(?:(?<hours>\d{1,4})h)?(?:(?<minutes>\d{1,5})m)?(?:(?<seconds>\d{1,6})s)?$",
|
||||
RegexOptions.Compiled | RegexOptions.Multiline);
|
||||
|
||||
public string Input { get; set; }
|
||||
public TimeSpan Time { get; set; }
|
||||
|
||||
private StoopidTime() { }
|
||||
|
||||
public static StoopidTime FromInput(string input)
|
||||
{
|
||||
var m = _regex.Match(input);
|
||||
|
||||
if (m.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Invalid string input format.");
|
||||
}
|
||||
if (m.Length == 0) throw new ArgumentException("Invalid string input format.");
|
||||
|
||||
var output = string.Empty;
|
||||
var namesAndValues = new Dictionary<string, int>();
|
||||
@@ -35,29 +32,18 @@ public class StoopidTime
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value < 1)
|
||||
{
|
||||
throw new ArgumentException($"Invalid {groupName} value.");
|
||||
}
|
||||
if (value < 1) throw new ArgumentException($"Invalid {groupName} value.");
|
||||
|
||||
namesAndValues[groupName] = value;
|
||||
output += m.Groups[groupName].Value + " " + groupName + " ";
|
||||
}
|
||||
var ts = new TimeSpan((30 * namesAndValues["months"]) +
|
||||
(7 * namesAndValues["weeks"]) +
|
||||
namesAndValues["days"],
|
||||
|
||||
var ts = new TimeSpan((30 * namesAndValues["months"]) + (7 * namesAndValues["weeks"]) + namesAndValues["days"],
|
||||
namesAndValues["hours"],
|
||||
namesAndValues["minutes"],
|
||||
namesAndValues["seconds"]);
|
||||
if (ts > TimeSpan.FromDays(90))
|
||||
{
|
||||
throw new ArgumentException("Time is too long.");
|
||||
}
|
||||
if (ts > TimeSpan.FromDays(90)) throw new ArgumentException("Time is too long.");
|
||||
|
||||
return new()
|
||||
{
|
||||
Input = input,
|
||||
Time = ts,
|
||||
};
|
||||
return new() { Input = input, Time = ts };
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
public sealed class ModuleTypeReader : NadekoTypeReader<ModuleInfo>
|
||||
@@ -34,11 +34,10 @@ public sealed class ModuleOrCrTypeReader : NadekoTypeReader<ModuleOrCrInfo>
|
||||
var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule())
|
||||
.FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input)
|
||||
?.Key;
|
||||
if (module is null &&
|
||||
input != "ACTUALCUSTOMREACTIONS")
|
||||
if (module is null && input != "ACTUALCUSTOMREACTIONS")
|
||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No such module found."));
|
||||
|
||||
return Task.FromResult(TypeReaderResult.FromSuccess(new ModuleOrCrInfo { Name = input, }));
|
||||
return Task.FromResult(TypeReaderResult.FromSuccess(new ModuleOrCrInfo { Name = input }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
[MeansImplicitUse(ImplicitUseTargetFlags.Default | ImplicitUseTargetFlags.WithInheritors )]
|
||||
[MeansImplicitUse(ImplicitUseTargetFlags.Default | ImplicitUseTargetFlags.WithInheritors)]
|
||||
public abstract class NadekoTypeReader<T> : TypeReader
|
||||
{
|
||||
public abstract Task<TypeReaderResult> ReadAsync(ICommandContext ctx, string input);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using NadekoBot.Common.TypeReaders.Models;
|
||||
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
@@ -32,7 +32,8 @@ public sealed class PermissionActionTypeReader : NadekoTypeReader<PermissionActi
|
||||
case "BAN":
|
||||
return Task.FromResult(TypeReaderResult.FromSuccess(PermissionAction.Disable));
|
||||
default:
|
||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Did not receive a valid boolean value"));
|
||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed,
|
||||
"Did not receive a valid boolean value"));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
using Color = SixLabors.ImageSharp.Color;
|
||||
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
using Color = SixLabors.ImageSharp.Color;
|
||||
|
||||
public sealed class Rgba32TypeReader : NadekoTypeReader<Color>
|
||||
{
|
||||
|
@@ -1,12 +1,14 @@
|
||||
#nullable disable
|
||||
using System.Text.RegularExpressions;
|
||||
#nullable disable
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
using NCalc;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
|
||||
{
|
||||
private static readonly Regex _percentRegex = new(@"^((?<num>100|\d{1,2})%)$", RegexOptions.Compiled);
|
||||
private readonly DbService _db;
|
||||
private readonly GamblingConfigService _gambling;
|
||||
|
||||
@@ -33,7 +35,7 @@ public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
|
||||
return TypeReaderResult.FromSuccess(new ShmartNumber(num, i));
|
||||
try
|
||||
{
|
||||
var expr = new NCalc.Expression(i, NCalc.EvaluateOptions.IgnoreCase);
|
||||
var expr = new Expression(i, EvaluateOptions.IgnoreCase);
|
||||
expr.EvaluateParameter += (str, ev) => EvaluateParam(str, ev, context);
|
||||
var lon = (long)decimal.Parse(expr.Evaluate().ToString());
|
||||
return TypeReaderResult.FromSuccess(new ShmartNumber(lon, input));
|
||||
@@ -44,7 +46,7 @@ public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
|
||||
}
|
||||
}
|
||||
|
||||
private void EvaluateParam(string name, NCalc.ParameterArgs args, ICommandContext ctx)
|
||||
private void EvaluateParam(string name, ParameterArgs args, ICommandContext ctx)
|
||||
{
|
||||
switch (name.ToUpperInvariant())
|
||||
{
|
||||
@@ -67,8 +69,6 @@ public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Regex _percentRegex = new(@"^((?<num>100|\d{1,2})%)$", RegexOptions.Compiled);
|
||||
|
||||
private long Cur(ICommandContext ctx)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using NadekoBot.Common.TypeReaders.Models;
|
||||
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common.Yml;
|
||||
|
||||
public class CommentAttribute : Attribute
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.TypeInspectors;
|
||||
@@ -17,15 +17,7 @@ public class CommentGatheringTypeInspector : TypeInspectorSkeleton
|
||||
|
||||
private sealed class CommentsPropertyDescriptor : IPropertyDescriptor
|
||||
{
|
||||
private readonly IPropertyDescriptor baseDescriptor;
|
||||
|
||||
public CommentsPropertyDescriptor(IPropertyDescriptor baseDescriptor)
|
||||
{
|
||||
this.baseDescriptor = baseDescriptor;
|
||||
Name = baseDescriptor.Name;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public string Name { get; }
|
||||
|
||||
public Type Type
|
||||
=> baseDescriptor.Type;
|
||||
@@ -47,6 +39,14 @@ public class CommentGatheringTypeInspector : TypeInspectorSkeleton
|
||||
public bool CanWrite
|
||||
=> baseDescriptor.CanWrite;
|
||||
|
||||
private readonly IPropertyDescriptor baseDescriptor;
|
||||
|
||||
public CommentsPropertyDescriptor(IPropertyDescriptor baseDescriptor)
|
||||
{
|
||||
this.baseDescriptor = baseDescriptor;
|
||||
Name = baseDescriptor.Name;
|
||||
}
|
||||
|
||||
public void Write(object target, object value)
|
||||
=> baseDescriptor.Write(target, value);
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
@@ -6,15 +6,7 @@ namespace NadekoBot.Common.Yml;
|
||||
|
||||
public sealed class CommentsObjectDescriptor : IObjectDescriptor
|
||||
{
|
||||
private readonly IObjectDescriptor innerDescriptor;
|
||||
|
||||
public CommentsObjectDescriptor(IObjectDescriptor innerDescriptor, string comment)
|
||||
{
|
||||
this.innerDescriptor = innerDescriptor;
|
||||
this.Comment = comment;
|
||||
}
|
||||
|
||||
public string Comment { get; private set; }
|
||||
public string Comment { get; }
|
||||
|
||||
public object Value
|
||||
=> innerDescriptor.Value;
|
||||
@@ -27,4 +19,12 @@ public sealed class CommentsObjectDescriptor : IObjectDescriptor
|
||||
|
||||
public ScalarStyle ScalarStyle
|
||||
=> innerDescriptor.ScalarStyle;
|
||||
|
||||
private readonly IObjectDescriptor innerDescriptor;
|
||||
|
||||
public CommentsObjectDescriptor(IObjectDescriptor innerDescriptor, string comment)
|
||||
{
|
||||
this.innerDescriptor = innerDescriptor;
|
||||
Comment = comment;
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Core.Events;
|
||||
using YamlDotNet.Serialization;
|
||||
@@ -15,11 +15,9 @@ public class CommentsObjectGraphVisitor : ChainedObjectGraphVisitor
|
||||
|
||||
public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context)
|
||||
{
|
||||
if (value is CommentsObjectDescriptor commentsDescriptor &&
|
||||
!string.IsNullOrWhiteSpace(commentsDescriptor.Comment))
|
||||
{
|
||||
if (value is CommentsObjectDescriptor commentsDescriptor
|
||||
&& !string.IsNullOrWhiteSpace(commentsDescriptor.Comment))
|
||||
context.Emit(new Comment(commentsDescriptor.Comment.Replace("\n", "\n# "), false));
|
||||
}
|
||||
|
||||
return base.EnterMapping(key, value, context);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.EventEmitters;
|
||||
@@ -19,9 +19,9 @@ public class MultilineScalarFlowStyleEmitter : ChainedEventEmitter
|
||||
var value = eventInfo.Source.Value as string;
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
var isMultiLine = value.IndexOfAny(new char[] { '\r', '\n', '\x85', '\x2028', '\x2029' }) >= 0;
|
||||
var isMultiLine = value.IndexOfAny(new[] { '\r', '\n', '\x85', '\x2028', '\x2029' }) >= 0;
|
||||
if (isMultiLine)
|
||||
eventInfo = new(eventInfo.Source) { Style = ScalarStyle.Literal, };
|
||||
eventInfo = new(eventInfo.Source) { Style = ScalarStyle.Literal };
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
using System.Globalization;
|
||||
#nullable disable
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using System.Globalization;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Core.Events;
|
||||
using YamlDotNet.Serialization;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Core.Events;
|
||||
using YamlDotNet.Serialization;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace NadekoBot.Common.Yml;
|
||||
|
||||
@@ -7,9 +8,10 @@ public class Yaml
|
||||
{
|
||||
public static ISerializer Serializer
|
||||
=> new SerializerBuilder().WithTypeInspector(inner => new CommentGatheringTypeInspector(inner))
|
||||
.WithEmissionPhaseObjectGraphVisitor(args => new CommentsObjectGraphVisitor(args.InnerVisitor))
|
||||
.WithEmissionPhaseObjectGraphVisitor(args
|
||||
=> new CommentsObjectGraphVisitor(args.InnerVisitor))
|
||||
.WithEventEmitter(args => new MultilineScalarFlowStyleEmitter(args))
|
||||
.WithNamingConvention(YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention.Instance)
|
||||
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
||||
.WithIndentedSequences()
|
||||
.WithTypeConverter(new Rgba32Converter())
|
||||
.WithTypeConverter(new CultureInfoConverter())
|
||||
@@ -17,8 +19,7 @@ public class Yaml
|
||||
.Build();
|
||||
|
||||
public static IDeserializer Deserializer
|
||||
=> new DeserializerBuilder()
|
||||
.WithNamingConvention(YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention.Instance)
|
||||
=> new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance)
|
||||
.WithTypeConverter(new Rgba32Converter())
|
||||
.WithTypeConverter(new CultureInfoConverter())
|
||||
.WithTypeConverter(new UriConverter())
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
namespace NadekoBot.Common.Yml;
|
||||
|
||||
public class YamlHelper
|
||||
@@ -18,20 +18,14 @@ public class YamlHelper
|
||||
|
||||
foreach (var c in point)
|
||||
{
|
||||
if (!IsHex(c))
|
||||
{
|
||||
return point;
|
||||
}
|
||||
if (!IsHex(c)) return point;
|
||||
|
||||
character = (character << 4) + AsHex(c);
|
||||
}
|
||||
|
||||
// Check the value and write the character.
|
||||
|
||||
if (character is (>= 0xD800 and <= 0xDFFF) or > 0x10FFFF)
|
||||
{
|
||||
return point;
|
||||
}
|
||||
if (character is (>= 0xD800 and <= 0xDFFF) or > 0x10FFFF) return point;
|
||||
|
||||
return char.ConvertFromUtf32(character);
|
||||
}
|
||||
@@ -41,15 +35,9 @@ public class YamlHelper
|
||||
|
||||
public static int AsHex(char c)
|
||||
{
|
||||
if (c <= '9')
|
||||
{
|
||||
return c - '0';
|
||||
}
|
||||
if (c <= '9') return c - '0';
|
||||
|
||||
if (c <= 'F')
|
||||
{
|
||||
return c - 'A' + 10;
|
||||
}
|
||||
if (c <= 'F') return c - 'A' + 10;
|
||||
|
||||
return c - 'a' + 10;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
@@ -29,8 +29,8 @@ public static class ClubExtensions
|
||||
=> Include(clubs).FirstOrDefault(c => c.Name.ToUpper() == name.ToUpper() && c.Discrim == discrim);
|
||||
|
||||
public static int GetNextDiscrim(this DbSet<ClubInfo> clubs, string name)
|
||||
=> Include(clubs).Where(x => x.Name.ToUpper() == name.ToUpper()).Select(x => x.Discrim).DefaultIfEmpty().Max() +
|
||||
1;
|
||||
=> Include(clubs).Where(x => x.Name.ToUpper() == name.ToUpper()).Select(x => x.Discrim).DefaultIfEmpty().Max()
|
||||
+ 1;
|
||||
|
||||
public static List<ClubInfo> GetClubLeaderboardPage(this DbSet<ClubInfo> clubs, int page)
|
||||
=> clubs.AsNoTracking().OrderByDescending(x => x.Xp).Skip(page * 9).Take(9).ToList();
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
#nullable disable
|
||||
using LinqToDB;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Db.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
#nullable disable
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Db.Models;
|
||||
using NadekoBot.Services.Database;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
@@ -26,9 +26,8 @@ public static class DiscordUserExtensions
|
||||
TotalXp = 0,
|
||||
CurrencyAmount = 0
|
||||
},
|
||||
old => new() { Username = username, Discriminator = discrim, AvatarId = avatarId, },
|
||||
() => new() { UserId = userId }
|
||||
);
|
||||
old => new() { Username = username, Discriminator = discrim, AvatarId = avatarId },
|
||||
() => new() { UserId = userId });
|
||||
|
||||
//temp is only used in updatecurrencystate, so that i don't overwrite real usernames/discrims with Unknown
|
||||
public static DiscordUser GetOrCreateUser(
|
||||
@@ -38,40 +37,22 @@ public static class DiscordUserExtensions
|
||||
string discrim,
|
||||
string avatarId)
|
||||
{
|
||||
ctx.EnsureUserCreated(userId,
|
||||
username,
|
||||
discrim,
|
||||
avatarId
|
||||
);
|
||||
return ctx.DiscordUser.Include(x => x.Club)
|
||||
.First(u => u.UserId == userId);
|
||||
ctx.EnsureUserCreated(userId, username, discrim, avatarId);
|
||||
return ctx.DiscordUser.Include(x => x.Club).First(u => u.UserId == userId);
|
||||
}
|
||||
|
||||
public static DiscordUser GetOrCreateUser(this NadekoContext ctx, IUser original)
|
||||
=> ctx.GetOrCreateUser(original.Id,
|
||||
original.Username,
|
||||
original.Discriminator,
|
||||
original.AvatarId
|
||||
);
|
||||
=> ctx.GetOrCreateUser(original.Id, original.Username, original.Discriminator, original.AvatarId);
|
||||
|
||||
public static int GetUserGlobalRank(this DbSet<DiscordUser> users, ulong id)
|
||||
=> users.AsQueryable()
|
||||
.Where(x => x.TotalXp >
|
||||
users.AsQueryable()
|
||||
.Where(y => y.UserId == id)
|
||||
.Select(y => y.TotalXp)
|
||||
.FirstOrDefault()
|
||||
)
|
||||
.Count() +
|
||||
1;
|
||||
.Where(x => x.TotalXp
|
||||
> users.AsQueryable().Where(y => y.UserId == id).Select(y => y.TotalXp).FirstOrDefault())
|
||||
.Count()
|
||||
+ 1;
|
||||
|
||||
public static DiscordUser[] GetUsersXpLeaderboardFor(this DbSet<DiscordUser> users, int page)
|
||||
=> users.AsQueryable()
|
||||
.OrderByDescending(x => x.TotalXp)
|
||||
.Skip(page * 9)
|
||||
.Take(9)
|
||||
.AsEnumerable()
|
||||
.ToArray();
|
||||
=> users.AsQueryable().OrderByDescending(x => x.TotalXp).Skip(page * 9).Take(9).AsEnumerable().ToArray();
|
||||
|
||||
public static List<DiscordUser> GetTopRichest(
|
||||
this DbSet<DiscordUser> users,
|
||||
@@ -86,19 +67,12 @@ public static class DiscordUserExtensions
|
||||
.ToList();
|
||||
|
||||
public static long GetUserCurrency(this DbSet<DiscordUser> users, ulong userId)
|
||||
=> users.AsNoTracking()
|
||||
.FirstOrDefault(x => x.UserId == userId)
|
||||
?.CurrencyAmount ??
|
||||
0;
|
||||
=> users.AsNoTracking().FirstOrDefault(x => x.UserId == userId)?.CurrencyAmount ?? 0;
|
||||
|
||||
public static void RemoveFromMany(this DbSet<DiscordUser> users, IEnumerable<ulong> ids)
|
||||
{
|
||||
var items = users.AsQueryable()
|
||||
.Where(x => ids.Contains(x.UserId));
|
||||
foreach (var item in items)
|
||||
{
|
||||
item.CurrencyAmount = 0;
|
||||
}
|
||||
var items = users.AsQueryable().Where(x => ids.Contains(x.UserId));
|
||||
foreach (var item in items) item.CurrencyAmount = 0;
|
||||
}
|
||||
|
||||
public static bool TryUpdateCurrencyState(
|
||||
@@ -115,14 +89,12 @@ public static class DiscordUserExtensions
|
||||
|
||||
// if remove - try to remove if he has more or equal than the amount
|
||||
// and return number of rows > 0 (was there a change)
|
||||
if (amount < 0 &&
|
||||
!allowNegative)
|
||||
if (amount < 0 && !allowNegative)
|
||||
{
|
||||
var rows = ctx.Database.ExecuteSqlInterpolated($@"
|
||||
UPDATE DiscordUser
|
||||
SET CurrencyAmount=CurrencyAmount+{amount}
|
||||
WHERE UserId={userId} AND CurrencyAmount>={-amount};"
|
||||
);
|
||||
WHERE UserId={userId} AND CurrencyAmount>={-amount};");
|
||||
return rows > 0;
|
||||
}
|
||||
|
||||
@@ -132,8 +104,7 @@ WHERE UserId={userId} AND CurrencyAmount>={-amount};"
|
||||
var rows = ctx.Database.ExecuteSqlInterpolated($@"
|
||||
UPDATE DiscordUser
|
||||
SET CurrencyAmount=CurrencyAmount+{amount}
|
||||
WHERE UserId={userId};"
|
||||
);
|
||||
WHERE UserId={userId};");
|
||||
return rows > 0;
|
||||
}
|
||||
|
||||
@@ -147,7 +118,6 @@ WHERE UserId={userId};"
|
||||
|
||||
// just update the amount, there is no new user data
|
||||
if (!updatedUserData)
|
||||
{
|
||||
ctx.Database.ExecuteSqlInterpolated($@"
|
||||
UPDATE OR IGNORE DiscordUser
|
||||
SET CurrencyAmount=CurrencyAmount+{amount}
|
||||
@@ -155,11 +125,8 @@ WHERE UserId={userId};
|
||||
|
||||
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp)
|
||||
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
|
||||
"
|
||||
);
|
||||
}
|
||||
");
|
||||
else
|
||||
{
|
||||
ctx.Database.ExecuteSqlInterpolated($@"
|
||||
UPDATE OR IGNORE DiscordUser
|
||||
SET CurrencyAmount=CurrencyAmount+{amount},
|
||||
@@ -170,9 +137,7 @@ WHERE UserId={userId};
|
||||
|
||||
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp)
|
||||
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
|
||||
"
|
||||
);
|
||||
}
|
||||
");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -1,18 +1,19 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Db.Models;
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class GuildConfigExtensions
|
||||
{
|
||||
public class GeneratingChannel
|
||||
private static List<WarningPunishment> DefaultWarnPunishments
|
||||
=> new()
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
}
|
||||
new() { Count = 3, Punishment = PunishmentAction.Kick },
|
||||
new() { Count = 5, Punishment = PunishmentAction.Ban }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets full stream role settings for the guild with the specified id.
|
||||
@@ -25,8 +26,7 @@ public static class GuildConfigExtensions
|
||||
var conf = ctx.GuildConfigsForId(guildId,
|
||||
set => set.Include(y => y.StreamRole)
|
||||
.Include(y => y.StreamRole.Whitelist)
|
||||
.Include(y => y.StreamRole.Blacklist)
|
||||
);
|
||||
.Include(y => y.StreamRole.Blacklist));
|
||||
|
||||
if (conf.StreamRole is null)
|
||||
conf.StreamRole = new();
|
||||
@@ -34,13 +34,6 @@ public static class GuildConfigExtensions
|
||||
return conf.StreamRole;
|
||||
}
|
||||
|
||||
private static List<WarningPunishment> DefaultWarnPunishments
|
||||
=> new()
|
||||
{
|
||||
new() { Count = 3, Punishment = PunishmentAction.Kick },
|
||||
new() { Count = 5, Punishment = PunishmentAction.Ban }
|
||||
};
|
||||
|
||||
private static IQueryable<GuildConfig> IncludeEverything(this DbSet<GuildConfig> configs)
|
||||
=> configs.AsQueryable()
|
||||
.AsSplitQuery()
|
||||
@@ -56,10 +49,7 @@ public static class GuildConfigExtensions
|
||||
public static IEnumerable<GuildConfig> GetAllGuildConfigs(
|
||||
this DbSet<GuildConfig> configs,
|
||||
List<ulong> availableGuilds)
|
||||
=> configs.IncludeEverything()
|
||||
.AsNoTracking()
|
||||
.Where(x => availableGuilds.Contains(x.GuildId))
|
||||
.ToList();
|
||||
=> configs.IncludeEverything().AsNoTracking().Where(x => availableGuilds.Contains(x.GuildId)).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Gets and creates if it doesn't exist a config for a guild.
|
||||
@@ -77,8 +67,7 @@ public static class GuildConfigExtensions
|
||||
|
||||
if (includes is null)
|
||||
{
|
||||
config = ctx.GuildConfigs.IncludeEverything()
|
||||
.FirstOrDefault(c => c.GuildId == guildId);
|
||||
config = ctx.GuildConfigs.IncludeEverything().FirstOrDefault(c => c.GuildId == guildId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -93,9 +82,8 @@ public static class GuildConfigExtensions
|
||||
GuildId = guildId,
|
||||
Permissions = Permissionv2.GetDefaultPermlist,
|
||||
WarningsInitialized = true,
|
||||
WarnPunishments = DefaultWarnPunishments,
|
||||
}
|
||||
);
|
||||
WarnPunishments = DefaultWarnPunishments
|
||||
});
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
@@ -126,9 +114,7 @@ public static class GuildConfigExtensions
|
||||
|
||||
public static IEnumerable<GuildConfig> Permissionsv2ForAll(this DbSet<GuildConfig> configs, List<ulong> include)
|
||||
{
|
||||
var query = configs.AsQueryable()
|
||||
.Where(x => include.Contains(x.GuildId))
|
||||
.Include(gc => gc.Permissions);
|
||||
var query = configs.AsQueryable().Where(x => include.Contains(x.GuildId)).Include(gc => gc.Permissions);
|
||||
|
||||
return query.ToList();
|
||||
}
|
||||
@@ -145,8 +131,7 @@ public static class GuildConfigExtensions
|
||||
ctx.GuildConfigs.Add(config = new() { GuildId = guildId, Permissions = Permissionv2.GetDefaultPermlist });
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
else if (config.Permissions is null ||
|
||||
!config.Permissions.Any()) // if no perms, add default ones
|
||||
else if (config.Permissions is null || !config.Permissions.Any()) // if no perms, add default ones
|
||||
{
|
||||
config.Permissions = Permissionv2.GetDefaultPermlist;
|
||||
ctx.SaveChanges();
|
||||
@@ -156,10 +141,7 @@ public static class GuildConfigExtensions
|
||||
}
|
||||
|
||||
public static IEnumerable<FollowedStream> GetFollowedStreams(this DbSet<GuildConfig> configs)
|
||||
=> configs.AsQueryable()
|
||||
.Include(x => x.FollowedStreams)
|
||||
.SelectMany(gc => gc.FollowedStreams)
|
||||
.ToArray();
|
||||
=> configs.AsQueryable().Include(x => x.FollowedStreams).SelectMany(gc => gc.FollowedStreams).ToArray();
|
||||
|
||||
public static IEnumerable<FollowedStream> GetFollowedStreams(this DbSet<GuildConfig> configs, List<ulong> included)
|
||||
=> configs.AsQueryable()
|
||||
@@ -186,11 +168,10 @@ public static class GuildConfigExtensions
|
||||
.Include(x => x.XpSettings)
|
||||
.ThenInclude(x => x.CurrencyRewards)
|
||||
.Include(x => x.XpSettings)
|
||||
.ThenInclude(x => x.ExclusionList)
|
||||
);
|
||||
.ThenInclude(x => x.ExclusionList));
|
||||
|
||||
if (gc.XpSettings is null)
|
||||
gc.XpSettings = new XpSettings();
|
||||
gc.XpSettings = new();
|
||||
|
||||
return gc.XpSettings;
|
||||
}
|
||||
@@ -200,6 +181,12 @@ public static class GuildConfigExtensions
|
||||
.Include(x => x.GenerateCurrencyChannelIds)
|
||||
.Where(x => x.GenerateCurrencyChannelIds.Any())
|
||||
.SelectMany(x => x.GenerateCurrencyChannelIds)
|
||||
.Select(x => new GeneratingChannel() { ChannelId = x.ChannelId, GuildId = x.GuildConfig.GuildId })
|
||||
.Select(x => new GeneratingChannel { ChannelId = x.ChannelId, GuildId = x.GuildConfig.GuildId })
|
||||
.ToArray();
|
||||
|
||||
public class GeneratingChannel
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
@@ -8,12 +8,11 @@ public static class MusicPlayerSettingsExtensions
|
||||
{
|
||||
public static async Task<MusicPlayerSettings> ForGuildAsync(this DbSet<MusicPlayerSettings> settings, ulong guildId)
|
||||
{
|
||||
var toReturn = await settings.AsQueryable()
|
||||
.FirstOrDefaultAsync(x => x.GuildId == guildId);
|
||||
var toReturn = await settings.AsQueryable().FirstOrDefaultAsync(x => x.GuildId == guildId);
|
||||
|
||||
if (toReturn is null)
|
||||
{
|
||||
var newSettings = new MusicPlayerSettings() { GuildId = guildId, PlayerRepeat = PlayerRepeatType.Queue };
|
||||
var newSettings = new MusicPlayerSettings { GuildId = guildId, PlayerRepeat = PlayerRepeatType.Queue };
|
||||
|
||||
await settings.AddAsync(newSettings);
|
||||
return newSettings;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
@@ -11,14 +11,9 @@ public static class MusicPlaylistExtensions
|
||||
if (num < 1)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
return playlists.AsQueryable()
|
||||
.Skip((num - 1) * 20)
|
||||
.Take(20)
|
||||
.Include(pl => pl.Songs)
|
||||
.ToList();
|
||||
return playlists.AsQueryable().Skip((num - 1) * 20).Take(20).Include(pl => pl.Songs).ToList();
|
||||
}
|
||||
|
||||
public static MusicPlaylist GetWithSongs(this DbSet<MusicPlaylist> playlists, int id)
|
||||
=> playlists.Include(mpl => mpl.Songs)
|
||||
.FirstOrDefault(mpl => mpl.Id == id);
|
||||
=> playlists.Include(mpl => mpl.Songs).FirstOrDefault(mpl => mpl.Id == id);
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
@@ -8,15 +8,11 @@ namespace NadekoBot.Db;
|
||||
public static class PollExtensions
|
||||
{
|
||||
public static IEnumerable<Poll> GetAllPolls(this DbSet<Poll> polls)
|
||||
=> polls.Include(x => x.Answers)
|
||||
.Include(x => x.Votes)
|
||||
.ToArray();
|
||||
=> polls.Include(x => x.Answers).Include(x => x.Votes).ToArray();
|
||||
|
||||
public static void RemovePoll(this NadekoContext ctx, int id)
|
||||
{
|
||||
var p = ctx.Poll.Include(x => x.Answers)
|
||||
.Include(x => x.Votes)
|
||||
.FirstOrDefault(x => x.Id == id);
|
||||
var p = ctx.Poll.Include(x => x.Answers).Include(x => x.Votes).FirstOrDefault(x => x.Id == id);
|
||||
|
||||
if (p is null)
|
||||
return;
|
||||
|
@@ -1,14 +1,13 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class QuoteExtensions
|
||||
{
|
||||
public static IEnumerable<Quote> GetForGuild(this DbSet<Quote> quotes, ulong guildId)
|
||||
=> quotes.AsQueryable()
|
||||
.Where(x => x.GuildId == guildId);
|
||||
=> quotes.AsQueryable().Where(x => x.GuildId == guildId);
|
||||
|
||||
public static IEnumerable<Quote> GetGroup(
|
||||
this DbSet<Quote> quotes,
|
||||
@@ -16,16 +15,13 @@ public static class QuoteExtensions
|
||||
int page,
|
||||
OrderType order)
|
||||
{
|
||||
var q = quotes.AsQueryable()
|
||||
.Where(x => x.GuildId == guildId);
|
||||
var q = quotes.AsQueryable().Where(x => x.GuildId == guildId);
|
||||
if (order == OrderType.Keyword)
|
||||
q = q.OrderBy(x => x.Keyword);
|
||||
else
|
||||
q = q.OrderBy(x => x.Id);
|
||||
|
||||
return q.Skip(15 * page)
|
||||
.Take(15)
|
||||
.ToArray();
|
||||
return q.Skip(15 * page).Take(15).ToArray();
|
||||
}
|
||||
|
||||
public static async Task<Quote> GetRandomQuoteByKeywordAsync(
|
||||
@@ -34,9 +30,8 @@ public static class QuoteExtensions
|
||||
string keyword)
|
||||
{
|
||||
var rng = new NadekoRandom();
|
||||
return (await quotes.AsQueryable()
|
||||
.Where(q => q.GuildId == guildId && q.Keyword == keyword)
|
||||
.ToListAsync()).OrderBy(q => rng.Next())
|
||||
return (await quotes.AsQueryable().Where(q => q.GuildId == guildId && q.Keyword == keyword).ToListAsync())
|
||||
.OrderBy(q => rng.Next())
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
@@ -48,9 +43,9 @@ public static class QuoteExtensions
|
||||
{
|
||||
var rngk = new NadekoRandom();
|
||||
return (await quotes.AsQueryable()
|
||||
.Where(q => q.GuildId == guildId &&
|
||||
q.Keyword == keyword &&
|
||||
EF.Functions.Like(q.Text.ToUpper(), $"%{text.ToUpper()}%")
|
||||
.Where(q => q.GuildId == guildId
|
||||
&& q.Keyword == keyword
|
||||
&& EF.Functions.Like(q.Text.ToUpper(), $"%{text.ToUpper()}%")
|
||||
// && q.Text.Contains(text, StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
.ToListAsync()).OrderBy(q => rngk.Next())
|
||||
@@ -58,7 +53,5 @@ public static class QuoteExtensions
|
||||
}
|
||||
|
||||
public static void RemoveAllByKeyword(this DbSet<Quote> quotes, ulong guildId, string keyword)
|
||||
=> quotes.RemoveRange(quotes.AsQueryable()
|
||||
.Where(x => x.GuildId == guildId && x.Keyword.ToUpper() == keyword)
|
||||
);
|
||||
=> quotes.RemoveRange(quotes.AsQueryable().Where(x => x.GuildId == guildId && x.Keyword.ToUpper() == keyword));
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Services.Database.Models;
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
@@ -9,16 +9,10 @@ public static class ReminderExtensions
|
||||
public static IEnumerable<Reminder> GetIncludedReminders(
|
||||
this DbSet<Reminder> reminders,
|
||||
IEnumerable<ulong> guildIds)
|
||||
=> reminders.AsQueryable()
|
||||
.Where(x => guildIds.Contains(x.ServerId) || x.ServerId == 0)
|
||||
.ToList();
|
||||
=> reminders.AsQueryable().Where(x => guildIds.Contains(x.ServerId) || x.ServerId == 0).ToList();
|
||||
|
||||
public static IEnumerable<Reminder> RemindersFor(this DbSet<Reminder> reminders, ulong userId, int page)
|
||||
=> reminders.AsQueryable()
|
||||
.Where(x => x.UserId == userId)
|
||||
.OrderBy(x => x.DateAdded)
|
||||
.Skip(page * 10)
|
||||
.Take(10);
|
||||
=> reminders.AsQueryable().Where(x => x.UserId == userId).OrderBy(x => x.DateAdded).Skip(page * 10).Take(10);
|
||||
|
||||
public static IEnumerable<Reminder> RemindersForServer(this DbSet<Reminder> reminders, ulong serverId, int page)
|
||||
=> reminders.AsQueryable()
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Services.Database.Models;
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
@@ -18,7 +18,5 @@ public static class SelfAssignableRolesExtensions
|
||||
}
|
||||
|
||||
public static IEnumerable<SelfAssignedRole> GetFromGuild(this DbSet<SelfAssignedRole> roles, ulong guildId)
|
||||
=> roles.AsQueryable()
|
||||
.Where(s => s.GuildId == guildId)
|
||||
.ToArray();
|
||||
=> roles.AsQueryable().Where(s => s.GuildId == guildId).ToArray();
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
#nullable disable
|
||||
using LinqToDB;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
@@ -13,13 +13,10 @@ public static class UserXpExtensions
|
||||
var usr = ctx.UserXpStats.FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId);
|
||||
|
||||
if (usr is null)
|
||||
{
|
||||
ctx.Add(usr = new()
|
||||
{
|
||||
Xp = 0, UserId = userId, NotifyOnLevelUp = XpNotificationLocation.None, GuildId = guildId,
|
||||
}
|
||||
);
|
||||
}
|
||||
Xp = 0, UserId = userId, NotifyOnLevelUp = XpNotificationLocation.None, GuildId = guildId
|
||||
});
|
||||
|
||||
return usr;
|
||||
}
|
||||
@@ -50,15 +47,14 @@ public static class UserXpExtensions
|
||||
// LIMIT 1));";
|
||||
=> xps.AsQueryable()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.GuildId == guildId &&
|
||||
x.Xp + x.AwardedXp >
|
||||
xps.AsQueryable()
|
||||
.Where(x => x.GuildId == guildId
|
||||
&& x.Xp + x.AwardedXp
|
||||
> xps.AsQueryable()
|
||||
.Where(y => y.UserId == userId && y.GuildId == guildId)
|
||||
.Select(y => y.Xp + y.AwardedXp)
|
||||
.FirstOrDefault()
|
||||
)
|
||||
.Count() +
|
||||
1;
|
||||
.FirstOrDefault())
|
||||
.Count()
|
||||
+ 1;
|
||||
|
||||
public static void ResetGuildUserXp(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
|
||||
=> xps.Delete(x => x.UserId == userId && x.GuildId == guildId);
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Db.Models;
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
@@ -28,17 +28,13 @@ public static class WaifuExtensions
|
||||
Func<DbSet<WaifuInfo>, IQueryable<WaifuInfo>> includes = null)
|
||||
{
|
||||
if (includes is null)
|
||||
{
|
||||
return waifus.Include(wi => wi.Waifu)
|
||||
.Include(wi => wi.Affinity)
|
||||
.Include(wi => wi.Claimer)
|
||||
.Include(wi => wi.Items)
|
||||
.FirstOrDefault(wi => wi.Waifu.UserId == userId);
|
||||
}
|
||||
|
||||
return includes(waifus)
|
||||
.AsQueryable()
|
||||
.FirstOrDefault(wi => wi.Waifu.UserId == userId);
|
||||
return includes(waifus).AsQueryable().FirstOrDefault(wi => wi.Waifu.UserId == userId);
|
||||
}
|
||||
|
||||
public static IEnumerable<WaifuLbResult> GetTop(this DbSet<WaifuInfo> waifus, int count, int skip = 0)
|
||||
@@ -62,16 +58,13 @@ public static class WaifuExtensions
|
||||
ClaimerDiscrim = x.Claimer == null ? null : x.Claimer.Discriminator,
|
||||
Username = x.Waifu.Username,
|
||||
Discrim = x.Waifu.Discriminator,
|
||||
Price = x.Price,
|
||||
}
|
||||
)
|
||||
Price = x.Price
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static decimal GetTotalValue(this DbSet<WaifuInfo> waifus)
|
||||
=> waifus.AsQueryable()
|
||||
.Where(x => x.ClaimerId != null)
|
||||
.Sum(x => x.Price);
|
||||
=> waifus.AsQueryable().Where(x => x.ClaimerId != null).Sum(x => x.Price);
|
||||
|
||||
public static ulong GetWaifuUserId(this DbSet<WaifuInfo> waifus, ulong ownerId, string name)
|
||||
=> waifus.AsQueryable()
|
||||
@@ -84,45 +77,48 @@ public static class WaifuExtensions
|
||||
{
|
||||
ctx.Database.ExecuteSqlInterpolated($@"
|
||||
INSERT OR IGNORE INTO WaifuInfo (AffinityId, ClaimerId, Price, WaifuId)
|
||||
VALUES ({null}, {null}, {1}, (SELECT Id FROM DiscordUser WHERE UserId={userId}));"
|
||||
);
|
||||
VALUES ({null}, {null}, {1}, (SELECT Id FROM DiscordUser WHERE UserId={userId}));");
|
||||
|
||||
var toReturn = ctx.WaifuInfo.AsQueryable()
|
||||
.Where(w => w.WaifuId ==
|
||||
ctx.Set<DiscordUser>()
|
||||
.Where(w => w.WaifuId
|
||||
== ctx.Set<DiscordUser>()
|
||||
.AsQueryable()
|
||||
.Where(u => u.UserId == userId)
|
||||
.Select(u => u.Id)
|
||||
.FirstOrDefault()
|
||||
)
|
||||
.FirstOrDefault())
|
||||
.Select(w => new WaifuInfoStats
|
||||
{
|
||||
FullName = ctx.Set<DiscordUser>()
|
||||
FullName =
|
||||
ctx.Set<DiscordUser>()
|
||||
.AsQueryable()
|
||||
.Where(u => u.UserId == userId)
|
||||
.Select(u => u.Username + "#" + u.Discriminator)
|
||||
.FirstOrDefault(),
|
||||
AffinityCount = ctx.Set<WaifuUpdate>()
|
||||
AffinityCount =
|
||||
ctx.Set<WaifuUpdate>()
|
||||
.AsQueryable()
|
||||
.Count(x => x.UserId == w.WaifuId &&
|
||||
x.UpdateType == WaifuUpdateType.AffinityChanged &&
|
||||
x.NewId != null
|
||||
),
|
||||
AffinityName = ctx.Set<DiscordUser>()
|
||||
.Count(x => x.UserId == w.WaifuId
|
||||
&& x.UpdateType == WaifuUpdateType.AffinityChanged
|
||||
&& x.NewId != null),
|
||||
AffinityName =
|
||||
ctx.Set<DiscordUser>()
|
||||
.AsQueryable()
|
||||
.Where(u => u.Id == w.AffinityId)
|
||||
.Select(u => u.Username + "#" + u.Discriminator)
|
||||
.FirstOrDefault(),
|
||||
ClaimCount = ctx.WaifuInfo.AsQueryable()
|
||||
.Count(x => x.ClaimerId == w.WaifuId),
|
||||
ClaimerName = ctx.Set<DiscordUser>()
|
||||
ClaimCount = ctx.WaifuInfo.AsQueryable().Count(x => x.ClaimerId == w.WaifuId),
|
||||
ClaimerName =
|
||||
ctx.Set<DiscordUser>()
|
||||
.AsQueryable()
|
||||
.Where(u => u.Id == w.ClaimerId)
|
||||
.Select(u => u.Username + "#" + u.Discriminator)
|
||||
.FirstOrDefault(),
|
||||
DivorceCount = ctx.Set<WaifuUpdate>()
|
||||
DivorceCount =
|
||||
ctx.Set<WaifuUpdate>()
|
||||
.AsQueryable()
|
||||
.Count(x => x.OldId == w.WaifuId && x.NewId == null && x.UpdateType == WaifuUpdateType.Claimed),
|
||||
.Count(x => x.OldId == w.WaifuId
|
||||
&& x.NewId == null
|
||||
&& x.UpdateType == WaifuUpdateType.Claimed),
|
||||
Price = w.Price,
|
||||
Claims = ctx.WaifuInfo.AsQueryable()
|
||||
.Include(x => x.Waifu)
|
||||
@@ -134,9 +130,8 @@ VALUES ({null}, {null}, {1}, (SELECT Id FROM DiscordUser WHERE UserId={userId}))
|
||||
.Where(x => x.AffinityId == w.WaifuId)
|
||||
.Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator)
|
||||
.ToList(),
|
||||
Items = w.Items,
|
||||
}
|
||||
)
|
||||
Items = w.Items
|
||||
})
|
||||
.FirstOrDefault();
|
||||
|
||||
if (toReturn is null)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Services.Database.Models;
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
@@ -31,8 +31,7 @@ public static class WarningExtensions
|
||||
.Skip(index)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (warn is null ||
|
||||
warn.Forgiven)
|
||||
if (warn is null || warn.Forgiven)
|
||||
return false;
|
||||
|
||||
warn.Forgiven = true;
|
||||
@@ -54,11 +53,8 @@ public static class WarningExtensions
|
||||
x.Forgiven = true;
|
||||
x.ForgivenBy = mod;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
public static Warning[] GetForGuild(this DbSet<Warning> warnings, ulong id)
|
||||
=> warnings.AsQueryable()
|
||||
.Where(x => x.GuildId == id)
|
||||
.ToArray();
|
||||
=> warnings.AsQueryable().Where(x => x.GuildId == id).ToArray();
|
||||
}
|
@@ -55,10 +55,9 @@ public class AntiSpamIgnore : DbEntity
|
||||
{
|
||||
public ulong ChannelId { get; set; }
|
||||
|
||||
public override int GetHashCode() => ChannelId.GetHashCode();
|
||||
public override int GetHashCode()
|
||||
=> ChannelId.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is AntiSpamIgnore inst
|
||||
? inst.ChannelId == ChannelId
|
||||
: false;
|
||||
=> obj is AntiSpamIgnore inst ? inst.ChannelId == ChannelId : false;
|
||||
}
|
@@ -8,7 +8,7 @@ public class AutoCommand : DbEntity
|
||||
public string ChannelName { get; set; }
|
||||
public ulong? GuildId { get; set; }
|
||||
public string GuildName { get; set; }
|
||||
public ulong? VoiceChannelId {get; set; }
|
||||
public ulong? VoiceChannelId { get; set; }
|
||||
public string VoiceChannelName { get; set; }
|
||||
public int Interval { get; set; }
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
@@ -8,6 +8,7 @@ public class ClubInfo : DbEntity
|
||||
{
|
||||
[MaxLength(20)]
|
||||
public string Name { get; set; }
|
||||
|
||||
public int Discrim { get; set; }
|
||||
|
||||
public string ImageUrl { get; set; } = string.Empty;
|
||||
|
@@ -7,10 +7,6 @@ public class CurrencyTransaction : DbEntity
|
||||
public string Reason { get; set; }
|
||||
public ulong UserId { get; set; }
|
||||
|
||||
public CurrencyTransaction Clone() => new()
|
||||
{
|
||||
Amount = Amount,
|
||||
Reason = Reason,
|
||||
UserId = UserId,
|
||||
};
|
||||
public CurrencyTransaction Clone()
|
||||
=> new() { Amount = Amount, Reason = Reason, UserId = UserId };
|
||||
}
|
@@ -13,12 +13,11 @@ public class CustomReaction : DbEntity
|
||||
public bool AllowTarget { get; set; }
|
||||
public string Reactions { get; set; }
|
||||
|
||||
public string[] GetReactions() =>
|
||||
string.IsNullOrWhiteSpace(Reactions)
|
||||
? Array.Empty<string>()
|
||||
: Reactions.Split("@@@");
|
||||
public string[] GetReactions()
|
||||
=> string.IsNullOrWhiteSpace(Reactions) ? Array.Empty<string>() : Reactions.Split("@@@");
|
||||
|
||||
public bool IsGlobal() => GuildId is null or 0;
|
||||
public bool IsGlobal()
|
||||
=> GuildId is null or 0;
|
||||
}
|
||||
|
||||
public class ReactionResponse : DbEntity
|
||||
|
@@ -7,5 +7,6 @@ public class DbEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
public DateTime? DateAdded { get; set; } = DateTime.UtcNow;
|
||||
}
|
@@ -10,6 +10,5 @@ public class DelMsgOnCmdChannel : DbEntity
|
||||
=> ChannelId.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is DelMsgOnCmdChannel x
|
||||
&& x.ChannelId == ChannelId;
|
||||
=> obj is DelMsgOnCmdChannel x && x.ChannelId == ChannelId;
|
||||
}
|
@@ -21,13 +21,11 @@ public class DiscordUser : DbEntity
|
||||
public long CurrencyAmount { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is DiscordUser du
|
||||
? du.UserId == UserId
|
||||
: false;
|
||||
=> obj is DiscordUser du ? du.UserId == UserId : false;
|
||||
|
||||
public override int GetHashCode()
|
||||
=> UserId.GetHashCode();
|
||||
|
||||
public override string ToString() =>
|
||||
Username + "#" + Discriminator;
|
||||
public override string ToString()
|
||||
=> Username + "#" + Discriminator;
|
||||
}
|
@@ -6,7 +6,8 @@ public class CurrencyEvent
|
||||
public enum Type
|
||||
{
|
||||
Reaction,
|
||||
GameStatus,
|
||||
|
||||
GameStatus
|
||||
//NotRaid,
|
||||
}
|
||||
|
||||
@@ -19,20 +20,24 @@ public class CurrencyEvent
|
||||
/// Amount of currency that the user will be rewarded.
|
||||
/// </summary>
|
||||
public long Amount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum amount of currency that can be handed out.
|
||||
/// </summary>
|
||||
public long PotSize { get; set; }
|
||||
|
||||
public List<AwardedUser> AwardedUsers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used as extra data storage for events which need it.
|
||||
/// </summary>
|
||||
public ulong ExtraId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// May be used for some future event.
|
||||
/// </summary>
|
||||
public ulong ExtraId2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// May be used for some future event.
|
||||
/// </summary>
|
||||
@@ -41,5 +46,4 @@ public class CurrencyEvent
|
||||
|
||||
public class AwardedUser
|
||||
{
|
||||
|
||||
}
|
@@ -13,7 +13,5 @@ public class FeedSub : DbEntity
|
||||
=> Url.GetHashCode(StringComparison.InvariantCulture) ^ GuildConfigId.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is FeedSub s
|
||||
&& s.Url.ToLower() == Url.ToLower()
|
||||
&& s.GuildConfigId == GuildConfigId;
|
||||
=> obj is FeedSub s && s.Url.ToLower() == Url.ToLower() && s.GuildConfigId == GuildConfigId;
|
||||
}
|
@@ -6,8 +6,7 @@ public class FilterLinksChannelId : DbEntity
|
||||
public ulong ChannelId { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is FilterLinksChannelId f
|
||||
&& f.ChannelId == ChannelId;
|
||||
=> obj is FilterLinksChannelId f && f.ChannelId == ChannelId;
|
||||
|
||||
public override int GetHashCode()
|
||||
=> ChannelId.GetHashCode();
|
||||
|
@@ -1,35 +1,36 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Modules.Searches.Common;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
public class FollowedStream : DbEntity
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
public string Username { get; set; }
|
||||
public FType Type { get; set; }
|
||||
public string Message { get; set; }
|
||||
|
||||
public enum FType
|
||||
{
|
||||
Twitch = 0,
|
||||
Picarto = 3,
|
||||
Youtube = 4,
|
||||
Facebook = 5,
|
||||
Facebook = 5
|
||||
}
|
||||
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
public string Username { get; set; }
|
||||
public FType Type { get; set; }
|
||||
public string Message { get; set; }
|
||||
|
||||
protected bool Equals(FollowedStream other)
|
||||
=> ChannelId == other.ChannelId
|
||||
&& Username.Trim().ToUpperInvariant() == other.Username.Trim().ToUpperInvariant()
|
||||
&& Type == other.Type;
|
||||
|
||||
public override int GetHashCode()
|
||||
=> HashCode.Combine(ChannelId, Username, (int) Type);
|
||||
=> HashCode.Combine(ChannelId, Username, (int)Type);
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is FollowedStream fs && Equals(fs);
|
||||
|
||||
public StreamDataKey CreateKey() => new(Type, Username.ToLower());
|
||||
public StreamDataKey CreateKey()
|
||||
=> new(Type, Username.ToLower());
|
||||
}
|
@@ -7,10 +7,8 @@ public class GCChannelId : DbEntity
|
||||
public ulong ChannelId { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is GCChannelId gc
|
||||
? gc.ChannelId == ChannelId
|
||||
: false;
|
||||
=> obj is GCChannelId gc ? gc.ChannelId == ChannelId : false;
|
||||
|
||||
public override int GetHashCode() =>
|
||||
this.ChannelId.GetHashCode();
|
||||
public override int GetHashCode()
|
||||
=> ChannelId.GetHashCode();
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user