| @@ -1,4 +1,5 @@ | |||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Runtime.InteropServices; | |||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| @@ -30,9 +30,9 @@ namespace Discord | |||||
| IReadOnlyCollection<IEmbed> Embeds { get; } | IReadOnlyCollection<IEmbed> Embeds { get; } | ||||
| /// <summary> Returns a collection of channel ids mentioned in this message. </summary> | /// <summary> Returns a collection of channel ids mentioned in this message. </summary> | ||||
| IReadOnlyCollection<ulong> MentionedChannelIds { get; } | IReadOnlyCollection<ulong> MentionedChannelIds { get; } | ||||
| /// <summary> Returns a collection of role ids mentioned in this message. </summary> | |||||
| IReadOnlyCollection<ulong> MentionedRoleIds { get; } | |||||
| /// <summary> Returns a collection of user ids mentioned in this message. </summary> | |||||
| /// <summary> Returns a collection of roles mentioned in this message. </summary> | |||||
| IReadOnlyCollection<IRole> MentionedRoles { get; } | |||||
| /// <summary> Returns a collection of users mentioned in this message. </summary> | |||||
| IReadOnlyCollection<IUser> MentionedUsers { get; } | IReadOnlyCollection<IUser> MentionedUsers { get; } | ||||
| /// <summary> Modifies this message. </summary> | /// <summary> Modifies this message. </summary> | ||||
| @@ -23,11 +23,11 @@ namespace Discord | |||||
| public IMessageChannel Channel { get; } | public IMessageChannel Channel { get; } | ||||
| public IUser Author { get; } | public IUser Author { get; } | ||||
| public ImmutableArray<Attachment> Attachments { get; private set; } | |||||
| public ImmutableArray<Embed> Embeds { get; private set; } | |||||
| public ImmutableArray<ulong> MentionedChannelIds { get; private set; } | |||||
| public ImmutableArray<ulong> MentionedRoleIds { get; private set; } | |||||
| public ImmutableArray<User> MentionedUsers { get; private set; } | |||||
| public IReadOnlyCollection<Attachment> Attachments { get; private set; } | |||||
| public IReadOnlyCollection<IEmbed> Embeds { get; private set; } | |||||
| public IReadOnlyCollection<ulong> MentionedChannelIds { get; private set; } | |||||
| public IReadOnlyCollection<IRole> MentionedRoles { get; private set; } | |||||
| public IReadOnlyCollection<IUser> MentionedUsers { get; private set; } | |||||
| public override DiscordClient Discord => (Channel as Entity<ulong>).Discord; | public override DiscordClient Discord => (Channel as Entity<ulong>).Discord; | ||||
| public DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); | public DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); | ||||
| @@ -41,9 +41,9 @@ namespace Discord | |||||
| if (channel is IGuildChannel) | if (channel is IGuildChannel) | ||||
| { | { | ||||
| MentionedUsers = ImmutableArray.Create<User>(); | |||||
| MentionedUsers = ImmutableArray.Create<IUser>(); | |||||
| MentionedChannelIds = ImmutableArray.Create<ulong>(); | MentionedChannelIds = ImmutableArray.Create<ulong>(); | ||||
| MentionedRoleIds = ImmutableArray.Create<ulong>(); | |||||
| MentionedRoles = ImmutableArray.Create<IRole>(); | |||||
| } | } | ||||
| Update(model, UpdateSource.Creation); | Update(model, UpdateSource.Creation); | ||||
| @@ -106,21 +106,31 @@ namespace Discord | |||||
| MentionedUsers = ImmutableArray.Create(mentions); | MentionedUsers = ImmutableArray.Create(mentions); | ||||
| } | } | ||||
| else | else | ||||
| MentionedUsers = ImmutableArray.Create<User>(); | |||||
| MentionedUsers = ImmutableArray.Create<IUser>(); | |||||
| } | } | ||||
| if (model.Content.IsSpecified) | if (model.Content.IsSpecified) | ||||
| { | { | ||||
| RawText = model.Content.Value; | RawText = model.Content.Value; | ||||
| if (Channel is IGuildChannel) | |||||
| if (guildChannel != null) | |||||
| { | { | ||||
| Text = MentionUtils.CleanUserMentions(RawText, MentionedUsers); | |||||
| MentionedChannelIds = MentionUtils.GetChannelMentions(RawText); | |||||
| var mentionedRoleIds = MentionUtils.GetRoleMentions(RawText); | |||||
| if (_isMentioningEveryone) | |||||
| mentionedRoleIds = mentionedRoleIds.Add(guildChannel.Guild.EveryoneRole.Id); | |||||
| MentionedRoleIds = mentionedRoleIds; | |||||
| var orderedMentionedUsers = ImmutableArray.CreateBuilder<IUser>(5); | |||||
| Text = MentionUtils.CleanUserMentions(RawText, Channel.IsAttached ? Channel : null, MentionedUsers, orderedMentionedUsers); | |||||
| MentionedUsers = orderedMentionedUsers.ToImmutable(); | |||||
| var roles = ImmutableArray.CreateBuilder<IRole>(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<ulong>(5); | |||||
| Text = MentionUtils.CleanChannelMentions(Text, guildChannel.Guild, channelIds); | |||||
| MentionedChannelIds = channelIds.ToImmutable(); | |||||
| } | |||||
| else | |||||
| MentionedChannelIds = MentionUtils.GetChannelMentions(RawText); | |||||
| } | } | ||||
| else | else | ||||
| Text = RawText; | Text = RawText; | ||||
| @@ -172,12 +182,6 @@ namespace Discord | |||||
| } | } | ||||
| public override string ToString() => Text; | public override string ToString() => Text; | ||||
| private string DebuggerDisplay => $"{Author}: {Text}{(Attachments.Length > 0 ? $" [{Attachments.Length} Attachments]" : "")}"; | |||||
| IReadOnlyCollection<Attachment> IMessage.Attachments => Attachments; | |||||
| IReadOnlyCollection<IEmbed> IMessage.Embeds => Embeds; | |||||
| IReadOnlyCollection<ulong> IMessage.MentionedChannelIds => MentionedChannelIds; | |||||
| IReadOnlyCollection<ulong> IMessage.MentionedRoleIds => MentionedRoleIds; | |||||
| IReadOnlyCollection<IUser> IMessage.MentionedUsers => MentionedUsers; | |||||
| private string DebuggerDisplay => $"{Author}: {Text}{(Attachments.Count > 0 ? $" [{Attachments.Count} Attachments]" : "")}"; | |||||
| } | } | ||||
| } | } | ||||
| @@ -4,6 +4,7 @@ using System.Collections.Immutable; | |||||
| using System.Globalization; | using System.Globalization; | ||||
| using System.Linq; | using System.Linq; | ||||
| using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||
| using System.Threading.Tasks; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| @@ -107,7 +108,7 @@ namespace Discord | |||||
| return builder; | return builder; | ||||
| } | } | ||||
| internal static string CleanUserMentions(string text, ImmutableArray<User> mentions) | |||||
| /*internal static string CleanUserMentions(string text, ImmutableArray<User> mentions) | |||||
| { | { | ||||
| return _userRegex.Replace(text, new MatchEvaluator(e => | return _userRegex.Replace(text, new MatchEvaluator(e => | ||||
| { | { | ||||
| @@ -123,58 +124,71 @@ namespace Discord | |||||
| } | } | ||||
| return e.Value; | return e.Value; | ||||
| })); | })); | ||||
| } | |||||
| internal static string CleanUserMentions<T>(string text, IReadOnlyDictionary<ulong, T> users, ImmutableArray<T>.Builder mentions = null) | |||||
| where T : IGuildUser | |||||
| }*/ | |||||
| internal static string CleanUserMentions(string text, IMessageChannel channel, IReadOnlyCollection<IUser> fallbackUsers, ImmutableArray<IUser>.Builder mentions = null) | |||||
| { | { | ||||
| return _channelRegex.Replace(text, new MatchEvaluator(e => | |||||
| return _userRegex.Replace(text, new MatchEvaluator(e => | |||||
| { | { | ||||
| ulong id; | ulong id; | ||||
| if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | ||||
| { | { | ||||
| T user; | |||||
| if (users.TryGetValue(id, out user)) | |||||
| 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; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (user != null) | |||||
| { | { | ||||
| if (users != null) | |||||
| mentions.Add(user); | |||||
| if (e.Value[2] == '!' && user.Nickname != null) | |||||
| return '@' + user.Nickname; | |||||
| else | |||||
| return '@' + user.Username; | |||||
| mentions.Add(user); | |||||
| if (e.Value[2] == '!') | |||||
| { | |||||
| var guildUser = user as IGuildUser; | |||||
| if (guildUser != null && guildUser.Nickname != null) | |||||
| return '@' + guildUser.Nickname; | |||||
| } | |||||
| return '@' + user.Username; | |||||
| } | } | ||||
| } | } | ||||
| return e.Value; | return e.Value; | ||||
| })); | })); | ||||
| } | } | ||||
| internal static string CleanChannelMentions<T>(string text, IReadOnlyDictionary<ulong, T> channels, ImmutableArray<T>.Builder mentions = null) | |||||
| where T : IGuildChannel | |||||
| internal static string CleanChannelMentions(string text, IGuild guild, ImmutableArray<ulong>.Builder mentions = null) | |||||
| { | { | ||||
| return _channelRegex.Replace(text, new MatchEvaluator(e => | return _channelRegex.Replace(text, new MatchEvaluator(e => | ||||
| { | { | ||||
| ulong id; | ulong id; | ||||
| if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | ||||
| { | { | ||||
| T channel; | |||||
| if (channels.TryGetValue(id, out channel)) | |||||
| var channel = guild.GetChannelAsync(id).GetAwaiter().GetResult() as IGuildChannel; | |||||
| if (channel != null) | |||||
| { | { | ||||
| if (channels != null) | |||||
| mentions.Add(channel); | |||||
| if (mentions != null) | |||||
| mentions.Add(channel.Id); | |||||
| return '#' + channel.Name; | return '#' + channel.Name; | ||||
| } | } | ||||
| } | } | ||||
| return e.Value; | return e.Value; | ||||
| })); | })); | ||||
| } | } | ||||
| internal static string CleanRoleMentions<T>(string text, IReadOnlyDictionary<ulong, T> roles, ImmutableArray<T>.Builder mentions = null) | |||||
| where T : IRole | |||||
| internal static string CleanRoleMentions(string text, IGuild guild, ImmutableArray<IRole>.Builder mentions = null) | |||||
| { | { | ||||
| return _channelRegex.Replace(text, new MatchEvaluator(e => | |||||
| return _roleRegex.Replace(text, new MatchEvaluator(e => | |||||
| { | { | ||||
| ulong id; | ulong id; | ||||
| if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | ||||
| { | { | ||||
| T role; | |||||
| if (roles.TryGetValue(id, out role)) | |||||
| var role = guild.GetRole(id); | |||||
| if (role != null) | |||||
| { | { | ||||
| if (mentions != null) | if (mentions != null) | ||||
| mentions.Add(role); | mentions.Add(role); | ||||