Browse Source

Claned up mentions, added sanitize handler to user/role/channel mentions

tags/1.0-rc
RogueException 8 years ago
parent
commit
2f3831dd6e
8 changed files with 223 additions and 220 deletions
  1. +2
    -1
      src/Discord.Net.Core/Entities/Messages/Mentions/ChannelMentionHandling.cs
  2. +2
    -1
      src/Discord.Net.Core/Entities/Messages/Mentions/RoleMentionHandling.cs
  3. +2
    -1
      src/Discord.Net.Core/Entities/Messages/Mentions/UserMentionHandling.cs
  4. +202
    -3
      src/Discord.Net.Core/Utils/MentionUtils.cs
  5. +0
    -198
      src/Discord.Net.Core/Utils/MentionsHelper.cs
  6. +7
    -7
      src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
  7. +1
    -2
      src/Discord.Net.WebSocket/API/Gateway/RequestMembersParams.cs
  8. +7
    -7
      src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs

+ 2
- 1
src/Discord.Net.Core/Entities/Messages/Mentions/ChannelMentionHandling.cs View File

@@ -4,6 +4,7 @@
{
Ignore = 0,
Remove,
Name
Name,
Sanitize
}
}

+ 2
- 1
src/Discord.Net.Core/Entities/Messages/Mentions/RoleMentionHandling.cs View File

@@ -4,6 +4,7 @@
{
Ignore = 0,
Remove,
Name
Name,
Sanitize
}
}

+ 2
- 1
src/Discord.Net.Core/Entities/Messages/Mentions/UserMentionHandling.cs View File

@@ -5,6 +5,7 @@
Ignore = 0,
Remove,
Name,
NameAndDiscriminator
NameAndDiscriminator,
Sanitize
}
}

+ 202
- 3
src/Discord.Net.Core/Utils/MentionUtils.cs View File

@@ -1,14 +1,27 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;

namespace Discord
{
public static class MentionUtils
{
private const char SanitizeChar = '\x200b';

private static readonly Regex _userRegex = new Regex(@"<@!?([0-9]+)>", RegexOptions.Compiled);
private static readonly Regex _channelRegex = new Regex(@"<#([0-9]+)>", RegexOptions.Compiled);
private static readonly Regex _roleRegex = new Regex(@"<@&([0-9]+)>", RegexOptions.Compiled);

//If the system can't be positive a user doesn't have a nickname, assume useNickname = true (source: Jake)
public static string MentionUser(ulong id) => MentionsHelper.MentionUser(id, true);
public static string MentionChannel(ulong id) => MentionsHelper.MentionChannel(id);
public static string MentionRole(ulong id) => MentionsHelper.MentionRole(id);
internal static string MentionUser(string id, bool useNickname = true) => useNickname ? $"<@!{id}>" : $"<@{id}>";
public static string MentionUser(ulong id) => MentionUser(id.ToString(), true);
internal static string MentionChannel(string id) => $"<#{id}>";
public static string MentionChannel(ulong id) => MentionChannel(id.ToString());
internal static string MentionRole(string id) => $"<@&{id}>";
public static string MentionRole(ulong id) => MentionRole(id.ToString());

/// <summary> Parses a provided user mention string. </summary>
public static ulong ParseUser(string mentionText)
@@ -81,5 +94,191 @@ namespace Discord
roleId = 0;
return false;
}

internal static ImmutableArray<TUser> GetUserMentions<TUser>(string text, IMessageChannel channel, IReadOnlyCollection<TUser> mentionedUsers)
where TUser : class, IUser
{
var matches = _userRegex.Matches(text);
var builder = ImmutableArray.CreateBuilder<TUser>(matches.Count);
foreach (var match in matches.OfType<Match>())
{
ulong id;
if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id))
{
TUser user = null;

//Verify this user was actually mentioned
foreach (var userMention in mentionedUsers)
{
if (userMention.Id == id)
{
user = channel?.GetUserAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult() as TUser;
if (user == null) //User not found, fallback to basic mention info
user = userMention;
break;
}
}

if (user != null)
builder.Add(user);
}
}
return builder.ToImmutable();
}
internal static ImmutableArray<ulong> GetChannelMentions(string text, IGuild guild)
{
var matches = _channelRegex.Matches(text);
var builder = ImmutableArray.CreateBuilder<ulong>(matches.Count);
foreach (var match in matches.OfType<Match>())
{
ulong id;
if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id))
builder.Add(id);
}
return builder.ToImmutable();
}
internal static ImmutableArray<TRole> GetRoleMentions<TRole>(string text, IGuild guild)
where TRole : class, IRole
{
if (guild == null)
return ImmutableArray.Create<TRole>();

var matches = _roleRegex.Matches(text);
var builder = ImmutableArray.CreateBuilder<TRole>(matches.Count);
foreach (var match in matches.OfType<Match>())
{
ulong id;
if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id))
{
var role = guild.GetRole(id) as TRole;
if (role != null)
builder.Add(role);
}
}
return builder.ToImmutable();
}

