using Discord; using Discord.Commands; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Kehyeedra3 { //https://github.com/Joe4evr/Discord.Addons/blob/master/src/Discord.Addons.Preconditions/Ratelimit/RatelimitAttribute.cs /// Sets how often a user is allowed to use this command /// or any command in this module. /// This is backed by an in-memory collection /// and will not persist with restarts. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] public sealed class RatelimitAttribute : PreconditionAttribute { private readonly uint _invokeLimit; private readonly bool _noLimitInDMs; private readonly bool _noLimitForAdmins; private readonly bool _applyPerGuild; private readonly TimeSpan _invokeLimitPeriod; private readonly Dictionary<(ulong, ulong?), CommandTimeout> _invokeTracker = new Dictionary<(ulong, ulong?), CommandTimeout>(); /// Sets how often a user is allowed to use this command. /// The number of times a user may use the command within a certain period. /// The amount of time since first invoke a user has until the limit is lifted. /// The scale in which the parameter should be measured. /// Set whether or not there is no limit to the command in DMs. Defaults to false. /// Set whether or not there is no limit to the command for guild admins. Defaults to false. /// Set whether or not to apply a limit per guild. Defaults to false. public RatelimitAttribute(uint times, double period, Measure measure, bool noLimitInDMs = false, bool noLimitForAdmins = false, bool applyPerGuild = false) { _invokeLimit = times; _noLimitInDMs = noLimitInDMs; _noLimitForAdmins = noLimitForAdmins; _applyPerGuild = applyPerGuild; //TODO: C# 7 candidate switch expression switch (measure) { case Measure.Days: _invokeLimitPeriod = TimeSpan.FromDays(period); break; case Measure.Hours: _invokeLimitPeriod = TimeSpan.FromHours(period); break; case Measure.Minutes: _invokeLimitPeriod = TimeSpan.FromMinutes(period); break; case Measure.Seconds: _invokeLimitPeriod = TimeSpan.FromSeconds(period); break; } } /// Sets how often a user is allowed to use this command. /// The number of times a user may use the command within a certain period. /// The amount of time since first invoke a user has until the limit is lifted. /// Set whether or not there is no limit to the command in DMs. Defaults to false. /// Set whether or not there is no limit to the command for guild admins. Defaults to false. /// Set whether or not to apply a limit per guild. Defaults to false. public RatelimitAttribute(uint times, TimeSpan period, bool noLimitInDMs = false, bool noLimitForAdmins = false, bool applyPerGuild = false) { _invokeLimit = times; _noLimitInDMs = noLimitInDMs; _noLimitForAdmins = noLimitForAdmins; _invokeLimitPeriod = period; _applyPerGuild = applyPerGuild; } /// /// Changed from: `CheckPermissions` to `CheckPermissionsAsync` public override Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) { if (_noLimitInDMs && context.Channel is IPrivateChannel) return Task.FromResult(PreconditionResult.FromSuccess()); if (_noLimitForAdmins && context.User is IGuildUser gu && gu.GuildPermissions.Administrator) return Task.FromResult(PreconditionResult.FromSuccess()); var now = DateTime.UtcNow; var key = _applyPerGuild ? (context.User.Id, context.Guild?.Id) : (context.User.Id, null); var timeout = (_invokeTracker.TryGetValue(key, out var t) && ((now - t.FirstInvoke) < _invokeLimitPeriod)) ? t : new CommandTimeout(now); timeout.TimesInvoked++; if (timeout.TimesInvoked <= _invokeLimit) { _invokeTracker[key] = timeout; return Task.FromResult(PreconditionResult.FromSuccess()); } else { return Task.FromResult(PreconditionResult.FromError("Ok retard calm down")); } } private class CommandTimeout { public uint TimesInvoked { get; set; } public DateTime FirstInvoke { get; } public CommandTimeout(DateTime timeStarted) { FirstInvoke = timeStarted; } } } /// Sets the scale of the period parameter. public enum Measure { /// Period is measured in days. Days, /// Period is measured in hours. Hours, /// Period is measured in minutes. Minutes, Seconds } }