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