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}.")); }
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);
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); }
/// <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); } }
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)); } }
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); }
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); }
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); }
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);
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); }
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()); } }
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); } }
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; } } }
public static ReactionRoleDTO FromDiscord(ReactionRole reactionRole) { var emoji = EmojiMapper.FromDiscord(reactionRole.Emoji); return(new ReactionRoleDTO(reactionRole.Message.Id, emoji, reactionRole.Role.Id)); }
public async Task AddAsync(ReactionRole reactionRole) { await _context.ReactionRoles.AddAsync(ReactionRoleDTO.FromDiscord(reactionRole)); }
public void Remove(ReactionRole reactionRole) { _context.ReactionRoles.Remove(ReactionRoleDTO.FromDiscord(reactionRole)); }
public static int Ordinal(this ReactionRole role) { return((int)role); }
public static bool IsUnset(this ReactionRole role) { return(role == ReactionRole.None); }
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); }
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); } }
public Task Add(ReactionRole value) { Context.ReactionRoles.Add(value); return(Context.SaveChangesAsync()); }
public Task Remove(ReactionRole value) { Context.ReactionRoles.Remove(value); return(Context.SaveChangesAsync()); }
/// <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); }