Search code examples
c#discorddsharp+

DSharpPlus - WaitForReaction on existing Messages


I am trying to create a discord bot using DSharpPlus library where if you react on a message with specific emoji, you will get a specific role. The concept is pretty straight forward but I fail to figure out one rather important concept. That is, how do I get the bot to listen for a reaction on an existing message all the time.

I tried to do it via commands and I got it to work, however the problem with this approach as I learned is that the bot only listens for reactions after I type a command and it only lasts a minute or so (based on configuration).

public class RoleCommands : BaseCommandModule
{
    [Command("join")]
    public async Task Join(CommandContext ctx)
    {
        var joinEmbed = new DiscordEmbedBuilder
        {
            Title = "Reaction with thumbs up!",
            Color = DiscordColor.Green
        };

        var joinMessage = await ctx.Channel.SendMessageAsync(embed: joinEmbed).ConfigureAwait(false);

        var thumbsUpEmoji = DiscordEmoji.FromName(ctx.Client, ":+1:");
        var thumbsDownEmoji = DiscordEmoji.FromName(ctx.Client, ":-1:");

        await joinMessage.CreateReactionAsync(thumbsUpEmoji).ConfigureAwait(false);
        await joinMessage.CreateReactionAsync(thumbsDownEmoji).ConfigureAwait(false);

        var interactivity = ctx.Client.GetInteractivity();

        var reactionResult = await interactivity.WaitForReactionAsync(x =>
            x.Message == joinMessage
            && x.User == ctx.User
            && x.Emoji == thumbsUpEmoji);

        if (reactionResult.Result.Emoji == thumbsUpEmoji)
        {
            var role = ctx.Guild.GetRole(773965440913375282);
            await ctx.Member.GrantRoleAsync(role).ConfigureAwait(false);

            await joinMessage.DeleteAsync().ConfigureAwait(false);
        }
    }
}

How can I do this outside of a command where I can pass it a message Id and then it listens to that message for reactions all the time as oppose to a limited time?


Solution

  • The full answer to my question is to use DiscordClient.MessageReactionAdded += OnReactionAdded; and to implement the method as such:

    private async Task OnReactionAdded(DiscordClient sender, MessageReactionRemoveEventArgs e)
    {
        var messageId = e.Message.Id;
        var guild = e.Message.Channel.Guild;
        var reactionName = e.Emoji.GetDiscordName();
    
        var reactionDetail = ReactionDetails.FirstOrDefault(x =>
            x.MessageId == messageId
            && x.GuildId == guild.Id
            && x.ReactionName == reactionName);
    
        if (reactionDetail != null)
        {
            var member = e.User as DiscordMember;
            if (member != null)
            {
                var role = guild.Roles.FirstOrDefault(x => x.Value.Id == reactionDetail.RoleId).Value;
                await member.GrantRoleAsync(role).ConfigureAwait(false);
            }
        }
    }