Skip to content

Commit cfcd84e

Browse files
committed
Inline reaction
1 parent 78b220a commit cfcd84e

7 files changed

Lines changed: 166 additions & 7 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using Discord.Commands;
2+
using Discord.WebSocket;
3+
using System;
4+
using System.Linq;
5+
using System.Threading.Tasks;
6+
7+
namespace Discord.Addons.Interactive
8+
{
9+
public class InlineReactionCallback : IReactionCallback
10+
{
11+
public RunMode RunMode => RunMode.Sync;
12+
13+
public ICriterion<SocketReaction> Criterion { get; }
14+
15+
public TimeSpan? Timeout { get; }
16+
17+
public SocketCommandContext Context { get; }
18+
19+
public IUserMessage Message { get; private set; }
20+
21+
readonly InteractiveService interactive;
22+
readonly ReactionCallbackData data;
23+
24+
public InlineReactionCallback(
25+
InteractiveService interactive,
26+
SocketCommandContext context,
27+
ReactionCallbackData data,
28+
ICriterion<SocketReaction> criterion = null)
29+
{
30+
this.interactive = interactive;
31+
Context = context;
32+
this.data = data;
33+
Criterion = criterion ?? new EmptyCriterion<SocketReaction>();
34+
Timeout = data.Timeout ?? TimeSpan.FromSeconds(30);
35+
}
36+
37+
public async Task DisplayAsync()
38+
{
39+
var message = await Context.Channel.SendMessageAsync(data.Text, embed: data.Embed).ConfigureAwait(false);
40+
Message = message;
41+
interactive.AddReactionCallback(message, this);
42+
43+
_ = Task.Run(async () =>
44+
{
45+
foreach (var item in data.Callbacks)
46+
await message.AddReactionAsync(item.Reaction);
47+
});
48+
49+
if (Timeout.HasValue)
50+
{
51+
_ = Task.Delay(Timeout.Value)
52+
.ContinueWith(_ => interactive.RemoveReactionCallback(message));
53+
}
54+
}
55+
56+
public async Task<bool> HandleCallbackAsync(SocketReaction reaction)
57+
{
58+
var reactionCallbackItem = data.Callbacks.FirstOrDefault(t => t.Reaction.Equals(reaction.Emote));
59+
if (reactionCallbackItem == null)
60+
return false;
61+
62+
await reactionCallbackItem.Callback(Context);
63+
return true;
64+
}
65+
}
66+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using Discord.Commands;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Threading.Tasks;
5+
6+
namespace Discord.Addons.Interactive
7+
{
8+
public class ReactionCallbackData
9+
{
10+
private readonly ICollection<ReactionCallbackItem> items;
11+
12+
public string Text { get; }
13+
public Embed Embed { get; }
14+
public TimeSpan? Timeout { get; }
15+
public IEnumerable<ReactionCallbackItem> Callbacks => items;
16+
17+
public ReactionCallbackData(string text, Embed embed = null, TimeSpan? timeout = null)
18+
{
19+
Text = text;
20+
Embed = embed;
21+
Timeout = timeout;
22+
items = new List<ReactionCallbackItem>();
23+
}
24+
25+
public ReactionCallbackData WithCallback(IEmote reaction, Func<SocketCommandContext, Task> callback)
26+
{
27+
var item = new ReactionCallbackItem(reaction, callback);
28+
items.Add(item);
29+
return this;
30+
}
31+
}
32+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Discord.Commands;
2+
using System;
3+
using System.Threading.Tasks;
4+
5+
namespace Discord.Addons.Interactive
6+
{
7+
public class ReactionCallbackItem
8+
{
9+
public IEmote Reaction { get; }
10+
public Func<SocketCommandContext, Task> Callback { get; }
11+
12+
public ReactionCallbackItem(IEmote reaction, Func<SocketCommandContext, Task> callback)
13+
{
14+
Reaction = reaction;
15+
Callback = callback;
16+
}
17+
}
18+
}

Discord.Addons.Interactive/InteractiveBase.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ public Task<IUserMessage> PagedReplyAsync(PaginatedMessage pager, bool fromSourc
4141
public Task<IUserMessage> PagedReplyAsync(PaginatedMessage pager, ICriterion<SocketReaction> criterion)
4242
=> Interactive.SendPaginatedMessageAsync(Context, pager, criterion);
4343

44+
public Task<IUserMessage> InlineReactionReplyAsync(ReactionCallbackData data, bool fromSourceUser = true)
45+
=> Interactive.SendMessageWithReactionCallbacksAsync(Context, data, fromSourceUser);
46+
4447
public RuntimeResult Ok(string reason = null) => new OkResult(reason);
4548
}
4649
}

Discord.Addons.Interactive/InteractiveService.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using Discord.Commands;
44
using Discord.WebSocket;
55
using System.Collections.Generic;
6-
using System.Linq;
76

87
namespace Discord.Addons.Interactive
98
{
@@ -46,7 +45,7 @@ async Task Handler(SocketMessage message)
4645
}
4746

4847
context.Client.MessageReceived += Handler;
49-
48+
5049
var trigger = eventTrigger.Task;
5150
var delay = Task.Delay(timeout.Value);
5251
var task = await Task.WhenAny(trigger, delay).ConfigureAwait(false);
@@ -76,6 +75,16 @@ public async Task<IUserMessage> SendPaginatedMessageAsync(SocketCommandContext c
7675
return callback.Message;
7776
}
7877

78+
public async Task<IUserMessage> SendMessageWithReactionCallbacksAsync(SocketCommandContext context, ReactionCallbackData callbacks, bool fromSourceUser = true)
79+
{
80+
var criterion = new Criteria<SocketReaction>();
81+
if(fromSourceUser)
82+
criterion.AddCriterion(new EnsureReactionFromSourceUserCriterion());
83+
var callback = new InlineReactionCallback(this, context, callbacks, criterion);
84+
await callback.DisplayAsync().ConfigureAwait(false);
85+
return callback.Message;
86+
}
87+
7988
public void AddReactionCallback(IMessage message, IReactionCallback callback)
8089
=> _callbacks[message.Id] = callback;
8190
public void RemoveReactionCallback(IMessage message)
@@ -84,7 +93,7 @@ public void RemoveReactionCallback(ulong id)
8493
=> _callbacks.Remove(id);
8594
public void ClearReactionCallbacks()
8695
=> _callbacks.Clear();
87-
96+
8897
private async Task HandleReactionAsync(Cacheable<IUserMessage, ulong> message, ISocketMessageChannel channel, SocketReaction reaction)
8998
{
9099
if (reaction.UserId == Discord.CurrentUser.Id) return;

SampleApp/Modules/SampleModule.cs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
using System;
2-
using System.Threading.Tasks;
1+
using Discord;
32
using Discord.Addons.Interactive;
43
using Discord.Commands;
4+
using System;
5+
using System.Threading.Tasks;
56

67
namespace SampleApp.Modules
78
{
@@ -40,5 +41,35 @@ public async Task Test_Paginator()
4041
var pages = new[] { "Page 1", "Page 2", "Page 3", "aaaaaa", "Page 5" };
4142
await PagedReplyAsync(pages);
4243
}
44+
45+
// InlineReactionReplyAsync will send a message and adds reactions on it.
46+
// Once an user adds a reaction, the callback is fired.
47+
// If callback was successfull next callback is not handled (message is unsubscribed).
48+
// Unsuccessful callback is a reaction that did not have a callback.
49+
[Command("reaction")]
50+
public async Task Test_ReactionReply()
51+
{
52+
await InlineReactionReplyAsync(new ReactionCallbackData("text")
53+
.WithCallback(new Emoji("👍"), c => c.Channel.SendMessageAsync("You've replied with 👍"))
54+
.WithCallback(new Emoji("👎"), c => c.Channel.SendMessageAsync("You've replied with 👎"))
55+
);
56+
}
57+
[Command("embedreaction")]
58+
public async Task Test_EmedReactionReply()
59+
{
60+
var one = new Emoji("1⃣");
61+
var two = new Emoji("2⃣");
62+
63+
var embed = new EmbedBuilder()
64+
.WithTitle("Choose one")
65+
.AddInlineField(one.Name, "Beer")
66+
.AddInlineField(two.Name, "Drink")
67+
.Build();
68+
69+
await InlineReactionReplyAsync(new ReactionCallbackData("text", embed)
70+
.WithCallback(one, c => c.Channel.SendMessageAsync("Here you go :beer:"))
71+
.WithCallback(two, c => c.Channel.SendMessageAsync("Here you go :tropical_drink:"))
72+
);
73+
}
4374
}
4475
}

SampleApp/SampleApp.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="Discord.Net.Commands" Version="1.0.1" />
10-
<PackageReference Include="Discord.Net.WebSocket" Version="1.0.1" />
9+
<PackageReference Include="Discord.Net.Commands" Version="1.0.2" />
10+
<PackageReference Include="Discord.Net.WebSocket" Version="1.0.2" />
1111
</ItemGroup>
1212

1313
<ItemGroup>

0 commit comments

Comments
 (0)