Updated common files

This commit is contained in:
Toastie (DCS Team) 2024-06-26 19:44:07 +12:00
parent b045015efb
commit a63889b2f3
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
61 changed files with 188 additions and 227 deletions

View file

@ -1,4 +1,4 @@
#nullable disable #nullable disable
namespace EllieBot.Common; namespace EllieBot.Common;
public enum AddRemove public enum AddRemove

View file

@ -1,4 +1,4 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace EllieBot.Common.Attributes; namespace EllieBot.Common.Attributes;

View file

@ -7,5 +7,5 @@ namespace EllieBot.Common;
[AttributeUsage(AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Class)]
public class DIIgnoreAttribute : Attribute public class DIIgnoreAttribute : Attribute
{ {
} }

View file

@ -1,4 +1,4 @@
namespace EllieBot.Common.Attributes; namespace EllieBot.Common.Attributes;
[AttributeUsage(AttributeTargets.Method)] [AttributeUsage(AttributeTargets.Method)]
public sealed class EllieOptionsAttribute<TOption> : Attribute public sealed class EllieOptionsAttribute<TOption> : Attribute

View file

@ -1,4 +1,4 @@
#nullable disable #nullable disable
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace EllieBot.Common; namespace EllieBot.Common;

View file

@ -1,4 +1,4 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace EllieBot.Common.Attributes; namespace EllieBot.Common.Attributes;
@ -16,4 +16,4 @@ public sealed class OwnerOnlyAttribute : PreconditionAttribute
? PreconditionResult.FromSuccess() ? PreconditionResult.FromSuccess()
: PreconditionResult.FromError("Not owner")); : PreconditionResult.FromError("Not owner"));
} }
} }

View file

@ -1,4 +1,4 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace EllieBot.Common.Attributes; namespace EllieBot.Common.Attributes;

View file

@ -1,4 +1,4 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Discord; namespace Discord;

View file

@ -1,4 +1,4 @@
using EllieBot.Common.Yml; using EllieBot.Common.Yml;
using YamlDotNet.Serialization; using YamlDotNet.Serialization;
namespace EllieBot.Common.Attributes; namespace EllieBot.Common.Attributes;

View file

@ -44,13 +44,13 @@ public sealed partial class BotConfig : ICloneable<BotConfig>
or all owners? (this might cause the bot to lag if there's a lot of owners specified) or all owners? (this might cause the bot to lag if there's a lot of owners specified)
""")] """)]
public bool ForwardToAllOwners { get; set; } public bool ForwardToAllOwners { get; set; }
[Comment(""" [Comment("""
Any messages sent by users in Bot's DM to be forwarded to the specified channel. Any messages sent by users in Bot's DM to be forwarded to the specified channel.
This option will only work when ForwardToAllOwners is set to false This option will only work when ForwardToAllOwners is set to false
""")] """)]
public ulong? ForwardToChannel { get; set; } public ulong? ForwardToChannel { get; set; }
[Comment(""" [Comment("""
Should the bot ignore messages from other bots? Should the bot ignore messages from other bots?
Settings this to false might get your bot banned if it gets into a spam loop with another bot. Settings this to false might get your bot banned if it gets into a spam loop with another bot.
@ -155,11 +155,11 @@ public sealed partial class BotConfig : ICloneable<BotConfig>
]; ];
} }
// [Comment(@"Whether the prefix will be a suffix, or prefix. // [Comment(@"Whether the prefix will be a suffix, or prefix.
// For example, if your prefix is ! you will run a command called 'cash' by typing either // For example, if your prefix is ! you will run a command called 'cash' by typing either
// '!cash @Someone' if your prefixIsSuffix: false or // '!cash @Someone' if your prefixIsSuffix: false or
// 'cash @Someone!' if your prefixIsSuffix: true")] // 'cash @Someone!' if your prefixIsSuffix: true")]
// public bool PrefixIsSuffix { get; set; } // public bool PrefixIsSuffix { get; set; }
// public string Prefixed(string text) => PrefixIsSuffix // public string Prefixed(string text) => PrefixIsSuffix
// ? text + Prefix // ? text + Prefix

View file

@ -1,4 +1,4 @@
namespace EllieBot.Common.Configs; namespace EllieBot.Common.Configs;
/// <summary> /// <summary>
/// Base interface for available config serializers /// Base interface for available config serializers

View file

