mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 17:58:26 -04:00
Added and applied styles for private readonly fields, private fields to Extensions and Common folders.
- Some renamings and code cleanups - Chained method calls, binary expressions and binary patterns will now break into newlines - Type param constraints and base constructor calls will be on the new line
This commit is contained in:
@@ -173,6 +173,14 @@ csharp_preserve_single_line_statements = true
|
|||||||
|
|
||||||
# Naming rules
|
# Naming rules
|
||||||
|
|
||||||
|
dotnet_naming_rule.private_readonly_field.symbols = private_readonly_field
|
||||||
|
dotnet_naming_rule.private_readonly_field.style = begins_with_underscore
|
||||||
|
dotnet_naming_rule.private_readonly_field.severity = warning
|
||||||
|
|
||||||
|
dotnet_naming_rule.private_field.symbols = private_field
|
||||||
|
dotnet_naming_rule.private_field.style = camel_case
|
||||||
|
dotnet_naming_rule.private_field.severity = warning
|
||||||
|
|
||||||
dotnet_naming_rule.const_fields.symbols = const_fields
|
dotnet_naming_rule.const_fields.symbols = const_fields
|
||||||
dotnet_naming_rule.const_fields.style = all_upper
|
dotnet_naming_rule.const_fields.style = all_upper
|
||||||
dotnet_naming_rule.const_fields.severity = warning
|
dotnet_naming_rule.const_fields.severity = warning
|
||||||
@@ -209,10 +217,6 @@ dotnet_naming_rule.async_method_should_be_ends_with_async.severity = error
|
|||||||
dotnet_naming_rule.async_method_should_be_ends_with_async.symbols = async_method
|
dotnet_naming_rule.async_method_should_be_ends_with_async.symbols = async_method
|
||||||
dotnet_naming_rule.async_method_should_be_ends_with_async.style = ends_with_async
|
dotnet_naming_rule.async_method_should_be_ends_with_async.style = ends_with_async
|
||||||
|
|
||||||
dotnet_naming_rule.private_field_should_be_begins_with_underscore.severity = error
|
|
||||||
dotnet_naming_rule.private_field_should_be_begins_with_underscore.symbols = private_field
|
|
||||||
dotnet_naming_rule.private_field_should_be_begins_with_underscore.style = begins_with_underscore
|
|
||||||
|
|
||||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = error
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = error
|
||||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||||
@@ -221,10 +225,6 @@ dotnet_naming_rule.local_variable_should_be_camel_case.severity = error
|
|||||||
dotnet_naming_rule.local_variable_should_be_camel_case.symbols = local_variable
|
dotnet_naming_rule.local_variable_should_be_camel_case.symbols = local_variable
|
||||||
dotnet_naming_rule.local_variable_should_be_camel_case.style = camel_case
|
dotnet_naming_rule.local_variable_should_be_camel_case.style = camel_case
|
||||||
|
|
||||||
dotnet_naming_rule.public_anything_should_be_pascal_case.severity = error
|
|
||||||
dotnet_naming_rule.public_anything_should_be_pascal_case.symbols = public_anything
|
|
||||||
dotnet_naming_rule.public_anything_should_be_pascal_case.style = pascal_case
|
|
||||||
|
|
||||||
# Symbol specifications
|
# Symbol specifications
|
||||||
|
|
||||||
dotnet_naming_symbols.const_fields.required_modifiers = const
|
dotnet_naming_symbols.const_fields.required_modifiers = const
|
||||||
@@ -262,8 +262,12 @@ dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, meth
|
|||||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.private_readonly_field.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.private_readonly_field.applicable_accessibilities = private
|
||||||
|
dotnet_naming_symbols.private_readonly_field.required_modifiers = readonly
|
||||||
|
|
||||||
dotnet_naming_symbols.private_field.applicable_kinds = field
|
dotnet_naming_symbols.private_field.applicable_kinds = field
|
||||||
dotnet_naming_symbols.private_field.applicable_accessibilities = private
|
dotnet_naming_symbols.private_field.applicable_accessibilities = private, protected
|
||||||
dotnet_naming_symbols.private_field.required_modifiers =
|
dotnet_naming_symbols.private_field.required_modifiers =
|
||||||
|
|
||||||
dotnet_naming_symbols.async_method.applicable_kinds = method, local_function
|
dotnet_naming_symbols.async_method.applicable_kinds = method, local_function
|
||||||
@@ -274,10 +278,6 @@ dotnet_naming_symbols.local_variable.applicable_kinds = parameter, local
|
|||||||
dotnet_naming_symbols.local_variable.applicable_accessibilities = local
|
dotnet_naming_symbols.local_variable.applicable_accessibilities = local
|
||||||
dotnet_naming_symbols.local_variable.required_modifiers =
|
dotnet_naming_symbols.local_variable.required_modifiers =
|
||||||
|
|
||||||
dotnet_naming_symbols.public_anything.applicable_kinds = property, field, event, class, struct, interface, enum, delegate, method
|
|
||||||
dotnet_naming_symbols.public_anything.applicable_accessibilities = public, internal
|
|
||||||
dotnet_naming_symbols.public_anything.required_modifiers =
|
|
||||||
|
|
||||||
# Naming styles
|
# Naming styles
|
||||||
|
|
||||||
|
|
||||||
@@ -330,8 +330,11 @@ resharper_csharp_wrap_parameters_style = chop_if_long
|
|||||||
resharper_force_chop_compound_if_expression = true
|
resharper_force_chop_compound_if_expression = true
|
||||||
resharper_keep_existing_linebreaks = false
|
resharper_keep_existing_linebreaks = false
|
||||||
resharper_max_formal_parameters_on_line = 3
|
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 = chop_always
|
||||||
|
|
||||||
resharper_csharp_wrap_before_first_type_parameter_constraint=true
|
resharper_csharp_wrap_before_first_type_parameter_constraint = true
|
||||||
resharper_csharp_place_type_constraints_on_same_line=false
|
resharper_csharp_place_type_constraints_on_same_line = false
|
||||||
resharper_csharp_wrap_before_extends_colon=true
|
resharper_csharp_wrap_before_extends_colon = true
|
||||||
resharper_csharp_place_constructor_initializer_on_same_line=false
|
resharper_csharp_place_constructor_initializer_on_same_line = false
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
namespace NadekoBot.Common.TypeReaders;
|
namespace NadekoBot.Common;
|
||||||
|
|
||||||
public enum AddRemove
|
public enum AddRemove
|
||||||
{
|
{
|
||||||
Add = int.MinValue,
|
Add = int.MinValue,
|
||||||
|
Remove = int.MinValue + 1,
|
||||||
Rem = int.MinValue + 1,
|
Rem = int.MinValue + 1,
|
||||||
Rm = int.MinValue + 1,
|
Rm = int.MinValue + 1,
|
||||||
}
|
}
|
@@ -4,13 +4,15 @@ namespace NadekoBot.Common;
|
|||||||
|
|
||||||
public class AsyncLazy<T> : Lazy<Task<T>>
|
public class AsyncLazy<T> : Lazy<Task<T>>
|
||||||
{
|
{
|
||||||
public AsyncLazy(Func<T> valueFactory) :
|
public AsyncLazy(Func<T> valueFactory)
|
||||||
base(() => Task.Run(valueFactory))
|
: base(() => Task.Run(valueFactory))
|
||||||
{ }
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public AsyncLazy(Func<Task<T>> taskFactory) :
|
public AsyncLazy(Func<Task<T>> taskFactory)
|
||||||
base(() => Task.Run(taskFactory))
|
: base(() => Task.Run(taskFactory))
|
||||||
{ }
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public TaskAwaiter<T> GetAwaiter()
|
public TaskAwaiter<T> GetAwaiter()
|
||||||
=> Value.GetAwaiter();
|
=> Value.GetAwaiter();
|
||||||
|
@@ -8,10 +8,7 @@ public class CmdStrings
|
|||||||
public string Description { get; }
|
public string Description { get; }
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public CmdStrings(
|
public CmdStrings([JsonProperty("args")] string[] usages, [JsonProperty("desc")] string description)
|
||||||
[JsonProperty("args")]string[] usages,
|
|
||||||
[JsonProperty("desc")]string description
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
Usages = usages;
|
Usages = usages;
|
||||||
Description = description;
|
Description = description;
|
||||||
|
@@ -3,14 +3,20 @@ using NadekoBot.Services.Database.Models;
|
|||||||
|
|
||||||
namespace NadekoBot.Common.Collections;
|
namespace NadekoBot.Common.Collections;
|
||||||
|
|
||||||
public class IndexedCollection<T> : IList<T> where T : class, IIndexed
|
public class IndexedCollection<T> : IList<T>
|
||||||
|
where T : class, IIndexed
|
||||||
{
|
{
|
||||||
public List<T> Source { get; }
|
public List<T> Source { get; }
|
||||||
private readonly object _locker = new();
|
private readonly object _locker = new();
|
||||||
|
|
||||||
public int Count => Source.Count;
|
public int Count
|
||||||
public bool IsReadOnly => false;
|
=> Source.Count;
|
||||||
public int IndexOf(T item) => item.Index;
|
|
||||||
|
public bool IsReadOnly
|
||||||
|
=> false;
|
||||||
|
|
||||||
|
public int IndexOf([NotNull] T item)
|
||||||
|
=> item.Index;
|
||||||
|
|
||||||
public IndexedCollection()
|
public IndexedCollection()
|
||||||
=> Source = new();
|
=> Source = new();
|
||||||
@@ -36,16 +42,17 @@ public class IndexedCollection<T> : IList<T> where T : class, IIndexed
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static implicit operator List<T>(IndexedCollection<T> x) =>
|
public static implicit operator List<T>(IndexedCollection<T> x)
|
||||||
x.Source;
|
=> x.Source;
|
||||||
|
|
||||||
public List<T> ToList() => Source.ToList();
|
public List<T> ToList()
|
||||||
|
=> Source.ToList();
|
||||||
|
|
||||||
public IEnumerator<T> GetEnumerator() =>
|
public IEnumerator<T> GetEnumerator()
|
||||||
Source.GetEnumerator();
|
=> Source.GetEnumerator();
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() =>
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
Source.GetEnumerator();
|
=> Source.GetEnumerator();
|
||||||
|
|
||||||
public void Add(T item)
|
public void Add(T item)
|
||||||
{
|
{
|
||||||
@@ -82,19 +89,21 @@ public class IndexedCollection<T> : IList<T> where T : class, IIndexed
|
|||||||
|
|
||||||
public virtual bool Remove(T item)
|
public virtual bool Remove(T item)
|
||||||
{
|
{
|
||||||
bool removed;
|
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
if (removed = Source.Remove(item))
|
if (Source.Remove(item))
|
||||||
{
|
{
|
||||||
for (var i = 0; i < Source.Count; i++)
|
for (var i = 0; i < Source.Count; i++)
|
||||||
{
|
{
|
||||||
if (Source[i].Index != i)
|
if (Source[i].Index != i)
|
||||||
Source[i].Index = i;
|
Source[i].Index = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return removed;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Insert(int index, T item)
|
public virtual void Insert(int index, T item)
|
||||||
|
@@ -18,14 +18,16 @@ next to the response. The color depends whether the command
|
|||||||
is completed, errored or in progress (pending)
|
is completed, errored or in progress (pending)
|
||||||
Color settings below are for the color of those lines.
|
Color settings below are for the color of those lines.
|
||||||
To get color's hex, you can go here https://htmlcolorcodes.com/
|
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; }
|
public ColorConfig Color { get; set; }
|
||||||
|
|
||||||
[Comment("Default bot language. It has to be in the list of supported languages (.langli)")]
|
[Comment("Default bot language. It has to be in the list of supported languages (.langli)")]
|
||||||
public CultureInfo DefaultLocale { get; set; }
|
public CultureInfo DefaultLocale { get; set; }
|
||||||
|
|
||||||
[Comment(@"Style in which executed commands will show up in the console.
|
[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; }
|
public ConsoleOutputType ConsoleOutputType { get; set; }
|
||||||
|
|
||||||
// [Comment(@"For what kind of updates will the bot check.
|
// [Comment(@"For what kind of updates will the bot check.
|
||||||
@@ -38,30 +40,35 @@ Allowed values: Simple, Normal, None")]
|
|||||||
[Comment(@"Do you want any messages sent by users in Bot's DM to be forwarded to the owner(s)?")]
|
[Comment(@"Do you want any messages sent by users in Bot's DM to be forwarded to the owner(s)?")]
|
||||||
public bool ForwardMessages { get; set; }
|
public bool ForwardMessages { get; set; }
|
||||||
|
|
||||||
[Comment(@"Do you want the message to be forwarded only to the first owner specified in the list of owners (in creds.yml),
|
[Comment(
|
||||||
or all owners? (this might cause the bot to lag if there's a lot of owners specified)")]
|
@"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)"
|
||||||
|
)]
|
||||||
public bool ForwardToAllOwners { get; set; }
|
public bool ForwardToAllOwners { get; set; }
|
||||||
|
|
||||||
[Comment(@"When a user DMs the bot with a message which is not a command
|
[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.
|
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)]
|
[YamlMember(ScalarStyle = ScalarStyle.Literal)]
|
||||||
public string DmHelpText { get; set; }
|
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.
|
[Comment(@"Only users who send a DM to the bot containing one of the specified words will get a DmHelpText response.
|
||||||
Case insensitive.
|
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; }
|
public List<string> DmHelpTextKeywords { get; set; }
|
||||||
|
|
||||||
[Comment(@"This is the response for the .h command")]
|
[Comment(@"This is the response for the .h command")]
|
||||||
[YamlMember(ScalarStyle = ScalarStyle.Literal)]
|
[YamlMember(ScalarStyle = ScalarStyle.Literal)]
|
||||||
public string HelpText { get; set; }
|
public string HelpText { get; set; }
|
||||||
|
|
||||||
[Comment(@"List of modules and commands completely blocked on the bot")]
|
[Comment(@"List of modules and commands completely blocked on the bot")]
|
||||||
public BlockedConfig Blocked { get; set; }
|
public BlockedConfig Blocked { get; set; }
|
||||||
|
|
||||||
[Comment(@"Which string will be used to recognize the commands")]
|
[Comment(@"Which string will be used to recognize the commands")]
|
||||||
public string Prefix { get; set; }
|
public string Prefix { get; set; }
|
||||||
|
|
||||||
[Comment(@"Toggles whether your bot will group greet/bye messages into a single message every 5 seconds.
|
[Comment(@"Toggles whether your bot will group greet/bye messages into a single message every 5 seconds.
|
||||||
1st user who joins will get greeted immediately
|
1st user who joins will get greeted immediately
|
||||||
If more users join within the next 5 seconds, they will be greeted in groups of 5.
|
If more users join within the next 5 seconds, they will be greeted in groups of 5.
|
||||||
@@ -70,12 +77,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.
|
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
|
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,
|
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; }
|
public bool GroupGreets { get; set; }
|
||||||
|
|
||||||
[Comment(@"Whether the bot will rotate through all specified statuses.
|
[Comment(@"Whether the bot will rotate through all specified statuses.
|
||||||
This setting can be changed via .rots command.
|
This setting can be changed via .rots command.
|
||||||
See RotatingStatuses submodule in Administration.")]
|
See RotatingStatuses submodule in Administration."
|
||||||
|
)]
|
||||||
public bool RotateStatuses { get; set; }
|
public bool RotateStatuses { get; set; }
|
||||||
|
|
||||||
// [Comment(@"Whether the prefix will be a suffix, or prefix.
|
// [Comment(@"Whether the prefix will be a suffix, or prefix.
|
||||||
@@ -159,10 +168,10 @@ public partial class ColorConfig
|
|||||||
{
|
{
|
||||||
[Comment(@"Color used for embed responses when command successfully executes")]
|
[Comment(@"Color used for embed responses when command successfully executes")]
|
||||||
public Rgba32 Ok { get; set; }
|
public Rgba32 Ok { get; set; }
|
||||||
|
|
||||||
[Comment(@"Color used for embed responses when command has an error")]
|
[Comment(@"Color used for embed responses when command has an error")]
|
||||||
public Rgba32 Error { get; set; }
|
public Rgba32 Error { get; set; }
|
||||||
|
|
||||||
[Comment(@"Color used for embed responses while command is doing work or is in progress")]
|
[Comment(@"Color used for embed responses while command is doing work or is in progress")]
|
||||||
public Rgba32 Pending { get; set; }
|
public Rgba32 Pending { get; set; }
|
||||||
|
|
||||||
@@ -173,7 +182,7 @@ public partial class ColorConfig
|
|||||||
Pending = Rgba32.ParseHex("faa61a");
|
Pending = Rgba32.ParseHex("faa61a");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ConsoleOutputType
|
public enum ConsoleOutputType
|
||||||
{
|
{
|
||||||
Normal = 0,
|
Normal = 0,
|
||||||
|
@@ -11,81 +11,93 @@ public sealed class Creds : IBotCredentials
|
|||||||
OwnerIds = new List<ulong>();
|
OwnerIds = new List<ulong>();
|
||||||
TotalShards = 1;
|
TotalShards = 1;
|
||||||
GoogleApiKey = string.Empty;
|
GoogleApiKey = string.Empty;
|
||||||
Votes = new(string.Empty, string.Empty, string.Empty, string.Empty);
|
Votes = new(string.Empty,
|
||||||
Patreon = new(string.Empty, string.Empty, string.Empty, string.Empty);
|
string.Empty,
|
||||||
|
string.Empty,
|
||||||
|
string.Empty
|
||||||
|
);
|
||||||
|
Patreon = new(string.Empty,
|
||||||
|
string.Empty,
|
||||||
|
string.Empty,
|
||||||
|
string.Empty
|
||||||
|
);
|
||||||
BotListToken = string.Empty;
|
BotListToken = string.Empty;
|
||||||
CleverbotApiKey = string.Empty;
|
CleverbotApiKey = string.Empty;
|
||||||
RedisOptions = "localhost:6379,syncTimeout=30000,responseTimeout=30000,allowAdmin=true,password=";
|
RedisOptions = "localhost:6379,syncTimeout=30000,responseTimeout=30000,allowAdmin=true,password=";
|
||||||
Db = new()
|
Db = new() { Type = "sqlite", ConnectionString = "Data Source=data/NadekoBot.db" };
|
||||||
{
|
|
||||||
Type = "sqlite",
|
|
||||||
ConnectionString = "Data Source=data/NadekoBot.db"
|
|
||||||
};
|
|
||||||
|
|
||||||
CoordinatorUrl = "http://localhost:3442";
|
CoordinatorUrl = "http://localhost:3442";
|
||||||
|
|
||||||
RestartCommand = new()
|
RestartCommand = new();
|
||||||
{
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Comment(@"DO NOT CHANGE")]
|
[Comment(@"DO NOT CHANGE")]
|
||||||
public int Version { get; set; }
|
public int Version { get; set; }
|
||||||
|
|
||||||
[Comment(@"Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/")]
|
[Comment(@"Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/")]
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
|
|
||||||
[Comment(@"List of Ids of the users who have bot owner permissions
|
[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; }
|
public ICollection<ulong> OwnerIds { get; set; }
|
||||||
|
|
||||||
[Comment(@"The number of shards that the bot will running on.
|
[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; }
|
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.
|
[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.
|
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; }
|
public string GoogleApiKey { get; set; }
|
||||||
|
|
||||||
[Comment(@"Settings for voting system for discordbots. Meant for use on global Nadeko.")]
|
[Comment(@"Settings for voting system for discordbots. Meant for use on global Nadeko.")]
|
||||||
public VotesSettings Votes { get; set; }
|
public VotesSettings Votes { get; set; }
|
||||||
|
|
||||||
[Comment(@"Patreon auto reward system settings.
|
[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; }
|
public PatreonSettings Patreon { get; set; }
|
||||||
|
|
||||||
[Comment(@"Api key for sending stats to DiscordBotList.")]
|
[Comment(@"Api key for sending stats to DiscordBotList.")]
|
||||||
public string BotListToken { get; set; }
|
public string BotListToken { get; set; }
|
||||||
|
|
||||||
[Comment(@"Official cleverbot api key.")]
|
[Comment(@"Official cleverbot api key.")]
|
||||||
public string CleverbotApiKey { get; set; }
|
public string CleverbotApiKey { get; set; }
|
||||||
|
|
||||||
[Comment(@"Redis connection string. Don't change if you don't know what you're doing.")]
|
[Comment(@"Redis connection string. Don't change if you don't know what you're doing.")]
|
||||||
public string RedisOptions { get; set; }
|
public string RedisOptions { get; set; }
|
||||||
|
|
||||||
[Comment(@"Database options. Don't change if you don't know what you're doing. Leave null for default values")]
|
[Comment(@"Database options. Don't change if you don't know what you're doing. Leave null for default values")]
|
||||||
public DbOptions Db { get; set; }
|
public DbOptions Db { get; set; }
|
||||||
|
|
||||||
[Comment(@"Address and port of the coordinator endpoint. Leave empty for default.
|
[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; }
|
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; }
|
public string RapidApiKey { get; set; }
|
||||||
|
|
||||||
[Comment(@"https://locationiq.com api key (register and you will receive the token in the email).
|
[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; }
|
public string LocationIqApiKey { get; set; }
|
||||||
|
|
||||||
[Comment(@"https://timezonedb.com api key (register and you will receive the token in the email).
|
[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; }
|
public string TimezoneDbApiKey { get; set; }
|
||||||
|
|
||||||
[Comment(@"https://pro.coinmarketcap.com/account/ api key. There is a free plan for personal use.
|
[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; }
|
public string CoinmarketcapApiKey { get; set; }
|
||||||
|
|
||||||
[Comment(@"Api key used for Osu related commands. Obtain this key at https://osu.ppy.sh/p/api")]
|
[Comment(@"Api key used for Osu related commands. Obtain this key at https://osu.ppy.sh/p/api")]
|
||||||
public string OsuApiKey { get; set; }
|
public string OsuApiKey { get; set; }
|
||||||
|
|
||||||
@@ -99,7 +111,8 @@ Linux default
|
|||||||
args: ""NadekoBot.dll -- {0}""
|
args: ""NadekoBot.dll -- {0}""
|
||||||
Windows default
|
Windows default
|
||||||
cmd: NadekoBot.exe
|
cmd: NadekoBot.exe
|
||||||
args: {0}")]
|
args: {0}"
|
||||||
|
)]
|
||||||
public RestartConfig RestartCommand { get; set; }
|
public RestartConfig RestartCommand { get; set; }
|
||||||
|
|
||||||
|
|
||||||
@@ -107,6 +120,7 @@ Windows default
|
|||||||
{
|
{
|
||||||
[Comment(@"Database type. Only sqlite supported atm")]
|
[Comment(@"Database type. Only sqlite supported atm")]
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
|
|
||||||
[Comment(@"Connection string. Will default to ""Data Source=data/NadekoBot.db""")]
|
[Comment(@"Connection string. Will default to ""Data Source=data/NadekoBot.db""")]
|
||||||
public string ConnectionString { get; set; }
|
public string ConnectionString { get; set; }
|
||||||
}
|
}
|
||||||
@@ -118,10 +132,16 @@ Windows default
|
|||||||
public string RefreshToken { get; set; }
|
public string RefreshToken { get; set; }
|
||||||
public string ClientSecret { get; set; }
|
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)")]
|
[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)"
|
||||||
|
)]
|
||||||
public string CampaignId { get; set; }
|
public string CampaignId { get; set; }
|
||||||
|
|
||||||
public PatreonSettings(string accessToken, string refreshToken, string clientSecret, string campaignId)
|
public PatreonSettings(
|
||||||
|
string accessToken,
|
||||||
|
string refreshToken,
|
||||||
|
string clientSecret,
|
||||||
|
string campaignId)
|
||||||
{
|
{
|
||||||
AccessToken = accessToken;
|
AccessToken = accessToken;
|
||||||
RefreshToken = refreshToken;
|
RefreshToken = refreshToken;
|
||||||
@@ -131,7 +151,6 @@ Windows default
|
|||||||
|
|
||||||
public PatreonSettings()
|
public PatreonSettings()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,28 +158,35 @@ Windows default
|
|||||||
{
|
{
|
||||||
[Comment(@"top.gg votes service url
|
[Comment(@"top.gg votes service url
|
||||||
This is the url of your instance of the NadekoBot.Votes api
|
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; }
|
public string TopggServiceUrl { get; set; }
|
||||||
|
|
||||||
[Comment(@"Authorization header value sent to the TopGG service url with each request
|
[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; }
|
public string TopggKey { get; set; }
|
||||||
|
|
||||||
[Comment(@"discords.com votes service url
|
[Comment(@"discords.com votes service url
|
||||||
This is the url of your instance of the NadekoBot.Votes api
|
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; }
|
public string DiscordsServiceUrl { get; set; }
|
||||||
|
|
||||||
[Comment(@"Authorization header value sent to the Discords service url with each request
|
[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 string DiscordsKey { get; set; }
|
||||||
|
|
||||||
public VotesSettings()
|
public VotesSettings()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VotesSettings(string topggServiceUrl, string topggKey, string discordsServiceUrl, string discordsKey)
|
public VotesSettings(
|
||||||
|
string topggServiceUrl,
|
||||||
|
string topggKey,
|
||||||
|
string discordsServiceUrl,
|
||||||
|
string discordsKey)
|
||||||
{
|
{
|
||||||
TopggServiceUrl = topggServiceUrl;
|
TopggServiceUrl = topggServiceUrl;
|
||||||
TopggKey = topggKey;
|
TopggKey = topggKey;
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
public class DownloadTracker : INService
|
public class DownloadTracker : INService
|
||||||
{
|
{
|
||||||
private ConcurrentDictionary<ulong, DateTime> LastDownloads { get; } = new();
|
private ConcurrentDictionary<ulong, DateTime> LastDownloads { get; } = new();
|
||||||
private readonly SemaphoreSlim downloadUsersSemaphore = new(1, 1);
|
private readonly SemaphoreSlim _downloadUsersSemaphore = new(1, 1);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ensures all users on the specified guild were downloaded within the last hour.
|
/// Ensures all users on the specified guild were downloaded within the last hour.
|
||||||
@@ -12,16 +12,16 @@ public class DownloadTracker : INService
|
|||||||
/// <returns>Task representing download state</returns>
|
/// <returns>Task representing download state</returns>
|
||||||
public async Task EnsureUsersDownloadedAsync(IGuild guild)
|
public async Task EnsureUsersDownloadedAsync(IGuild guild)
|
||||||
{
|
{
|
||||||
await downloadUsersSemaphore.WaitAsync();
|
await _downloadUsersSemaphore.WaitAsync();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
// download once per hour at most
|
// download once per hour at most
|
||||||
var added = LastDownloads.AddOrUpdate(
|
var added = LastDownloads.AddOrUpdate(guild.Id,
|
||||||
guild.Id,
|
|
||||||
now,
|
now,
|
||||||
(key, 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
|
// means that this entry was just added - download the users
|
||||||
if (added == now)
|
if (added == now)
|
||||||
@@ -29,7 +29,7 @@ public class DownloadTracker : INService
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
downloadUsersSemaphore.Release();
|
_downloadUsersSemaphore.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -6,7 +6,7 @@ public static class Helpers
|
|||||||
{
|
{
|
||||||
if (!Console.IsInputRedirected)
|
if (!Console.IsInputRedirected)
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
|
|
||||||
Environment.Exit(exitCode);
|
Environment.Exit(exitCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -21,7 +21,7 @@ public interface IBotCredentials
|
|||||||
string CoinmarketcapApiKey { get; }
|
string CoinmarketcapApiKey { get; }
|
||||||
string CoordinatorUrl { get; set; }
|
string CoordinatorUrl { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RestartConfig
|
public class RestartConfig
|
||||||
{
|
{
|
||||||
public string Cmd { get; set; }
|
public string Cmd { get; set; }
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
namespace NadekoBot.Common;
|
namespace NadekoBot.Common;
|
||||||
|
|
||||||
public interface ICloneable<T> where T : new()
|
public interface ICloneable<T>
|
||||||
|
where T : new()
|
||||||
{
|
{
|
||||||
public T Clone();
|
public T Clone();
|
||||||
}
|
}
|
@@ -1,23 +1,13 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
|
||||||
|
|
||||||
namespace NadekoBot.Common.JsonConverters;
|
namespace NadekoBot.Common.JsonConverters;
|
||||||
|
|
||||||
public class Rgba32Converter : JsonConverter<Rgba32>
|
|
||||||
{
|
|
||||||
public override Rgba32 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
||||||
=> Rgba32.ParseHex(reader.GetString());
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, Rgba32 value, JsonSerializerOptions options)
|
|
||||||
=> writer.WriteStringValue(value.ToHex());
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CultureInfoConverter : JsonConverter<CultureInfo>
|
public class CultureInfoConverter : JsonConverter<CultureInfo>
|
||||||
{
|
{
|
||||||
public override CultureInfo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override CultureInfo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
=> new(reader.GetString());
|
=> new(reader.GetString() ?? "en-US");
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, CultureInfo value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, CultureInfo value, JsonSerializerOptions options)
|
||||||
=> writer.WriteStringValue(value.Name);
|
=> writer.WriteStringValue(value.Name);
|
14
src/NadekoBot/Common/JsonConverters/Rgba32Converter.cs
Normal file
14
src/NadekoBot/Common/JsonConverters/Rgba32Converter.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace NadekoBot.Common.JsonConverters;
|
||||||
|
|
||||||
|
public class Rgba32Converter : JsonConverter<Rgba32>
|
||||||
|
{
|
||||||
|
public override Rgba32 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
=> Rgba32.ParseHex(reader.GetString());
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Rgba32 value, JsonSerializerOptions options)
|
||||||
|
=> writer.WriteStringValue(value.ToHex());
|
||||||
|
}
|
@@ -4,14 +4,17 @@ namespace NadekoBot.Common;
|
|||||||
|
|
||||||
// needs proper invalid input check (character array input out of range)
|
// needs proper invalid input check (character array input out of range)
|
||||||
// needs negative number support
|
// needs negative number support
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
#pragma warning disable IDE1006
|
||||||
public readonly struct kwum : IEquatable<kwum>
|
public readonly struct kwum : IEquatable<kwum>
|
||||||
|
#pragma warning restore IDE1006
|
||||||
{
|
{
|
||||||
private readonly int _value;
|
private readonly int _value;
|
||||||
private const string ValidCharacters = "23456789abcdefghijkmnpqrstuvwxyz";
|
private const string VALID_CHARACTERS = "23456789abcdefghijkmnpqrstuvwxyz";
|
||||||
|
|
||||||
public kwum(int num)
|
public kwum(int num)
|
||||||
=> _value = num;
|
=> _value = num;
|
||||||
|
|
||||||
public kwum(in char c)
|
public kwum(in char c)
|
||||||
{
|
{
|
||||||
if (!IsValidChar(c))
|
if (!IsValidChar(c))
|
||||||
@@ -21,11 +24,11 @@ public readonly struct kwum : IEquatable<kwum>
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static int InternalCharToValue(in char c)
|
private static int InternalCharToValue(in char c)
|
||||||
=> ValidCharacters.IndexOf(c);
|
=> VALID_CHARACTERS.IndexOf(c);
|
||||||
|
|
||||||
public kwum(in ReadOnlySpan<char> input)
|
public kwum(in ReadOnlySpan<char> input)
|
||||||
{;
|
{
|
||||||
_value = 0;
|
_value = 0;
|
||||||
for (var index = 0; index < input.Length; index++)
|
for (var index = 0; index < input.Length; index++)
|
||||||
{
|
{
|
||||||
@@ -33,14 +36,14 @@ public readonly struct kwum : IEquatable<kwum>
|
|||||||
if (!IsValidChar(c))
|
if (!IsValidChar(c))
|
||||||
throw new ArgumentException("All characters need to be a valid kwum characters.", nameof(input));
|
throw new ArgumentException("All characters need to be a valid kwum characters.", nameof(input));
|
||||||
|
|
||||||
_value += ValidCharacters.IndexOf(c) * (int)Math.Pow(ValidCharacters.Length, input.Length - index - 1);
|
_value += VALID_CHARACTERS.IndexOf(c) * (int)Math.Pow(VALID_CHARACTERS.Length, input.Length - index - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryParse(in ReadOnlySpan<char> input, out kwum value)
|
public static bool TryParse(in ReadOnlySpan<char> input, out kwum value)
|
||||||
{
|
{
|
||||||
value = default;
|
value = default;
|
||||||
foreach(var c in input)
|
foreach (var c in input)
|
||||||
if (!IsValidChar(c))
|
if (!IsValidChar(c))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -59,25 +62,26 @@ public readonly struct kwum : IEquatable<kwum>
|
|||||||
|
|
||||||
public static implicit operator long(kwum kwum)
|
public static implicit operator long(kwum kwum)
|
||||||
=> kwum._value;
|
=> kwum._value;
|
||||||
|
|
||||||
public static implicit operator int(kwum kwum)
|
public static implicit operator int(kwum kwum)
|
||||||
=> kwum._value;
|
=> kwum._value;
|
||||||
|
|
||||||
public static implicit operator kwum(int num)
|
public static implicit operator kwum(int num)
|
||||||
=> new(num);
|
=> new(num);
|
||||||
|
|
||||||
public static bool IsValidChar(char c)
|
public static bool IsValidChar(char c)
|
||||||
=> ValidCharacters.Contains(c);
|
=> VALID_CHARACTERS.Contains(c);
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
var count = ValidCharacters.Length;
|
var count = VALID_CHARACTERS.Length;
|
||||||
var localValue = _value;
|
var localValue = _value;
|
||||||
var arrSize = (int)Math.Log(localValue, count) + 1;
|
var arrSize = (int)Math.Log(localValue, count) + 1;
|
||||||
Span<char> chars = new char[arrSize];
|
Span<char> chars = new char[arrSize];
|
||||||
while (localValue > 0)
|
while (localValue > 0)
|
||||||
{
|
{
|
||||||
localValue = Math.DivRem(localValue, count, out var rem);
|
localValue = Math.DivRem(localValue, count, out var rem);
|
||||||
chars[--arrSize] = ValidCharacters[rem];
|
chars[--arrSize] = VALID_CHARACTERS[rem];
|
||||||
}
|
}
|
||||||
|
|
||||||
return new(chars);
|
return new(chars);
|
||||||
|
@@ -4,10 +4,14 @@ namespace NadekoBot.Common;
|
|||||||
|
|
||||||
public class LbOpts : INadekoCommandOptions
|
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 bool Clean { get; set; }
|
||||||
|
|
||||||
public void NormalizeOptions()
|
public void NormalizeOptions()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -17,18 +17,21 @@ public class LoginErrorHandler
|
|||||||
case HttpStatusCode.Unauthorized:
|
case HttpStatusCode.Unauthorized:
|
||||||
Log.Error("Your bot token is wrong.\n" +
|
Log.Error("Your bot token is wrong.\n" +
|
||||||
"You can find the bot token under the Bot tab in the developer page.\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");
|
"Fix your token in the credentials file and restart the bot"
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HttpStatusCode.BadRequest:
|
case HttpStatusCode.BadRequest:
|
||||||
Log.Error("Something has been incorrectly formatted in your credentials file.\n" +
|
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.");
|
"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");
|
Log.Error("If you are on Linux, make sure Redis is installed and running");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HttpStatusCode.RequestTimeout:
|
case HttpStatusCode.RequestTimeout:
|
||||||
Log.Error("The request timed out. Make sure you have no external program blocking the bot " +
|
Log.Error("The request timed out. Make sure you have no external program blocking the bot " +
|
||||||
"from connecting to the internet");
|
"from connecting to the internet"
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HttpStatusCode.ServiceUnavailable:
|
case HttpStatusCode.ServiceUnavailable:
|
||||||
@@ -38,7 +41,8 @@ public class LoginErrorHandler
|
|||||||
|
|
||||||
case HttpStatusCode.TooManyRequests:
|
case HttpStatusCode.TooManyRequests:
|
||||||
Log.Error("Your bot has been ratelimited by Discord. Please, try again later.\n" +
|
Log.Error("Your bot has been ratelimited by Discord. Please, try again later.\n" +
|
||||||
"Global ratelimits usually last for an hour");
|
"Global ratelimits usually last for an hour"
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -46,6 +50,6 @@ public class LoginErrorHandler
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Fatal(ex.ToString());
|
Log.Fatal(ex, "Fatal error occurred while loading credentials");
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,6 +0,0 @@
|
|||||||
namespace NadekoBot.Common.ModuleBehaviors;
|
|
||||||
|
|
||||||
public interface IInputTransformer
|
|
||||||
{
|
|
||||||
Task<string> TransformInput(IGuild guild, IMessageChannel channel, IUser user, string input);
|
|
||||||
}
|
|
10
src/NadekoBot/Common/ModuleBehaviors/IInputTransformer.cs
Normal file
10
src/NadekoBot/Common/ModuleBehaviors/IInputTransformer.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace NadekoBot.Common.ModuleBehaviors;
|
||||||
|
|
||||||
|
public interface IInputTransformer
|
||||||
|
{
|
||||||
|
Task<string> TransformInput(
|
||||||
|
IGuild guild,
|
||||||
|
IMessageChannel channel,
|
||||||
|
IUser user,
|
||||||
|
string input);
|
||||||
|
}
|
@@ -3,6 +3,6 @@
|
|||||||
public interface ILateBlocker
|
public interface ILateBlocker
|
||||||
{
|
{
|
||||||
public int Priority { get; }
|
public int Priority { get; }
|
||||||
|
|
||||||
Task<bool> TryBlockLate(ICommandContext context, string moduleName, CommandInfo command);
|
Task<bool> TryBlockLate(ICommandContext context, string moduleName, CommandInfo command);
|
||||||
}
|
}
|
@@ -1,60 +1,78 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
namespace NadekoBot.Modules;
|
namespace NadekoBot.Modules;
|
||||||
|
|
||||||
[UsedImplicitly(ImplicitUseTargetFlags.Default
|
[UsedImplicitly(ImplicitUseTargetFlags.Default |
|
||||||
| ImplicitUseTargetFlags.WithInheritors
|
ImplicitUseTargetFlags.WithInheritors |
|
||||||
| ImplicitUseTargetFlags.WithMembers)]
|
ImplicitUseTargetFlags.WithMembers
|
||||||
|
)]
|
||||||
public abstract class NadekoModule : ModuleBase
|
public abstract class NadekoModule : ModuleBase
|
||||||
{
|
{
|
||||||
protected CultureInfo _cultureInfo { get; set; }
|
protected CultureInfo Culture { get; set; }
|
||||||
public IBotStrings Strings { get; set; }
|
public IBotStrings Strings { get; set; }
|
||||||
public CommandHandler CmdHandler { get; set; }
|
public CommandHandler CmdHandler { get; set; }
|
||||||
public ILocalization Localization { get; set; }
|
public ILocalization Localization { get; set; }
|
||||||
public IEmbedBuilderService _eb { get; set; }
|
public IEmbedBuilderService _eb { get; set; }
|
||||||
|
|
||||||
public string Prefix => CmdHandler.GetPrefix(ctx.Guild);
|
public string Prefix
|
||||||
|
=> CmdHandler.GetPrefix(ctx.Guild);
|
||||||
|
|
||||||
protected ICommandContext ctx => Context;
|
protected ICommandContext ctx
|
||||||
|
=> Context;
|
||||||
protected NadekoModule()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void BeforeExecute(CommandInfo cmd)
|
protected override void BeforeExecute(CommandInfo cmd)
|
||||||
=> _cultureInfo = Localization.GetCultureInfo(ctx.Guild?.Id);
|
=> Culture = Localization.GetCultureInfo(ctx.Guild?.Id);
|
||||||
|
|
||||||
protected string GetText(in LocStr data) =>
|
protected string GetText(in LocStr data)
|
||||||
Strings.GetText(data, _cultureInfo);
|
=> Strings.GetText(data, Culture);
|
||||||
|
|
||||||
public Task<IUserMessage> SendErrorAsync(string error)
|
public Task<IUserMessage> SendErrorAsync(string error)
|
||||||
=> ctx.Channel.SendErrorAsync(_eb, error);
|
=> ctx.Channel.SendErrorAsync(_eb, error);
|
||||||
|
|
||||||
public Task<IUserMessage> SendErrorAsync(string title, string error, string url = null, string footer = null)
|
public Task<IUserMessage> SendErrorAsync(
|
||||||
=> ctx.Channel.SendErrorAsync(_eb, title, error, url, footer);
|
string title,
|
||||||
|
string error,
|
||||||
|
string url = null,
|
||||||
|
string footer = null)
|
||||||
|
=> ctx.Channel.SendErrorAsync(_eb,
|
||||||
|
title,
|
||||||
|
error,
|
||||||
|
url,
|
||||||
|
footer
|
||||||
|
);
|
||||||
|
|
||||||
public Task<IUserMessage> SendConfirmAsync(string text)
|
public Task<IUserMessage> SendConfirmAsync(string text)
|
||||||
=> ctx.Channel.SendConfirmAsync(_eb, text);
|
=> ctx.Channel.SendConfirmAsync(_eb, text);
|
||||||
|
|
||||||
public Task<IUserMessage> SendConfirmAsync(string title, string text, string url = null, string footer = null)
|
public Task<IUserMessage> SendConfirmAsync(
|
||||||
=> ctx.Channel.SendConfirmAsync(_eb, title, text, url, footer);
|
string title,
|
||||||
|
string text,
|
||||||
|
string url = null,
|
||||||
|
string footer = null)
|
||||||
|
=> ctx.Channel.SendConfirmAsync(_eb,
|
||||||
|
title,
|
||||||
|
text,
|
||||||
|
url,
|
||||||
|
footer
|
||||||
|
);
|
||||||
|
|
||||||
public Task<IUserMessage> SendPendingAsync(string text)
|
public Task<IUserMessage> SendPendingAsync(string text)
|
||||||
=> ctx.Channel.SendPendingAsync(_eb, text);
|
=> ctx.Channel.SendPendingAsync(_eb, text);
|
||||||
|
|
||||||
public Task<IUserMessage> ErrorLocalizedAsync(LocStr str)
|
public Task<IUserMessage> ErrorLocalizedAsync(LocStr str)
|
||||||
=> SendErrorAsync(GetText(str));
|
=> SendErrorAsync(GetText(str));
|
||||||
|
|
||||||
public Task<IUserMessage> PendingLocalizedAsync(LocStr str)
|
public Task<IUserMessage> PendingLocalizedAsync(LocStr str)
|
||||||
=> SendPendingAsync(GetText(str));
|
=> SendPendingAsync(GetText(str));
|
||||||
|
|
||||||
public Task<IUserMessage> ConfirmLocalizedAsync(LocStr str)
|
public Task<IUserMessage> ConfirmLocalizedAsync(LocStr str)
|
||||||
=> SendConfirmAsync(GetText(str));
|
=> SendConfirmAsync(GetText(str));
|
||||||
|
|
||||||
public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str)
|
public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str)
|
||||||
=> SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
|
=> SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
|
||||||
|
|
||||||
public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str)
|
public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str)
|
||||||
=> SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
|
=> SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
|
||||||
|
|
||||||
public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str)
|
public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str)
|
||||||
@@ -62,17 +80,19 @@ public abstract class NadekoModule : ModuleBase
|
|||||||
|
|
||||||
public async Task<bool> PromptUserConfirmAsync(IEmbedBuilder embed)
|
public async Task<bool> PromptUserConfirmAsync(IEmbedBuilder embed)
|
||||||
{
|
{
|
||||||
embed
|
embed.WithPendingColor()
|
||||||
.WithPendingColor()
|
|
||||||
.WithFooter("yes/no");
|
.WithFooter("yes/no");
|
||||||
|
|
||||||
var msg = await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
var msg = await ctx.Channel.EmbedAsync(embed)
|
||||||
|
.ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var input = await GetUserInputAsync(ctx.User.Id, ctx.Channel.Id).ConfigureAwait(false);
|
var input = await GetUserInputAsync(ctx.User.Id, ctx.Channel.Id)
|
||||||
|
.ConfigureAwait(false);
|
||||||
input = input?.ToUpperInvariant();
|
input = input?.ToUpperInvariant();
|
||||||
|
|
||||||
if (input != "YES" && input != "Y")
|
if (input != "YES" &&
|
||||||
|
input != "Y")
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -94,7 +114,9 @@ public abstract class NadekoModule : ModuleBase
|
|||||||
{
|
{
|
||||||
dsc.MessageReceived += MessageReceived;
|
dsc.MessageReceived += MessageReceived;
|
||||||
|
|
||||||
if (await Task.WhenAny(userInputTask.Task, Task.Delay(10000)).ConfigureAwait(false) != userInputTask.Task)
|
if (await Task.WhenAny(userInputTask.Task, Task.Delay(10000))
|
||||||
|
.ConfigureAwait(false) !=
|
||||||
|
userInputTask.Task)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -109,21 +131,23 @@ public abstract class NadekoModule : ModuleBase
|
|||||||
Task MessageReceived(SocketMessage arg)
|
Task MessageReceived(SocketMessage arg)
|
||||||
{
|
{
|
||||||
var _ = Task.Run(() =>
|
var _ = Task.Run(() =>
|
||||||
{
|
|
||||||
if (arg is not SocketUserMessage userMsg ||
|
|
||||||
userMsg.Channel is not ITextChannel chan ||
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
);
|
||||||
if (userInputTask.TrySetResult(arg.Content))
|
|
||||||
{
|
|
||||||
userMsg.DeleteAfter(1);
|
|
||||||
}
|
|
||||||
return Task.CompletedTask;
|
|
||||||
});
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,20 +156,12 @@ public abstract class NadekoModule : ModuleBase
|
|||||||
public abstract class NadekoModule<TService> : NadekoModule
|
public abstract class NadekoModule<TService> : NadekoModule
|
||||||
{
|
{
|
||||||
public TService _service { get; set; }
|
public TService _service { get; set; }
|
||||||
|
|
||||||
protected NadekoModule() : base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class NadekoSubmodule : NadekoModule
|
public abstract class NadekoSubmodule : NadekoModule
|
||||||
{
|
{
|
||||||
protected NadekoSubmodule() : base() { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class NadekoSubmodule<TService> : NadekoModule<TService>
|
public abstract class NadekoSubmodule<TService> : NadekoModule<TService>
|
||||||
{
|
{
|
||||||
protected NadekoSubmodule() : base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -1,6 +0,0 @@
|
|||||||
namespace NadekoBot.Modules;
|
|
||||||
|
|
||||||
public static class NadekoModuleExtensions
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
@@ -6,7 +6,8 @@ public class NadekoRandom : Random
|
|||||||
{
|
{
|
||||||
private readonly RandomNumberGenerator _rng;
|
private readonly RandomNumberGenerator _rng;
|
||||||
|
|
||||||
public NadekoRandom() : base()
|
public NadekoRandom()
|
||||||
|
: base()
|
||||||
=> _rng = RandomNumberGenerator.Create();
|
=> _rng = RandomNumberGenerator.Create();
|
||||||
|
|
||||||
public override int Next()
|
public override int Next()
|
||||||
|
@@ -1,12 +1,15 @@
|
|||||||
namespace NadekoBot.Common;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace NadekoBot.Common;
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
|
||||||
|
[SuppressMessage("Style", "IDE0022:Use expression body for methods")]
|
||||||
public sealed class NoPublicBotAttribute : PreconditionAttribute
|
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
|
#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/)."));
|
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/)."));
|
||||||
#else
|
#else
|
||||||
return Task.FromResult(PreconditionResult.FromSuccess());
|
return Task.FromResult(PreconditionResult.FromSuccess());
|
||||||
#endif
|
#endif
|
||||||
|
@@ -4,15 +4,18 @@ namespace NadekoBot.Common;
|
|||||||
|
|
||||||
public static class OptionsParser
|
public static class OptionsParser
|
||||||
{
|
{
|
||||||
public static T ParseFrom<T>(string[] args) where T : INadekoCommandOptions, new()
|
public static T ParseFrom<T>(string[] args)
|
||||||
|
where T : INadekoCommandOptions, new()
|
||||||
=> ParseFrom(new T(), args).Item1;
|
=> ParseFrom(new T(), args).Item1;
|
||||||
|
|
||||||
public static (T, bool) ParseFrom<T>(T options, string[] args) where T : INadekoCommandOptions
|
public static (T, bool) ParseFrom<T>(T options, string[] args)
|
||||||
|
where T : INadekoCommandOptions
|
||||||
{
|
{
|
||||||
using var p = new Parser(x =>
|
using var p = new Parser(x =>
|
||||||
{
|
{
|
||||||
x.HelpWriter = null;
|
x.HelpWriter = null;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
var res = p.ParseArguments<T>(args);
|
var res = p.ParseArguments<T>(args);
|
||||||
options = res.MapResult(x => x, x => options);
|
options = res.MapResult(x => x, x => options);
|
||||||
options.NormalizeOptions();
|
options.NormalizeOptions();
|
||||||
|
@@ -4,37 +4,54 @@ namespace NadekoBot.Common;
|
|||||||
|
|
||||||
public class OsuUserBests
|
public class OsuUserBests
|
||||||
{
|
{
|
||||||
[JsonProperty("beatmap_id")] public string BeatmapId { get; set; }
|
[JsonProperty("beatmap_id")]
|
||||||
|
public string BeatmapId { get; set; }
|
||||||
|
|
||||||
[JsonProperty("score_id")] public string ScoreId { get; set; }
|
[JsonProperty("score_id")]
|
||||||
|
public string ScoreId { get; set; }
|
||||||
|
|
||||||
[JsonProperty("score")] public string Score { get; set; }
|
[JsonProperty("score")]
|
||||||
|
public string Score { get; set; }
|
||||||
|
|
||||||
[JsonProperty("maxcombo")] public string Maxcombo { get; set; }
|
[JsonProperty("maxcombo")]
|
||||||
|
public string Maxcombo { get; set; }
|
||||||
|
|
||||||
[JsonProperty("count50")] public double Count50 { get; set; }
|
[JsonProperty("count50")]
|
||||||
|
public double Count50 { get; set; }
|
||||||
|
|
||||||
[JsonProperty("count100")] public double Count100 { get; set; }
|
[JsonProperty("count100")]
|
||||||
|
public double Count100 { get; set; }
|
||||||
|
|
||||||
[JsonProperty("count300")] public double Count300 { get; set; }
|
[JsonProperty("count300")]
|
||||||
|
public double Count300 { get; set; }
|
||||||
|
|
||||||
[JsonProperty("countmiss")] public int Countmiss { get; set; }
|
[JsonProperty("countmiss")]
|
||||||
|
public int Countmiss { get; set; }
|
||||||
|
|
||||||
[JsonProperty("countkatu")] public double Countkatu { get; set; }
|
[JsonProperty("countkatu")]
|
||||||
|
public double Countkatu { get; set; }
|
||||||
|
|
||||||
[JsonProperty("countgeki")] public double Countgeki { get; set; }
|
[JsonProperty("countgeki")]
|
||||||
|
public double Countgeki { get; set; }
|
||||||
|
|
||||||
[JsonProperty("perfect")] public string Perfect { get; set; }
|
[JsonProperty("perfect")]
|
||||||
|
public string Perfect { get; set; }
|
||||||
|
|
||||||
[JsonProperty("enabled_mods")] public int EnabledMods { get; set; }
|
[JsonProperty("enabled_mods")]
|
||||||
|
public int EnabledMods { get; set; }
|
||||||
|
|
||||||
[JsonProperty("user_id")] public string UserId { get; set; }
|
[JsonProperty("user_id")]
|
||||||
|
public string UserId { get; set; }
|
||||||
|
|
||||||
[JsonProperty("date")] public string Date { get; set; }
|
[JsonProperty("date")]
|
||||||
|
public string Date { get; set; }
|
||||||
|
|
||||||
[JsonProperty("rank")] public string Rank { get; set; }
|
[JsonProperty("rank")]
|
||||||
|
public string Rank { get; set; }
|
||||||
|
|
||||||
[JsonProperty("pp")] public double Pp { get; set; }
|
[JsonProperty("pp")]
|
||||||
|
public double Pp { get; set; }
|
||||||
|
|
||||||
[JsonProperty("replay_available")] public string ReplayAvailable { get; set; }
|
[JsonProperty("replay_available")]
|
||||||
|
public string ReplayAvailable { get; set; }
|
||||||
}
|
}
|
@@ -2,21 +2,24 @@
|
|||||||
|
|
||||||
public static class PlatformHelper
|
public static class PlatformHelper
|
||||||
{
|
{
|
||||||
private const int ProcessorCountRefreshIntervalMs = 30000;
|
private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30000;
|
||||||
|
|
||||||
private static volatile int _processorCount;
|
private static volatile int processorCount;
|
||||||
private static volatile int _lastProcessorCountRefreshTicks;
|
private static volatile int lastProcessorCountRefreshTicks;
|
||||||
|
|
||||||
public static int ProcessorCount {
|
public static int ProcessorCount
|
||||||
get {
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
var now = Environment.TickCount;
|
var now = Environment.TickCount;
|
||||||
if (_processorCount == 0 || now - _lastProcessorCountRefreshTicks >= ProcessorCountRefreshIntervalMs)
|
if (processorCount == 0 ||
|
||||||
|
now - lastProcessorCountRefreshTicks >= PROCESSOR_COUNT_REFRESH_INTERVAL_MS)
|
||||||
{
|
{
|
||||||
_processorCount = Environment.ProcessorCount;
|
processorCount = Environment.ProcessorCount;
|
||||||
_lastProcessorCountRefreshTicks = now;
|
lastProcessorCountRefreshTicks = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _processorCount;
|
return processorCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -12,19 +12,21 @@ public class SearchPokemon
|
|||||||
|
|
||||||
public class BaseStatsClass
|
public class BaseStatsClass
|
||||||
{
|
{
|
||||||
public int HP { get; set; }
|
public int Hp { get; set; }
|
||||||
public int ATK { get; set; }
|
public int Atk { get; set; }
|
||||||
public int DEF { get; set; }
|
public int Def { get; set; }
|
||||||
public int SPA { get; set; }
|
public int Spa { get; set; }
|
||||||
public int SPD { get; set; }
|
public int Spd { get; set; }
|
||||||
public int SPE { get; set; }
|
public int Spe { get; set; }
|
||||||
|
|
||||||
public override string ToString() => $@"💚**HP:** {HP,-4} ⚔**ATK:** {ATK,-4} 🛡**DEF:** {DEF,-4}
|
public override string ToString()
|
||||||
✨**SPA:** {SPA,-4} 🎇**SPD:** {SPD,-4} 💨**SPE:** {SPE,-4}";
|
=> $@"💚**HP:** {Hp,-4} ⚔**ATK:** {Atk,-4} 🛡**DEF:** {Def,-4}
|
||||||
|
✨**SPA:** {Spa,-4} 🎇**SPD:** {Spd,-4} 💨**SPE:** {Spe,-4}";
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty("num")]
|
[JsonProperty("num")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
public string Species { get; set; }
|
public string Species { get; set; }
|
||||||
public string[] Types { get; set; }
|
public string[] Types { get; set; }
|
||||||
public GenderRatioClass GenderRatio { get; set; }
|
public GenderRatioClass GenderRatio { get; set; }
|
||||||
|
@@ -3,22 +3,20 @@
|
|||||||
public class EventPubSub : IPubSub
|
public class EventPubSub : IPubSub
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, Dictionary<Delegate, List<Func<object, ValueTask>>>> _actions = new();
|
private readonly Dictionary<string, Dictionary<Delegate, List<Func<object, ValueTask>>>> _actions = new();
|
||||||
private readonly object locker = new();
|
private readonly object _locker = new();
|
||||||
|
|
||||||
public Task Sub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action)
|
public Task Sub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action)
|
||||||
{
|
{
|
||||||
Func<object, ValueTask> localAction = obj => action((TData) obj);
|
Func<object, ValueTask> localAction = obj => action((TData)obj);
|
||||||
lock(locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
Dictionary<Delegate, List<Func<object, ValueTask>>> keyActions;
|
if (!_actions.TryGetValue(key.Key, out var keyActions))
|
||||||
if (!_actions.TryGetValue(key.Key, out keyActions))
|
|
||||||
{
|
{
|
||||||
keyActions = new();
|
keyActions = new();
|
||||||
_actions[key.Key] = keyActions;
|
_actions[key.Key] = keyActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Func<object, ValueTask>> sameActions;
|
if (!keyActions.TryGetValue(action, out var sameActions))
|
||||||
if (!keyActions.TryGetValue(action, out sameActions))
|
|
||||||
{
|
{
|
||||||
sameActions = new();
|
sameActions = new();
|
||||||
keyActions[action] = sameActions;
|
keyActions[action] = sameActions;
|
||||||
@@ -29,19 +27,17 @@ public class EventPubSub : IPubSub
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Pub<TData>(in TypedKey<TData> key, TData data)
|
public Task Pub<TData>(in TypedKey<TData> key, TData data)
|
||||||
{
|
{
|
||||||
lock (locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
if(_actions.TryGetValue(key.Key, out var actions))
|
if (_actions.TryGetValue(key.Key, out var actions))
|
||||||
{
|
{
|
||||||
// if this class ever gets used, this needs to be properly implemented
|
// if this class ever gets used, this needs to be properly implemented
|
||||||
// 1. ignore all valuetasks which are completed
|
// 1. ignore all valuetasks which are completed
|
||||||
// 2. return task.whenall all other tasks
|
// 2. return task.whenall all other tasks
|
||||||
return Task.WhenAll(actions
|
return Task.WhenAll(actions.SelectMany(kvp => kvp.Value).Select(action => action(data).AsTask()));
|
||||||
.SelectMany(kvp => kvp.Value)
|
|
||||||
.Select(action => action(data).AsTask()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@@ -50,12 +46,11 @@ public class EventPubSub : IPubSub
|
|||||||
|
|
||||||
public Task Unsub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action)
|
public Task Unsub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action)
|
||||||
{
|
{
|
||||||
lock (locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
// get subscriptions for this action
|
// get subscriptions for this action
|
||||||
if (_actions.TryGetValue(key.Key, out var actions))
|
if (_actions.TryGetValue(key.Key, out var actions))
|
||||||
{
|
{
|
||||||
var hashCode = action.GetHashCode();
|
|
||||||
// get subscriptions which have the same action hash code
|
// get subscriptions which have the same action hash code
|
||||||
// note: having this as a list allows for multiple subscriptions of
|
// note: having this as a list allows for multiple subscriptions of
|
||||||
// the same insance's/static method
|
// the same insance's/static method
|
||||||
@@ -63,13 +58,13 @@ public class EventPubSub : IPubSub
|
|||||||
{
|
{
|
||||||
// remove last subscription
|
// remove last subscription
|
||||||
sameActions.RemoveAt(sameActions.Count - 1);
|
sameActions.RemoveAt(sameActions.Count - 1);
|
||||||
|
|
||||||
// if the last subscription was the only subscription
|
// if the last subscription was the only subscription
|
||||||
// we can safely remove this action's dictionary entry
|
// we can safely remove this action's dictionary entry
|
||||||
if (sameActions.Count == 0)
|
if (sameActions.Count == 0)
|
||||||
{
|
{
|
||||||
actions.Remove(action);
|
actions.Remove(action);
|
||||||
|
|
||||||
// if our dictionary has no more elements after
|
// if our dictionary has no more elements after
|
||||||
// removing the entry
|
// removing the entry
|
||||||
// it's safe to remove it from the key's subscriptions
|
// it's safe to remove it from the key's subscriptions
|
||||||
|
@@ -5,23 +5,19 @@ namespace NadekoBot.Common;
|
|||||||
|
|
||||||
public class JsonSeria : ISeria
|
public class JsonSeria : ISeria
|
||||||
{
|
{
|
||||||
private readonly JsonSerializerOptions serializerOptions = new()
|
private readonly JsonSerializerOptions _serializerOptions = new()
|
||||||
{
|
{
|
||||||
Converters =
|
Converters = { new Rgba32Converter(), new CultureInfoConverter(), }
|
||||||
{
|
|
||||||
new Rgba32Converter(),
|
|
||||||
new CultureInfoConverter(),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
public byte[] Serialize<T>(T data)
|
|
||||||
=> JsonSerializer.SerializeToUtf8Bytes(data, serializerOptions);
|
public byte[] Serialize<T>(T data)
|
||||||
|
=> JsonSerializer.SerializeToUtf8Bytes(data, _serializerOptions);
|
||||||
|
|
||||||
public T Deserialize<T>(byte[] data)
|
public T Deserialize<T>(byte[] data)
|
||||||
{
|
{
|
||||||
if (data is null)
|
if (data is null)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
|
return JsonSerializer.Deserialize<T>(data, _serializerOptions);
|
||||||
return JsonSerializer.Deserialize<T>(data, serializerOptions);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -18,13 +18,15 @@ public sealed class RedisPubSub : IPubSub
|
|||||||
public Task Pub<TData>(in TypedKey<TData> key, TData data)
|
public Task Pub<TData>(in TypedKey<TData> key, TData data)
|
||||||
{
|
{
|
||||||
var serialized = _serializer.Serialize(data);
|
var serialized = _serializer.Serialize(data);
|
||||||
return _multi.GetSubscriber().PublishAsync($"{_creds.RedisKey()}:{key.Key}", serialized, CommandFlags.FireAndForget);
|
return _multi.GetSubscriber()
|
||||||
|
.PublishAsync($"{_creds.RedisKey()}:{key.Key}", serialized, CommandFlags.FireAndForget);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Sub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action)
|
public Task Sub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action)
|
||||||
{
|
{
|
||||||
var eventName = key.Key;
|
var eventName = key.Key;
|
||||||
return _multi.GetSubscriber().SubscribeAsync($"{_creds.RedisKey()}:{eventName}", async (ch, data) =>
|
|
||||||
|
async void OnSubscribeHandler(RedisChannel _, RedisValue data)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -33,8 +35,10 @@ public sealed class RedisPubSub : IPubSub
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Error($"Error handling the event {eventName}: {ex.Message}");
|
Log.Error("Error handling the event {EventName}: {ErrorMessage}", eventName, ex.Message);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
return _multi.GetSubscriber().SubscribeAsync($"{_creds.RedisKey()}:{eventName}", OnSubscribeHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -9,18 +9,22 @@ public readonly struct TypedKey<TData>
|
|||||||
|
|
||||||
public static implicit operator TypedKey<TData>(in string input)
|
public static implicit operator TypedKey<TData>(in string input)
|
||||||
=> new(input);
|
=> new(input);
|
||||||
|
|
||||||
public static implicit operator string(in TypedKey<TData> input)
|
public static implicit operator string(in TypedKey<TData> input)
|
||||||
=> input.Key;
|
=> input.Key;
|
||||||
|
|
||||||
public static bool operator ==(in TypedKey<TData> left, in TypedKey<TData> right)
|
public static bool operator ==(in TypedKey<TData> left, in TypedKey<TData> right)
|
||||||
=> left.Key == right.Key;
|
=> left.Key == right.Key;
|
||||||
|
|
||||||
public static bool operator !=(in TypedKey<TData> left, in TypedKey<TData> right)
|
public static bool operator !=(in TypedKey<TData> left, in TypedKey<TData> right)
|
||||||
=> !(left == right);
|
=> !(left == right);
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
=> obj is TypedKey<TData> o && o == this;
|
=> obj is TypedKey<TData> o && o == this;
|
||||||
|
|
||||||
public override int GetHashCode() => Key?.GetHashCode() ?? 0;
|
public override int GetHashCode()
|
||||||
|
=> Key?.GetHashCode() ?? 0;
|
||||||
|
|
||||||
public override string ToString() => Key;
|
public override string ToString()
|
||||||
|
=> Key;
|
||||||
}
|
}
|
@@ -10,28 +10,31 @@ public class YamlSeria : IConfigSeria
|
|||||||
private readonly ISerializer _serializer;
|
private readonly ISerializer _serializer;
|
||||||
private readonly IDeserializer _deserializer;
|
private readonly IDeserializer _deserializer;
|
||||||
|
|
||||||
private static readonly Regex CodePointRegex
|
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}))",
|
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
|
||||||
|
);
|
||||||
|
|
||||||
public YamlSeria()
|
public YamlSeria()
|
||||||
{
|
{
|
||||||
_serializer = Yaml.Serializer;
|
_serializer = Yaml.Serializer;
|
||||||
_deserializer = Yaml.Deserializer;
|
_deserializer = Yaml.Deserializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Serialize<T>(T obj)
|
public string Serialize<T>(T obj)
|
||||||
{
|
{
|
||||||
var escapedOutput = _serializer.Serialize(obj);
|
var escapedOutput = _serializer.Serialize(obj);
|
||||||
var output = CodePointRegex.Replace(escapedOutput, me =>
|
var output = _codePointRegex.Replace(escapedOutput,
|
||||||
{
|
me =>
|
||||||
var str = me.Groups["code"].Value;
|
{
|
||||||
var newString = YamlHelper.UnescapeUnicodeCodePoint(str);
|
var str = me.Groups["code"].Value;
|
||||||
return newString;
|
var newString = YamlHelper.UnescapeUnicodeCodePoint(str);
|
||||||
});
|
return newString;
|
||||||
|
}
|
||||||
|
);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Deserialize<T>(string data)
|
public T Deserialize<T>(string data)
|
||||||
=> _deserializer.Deserialize<T>(data);
|
=> _deserializer.Deserialize<T>(data);
|
||||||
}
|
}
|
@@ -5,21 +5,29 @@ namespace NadekoBot.Common;
|
|||||||
|
|
||||||
public class ReplacementBuilder
|
public class ReplacementBuilder
|
||||||
{
|
{
|
||||||
private static readonly Regex rngRegex = new("%rng(?:(?<from>(?:-)?\\d+)-(?<to>(?:-)?\\d+))?%", RegexOptions.Compiled);
|
private static readonly Regex _rngRegex = new("%rng(?:(?<from>(?:-)?\\d+)-(?<to>(?:-)?\\d+))?%",
|
||||||
|
RegexOptions.Compiled
|
||||||
|
);
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, Func<string>> _reps = new();
|
private readonly ConcurrentDictionary<string, Func<string>> _reps = new();
|
||||||
private readonly ConcurrentDictionary<Regex, Func<Match, string>> _regex = new();
|
private readonly ConcurrentDictionary<Regex, Func<Match, string>> _regex = new();
|
||||||
|
|
||||||
public ReplacementBuilder()
|
public ReplacementBuilder()
|
||||||
=> WithRngRegex();
|
=> WithRngRegex();
|
||||||
|
|
||||||
public ReplacementBuilder WithDefault(IUser usr, IMessageChannel ch, SocketGuild g, DiscordSocketClient client)
|
public ReplacementBuilder WithDefault(
|
||||||
=> this.WithUser(usr)
|
IUser usr,
|
||||||
.WithChannel(ch)
|
IMessageChannel ch,
|
||||||
.WithServer(client, g)
|
SocketGuild g,
|
||||||
.WithClient(client);
|
DiscordSocketClient client)
|
||||||
|
=> this.WithUser(usr).WithChannel(ch).WithServer(client, g).WithClient(client);
|
||||||
|
|
||||||
public ReplacementBuilder WithDefault(ICommandContext ctx) =>
|
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)
|
public ReplacementBuilder WithMention(DiscordSocketClient client)
|
||||||
{
|
{
|
||||||
@@ -30,12 +38,14 @@ public class ReplacementBuilder
|
|||||||
public ReplacementBuilder WithClient(DiscordSocketClient client)
|
public ReplacementBuilder WithClient(DiscordSocketClient client)
|
||||||
{
|
{
|
||||||
WithMention(client);
|
WithMention(client);
|
||||||
|
|
||||||
_reps.TryAdd("%bot.status%", () => client.Status.ToString());
|
_reps.TryAdd("%bot.status%", () => client.Status.ToString());
|
||||||
_reps.TryAdd("%bot.latency%", () => client.Latency.ToString());
|
_reps.TryAdd("%bot.latency%", () => client.Latency.ToString());
|
||||||
_reps.TryAdd("%bot.name%", () => client.CurrentUser.Username);
|
_reps.TryAdd("%bot.name%", () => client.CurrentUser.Username);
|
||||||
_reps.TryAdd("%bot.fullname%", () => client.CurrentUser.ToString());
|
_reps.TryAdd("%bot.fullname%", () => client.CurrentUser.ToString());
|
||||||
_reps.TryAdd("%bot.time%", () => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()));
|
_reps.TryAdd("%bot.time%",
|
||||||
|
() => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials())
|
||||||
|
);
|
||||||
_reps.TryAdd("%bot.discrim%", () => client.CurrentUser.Discriminator);
|
_reps.TryAdd("%bot.discrim%", () => client.CurrentUser.Discriminator);
|
||||||
_reps.TryAdd("%bot.id%", () => client.CurrentUser.Id.ToString());
|
_reps.TryAdd("%bot.id%", () => client.CurrentUser.Id.ToString());
|
||||||
_reps.TryAdd("%bot.avatar%", () => client.CurrentUser.RealAvatarUrl()?.ToString());
|
_reps.TryAdd("%bot.avatar%", () => client.CurrentUser.RealAvatarUrl()?.ToString());
|
||||||
@@ -51,19 +61,20 @@ public class ReplacementBuilder
|
|||||||
_reps.TryAdd("%server.members%", () => g is { } sg ? sg.MemberCount.ToString() : "?");
|
_reps.TryAdd("%server.members%", () => g is { } sg ? sg.MemberCount.ToString() : "?");
|
||||||
_reps.TryAdd("%server.boosters%", () => g.PremiumSubscriptionCount.ToString());
|
_reps.TryAdd("%server.boosters%", () => g.PremiumSubscriptionCount.ToString());
|
||||||
_reps.TryAdd("%server.boost_level%", () => ((int)g.PremiumTier).ToString());
|
_reps.TryAdd("%server.boost_level%", () => ((int)g.PremiumTier).ToString());
|
||||||
_reps.TryAdd("%server.time%", () =>
|
_reps.TryAdd("%server.time%",
|
||||||
{
|
() =>
|
||||||
var to = TimeZoneInfo.Local;
|
|
||||||
if (g != null)
|
|
||||||
{
|
{
|
||||||
if (GuildTimezoneService.AllServices.TryGetValue(client.CurrentUser.Id, out var tz))
|
var to = TimeZoneInfo.Local;
|
||||||
to = tz.GetTimeZoneOrDefault(g.Id) ?? 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,
|
return TimeZoneInfo.ConvertTime(DateTime.UtcNow, TimeZoneInfo.Utc, to).ToString("HH:mm ") +
|
||||||
TimeZoneInfo.Utc,
|
to.StandardName.GetInitials();
|
||||||
to).ToString("HH:mm ") + to.StandardName.GetInitials();
|
}
|
||||||
});
|
);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +91,7 @@ public class ReplacementBuilder
|
|||||||
|
|
||||||
public ReplacementBuilder WithUser(IUser user)
|
public ReplacementBuilder WithUser(IUser user)
|
||||||
{
|
{
|
||||||
WithManyUsers(new[] {user});
|
WithManyUsers(new[] { user });
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,10 +103,18 @@ public class ReplacementBuilder
|
|||||||
_reps.TryAdd("%user.discrim%", () => string.Join(" ", users.Select(user => user.Discriminator)));
|
_reps.TryAdd("%user.discrim%", () => string.Join(" ", users.Select(user => user.Discriminator)));
|
||||||
_reps.TryAdd("%user.avatar%", () => string.Join(" ", users.Select(user => user.RealAvatarUrl()?.ToString())));
|
_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.id%", () => string.Join(" ", users.Select(user => user.Id.ToString())));
|
||||||
_reps.TryAdd("%user.created_time%", () => string.Join(" ", users.Select(user => user.CreatedAt.ToString("HH:mm"))));
|
_reps.TryAdd("%user.created_time%",
|
||||||
_reps.TryAdd("%user.created_date%", () => string.Join(" ", users.Select(user => user.CreatedAt.ToString("dd.MM.yyyy"))));
|
() => string.Join(" ", users.Select(user => user.CreatedAt.ToString("HH:mm")))
|
||||||
_reps.TryAdd("%user.joined_time%", () => 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") ?? "-")));
|
_reps.TryAdd("%user.created_date%",
|
||||||
|
() => 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") ?? "-"))
|
||||||
|
);
|
||||||
|
_reps.TryAdd("%user.joined_date%",
|
||||||
|
() => string.Join(" ", users.Select(user => (user as IGuildUser)?.JoinedAt?.ToString("dd.MM.yyyy") ?? "-"))
|
||||||
|
);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,21 +129,24 @@ public class ReplacementBuilder
|
|||||||
public ReplacementBuilder WithRngRegex()
|
public ReplacementBuilder WithRngRegex()
|
||||||
{
|
{
|
||||||
var rng = new NadekoRandom();
|
var rng = new NadekoRandom();
|
||||||
_regex.TryAdd(rngRegex, match =>
|
_regex.TryAdd(_rngRegex,
|
||||||
{
|
match =>
|
||||||
if (!int.TryParse(match.Groups["from"].ToString(), out var from))
|
{
|
||||||
from = 0;
|
if (!int.TryParse(match.Groups["from"].ToString(), out var from))
|
||||||
if (!int.TryParse(match.Groups["to"].ToString(), out var to))
|
from = 0;
|
||||||
to = 0;
|
if (!int.TryParse(match.Groups["to"].ToString(), out var to))
|
||||||
|
to = 0;
|
||||||
|
|
||||||
if (from == 0 && to == 0)
|
if (from == 0 &&
|
||||||
return rng.Next(0, 11).ToString();
|
to == 0)
|
||||||
|
return rng.Next(0, 11).ToString();
|
||||||
|
|
||||||
if (from >= to)
|
if (from >= to)
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
||||||
return rng.Next(from, to + 1).ToString();
|
return rng.Next(from, to + 1).ToString();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,10 +18,10 @@ public class Replacer
|
|||||||
if (string.IsNullOrWhiteSpace(input))
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
return input;
|
return input;
|
||||||
|
|
||||||
foreach (var (Key, Text) in _replacements)
|
foreach (var (key, text) in _replacements)
|
||||||
{
|
{
|
||||||
if (input.Contains(Key))
|
if (input.Contains(key))
|
||||||
input = input.Replace(Key, Text(), StringComparison.InvariantCulture);
|
input = input.Replace(key, text(), StringComparison.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var item in _regex)
|
foreach (var item in _regex)
|
||||||
@@ -58,8 +58,7 @@ public class Replacer
|
|||||||
{
|
{
|
||||||
newEmbedData.Author = new()
|
newEmbedData.Author = new()
|
||||||
{
|
{
|
||||||
Name = Replace(embedData.Author.Name),
|
Name = Replace(embedData.Author.Name), IconUrl = Replace(embedData.Author.IconUrl)
|
||||||
IconUrl = Replace(embedData.Author.IconUrl)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,9 +69,7 @@ public class Replacer
|
|||||||
{
|
{
|
||||||
var newF = new SmartTextEmbedField
|
var newF = new SmartTextEmbedField
|
||||||
{
|
{
|
||||||
Name = Replace(f.Name),
|
Name = Replace(f.Name), Value = Replace(f.Value), Inline = f.Inline
|
||||||
Value = Replace(f.Value),
|
|
||||||
Inline = f.Inline
|
|
||||||
};
|
};
|
||||||
fields.Add(newF);
|
fields.Add(newF);
|
||||||
}
|
}
|
||||||
@@ -84,8 +81,7 @@ public class Replacer
|
|||||||
{
|
{
|
||||||
newEmbedData.Footer = new()
|
newEmbedData.Footer = new()
|
||||||
{
|
{
|
||||||
Text = Replace(embedData.Footer.Text),
|
Text = Replace(embedData.Footer.Text), IconUrl = Replace(embedData.Footer.IconUrl)
|
||||||
IconUrl = Replace(embedData.Footer.IconUrl)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,9 +24,7 @@ public struct ShmartNumber : IEquatable<ShmartNumber>
|
|||||||
=> Value.ToString();
|
=> Value.ToString();
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
=> obj is ShmartNumber sn
|
=> obj is ShmartNumber sn && Equals(sn);
|
||||||
? Equals(sn)
|
|
||||||
: false;
|
|
||||||
|
|
||||||
public bool Equals(ShmartNumber other)
|
public bool Equals(ShmartNumber other)
|
||||||
=> other.Value == Value;
|
=> other.Value == Value;
|
||||||
|
@@ -15,14 +15,13 @@ public sealed record SmartEmbedText : SmartText
|
|||||||
|
|
||||||
public uint Color { get; set; } = 7458112;
|
public uint Color { get; set; } = 7458112;
|
||||||
|
|
||||||
public bool IsValid =>
|
public bool IsValid
|
||||||
!string.IsNullOrWhiteSpace(Title) ||
|
=> !string.IsNullOrWhiteSpace(Title) || !string.IsNullOrWhiteSpace(Description) ||
|
||||||
!string.IsNullOrWhiteSpace(Description) ||
|
!string.IsNullOrWhiteSpace(Url) || !string.IsNullOrWhiteSpace(Thumbnail) ||
|
||||||
!string.IsNullOrWhiteSpace(Url) ||
|
!string.IsNullOrWhiteSpace(Image) ||
|
||||||
!string.IsNullOrWhiteSpace(Thumbnail) ||
|
(Footer != null &&
|
||||||
!string.IsNullOrWhiteSpace(Image) ||
|
(!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl))) ||
|
||||||
(Footer != null && (!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl))) ||
|
Fields is { Length: > 0 };
|
||||||
Fields is { Length: > 0 };
|
|
||||||
|
|
||||||
public static SmartEmbedText FromEmbed(IEmbed eb, string plainText = null)
|
public static SmartEmbedText FromEmbed(IEmbed eb, string plainText = null)
|
||||||
{
|
{
|
||||||
@@ -34,42 +33,23 @@ public sealed record SmartEmbedText : SmartText
|
|||||||
Url = eb.Url,
|
Url = eb.Url,
|
||||||
Thumbnail = eb.Thumbnail?.Url,
|
Thumbnail = eb.Thumbnail?.Url,
|
||||||
Image = eb.Image?.Url,
|
Image = eb.Image?.Url,
|
||||||
Author = eb.Author is { } ea
|
Author = eb.Author is { } ea ? new() { Name = ea.Name, Url = ea.Url, IconUrl = ea.IconUrl } : null,
|
||||||
? new()
|
Footer = eb.Footer is { } ef ? new() { Text = ef.Text, IconUrl = ef.IconUrl } : null
|
||||||
{
|
|
||||||
Name = ea.Name,
|
|
||||||
Url = ea.Url,
|
|
||||||
IconUrl = ea.IconUrl
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
Footer = eb.Footer is { } ef
|
|
||||||
? new()
|
|
||||||
{
|
|
||||||
Text = ef.Text,
|
|
||||||
IconUrl = ef.IconUrl
|
|
||||||
}
|
|
||||||
: null
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (eb.Fields.Length > 0)
|
if (eb.Fields.Length > 0)
|
||||||
set.Fields = eb
|
set.Fields = eb.Fields.Select(field
|
||||||
.Fields
|
=> new SmartTextEmbedField() { Inline = field.Inline, Name = field.Name, Value = field.Value, }
|
||||||
.Select(field => new SmartTextEmbedField()
|
)
|
||||||
{
|
|
||||||
Inline = field.Inline,
|
|
||||||
Name = field.Name,
|
|
||||||
Value = field.Value,
|
|
||||||
})
|
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
set.Color = eb.Color?.RawValue ?? 0;
|
set.Color = eb.Color?.RawValue ?? 0;
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EmbedBuilder GetEmbed()
|
public EmbedBuilder GetEmbed()
|
||||||
{
|
{
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder().WithColor(Color);
|
||||||
.WithColor(Color);
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(Title))
|
if (!string.IsNullOrWhiteSpace(Title))
|
||||||
embed.WithTitle(Title);
|
embed.WithTitle(Title);
|
||||||
@@ -77,26 +57,31 @@ public sealed record SmartEmbedText : SmartText
|
|||||||
if (!string.IsNullOrWhiteSpace(Description))
|
if (!string.IsNullOrWhiteSpace(Description))
|
||||||
embed.WithDescription(Description);
|
embed.WithDescription(Description);
|
||||||
|
|
||||||
if (Url != null && Uri.IsWellFormedUriString(Url, UriKind.Absolute))
|
if (Url != null &&
|
||||||
|
Uri.IsWellFormedUriString(Url, UriKind.Absolute))
|
||||||
embed.WithUrl(Url);
|
embed.WithUrl(Url);
|
||||||
|
|
||||||
if (Footer != null)
|
if (Footer != null)
|
||||||
{
|
{
|
||||||
embed.WithFooter(efb =>
|
embed.WithFooter(efb =>
|
||||||
{
|
{
|
||||||
efb.WithText(Footer.Text);
|
efb.WithText(Footer.Text);
|
||||||
if (Uri.IsWellFormedUriString(Footer.IconUrl, UriKind.Absolute))
|
if (Uri.IsWellFormedUriString(Footer.IconUrl, UriKind.Absolute))
|
||||||
efb.WithIconUrl(Footer.IconUrl);
|
efb.WithIconUrl(Footer.IconUrl);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Thumbnail != null && Uri.IsWellFormedUriString(Thumbnail, UriKind.Absolute))
|
if (Thumbnail != null &&
|
||||||
|
Uri.IsWellFormedUriString(Thumbnail, UriKind.Absolute))
|
||||||
embed.WithThumbnailUrl(Thumbnail);
|
embed.WithThumbnailUrl(Thumbnail);
|
||||||
|
|
||||||
if (Image != null && Uri.IsWellFormedUriString(Image, UriKind.Absolute))
|
if (Image != null &&
|
||||||
|
Uri.IsWellFormedUriString(Image, UriKind.Absolute))
|
||||||
embed.WithImageUrl(Image);
|
embed.WithImageUrl(Image);
|
||||||
|
|
||||||
if (Author != null && !string.IsNullOrWhiteSpace(Author.Name))
|
if (Author != null &&
|
||||||
|
!string.IsNullOrWhiteSpace(Author.Name))
|
||||||
{
|
{
|
||||||
if (!Uri.IsWellFormedUriString(Author.IconUrl, UriKind.Absolute))
|
if (!Uri.IsWellFormedUriString(Author.IconUrl, UriKind.Absolute))
|
||||||
Author.IconUrl = null;
|
Author.IconUrl = null;
|
||||||
@@ -110,7 +95,8 @@ public sealed record SmartEmbedText : SmartText
|
|||||||
{
|
{
|
||||||
foreach (var f in Fields)
|
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);
|
embed.AddField(f.Name, f.Value, f.Inline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,8 +4,11 @@ namespace NadekoBot;
|
|||||||
|
|
||||||
public abstract record SmartText
|
public abstract record SmartText
|
||||||
{
|
{
|
||||||
public bool IsEmbed => this is SmartEmbedText;
|
public bool IsEmbed
|
||||||
public bool IsPlainText => this is SmartPlainText;
|
=> this is SmartEmbedText;
|
||||||
|
|
||||||
|
public bool IsPlainText
|
||||||
|
=> this is SmartPlainText;
|
||||||
|
|
||||||
public static SmartText operator +(SmartText text, string input)
|
public static SmartText operator +(SmartText text, string input)
|
||||||
=> text switch
|
=> text switch
|
||||||
@@ -14,7 +17,7 @@ public abstract record SmartText
|
|||||||
SmartPlainText spt => new SmartPlainText(spt.Text + input),
|
SmartPlainText spt => new SmartPlainText(spt.Text + input),
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(text))
|
_ => throw new ArgumentOutOfRangeException(nameof(text))
|
||||||
};
|
};
|
||||||
|
|
||||||
public static SmartText operator +(string input, SmartText text)
|
public static SmartText operator +(string input, SmartText text)
|
||||||
=> text switch
|
=> text switch
|
||||||
{
|
{
|
||||||
@@ -22,10 +25,11 @@ public abstract record SmartText
|
|||||||
SmartPlainText spt => new SmartPlainText(input + spt.Text),
|
SmartPlainText spt => new SmartPlainText(input + spt.Text),
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(text))
|
_ => throw new ArgumentOutOfRangeException(nameof(text))
|
||||||
};
|
};
|
||||||
|
|
||||||
public static SmartText CreateFrom(string input)
|
public static SmartText CreateFrom(string input)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(input) || !input.TrimStart().StartsWith("{"))
|
if (string.IsNullOrWhiteSpace(input) ||
|
||||||
|
!input.TrimStart().StartsWith("{"))
|
||||||
{
|
{
|
||||||
return new SmartPlainText(input);
|
return new SmartPlainText(input);
|
||||||
}
|
}
|
||||||
@@ -34,6 +38,9 @@ public abstract record SmartText
|
|||||||
{
|
{
|
||||||
var smartEmbedText = JsonConvert.DeserializeObject<SmartEmbedText>(input);
|
var smartEmbedText = JsonConvert.DeserializeObject<SmartEmbedText>(input);
|
||||||
|
|
||||||
|
if (smartEmbedText is null)
|
||||||
|
throw new();
|
||||||
|
|
||||||
smartEmbedText.NormalizeFields();
|
smartEmbedText.NormalizeFields();
|
||||||
|
|
||||||
if (!smartEmbedText.IsValid)
|
if (!smartEmbedText.IsValid)
|
||||||
|
@@ -5,8 +5,7 @@ namespace NadekoBot;
|
|||||||
public class SmartTextEmbedAuthor
|
public class SmartTextEmbedAuthor
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string IconUrl { get; set; }
|
|
||||||
[JsonProperty("icon_url")]
|
[JsonProperty("icon_url")]
|
||||||
private string Icon_Url { set => IconUrl = value; }
|
public string IconUrl { get; set; }
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
}
|
}
|
@@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
namespace NadekoBot;
|
namespace NadekoBot;
|
||||||
|
|
||||||
|
// todo test smarttextembedfooter and smarttextembedauthor
|
||||||
|
|
||||||
public class SmartTextEmbedFooter
|
public class SmartTextEmbedFooter
|
||||||
{
|
{
|
||||||
public string Text { get; set; }
|
public string Text { get; set; }
|
||||||
public string IconUrl { get; set; }
|
|
||||||
[JsonProperty("icon_url")]
|
[JsonProperty("icon_url")]
|
||||||
private string Icon_Url { set => IconUrl = value; }
|
public string IconUrl { get; set; }
|
||||||
}
|
}
|
@@ -17,36 +17,37 @@ public sealed class ReactionEventWrapper : IDisposable
|
|||||||
_client.ReactionsCleared += Discord_ReactionsCleared;
|
_client.ReactionsCleared += Discord_ReactionsCleared;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task Discord_ReactionsCleared(
|
private Task Discord_ReactionsCleared(Cacheable<IUserMessage, ulong> msg, Cacheable<IMessageChannel, ulong> channel)
|
||||||
Cacheable<IUserMessage, ulong> msg,
|
|
||||||
Cacheable<IMessageChannel, ulong> channel)
|
|
||||||
{
|
{
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
if (msg.Id == Message.Id)
|
try
|
||||||
OnReactionsCleared?.Invoke();
|
{
|
||||||
|
if (msg.Id == Message.Id)
|
||||||
|
OnReactionsCleared?.Invoke();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
}
|
}
|
||||||
catch { }
|
);
|
||||||
});
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task Discord_ReactionRemoved(
|
private Task Discord_ReactionRemoved(
|
||||||
Cacheable<IUserMessage, ulong> msg,
|
Cacheable<IUserMessage, ulong> msg,
|
||||||
Cacheable<IMessageChannel, ulong> cacheable, SocketReaction reaction)
|
Cacheable<IMessageChannel, ulong> cacheable,
|
||||||
|
SocketReaction reaction)
|
||||||
{
|
{
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
if (msg.Id == Message.Id)
|
try
|
||||||
OnReactionRemoved?.Invoke(reaction);
|
{
|
||||||
|
if (msg.Id == Message.Id)
|
||||||
|
OnReactionRemoved?.Invoke(reaction);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
}
|
}
|
||||||
catch { }
|
);
|
||||||
});
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@@ -57,16 +58,17 @@ public sealed class ReactionEventWrapper : IDisposable
|
|||||||
SocketReaction reaction)
|
SocketReaction reaction)
|
||||||
{
|
{
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
if (msg.Id == Message.Id)
|
try
|
||||||
OnReactionAdded?.Invoke(reaction);
|
{
|
||||||
|
if (msg.Id == Message.Id)
|
||||||
|
OnReactionAdded?.Invoke(reaction);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch
|
);
|
||||||
{
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
@@ -36,10 +36,7 @@ public sealed class CommandOrCrTypeReader : NadekoTypeReader<CommandOrCrInfo>
|
|||||||
private readonly CustomReactionsService _crs;
|
private readonly CustomReactionsService _crs;
|
||||||
private readonly CommandHandler _commandHandler;
|
private readonly CommandHandler _commandHandler;
|
||||||
|
|
||||||
public CommandOrCrTypeReader(
|
public CommandOrCrTypeReader(CommandService cmds, CustomReactionsService crs, CommandHandler commandHandler)
|
||||||
CommandService cmds,
|
|
||||||
CustomReactionsService crs,
|
|
||||||
CommandHandler commandHandler)
|
|
||||||
{
|
{
|
||||||
_cmds = cmds;
|
_cmds = cmds;
|
||||||
_crs = crs;
|
_crs = crs;
|
||||||
@@ -58,8 +55,12 @@ public sealed class CommandOrCrTypeReader : NadekoTypeReader<CommandOrCrInfo>
|
|||||||
var cmd = await new CommandTypeReader(_commandHandler, _cmds).ReadAsync(context, input).ConfigureAwait(false);
|
var cmd = await new CommandTypeReader(_commandHandler, _cmds).ReadAsync(context, input).ConfigureAwait(false);
|
||||||
if (cmd.IsSuccess)
|
if (cmd.IsSuccess)
|
||||||
{
|
{
|
||||||
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name, CommandOrCrInfo.Type.Normal));
|
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name,
|
||||||
|
CommandOrCrInfo.Type.Normal
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TypeReaderResult.FromError(CommandError.ParseFailed, "No such command or cr found.");
|
return TypeReaderResult.FromError(CommandError.ParseFailed, "No such command or cr found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,7 +75,9 @@ public class CommandOrCrInfo
|
|||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public Type CmdType { get; set; }
|
public Type CmdType { get; set; }
|
||||||
public bool IsCustom => CmdType == Type.Custom;
|
|
||||||
|
public bool IsCustom
|
||||||
|
=> CmdType == Type.Custom;
|
||||||
|
|
||||||
public CommandOrCrInfo(string input, Type type)
|
public CommandOrCrInfo(string input, Type type)
|
||||||
{
|
{
|
||||||
|
@@ -12,8 +12,11 @@ public sealed class GuildDateTimeTypeReader : NadekoTypeReader<GuildDateTime>
|
|||||||
public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
|
public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
|
||||||
{
|
{
|
||||||
var gdt = Parse(context.Guild.Id, input);
|
var gdt = Parse(context.Guild.Id, input);
|
||||||
if(gdt is null)
|
if (gdt is null)
|
||||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Input string is in an incorrect format."));
|
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed,
|
||||||
|
"Input string is in an incorrect format."
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return Task.FromResult(TypeReaderResult.FromSuccess(gdt));
|
return Task.FromResult(TypeReaderResult.FromSuccess(gdt));
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,9 @@ public sealed class ModuleTypeReader : NadekoTypeReader<ModuleInfo>
|
|||||||
public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
|
public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
|
||||||
{
|
{
|
||||||
input = input.ToUpperInvariant();
|
input = input.ToUpperInvariant();
|
||||||
var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule()).FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input)?.Key;
|
var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule())
|
||||||
|
.FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input)
|
||||||
|
?.Key;
|
||||||
if (module is null)
|
if (module is null)
|
||||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No such module found."));
|
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No such module found."));
|
||||||
|
|
||||||
@@ -28,14 +30,14 @@ public sealed class ModuleOrCrTypeReader : NadekoTypeReader<ModuleOrCrInfo>
|
|||||||
public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
|
public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
|
||||||
{
|
{
|
||||||
input = input.ToUpperInvariant();
|
input = input.ToUpperInvariant();
|
||||||
var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule()).FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input)?.Key;
|
var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule())
|
||||||
if (module is null && input != "ACTUALCUSTOMREACTIONS")
|
.FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input)
|
||||||
|
?.Key;
|
||||||
|
if (module is null &&
|
||||||
|
input != "ACTUALCUSTOMREACTIONS")
|
||||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No such module found."));
|
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No such module found."));
|
||||||
|
|
||||||
return Task.FromResult(TypeReaderResult.FromSuccess(new ModuleOrCrInfo
|
return Task.FromResult(TypeReaderResult.FromSuccess(new ModuleOrCrInfo { Name = input, }));
|
||||||
{
|
|
||||||
Name = input,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
namespace NadekoBot.Common.TypeReaders;
|
namespace NadekoBot.Common.TypeReaders;
|
||||||
|
|
||||||
|
[MeansImplicitUse(ImplicitUseTargetFlags.Default | ImplicitUseTargetFlags.WithInheritors )]
|
||||||
public abstract class NadekoTypeReader<T> : TypeReader
|
public abstract class NadekoTypeReader<T> : TypeReader
|
||||||
{
|
{
|
||||||
public abstract Task<TypeReaderResult> ReadAsync(ICommandContext ctx, string input);
|
public abstract Task<TypeReaderResult> ReadAsync(ICommandContext ctx, string input);
|
||||||
|
@@ -63,12 +63,10 @@ public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
|
|||||||
case "MAX":
|
case "MAX":
|
||||||
args.Result = Max(ctx);
|
args.Result = Max(ctx);
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Regex percentRegex = new(@"^((?<num>100|\d{1,2})%)$", RegexOptions.Compiled);
|
private static readonly Regex _percentRegex = new(@"^((?<num>100|\d{1,2})%)$", RegexOptions.Compiled);
|
||||||
|
|
||||||
private long Cur(ICommandContext ctx)
|
private long Cur(ICommandContext ctx)
|
||||||
{
|
{
|
||||||
@@ -80,15 +78,13 @@ public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
|
|||||||
{
|
{
|
||||||
var settings = _gambling.Data;
|
var settings = _gambling.Data;
|
||||||
var max = settings.MaxBet;
|
var max = settings.MaxBet;
|
||||||
return max == 0
|
return max == 0 ? Cur(ctx) : max;
|
||||||
? Cur(ctx)
|
|
||||||
: max;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryHandlePercentage(ICommandContext ctx, string input, out long num)
|
private bool TryHandlePercentage(ICommandContext ctx, string input, out long num)
|
||||||
{
|
{
|
||||||
num = 0;
|
num = 0;
|
||||||
var m = percentRegex.Match(input);
|
var m = _percentRegex.Match(input);
|
||||||
if (m.Captures.Count != 0)
|
if (m.Captures.Count != 0)
|
||||||
{
|
{
|
||||||
if (!long.TryParse(m.Groups["num"].ToString(), out var percent))
|
if (!long.TryParse(m.Groups["num"].ToString(), out var percent))
|
||||||
@@ -97,6 +93,7 @@ public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
|
|||||||
num = (long)(Cur(ctx) * (percent / 100.0f));
|
num = (long)(Cur(ctx) * (percent / 100.0f));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -12,9 +12,7 @@ public class CommentGatheringTypeInspector : TypeInspectorSkeleton
|
|||||||
=> this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException("innerTypeDescriptor");
|
=> this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException("innerTypeDescriptor");
|
||||||
|
|
||||||
public override IEnumerable<IPropertyDescriptor> GetProperties(Type type, object container)
|
public override IEnumerable<IPropertyDescriptor> GetProperties(Type type, object container)
|
||||||
=> innerTypeDescriptor
|
=> innerTypeDescriptor.GetProperties(type, container).Select(d => new CommentsPropertyDescriptor(d));
|
||||||
.GetProperties(type, container)
|
|
||||||
.Select(d => new CommentsPropertyDescriptor(d));
|
|
||||||
|
|
||||||
private sealed class CommentsPropertyDescriptor : IPropertyDescriptor
|
private sealed class CommentsPropertyDescriptor : IPropertyDescriptor
|
||||||
{
|
{
|
||||||
@@ -31,14 +29,16 @@ public class CommentGatheringTypeInspector : TypeInspectorSkeleton
|
|||||||
public Type Type
|
public Type Type
|
||||||
=> baseDescriptor.Type;
|
=> baseDescriptor.Type;
|
||||||
|
|
||||||
public Type TypeOverride {
|
public Type TypeOverride
|
||||||
|
{
|
||||||
get => baseDescriptor.TypeOverride;
|
get => baseDescriptor.TypeOverride;
|
||||||
set => baseDescriptor.TypeOverride = value;
|
set => baseDescriptor.TypeOverride = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Order { get; set; }
|
public int Order { get; set; }
|
||||||
|
|
||||||
public ScalarStyle ScalarStyle {
|
public ScalarStyle ScalarStyle
|
||||||
|
{
|
||||||
get => baseDescriptor.ScalarStyle;
|
get => baseDescriptor.ScalarStyle;
|
||||||
set => baseDescriptor.ScalarStyle = value;
|
set => baseDescriptor.ScalarStyle = value;
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,8 @@ public class CommentGatheringTypeInspector : TypeInspectorSkeleton
|
|||||||
public void Write(object target, object value)
|
public void Write(object target, object value)
|
||||||
=> baseDescriptor.Write(target, value);
|
=> baseDescriptor.Write(target, value);
|
||||||
|
|
||||||
public T GetCustomAttribute<T>() where T : Attribute
|
public T GetCustomAttribute<T>()
|
||||||
|
where T : Attribute
|
||||||
=> baseDescriptor.GetCustomAttribute<T>();
|
=> baseDescriptor.GetCustomAttribute<T>();
|
||||||
|
|
||||||
public IObjectDescriptor Read(object target)
|
public IObjectDescriptor Read(object target)
|
||||||
|
@@ -14,8 +14,8 @@ public class CommentsObjectGraphVisitor : ChainedObjectGraphVisitor
|
|||||||
|
|
||||||
public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context)
|
public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context)
|
||||||
{
|
{
|
||||||
var commentsDescriptor = value as CommentsObjectDescriptor;
|
if (value is CommentsObjectDescriptor commentsDescriptor &&
|
||||||
if (commentsDescriptor != null && !string.IsNullOrWhiteSpace(commentsDescriptor.Comment))
|
!string.IsNullOrWhiteSpace(commentsDescriptor.Comment))
|
||||||
{
|
{
|
||||||
context.Emit(new Comment(commentsDescriptor.Comment.Replace("\n", "\n# "), false));
|
context.Emit(new Comment(commentsDescriptor.Comment.Replace("\n", "\n# "), false));
|
||||||
}
|
}
|
||||||
|
@@ -7,11 +7,12 @@ namespace NadekoBot.Common.Yml;
|
|||||||
public class MultilineScalarFlowStyleEmitter : ChainedEventEmitter
|
public class MultilineScalarFlowStyleEmitter : ChainedEventEmitter
|
||||||
{
|
{
|
||||||
public MultilineScalarFlowStyleEmitter(IEventEmitter nextEmitter)
|
public MultilineScalarFlowStyleEmitter(IEventEmitter nextEmitter)
|
||||||
: base(nextEmitter) { }
|
: base(nextEmitter)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter)
|
public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (typeof(string).IsAssignableFrom(eventInfo.Source.Type))
|
if (typeof(string).IsAssignableFrom(eventInfo.Source.Type))
|
||||||
{
|
{
|
||||||
var value = eventInfo.Source.Value as string;
|
var value = eventInfo.Source.Value as string;
|
||||||
@@ -19,10 +20,7 @@ public class MultilineScalarFlowStyleEmitter : ChainedEventEmitter
|
|||||||
{
|
{
|
||||||
var isMultiLine = value.IndexOfAny(new char[] { '\r', '\n', '\x85', '\x2028', '\x2029' }) >= 0;
|
var isMultiLine = value.IndexOfAny(new char[] { '\r', '\n', '\x85', '\x2028', '\x2029' }) >= 0;
|
||||||
if (isMultiLine)
|
if (isMultiLine)
|
||||||
eventInfo = new(eventInfo.Source)
|
eventInfo = new(eventInfo.Source) { Style = ScalarStyle.Literal, };
|
||||||
{
|
|
||||||
Style = ScalarStyle.Literal,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,11 +21,11 @@ public class Rgba32Converter : IYamlTypeConverter
|
|||||||
public void WriteYaml(IEmitter emitter, object value, Type type)
|
public void WriteYaml(IEmitter emitter, object value, Type type)
|
||||||
{
|
{
|
||||||
var color = (Rgba32)value;
|
var color = (Rgba32)value;
|
||||||
var val = (uint) ((color.B << 0) | (color.G << 8) | (color.R << 16));
|
var val = (uint)((color.B << 0) | (color.G << 8) | (color.R << 16));
|
||||||
emitter.Emit(new Scalar(val.ToString("X6").ToLower()));
|
emitter.Emit(new Scalar(val.ToString("X6").ToLower()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CultureInfoConverter : IYamlTypeConverter
|
public class CultureInfoConverter : IYamlTypeConverter
|
||||||
{
|
{
|
||||||
public bool Accepts(Type type)
|
public bool Accepts(Type type)
|
||||||
|
@@ -4,22 +4,23 @@ namespace NadekoBot.Common.Yml;
|
|||||||
|
|
||||||
public class Yaml
|
public class Yaml
|
||||||
{
|
{
|
||||||
public static ISerializer Serializer => new SerializerBuilder()
|
public static ISerializer Serializer
|
||||||
.WithTypeInspector(inner => new CommentGatheringTypeInspector(inner))
|
=> new SerializerBuilder().WithTypeInspector(inner => new CommentGatheringTypeInspector(inner))
|
||||||
.WithEmissionPhaseObjectGraphVisitor(args => new CommentsObjectGraphVisitor(args.InnerVisitor))
|
.WithEmissionPhaseObjectGraphVisitor(args => new CommentsObjectGraphVisitor(args.InnerVisitor))
|
||||||
.WithEventEmitter(args => new MultilineScalarFlowStyleEmitter(args))
|
.WithEventEmitter(args => new MultilineScalarFlowStyleEmitter(args))
|
||||||
.WithNamingConvention(YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention.Instance)
|
.WithNamingConvention(YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention.Instance)
|
||||||
.WithIndentedSequences()
|
.WithIndentedSequences()
|
||||||
.WithTypeConverter(new Rgba32Converter())
|
.WithTypeConverter(new Rgba32Converter())
|
||||||
.WithTypeConverter(new CultureInfoConverter())
|
.WithTypeConverter(new CultureInfoConverter())
|
||||||
.WithTypeConverter(new UriConverter())
|
.WithTypeConverter(new UriConverter())
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
public static IDeserializer Deserializer => new DeserializerBuilder()
|
public static IDeserializer Deserializer
|
||||||
.WithNamingConvention(YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention.Instance)
|
=> new DeserializerBuilder()
|
||||||
.WithTypeConverter(new Rgba32Converter())
|
.WithNamingConvention(YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention.Instance)
|
||||||
.WithTypeConverter(new CultureInfoConverter())
|
.WithTypeConverter(new Rgba32Converter())
|
||||||
.WithTypeConverter(new UriConverter())
|
.WithTypeConverter(new CultureInfoConverter())
|
||||||
.IgnoreUnmatchedProperties()
|
.WithTypeConverter(new UriConverter())
|
||||||
.Build();
|
.IgnoreUnmatchedProperties()
|
||||||
|
.Build();
|
||||||
}
|
}
|
@@ -15,27 +15,28 @@ public class YamlHelper
|
|||||||
|
|
||||||
// Scan the character value.
|
// Scan the character value.
|
||||||
|
|
||||||
foreach(var c in point)
|
foreach (var c in point)
|
||||||
{
|
{
|
||||||
if (!IsHex(c))
|
if (!IsHex(c))
|
||||||
{
|
{
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
character = (character << 4) + AsHex(c);
|
character = (character << 4) + AsHex(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the value and write the character.
|
// Check the value and write the character.
|
||||||
|
|
||||||
if (character is >= 0xD800 and <= 0xDFFF or > 0x10FFFF)
|
if (character is (>= 0xD800 and <= 0xDFFF) or > 0x10FFFF)
|
||||||
{
|
{
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
return char.ConvertFromUtf32(character);
|
return char.ConvertFromUtf32(character);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsHex(char c)
|
public static bool IsHex(char c)
|
||||||
=> c is >= '0' and <= '9' or >= 'A' and <= 'F' or >= 'a' and <= 'f';
|
=> c is (>= '0' and <= '9') or (>= 'A' and <= 'F') or (>= 'a' and <= 'f');
|
||||||
|
|
||||||
public static int AsHex(char c)
|
public static int AsHex(char c)
|
||||||
{
|
{
|
||||||
@@ -43,10 +44,12 @@ public class YamlHelper
|
|||||||
{
|
{
|
||||||
return c - '0';
|
return c - '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c <= 'F')
|
if (c <= 'F')
|
||||||
{
|
{
|
||||||
return c - 'A' + 10;
|
return c - 'A' + 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
return c - 'a' + 10;
|
return c - 'a' + 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -13,12 +13,13 @@ public static class ClubExtensions
|
|||||||
.ThenInclude(x => x.User)
|
.ThenInclude(x => x.User)
|
||||||
.Include(x => x.Users)
|
.Include(x => x.Users)
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
|
|
||||||
public static ClubInfo GetByOwner(this DbSet<ClubInfo> clubs, ulong userId)
|
public static ClubInfo GetByOwner(this DbSet<ClubInfo> clubs, ulong userId)
|
||||||
=> Include(clubs).FirstOrDefault(c => c.Owner.UserId == userId);
|
=> Include(clubs).FirstOrDefault(c => c.Owner.UserId == userId);
|
||||||
|
|
||||||
public static ClubInfo GetByOwnerOrAdmin(this DbSet<ClubInfo> clubs, ulong userId)
|
public static ClubInfo GetByOwnerOrAdmin(this DbSet<ClubInfo> clubs, ulong userId)
|
||||||
=> Include(clubs).FirstOrDefault(c => c.Owner.UserId == userId
|
=> Include(clubs)
|
||||||
|| c.Users.Any(u => u.UserId == userId && u.IsClubAdmin));
|
.FirstOrDefault(c => c.Owner.UserId == userId || c.Users.Any(u => u.UserId == userId && u.IsClubAdmin));
|
||||||
|
|
||||||
public static ClubInfo GetByMember(this DbSet<ClubInfo> clubs, ulong userId)
|
public static ClubInfo GetByMember(this DbSet<ClubInfo> clubs, ulong userId)
|
||||||
=> Include(clubs).FirstOrDefault(c => c.Users.Any(u => u.UserId == userId));
|
=> Include(clubs).FirstOrDefault(c => c.Users.Any(u => u.UserId == userId));
|
||||||
@@ -27,17 +28,9 @@ public static class ClubExtensions
|
|||||||
=> Include(clubs).FirstOrDefault(c => c.Name.ToUpper() == name.ToUpper() && c.Discrim == discrim);
|
=> Include(clubs).FirstOrDefault(c => c.Name.ToUpper() == name.ToUpper() && c.Discrim == discrim);
|
||||||
|
|
||||||
public static int GetNextDiscrim(this DbSet<ClubInfo> clubs, string name)
|
public static int GetNextDiscrim(this DbSet<ClubInfo> clubs, string name)
|
||||||
=> Include(clubs)
|
=> Include(clubs).Where(x => x.Name.ToUpper() == name.ToUpper()).Select(x => x.Discrim).DefaultIfEmpty().Max() +
|
||||||
.Where(x => x.Name.ToUpper() == name.ToUpper())
|
1;
|
||||||
.Select(x => x.Discrim)
|
|
||||||
.DefaultIfEmpty()
|
|
||||||
.Max() + 1;
|
|
||||||
|
|
||||||
public static List<ClubInfo> GetClubLeaderboardPage(this DbSet<ClubInfo> clubs, int page)
|
public static List<ClubInfo> GetClubLeaderboardPage(this DbSet<ClubInfo> clubs, int page)
|
||||||
=> clubs
|
=> clubs.AsNoTracking().OrderByDescending(x => x.Xp).Skip(page * 9).Take(9).ToList();
|
||||||
.AsNoTracking()
|
|
||||||
.OrderByDescending(x => x.Xp)
|
|
||||||
.Skip(page * 9)
|
|
||||||
.Take(9)
|
|
||||||
.ToList();
|
|
||||||
}
|
}
|
@@ -10,11 +10,7 @@ public static class CustomReactionsExtensions
|
|||||||
=> crs.Delete(x => x.GuildId == guildId);
|
=> crs.Delete(x => x.GuildId == guildId);
|
||||||
|
|
||||||
public static IEnumerable<CustomReaction> ForId(this DbSet<CustomReaction> crs, ulong id)
|
public static IEnumerable<CustomReaction> ForId(this DbSet<CustomReaction> crs, ulong id)
|
||||||
=> crs
|
=> crs.AsNoTracking().AsQueryable().Where(x => x.GuildId == id).ToList();
|
||||||
.AsNoTracking()
|
|
||||||
.AsQueryable()
|
|
||||||
.Where(x => x.GuildId == id)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
public static CustomReaction GetByGuildIdAndInput(this DbSet<CustomReaction> crs, ulong? guildId, string input)
|
public static CustomReaction GetByGuildIdAndInput(this DbSet<CustomReaction> crs, ulong? guildId, string input)
|
||||||
=> crs.FirstOrDefault(x => x.GuildId == guildId && x.Trigger.ToUpper() == input);
|
=> crs.FirstOrDefault(x => x.GuildId == guildId && x.Trigger.ToUpper() == input);
|
||||||
|
@@ -5,6 +5,7 @@ namespace NadekoBot.Db;
|
|||||||
|
|
||||||
public static class DbExtensions
|
public static class DbExtensions
|
||||||
{
|
{
|
||||||
public static T GetById<T>(this DbSet<T> set, int id) where T: DbEntity
|
public static T GetById<T>(this DbSet<T> set, int id)
|
||||||
|
where T : DbEntity
|
||||||
=> set.FirstOrDefault(x => x.Id == id);
|
=> set.FirstOrDefault(x => x.Id == id);
|
||||||
}
|
}
|
@@ -8,10 +8,15 @@ namespace NadekoBot.Db;
|
|||||||
|
|
||||||
public static class DiscordUserExtensions
|
public static class DiscordUserExtensions
|
||||||
{
|
{
|
||||||
public static void EnsureUserCreated(this NadekoContext ctx, ulong userId, string username, string discrim, string avatarId)
|
public static void EnsureUserCreated(
|
||||||
=> ctx.DiscordUser
|
this NadekoContext ctx,
|
||||||
.ToLinqToDBTable()
|
ulong userId,
|
||||||
.InsertOrUpdate(() => new()
|
string username,
|
||||||
|
string discrim,
|
||||||
|
string avatarId)
|
||||||
|
=> ctx.DiscordUser.ToLinqToDBTable()
|
||||||
|
.InsertOrUpdate(
|
||||||
|
() => new()
|
||||||
{
|
{
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
Username = username,
|
Username = username,
|
||||||
@@ -20,36 +25,44 @@ public static class DiscordUserExtensions
|
|||||||
TotalXp = 0,
|
TotalXp = 0,
|
||||||
CurrencyAmount = 0
|
CurrencyAmount = 0
|
||||||
},
|
},
|
||||||
old => new()
|
old => new() { Username = username, Discriminator = discrim, AvatarId = avatarId, },
|
||||||
{
|
() => new() { UserId = userId }
|
||||||
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
|
//temp is only used in updatecurrencystate, so that i don't overwrite real usernames/discrims with Unknown
|
||||||
public static DiscordUser GetOrCreateUser(this NadekoContext ctx, ulong userId, string username, string discrim, string avatarId)
|
public static DiscordUser GetOrCreateUser(
|
||||||
|
this NadekoContext ctx,
|
||||||
|
ulong userId,
|
||||||
|
string username,
|
||||||
|
string discrim,
|
||||||
|
string avatarId)
|
||||||
{
|
{
|
||||||
ctx.EnsureUserCreated(userId, username, discrim, avatarId);
|
ctx.EnsureUserCreated(userId,
|
||||||
return ctx.DiscordUser
|
username,
|
||||||
.Include(x => x.Club)
|
discrim,
|
||||||
|
avatarId
|
||||||
|
);
|
||||||
|
return ctx.DiscordUser.Include(x => x.Club)
|
||||||
.First(u => u.UserId == userId);
|
.First(u => u.UserId == userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DiscordUser GetOrCreateUser(this NadekoContext ctx, IUser original)
|
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)
|
public static int GetUserGlobalRank(this DbSet<DiscordUser> users, ulong id)
|
||||||
=> users.AsQueryable()
|
=> users.AsQueryable()
|
||||||
.Where(x => x.TotalXp > users
|
.Where(x => x.TotalXp >
|
||||||
.AsQueryable()
|
users.AsQueryable()
|
||||||
.Where(y => y.UserId == id)
|
.Where(y => y.UserId == id)
|
||||||
.Select(y => y.TotalXp)
|
.Select(y => y.TotalXp)
|
||||||
.FirstOrDefault())
|
.FirstOrDefault()
|
||||||
.Count() + 1;
|
)
|
||||||
|
.Count() +
|
||||||
|
1;
|
||||||
|
|
||||||
public static DiscordUser[] GetUsersXpLeaderboardFor(this DbSet<DiscordUser> users, int page)
|
public static DiscordUser[] GetUsersXpLeaderboardFor(this DbSet<DiscordUser> users, int page)
|
||||||
=> users.AsQueryable()
|
=> users.AsQueryable()
|
||||||
@@ -59,7 +72,11 @@ public static class DiscordUserExtensions
|
|||||||
.AsEnumerable()
|
.AsEnumerable()
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
public static List<DiscordUser> GetTopRichest(this DbSet<DiscordUser> users, ulong botId, int count, int page = 0)
|
public static List<DiscordUser> GetTopRichest(
|
||||||
|
this DbSet<DiscordUser> users,
|
||||||
|
ulong botId,
|
||||||
|
int count,
|
||||||
|
int page = 0)
|
||||||
=> users.AsQueryable()
|
=> users.AsQueryable()
|
||||||
.Where(c => c.CurrencyAmount > 0 && botId != c.UserId)
|
.Where(c => c.CurrencyAmount > 0 && botId != c.UserId)
|
||||||
.OrderByDescending(c => c.CurrencyAmount)
|
.OrderByDescending(c => c.CurrencyAmount)
|
||||||
@@ -67,40 +84,44 @@ public static class DiscordUserExtensions
|
|||||||
.Take(count)
|
.Take(count)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
public static List<DiscordUser> GetTopRichest(this DbSet<DiscordUser> users, ulong botId, int count)
|
public static long GetUserCurrency(this DbSet<DiscordUser> users, ulong userId)
|
||||||
=> users.AsQueryable()
|
=> users.AsNoTracking()
|
||||||
.Where(c => c.CurrencyAmount > 0 && botId != c.UserId)
|
.FirstOrDefault(x => x.UserId == userId)
|
||||||
.OrderByDescending(c => c.CurrencyAmount)
|
?.CurrencyAmount ??
|
||||||
.Take(count)
|
0;
|
||||||
.ToList();
|
|
||||||
|
|
||||||
public static long GetUserCurrency(this DbSet<DiscordUser> users, ulong userId) =>
|
|
||||||
users.AsNoTracking()
|
|
||||||
.FirstOrDefault(x => x.UserId == userId)
|
|
||||||
?.CurrencyAmount ?? 0;
|
|
||||||
|
|
||||||
public static void RemoveFromMany(this DbSet<DiscordUser> users, IEnumerable<ulong> ids)
|
public static void RemoveFromMany(this DbSet<DiscordUser> users, IEnumerable<ulong> ids)
|
||||||
{
|
{
|
||||||
var items = users.AsQueryable().Where(x => ids.Contains(x.UserId));
|
var items = users.AsQueryable()
|
||||||
|
.Where(x => ids.Contains(x.UserId));
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
item.CurrencyAmount = 0;
|
item.CurrencyAmount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryUpdateCurrencyState(this NadekoContext ctx, ulong userId, string name, string discrim, string avatarId, long amount, bool allowNegative = false)
|
public static bool TryUpdateCurrencyState(
|
||||||
|
this NadekoContext ctx,
|
||||||
|
ulong userId,
|
||||||
|
string name,
|
||||||
|
string discrim,
|
||||||
|
string avatarId,
|
||||||
|
long amount,
|
||||||
|
bool allowNegative = false)
|
||||||
{
|
{
|
||||||
if (amount == 0)
|
if (amount == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// if remove - try to remove if he has more or equal than the amount
|
// if remove - try to remove if he has more or equal than the amount
|
||||||
// and return number of rows > 0 (was there a change)
|
// and return number of rows > 0 (was there a change)
|
||||||
if (amount < 0 && !allowNegative)
|
if (amount < 0 &&
|
||||||
|
!allowNegative)
|
||||||
{
|
{
|
||||||
var rows = ctx.Database.ExecuteSqlInterpolated($@"
|
var rows = ctx.Database.ExecuteSqlInterpolated($@"
|
||||||
UPDATE DiscordUser
|
UPDATE DiscordUser
|
||||||
SET CurrencyAmount=CurrencyAmount+{amount}
|
SET CurrencyAmount=CurrencyAmount+{amount}
|
||||||
WHERE UserId={userId} AND CurrencyAmount>={-amount};");
|
WHERE UserId={userId} AND CurrencyAmount>={-amount};"
|
||||||
|
);
|
||||||
return rows > 0;
|
return rows > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +131,8 @@ WHERE UserId={userId} AND CurrencyAmount>={-amount};");
|
|||||||
var rows = ctx.Database.ExecuteSqlInterpolated($@"
|
var rows = ctx.Database.ExecuteSqlInterpolated($@"
|
||||||
UPDATE DiscordUser
|
UPDATE DiscordUser
|
||||||
SET CurrencyAmount=CurrencyAmount+{amount}
|
SET CurrencyAmount=CurrencyAmount+{amount}
|
||||||
WHERE UserId={userId};");
|
WHERE UserId={userId};"
|
||||||
|
);
|
||||||
return rows > 0;
|
return rows > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,22 +140,22 @@ WHERE UserId={userId};");
|
|||||||
// if it exists, sum current amount with the new one, if it doesn't
|
// if it exists, sum current amount with the new one, if it doesn't
|
||||||
// he just has the new amount
|
// he just has the new amount
|
||||||
var updatedUserData = !string.IsNullOrWhiteSpace(name);
|
var updatedUserData = !string.IsNullOrWhiteSpace(name);
|
||||||
name = name ?? "Unknown";
|
name ??= "Unknown";
|
||||||
discrim = discrim ?? "????";
|
discrim ??= "????";
|
||||||
avatarId = avatarId ?? "";
|
avatarId ??= "";
|
||||||
|
|
||||||
// just update the amount, there is no new user data
|
// just update the amount, there is no new user data
|
||||||
if (!updatedUserData)
|
if (!updatedUserData)
|
||||||
{
|
{
|
||||||
var rows = ctx.Database.ExecuteSqlInterpolated($@"
|
ctx.Database.ExecuteSqlInterpolated($@"
|
||||||
UPDATE OR IGNORE DiscordUser
|
UPDATE OR IGNORE DiscordUser
|
||||||
SET CurrencyAmount=CurrencyAmount+{amount}
|
SET CurrencyAmount=CurrencyAmount+{amount}
|
||||||
WHERE UserId={userId};
|
WHERE UserId={userId};
|
||||||
|
|
||||||
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp)
|
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp)
|
||||||
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
|
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
|
||||||
");
|
"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -147,14 +169,15 @@ WHERE UserId={userId};
|
|||||||
|
|
||||||
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp)
|
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp)
|
||||||
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
|
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
|
||||||
");
|
"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static decimal GetTotalCurrency(this DbSet<DiscordUser> users)
|
public static decimal GetTotalCurrency(this DbSet<DiscordUser> users)
|
||||||
=> users
|
=> users.Sum((Func<DiscordUser, decimal>)(x => x.CurrencyAmount));
|
||||||
.Sum((Func<DiscordUser, decimal>)(x => x.CurrencyAmount));
|
|
||||||
|
|
||||||
public static decimal GetTopOnePercentCurrency(this DbSet<DiscordUser> users, ulong botId)
|
public static decimal GetTopOnePercentCurrency(this DbSet<DiscordUser> users, ulong botId)
|
||||||
=> users.AsQueryable()
|
=> users.AsQueryable()
|
||||||
|
@@ -12,7 +12,7 @@ public static class GuildConfigExtensions
|
|||||||
public ulong GuildId { get; set; }
|
public ulong GuildId { get; set; }
|
||||||
public ulong ChannelId { get; set; }
|
public ulong ChannelId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets full stream role settings for the guild with the specified id.
|
/// Gets full stream role settings for the guild with the specified id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -21,31 +21,27 @@ public static class GuildConfigExtensions
|
|||||||
/// <returns>Guild'p stream role settings</returns>
|
/// <returns>Guild'p stream role settings</returns>
|
||||||
public static StreamRoleSettings GetStreamRoleSettings(this NadekoContext ctx, ulong guildId)
|
public static StreamRoleSettings GetStreamRoleSettings(this NadekoContext ctx, ulong guildId)
|
||||||
{
|
{
|
||||||
var conf = ctx.GuildConfigsForId(guildId, set => set.Include(y => y.StreamRole)
|
var conf = ctx.GuildConfigsForId(guildId,
|
||||||
.Include(y => y.StreamRole.Whitelist)
|
set => set.Include(y => y.StreamRole)
|
||||||
.Include(y => y.StreamRole.Blacklist));
|
.Include(y => y.StreamRole.Whitelist)
|
||||||
|
.Include(y => y.StreamRole.Blacklist)
|
||||||
|
);
|
||||||
|
|
||||||
if (conf.StreamRole is null)
|
if (conf.StreamRole is null)
|
||||||
conf.StreamRole = new();
|
conf.StreamRole = new();
|
||||||
|
|
||||||
return conf.StreamRole;
|
return conf.StreamRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<WarningPunishment> DefaultWarnPunishments =>
|
private static List<WarningPunishment> DefaultWarnPunishments
|
||||||
new() {
|
=> new()
|
||||||
new() {
|
{
|
||||||
Count = 3,
|
new() { Count = 3, Punishment = PunishmentAction.Kick },
|
||||||
Punishment = PunishmentAction.Kick
|
new() { Count = 5, Punishment = PunishmentAction.Ban }
|
||||||
},
|
|
||||||
new() {
|
|
||||||
Count = 5,
|
|
||||||
Punishment = PunishmentAction.Ban
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private static IQueryable<GuildConfig> IncludeEverything(this DbSet<GuildConfig> configs)
|
private static IQueryable<GuildConfig> IncludeEverything(this DbSet<GuildConfig> configs)
|
||||||
=> configs
|
=> configs.AsQueryable()
|
||||||
.AsQueryable()
|
|
||||||
.AsSplitQuery()
|
.AsSplitQuery()
|
||||||
.Include(gc => gc.CommandCooldowns)
|
.Include(gc => gc.CommandCooldowns)
|
||||||
.Include(gc => gc.FollowedStreams)
|
.Include(gc => gc.FollowedStreams)
|
||||||
@@ -56,9 +52,10 @@ public static class GuildConfigExtensions
|
|||||||
.Include(gc => gc.ReactionRoleMessages)
|
.Include(gc => gc.ReactionRoleMessages)
|
||||||
.ThenInclude(x => x.ReactionRoles);
|
.ThenInclude(x => x.ReactionRoles);
|
||||||
|
|
||||||
public static IEnumerable<GuildConfig> GetAllGuildConfigs(this DbSet<GuildConfig> configs, List<ulong> availableGuilds)
|
public static IEnumerable<GuildConfig> GetAllGuildConfigs(
|
||||||
=> configs
|
this DbSet<GuildConfig> configs,
|
||||||
.IncludeEverything()
|
List<ulong> availableGuilds)
|
||||||
|
=> configs.IncludeEverything()
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(x => availableGuilds.Contains(x.GuildId))
|
.Where(x => availableGuilds.Contains(x.GuildId))
|
||||||
.ToList();
|
.ToList();
|
||||||
@@ -70,15 +67,16 @@ public static class GuildConfigExtensions
|
|||||||
/// <param name="guildId">Id of the guide</param>
|
/// <param name="guildId">Id of the guide</param>
|
||||||
/// <param name="includes">Use to manipulate the set however you want. Pass null to include everything</param>
|
/// <param name="includes">Use to manipulate the set however you want. Pass null to include everything</param>
|
||||||
/// <returns>Config for the guild</returns>
|
/// <returns>Config for the guild</returns>
|
||||||
public static GuildConfig GuildConfigsForId(this NadekoContext ctx, ulong guildId, Func<DbSet<GuildConfig>, IQueryable<GuildConfig>> includes)
|
public static GuildConfig GuildConfigsForId(
|
||||||
|
this NadekoContext ctx,
|
||||||
|
ulong guildId,
|
||||||
|
Func<DbSet<GuildConfig>, IQueryable<GuildConfig>> includes)
|
||||||
{
|
{
|
||||||
GuildConfig config;
|
GuildConfig config;
|
||||||
|
|
||||||
if (includes is null)
|
if (includes is null)
|
||||||
{
|
{
|
||||||
config = ctx
|
config = ctx.GuildConfigs.IncludeEverything()
|
||||||
.GuildConfigs
|
|
||||||
.IncludeEverything()
|
|
||||||
.FirstOrDefault(c => c.GuildId == guildId);
|
.FirstOrDefault(c => c.GuildId == guildId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -90,12 +88,13 @@ public static class GuildConfigExtensions
|
|||||||
if (config is null)
|
if (config is null)
|
||||||
{
|
{
|
||||||
ctx.GuildConfigs.Add(config = new()
|
ctx.GuildConfigs.Add(config = new()
|
||||||
{
|
{
|
||||||
GuildId = guildId,
|
GuildId = guildId,
|
||||||
Permissions = Permissionv2.GetDefaultPermlist,
|
Permissions = Permissionv2.GetDefaultPermlist,
|
||||||
WarningsInitialized = true,
|
WarningsInitialized = true,
|
||||||
WarnPunishments = DefaultWarnPunishments,
|
WarnPunishments = DefaultWarnPunishments,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
ctx.SaveChanges();
|
ctx.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,21 +109,17 @@ public static class GuildConfigExtensions
|
|||||||
|
|
||||||
public static LogSetting LogSettingsFor(this NadekoContext ctx, ulong guildId)
|
public static LogSetting LogSettingsFor(this NadekoContext ctx, ulong guildId)
|
||||||
{
|
{
|
||||||
var logSetting = ctx.LogSettings
|
var logSetting = ctx.LogSettings.AsQueryable()
|
||||||
.AsQueryable()
|
|
||||||
.Include(x => x.LogIgnores)
|
.Include(x => x.LogIgnores)
|
||||||
.Where(x => x.GuildId == guildId)
|
.Where(x => x.GuildId == guildId)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (logSetting is null)
|
if (logSetting is null)
|
||||||
{
|
{
|
||||||
ctx.LogSettings.Add(logSetting = new ()
|
ctx.LogSettings.Add(logSetting = new() { GuildId = guildId });
|
||||||
{
|
|
||||||
GuildId = guildId
|
|
||||||
});
|
|
||||||
ctx.SaveChanges();
|
ctx.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
return logSetting;
|
return logSetting;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,23 +134,18 @@ public static class GuildConfigExtensions
|
|||||||
|
|
||||||
public static GuildConfig GcWithPermissionsv2For(this NadekoContext ctx, ulong guildId)
|
public static GuildConfig GcWithPermissionsv2For(this NadekoContext ctx, ulong guildId)
|
||||||
{
|
{
|
||||||
var config = ctx
|
var config = ctx.GuildConfigs.AsQueryable()
|
||||||
.GuildConfigs
|
|
||||||
.AsQueryable()
|
|
||||||
.Where(gc => gc.GuildId == guildId)
|
.Where(gc => gc.GuildId == guildId)
|
||||||
.Include(gc => gc.Permissions)
|
.Include(gc => gc.Permissions)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (config is null) // if there is no guildconfig, create new one
|
if (config is null) // if there is no guildconfig, create new one
|
||||||
{
|
{
|
||||||
ctx.GuildConfigs.Add(config = new()
|
ctx.GuildConfigs.Add(config = new() { GuildId = guildId, Permissions = Permissionv2.GetDefaultPermlist });
|
||||||
{
|
|
||||||
GuildId = guildId,
|
|
||||||
Permissions = Permissionv2.GetDefaultPermlist
|
|
||||||
});
|
|
||||||
ctx.SaveChanges();
|
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;
|
config.Permissions = Permissionv2.GetDefaultPermlist;
|
||||||
ctx.SaveChanges();
|
ctx.SaveChanges();
|
||||||
@@ -165,8 +155,7 @@ public static class GuildConfigExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<FollowedStream> GetFollowedStreams(this DbSet<GuildConfig> configs)
|
public static IEnumerable<FollowedStream> GetFollowedStreams(this DbSet<GuildConfig> configs)
|
||||||
=> configs
|
=> configs.AsQueryable()
|
||||||
.AsQueryable()
|
|
||||||
.Include(x => x.FollowedStreams)
|
.Include(x => x.FollowedStreams)
|
||||||
.SelectMany(gc => gc.FollowedStreams)
|
.SelectMany(gc => gc.FollowedStreams)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
@@ -196,7 +185,8 @@ public static class GuildConfigExtensions
|
|||||||
.Include(x => x.XpSettings)
|
.Include(x => x.XpSettings)
|
||||||
.ThenInclude(x => x.CurrencyRewards)
|
.ThenInclude(x => x.CurrencyRewards)
|
||||||
.Include(x => x.XpSettings)
|
.Include(x => x.XpSettings)
|
||||||
.ThenInclude(x => x.ExclusionList));
|
.ThenInclude(x => x.ExclusionList)
|
||||||
|
);
|
||||||
|
|
||||||
if (gc.XpSettings is null)
|
if (gc.XpSettings is null)
|
||||||
gc.XpSettings = new XpSettings();
|
gc.XpSettings = new XpSettings();
|
||||||
@@ -205,15 +195,10 @@ public static class GuildConfigExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<GeneratingChannel> GetGeneratingChannels(this DbSet<GuildConfig> configs)
|
public static IEnumerable<GeneratingChannel> GetGeneratingChannels(this DbSet<GuildConfig> configs)
|
||||||
=> configs
|
=> configs.AsQueryable()
|
||||||
.AsQueryable()
|
|
||||||
.Include(x => x.GenerateCurrencyChannelIds)
|
.Include(x => x.GenerateCurrencyChannelIds)
|
||||||
.Where(x => x.GenerateCurrencyChannelIds.Any())
|
.Where(x => x.GenerateCurrencyChannelIds.Any())
|
||||||
.SelectMany(x => x.GenerateCurrencyChannelIds)
|
.SelectMany(x => x.GenerateCurrencyChannelIds)
|
||||||
.Select(x => new GeneratingChannel()
|
.Select(x => new GeneratingChannel() { ChannelId = x.ChannelId, GuildId = x.GuildConfig.GuildId })
|
||||||
{
|
|
||||||
ChannelId = x.ChannelId,
|
|
||||||
GuildId = x.GuildConfig.GuildId
|
|
||||||
})
|
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
@@ -7,17 +7,12 @@ public static class MusicPlayerSettingsExtensions
|
|||||||
{
|
{
|
||||||
public static async Task<MusicPlayerSettings> ForGuildAsync(this DbSet<MusicPlayerSettings> settings, ulong guildId)
|
public static async Task<MusicPlayerSettings> ForGuildAsync(this DbSet<MusicPlayerSettings> settings, ulong guildId)
|
||||||
{
|
{
|
||||||
var toReturn = await settings
|
var toReturn = await settings.AsQueryable()
|
||||||
.AsQueryable()
|
|
||||||
.FirstOrDefaultAsync(x => x.GuildId == guildId);
|
.FirstOrDefaultAsync(x => x.GuildId == guildId);
|
||||||
|
|
||||||
if (toReturn is null)
|
if (toReturn is null)
|
||||||
{
|
{
|
||||||
var newSettings = new MusicPlayerSettings()
|
var newSettings = new MusicPlayerSettings() { GuildId = guildId, PlayerRepeat = PlayerRepeatType.Queue };
|
||||||
{
|
|
||||||
GuildId = guildId,
|
|
||||||
PlayerRepeat = PlayerRepeatType.Queue
|
|
||||||
};
|
|
||||||
|
|
||||||
await settings.AddAsync(newSettings);
|
await settings.AddAsync(newSettings);
|
||||||
return newSettings;
|
return newSettings;
|
||||||
|
@@ -10,16 +10,14 @@ public static class MusicPlaylistExtensions
|
|||||||
if (num < 1)
|
if (num < 1)
|
||||||
throw new IndexOutOfRangeException();
|
throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
return playlists
|
return playlists.AsQueryable()
|
||||||
.AsQueryable()
|
|
||||||
.Skip((num - 1) * 20)
|
.Skip((num - 1) * 20)
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.Include(pl => pl.Songs)
|
.Include(pl => pl.Songs)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MusicPlaylist GetWithSongs(this DbSet<MusicPlaylist> playlists, int id) =>
|
public static MusicPlaylist GetWithSongs(this DbSet<MusicPlaylist> playlists, int id)
|
||||||
playlists
|
=> playlists.Include(mpl => mpl.Songs)
|
||||||
.Include(mpl => mpl.Songs)
|
|
||||||
.FirstOrDefault(mpl => mpl.Id == id);
|
.FirstOrDefault(mpl => mpl.Id == id);
|
||||||
}
|
}
|
@@ -13,27 +13,25 @@ public static class PollExtensions
|
|||||||
|
|
||||||
public static void RemovePoll(this NadekoContext ctx, int id)
|
public static void RemovePoll(this NadekoContext ctx, int id)
|
||||||
{
|
{
|
||||||
var p = ctx
|
var p = ctx.Poll.Include(x => x.Answers)
|
||||||
.Poll
|
|
||||||
.Include(x => x.Answers)
|
|
||||||
.Include(x => x.Votes)
|
.Include(x => x.Votes)
|
||||||
.FirstOrDefault(x => x.Id == id);
|
.FirstOrDefault(x => x.Id == id);
|
||||||
|
|
||||||
if (p is null)
|
if (p is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (p.Votes != null)
|
if (p.Votes != null)
|
||||||
{
|
{
|
||||||
ctx.RemoveRange(p.Votes);
|
ctx.RemoveRange(p.Votes);
|
||||||
p.Votes.Clear();
|
p.Votes.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p.Answers != null)
|
if (p.Answers != null)
|
||||||
{
|
{
|
||||||
ctx.RemoveRange(p.Answers);
|
ctx.RemoveRange(p.Answers);
|
||||||
p.Answers.Clear();
|
p.Answers.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Poll.Remove(p);
|
ctx.Poll.Remove(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -6,43 +6,58 @@ namespace NadekoBot.Db;
|
|||||||
public static class QuoteExtensions
|
public static class QuoteExtensions
|
||||||
{
|
{
|
||||||
public static IEnumerable<Quote> GetForGuild(this DbSet<Quote> quotes, ulong guildId)
|
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, ulong guildId, int page, OrderType order)
|
public static IEnumerable<Quote> GetGroup(
|
||||||
|
this DbSet<Quote> quotes,
|
||||||
|
ulong guildId,
|
||||||
|
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)
|
if (order == OrderType.Keyword)
|
||||||
q = q.OrderBy(x => x.Keyword);
|
q = q.OrderBy(x => x.Keyword);
|
||||||
else
|
else
|
||||||
q = q.OrderBy(x => x.Id);
|
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(this DbSet<Quote> quotes, ulong guildId, string keyword)
|
public static async Task<Quote> GetRandomQuoteByKeywordAsync(
|
||||||
|
this DbSet<Quote> quotes,
|
||||||
|
ulong guildId,
|
||||||
|
string keyword)
|
||||||
{
|
{
|
||||||
var rng = new NadekoRandom();
|
var rng = new NadekoRandom();
|
||||||
return (await quotes.AsQueryable()
|
return (await quotes.AsQueryable()
|
||||||
.Where(q => q.GuildId == guildId && q.Keyword == keyword)
|
.Where(q => q.GuildId == guildId && q.Keyword == keyword)
|
||||||
.ToListAsync())
|
.ToListAsync()).OrderBy(q => rng.Next())
|
||||||
.OrderBy(q => rng.Next())
|
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Quote> SearchQuoteKeywordTextAsync(this DbSet<Quote> quotes, ulong guildId, string keyword, string text)
|
public static async Task<Quote> SearchQuoteKeywordTextAsync(
|
||||||
|
this DbSet<Quote> quotes,
|
||||||
|
ulong guildId,
|
||||||
|
string keyword,
|
||||||
|
string text)
|
||||||
{
|
{
|
||||||
var rngk = new NadekoRandom();
|
var rngk = new NadekoRandom();
|
||||||
return (await quotes.AsQueryable()
|
return (await quotes.AsQueryable()
|
||||||
.Where(q => q.GuildId == guildId
|
.Where(q => q.GuildId == guildId &&
|
||||||
&& q.Keyword == keyword
|
q.Keyword == keyword &&
|
||||||
&& EF.Functions.Like(q.Text.ToUpper(), $"%{text.ToUpper()}%")
|
EF.Functions.Like(q.Text.ToUpper(), $"%{text.ToUpper()}%")
|
||||||
// && q.Text.Contains(text, StringComparison.OrdinalIgnoreCase)
|
// && q.Text.Contains(text, StringComparison.OrdinalIgnoreCase)
|
||||||
)
|
)
|
||||||
.ToListAsync())
|
.ToListAsync()).OrderBy(q => rngk.Next())
|
||||||
.OrderBy(q => rngk.Next())
|
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RemoveAllByKeyword(this DbSet<Quote> quotes, ulong guildId, string keyword)
|
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)
|
||||||
|
);
|
||||||
}
|
}
|
@@ -5,7 +5,9 @@ namespace NadekoBot.Db;
|
|||||||
|
|
||||||
public static class ReminderExtensions
|
public static class ReminderExtensions
|
||||||
{
|
{
|
||||||
public static IEnumerable<Reminder> GetIncludedReminders(this DbSet<Reminder> reminders, IEnumerable<ulong> guildIds)
|
public static IEnumerable<Reminder> GetIncludedReminders(
|
||||||
|
this DbSet<Reminder> reminders,
|
||||||
|
IEnumerable<ulong> guildIds)
|
||||||
=> reminders.AsQueryable()
|
=> reminders.AsQueryable()
|
||||||
.Where(x => guildIds.Contains(x.ServerId) || x.ServerId == 0)
|
.Where(x => guildIds.Contains(x.ServerId) || x.ServerId == 0)
|
||||||
.ToList();
|
.ToList();
|
||||||
@@ -16,7 +18,7 @@ public static class ReminderExtensions
|
|||||||
.OrderBy(x => x.DateAdded)
|
.OrderBy(x => x.DateAdded)
|
||||||
.Skip(page * 10)
|
.Skip(page * 10)
|
||||||
.Take(10);
|
.Take(10);
|
||||||
|
|
||||||
public static IEnumerable<Reminder> RemindersForServer(this DbSet<Reminder> reminders, ulong serverId, int page)
|
public static IEnumerable<Reminder> RemindersForServer(this DbSet<Reminder> reminders, ulong serverId, int page)
|
||||||
=> reminders.AsQueryable()
|
=> reminders.AsQueryable()
|
||||||
.Where(x => x.ServerId == serverId)
|
.Where(x => x.ServerId == serverId)
|
||||||
|
@@ -16,8 +16,8 @@ public static class SelfAssignableRolesExtensions
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<SelfAssignedRole> GetFromGuild(this DbSet<SelfAssignedRole> roles, ulong guildId)
|
public static IEnumerable<SelfAssignedRole> GetFromGuild(this DbSet<SelfAssignedRole> roles, ulong guildId)
|
||||||
=> roles.AsQueryable()
|
=> roles.AsQueryable()
|
||||||
.Where(s => s.GuildId == guildId)
|
.Where(s => s.GuildId == guildId)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
@@ -14,20 +14,17 @@ public static class UserXpExtensions
|
|||||||
if (usr is null)
|
if (usr is null)
|
||||||
{
|
{
|
||||||
ctx.Add(usr = new()
|
ctx.Add(usr = new()
|
||||||
{
|
{
|
||||||
Xp = 0,
|
Xp = 0, UserId = userId, NotifyOnLevelUp = XpNotificationLocation.None, GuildId = guildId,
|
||||||
UserId = userId,
|
}
|
||||||
NotifyOnLevelUp = XpNotificationLocation.None,
|
);
|
||||||
GuildId = guildId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return usr;
|
return usr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<UserXpStats> GetUsersFor(this DbSet<UserXpStats> xps, ulong guildId, int page)
|
public static List<UserXpStats> GetUsersFor(this DbSet<UserXpStats> xps, ulong guildId, int page)
|
||||||
=> xps
|
=> xps.AsQueryable()
|
||||||
.AsQueryable()
|
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(x => x.GuildId == guildId)
|
.Where(x => x.GuildId == guildId)
|
||||||
.OrderByDescending(x => x.Xp + x.AwardedXp)
|
.OrderByDescending(x => x.Xp + x.AwardedXp)
|
||||||
@@ -36,8 +33,7 @@ public static class UserXpExtensions
|
|||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
public static List<UserXpStats> GetTopUserXps(this DbSet<UserXpStats> xps, ulong guildId, int count)
|
public static List<UserXpStats> GetTopUserXps(this DbSet<UserXpStats> xps, ulong guildId, int count)
|
||||||
=> xps
|
=> xps.AsQueryable()
|
||||||
.AsQueryable()
|
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(x => x.GuildId == guildId)
|
.Where(x => x.GuildId == guildId)
|
||||||
.OrderByDescending(x => x.Xp + x.AwardedXp)
|
.OrderByDescending(x => x.Xp + x.AwardedXp)
|
||||||
@@ -51,15 +47,17 @@ public static class UserXpExtensions
|
|||||||
// FROM UserXpStats
|
// FROM UserXpStats
|
||||||
// WHERE UserId = @p2 AND GuildId = @p1
|
// WHERE UserId = @p2 AND GuildId = @p1
|
||||||
// LIMIT 1));";
|
// LIMIT 1));";
|
||||||
=> xps
|
=> xps.AsQueryable()
|
||||||
.AsQueryable()
|
.AsNoTracking()
|
||||||
.AsNoTracking()
|
.Where(x => x.GuildId == guildId &&
|
||||||
.Where(x => x.GuildId == guildId && x.Xp + x.AwardedXp >
|
x.Xp + x.AwardedXp >
|
||||||
xps.AsQueryable()
|
xps.AsQueryable()
|
||||||
.Where(y => y.UserId == userId && y.GuildId == guildId)
|
.Where(y => y.UserId == userId && y.GuildId == guildId)
|
||||||
.Select(y => y.Xp + y.AwardedXp)
|
.Select(y => y.Xp + y.AwardedXp)
|
||||||
.FirstOrDefault())
|
.FirstOrDefault()
|
||||||
.Count() + 1;
|
)
|
||||||
|
.Count() +
|
||||||
|
1;
|
||||||
|
|
||||||
public static void ResetGuildUserXp(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
|
public static void ResetGuildUserXp(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
|
||||||
=> xps.Delete(x => x.UserId == userId && x.GuildId == guildId);
|
=> xps.Delete(x => x.UserId == userId && x.GuildId == guildId);
|
||||||
|
@@ -7,21 +7,24 @@ namespace NadekoBot.Db;
|
|||||||
|
|
||||||
public class WaifuInfoStats
|
public class WaifuInfoStats
|
||||||
{
|
{
|
||||||
public string FullName { get; set; }
|
public string FullName { get; init; }
|
||||||
public int Price { get; set; }
|
public int Price { get; init; }
|
||||||
public string ClaimerName { get; set; }
|
public string ClaimerName { get; init; }
|
||||||
public string AffinityName { get; set; }
|
public string AffinityName { get; init; }
|
||||||
public int AffinityCount { get; set; }
|
public int AffinityCount { get; init; }
|
||||||
public int DivorceCount { get; set; }
|
public int DivorceCount { get; init; }
|
||||||
public int ClaimCount { get; set; }
|
public int ClaimCount { get; init; }
|
||||||
public List<WaifuItem> Items { get; set; }
|
public List<WaifuItem> Items { get; init; }
|
||||||
public List<string> Claims { get; set; }
|
public List<string> Claims { get; init; }
|
||||||
public List<string> Fans { get; set; }
|
public List<string> Fans { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class WaifuExtensions
|
public static class WaifuExtensions
|
||||||
{
|
{
|
||||||
public static WaifuInfo ByWaifuUserId(this DbSet<WaifuInfo> waifus, ulong userId, Func<DbSet<WaifuInfo>, IQueryable<WaifuInfo>> includes = null)
|
public static WaifuInfo ByWaifuUserId(
|
||||||
|
this DbSet<WaifuInfo> waifus,
|
||||||
|
ulong userId,
|
||||||
|
Func<DbSet<WaifuInfo>, IQueryable<WaifuInfo>> includes = null)
|
||||||
{
|
{
|
||||||
if (includes is null)
|
if (includes is null)
|
||||||
{
|
{
|
||||||
@@ -51,31 +54,28 @@ public static class WaifuExtensions
|
|||||||
.Skip(skip)
|
.Skip(skip)
|
||||||
.Take(count)
|
.Take(count)
|
||||||
.Select(x => new WaifuLbResult
|
.Select(x => new WaifuLbResult
|
||||||
{
|
{
|
||||||
Affinity = x.Affinity == null ? null : x.Affinity.Username,
|
Affinity = x.Affinity == null ? null : x.Affinity.Username,
|
||||||
AffinityDiscrim = x.Affinity == null ? null : x.Affinity.Discriminator,
|
AffinityDiscrim = x.Affinity == null ? null : x.Affinity.Discriminator,
|
||||||
Claimer = x.Claimer == null ? null : x.Claimer.Username,
|
Claimer = x.Claimer == null ? null : x.Claimer.Username,
|
||||||
ClaimerDiscrim = x.Claimer == null ? null : x.Claimer.Discriminator,
|
ClaimerDiscrim = x.Claimer == null ? null : x.Claimer.Discriminator,
|
||||||
Username = x.Waifu.Username,
|
Username = x.Waifu.Username,
|
||||||
Discrim = x.Waifu.Discriminator,
|
Discrim = x.Waifu.Discriminator,
|
||||||
Price = x.Price,
|
Price = x.Price,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static decimal GetTotalValue(this DbSet<WaifuInfo> waifus)
|
public static decimal GetTotalValue(this DbSet<WaifuInfo> waifus)
|
||||||
=> waifus
|
=> waifus.AsQueryable()
|
||||||
.AsQueryable()
|
|
||||||
.Where(x => x.ClaimerId != null)
|
.Where(x => x.ClaimerId != null)
|
||||||
.Sum(x => x.Price);
|
.Sum(x => x.Price);
|
||||||
|
|
||||||
public static ulong GetWaifuUserId(this DbSet<WaifuInfo> waifus, ulong ownerId, string name)
|
public static ulong GetWaifuUserId(this DbSet<WaifuInfo> waifus, ulong ownerId, string name)
|
||||||
=> waifus
|
=> waifus.AsQueryable()
|
||||||
.AsQueryable()
|
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(x => x.Claimer.UserId == ownerId
|
.Where(x => x.Claimer.UserId == ownerId && x.Waifu.Username + "#" + x.Waifu.Discriminator == name)
|
||||||
&& x.Waifu.Username + "#" + x.Waifu.Discriminator == name)
|
|
||||||
.Select(x => x.Waifu.UserId)
|
.Select(x => x.Waifu.UserId)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
@@ -83,74 +83,64 @@ public static class WaifuExtensions
|
|||||||
{
|
{
|
||||||
ctx.Database.ExecuteSqlInterpolated($@"
|
ctx.Database.ExecuteSqlInterpolated($@"
|
||||||
INSERT OR IGNORE INTO WaifuInfo (AffinityId, ClaimerId, Price, WaifuId)
|
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
|
var toReturn = ctx.WaifuInfo.AsQueryable()
|
||||||
.AsQueryable()
|
.Where(w => w.WaifuId ==
|
||||||
.Where(w => w.WaifuId == ctx.Set<DiscordUser>()
|
ctx.Set<DiscordUser>()
|
||||||
.AsQueryable()
|
.AsQueryable()
|
||||||
.Where(u => u.UserId == userId)
|
.Where(u => u.UserId == userId)
|
||||||
.Select(u => u.Id).FirstOrDefault())
|
.Select(u => u.Id)
|
||||||
|
.FirstOrDefault()
|
||||||
|
)
|
||||||
.Select(w => new WaifuInfoStats
|
.Select(w => new WaifuInfoStats
|
||||||
{
|
{
|
||||||
FullName = ctx.Set<DiscordUser>()
|
FullName = ctx.Set<DiscordUser>()
|
||||||
.AsQueryable()
|
.AsQueryable()
|
||||||
.Where(u => u.UserId == userId)
|
.Where(u => u.UserId == userId)
|
||||||
.Select(u => u.Username + "#" + u.Discriminator)
|
.Select(u => u.Username + "#" + u.Discriminator)
|
||||||
.FirstOrDefault(),
|
.FirstOrDefault(),
|
||||||
|
AffinityCount = ctx.Set<WaifuUpdate>()
|
||||||
AffinityCount = ctx.Set<WaifuUpdate>()
|
.AsQueryable()
|
||||||
.AsQueryable()
|
.Count(x => x.UserId == w.WaifuId &&
|
||||||
.Count(x => x.UserId == w.WaifuId &&
|
x.UpdateType == WaifuUpdateType.AffinityChanged &&
|
||||||
x.UpdateType == WaifuUpdateType.AffinityChanged &&
|
x.NewId != null
|
||||||
x.NewId != null),
|
),
|
||||||
|
AffinityName = ctx.Set<DiscordUser>()
|
||||||
AffinityName = ctx.Set<DiscordUser>()
|
.AsQueryable()
|
||||||
.AsQueryable()
|
.Where(u => u.Id == w.AffinityId)
|
||||||
.Where(u => u.Id == w.AffinityId)
|
.Select(u => u.Username + "#" + u.Discriminator)
|
||||||
.Select(u => u.Username + "#" + u.Discriminator)
|
.FirstOrDefault(),
|
||||||
.FirstOrDefault(),
|
ClaimCount = ctx.WaifuInfo.AsQueryable()
|
||||||
|
.Count(x => x.ClaimerId == w.WaifuId),
|
||||||
ClaimCount = ctx.WaifuInfo
|
ClaimerName = ctx.Set<DiscordUser>()
|
||||||
.AsQueryable()
|
.AsQueryable()
|
||||||
.Count(x => x.ClaimerId == w.WaifuId),
|
.Where(u => u.Id == w.ClaimerId)
|
||||||
|
.Select(u => u.Username + "#" + u.Discriminator)
|
||||||
ClaimerName = ctx.Set<DiscordUser>()
|
.FirstOrDefault(),
|
||||||
.AsQueryable()
|
DivorceCount = ctx.Set<WaifuUpdate>()
|
||||||
.Where(u => u.Id == w.ClaimerId)
|
.AsQueryable()
|
||||||
.Select(u => u.Username + "#" + u.Discriminator)
|
.Count(x => x.OldId == w.WaifuId && x.NewId == null && x.UpdateType == WaifuUpdateType.Claimed),
|
||||||
.FirstOrDefault(),
|
Price = w.Price,
|
||||||
|
Claims = ctx.WaifuInfo.AsQueryable()
|
||||||
DivorceCount = ctx
|
.Include(x => x.Waifu)
|
||||||
.Set<WaifuUpdate>()
|
.Where(x => x.ClaimerId == w.WaifuId)
|
||||||
.AsQueryable()
|
.Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator)
|
||||||
.Count(x => x.OldId == w.WaifuId &&
|
.ToList(),
|
||||||
x.NewId == null &&
|
Fans = ctx.WaifuInfo.AsQueryable()
|
||||||
x.UpdateType == WaifuUpdateType.Claimed),
|
.Include(x => x.Waifu)
|
||||||
|
.Where(x => x.AffinityId == w.WaifuId)
|
||||||
Price = w.Price,
|
.Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator)
|
||||||
|
.ToList(),
|
||||||
Claims = ctx.WaifuInfo
|
Items = w.Items,
|
||||||
.AsQueryable()
|
}
|
||||||
.Include(x => x.Waifu)
|
)
|
||||||
.Where(x => x.ClaimerId == w.WaifuId)
|
|
||||||
.Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator)
|
|
||||||
.ToList(),
|
|
||||||
|
|
||||||
Fans = ctx.WaifuInfo
|
|
||||||
.AsQueryable()
|
|
||||||
.Include(x => x.Waifu)
|
|
||||||
.Where(x => x.AffinityId == w.WaifuId)
|
|
||||||
.Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator)
|
|
||||||
.ToList(),
|
|
||||||
|
|
||||||
Items = w.Items,
|
|
||||||
})
|
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (toReturn is null)
|
if (toReturn is null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -14,17 +14,24 @@ public static class WarningExtensions
|
|||||||
return query.ToArray();
|
return query.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool Forgive(this DbSet<Warning> warnings, ulong guildId, ulong userId, string mod, int index)
|
public static bool Forgive(
|
||||||
|
this DbSet<Warning> warnings,
|
||||||
|
ulong guildId,
|
||||||
|
ulong userId,
|
||||||
|
string mod,
|
||||||
|
int index)
|
||||||
{
|
{
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
|
||||||
var warn = warnings.AsQueryable().Where(x => x.GuildId == guildId && x.UserId == userId)
|
var warn = warnings.AsQueryable()
|
||||||
|
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
||||||
.OrderByDescending(x => x.DateAdded)
|
.OrderByDescending(x => x.DateAdded)
|
||||||
.Skip(index)
|
.Skip(index)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (warn is null || warn.Forgiven)
|
if (warn is null ||
|
||||||
|
warn.Forgiven)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
warn.Forgiven = true;
|
warn.Forgiven = true;
|
||||||
@@ -32,17 +39,25 @@ public static class WarningExtensions
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ForgiveAll(this DbSet<Warning> warnings, ulong guildId, ulong userId, string mod)
|
public static async Task ForgiveAll(
|
||||||
=> await warnings.AsQueryable().Where(x => x.GuildId == guildId && x.UserId == userId)
|
this DbSet<Warning> warnings,
|
||||||
|
ulong guildId,
|
||||||
|
ulong userId,
|
||||||
|
string mod)
|
||||||
|
=> await warnings.AsQueryable()
|
||||||
|
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
||||||
.ForEachAsync(x =>
|
.ForEachAsync(x =>
|
||||||
{
|
|
||||||
if (x.Forgiven != true)
|
|
||||||
{
|
{
|
||||||
x.Forgiven = true;
|
if (x.Forgiven != true)
|
||||||
x.ForgivenBy = mod;
|
{
|
||||||
|
x.Forgiven = true;
|
||||||
|
x.ForgivenBy = mod;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
public static Warning[] GetForGuild(this DbSet<Warning> warnings, ulong id)
|
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();
|
||||||
}
|
}
|
@@ -43,8 +43,8 @@ public partial class Administration
|
|||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task LanguageSet()
|
public async Task LanguageSet()
|
||||||
=> await ReplyConfirmLocalizedAsync(strs.lang_set_show(
|
=> await ReplyConfirmLocalizedAsync(strs.lang_set_show(
|
||||||
Format.Bold(_cultureInfo.ToString()),
|
Format.Bold(Culture.ToString()),
|
||||||
Format.Bold(_cultureInfo.NativeName)));
|
Format.Bold(Culture.NativeName)));
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
|
@@ -1002,8 +1002,7 @@ public sealed class LogCommandService : ILogCommandService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var msg = optMsg.Value as IUserMessage;
|
if (optMsg.Value is not IUserMessage msg || msg.IsAuthor(_client))
|
||||||
if (msg is null || msg.IsAuthor(_client))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_ignoreMessageIds.Contains(msg.Id))
|
if (_ignoreMessageIds.Contains(msg.Id))
|
||||||
@@ -1058,8 +1057,7 @@ public sealed class LogCommandService : ILogCommandService
|
|||||||
if (imsg2 is not IUserMessage after || after.IsAuthor(_client))
|
if (imsg2 is not IUserMessage after || after.IsAuthor(_client))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var before = (optmsg.HasValue ? optmsg.Value : null) as IUserMessage;
|
if ((optmsg.HasValue ? optmsg.Value : null) is not IUserMessage before)
|
||||||
if (before is null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ch is not ITextChannel channel)
|
if (ch is not ITextChannel channel)
|
||||||
|
@@ -167,9 +167,7 @@ public class VcRoleService : INService
|
|||||||
private Task ClientOnUserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState,
|
private Task ClientOnUserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState,
|
||||||
SocketVoiceState newState)
|
SocketVoiceState newState)
|
||||||
{
|
{
|
||||||
|
if (usr is not SocketGuildUser gusr)
|
||||||
var gusr = usr as SocketGuildUser;
|
|
||||||
if (gusr is null)
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
var oldVc = oldState.VoiceChannel;
|
var oldVc = oldState.VoiceChannel;
|
||||||
|
@@ -465,7 +465,7 @@ public partial class Administration
|
|||||||
.AddField(GetText(strs.duration),
|
.AddField(GetText(strs.duration),
|
||||||
time.Time.Humanize(3,
|
time.Time.Humanize(3,
|
||||||
minUnit: TimeUnit.Minute,
|
minUnit: TimeUnit.Minute,
|
||||||
culture: _cultureInfo),
|
culture: Culture),
|
||||||
true);
|
true);
|
||||||
|
|
||||||
if (dmFailed)
|
if (dmFailed)
|
||||||
|
@@ -152,8 +152,7 @@ public class ReactionEvent : ICurrencyEvent
|
|||||||
{
|
{
|
||||||
if (_emote.Name != r.Emote.Name)
|
if (_emote.Name != r.Emote.Name)
|
||||||
return;
|
return;
|
||||||
var gu = (r.User.IsSpecified ? r.User.Value : null) as IGuildUser;
|
if ((r.User.IsSpecified ? r.User.Value : null) is not IGuildUser gu // no unknown users, as they could be bots, or alts
|
||||||
if (gu is null // no unknown users, as they could be bots, or alts
|
|
||||||
|| msg.Id != _msg.Id // same message
|
|| msg.Id != _msg.Id // same message
|
||||||
|| gu.IsBot // no bots
|
|| gu.IsBot // no bots
|
||||||
|| (DateTime.UtcNow - gu.CreatedAt).TotalDays <= 5 // no recently created accounts
|
|| (DateTime.UtcNow - gu.CreatedAt).TotalDays <= 5 // no recently created accounts
|
||||||
|
@@ -35,7 +35,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
|
|
||||||
private string n(long cur)
|
private string n(long cur)
|
||||||
{
|
{
|
||||||
var flowersCi = (CultureInfo)_cultureInfo.Clone();
|
var flowersCi = (CultureInfo)Culture.Clone();
|
||||||
flowersCi.NumberFormat.CurrencySymbol = CurrencySign;
|
flowersCi.NumberFormat.CurrencySymbol = CurrencySign;
|
||||||
Log.Information(string.Join(",", flowersCi.NumberFormat.NativeDigits));
|
Log.Information(string.Join(",", flowersCi.NumberFormat.NativeDigits));
|
||||||
return cur.ToString("C0", flowersCi);
|
return cur.ToString("C0", flowersCi);
|
||||||
@@ -59,12 +59,12 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
}
|
}
|
||||||
var embed = _eb.Create()
|
var embed = _eb.Create()
|
||||||
.WithTitle(GetText(strs.economy_state))
|
.WithTitle(GetText(strs.economy_state))
|
||||||
.AddField(GetText(strs.currency_owned), ((BigInteger)(ec.Cash - ec.Bot)).ToString("N", _cultureInfo) + CurrencySign)
|
.AddField(GetText(strs.currency_owned), ((BigInteger)(ec.Cash - ec.Bot)).ToString("N", Culture) + CurrencySign)
|
||||||
.AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
|
.AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
|
||||||
.AddField(GetText(strs.currency_planted), (BigInteger)ec.Planted)
|
.AddField(GetText(strs.currency_planted), (BigInteger)ec.Planted)
|
||||||
.AddField(GetText(strs.owned_waifus_total), (BigInteger)ec.Waifus + CurrencySign)
|
.AddField(GetText(strs.owned_waifus_total), (BigInteger)ec.Waifus + CurrencySign)
|
||||||
.AddField(GetText(strs.bot_currency), n(ec.Bot))
|
.AddField(GetText(strs.bot_currency), n(ec.Bot))
|
||||||
.AddField(GetText(strs.total), ((BigInteger)(ec.Cash + ec.Planted + ec.Waifus)).ToString("N", _cultureInfo) + CurrencySign)
|
.AddField(GetText(strs.total), ((BigInteger)(ec.Cash + ec.Planted + ec.Waifus)).ToString("N", Culture) + CurrencySign)
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
// ec.Cash already contains ec.Bot as it's the total of all values in the CurrencyAmount column of the DiscordUser table
|
// ec.Cash already contains ec.Bot as it's the total of all values in the CurrencyAmount column of the DiscordUser table
|
||||||
await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
|
@@ -27,8 +27,7 @@ public class CurrencyEventsService : INService
|
|||||||
EventOptions opts, Func<CurrencyEvent.Type, EventOptions, long, IEmbedBuilder> embed)
|
EventOptions opts, Func<CurrencyEvent.Type, EventOptions, long, IEmbedBuilder> embed)
|
||||||
{
|
{
|
||||||
var g = _client.GetGuild(guildId);
|
var g = _client.GetGuild(guildId);
|
||||||
var ch = g?.GetChannel(channelId) as SocketTextChannel;
|
if (g?.GetChannel(channelId) is not SocketTextChannel ch)
|
||||||
if (ch is null)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ICurrencyEvent ce;
|
ICurrencyEvent ce;
|
||||||
|
@@ -159,8 +159,7 @@ public class PlantPickService : INService
|
|||||||
|
|
||||||
private Task PotentialFlowerGeneration(IUserMessage imsg)
|
private Task PotentialFlowerGeneration(IUserMessage imsg)
|
||||||
{
|
{
|
||||||
var msg = imsg as SocketUserMessage;
|
if (imsg is not SocketUserMessage msg || msg.Author.IsBot)
|
||||||
if (msg is null || msg.Author.IsBot)
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
if (imsg.Channel is not ITextChannel channel)
|
if (imsg.Channel is not ITextChannel channel)
|
||||||
|
@@ -196,8 +196,7 @@ public class TriviaGame
|
|||||||
|
|
||||||
var umsg = imsg as SocketUserMessage;
|
var umsg = imsg as SocketUserMessage;
|
||||||
|
|
||||||
var textChannel = umsg?.Channel as ITextChannel;
|
if (umsg?.Channel is not ITextChannel textChannel || textChannel.Guild != Guild)
|
||||||
if (textChannel is null || textChannel.Guild != Guild)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var guildUser = (IGuildUser)umsg.Author;
|
var guildUser = (IGuildUser)umsg.Author;
|
||||||
|
@@ -133,8 +133,7 @@ public class TypingGame
|
|||||||
{
|
{
|
||||||
if (imsg.Author.IsBot)
|
if (imsg.Author.IsBot)
|
||||||
return;
|
return;
|
||||||
var msg = imsg as SocketUserMessage;
|
if (imsg is not SocketUserMessage msg)
|
||||||
if (msg is null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.Channel is null || this.Channel.Id != msg.Channel.Id) return;
|
if (this.Channel is null || this.Channel.Id != msg.Channel.Id) return;
|
||||||
|
@@ -97,9 +97,8 @@ public sealed class FilterService : IEarlyBehavior
|
|||||||
var _ = Task.Run(() =>
|
var _ = Task.Run(() =>
|
||||||
{
|
{
|
||||||
var guild = (channel as ITextChannel)?.Guild;
|
var guild = (channel as ITextChannel)?.Guild;
|
||||||
var usrMsg = newMsg as IUserMessage;
|
|
||||||
|
|
||||||
if (guild is null || usrMsg is null)
|
if (guild is null || newMsg is not IUserMessage usrMsg)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
return RunBehavior(guild, usrMsg);
|
return RunBehavior(guild, usrMsg);
|
||||||
|
@@ -56,7 +56,7 @@ public partial class Searches
|
|||||||
.WithDescription(string.IsNullOrWhiteSpace(kvp.Value.Desc)
|
.WithDescription(string.IsNullOrWhiteSpace(kvp.Value.Desc)
|
||||||
? kvp.Value.ShortDesc
|
? kvp.Value.ShortDesc
|
||||||
: kvp.Value.Desc)
|
: kvp.Value.Desc)
|
||||||
.AddField(GetText(strs.rating), kvp.Value.Rating.ToString(_cultureInfo), true));
|
.AddField(GetText(strs.rating), kvp.Value.Rating.ToString(Culture), true));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -626,9 +626,7 @@ public class SearchesService : INService
|
|||||||
|
|
||||||
var results = elems.Select(elem =>
|
var results = elems.Select(elem =>
|
||||||
{
|
{
|
||||||
var anchor = elem.QuerySelector(".result__a") as IHtmlAnchorElement;
|
if (elem.QuerySelector(".result__a") is not IHtmlAnchorElement anchor)
|
||||||
|
|
||||||
if (anchor is null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var href = anchor.Href;
|
var href = anchor.Href;
|
||||||
|
@@ -127,7 +127,7 @@ public partial class Utility
|
|||||||
var diff = when - DateTime.UtcNow;
|
var diff = when - DateTime.UtcNow;
|
||||||
embed.AddField(
|
embed.AddField(
|
||||||
$"#{++i + (page * 10)} {rem.When:HH:mm yyyy-MM-dd} UTC " +
|
$"#{++i + (page * 10)} {rem.When:HH:mm yyyy-MM-dd} UTC " +
|
||||||
$"(in {diff.Humanize(2, minUnit: TimeUnit.Minute, culture: _cultureInfo)})",
|
$"(in {diff.Humanize(2, minUnit: TimeUnit.Minute, culture: Culture)})",
|
||||||
$@"`Target:` {(rem.IsPrivate ? "DM" : "Channel")}
|
$@"`Target:` {(rem.IsPrivate ? "DM" : "Channel")}
|
||||||
`TargetId:` {rem.ChannelId}
|
`TargetId:` {rem.ChannelId}
|
||||||
`Message:` {rem.Message?.TrimTo(50)}", false);
|
`Message:` {rem.Message?.TrimTo(50)}", false);
|
||||||
@@ -230,7 +230,7 @@ public partial class Utility
|
|||||||
"⏰ " + GetText(strs.remind(
|
"⏰ " + GetText(strs.remind(
|
||||||
Format.Bold(!isPrivate ? $"<#{targetId}>" : ctx.User.Username),
|
Format.Bold(!isPrivate ? $"<#{targetId}>" : ctx.User.Username),
|
||||||
Format.Bold(message),
|
Format.Bold(message),
|
||||||
ts.Humanize(3, minUnit: TimeUnit.Second, culture: _cultureInfo),
|
ts.Humanize(3, minUnit: TimeUnit.Second, culture: Culture),
|
||||||
gTime, gTime))).ConfigureAwait(false);
|
gTime, gTime))).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
@@ -106,14 +106,6 @@ public partial class Xp : NadekoModule<XpService>
|
|||||||
}, allRewards.Count, 9);
|
}, allRewards.Count, 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum AddRemove
|
|
||||||
{
|
|
||||||
Add = 0,
|
|
||||||
Remove = 1,
|
|
||||||
Rm = 1,
|
|
||||||
Rem = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[UserPerm(GuildPerm.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
@@ -8,7 +8,6 @@
|
|||||||
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
||||||
<OutputType>exe</OutputType>
|
<OutputType>exe</OutputType>
|
||||||
<ApplicationIcon>nadeko_icon.ico</ApplicationIcon>
|
<ApplicationIcon>nadeko_icon.ico</ApplicationIcon>
|
||||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -7,16 +7,16 @@ namespace NadekoBot.Services;
|
|||||||
|
|
||||||
public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly ConnectionMultiplexer _con;
|
private readonly ConnectionMultiplexer _con;
|
||||||
private readonly IBotCredentials _creds;
|
private readonly IBotCredentials _creds;
|
||||||
private readonly HttpClient _http;
|
private readonly HttpClient _http;
|
||||||
private readonly string _imagesPath;
|
private readonly string _imagesPath;
|
||||||
|
|
||||||
private IDatabase _db => _con.GetDatabase();
|
private IDatabase Db
|
||||||
|
=> _con.GetDatabase();
|
||||||
|
|
||||||
private const string _basePath = "data/";
|
private const string BASE_PATH = "data/";
|
||||||
private const string _cardsPath = $"{_basePath}images/cards";
|
private const string CARDS_PATH = $"{BASE_PATH}images/cards";
|
||||||
|
|
||||||
public ImageUrls ImageUrls { get; private set; }
|
public ImageUrls ImageUrls { get; private set; }
|
||||||
|
|
||||||
@@ -35,42 +35,42 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
|||||||
XpBg
|
XpBg
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<byte[]> Heads
|
public IReadOnlyList<byte[]> Heads
|
||||||
=> GetByteArrayData(ImageKeys.CoinHeads);
|
=> GetByteArrayData(ImageKeys.CoinHeads);
|
||||||
|
|
||||||
public IReadOnlyList<byte[]> Tails
|
public IReadOnlyList<byte[]> Tails
|
||||||
=> GetByteArrayData(ImageKeys.CoinTails);
|
=> GetByteArrayData(ImageKeys.CoinTails);
|
||||||
|
|
||||||
public IReadOnlyList<byte[]> Dice
|
public IReadOnlyList<byte[]> Dice
|
||||||
=> GetByteArrayData(ImageKeys.Dice);
|
=> GetByteArrayData(ImageKeys.Dice);
|
||||||
|
|
||||||
public IReadOnlyList<byte[]> SlotEmojis
|
public IReadOnlyList<byte[]> SlotEmojis
|
||||||
=> GetByteArrayData(ImageKeys.SlotEmojis);
|
=> GetByteArrayData(ImageKeys.SlotEmojis);
|
||||||
|
|
||||||
public IReadOnlyList<byte[]> Currency
|
public IReadOnlyList<byte[]> Currency
|
||||||
=> GetByteArrayData(ImageKeys.Currency);
|
=> GetByteArrayData(ImageKeys.Currency);
|
||||||
|
|
||||||
public byte[] SlotBackground
|
public byte[] SlotBackground
|
||||||
=> GetByteData(ImageKeys.SlotBg);
|
=> GetByteData(ImageKeys.SlotBg);
|
||||||
|
|
||||||
public byte[] RategirlMatrix
|
public byte[] RategirlMatrix
|
||||||
=> GetByteData(ImageKeys.RategirlMatrix);
|
=> GetByteData(ImageKeys.RategirlMatrix);
|
||||||
|
|
||||||
public byte[] RategirlDot
|
public byte[] RategirlDot
|
||||||
=> GetByteData(ImageKeys.RategirlDot);
|
=> GetByteData(ImageKeys.RategirlDot);
|
||||||
|
|
||||||
public byte[] XpBackground
|
public byte[] XpBackground
|
||||||
=> GetByteData(ImageKeys.XpBg);
|
=> GetByteData(ImageKeys.XpBg);
|
||||||
|
|
||||||
public byte[] Rip
|
public byte[] Rip
|
||||||
=> GetByteData(ImageKeys.RipBg);
|
=> GetByteData(ImageKeys.RipBg);
|
||||||
|
|
||||||
public byte[] RipOverlay
|
public byte[] RipOverlay
|
||||||
=> GetByteData(ImageKeys.RipOverlay);
|
=> GetByteData(ImageKeys.RipOverlay);
|
||||||
|
|
||||||
public byte[] GetCard(string key)
|
public byte[] GetCard(string key)
|
||||||
// since cards are always local for now, don't cache them
|
// since cards are always local for now, don't cache them
|
||||||
=> File.ReadAllBytes(Path.Join(_cardsPath, key + ".jpg"));
|
=> File.ReadAllBytes(Path.Join(CARDS_PATH, key + ".jpg"));
|
||||||
|
|
||||||
public async Task OnReadyAsync()
|
public async Task OnReadyAsync()
|
||||||
{
|
{
|
||||||
@@ -85,7 +85,7 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
|||||||
_con = con;
|
_con = con;
|
||||||
_creds = creds;
|
_creds = creds;
|
||||||
_http = new();
|
_http = new();
|
||||||
_imagesPath = Path.Combine(_basePath, "images.yml");
|
_imagesPath = Path.Combine(BASE_PATH, "images.yml");
|
||||||
|
|
||||||
Migrate();
|
Migrate();
|
||||||
|
|
||||||
@@ -95,58 +95,50 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
|||||||
private void Migrate()
|
private void Migrate()
|
||||||
{
|
{
|
||||||
// migrate to yml
|
// migrate to yml
|
||||||
if (File.Exists(Path.Combine(_basePath, "images.json")))
|
if (File.Exists(Path.Combine(BASE_PATH, "images.json")))
|
||||||
{
|
{
|
||||||
var oldFilePath = Path.Combine(_basePath, "images.json");
|
var oldFilePath = Path.Combine(BASE_PATH, "images.json");
|
||||||
var backupFilePath = Path.Combine(_basePath, "images.json.backup");
|
var backupFilePath = Path.Combine(BASE_PATH, "images.json.backup");
|
||||||
|
|
||||||
var oldData = JsonConvert.DeserializeObject<OldImageUrls>(
|
var oldData = JsonConvert.DeserializeObject<OldImageUrls>(File.ReadAllText(oldFilePath));
|
||||||
File.ReadAllText(oldFilePath));
|
|
||||||
|
|
||||||
if (oldData is not null)
|
if (oldData is not null)
|
||||||
{
|
{
|
||||||
var newData = new ImageUrls()
|
var newData = new ImageUrls()
|
||||||
{
|
{
|
||||||
Coins = new()
|
Coins =
|
||||||
{
|
new()
|
||||||
Heads = oldData.Coins.Heads.Length == 1 &&
|
{
|
||||||
oldData.Coins.Heads[0].ToString() == "https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/coins/heads.png"
|
Heads = oldData.Coins.Heads.Length == 1 &&
|
||||||
? new[] { new Uri("https://cdn.nadeko.bot/coins/heads3.png") }
|
oldData.Coins.Heads[0].ToString() ==
|
||||||
: oldData.Coins.Heads,
|
"https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/coins/heads.png"
|
||||||
Tails = oldData.Coins.Tails.Length == 1 &&
|
? new[] { new Uri("https://cdn.nadeko.bot/coins/heads3.png") }
|
||||||
oldData.Coins.Tails[0].ToString() == "https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/coins/tails.png"
|
: oldData.Coins.Heads,
|
||||||
? new[] { new Uri("https://cdn.nadeko.bot/coins/tails3.png") }
|
Tails = oldData.Coins.Tails.Length == 1 &&
|
||||||
: oldData.Coins.Tails,
|
oldData.Coins.Tails[0].ToString() ==
|
||||||
},
|
"https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/coins/tails.png"
|
||||||
|
? new[] { new Uri("https://cdn.nadeko.bot/coins/tails3.png") }
|
||||||
|
: oldData.Coins.Tails,
|
||||||
|
},
|
||||||
Dice = oldData.Dice.Map(x => x.ToNewCdn()),
|
Dice = oldData.Dice.Map(x => x.ToNewCdn()),
|
||||||
Currency = oldData.Currency.Map(x => x.ToNewCdn()),
|
Currency = oldData.Currency.Map(x => x.ToNewCdn()),
|
||||||
Rategirl = new()
|
Rategirl =
|
||||||
{
|
new()
|
||||||
Dot = oldData.Rategirl.Dot.ToNewCdn(),
|
{
|
||||||
Matrix = oldData.Rategirl.Matrix.ToNewCdn()
|
Dot = oldData.Rategirl.Dot.ToNewCdn(), Matrix = oldData.Rategirl.Matrix.ToNewCdn()
|
||||||
},
|
},
|
||||||
Rip = new()
|
Rip = new() { Bg = oldData.Rip.Bg.ToNewCdn(), Overlay = oldData.Rip.Overlay.ToNewCdn(), },
|
||||||
{
|
|
||||||
Bg = oldData.Rip.Bg.ToNewCdn(),
|
|
||||||
Overlay = oldData.Rip.Overlay.ToNewCdn(),
|
|
||||||
},
|
|
||||||
Slots = new()
|
Slots = new()
|
||||||
{
|
{
|
||||||
Bg = new("https://cdn.nadeko.bot/slots/slots_bg.png"),
|
Bg = new("https://cdn.nadeko.bot/slots/slots_bg.png"),
|
||||||
Emojis = new[]
|
Emojis = new[]
|
||||||
{
|
{
|
||||||
"https://cdn.nadeko.bot/slots/0.png",
|
"https://cdn.nadeko.bot/slots/0.png", "https://cdn.nadeko.bot/slots/1.png",
|
||||||
"https://cdn.nadeko.bot/slots/1.png",
|
"https://cdn.nadeko.bot/slots/2.png", "https://cdn.nadeko.bot/slots/3.png",
|
||||||
"https://cdn.nadeko.bot/slots/2.png",
|
"https://cdn.nadeko.bot/slots/4.png", "https://cdn.nadeko.bot/slots/5.png"
|
||||||
"https://cdn.nadeko.bot/slots/3.png",
|
|
||||||
"https://cdn.nadeko.bot/slots/4.png",
|
|
||||||
"https://cdn.nadeko.bot/slots/5.png"
|
|
||||||
}.Map(x => new Uri(x))
|
}.Map(x => new Uri(x))
|
||||||
},
|
},
|
||||||
Xp = new()
|
Xp = new() { Bg = oldData.Xp.Bg.ToNewCdn(), },
|
||||||
{
|
|
||||||
Bg = oldData.Xp.Bg.ToNewCdn(),
|
|
||||||
},
|
|
||||||
Version = 2,
|
Version = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -216,25 +208,25 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
|||||||
if (data is null)
|
if (data is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await _db.StringSetAsync(GetRedisKey(key), data);
|
await Db.StringSetAsync(GetRedisKey(key), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Load(ImageKeys key, Uri[] uris)
|
private async Task Load(ImageKeys key, Uri[] uris)
|
||||||
{
|
{
|
||||||
await _db.KeyDeleteAsync(GetRedisKey(key));
|
await Db.KeyDeleteAsync(GetRedisKey(key));
|
||||||
var imageData = await Task.WhenAll(uris.Select(GetImageData));
|
var imageData = await Task.WhenAll(uris.Select(GetImageData));
|
||||||
var vals = imageData
|
var vals = imageData.Where(x => x is not null).Select(x => (RedisValue)x).ToArray();
|
||||||
.Where(x => x is not null)
|
|
||||||
.Select(x => (RedisValue)x)
|
await Db.ListRightPushAsync(GetRedisKey(key), vals);
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
await _db.ListRightPushAsync(GetRedisKey(key), vals);
|
|
||||||
|
|
||||||
if (uris.Length != vals.Length)
|
if (uris.Length != vals.Length)
|
||||||
{
|
{
|
||||||
Log.Information("{Loaded}/{Max} URIs for the key '{ImageKey}' have been loaded.\n" +
|
Log.Information("{Loaded}/{Max} URIs for the key '{ImageKey}' have been loaded.\n" +
|
||||||
"Some of the supplied URIs are either unavailable or invalid.",
|
"Some of the supplied URIs are either unavailable or invalid",
|
||||||
vals.Length, uris.Length, key);
|
vals.Length,
|
||||||
|
uris.Length,
|
||||||
|
key
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,24 +256,23 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> AllKeysExist()
|
private async Task<bool> AllKeysExist()
|
||||||
{
|
{
|
||||||
var tasks = await Task.WhenAll(GetAllKeys()
|
var tasks = await Task.WhenAll(GetAllKeys().Select(x => Db.KeyExistsAsync(GetRedisKey(x))));
|
||||||
.Select(x => _db.KeyExistsAsync(GetRedisKey(x))));
|
|
||||||
|
|
||||||
return tasks.All(exist => exist);
|
return tasks.All(exist => exist);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<ImageKeys> GetAllKeys() =>
|
private IEnumerable<ImageKeys> GetAllKeys()
|
||||||
Enum.GetValues<ImageKeys>();
|
=> Enum.GetValues<ImageKeys>();
|
||||||
|
|
||||||
private byte[][] GetByteArrayData(ImageKeys key)
|
private byte[][] GetByteArrayData(ImageKeys key)
|
||||||
=> _db.ListRange(GetRedisKey(key)).Map(x => (byte[])x);
|
=> Db.ListRange(GetRedisKey(key)).Map(x => (byte[])x);
|
||||||
|
|
||||||
private byte[] GetByteData(ImageKeys key)
|
private byte[] GetByteData(ImageKeys key)
|
||||||
=> _db.StringGet(GetRedisKey(key));
|
=> Db.StringGet(GetRedisKey(key));
|
||||||
|
|
||||||
private RedisKey GetRedisKey(ImageKeys key)
|
private RedisKey GetRedisKey(ImageKeys key)
|
||||||
=> _creds.RedisKey() + "_image_" + key;
|
=> _creds.RedisKey() + "_image_" + key;
|
||||||
}
|
}
|
@@ -10,35 +10,33 @@ public class RedisLocalDataCache : ILocalDataCache
|
|||||||
private readonly ConnectionMultiplexer _con;
|
private readonly ConnectionMultiplexer _con;
|
||||||
private readonly IBotCredentials _creds;
|
private readonly IBotCredentials _creds;
|
||||||
|
|
||||||
private IDatabase _db => _con.GetDatabase();
|
private const string POKEMON_ABILITIES_FILE = "data/pokemon/pokemon_abilities.json";
|
||||||
|
private const string POKEMON_LIST_FILE = "data/pokemon/pokemon_list.json";
|
||||||
private const string pokemonAbilitiesFile = "data/pokemon/pokemon_abilities.json";
|
private const string POKEMON_MAP_PATH = "data/pokemon/name-id_map.json";
|
||||||
private const string pokemonListFile = "data/pokemon/pokemon_list.json";
|
private const string QUESTIONS_FILE = "data/trivia_questions.json";
|
||||||
private const string pokemonMapPath = "data/pokemon/name-id_map.json";
|
|
||||||
private const string questionsFile = "data/trivia_questions.json";
|
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, SearchPokemon> Pokemons
|
public IReadOnlyDictionary<string, SearchPokemon> Pokemons
|
||||||
{
|
{
|
||||||
get => Get<Dictionary<string, SearchPokemon>>("pokemon_list");
|
get => Get<Dictionary<string, SearchPokemon>>("pokemon_list");
|
||||||
private set => Set("pokemon_list", value);
|
private init => Set("pokemon_list", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, SearchPokemonAbility> PokemonAbilities
|
public IReadOnlyDictionary<string, SearchPokemonAbility> PokemonAbilities
|
||||||
{
|
{
|
||||||
get => Get<Dictionary<string, SearchPokemonAbility>>("pokemon_abilities");
|
get => Get<Dictionary<string, SearchPokemonAbility>>("pokemon_abilities");
|
||||||
private set => Set("pokemon_abilities", value);
|
private init => Set("pokemon_abilities", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TriviaQuestion[] TriviaQuestions
|
public TriviaQuestion[] TriviaQuestions
|
||||||
{
|
{
|
||||||
get => Get<TriviaQuestion[]>("trivia_questions");
|
get => Get<TriviaQuestion[]>("trivia_questions");
|
||||||
private set => Set("trivia_questions", value);
|
private init => Set("trivia_questions", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, string> PokemonMap
|
public IReadOnlyDictionary<int, string> PokemonMap
|
||||||
{
|
{
|
||||||
get => Get<Dictionary<int, string>>("pokemon_map");
|
get => Get<Dictionary<int, string>>("pokemon_map");
|
||||||
private set => Set("pokemon_map", value);
|
private init => Set("pokemon_map", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RedisLocalDataCache(ConnectionMultiplexer con, IBotCredentials creds, DiscordSocketClient client)
|
public RedisLocalDataCache(ConnectionMultiplexer con, IBotCredentials creds, DiscordSocketClient client)
|
||||||
@@ -49,29 +47,33 @@ public class RedisLocalDataCache : ILocalDataCache
|
|||||||
|
|
||||||
if (shardId == 0)
|
if (shardId == 0)
|
||||||
{
|
{
|
||||||
if (!File.Exists(pokemonListFile))
|
if (!File.Exists(POKEMON_LIST_FILE))
|
||||||
{
|
{
|
||||||
Log.Warning($"{pokemonListFile} is missing. Pokemon abilities not loaded");
|
Log.Warning($"{POKEMON_LIST_FILE} is missing. Pokemon abilities not loaded");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Pokemons = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(File.ReadAllText(pokemonListFile));
|
Pokemons =
|
||||||
|
JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(File.ReadAllText(POKEMON_LIST_FILE));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!File.Exists(pokemonAbilitiesFile))
|
if (!File.Exists(POKEMON_ABILITIES_FILE))
|
||||||
{
|
{
|
||||||
Log.Warning($"{pokemonAbilitiesFile} is missing. Pokemon abilities not loaded.");
|
Log.Warning($"{POKEMON_ABILITIES_FILE} is missing. Pokemon abilities not loaded.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PokemonAbilities = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemonAbility>>(File.ReadAllText(pokemonAbilitiesFile));
|
PokemonAbilities =
|
||||||
|
JsonConvert.DeserializeObject<Dictionary<string, SearchPokemonAbility>>(
|
||||||
|
File.ReadAllText(POKEMON_ABILITIES_FILE)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TriviaQuestions = JsonConvert.DeserializeObject<TriviaQuestion[]>(File.ReadAllText(questionsFile));
|
TriviaQuestions = JsonConvert.DeserializeObject<TriviaQuestion[]>(File.ReadAllText(QUESTIONS_FILE));
|
||||||
PokemonMap = JsonConvert.DeserializeObject<PokemonNameId[]>(File.ReadAllText(pokemonMapPath))
|
PokemonMap = JsonConvert.DeserializeObject<PokemonNameId[]>(File.ReadAllText(POKEMON_MAP_PATH))
|
||||||
.ToDictionary(x => x.Id, x => x.Name);
|
?.ToDictionary(x => x.Id, x => x.Name) ?? new();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -81,9 +83,10 @@ public class RedisLocalDataCache : ILocalDataCache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private T Get<T>(string key) where T : class
|
private T Get<T>(string key)
|
||||||
=> JsonConvert.DeserializeObject<T>(_db.StringGet($"{_creds.RedisKey()}_localdata_{key}"));
|
where T : class
|
||||||
|
=> JsonConvert.DeserializeObject<T>(_con.GetDatabase().StringGet($"{_creds.RedisKey()}_localdata_{key}"));
|
||||||
|
|
||||||
private void Set(string key, object obj)
|
private void Set(string key, object obj)
|
||||||
=> _db.StringSet($"{_creds.RedisKey()}_localdata_{key}", JsonConvert.SerializeObject(obj));
|
=> _con.GetDatabase().StringSet($"{_creds.RedisKey()}_localdata_{key}", JsonConvert.SerializeObject(obj));
|
||||||
}
|
}
|
@@ -11,8 +11,7 @@ public static class ServiceCollectionExtensions
|
|||||||
{
|
{
|
||||||
public static IServiceCollection AddBotStringsServices(this IServiceCollection services, int totalShards)
|
public static IServiceCollection AddBotStringsServices(this IServiceCollection services, int totalShards)
|
||||||
=> totalShards <= 1
|
=> totalShards <= 1
|
||||||
? services
|
? services.AddSingleton<IStringsSource, LocalFileStringsSource>()
|
||||||
.AddSingleton<IStringsSource, LocalFileStringsSource>()
|
|
||||||
.AddSingleton<IBotStringsProvider, LocalBotStringsProvider>()
|
.AddSingleton<IBotStringsProvider, LocalBotStringsProvider>()
|
||||||
.AddSingleton<IBotStrings, BotStrings>()
|
.AddSingleton<IBotStrings, BotStrings>()
|
||||||
: services.AddSingleton<IStringsSource, LocalFileStringsSource>()
|
: services.AddSingleton<IStringsSource, LocalFileStringsSource>()
|
||||||
@@ -25,7 +24,8 @@ public static class ServiceCollectionExtensions
|
|||||||
|
|
||||||
foreach (var type in Assembly.GetCallingAssembly().ExportedTypes.Where(x => x.IsSealed))
|
foreach (var type in Assembly.GetCallingAssembly().ExportedTypes.Where(x => x.IsSealed))
|
||||||
{
|
{
|
||||||
if (type.BaseType?.IsGenericType == true && type.BaseType.GetGenericTypeDefinition() == baseType)
|
if (type.BaseType?.IsGenericType == true &&
|
||||||
|
type.BaseType.GetGenericTypeDefinition() == baseType)
|
||||||
{
|
{
|
||||||
services.AddSingleton(type);
|
services.AddSingleton(type);
|
||||||
services.AddSingleton(x => (IConfigService)x.GetRequiredService(type));
|
services.AddSingleton(x => (IConfigService)x.GetRequiredService(type));
|
||||||
@@ -39,8 +39,7 @@ public static class ServiceCollectionExtensions
|
|||||||
=> services.AddSealedSubclassesOf(typeof(IConfigMigrator));
|
=> services.AddSealedSubclassesOf(typeof(IConfigMigrator));
|
||||||
|
|
||||||
public static IServiceCollection AddMusic(this IServiceCollection services)
|
public static IServiceCollection AddMusic(this IServiceCollection services)
|
||||||
=> services
|
=> services.AddSingleton<IMusicService, MusicService>()
|
||||||
.AddSingleton<IMusicService, MusicService>()
|
|
||||||
.AddSingleton<ITrackResolveProvider, TrackResolveProvider>()
|
.AddSingleton<ITrackResolveProvider, TrackResolveProvider>()
|
||||||
.AddSingleton<IYoutubeResolver, YtdlYoutubeResolver>()
|
.AddSingleton<IYoutubeResolver, YtdlYoutubeResolver>()
|
||||||
.AddSingleton<ISoundcloudResolver, SoundcloudResolver>()
|
.AddSingleton<ISoundcloudResolver, SoundcloudResolver>()
|
||||||
@@ -49,14 +48,13 @@ public static class ServiceCollectionExtensions
|
|||||||
.AddSingleton<ITrackCacher, RedisTrackCacher>()
|
.AddSingleton<ITrackCacher, RedisTrackCacher>()
|
||||||
.AddSingleton<YtLoader>()
|
.AddSingleton<YtLoader>()
|
||||||
.AddSingleton<IPlaceholderProvider>(svc => svc.GetRequiredService<IMusicService>());
|
.AddSingleton<IPlaceholderProvider>(svc => svc.GetRequiredService<IMusicService>());
|
||||||
|
|
||||||
// consider using scrutor, because slightly different versions
|
// consider using scrutor, because slightly different versions
|
||||||
// of this might be needed in several different places
|
// of this might be needed in several different places
|
||||||
public static IServiceCollection AddSealedSubclassesOf(this IServiceCollection services, Type baseType)
|
public static IServiceCollection AddSealedSubclassesOf(this IServiceCollection services, Type baseType)
|
||||||
{
|
{
|
||||||
var subTypes = Assembly.GetCallingAssembly()
|
var subTypes = Assembly.GetCallingAssembly()
|
||||||
.ExportedTypes
|
.ExportedTypes.Where(type => type.IsSealed && baseType.IsAssignableFrom(type));
|
||||||
.Where(type => type.IsSealed && baseType.IsAssignableFrom(type));
|
|
||||||
|
|
||||||
foreach (var subType in subTypes)
|
foreach (var subType in subTypes)
|
||||||
{
|
{
|
Reference in New Issue
Block a user