Esempio n. 1
0
        public async ValueTask <CommandResult> HandleAsync(ICommandContext context)
        {
            var message = await DiscordMessageHelper.FromLinkAsync(context.Guild, MessageLink);

            if (message is null)
            {
                return(CommandResult.InvalidParameter("Invalid message link."));
            }

            var emoji = await _unitOfWork.ReactionRoles.GetEmojiAsync(context.Client, message, Role);

            var messageLink = Markdown.Link("message", message.JumpLink.ToString());

            if (emoji == null)
            {
                return(CommandResult.InvalidOperation($"This {messageLink} does not have self-assignable role {Role.Mention}."));
            }

            await message.DeleteReactionsEmojiAsync(emoji);

            var reactionRole = new ReactionRole(message, emoji, Role);

            _unitOfWork.ReactionRoles.Remove(reactionRole);
            await _unitOfWork.CompleteAsync();

            return(CommandResult.Success($"Removed self-assignable role {Role.Mention} from {messageLink}."));
        }
Esempio n. 2
0
        public async ValueTask <CommandResult> HandleAsync(ICommandContext context)
        {
            var message = await DiscordMessageHelper.FromLinkAsync(context.Guild, MessageLink);

            if (message is null)
            {
                return(CommandResult.InvalidParameter("Invalid message link."));
            }

            var emoji = DiscordEmojiHelper.Parse(context.Client, EmojiText);

            if (emoji is null)
            {
                return(CommandResult.InvalidParameter("Invalid emoji."));
            }

            // TODO: Check if there will be collision in db and show the user nice error message.
            var reactionRole = new ReactionRole(message, emoji, Role);
            await _unitOfWork.ReactionRoles.AddAsync(reactionRole);

            await message.CreateReactionAsync(emoji);

            await _unitOfWork.CompleteAsync();

            var messageLink = Markdown.Link("message", message.JumpLink.ToString());

            return(CommandResult.Success($"Added self-assignable role {Role.Mention} with emoji {emoji} to {messageLink}."));
        }
        public async Task ShouldAddReactionRole_WhenNoReactionRolesExist()
        {
            ulong roleId    = 5;
            ulong guildId   = 69;
            ulong channelId = 215u;
            ulong messageId = 215u;
            var   emoji     = new Emoji
            {
                Id   = 4126u,
                Name = "TEST"
            };

            ReactionRole addedRole = null;
            var          mockDbSet = _dbContext.ConfigureMockDbSet(x => x.ReactionRoles);

            mockDbSet.When(x => x.AddAsync(Arg.Any <ReactionRole>())).Do(x => addedRole = x.Arg <ReactionRole>());

            await _appFixture.SendAsync(new AddReactionRoleCommand
            {
                RoleId    = roleId,
                GuildId   = guildId,
                ChannelId = channelId,
                MessageId = messageId,
                Emoji     = emoji
            });

            await _dbContext.Received(1).SaveChangesAsync(default);
Esempio n. 4
0
        private async Task <ReactionRole> GetReactionRoleIfValid(Cacheable <IUserMessage, ulong> message, ISocketMessageChannel channel, SocketReaction reaction)
        {
            // Don't modify bot roles
            if (reaction.UserId == Program.DiscordClient.CurrentUser.Id)
            {
                return(new ReactionRole());
            }

            // Don't process if message is not a reaction role message
            if (!this.messageReactionRoleLookup.ContainsKey(message.Id))
            {
                return(new ReactionRole());
            }

            // Load Reaction Roles for Message
            List <ReactionRole> reactionRoles = await ReactionRoleDatabase.LoadAll(new Dictionary <string, object> {
                { "MessageId", message.Id }
            });

            // Get first reaction role (should only be one for each message id)
            ReactionRole reactionRole = reactionRoles.GetFirst();

            // Load Reaction Role itemsfor Message
            reactionRole.Reactions = await ReactionRoleItemDatabase.LoadAll(new Dictionary <string, object>
            {
                { "ReactionRoleId", reactionRole.Id },
            });

            return(reactionRole);
        }
Esempio n. 5
0
 /// <summary>
 /// Assigns a reaction role and group id to all atoms in a molecule.
 /// </summary>
 /// <param name="mol">molecule</param>
 /// <param name="role">role to assign</param>
 /// <param name="groupId">group id</param>
 private static void AssignRoleAndGroup(IAtomContainer mol, ReactionRole role, int groupId)
 {
     foreach (IAtom atom in mol.Atoms)
     {
         atom.SetProperty(CDKPropertyName.ReactionRole, role);
         atom.SetProperty(CDKPropertyName.ReactionGroup, groupId);
     }
 }
Esempio n. 6
0
        public async Task UpdateReactionRole(ulong guildId, ReactionRole reactionRole)
        {
            reactionRole.GuildId = guildId;
            await this.reactionRoleDb.Save(reactionRole);

            // Save header
            if (reactionRole.ChannelId.HasValue)
            {
                await this.reactionRoleHeaderDb.Save(new ReactionRoleHeader(reactionRole));
            }
        }
Esempio n. 7
0
        private async Task UpdateReactionRoleAndHeader(string roleId)
        {
            // Update Role Updated value
            ReactionRole role = await this.reactionRoleDb.Load(roleId);

            await this.reactionRoleDb.Save(role);

            // Update Role Header Updated value
            ReactionRoleHeader roleHeader = await this.reactionRoleHeaderDb.Load(roleId);

            await this.reactionRoleHeaderDb.Save(roleHeader);
        }
Esempio n. 8
0
        public async void RemoveMonitoredReactionAsync(ReactionRole reactionRole)
        {
            if (!MonitoredReactions.TryGetValue(reactionRole.Id, out var reactionSet))
            {
                return;
            }

            reactionSet.Remove(reactionRole.EmoteName, out _);

            var message = await DiscordClient.GetGuild(reactionRole.OwningServer.ServerId)
                          .GetTextChannel(reactionRole.ChannelId).GetMessageAsync(reactionRole.Id);

            await message.RemoveReactionAsync(new Emoji(reactionRole.EmoteName), DiscordClient.CurrentUser);

            Logger.Information("Removed role reaction monitoring for emote {0} on message {1} for role {2}",
                               reactionRole.EmoteName, reactionRole.MessageId, reactionRole.RoleId);
        }
Esempio n. 9
0
        public async Task AddMonitoredReactionAsync(ReactionRole reactionRole)
        {
            if (!MonitoredReactions.TryGetValue(reactionRole.Id, out var reactionSet))
            {
                reactionSet = new Dictionary <string, ReactionRole>();
                MonitoredReactions.Add(reactionRole.Id, reactionSet);

                var message = await DiscordClient.GetGuild(reactionRole.OwningServer.ServerId)
                              .GetTextChannel(reactionRole.ChannelId).GetMessageAsync(reactionRole.Id);

                await message.AddReactionAsync(new Emoji(reactionRole.EmoteName));
            }

            reactionSet[reactionRole.EmoteName] = reactionRole;
            Logger.Information("Added role reaction monitoring for emote {0} on message {1} for role {2}",
                               reactionRole.EmoteName, reactionRole.MessageId, reactionRole.RoleId);
        }
Esempio n. 10
0
        public async Task ShouldRemoveReactionRole_WhenReactionRolesExists()
        {
            const int id = 5;

            ReactionRole role      = null;
            var          mockDbSet = _dbContext.ConfigureMockDbSet(x => x.ReactionRoles, new ReactionRole {
                Id = id
            });

            mockDbSet.When(x => x.Remove(Arg.Any <ReactionRole>())).Do(x => role = x.Arg <ReactionRole>());

            await _appFixture.SendAsync(new RemoveReactionRoleCommand
            {
                Id = id
            });

            await _dbContext.Received(1).SaveChangesAsync(default);
Esempio n. 11
0
        public async Task DeleteReactionRole(ulong guildId, string itemId)
        {
            ReactionRole item = await this.reactionRoleDb.Load(itemId);

            if (item == null)
            {
                return;
            }

            if (item.GuildId != guildId)
            {
                throw new Exception("Attempt to delete another guilds reaction role");
            }

            await this.reactionRoleDb.Delete(itemId);

            await this.reactionRoleHeaderDb.Delete(itemId);
        }
Esempio n. 12
0
        private async Task ReactionAdded(Cacheable <IUserMessage, ulong> message, ISocketMessageChannel channel, SocketReaction reaction)
        {
            try
            {
                // Try get reaction role
                ReactionRole reactionRole = await this.GetReactionRoleIfValid(message, channel, reaction);

                // Reaction Role not found or no Reactions - skip
                if (reactionRole == null || !reactionRole.Reactions.Any())
                {
                    return;
                }

                // If Item matching added Reaction doesn't exist for reaction role - skip
                ReactionRoleItem item = reactionRole.Reactions.FirstOrDefault(x => x.ReactionEmote.Name == reaction.Emote.Name && x.Role != null);
                if (item == null)
                {
                    return;
                }

                // Need to fetch message and guild to get user and role
                IUserMessage userMessage = await message.DownloadAsync();

                IGuild     guild = userMessage.GetGuild();
                IGuildUser user  = await guild.GetUserAsync(reaction.UserId);

                IRole role = guild.GetRole(item.Role.GetValueOrDefault());
                if (role != null)
                {
                    if (!user.RoleIds.Contains(role.Id))
                    {
                        await user.AddRoleAsync(role);
                    }
                }
            }
            catch (Exception ex)
            {
                await Utils.Logger.LogExceptionToDiscordChannel(
                    ex,
                    $"Role Reaction Added - MessageId: {message.Id}",
                    (channel as IGuildChannel)?.GuildId.ToString(),
                    (reaction.User.GetValueOrDefault() as IGuildUser)?.GetName());
            }
        }
Esempio n. 13
0
        private async Task ReactionRemoved(Cacheable <IUserMessage, ulong> message, ISocketMessageChannel channel, SocketReaction reaction)
        {
            try
            {
                // Try get reaction role
                ReactionRole reactionRole = await this.GetReactionRoleIfValid(message, channel, reaction);

                // Reaction Role not found or no Reactions - skip
                if (reactionRole == null || !reactionRole.Reactions.Any())
                {
                    return;
                }

                // If Item matching added Reaction doesn't exist for reaction role - skip
                ReactionRoleItem item = reactionRole.Reactions.FirstOrDefault(x => x.ReactionEmote.Name == reaction.Emote.Name && x.Role != null);
                if (item == null)
                {
                    return;
                }

                IUserMessage userMessage = await message.DownloadAsync();

                IGuild     guild = userMessage.GetGuild();
                IGuildUser user  = await guild.GetUserAsync(reaction.UserId);

                IRole role = guild.GetRole(item.Role.GetValueOrDefault());
                if (role != null)
                {
                    if (user.RoleIds.Contains(role.Id))
                    {
                        await user.RemoveRoleAsync(role);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Write(ex);
            }
        }
Esempio n. 14
0
        public async Task AddRoleReaction([Remainder] string args = null)
        {
            if (IsAdmin())
            {
                var text = ParseArgs(args, "text");

                var emote   = Emote.Parse("<:accepted:579773449590013964>");
                var role    = Context.Message.MentionedRoles.FirstOrDefault();
                var channel = Context.Message.MentionedChannels.FirstOrDefault();
                if (role == null || emote == null || channel == null)
                {
                    await ReplyAsync($"{(role == null ? "Role" : "")}{(emote == null ? "Emote" : "")}{(role == null ? "Channel" : "")} not found");
                }
                else
                {
                    var message = await(channel as IMessageChannel).SendMessageAsync(text);

                    var reactionRole = new ReactionRole
                    {
                        GuildId   = (long)Context.Guild.Id,
                        MessageId = (long)message.Id,
                        RoleId    = (long)role.Id,
                        EmojiId   = (long)emote.Id
                    };

                    DatabaseService.DatabaseContext.ReactionRoles.Add(reactionRole);
                    await DatabaseService.DatabaseContext.SaveChangesAsync();

                    await message.AddReactionAsync(emote);

                    MessageUpdateService.ReactionAdded   += reactionRole.RoleAdded;
                    MessageUpdateService.ReactionRemoved += reactionRole.RoleRemoved;
                    MessageUpdateService.MessageDeleted  += reactionRole.MessageDeleted;
                }
            }
        }
Esempio n. 15
0
        public static ReactionRoleDTO FromDiscord(ReactionRole reactionRole)
        {
            var emoji = EmojiMapper.FromDiscord(reactionRole.Emoji);

            return(new ReactionRoleDTO(reactionRole.Message.Id, emoji, reactionRole.Role.Id));
        }
Esempio n. 16
0
 public async Task AddAsync(ReactionRole reactionRole)
 {
     await _context.ReactionRoles.AddAsync(ReactionRoleDTO.FromDiscord(reactionRole));
 }
Esempio n. 17
0
 public void Remove(ReactionRole reactionRole)
 {
     _context.ReactionRoles.Remove(ReactionRoleDTO.FromDiscord(reactionRole));
 }
Esempio n. 18
0
 public static int Ordinal(this ReactionRole role)
 {
     return((int)role);
 }
Esempio n. 19
0
 public static bool IsUnset(this ReactionRole role)
 {
     return(role == ReactionRole.None);
 }
Esempio n. 20
0
        public async Task Command(ulong messageId, ITextChannel channel, string emote, IRole role, params string[] args)
        {
            IMessage message = await channel.GetMessageAsync(messageId);

            IEmote emoteRes;

            if (Emote.TryParse(emote, out Emote result))
            {
                emoteRes = result;
            }
            else
            {
                emoteRes = new Emoji(emote);
            }

            var emoteRolePair = new Dictionary <IEmote, IRole>
            {
                { emoteRes, role }
            };

            if (args.Any())
            {
                // If there aren't an even amount of args, we know the user messed up.
                if ((args.Length % 2) != 0)
                {
                    throw new KaguyaSupportException("There were an invalid amount of additional arguments provided. " +
                                                     "Note that for each additional entry, there must be an " +
                                                     "emote followed by a role.");
                }

                var emoteRolePairs = new string[args.Length / 2][];

                int count = 0;
                for (int i = 0; i < args.Length; i += 2)
                {
                    var rolePair = new string[2];
                    rolePair[0] = args[i];
                    rolePair[1] = args[i + 1];

                    emoteRolePairs[count] = rolePair;
                    count++;
                }

                foreach (string[] pair in emoteRolePairs)
                {
                    string emoteText = pair[0];
                    string roleText  = pair[1];

                    bool validEmote = false;
                    bool validRole  = false;

                    if (Emote.TryParse(emoteText, out Emote emoteResult) || pair[1].GetType() == typeof(Emoji))
                    {
                        validEmote = true;
                    }

                    IRole roleResult = Context.Guild.Roles.FirstOrDefault(x => x.Name.ToLower() == roleText.ToLower());
                    if (roleResult != null)
                    {
                        validRole = true;
                    }

                    if (validEmote == false)
                    {
                        throw new KaguyaSupportException("Failed to parse a valid emote from the provided " +
                                                         $"input: Emote: '{emoteText}'\n\n" +
                                                         $"Note that the emote must be from this server only and " +
                                                         $"cannot be a standard emoji.");
                    }

                    if (validRole == false)
                    {
                        throw new KaguyaSupportException("Failed to parse a valid role from the provided " +
                                                         $"input: Role: '{roleText}'");
                    }

                    emoteRolePair.Add(emoteResult, Context.Guild.GetRole(roleResult.Id));
                }
            }

            if (message == null)
            {
                throw new KaguyaSupportException("The message with this ID could not be found in the specified channel. " +
                                                 "You must specify the 'channel' argument for this command if you are " +
                                                 "executing the command from another channel. \n\n" +
                                                 $"Example: '{{prefix}}crr {messageId} {{#some-channel}} ...'");
            }

            var cacheToSend = new List <ReactionRole>(emoteRolePair.Count);

            int cacheListIndex = 0;
            var respSb         = new StringBuilder();

            foreach (KeyValuePair <IEmote, IRole> pair in emoteRolePair)
            {
                bool         isEmoji = false;
                IEmote       pEmote  = pair.Key;
                IRole        pRole   = pair.Value;
                ReactionRole rr;

                if (pEmote is Emote customEmote)
                {
                    rr = new ReactionRole
                    {
                        EmoteNameorId = customEmote.Id.ToString(),
                        MessageId     = message.Id,
                        RoleId        = pRole.Id,
                        ServerId      = Context.Guild.Id
                    };
                }
                else if (pEmote is Emoji standardEmoji)
                {
                    rr = new ReactionRole
                    {
                        EmoteNameorId = standardEmoji.Name,
                        MessageId     = message.Id,
                        RoleId        = pRole.Id,
                        ServerId      = Context.Guild.Id
                    };

                    isEmoji = true;
                }
                else
                {
                    throw new KaguyaSupportException("The reaction role isn't an Emoji or Emote!!");
                }

                if (pRole.IsManaged)
                {
                    throw new KaguyaSupportException($"Role '{pRole.Name}' is managed by an integration or a bot. It may not be " +
                                                     "assigned to users. Therefore, they may not be assigned to " +
                                                     "reaction roles either.");
                }

                ReactionRole possibleMatch;

                if (isEmoji)
                {
                    possibleMatch = await DatabaseQueries.GetFirstMatchAsync <ReactionRole>(x =>
                                                                                            x.EmoteNameorId == pEmote.Name &&
                                                                                            x.RoleId == pRole.Id &&
                                                                                            x.MessageId == rr.MessageId);
                }
                else
                {
                    possibleMatch = await DatabaseQueries.GetFirstMatchAsync <ReactionRole>(x =>
                                                                                            x.EmoteNameorId == (pEmote as Emote).Id.ToString() &&
                                                                                            x.RoleId == pRole.Id &&
                                                                                            x.MessageId == rr.MessageId);
                }

                IReadOnlyDictionary <IEmote, ReactionMetadata> messageReactions = message.Reactions;

                // If the reaction is in the database, and Kaguya has a emote-role pair for this emote, throw an error.
                if (possibleMatch != null &&
                    messageReactions.Keys.Contains(pEmote) &&
                    messageReactions.GetValueOrDefault(pEmote).IsMe)
                {
                    throw new KaguyaSupportException($"The emote '{emote}' has already been assigned to role {role} " +
                                                     "as a reaction role.");
                }

                try
                {
                    await message.AddReactionAsync(pEmote);

                    await DatabaseQueries.InsertAsync(rr);

                    cacheToSend.Insert(cacheListIndex, rr);
                    respSb.AppendLine($"Successfully linked {pEmote} to {pRole.Mention}");
                }
                catch (Discord.Net.HttpException e)
                {
                    if (e.HttpCode == HttpStatusCode.BadRequest)
                    {
                        throw new KaguyaSupportException($"An error occurred when attempting to make the reaction role " +
                                                         $"for the '{pEmote.Name}' emote. This error occurs when Discord " +
                                                         $"doesn't know how to process an emote. This can happen if you " +
                                                         $"copy/paste the emote into the Discord text box instead of " +
                                                         $"manually typing out the emote yourself. Discord is really " +
                                                         $"finnicky when it comes to emotes.");
                    }

                    throw new KaguyaSupportException($"An unknown error occurred.\n\n" +
                                                     $"Exception Message: {e.Message}\nInner Exception: {e.InnerException}");
                }
                catch (Exception)
                {
                    throw new KaguyaSupportException("An error occurred when inserting the reaction role " +
                                                     "into the database.\n\n" +
                                                     $"Emote: {pEmote}\n" +
                                                     $"Role: {pRole}");
                }
            }

            var embed = new KaguyaEmbedBuilder(EmbedColor.YELLOW)
            {
                Title       = "Kaguya Reaction Roles",
                Description = respSb.ToString()
            };

            UpdatedCache?.Invoke(cacheToSend);
            await SendEmbedAsync(embed);
        }
Esempio n. 21
0
        public async Task CreateReactonRoleMessageAsync(CommandContext ctx, DiscordChannel channel = null)
        {
            if (channel == null)
            {
                channel = ctx.Channel;
            }
            var inter = ctx.Client.GetInteractivity();
            var msg   = await ctx.RespondAsync("Please enter the ID of the Role, **without a prefix**");

            var result = await inter.WaitForMessageAsync(x => x.Channel == channel && x.Author.Id == ctx.User.Id);

            var rr = new ReactionRole();

            while (true)
            {
                if (result.TimedOut)
                {
                    return;
                }
                else if (result.Result?.Content.ToLower() == "next")
                {
                    await result.Result.DeleteAsync();

                    break;
                }

                if (ulong.TryParse(result.Result.Content, out var roleId))
                {
                    var rrStuff = new ReactionRole.ReactionRoleCombination();
                    rrStuff.RoleId = roleId;
                    var role = ctx.Guild.GetRole(roleId);
                    await result.Result.DeleteAsync();

                    await msg.ModifyAsync("Please enter the emoji you'd like to use for this role now");

                    result = await inter.WaitForMessageAsync(x => x.Channel == channel && x.Author.Id == ctx.User.Id);

                    rrStuff.EmojiName = result.Result.Content.Replace(" ", "");
                    rr.ReactionIds.Add(rrStuff);

                    rrStuff = new ReactionRole.ReactionRoleCombination();
                    await msg.ModifyAsync("Please enter the next role ID or enter 'next' to set text");

                    await result.Result.DeleteAsync();

                    result = await inter.WaitForMessageAsync(x => x.Channel == channel && x.Author.Id == ctx.User.Id);
                }
            }
            await msg.ModifyAsync("Please enter the message that should be send now");

            result = await inter.WaitForMessageAsync(x => x.Channel == channel && x.Author.Id == ctx.User.Id);

            if (result.TimedOut)
            {
                return;
            }

            var resMsg = await channel.SendMessageAsync(result.Result.Content);

            Regex EmoteRegex = new Regex(@"^<(?<animated>a)?:(?<name>[a-zA-Z0-9_]+?):(?<id>\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);

            foreach (var item in rr.ReactionIds)
            {
                DiscordEmoji em = default;
                try
                {
                    em = DiscordEmoji.FromUnicode(ctx.Client, item.EmojiName);
                }
                catch { }

                var m = EmoteRegex.Match(item.EmojiName);
                if (m.Success)
                {
                    var sid  = m.Groups["id"].Value;
                    var name = m.Groups["name"].Value;
                    var anim = m.Groups["animated"].Success;

                    if (!ulong.TryParse(sid, NumberStyles.Integer, CultureInfo.InvariantCulture, out var id))
                    {
                        return;
                    }

                    try
                    {
                        em = DiscordEmoji.FromGuildEmote(ctx.Client, id);
                    }
                    catch { return; }
                }
                if (em == default)
                {
                    return;
                }
                await resMsg.CreateReactionAsync(em);

                rr.MessageId = resMsg.Id;
                var txt = JsonSerializer.Serialize(rr);
                await File.WriteAllTextAsync($@"ReactionRoleMessages\\{resMsg.Id}.json", txt);
            }
        }
Esempio n. 22
0
 public Task Add(ReactionRole value)
 {
     Context.ReactionRoles.Add(value);
     return(Context.SaveChangesAsync());
 }
Esempio n. 23
0
 public Task Remove(ReactionRole value)
 {
     Context.ReactionRoles.Remove(value);
     return(Context.SaveChangesAsync());
 }
Esempio n. 24
0
        /// <summary>
        /// Safely access the reaction role of an atom, returns <see cref="ReactionRole.None"/> if null.
        /// </summary>
        /// <param name="atom">atom</param>
        /// <returns>mapidx, None if undefined</returns>
        private static ReactionRole Role(IAtom atom)
        {
            ReactionRole role = atom.GetProperty <ReactionRole>(CDKPropertyName.ReactionRole, ReactionRole.None);

            return(role);
        }