@ -1,7 +1,7 @@
#nullable disable #nullable disable
using EllieBot.Common.Yml; using EllieBot.Common.Yml;
namespace Ellie.Common; namespace EllieBot.Common;
public sealed class Creds : IBotCredentials public sealed class Creds : IBotCredentials
{ {
@ -16,7 +16,7 @@ public sealed class Creds : IBotCredentials
**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("Keep this on 'true' unless you're sure your bot shouldn't use privileged intents or you're waiting to be accepted")] [Comment("Keep this on 'true' unless you're sure your bot shouldn't use privileged intents or you're waiting to be accepted")]
public bool UsePrivilegedIntents { get; set; } public bool UsePrivilegedIntents { get; set; }
@ -29,15 +29,24 @@ public sealed class Creds : IBotCredentials
""")] """)]
public int TotalShards { get; set; } public int TotalShards { get; set; }
[Comment( [Comment("""
Pledge 5$ or more on https://patreon.com/elliebot and connect your discord account to Patreon.
Go to https://dashy.elliebot.net and login with your discord account
Go to the Keys page and click "Generate New Key" and copy it here
You and anyone else with the permission to run `.prompt` command will be able to use natural language to run bot's commands.
For example '@Bot how's the weather in Paris' will return the current weather in Paris as if you were to run `.weather Paris` command
""")]
public string EllieAiToken { 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. 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( [Comment(
""" """
Create a new custom search here https://programmablesearchengine.google.com/cse/create/new Create a new custom search here https://programmablesearchengine.google.com/cse/create/new
Enable SafeSearch Enable SafeSearch
@ -64,16 +73,16 @@ public sealed class Creds : IBotCredentials
[Comment("""Official cleverbot api key.""")] [Comment("""Official cleverbot api key.""")]
public string CleverbotApiKey { get; set; } public string CleverbotApiKey { get; set; }
[Comment(@"Official GPT-3 api key.")] [Comment(@"OpenAi api key.")]
public string Gpt3ApiKey { get; set; } public string Gpt3ApiKey { get; set; }
[Comment(""" [Comment("""
Which cache implementation should bot use. Which cache implementation should bot use.
'memory' - Cache will be in memory of the bot's process itself. Only use this on bots with a single shard. When the bot is restarted the cache is reset. 'memory' - Cache will be in memory of the bot's process itself. Only use this on bots with a single shard. When the bot is restarted the cache is reset.
'redis' - Uses redis (which needs to be separately downloaded and installed). The cache will persist through bot restarts. You can configure connection string in creds.yml 'redis' - Uses redis (which needs to be separately downloaded and installed). The cache will persist through bot restarts. You can configure connection string in creds.yml
""")] """)]
public BotCacheImplemenation BotCache { get; set; } public BotCacheImplemenation BotCache { get; set; }
[Comment(""" [Comment("""
Redis connection string. Don't change if you don't know what you're doing. Redis connection string. Don't change if you don't know what you're doing.
Only used if botCache is set to 'redis' Only used if botCache is set to 'redis'
@ -110,10 +119,10 @@ public sealed class Creds : IBotCredentials
Used for cryptocurrency related commands. Used for cryptocurrency related commands.
""")] """)]
public string CoinmarketcapApiKey { get; set; } public string CoinmarketcapApiKey { get; set; }
// [Comment(@"https://polygon.io/dashboard/api-keys api key. Free plan allows for 5 queries per minute. // [Comment(@"https://polygon.io/dashboard/api-keys api key. Free plan allows for 5 queries per minute.
// Used for stocks related commands.")] // Used for stocks related commands.")]
// public string PolygonIoApiKey { get; set; } // public string PolygonIoApiKey { 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; }
@ -171,7 +180,7 @@ public sealed class Creds : IBotCredentials
RestartCommand = new RestartConfig(); RestartCommand = new RestartConfig();
Google = new GoogleApiConfig(); Google = new GoogleApiConfig();
} }
public class DbOptions public class DbOptions
: IDbOptions : IDbOptions
{ {
@ -270,3 +279,6 @@ public class GoogleApiConfig : IGoogleApiConfig
public string SearchId { get; init; } public string SearchId { get; init; }
public string ImageSearchId { get; init; } public string ImageSearchId { get; init; }
} }

View file

@ -1,4 +1,4 @@
using MessageType = Discord.MessageType; using MessageType = Discord.MessageType;
namespace EllieBot.Modules.Administration; namespace EllieBot.Modules.Administration;

View file

@ -1,4 +1,4 @@
#nullable disable #nullable disable
namespace EllieBot.Common; namespace EllieBot.Common;
public class DownloadTracker : IEService public class DownloadTracker : IEService

View file

@ -91,7 +91,7 @@ public abstract class EllieModule : ModuleBase
if (validate is not null && !validate(arg.Content)) if (validate is not null && !validate(arg.Content))
return Task.CompletedTask; return Task.CompletedTask;
if (userInputTask.TrySetResult(arg.Content)) if (userInputTask.TrySetResult(arg.Content))
userMsg.DeleteAfter(1); userMsg.DeleteAfter(1);

View file

@ -28,7 +28,7 @@ public enum LogType
VoicePresence, VoicePresence,
UserMuted, UserMuted,
UserWarned, UserWarned,
ThreadDeleted, ThreadDeleted,
ThreadCreated ThreadCreated
} }

View file

@ -17,14 +17,14 @@ public partial class PermCheckResult
{ {
public bool IsAllowed public bool IsAllowed
=> IsT0; => IsT0;
public bool IsCooldown public bool IsCooldown
=> IsT1; => IsT1;
public bool IsGlobalBlock public bool IsGlobalBlock
=> IsT2; => IsT2;
public bool IsDisallowed public bool IsDisallowed
=> IsT3; => IsT3;
} }

View file

@ -1,4 +1,4 @@
#nullable disable #nullable disable
namespace EllieBot.Common; namespace EllieBot.Common;
public interface IPlaceholderProvider public interface IPlaceholderProvider

View file

@ -5,7 +5,7 @@ using Cloneable;
namespace EllieBot.Common; namespace EllieBot.Common;
[Cloneable] [Cloneable]
public partial class ImageUrls : ICloneable<ImageUrls> public partial class ImageUrls : ICloneable<ImageUrls>
{ {
[Comment("DO NOT CHANGE")] [Comment("DO NOT CHANGE")]
public int Version { get; set; } = 3; public int Version { get; set; } = 3;

View file

@ -1,4 +1,4 @@
namespace EllieBot; namespace EllieBot;
public abstract class EllieInteractionBase public abstract class EllieInteractionBase
{ {
@ -85,6 +85,9 @@ public abstract class EllieInteractionBase
public Task ExecuteOnActionAsync(SocketMessageComponent smc) public Task ExecuteOnActionAsync(SocketMessageComponent smc)
=> _onAction(smc); => _onAction(smc);
public void SetCompleted()
=> _interactionCompletedSource.TrySetResult(true);
} }
public sealed class EllieModalSubmitHandler public sealed class EllieModalSubmitHandler

View file

@ -20,9 +20,9 @@ public interface IEllieInteractionService
SelectMenuBuilder menu, SelectMenuBuilder menu,
Func<SocketMessageComponent, Task> onTrigger, Func<SocketMessageComponent, Task> onTrigger,
bool singleUse = true); bool singleUse = true);
EllieInteractionBase Create( EllieInteractionBase Create(
ulong userId, ulong userId,
ButtonBuilder button, ButtonBuilder button,
ModalBuilder modal, ModalBuilder modal,
Func<SocketModal, Task> onTrigger, Func<SocketModal, Task> onTrigger,

View file

@ -1,4 +1,4 @@
namespace EllieBot; namespace EllieBot;
public sealed class EllieButtonInteractionHandler : EllieInteractionBase public sealed class EllieButtonInteractionHandler : EllieInteractionBase
{ {
@ -16,6 +16,6 @@ public sealed class EllieButtonInteractionHandler : EllieInteractionBase
public ButtonBuilder Button { get; } public ButtonBuilder Button { get; }
public override void AddTo(ComponentBuilder cb) public override void AddTo(ComponentBuilder cb)
=> cb.WithButton(Button); => cb.WithButton(Button);
} }

View file

@ -7,8 +7,8 @@ namespace EllieBot.Common.JsonConverters;
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() ?? "en_US"); => 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);
} }