internal static string ResolveUserMentions(string text, IMessageChannel channel, IReadOnlyCollection<IUser> mentions, UserMentionHandling mode)
{
if (mode == UserMentionHandling.Ignore) return text;

return _userRegex.Replace(text, new MatchEvaluator(e =>
{
ulong id;
if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id))
{
IUser user = null;
foreach (var mention in mentions)
{
if (mention.Id == id)
{
user = mention;
break;
}
}
if (user != null)
{
string name = user.Username;

var guildUser = user as IGuildUser;
if (e.Value[2] == '!')
{
if (guildUser != null && guildUser.Nickname != null)
name = guildUser.Nickname;
}

switch (mode)
{
case UserMentionHandling.Name:
return $"@{name}";
case UserMentionHandling.NameAndDiscriminator:
return $"@{name}#{user.Discriminator}";
case UserMentionHandling.Sanitize:
return MentionUser($"{SanitizeChar}{id}");
case UserMentionHandling.Remove:
default:
return "";
}
}
}
return e.Value;
}));
}
internal static string ResolveChannelMentions(string text, IGuild guild, ChannelMentionHandling mode)
{
if (mode == ChannelMentionHandling.Ignore) return text;

return _channelRegex.Replace(text, new MatchEvaluator(e =>
{
ulong id;
if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id))
{
switch (mode)
{
case ChannelMentionHandling.Name:
IGuildChannel channel = null;
channel = guild.GetChannelAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult();
if (channel != null)
return $"#{channel.Name}";
else
return $"#deleted-channel";
case ChannelMentionHandling.Sanitize:
return MentionChannel($"{SanitizeChar}{id}");
case ChannelMentionHandling.Remove:
default:
return "";
}
}
return e.Value;
}));
}
internal static string ResolveRoleMentions(string text, IReadOnlyCollection<IRole> mentions, RoleMentionHandling mode)
{
if (mode == RoleMentionHandling.Ignore) return text;

return _roleRegex.Replace(text, new MatchEvaluator(e =>
{
ulong id;
if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id))
{
switch (mode)
{
case RoleMentionHandling.Name:
IRole role = null;
foreach (var mention in mentions)
{
if (mention.Id == id)
{
role = mention;
break;
}
}
if (role != null)
return $"{role.Name}";
else
return $"deleted-role";
case RoleMentionHandling.Sanitize:
return MentionRole($"{SanitizeChar}{id}");
case RoleMentionHandling.Remove:
default:
return "";
}
}
return e.Value;
}));
}
internal static string ResolveEveryoneMentions(string text, EveryoneMentionHandling mode)
{
if (mode == EveryoneMentionHandling.Ignore) return text;

switch (mode)
{
case EveryoneMentionHandling.Sanitize:
return text.Replace("@everyone", $"@{SanitizeChar}everyone").Replace("@here", $"@{SanitizeChar}here");
case EveryoneMentionHandling.Remove:
default:
return text.Replace("@everyone", "").Replace("@here", "");
}
}
}
}

+ 0
- 198
src/Discord.Net.Core/Utils/MentionsHelper.cs View File

@@ -1,198 +0,0 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;

