Applied codestyle to all .cs files

This commit is contained in:
Kwoth
2021-12-29 06:07:16 +01:00
parent 723447c7d4
commit 82000c97a4
543 changed files with 13221 additions and 14059 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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
}

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
using System.Runtime.CompilerServices;
namespace NadekoBot.Common;

View File

@@ -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());

View File

@@ -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();
}

View File

@@ -6,5 +6,5 @@ public sealed class NadekoOptionsAttribute : Attribute
public Type OptionType { get; set; }
public NadekoOptionsAttribute(Type t)
=> this.OptionType = t;
=> OptionType = t;
}

View File

@@ -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"));
}
}

View File

@@ -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)
{
}
}

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
using Newtonsoft.Json;
namespace NadekoBot.Common;

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
// License MIT
// Source: https://github.com/i3arnon/ConcurrentHashSet
@@ -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}"/>.
/// </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,53 +52,112 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
}
}
bool ICollection<T>.IsReadOnly
=> false;
/// <summary>
/// Initializes a new instance of the <see
/// 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
/// 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
/// <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>
/// 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
/// 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>
/// <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)
@@ -151,31 +169,39 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
/// class that is empty, has the specified concurrency level and capacity, and uses the specified
/// <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>
/// <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
/// 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}" />.
/// </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).
/// </exception>
@@ -194,12 +220,18 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
/// has the specified concurrency level, has the specified initial capacity, and 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="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.
/// -or-
@@ -209,10 +241,7 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
/// <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));
@@ -225,43 +254,45 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<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}" />.
/// </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
/// <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>
/// 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.
/// </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];
@@ -272,17 +303,6 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
_comparer = comparer ?? throw new ArgumentNullException(nameof(comparer));
}
/// <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}" />.
/// </summary>
@@ -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,67 +353,47 @@ 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>
/// <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
@@ -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;

View File

@@ -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();
}

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot.Common;
public class CommandData

View File

@@ -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
}

View File

@@ -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;
}
}
}
}

View File

@@ -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)

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot.Common;
public static class Helpers

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot;
public interface IBotCredentials

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot.Common;
public interface ICloneable<T>

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot;
public interface IEmbedBuilder
@@ -19,5 +19,5 @@ public enum EmbedColor
{
Ok,
Pending,
Error,
Error
}

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot.Common;
public interface INadekoCommandOptions

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
using NadekoBot.Common.Yml;
namespace NadekoBot.Common;

View File

@@ -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;

View File

@@ -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()

View File

@@ -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:

View File

@@ -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;
}
}

View File

@@ -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()

View File

@@ -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/)."));

View File

@@ -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();

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot.Common;
public class OsuMapData

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
using Newtonsoft.Json;
namespace NadekoBot.Common;

View File

@@ -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;

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot.Common.Pokemon;
public class PokemonNameId

View File

@@ -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; }
}

View File

@@ -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);
}
}

View File

@@ -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)

View File

@@ -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)
{

View File

@@ -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()
{
@@ -31,8 +30,7 @@ public class YamlSeria : IConfigSeria
var str = me.Groups["code"].Value;
var newString = YamlHelper.UnescapeUnicodeCodePoint(str);
return newString;
}
);
});
return output;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot.Common;
public struct ShmartNumber : IEquatable<ShmartNumber>

View File

@@ -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,7 +100,6 @@ public sealed record SmartEmbedText : SmartText
public void NormalizeFields()
{
if (Fields is { Length: > 0 })
{
foreach (var f in Fields)
{
f.Name = f.Name.TrimTo(256);
@@ -116,4 +107,3 @@ public sealed record SmartEmbedText : SmartText
}
}
}
}

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot;
public sealed record SmartPlainText : SmartText

View File

@@ -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;
}

View File

@@ -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; }
}

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot;
public class SmartTextEmbedField

View File

@@ -8,6 +8,7 @@ namespace NadekoBot;
public class SmartTextEmbedFooter
{
public string Text { get; set; }
[JsonProperty("icon_url")]
public string IconUrl { get; set; }
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot.Common.TypeReaders;
public sealed class EmoteTypeReader : NadekoTypeReader<Emote>

View File

@@ -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));
}

View File

@@ -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"));
}
}

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot.Common.TypeReaders;
public sealed class KwumTypeReader : NadekoTypeReader<kwum>

View File

@@ -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();
}

View File

@@ -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 };
}
}

View File

@@ -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 }));
}
}

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot.Common.TypeReaders;
[MeansImplicitUse(ImplicitUseTargetFlags.Default | ImplicitUseTargetFlags.WithInheritors)]

View File

@@ -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"));
}
}
}

View File

@@ -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>
{

View File

@@ -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();

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
using NadekoBot.Common.TypeReaders.Models;
namespace NadekoBot.Common.TypeReaders;

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace NadekoBot.Common.Yml;
public class CommentAttribute : Attribute

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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 };
}
}

View File

@@ -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;

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;

View File

@@ -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())

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database.Models;

View File

@@ -1,6 +1,6 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
#nullable disable
using LinqToDB;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Db;

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database.Models;

View File

@@ -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;
}

View File

@@ -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; }
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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()

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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)

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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 };
}

View File

@@ -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

View File

@@ -7,5 +7,6 @@ public class DbEntity
{
[Key]
public int Id { get; set; }
public DateTime? DateAdded { get; set; } = DateTime.UtcNow;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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
{
}

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -1,25 +1,25 @@
#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()
@@ -31,5 +31,6 @@ public class FollowedStream : DbEntity
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());
}

View File

@@ -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();
}

View File

@@ -12,7 +12,9 @@ public class GuildConfig : DbEntity
public bool DeleteMessageOnCommand { get; set; }
public HashSet<DelMsgOnCmdChannel> DelMsgOnCmdChannels { get; set; } = new();
public string AutoAssignRoleIds { get; set; }
//greet stuff
public bool AutoDeleteGreetMessages { get; set; } //unused
public bool AutoDeleteByeMessages { get; set; } // unused
@@ -28,15 +30,6 @@ public class GuildConfig : DbEntity
public bool SendChannelGreetMessage { get; set; }
public string ChannelGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
#region Boost Message
public bool SendBoostMessage { get; set; }
public string BoostMessage { get; set; } = "%user% just boosted this server!";
public ulong BoostMessageChannelId { get; set; }
public int BoostMessageDeleteAfter { get; set; }
#endregion
public bool SendChannelByeMessage { get; set; }
public string ChannelByeMessageText { get; set; } = "%user% has left!";
@@ -104,4 +97,13 @@ public class GuildConfig : DbEntity
public List<GroupName> SelfAssignableRoleGroupNames { get; set; }
public int WarnExpireHours { get; set; } = 0;
public WarnExpireAction WarnExpireAction { get; set; } = WarnExpireAction.Clear;
#region Boost Message
public bool SendBoostMessage { get; set; }
public string BoostMessage { get; set; } = "%user% just boosted this server!";
public ulong BoostMessageChannelId { get; set; }
public int BoostMessageDeleteAfter { get; set; }
#endregion
}

View File

@@ -12,5 +12,5 @@ public class IgnoredLogItem : DbEntity
public enum IgnoredItemType
{
Channel,
User,
User
}

Some files were not shown because too many files have changed in this diff Show More