View file

@ -0,0 +1,30 @@
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
public class NumberToStringConverter : JsonConverter<object>
{
public override bool CanConvert(Type typeToConvert)
=> typeof(string) == typeToConvert;
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
switch (reader.TokenType)
{
case JsonTokenType.Number:
return reader.TryGetInt64(out var l)
? l.ToString()
: reader.GetDouble().ToString(CultureInfo.InvariantCulture);
case JsonTokenType.String:
return reader.GetString() ?? string.Empty;
default:
{
using var document = JsonDocument.ParseValue(ref reader);
return document.RootElement.Clone().ToString();
}
}
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
=> writer.WriteStringValue(value.ToString());
}

View file

@ -1,4 +1,4 @@
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;

View file

@ -9,7 +9,7 @@ public static class Linq2DbExpressions
[ExpressionMethod(nameof(GuildOnShardExpression))] [ExpressionMethod(nameof(GuildOnShardExpression))]
public static bool GuildOnShard(ulong guildId, int totalShards, int shardId) public static bool GuildOnShard(ulong guildId, int totalShards, int shardId)
=> throw new NotSupportedException(); => throw new NotSupportedException();
private static Expression<Func<ulong, int, int, bool>> GuildOnShardExpression() private static Expression<Func<ulong, int, int, bool>> GuildOnShardExpression()
=> (guildId, totalShards, shardId) => (guildId, totalShards, shardId)
=> guildId / 4194304 % (ulong)totalShards == (ulong)shardId; => guildId / 4194304 % (ulong)totalShards == (ulong)shardId;

View file

@ -1,4 +1,4 @@
namespace EllieBot.Common.ModuleBehaviors; namespace EllieBot.Common.ModuleBehaviors;
/// <summary> /// <summary>
/// Executed if no command was found for this message /// Executed if no command was found for this message

View file

