Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -258,4 +258,5 @@ paket-files/

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
*.pyc
/SampleApp/*.ignore
2 changes: 1 addition & 1 deletion Discord.Addons.Interactive/Criteria/Criteria.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Discord.Addons.Interactive
{
public class Criteria<T> : ICriterion<T>
{
private List<ICriterion<T>> _critiera = new List<ICriterion<T>>();
private readonly List<ICriterion<T>> _critiera = new List<ICriterion<T>>();

public Criteria<T> AddCriterion(ICriterion<T> criterion)
{
Expand Down
6 changes: 1 addition & 5 deletions Discord.Addons.Interactive/Criteria/EmptyCriterion.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Discord.Commands;
using Discord.WebSocket;

namespace Discord.Addons.Interactive
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Threading.Tasks;
using Discord.Commands;
using JetBrains.Annotations;

namespace Discord.Addons.Interactive
{
public class EnsureFromChannelCriterion : ICriterion<IMessage>
[UsedImplicitly]
public class EnsureFromChannelCriterion
: ICriterion<IMessage>
{
private readonly ulong _channelId;

Expand All @@ -12,7 +15,7 @@ public EnsureFromChannelCriterion(IMessageChannel channel)

public Task<bool> JudgeAsync(SocketCommandContext sourceContext, IMessage parameter)
{
bool ok = _channelId == parameter.Channel.Id;
var ok = _channelId == parameter.Channel.Id;
return Task.FromResult(ok);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public EnsureFromUserCriterion(ulong id)

public Task<bool> JudgeAsync(SocketCommandContext sourceContext, IMessage parameter)
{
bool ok = _id == parameter.Author.Id;
var ok = _id == parameter.Author.Id;
return Task.FromResult(ok);
}
}
Expand Down
6 changes: 1 addition & 5 deletions Discord.Addons.Interactive/Criteria/ICriterion.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
using Discord.WebSocket;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Discord.Commands;

namespace Discord.Addons.Interactive
Expand Down
10 changes: 4 additions & 6 deletions Discord.Addons.Interactive/Discord.Addons.Interactive.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@
<RepositoryUrl>git://github.com/foxbot/Discord.Addons.Interactive</RepositoryUrl>
<PackageIconUrl>https://github.com/foxbot/Discord.Addons.Interactive/raw/master/marketing/PackageLogo.png</PackageIconUrl>

<VersionPrefix Condition=" '$(Version)' != '' ">$(Version)</VersionPrefix>
<VersionPrefix Condition=" '$(Version)' == '' ">0.0.0-dev</VersionPrefix>

<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Discord.Net.Commands" Version="2.2.0" />
<PackageReference Include="Discord.Net.WebSocket" Version="2.2.0" />
<PackageReference Include="Discord.Net.Commands" Version="3.19.0" />
<PackageReference Include="Discord.Net.WebSocket" Version="3.19.0" />
<PackageReference Include="JetBrains.Annotations" Version="2025.2.4" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=callbacks/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=criteria/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=paginator/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=results/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
12 changes: 8 additions & 4 deletions Discord.Addons.Interactive/InteractiveBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@

namespace Discord.Addons.Interactive
{
public abstract class InteractiveBase : InteractiveBase<SocketCommandContext>
public abstract class InteractiveBase
: InteractiveBase<SocketCommandContext>
{
}

public abstract class InteractiveBase<T> : ModuleBase<T>
public abstract class InteractiveBase<T>
: ModuleBase<T>
where T : SocketCommandContext
{
public InteractiveService Interactive { get; set; }

public Task<SocketMessage> NextMessageAsync(ICriterion<SocketMessage> criterion, TimeSpan? timeout = null, CancellationToken token = default(CancellationToken))
public Task<SocketMessage> NextMessageAsync(ICriterion<SocketMessage> criterion, TimeSpan? timeout = null, CancellationToken token = default)
=> Interactive.NextMessageAsync(Context, criterion, timeout, token);
public Task<SocketMessage> NextMessageAsync(bool fromSourceUser = true, bool inSourceChannel = true, TimeSpan? timeout = null, CancellationToken token = default(CancellationToken))
public Task<SocketMessage> NextMessageAsync(bool fromSourceUser = true, bool inSourceChannel = true, TimeSpan? timeout = null, CancellationToken token = default)
=> Interactive.NextMessageAsync(Context, fromSourceUser, inSourceChannel, timeout, token);

public Task<IUserMessage> ReplyAndDeleteAsync(string content, bool isTTS = false, Embed embed = null, TimeSpan? timeout = null, RequestOptions options = null)
Expand All @@ -32,13 +34,15 @@ public Task<IUserMessage> PagedReplyAsync(IEnumerable<object> pages, bool fromSo
};
return PagedReplyAsync(pager, fromSourceUser);
}

public Task<IUserMessage> PagedReplyAsync(PaginatedMessage pager, bool fromSourceUser = true)
{
var criterion = new Criteria<SocketReaction>();
if (fromSourceUser)
criterion.AddCriterion(new EnsureReactionFromSourceUserCriterion());
return PagedReplyAsync(pager, criterion);
}

public Task<IUserMessage> PagedReplyAsync(PaginatedMessage pager, ICriterion<SocketReaction> criterion)
=> Interactive.SendPaginatedMessageAsync(Context, pager, criterion);

Expand Down
58 changes: 35 additions & 23 deletions Discord.Addons.Interactive/InteractiveService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,28 @@
using System.Threading.Tasks;
using Discord.Commands;
using Discord.WebSocket;
using JetBrains.Annotations;

namespace Discord.Addons.Interactive
{
public class InteractiveService : IDisposable
public class InteractiveService
: IDisposable
{
public BaseSocketClient Discord { get; }

private Dictionary<ulong, IReactionCallback> _callbacks;
private TimeSpan _defaultTimeout;
private readonly Dictionary<ulong, IReactionCallback> _callbacks;
private readonly TimeSpan _defaultTimeout;

// helpers to allow DI containers to resolve without a custom factory
public InteractiveService(DiscordSocketClient discord, InteractiveServiceConfig config = null)
: this((BaseSocketClient)discord, config) {}

: this((BaseSocketClient)discord, config)
{
}

public InteractiveService(DiscordShardedClient discord, InteractiveServiceConfig config = null)
: this((BaseSocketClient)discord, config) {}
: this((BaseSocketClient)discord, config)
{
}

public InteractiveService(BaseSocketClient discord, InteractiveServiceConfig config = null)
{
Expand All @@ -32,24 +38,28 @@ public InteractiveService(BaseSocketClient discord, InteractiveServiceConfig con
_callbacks = new Dictionary<ulong, IReactionCallback>();
}

public Task<SocketMessage> NextMessageAsync(SocketCommandContext context,
public Task<SocketMessage> NextMessageAsync(
SocketCommandContext context,
bool fromSourceUser = true,
bool inSourceChannel = true,
TimeSpan? timeout = null,
CancellationToken token = default(CancellationToken))
CancellationToken token = default)
{
var criterion = new Criteria<SocketMessage>();
if (fromSourceUser)
criterion.AddCriterion(new EnsureSourceUserCriterion());
if (inSourceChannel)
criterion.AddCriterion(new EnsureSourceChannelCriterion());

return NextMessageAsync(context, criterion, timeout, token);
}

public async Task<SocketMessage> NextMessageAsync(SocketCommandContext context,
[ItemCanBeNull]
public async Task<SocketMessage> NextMessageAsync(
SocketCommandContext context,
ICriterion<SocketMessage> criterion,
TimeSpan? timeout = null,
CancellationToken token = default(CancellationToken))
CancellationToken token = default)
{
timeout = timeout ?? _defaultTimeout;

Expand All @@ -69,15 +79,14 @@ async Task Handler(SocketMessage message)

var trigger = eventTrigger.Task;
var cancel = cancelTrigger.Task;
var delay = Task.Delay(timeout.Value);
var delay = Task.Delay(timeout.Value, token);
var task = await Task.WhenAny(trigger, delay, cancel).ConfigureAwait(false);

context.Client.MessageReceived -= Handler;

if (task == trigger)
return await trigger.ConfigureAwait(false);
else
return null;
return null;
}

public async Task<IUserMessage> ReplyAndDeleteAsync(SocketCommandContext context,
Expand All @@ -103,17 +112,17 @@ public async Task<IUserMessage> SendPaginatedMessageAsync(SocketCommandContext c
return callback.Message;
}

public void AddReactionCallback(IMessage message, IReactionCallback callback)
=> _callbacks[message.Id] = callback;
public void RemoveReactionCallback(IMessage message)
=> RemoveReactionCallback(message.Id);
public void RemoveReactionCallback(ulong id)
=> _callbacks.Remove(id);
public void ClearReactionCallbacks()
=> _callbacks.Clear();
public void AddReactionCallback(IMessage message, IReactionCallback callback) => _callbacks[message.Id] = callback;

public void RemoveReactionCallback(IMessage message) => RemoveReactionCallback(message.Id);

public void RemoveReactionCallback(ulong id) => _callbacks.Remove(id);

public void ClearReactionCallbacks() => _callbacks.Clear();

private async Task HandleReactionAsync(Cacheable<IUserMessage, ulong> message,
ISocketMessageChannel channel,
private async Task HandleReactionAsync(
Cacheable<IUserMessage, ulong> message,
Cacheable<IMessageChannel, ulong> cacheable,
SocketReaction reaction)
{
if (reaction.UserId == Discord.CurrentUser.Id) return;
Expand All @@ -129,6 +138,9 @@ private async Task HandleReactionAsync(Cacheable<IUserMessage, ulong> message,
RemoveReactionCallback(message.Id);
});
break;

case RunMode.Sync:
case RunMode.Default:
default:
if (await callback.HandleCallbackAsync(reaction).ConfigureAwait(false))
RemoveReactionCallback(message.Id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal class EnsureIsIntegerCriterion : ICriterion<SocketMessage>
{
public Task<bool> JudgeAsync(SocketCommandContext sourceContext, SocketMessage parameter)
{
bool ok = int.TryParse(parameter.Content, out _);
var ok = int.TryParse(parameter.Content, out _);
return Task.FromResult(ok);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

namespace Discord.Addons.Interactive
{
internal class EnsureReactionFromSourceUserCriterion : ICriterion<SocketReaction>
internal class EnsureReactionFromSourceUserCriterion
: ICriterion<SocketReaction>
{
public Task<bool> JudgeAsync(SocketCommandContext sourceContext, SocketReaction parameter)
{
bool ok = parameter.UserId == sourceContext.User.Id;
return Task.FromResult(ok);
return Task.FromResult(parameter.UserId == sourceContext.User.Id);
}
}
}
15 changes: 7 additions & 8 deletions Discord.Addons.Interactive/Paginator/PaginatedMessageCallback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ public class PaginatedMessageCallback : IReactionCallback
public IUserMessage Message { get; private set; }

public RunMode RunMode => RunMode.Sync;
public ICriterion<SocketReaction> Criterion => _criterion;
public ICriterion<SocketReaction> Criterion { get; }

public TimeSpan? Timeout => options.Timeout;

private readonly ICriterion<SocketReaction> _criterion;
private readonly PaginatedMessage _pager;

private PaginatedAppearanceOptions options => _pager.Options;
Expand All @@ -32,7 +32,7 @@ public PaginatedMessageCallback(InteractiveService interactive,
{
Interactive = interactive;
Context = sourceContext;
_criterion = criterion ?? new EmptyCriterion<SocketReaction>();
Criterion = criterion ?? new EmptyCriterion<SocketReaction>();
_pager = pager;
pages = _pager.Pages.Count();
if (_pager.Pages is IEnumerable<EmbedFieldBuilder>)
Expand All @@ -53,9 +53,8 @@ public async Task DisplayAsync()
await message.AddReactionAsync(options.Next);
await message.AddReactionAsync(options.Last);

var manageMessages = (Context.Channel is IGuildChannel guildChannel)
? (Context.User as IGuildUser).GetPermissions(guildChannel).ManageMessages
: false;
var manageMessages = Context.Channel is IGuildChannel guildChannel
&& ((IGuildUser)Context.User).GetPermissions(guildChannel).ManageMessages;

if (options.JumpDisplayOptions == JumpDisplayOptions.Always
|| (options.JumpDisplayOptions == JumpDisplayOptions.WithManageMessages && manageMessages))
Expand All @@ -67,12 +66,12 @@ public async Task DisplayAsync()
await message.AddReactionAsync(options.Info);
});
// TODO: (Next major version) timeouts need to be handled at the service-level!
if (Timeout.HasValue && Timeout.Value != null)
if (Timeout.HasValue)
{
_ = Task.Delay(Timeout.Value).ContinueWith(_ =>
{
Interactive.RemoveReactionCallback(message);
_ = Message.DeleteAsync();
Message.DeleteAsync();
});
}
}
Expand Down
14 changes: 10 additions & 4 deletions SampleApp/SampleApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Discord.Net.Commands" Version="2.2.0" />
<PackageReference Include="Discord.Net.WebSocket" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.3" />
<PackageReference Include="Discord.Net.Commands" Version="3.1.0" />
<PackageReference Include="Discord.Net.WebSocket" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Discord.Addons.Interactive\Discord.Addons.Interactive.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="token.ignore">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>