From efc87ef06db3af5dbbfcec3f2ee064dc0c264db4 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 13 Jul 2016 13:07:24 -0300 Subject: [PATCH] Removed IMessage.Text, renamed RawText -> Text, added Resolve --- src/Discord.Net.Commands/CommandService.cs | 4 +- .../Extensions/MessageExtensions.cs | 6 +- src/Discord.Net/Entities/Messages/IMessage.cs | 9 +- src/Discord.Net/Entities/Messages/Message.cs | 45 +++--- .../Entities/Messages/UserResolveMode.cs | 8 ++ src/Discord.Net/Utilities/MentionUtils.cs | 129 +++++++++++------- 6 files changed, 123 insertions(+), 78 deletions(-) create mode 100644 src/Discord.Net/Entities/Messages/UserResolveMode.cs diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index fd826f060..864b69a91 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -225,7 +225,7 @@ namespace Discord.Commands return false; } - public SearchResult Search(IMessage message, int argPos) => Search(message, message.RawText.Substring(argPos)); + public SearchResult Search(IMessage message, int argPos) => Search(message, message.Text.Substring(argPos)); public SearchResult Search(IMessage message, string input) { string lowerInput = input.ToLowerInvariant(); @@ -237,7 +237,7 @@ namespace Discord.Commands return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command."); } - public Task Execute(IMessage message, int argPos) => Execute(message, message.RawText.Substring(argPos)); + public Task Execute(IMessage message, int argPos) => Execute(message, message.Text.Substring(argPos)); public async Task Execute(IMessage message, string input) { var searchResult = Search(message, input); diff --git a/src/Discord.Net.Commands/Extensions/MessageExtensions.cs b/src/Discord.Net.Commands/Extensions/MessageExtensions.cs index b2e3e173c..f2dd3adfb 100644 --- a/src/Discord.Net.Commands/Extensions/MessageExtensions.cs +++ b/src/Discord.Net.Commands/Extensions/MessageExtensions.cs @@ -4,7 +4,7 @@ { public static bool HasCharPrefix(this IMessage msg, char c, ref int argPos) { - var text = msg.RawText; + var text = msg.Text; if (text.Length > 0 && text[0] == c) { argPos = 1; @@ -14,7 +14,7 @@ } public static bool HasStringPrefix(this IMessage msg, string str, ref int argPos) { - var text = msg.RawText; + var text = msg.Text; //str = str + ' '; if (text.StartsWith(str)) { @@ -25,7 +25,7 @@ } public static bool HasMentionPrefix(this IMessage msg, IUser user, ref int argPos) { - var text = msg.RawText; + var text = msg.Text; string mention = user.Mention + ' '; if (text.StartsWith(mention)) { diff --git a/src/Discord.Net/Entities/Messages/IMessage.cs b/src/Discord.Net/Entities/Messages/IMessage.cs index e33670acb..df620a516 100644 --- a/src/Discord.Net/Entities/Messages/IMessage.cs +++ b/src/Discord.Net/Entities/Messages/IMessage.cs @@ -13,9 +13,7 @@ namespace Discord bool IsTTS { get; } /// Returns true if this message was added to its channel's pinned messages. bool IsPinned { get; } - /// Returns the original, unprocessed text for this message. - string RawText { get; } - /// Returns the text for this message after mention processing. + /// Returns the text for this message. string Text { get; } /// Gets the time this message was sent. DateTimeOffset Timestamp { get; } @@ -41,5 +39,10 @@ namespace Discord Task PinAsync(); /// Removes this message from its channel's pinned messages. Task UnpinAsync(); + + /// Transforms this message's text into a human readable form, resolving things like mentions to that object's name. + string Resolve(int startIndex, int length, UserResolveMode userMode = UserResolveMode.NameOnly); + /// Transforms this message's text into a human readable form, resolving things like mentions to that object's name. + string Resolve(UserResolveMode userMode = UserResolveMode.NameOnly); } } \ No newline at end of file diff --git a/src/Discord.Net/Entities/Messages/Message.cs b/src/Discord.Net/Entities/Messages/Message.cs index 225890578..32a4303da 100644 --- a/src/Discord.Net/Entities/Messages/Message.cs +++ b/src/Discord.Net/Entities/Messages/Message.cs @@ -16,7 +16,6 @@ namespace Discord private long? _editedTimestampTicks; public bool IsTTS { get; private set; } - public string RawText { get; private set; } public string Text { get; private set; } public bool IsPinned { get; private set; } @@ -111,29 +110,15 @@ namespace Discord if (model.Content.IsSpecified) { - RawText = model.Content.Value; + var text = model.Content.Value; if (guildChannel != null) { - var orderedMentionedUsers = ImmutableArray.CreateBuilder(5); - Text = MentionUtils.CleanUserMentions(RawText, Channel.IsAttached ? Channel : null, MentionedUsers, orderedMentionedUsers); - MentionedUsers = orderedMentionedUsers.ToImmutable(); - - var roles = ImmutableArray.CreateBuilder(5); - Text = MentionUtils.CleanRoleMentions(Text, guildChannel.Guild, roles); - MentionedRoles = roles.ToImmutable(); - - if (guildChannel.IsAttached) //It's too expensive to do a channel lookup in REST mode - { - var channelIds = ImmutableArray.CreateBuilder(5); - Text = MentionUtils.CleanChannelMentions(Text, guildChannel.Guild, channelIds); - MentionedChannelIds = channelIds.ToImmutable(); - } - else - MentionedChannelIds = MentionUtils.GetChannelMentions(RawText); + MentionedUsers = MentionUtils.GetUserMentions(text, Channel.IsAttached ? Channel : null, MentionedUsers); + MentionedChannelIds = MentionUtils.GetChannelMentions(text, guildChannel.Guild); + MentionedRoles = MentionUtils.GetRoleMentions(text, guildChannel.Guild); } - else - Text = RawText; + Text = text; } } @@ -168,16 +153,32 @@ namespace Discord else await Discord.ApiClient.DeleteDMMessageAsync(Channel.Id, Id).ConfigureAwait(false); } - /// Adds this message to its channel's pinned messages. public async Task PinAsync() { await Discord.ApiClient.AddPinAsync(Channel.Id, Id).ConfigureAwait(false); } - /// Removes this message from its channel's pinned messages. public async Task UnpinAsync() { await Discord.ApiClient.RemovePinAsync(Channel.Id, Id).ConfigureAwait(false); } + + public string Resolve(int startIndex, int length, UserResolveMode userMode = UserResolveMode.NameOnly) + => Resolve(Text.Substring(startIndex, length), userMode); + public string Resolve(UserResolveMode userMode = UserResolveMode.NameOnly) + => Resolve(Text, userMode); + + private string Resolve(string text, UserResolveMode userMode = UserResolveMode.NameOnly) + { + var guild = (Channel as IGuildChannel)?.Guild; + text = MentionUtils.ResolveUserMentions(text, Channel, MentionedUsers, userMode); + if (guild != null) + { + if (guild.IsAttached) //It's too expensive to do a channel lookup in REST mode + text = MentionUtils.ResolveChannelMentions(text, guild); + text = MentionUtils.ResolveRoleMentions(text, guild, MentionedRoles); + } + return text; + } public override string ToString() => Text; private string DebuggerDisplay => $"{Author}: {Text}{(Attachments.Count > 0 ? $" [{Attachments.Count} Attachments]" : "")}"; diff --git a/src/Discord.Net/Entities/Messages/UserResolveMode.cs b/src/Discord.Net/Entities/Messages/UserResolveMode.cs new file mode 100644 index 000000000..210c54f30 --- /dev/null +++ b/src/Discord.Net/Entities/Messages/UserResolveMode.cs @@ -0,0 +1,8 @@ +namespace Discord +{ + public enum UserResolveMode + { + NameOnly = 0, + NameAndDiscriminator + } +} diff --git a/src/Discord.Net/Utilities/MentionUtils.cs b/src/Discord.Net/Utilities/MentionUtils.cs index 6bb05d6cd..96ed6c61a 100644 --- a/src/Discord.Net/Utilities/MentionUtils.cs +++ b/src/Discord.Net/Utilities/MentionUtils.cs @@ -65,6 +65,7 @@ namespace Discord channelId = 0; return false; } + /// Parses a provided role mention string. public static ulong ParseRole(string mentionText) { @@ -87,44 +88,72 @@ namespace Discord roleId = 0; return false; } + + internal static ImmutableArray GetUserMentions(string text, IMessageChannel channel, IReadOnlyCollection fallbackUsers) + { + var matches = _userRegex.Matches(text); + var builder = ImmutableArray.CreateBuilder(matches.Count); + foreach (var match in matches.OfType()) + { + ulong id; + if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) + { + IUser user = null; + if (channel != null) + user = channel.GetUserAsync(id).GetAwaiter().GetResult() as IUser; + if (user == null) + { + foreach (var fallbackUser in fallbackUsers) + { + if (fallbackUser.Id == id) + { + user = fallbackUser; + break; + } + } + } - /// Gets the ids of all users mentioned in a provided text. - public static ImmutableArray GetUserMentions(string text) => GetMentions(text, _userRegex).ToImmutable(); - /// Gets the ids of all channels mentioned in a provided text. - public static ImmutableArray GetChannelMentions(string text) => GetMentions(text, _channelRegex).ToImmutable(); - /// Gets the ids of all roles mentioned in a provided text. - public static ImmutableArray GetRoleMentions(string text) => GetMentions(text, _roleRegex).ToImmutable(); - private static ImmutableArray.Builder GetMentions(string text, Regex regex) + if (user != null) + builder.Add(user); + } + } + return builder.ToImmutable(); + } + internal static ImmutableArray GetChannelMentions(string text, IGuild guild) { - var matches = regex.Matches(text); + var matches = _channelRegex.Matches(text); var builder = ImmutableArray.CreateBuilder(matches.Count); foreach (var match in matches.OfType()) { ulong id; if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) + { + /*var channel = guild.GetChannelAsync(id).GetAwaiter().GetResult(); + if (channel != null) + builder.Add(channel);*/ builder.Add(id); + } } - return builder; + return builder.ToImmutable(); } - - /*internal static string CleanUserMentions(string text, ImmutableArray mentions) + internal static ImmutableArray GetRoleMentions(string text, IGuild guild) { - return _userRegex.Replace(text, new MatchEvaluator(e => + var matches = _roleRegex.Matches(text); + var builder = ImmutableArray.CreateBuilder(matches.Count); + foreach (var match in matches.OfType()) { ulong id; - if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) + if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) { - for (int i = 0; i < mentions.Length; i++) - { - var mention = mentions[i]; - if (mention.Id == id) - return '@' + mention.Username; - } + var role = guild.GetRole(id); + if (role != null) + builder.Add(role); } - return e.Value; - })); - }*/ - internal static string CleanUserMentions(string text, IMessageChannel channel, IReadOnlyCollection fallbackUsers, ImmutableArray.Builder mentions = null) + } + return builder.ToImmutable(); + } + + internal static string ResolveUserMentions(string text, IMessageChannel channel, IReadOnlyCollection mentions, UserResolveMode mode) { return _userRegex.Replace(text, new MatchEvaluator(e => { @@ -132,67 +161,71 @@ namespace Discord if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) { IUser user = null; - if (channel != null) - user = channel.GetUserAsync(id).GetAwaiter().GetResult() as IUser; - if (user == null) + foreach (var mention in mentions) { - foreach (var fallbackUser in fallbackUsers) + if (mention.Id == id) { - if (fallbackUser.Id == id) - { - user = fallbackUser; - break; - } + user = mention; + break; } } if (user != null) { - mentions.Add(user); + string name = user.Username; + var guildUser = user as IGuildUser; if (e.Value[2] == '!') { - var guildUser = user as IGuildUser; if (guildUser != null && guildUser.Nickname != null) - return '@' + guildUser.Nickname; + name = guildUser.Nickname; + } + + switch (mode) + { + case UserResolveMode.NameOnly: + default: + return $"@{name}"; + case UserResolveMode.NameAndDiscriminator: + return $"@{name}#{user.Discriminator}"; } - return '@' + user.Username; } } return e.Value; })); } - internal static string CleanChannelMentions(string text, IGuild guild, ImmutableArray.Builder mentions = null) + internal static string ResolveChannelMentions(string text, IGuild guild) { return _channelRegex.Replace(text, new MatchEvaluator(e => { ulong id; if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) { - var channel = guild.GetChannelAsync(id).GetAwaiter().GetResult() as IGuildChannel; + IGuildChannel channel = null; + channel = guild.GetChannelAsync(id).GetAwaiter().GetResult(); if (channel != null) - { - if (mentions != null) - mentions.Add(channel.Id); return '#' + channel.Name; - } } return e.Value; })); } - internal static string CleanRoleMentions(string text, IGuild guild, ImmutableArray.Builder mentions = null) + internal static string ResolveRoleMentions(string text, IGuild guild, IReadOnlyCollection mentions) { return _roleRegex.Replace(text, new MatchEvaluator(e => { ulong id; if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) { - var role = guild.GetRole(id); - if (role != null) + IRole role = null; + foreach (var mention in mentions) { - if (mentions != null) - mentions.Add(role); - return '@' + role.Name; + if (mention.Id == id) + { + role = mention; + break; + } } + if (role != null) + return '@' + role.Name; } return e.Value; }));