@ -1,4 +1,4 @@
namespace EllieBot.Common.ModuleBehaviors; namespace EllieBot.Common.ModuleBehaviors;
/// <summary> /// <summary>
/// Implemented by modules to handle non-bot messages received /// Implemented by modules to handle non-bot messages received

View file

@ -1,4 +1,4 @@
namespace EllieBot.Common.ModuleBehaviors; namespace EllieBot.Common.ModuleBehaviors;
/// <summary> /// <summary>
/// This interface's method is executed after the command successfully finished execution. /// This interface's method is executed after the command successfully finished execution.

View file

@ -1,4 +1,4 @@
namespace EllieBot.Common.ModuleBehaviors; namespace EllieBot.Common.ModuleBehaviors;
/// <summary> /// <summary>
/// This interface's method is executed after a command was found but before it was executed. /// This interface's method is executed after a command was found but before it was executed.

View file

@ -1,4 +1,4 @@
namespace EllieBot.Common.ModuleBehaviors; namespace EllieBot.Common.ModuleBehaviors;
/// <summary> /// <summary>
/// Implemented by services which may transform input before a command is searched for /// Implemented by services which may transform input before a command is searched for

View file

@ -1,4 +1,4 @@
namespace EllieBot.Common.ModuleBehaviors; namespace EllieBot.Common.ModuleBehaviors;
/// <summary> /// <summary>
/// All services which need to execute something after /// All services which need to execute something after

View file

