Claned up mentions, added sanitize handler to user/role/channel mentions
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
{
|
||||
Ignore = 0,
|
||||
Remove,
|
||||
Name
|
||||
Name,
|
||||
Sanitize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{
|
||||
Ignore = 0,
|
||||
Remove,
|
||||
Name
|
||||
Name,
|
||||
Sanitize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
Ignore = 0,
|
||||
Remove,
|
||||
Name,
|
||||
NameAndDiscriminator
|
||||
NameAndDiscriminator,
|
||||
Sanitize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user