* dev: Greet stuff moved to its own table in the database. GreetSettings

* fix: Fixed placeholders not working
* fix: Fixed some countries in countries.yml for hangman game
* add: Added custom status overload for \`.adpl\`
* dev: Removed some unused strings
* fix: Fixed postgres support in Nadeko
* remove: Removed mysql support, it was broken for a while and some queries weren't compiling.
* dev: Updated image library
* fix: Some command strings fixed and clarified
This commit is contained in:
Kwoth
2024-09-15 22:44:37 +00:00
parent 28ad6db2de
commit 021e7978da
86 changed files with 4899 additions and 82742 deletions

View File

@@ -71,6 +71,22 @@ public static class EnumerableExtensions
this IEnumerable<KeyValuePair<TKey, TValue>> dict)
where TKey : notnull
=> new(dict);
/// <summary>
/// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}" /> class
/// that contains elements copied from the specified <see cref="IEnumerable{T}" />
/// has the default concurrency level, has the default initial capacity,
/// and uses the default comparer for the key type.
/// </summary>
/// <param name="dict">
/// The <see cref="IEnumerable{T}" /> whose elements are copied to the new
/// <see cref="ConcurrentDictionary{TKey,TValue}" />.
/// </param>
/// <returns>A new instance of the <see cref="ConcurrentDictionary{TKey,TValue}" /> class</returns>
public static ConcurrentHashSet<TValue> ToConcurrentSet<TValue>(
this IReadOnlyCollection<TValue> dict)
where TValue : notnull
=> new(dict);
public static IndexedCollection<T> ToIndexed<T>(this IEnumerable<T> enumerable)
where T : class, IIndexed

View File

@@ -13,7 +13,7 @@ namespace NadekoBot.Common.Configs;
public sealed partial class BotConfig : ICloneable<BotConfig>
{
[Comment("""DO NOT CHANGE""")]
public int Version { get; set; } = 7;
public int Version { get; set; } = 8;
[Comment("""
Most commands, when executed, have a small colored line
@@ -85,19 +85,6 @@ public sealed partial class BotConfig : ICloneable<BotConfig>
[Comment("""Which string will be used to recognize the commands""")]
public string Prefix { get; set; }
[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
If more users join within the next 5 seconds, they will be greeted in groups of 5.
This will cause %user.mention% and other placeholders to be replaced with multiple users.
Keep in mind this might break some of your embeds - for example if you have %user.avatar% in the thumbnail,
it will become invalid, as it will resolve to a list of avatars of grouped users.
note: This setting is primarily used if you're afraid of raids, or you're running medium/large bots where some
servers might get hundreds of people join at once. This is used to prevent the bot from getting ratelimited,
and (slightly) reduce the greet spam in those servers.
""")]
public bool GroupGreets { get; set; }
[Comment("""
Whether the bot will rotate through all specified statuses.
This setting can be changed via .ropl command.
@@ -145,7 +132,6 @@ public sealed partial class BotConfig : ICloneable<BotConfig>
Blocked = blocked;
Prefix = ".";
RotateStatuses = false;
GroupGreets = false;
DmHelpTextKeywords =
[
"help",

View File

@@ -1,5 +1,6 @@
#nullable disable
using LinqToDB;
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
namespace NadekoBot.Common;

View File

@@ -7,7 +7,7 @@ public sealed class ReplacementContext
public DiscordSocketClient? Client { get; }
public IGuild? Guild { get; }
public IMessageChannel? Channel { get; }
public IUser[]? Users { get; }
public IUser? User { get; }
private readonly List<ReplacementInfo> _overrides = new();
private readonly HashSet<string> _tokens = new();
@@ -21,10 +21,11 @@ public sealed class ReplacementContext
public IReadOnlyList<RegexReplacementInfo> RegexOverrides
=> _regexOverrides.AsReadOnly();
public ReplacementContext(ICommandContext cmdContext) : this(cmdContext.Client as DiscordSocketClient,
cmdContext.Guild,
cmdContext.Channel,
cmdContext.User)
public ReplacementContext(ICommandContext cmdContext)
: this(cmdContext.Client as DiscordSocketClient,
cmdContext.Guild,
cmdContext.Channel,
cmdContext.User)
{
}
@@ -32,12 +33,12 @@ public sealed class ReplacementContext
DiscordSocketClient? client = null,
IGuild? guild = null,
IMessageChannel? channel = null,
params IUser[]? users)
IUser? user = null)
{
Client = client;
Guild = guild;
Channel = channel;
Users = users;
User = user;
}
public ReplacementContext WithOverride(string key, Func<ValueTask<string>> repFactory)

View File

@@ -61,6 +61,18 @@ public sealed partial class ReplacementPatternStore
private void WithUsers()
{
Register("%user%", static (IUser user) => user.Mention);
Register("%user.mention%", static (IUser user) => user.Mention);
Register("%user.fullname%", static (IUser user) => user.ToString()!);
Register("%user.name%", static (IUser user) => user.Username);
Register("%user.discrim%", static (IUser user) => user.Discriminator);
Register("%user.avatar%", static (IUser user) => user.RealAvatarUrl().ToString());
Register("%user.id%", static (IUser user) => user.Id.ToString());
Register("%user.created_time%", static (IUser user) => user.CreatedAt.ToString("HH:mm"));
Register("%user.created_date%", static (IUser user) => user.CreatedAt.ToString("dd.MM.yyyy"));
Register("%user.joined_time%", static (IGuildUser user) => user.JoinedAt?.ToString("HH:mm"));
Register("%user.joined_date%", static (IGuildUser user) => user.JoinedAt?.ToString("dd.MM.yyyy"));
Register("%user%",
static (IUser[] users) => string.Join(" ", users.Select(user => user.Mention)));
Register("%user.mention%",

View File

@@ -40,8 +40,8 @@ public sealed class ReplacementService : IReplacementService, INService
if (repCtx.Guild is not null)
obj.Add(repCtx.Guild);
if (repCtx.Users is not null)
obj.Add(repCtx.Users);
if (repCtx.User is not null)
obj.Add(repCtx.User);
if (repCtx.Channel is not null)
obj.Add(repCtx.Channel);
@@ -86,9 +86,9 @@ public sealed class ReplacementService : IReplacementService, INService
objs.Add(repCtx.Channel);
}
if (repCtx.Users is not null)
if (repCtx.User is not null)
{
objs.Add(repCtx.Users);
objs.Add(repCtx.User);
}
if (repCtx.Guild is not null)
@@ -117,9 +117,9 @@ public sealed class ReplacementService : IReplacementService, INService
objs.Add(repCtx.Channel);
}
if (repCtx.Users is not null)
if (repCtx.User is not null)
{
objs.Add(repCtx.Users);
objs.Add(repCtx.User);
}
if (repCtx.Guild is not null)

View File

@@ -9,8 +9,8 @@ public sealed partial class Replacer
private readonly IEnumerable<RegexReplacementInfo> _regexReps;
private readonly object[] _inputData;
[GeneratedRegex(@"\%[\p{L}\p{N}\._]*[\p{L}\p{N}]+[\p{L}\p{N}\._]*\%")]
private static partial Regex TokenExtractionRegex();
// [GeneratedRegex(@"\%[\p{L}\p{N}\._]*[\p{L}\p{N}]+[\p{L}\p{N}\._]*\%")]
// private static partial Regex TokenExtractionRegex();
public Replacer(IEnumerable<ReplacementInfo> reps, IEnumerable<RegexReplacementInfo> regexReps, object[] inputData)
{
@@ -24,19 +24,19 @@ public sealed partial class Replacer
if (string.IsNullOrWhiteSpace(input))
return input;
var matches = TokenExtractionRegex().IsMatch(input);
// var matches = TokenExtractionRegex().IsMatch(input);
if (matches)
// if (matches)
// {
foreach (var rep in _reps)
{
foreach (var rep in _reps)
if (input.Contains(rep.Token, StringComparison.InvariantCulture))
{
if (input.Contains(rep.Token, StringComparison.InvariantCulture))
{
var objs = GetParams(rep.InputTypes);
input = input.Replace(rep.Token, await rep.GetValueAsync(objs), StringComparison.InvariantCulture);
}
var objs = GetParams(rep.InputTypes);
input = input.Replace(rep.Token, await rep.GetValueAsync(objs), StringComparison.InvariantCulture);
}
}
// }
foreach (var rep in _regexReps)
{
@@ -47,7 +47,7 @@ public sealed partial class Replacer
if (match.Success)
{
sb.Append(input, 0, match.Index)
.Append(await rep.GetValueAsync(match, objs));
.Append(await rep.GetValueAsync(match, objs));
var lastIndex = match.Index + match.Length;
sb.Append(input, lastIndex, input.Length - lastIndex);
@@ -91,16 +91,18 @@ public sealed partial class Replacer
=> new()
{
Embeds = await embedArr.Embeds.Map(async e => await ReplaceAsync(e) with
{
Color = e.Color
}).WhenAll(),
{
Color = e.Color
})
.WhenAll(),
Content = await ReplaceAsync(embedArr.Content)
};
private async ValueTask<SmartPlainText> ReplaceAsync(SmartPlainText plain)
=> await ReplaceAsync(plain.Text);
private async Task<T> ReplaceAsync<T>(T embedData) where T : SmartEmbedTextBase, new()
private async Task<T> ReplaceAsync<T>(T embedData)
where T : SmartEmbedTextBase, new()
{
var newEmbedData = new T
{
@@ -117,13 +119,14 @@ public sealed partial class Replacer
IconUrl = await ReplaceAsync(embedData.Author.IconUrl)
},
Fields = await Task.WhenAll(embedData
.Fields?
.Map(async f => new SmartTextEmbedField
{
Name = await ReplaceAsync(f.Name),
Value = await ReplaceAsync(f.Value),
Inline = f.Inline
}) ?? []),
.Fields?
.Map(async f => new SmartTextEmbedField
{
Name = await ReplaceAsync(f.Name),
Value = await ReplaceAsync(f.Value),
Inline = f.Inline
})
?? []),
Footer = embedData.Footer is null
? null
: new()

View File

@@ -69,5 +69,11 @@ public sealed class BotConfigService : ConfigServiceBase<BotConfig>
c.Version = 7;
c.IgnoreOtherBots = true;
});
if(data.Version < 8)
ModifyConfig(c =>
{
c.Version = 8;
});
}
}