namespace Discord
{
internal static class MentionsHelper
{
private static readonly Regex _userRegex = new Regex(@"<@!?([0-9]+)>", RegexOptions.Compiled);
private static readonly Regex _channelRegex = new Regex(@"<#([0-9]+)>", RegexOptions.Compiled);
private static readonly Regex _roleRegex = new Regex(@"<@&([0-9]+)>", RegexOptions.Compiled);

//If the system can't be positive a user doesn't have a nickname, assume useNickname = true (source: Jake)
internal static string MentionUser(ulong id, bool useNickname = true) => useNickname ? $"<@!{id}>" : $"<@{id}>";
internal static string MentionChannel(ulong id) => $"<#{id}>";
internal static string MentionRole(ulong id) => $"<@&{id}>";

internal static ImmutableArray<TUser> GetUserMentions<TUser>(string text, IMessageChannel channel, IReadOnlyCollection<TUser> mentionedUsers)
where TUser : class, IUser
{
var matches = _userRegex.Matches(text);
var builder = ImmutableArray.CreateBuilder<TUser>(matches.Count);
foreach (var match in matches.OfType<Match>())
{
ulong id;
if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id))
{
TUser user = null;

//Verify this user was actually mentioned
foreach (var userMention in mentionedUsers)
{
if (userMention.Id == id)
{
user = channel?.GetUserAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult() as TUser;
if (user == null) //User not found, fallback to basic mention info
user = userMention;
break;
}
}

if (user != null)
builder.Add(user);
}
}
return builder.ToImmutable();
}
internal static ImmutableArray<ulong> GetChannelMentions(string text, IGuild guild)
{
var matches = _channelRegex.Matches(text);
var builder = ImmutableArray.CreateBuilder<ulong>(matches.Count);
foreach (var match in matches.OfType<Match>())
{
ulong id;
if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id))
builder.Add(id);
}
return builder.ToImmutable();
}
internal static ImmutableArray<TRole> GetRoleMentions<TRole>(string text, IGuild guild)
where TRole : class, IRole
{
if (guild == null)
return ImmutableArray.Create<TRole>();

var matches = _roleRegex.Matches(text);
var builder = ImmutableArray.CreateBuilder<TRole>(matches.Count);
foreach (var match in matches.OfType<Match>())
{
ulong id;
if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id))
{
var role = guild.GetRole(id) as TRole;
if (role != null)
builder.Add(role);
}
}
return builder.ToImmutable();
}

internal static string ResolveUserMentions(string text, IMessageChannel channel, IReadOnlyCollection<IUser> mentions, UserMentionHandling mode)
{
if (mode == UserMentionHandling.Ignore) return text;

return _userRegex.Replace(text, new MatchEvaluator(e =>
{
ulong id;
if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id))
{
IUser user = null;
foreach (var mention in mentions)
{
if (mention.Id == id)
{
user = mention;
break;
}
}
if (user != null)
{
string name = user.Username;

var guildUser = user as IGuildUser;
if (e.Value[2] == '!')
{
if (guildUser != null && guildUser.Nickname != null)
name = guildUser.Nickname;
}

switch (mode)
{
case UserMentionHandling.Remove:
default:
return "";
case UserMentionHandling.Name:
return $"@{name}";
case UserMentionHandling.NameAndDiscriminator:
return $"@{name}#{user.Discriminator}";
}
}
}
return e.Value;
}));
}
internal static string ResolveChannelMentions(string text, IGuild guild, ChannelMentionHandling mode)
{
if (mode == ChannelMentionHandling.Ignore) return text;

return _channelRegex.Replace(text, new MatchEvaluator(e =>
{
ulong id;
if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id))
{
switch (mode)
{
case ChannelMentionHandling.Remove:
return "";
case ChannelMentionHandling.Name:
IGuildChannel channel = null;
channel = guild.GetChannelAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult();
if (channel != null)
return $"#{channel.Name}";
else
return $"#deleted-channel";
}
}
return e.Value;
}));
}
internal static string ResolveRoleMentions(string text, IReadOnlyCollection<IRole> mentions, RoleMentionHandling mode)
{
if (mode == RoleMentionHandling.Ignore) return text;

return _roleRegex.Replace(text, new MatchEvaluator(e =>
{
ulong id;
if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id))
{
switch (mode)
{
case RoleMentionHandling.Remove:
return "";
case RoleMentionHandling.Name:
IRole role = null;
foreach (var mention in mentions)
{
if (mention.Id == id)
{
role = mention;
break;
}
}
if (role != null)
return $"@{role.Name}";
else
return $"@deleted-role";
}
}
return e.Value;
}));
}
internal static string ResolveEveryoneMentions(string text, EveryoneMentionHandling mode)
{
if (mode == EveryoneMentionHandling.Ignore) return text;

switch (mode)
{
case EveryoneMentionHandling.Sanitize:
return text.Replace("@everyone", "@\x200beveryone").Replace("@here", "@\x200bhere");
case EveryoneMentionHandling.Remove:
default:
return text.Replace("@everyone", "").Replace("@here", "");
}
}
}
}