@ -1,4 +1,4 @@
using DryIoc; using DryIoc;
namespace EllieBot.Extensions; namespace EllieBot.Extensions;
@ -11,7 +11,7 @@ public static class DryIocExtensions
return container; return container;
} }
public static IContainer AddSingleton<TSvc, TImpl>(this IContainer container, TImpl obj) public static IContainer AddSingleton<TSvc, TImpl>(this IContainer container, TImpl obj)
where TImpl : TSvc where TImpl : TSvc
{ {
@ -41,7 +41,7 @@ public static class DryIocExtensions
return container; return container;
} }
public static IContainer AddSingleton<TImpl>(this IContainer container, Func<IResolverContext, TImpl> factory) public static IContainer AddSingleton<TImpl>(this IContainer container, Func<IResolverContext, TImpl> factory)
{ {
container.RegisterDelegate(factory); container.RegisterDelegate(factory);

View file

@ -1,5 +1,12 @@
namespace EllieBot.Modules.Patronage; namespace EllieBot.Modules.Patronage;
public enum LimitedFeatureName
{
ChatBot,
ReactionRole,
Prune,
}
public readonly struct FeatureLimitKey public readonly struct FeatureLimitKey
{ {
public string PrettyName { get; init; } public string PrettyName { get; init; }

View file

@ -1,8 +0,0 @@
namespace EllieBot.Modules.Patronage;
public readonly struct FeatureQuotaStats
{
public (uint Cur, uint Max) Hourly { get; init; }
public (uint Cur, uint Max) Daily { get; init; }
public (uint Cur, uint Max) Monthly { get; init; }
}

View file

@ -31,26 +31,15 @@ public interface IPatronageService
/// </summary> /// </summary>
/// <param name="userId">UserId for which to get the patron data for.</param> /// <param name="userId">UserId for which to get the patron data for.</param>
/// <returns>A patron with the specifeid userId</returns> /// <returns>A patron with the specifeid userId</returns>
public Task<Patron> GetPatronAsync(ulong userId); public Task<Patron?> GetPatronAsync(ulong userId);
/// <summary> Task<bool> LimitHitAsync(LimitedFeatureName key, ulong userId, int amount = 1);
/// Gets the quota statistic for the user/patron specified by the userId Task<bool> LimitForceHit(LimitedFeatureName key, ulong userId, int amount);
/// </summary> Task<QuotaLimit> GetUserLimit(LimitedFeatureName name, ulong userId);
/// <param name="userId">UserId of the user for which to get the quota statistic for</param>
/// <returns>Quota stats for the specified user</returns>
Task<UserQuotaStats> GetUserQuotaStatistic(ulong userId);
Task<FeatureLimit> TryGetFeatureLimitAsync(FeatureLimitKey key, ulong userId, int? defaultValue); Task<Dictionary<LimitedFeatureName, (int, QuotaLimit)>> LimitStats(ulong userId);
ValueTask<OneOf<(uint Hourly, uint Daily, uint Monthly), QuotaLimit>> TryIncrementQuotaCounterAsync(
ulong userId,
bool isSelf,
FeatureType featureType,
string featureName,
uint? maybeHourly,
uint? maybeDaily,
uint? maybeMonthly);
PatronConfigData GetConfig(); PatronConfigData GetConfig();
int PercentBonus(Patron? user);
int PercentBonus(long amount);
} }

View file

@ -13,7 +13,7 @@ public readonly struct Patron
public ulong UserId { get; init; } public ulong UserId { get; init; }
/// <summary> /// <summary>
/// Amount the Patron is currently pledging or paid /// Amount the Patron is currently pledging or paid in cents
/// </summary> /// </summary>
public int Amount { get; init; } public int Amount { get; init; }

View file

@ -7,31 +7,11 @@ namespace EllieBot.Modules.Patronage;
public partial class PatronConfigData : ICloneable<PatronConfigData> public partial class PatronConfigData : ICloneable<PatronConfigData>
{ {
[Comment("DO NOT CHANGE")] [Comment("DO NOT CHANGE")]
public int Version { get; set; } = 2; public int Version { get; set; } = 3;
[Comment("Whether the patronage feature is enabled")] [Comment("Whether the patronage feature is enabled")]
public bool IsEnabled { get; set; } public bool IsEnabled { get; set; }
[Comment("List of patron only features and relevant quota data")] [Comment("Who can do how much of what")]
public FeatureQuotas Quotas { get; set; } public Dictionary<int, Dictionary<LimitedFeatureName, QuotaLimit>> Limits { get; set; } = new();
public PatronConfigData()
{
Quotas = new();
}
public class FeatureQuotas
{
[Comment("Dictionary of feature names with their respective limits. Set to null for unlimited")]
public Dictionary<string, Dictionary<PatronTier, int?>> Features { get; set; } = new();
[Comment("Dictionary of commands with their respective quota data")]
public Dictionary<string, Dictionary<PatronTier, Dictionary<QuotaPer, uint>?>> Commands { get; set; } = new();
[Comment("Dictionary of groups with their respective quota data")]
public Dictionary<string, Dictionary<PatronTier, Dictionary<QuotaPer, uint>?>> Groups { get; set; } = new();
[Comment("Dictionary of modules with their respective quota data")]
public Dictionary<string, Dictionary<PatronTier, Dictionary<QuotaPer, uint>?>> Modules { get; set; } = new();
}
} }

View file

@ -8,15 +8,6 @@ public static class PatronExtensions
_ => $"Patron Tier {tier}", _ => $"Patron Tier {tier}",
}; };
public static string ToFullName(this QuotaPer per)
=> per switch
{
QuotaPer.PerDay => "per day",
QuotaPer.PerHour => "per hour",
QuotaPer.PerMonth => "per month",
_ => "Unknown",
};
public static DateTime DayOfNextMonth(this DateTime date, int day) public static DateTime DayOfNextMonth(this DateTime date, int day)
{ {
var nextMonth = date.AddMonths(1); var nextMonth = date.AddMonths(1);

View file

@ -10,57 +10,16 @@ public readonly struct QuotaLimit
/// <summary> /// <summary>
/// Amount of usages reached, which is the limit /// Amount of usages reached, which is the limit
/// </summary> /// </summary>
public uint Quota { get; init; } public int Quota { get; init; }
/// <summary> /// <summary>
/// Which period is this quota limit for (hourly, daily, monthly, etc...) /// Which period is this quota limit for (hourly, daily, monthly, etc...)
/// </summary> /// </summary>
public QuotaPer QuotaPeriod { get; init; } public QuotaPer QuotaPeriod { get; init; }
/// <summary> public QuotaLimit(int quota, QuotaPer quotaPeriod)
/// When does this quota limit reset
/// </summary>
public DateTime ResetsAt { get; init; }
/// <summary>
/// Type of the feature this quota limit is for
/// </summary>
public FeatureType FeatureType { get; init; }
/// <summary>
/// Name of the feature this quota limit is for
/// </summary>
public string Feature { get; init; }
/// <summary>
/// Whether it is the user's own quota (true), or server owners (false)
/// </summary>
public bool IsOwnQuota { get; init; }
}
/// <summary>
/// Respresent information about the feature limit
/// </summary>
public readonly struct FeatureLimit
{
/// <summary>
/// Whether this limit comes from the patronage system
/// </summary>
public bool IsPatronLimit { get; init; } = false;
/// <summary>
/// Maximum limit allowed
/// </summary>
public int? Quota { get; init; } = null;
/// <summary>
/// Name of the limit
/// </summary>
public string Name { get; init; } = string.Empty;
public FeatureLimit()
{ {
Quota = quota;
QuotaPeriod = quotaPeriod;
} }
} }

View file

@ -5,4 +5,5 @@ public enum QuotaPer
PerHour, PerHour,
PerDay, PerDay,
PerMonth, PerMonth,
Total,
} }

View file

@ -1,25 +0,0 @@
namespace EllieBot.Modules.Patronage;
public readonly struct UserQuotaStats
{
private static readonly IReadOnlyDictionary<string, FeatureQuotaStats> _emptyDictionary
= new Dictionary<string, FeatureQuotaStats>();
public PatronTier Tier { get; init; }
= PatronTier.None;
public IReadOnlyDictionary<string, FeatureQuotaStats> Features { get; init; }
= _emptyDictionary;
public IReadOnlyDictionary<string, FeatureQuotaStats> Commands { get; init; }
= _emptyDictionary;
public IReadOnlyDictionary<string, FeatureQuotaStats> Groups { get; init; }
= _emptyDictionary;
public IReadOnlyDictionary<string, FeatureQuotaStats> Modules { get; init; }
= _emptyDictionary;
public UserQuotaStats()
{
}
}

