| @@ -165,5 +165,17 @@ namespace Discord | |||||
| /// clock. Your system will still need a stable clock. | /// clock. Your system will still need a stable clock. | ||||
| /// </remarks> | /// </remarks> | ||||
| public bool UseSystemClock { get; set; } = true; | public bool UseSystemClock { get; set; } = true; | ||||
| /// <summary> | |||||
| /// Gets or sets whether or not the internal experation check uses the system date | |||||
| /// + snowflake date to check if an interaction can be responded to. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// If set to <see langword="false"/> then the CreatedAt property in an interaction | |||||
| /// will be set to when it was received instead of the snowflakes date. | |||||
| /// <br/> | |||||
| /// <b>This will still require a stable clock on your system.</b> | |||||
| /// </remarks> | |||||
| public bool UseInteractionSnowflakeDate { get; set; } = true; | |||||
| } | } | ||||
| } | } | ||||
| @@ -35,6 +35,7 @@ namespace Discord.Rest | |||||
| public ISelfUser CurrentUser { get; protected set; } | public ISelfUser CurrentUser { get; protected set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public TokenType TokenType => ApiClient.AuthTokenType; | public TokenType TokenType => ApiClient.AuthTokenType; | ||||
| internal bool UseInteractionSnowflakeDate { get; private set; } | |||||
| /// <summary> Creates a new REST-only Discord client. </summary> | /// <summary> Creates a new REST-only Discord client. </summary> | ||||
| internal BaseDiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client) | internal BaseDiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client) | ||||
| @@ -47,6 +48,8 @@ namespace Discord.Rest | |||||
| _restLogger = LogManager.CreateLogger("Rest"); | _restLogger = LogManager.CreateLogger("Rest"); | ||||
| _isFirstLogin = config.DisplayInitialLog; | _isFirstLogin = config.DisplayInitialLog; | ||||
| UseInteractionSnowflakeDate = config.UseInteractionSnowflakeDate; | |||||
| ApiClient.RequestQueue.RateLimitTriggered += async (id, info, endpoint) => | ApiClient.RequestQueue.RateLimitTriggered += async (id, info, endpoint) => | ||||
| { | { | ||||
| if (info == null) | if (info == null) | ||||
| @@ -33,8 +33,7 @@ namespace Discord.Rest | |||||
| public RestUser User { get; private set; } | public RestUser User { get; private set; } | ||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| public DateTimeOffset CreatedAt | |||||
| => SnowflakeUtils.FromSnowflake(Id); | |||||
| public DateTimeOffset CreatedAt { get; private set; } | |||||
| internal abstract bool _hasResponded { get; set; } | internal abstract bool _hasResponded { get; set; } | ||||
| @@ -57,7 +56,9 @@ namespace Discord.Rest | |||||
| internal RestInteraction(BaseDiscordClient discord, ulong id) | internal RestInteraction(BaseDiscordClient discord, ulong id) | ||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| CreatedAt = discord.UseInteractionSnowflakeDate | |||||
| ? SnowflakeUtils.FromSnowflake(Id) | |||||
| : DateTime.UtcNow; | |||||
| } | } | ||||
| internal static async Task<RestInteraction> CreateAsync(DiscordRestClient client, Model model) | internal static async Task<RestInteraction> CreateAsync(DiscordRestClient client, Model model) | ||||
| @@ -26,7 +26,7 @@ namespace Discord.WebSocket | |||||
| public SocketUserMessage Message { get; private set; } | public SocketUserMessage Message { get; private set; } | ||||
| private object _lock = new object(); | private object _lock = new object(); | ||||
| internal override bool _hasResponded { get; set; } = false; | |||||
| public override bool HasResponded { get; internal set; } = false; | |||||
| internal SocketMessageComponent(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | internal SocketMessageComponent(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | ||||
| : base(client, model.Id, channel) | : base(client, model.Id, channel) | ||||
| @@ -130,7 +130,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| if (_hasResponded) | |||||
| if (HasResponded) | |||||
| { | { | ||||
| throw new InvalidOperationException("Cannot respond, update, or defer twice to the same interaction"); | throw new InvalidOperationException("Cannot respond, update, or defer twice to the same interaction"); | ||||
| } | } | ||||
| @@ -140,7 +140,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| _hasResponded = true; | |||||
| HasResponded = true; | |||||
| } | } | ||||
| } | } | ||||
| @@ -225,7 +225,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| if (_hasResponded) | |||||
| if (HasResponded) | |||||
| { | { | ||||
| throw new InvalidOperationException("Cannot respond, update, or defer twice to the same interaction"); | throw new InvalidOperationException("Cannot respond, update, or defer twice to the same interaction"); | ||||
| } | } | ||||
| @@ -235,7 +235,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| _hasResponded = true; | |||||
| HasResponded = true; | |||||
| } | } | ||||
| } | } | ||||
| @@ -380,7 +380,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| if (_hasResponded) | |||||
| if (HasResponded) | |||||
| { | { | ||||
| throw new InvalidOperationException("Cannot respond or defer twice to the same interaction"); | throw new InvalidOperationException("Cannot respond or defer twice to the same interaction"); | ||||
| } | } | ||||
| @@ -390,7 +390,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| _hasResponded = true; | |||||
| HasResponded = true; | |||||
| } | } | ||||
| } | } | ||||
| @@ -408,7 +408,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| if (_hasResponded) | |||||
| if (HasResponded) | |||||
| { | { | ||||
| throw new InvalidOperationException("Cannot respond or defer twice to the same interaction"); | throw new InvalidOperationException("Cannot respond or defer twice to the same interaction"); | ||||
| } | } | ||||
| @@ -418,7 +418,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| _hasResponded = true; | |||||
| HasResponded = true; | |||||
| } | } | ||||
| } | } | ||||
| @@ -18,7 +18,7 @@ namespace Discord.WebSocket | |||||
| /// </summary> | /// </summary> | ||||
| public new SocketAutocompleteInteractionData Data { get; } | public new SocketAutocompleteInteractionData Data { get; } | ||||
| internal override bool _hasResponded { get; set; } | |||||
| public override bool HasResponded { get; internal set; } | |||||
| private object _lock = new object(); | private object _lock = new object(); | ||||
| internal SocketAutocompleteInteraction(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | internal SocketAutocompleteInteraction(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | ||||
| @@ -60,7 +60,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| if (_hasResponded) | |||||
| if (HasResponded) | |||||
| { | { | ||||
| throw new InvalidOperationException("Cannot respond twice to the same interaction"); | throw new InvalidOperationException("Cannot respond twice to the same interaction"); | ||||
| } | } | ||||
| @@ -69,7 +69,7 @@ namespace Discord.WebSocket | |||||
| await InteractionHelper.SendAutocompleteResultAsync(Discord, result, Id, Token, options).ConfigureAwait(false); | await InteractionHelper.SendAutocompleteResultAsync(Discord, result, Id, Token, options).ConfigureAwait(false); | ||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| _hasResponded = true; | |||||
| HasResponded = true; | |||||
| } | } | ||||
| } | } | ||||
| @@ -31,7 +31,7 @@ namespace Discord.WebSocket | |||||
| /// </summary> | /// </summary> | ||||
| internal new SocketCommandBaseData Data { get; } | internal new SocketCommandBaseData Data { get; } | ||||
| internal override bool _hasResponded { get; set; } | |||||
| public override bool HasResponded { get; internal set; } | |||||
| private object _lock = new object(); | private object _lock = new object(); | ||||
| @@ -124,7 +124,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| if (_hasResponded) | |||||
| if (HasResponded) | |||||
| { | { | ||||
| throw new InvalidOperationException("Cannot respond twice to the same interaction"); | throw new InvalidOperationException("Cannot respond twice to the same interaction"); | ||||
| } | } | ||||
| @@ -134,7 +134,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| _hasResponded = true; | |||||
| HasResponded = true; | |||||
| } | } | ||||
| } | } | ||||
| @@ -283,7 +283,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| if (_hasResponded) | |||||
| if (HasResponded) | |||||
| { | { | ||||
| throw new InvalidOperationException("Cannot respond or defer twice to the same interaction"); | throw new InvalidOperationException("Cannot respond or defer twice to the same interaction"); | ||||
| } | } | ||||
| @@ -293,7 +293,7 @@ namespace Discord.WebSocket | |||||
| lock (_lock) | lock (_lock) | ||||
| { | { | ||||
| _hasResponded = true; | |||||
| HasResponded = true; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -44,10 +44,16 @@ namespace Discord.WebSocket | |||||
| public int Version { get; private set; } | public int Version { get; private set; } | ||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| public DateTimeOffset CreatedAt | |||||
| => SnowflakeUtils.FromSnowflake(Id); | |||||
| public DateTimeOffset CreatedAt { get; private set; } | |||||
| internal abstract bool _hasResponded { get; set; } | |||||
| /// <summary> | |||||
| /// Gets whether or not this interaction has been responded to. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This property is locally set -- if you're running multiple bots | |||||
| /// off the same token then this property won't be in sync with them. | |||||
| /// </remarks> | |||||
| public abstract bool HasResponded { get; internal set; } | |||||
| /// <summary> | /// <summary> | ||||
| /// <see langword="true"/> if the token is valid for replying to, otherwise <see langword="false"/>. | /// <see langword="true"/> if the token is valid for replying to, otherwise <see langword="false"/>. | ||||
| @@ -59,6 +65,10 @@ namespace Discord.WebSocket | |||||
| : base(client, id) | : base(client, id) | ||||
| { | { | ||||
| Channel = channel; | Channel = channel; | ||||
| CreatedAt = client.UseInteractionSnowflakeDate | |||||
| ? SnowflakeUtils.FromSnowflake(Id) | |||||
| : DateTime.UtcNow; | |||||
| } | } | ||||
| internal static SocketInteraction Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | internal static SocketInteraction Create(DiscordSocketClient client, Model model, ISocketMessageChannel channel) | ||||