+ 7
- 7
src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs View File

@@ -102,9 +102,9 @@ namespace Discord.Rest
{
var text = model.Content.Value;
_mentionedUsers = MentionsHelper.GetUserMentions(text, null, mentions);
_mentionedChannelIds = MentionsHelper.GetChannelMentions(text, null);
_mentionedRoles = MentionsHelper.GetRoleMentions<RestRole>(text, null);
_mentionedUsers = MentionUtils.GetUserMentions(text, null, mentions);
_mentionedChannelIds = MentionUtils.GetChannelMentions(text, null);
_mentionedRoles = MentionUtils.GetRoleMentions<RestRole>(text, null);
model.Content = text;
}
}
@@ -128,10 +128,10 @@ namespace Discord.Rest
public string Resolve(string text, UserMentionHandling userHandling, ChannelMentionHandling channelHandling,
RoleMentionHandling roleHandling, EveryoneMentionHandling everyoneHandling)
{
text = MentionsHelper.ResolveUserMentions(text, null, MentionedUsers, userHandling);
text = MentionsHelper.ResolveChannelMentions(text, null, channelHandling);
text = MentionsHelper.ResolveRoleMentions(text, MentionedRoles, roleHandling);
text = MentionsHelper.ResolveEveryoneMentions(text, everyoneHandling);
text = MentionUtils.ResolveUserMentions(text, null, MentionedUsers, userHandling);
text = MentionUtils.ResolveChannelMentions(text, null, channelHandling);
text = MentionUtils.ResolveRoleMentions(text, MentionedRoles, roleHandling);
text = MentionUtils.ResolveEveryoneMentions(text, everyoneHandling);
return text;
}



+ 1
- 2
src/Discord.Net.WebSocket/API/Gateway/RequestMembersParams.cs View File

@@ -1,7 +1,6 @@
#pragma warning disable CS1591
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;

namespace Discord.API.Gateway
{
@@ -14,6 +13,6 @@ namespace Discord.API.Gateway
public int Limit { get; set; }

[JsonProperty("guild_id")]
private ulong[] GuildIds { get; set; }
public IEnumerable<ulong> GuildIds { get; set; }
}
}

+ 7
- 7
src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs View File

@@ -105,9 +105,9 @@ namespace Discord.WebSocket
var text = model.Content.Value;
var guild = (Channel as SocketGuildChannel)?.Guild;

_mentionedUsers = MentionsHelper.GetUserMentions(text, Channel, mentions);
_mentionedChannelIds = MentionsHelper.GetChannelMentions(text, guild);
_mentionedRoles = MentionsHelper.GetRoleMentions<RestRole>(text, guild);
_mentionedUsers = MentionUtils.GetUserMentions(text, Channel, mentions);
_mentionedChannelIds = MentionUtils.GetChannelMentions(text, guild);
_mentionedRoles = MentionUtils.GetRoleMentions<RestRole>(text, guild);
model.Content = text;
}
}
@@ -131,10 +131,10 @@ namespace Discord.WebSocket
public string Resolve(string text, UserMentionHandling userHandling, ChannelMentionHandling channelHandling,
RoleMentionHandling roleHandling, EveryoneMentionHandling everyoneHandling)
{
text = MentionsHelper.ResolveUserMentions(text, null, MentionedUsers, userHandling);
text = MentionsHelper.ResolveChannelMentions(text, null, channelHandling);
text = MentionsHelper.ResolveRoleMentions(text, MentionedRoles, roleHandling);
text = MentionsHelper.ResolveEveryoneMentions(text, everyoneHandling);
text = MentionUtils.ResolveUserMentions(text, null, MentionedUsers, userHandling);
text = MentionUtils.ResolveChannelMentions(text, null, channelHandling);
text = MentionUtils.ResolveRoleMentions(text, MentionedRoles, roleHandling);
text = MentionUtils.ResolveEveryoneMentions(text, everyoneHandling);
return text;
}



Loading…
Cancel
Save