View file

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

View file

@ -95,7 +95,7 @@ public sealed partial class ReplacementPatternStore
Register(_rngRegex, Register(_rngRegex,
match => match =>
{ {
var rng = new EllieRandom(); var rng = new NadekoRandom();
if (!int.TryParse(match.Groups["from"].ToString(), out var from)) if (!int.TryParse(match.Groups["from"].ToString(), out var from))
from = 0; from = 0;
if (!int.TryParse(match.Groups["to"].ToString(), out var to)) if (!int.TryParse(match.Groups["to"].ToString(), out var to))

View file

@ -33,15 +33,15 @@ public sealed class MessageSenderService : IMessageSenderService, IEService
=> new ResponseBuilder(_bs, _bcs, _client) => new ResponseBuilder(_bs, _bcs, _client)
.Channel(smc.Channel); .Channel(smc.Channel);
public EllieEmbedBuilder CreateEmbed() public NadekoEmbedBuilder CreateEmbed()
=> new EllieEmbedBuilder(_bcs); => new NadekoEmbedBuilder(_bcs);
} }
public class EllieEmbedBuilder : EmbedBuilder public class NadekoEmbedBuilder : EmbedBuilder
{ {
private readonly BotConfig _bc; private readonly BotConfig _bc;
public EllieEmbedBuilder(BotConfigService bcs) public NadekoEmbedBuilder(BotConfigService bcs)
{ {
_bc = bcs.Data; _bc = bcs.Data;
} }

View file

@ -11,6 +11,10 @@ public partial class ResponseBuilder
private readonly ResponseBuilder _builder; private readonly ResponseBuilder _builder;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private int currentPage; private int currentPage;
private EllieButtonInteractionHandler left;
private EllieButtonInteractionHandler right;
private EllieInteractionBase? extra;
public PaginationSender( public PaginationSender(
SourcedPaginatedResponseBuilder<T> paginationBuilder, SourcedPaginatedResponseBuilder<T> paginationBuilder,
@ -106,6 +110,8 @@ public partial class ResponseBuilder
return (leftBtnInter, maybeInter, rightBtnInter); return (leftBtnInter, maybeInter, rightBtnInter);
} }
(left, extra, right) = await GetInteractions();
async Task UpdatePageAsync(SocketMessageComponent smc) async Task UpdatePageAsync(SocketMessageComponent smc)
{ {
@ -114,21 +120,25 @@ public partial class ResponseBuilder
if (_paginationBuilder.AddPaginatedFooter) if (_paginationBuilder.AddPaginatedFooter)
toSend.AddPaginatedFooter(currentPage, lastPage); toSend.AddPaginatedFooter(currentPage, lastPage);
var (left, extra, right) = (await GetInteractions()); left.SetCompleted();
right.SetCompleted();
extra?.SetCompleted();
(left, extra, right) = (await GetInteractions());
var cb = new ComponentBuilder(); var cb = new ComponentBuilder();
left.AddTo(cb); left.AddTo(cb);
right.AddTo(cb); right.AddTo(cb);
extra?.AddTo(cb); extra?.AddTo(cb);
await smc.ModifyOriginalResponseAsync(x => await smc.ModifyOriginalResponseAsync(x =>
{ {
x.Embed = toSend.Build(); x.Embed = toSend.Build();
x.Components = cb.Build(); x.Components = cb.Build();
}); });
await Task.WhenAll(left.RunAsync(smc.Message), extra?.RunAsync(smc.Message) ?? Task.CompletedTask, right.RunAsync(smc.Message));
} }
var (left, extra, right) = await GetInteractions();
var cb = new ComponentBuilder(); var cb = new ComponentBuilder();
left.AddTo(cb); left.AddTo(cb);
@ -144,9 +154,11 @@ public partial class ResponseBuilder
if (lastPage == 0 && _paginationBuilder.InteractionFunc is null) if (lastPage == 0 && _paginationBuilder.InteractionFunc is null)
return; return;
await Task.WhenAll(left.RunAsync(msg), extra?.RunAsync(msg) ?? Task.CompletedTask, right.RunAsync(msg)); await Task.WhenAll(left.RunAsync(msg), extra?.RunAsync(msg) ?? Task.CompletedTask, right.RunAsync(msg));
await Task.Delay(30_000);
await msg.ModifyAsync(mp => mp.Components = new ComponentBuilder().Build()); await msg.ModifyAsync(mp => mp.Components = new ComponentBuilder().Build());
} }
} }

View file

@ -1,4 +1,4 @@
using DryIoc; using DryIoc;
using LinqToDB.Extensions; using LinqToDB.Extensions;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using EllieBot.Modules.Music; using EllieBot.Modules.Music;
@ -33,7 +33,7 @@ public static class ServiceCollectionExtensions
public static IContainer AddConfigServices(this IContainer svcs, Assembly a) public static IContainer AddConfigServices(this IContainer svcs, Assembly a)
{ {
foreach (var type in a.GetTypes() foreach (var type in a.GetTypes()
.Where(x => !x.IsAbstract && x.IsAssignableToGenericType(typeof(ConfigServiceBase<>)))) .Where(x => !x.IsAbstract && x.IsAssignableToGenericType(typeof(ConfigServiceBase<>))))
{ {
@ -41,7 +41,7 @@ public static class ServiceCollectionExtensions
getServiceTypes: type => type.GetImplementedTypes(ReflectionTools.AsImplementedType.SourceType), getServiceTypes: type => type.GetImplementedTypes(ReflectionTools.AsImplementedType.SourceType),
getImplFactory: type => ReflectionFactory.Of(type, Reuse.Singleton)); getImplFactory: type => ReflectionFactory.Of(type, Reuse.Singleton));
} }
return svcs; return svcs;
} }
@ -95,7 +95,7 @@ public static class ServiceCollectionExtensions
}); });
var prov = proxySvcs.BuildServiceProvider(); var prov = proxySvcs.BuildServiceProvider();
svcs.RegisterDelegate<IHttpClientFactory>(_ => prov.GetRequiredService<IHttpClientFactory>()); svcs.RegisterDelegate<IHttpClientFactory>(_ => prov.GetRequiredService<IHttpClientFactory>());
svcs.RegisterDelegate<HttpClient>(_ => prov.GetRequiredService<HttpClient>()); svcs.RegisterDelegate<HttpClient>(_ => prov.GetRequiredService<HttpClient>());
@ -113,7 +113,7 @@ public static class ServiceCollectionExtensions
typeof(IInputTransformer), typeof(IInputTransformer),
typeof(IEService) typeof(IEService)
]; ];
foreach (var svc in a.GetTypes() foreach (var svc in a.GetTypes()
.Where(type => type.IsClass && types.Any(t => type.IsAssignableTo(t)) && !type.HasAttribute<DIIgnoreAttribute>() .Where(type => type.IsClass && types.Any(t => type.IsAssignableTo(t)) && !type.HasAttribute<DIIgnoreAttribute>()
#if GLOBAL_ELLIE #if GLOBAL_ELLIE

View file

@ -9,7 +9,7 @@ namespace EllieBot.Services;
public class CommandHandler : IEService, IReadyExecutor, ICommandHandler public class CommandHandler : IEService, IReadyExecutor, ICommandHandler
{ {
private const int GLOBAL_COMMANDS_COOLDOWN = 750; private const int GLOBAL_COMMANDS_COOLDOWN = 200;
private const float ONE_THOUSANDTH = 1.0f / 1000; private const float ONE_THOUSANDTH = 1.0f / 1000;
@ -262,7 +262,7 @@ public class CommandHandler : IEService, IReadyExecutor, ICommandHandler
var blockTime = Environment.TickCount - startTime; var blockTime = Environment.TickCount - startTime;
var messageContent = await _behaviorHandler.RunInputTransformersAsync(guild, usrMsg); var messageContent = await _behaviorHandler.RunInputTransformersAsync(guild, usrMsg);
var prefix = GetPrefix(guild?.Id); var prefix = GetPrefix(guild?.Id);
var isPrefixCommand = messageContent.StartsWith(".prefix", StringComparison.InvariantCultureIgnoreCase); var isPrefixCommand = messageContent.StartsWith(".prefix", StringComparison.InvariantCultureIgnoreCase);
// execute the command and measure the time it took // execute the command and measure the time it took

View file

@ -1,4 +1,4 @@
#nullable disable #nullable disable
namespace EllieBot.Services; namespace EllieBot.Services;
public interface IBehaviorHandler public interface IBehaviorHandler
@ -7,7 +7,7 @@ public interface IBehaviorHandler
Task AddRangeAsync(IEnumerable<ICustomBehavior> behavior); Task AddRangeAsync(IEnumerable<ICustomBehavior> behavior);
Task<bool> RemoveAsync(ICustomBehavior behavior); Task<bool> RemoveAsync(ICustomBehavior behavior);
Task RemoveRangeAsync(IEnumerable<ICustomBehavior> behs); Task RemoveRangeAsync(IEnumerable<ICustomBehavior> behs);
Task<bool> RunExecOnMessageAsync(SocketGuild guild, IUserMessage usrMsg); Task<bool> RunExecOnMessageAsync(SocketGuild guild, IUserMessage usrMsg);
Task<string> RunInputTransformersAsync(SocketGuild guild, IUserMessage usrMsg); Task<string> RunInputTransformersAsync(SocketGuild guild, IUserMessage usrMsg);
Task<bool> RunPreCommandAsync(ICommandContext context, CommandInfo cmd); Task<bool> RunPreCommandAsync(ICommandContext context, CommandInfo cmd);

View file

@ -1,4 +1,4 @@
#nullable disable #nullable disable
using System.Globalization; using System.Globalization;
namespace EllieBot.Services; namespace EllieBot.Services;

View file

@ -1,4 +1,4 @@
namespace EllieBot.Common; namespace EllieBot.Common;
public interface ITimezoneService public interface ITimezoneService
{ {

View file

@ -53,7 +53,7 @@ public class YtdlOperation
} }
catch (Win32Exception) catch (Win32Exception)
{ {
Log.Error("youtube-dl is likely not installed. " + "Please install it before running the command again"); Log.Error("youtube-dl is likely not installed. Please install it before running the command again");
return default; return default;
} }
catch (Exception ex) catch (Exception ex)

View file

@ -49,21 +49,21 @@ public sealed class BotConfigService : ConfigServiceBase<BotConfig>
.ToHashSet(); .ToHashSet();
}); });
} }
if (data.Version < 4) if (data.Version < 4)
ModifyConfig(c => ModifyConfig(c =>
{ {
c.Version = 4; c.Version = 4;
c.CheckForUpdates = true; c.CheckForUpdates = true;
}); });
if (data.Version < 5) if(data.Version < 5)
ModifyConfig(c => ModifyConfig(c =>
{ {
c.Version = 5; c.Version = 5;
}); });
if (data.Version < 7) if(data.Version < 7)
ModifyConfig(c => ModifyConfig(c =>
{ {
c.Version = 7; c.Version = 7;

View file

@ -1,4 +1,4 @@
#nullable disable #nullable disable
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using System.Globalization; using System.Globalization;
@ -33,7 +33,7 @@ public static class ConfigParsers
} }
public static bool InsensitiveEnum<T>(string input, out T output) public static bool InsensitiveEnum<T>(string input, out T output)
where T : struct where T: struct
=> Enum.TryParse(input, true, out output); => Enum.TryParse(input, true, out output);
} }

View file

@ -1,9 +1,9 @@
using EllieBot.Common.Configs; using EllieBot.Common.Configs;
using EllieBot.Common.Yml; using EllieBot.Common.Yml;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
namespace EllieBot.Services; namespace NadekoBot.Services;
/// <summary> /// <summary>
/// Base service for all settings services /// Base service for all settings services

View file

@ -1,4 +1,4 @@
#nullable disable #nullable disable
namespace EllieBot.Services; namespace EllieBot.Services;
/// <summary> /// <summary>

View file

@ -1,4 +1,4 @@
#nullable disable #nullable disable
namespace EllieBot.Services; namespace EllieBot.Services;
/// <summary> /// <summary>

View file

@ -1,4 +1,4 @@
#nullable disable #nullable disable
namespace EllieBot.Common.Yml; namespace EllieBot.Common.Yml;
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]

View file

@ -5,7 +5,6 @@ using YamlDotNet.Serialization.EventEmitters;
namespace EllieBot.Common.Yml; namespace EllieBot.Common.Yml;
public class MultilineScalarFlowStyleEmitter : ChainedEventEmitter public class MultilineScalarFlowStyleEmitter : ChainedEventEmitter
{ {
public MultilineScalarFlowStyleEmitter(IEventEmitter nextEmitter) public MultilineScalarFlowStyleEmitter(IEventEmitter nextEmitter)

View file

@ -27,4 +27,14 @@ public static class CommandContextExtensions
public static Task WarningAsync(this ICommandContext ctx) public static Task WarningAsync(this ICommandContext ctx)
=> ctx.ReactAsync(MsgType.Pending); => ctx.ReactAsync(MsgType.Pending);
public static Task OkAsync(this IUserMessage msg)
=> msg.AddReactionAsync(_okEmoji);
public static Task ErrorAsync(this IUserMessage msg)
=> msg.AddReactionAsync(_errorEmoji);
public static Task WarningAsync(this IUserMessage msg)
=> msg.AddReactionAsync(_warnEmoji);
} }

View file

@ -229,6 +229,7 @@ public static class Extensions
public static IEnumerable<IRole> GetRoles(this IGuildUser user) public static IEnumerable<IRole> GetRoles(this IGuildUser user)
=> user.RoleIds.Select(r => user.Guild.GetRole(r)).Where(r => r is not null); => user.RoleIds.Select(r => user.Guild.GetRole(r)).Where(r => r is not null);
// todo remove
public static void Lap(this Stopwatch sw, string checkpoint) public static void Lap(this Stopwatch sw, string checkpoint)
{ {
Log.Information("Checkpoint {CheckPoint}: {Time}ms", checkpoint, sw.Elapsed.TotalMilliseconds); Log.Information("Checkpoint {CheckPoint}: {Time}ms", checkpoint, sw.Elapsed.TotalMilliseconds);