fix: #1314 Don't parse tags within code blocks (#1318)

* implement a fix for tags being found in code blocks

still needs polish, consider this a rough draft

* refactor to reuse a local function

uses CheckWrappedInCode to check that there are no code blocks that surround the tag being parsed

* Add more test coverage of MessageHelper.ParseTags

* reset indexes for @ here mention

* add a test case to catch error fixed from prev commit

* wip commit of most test cases working

* fix the Enclosed in block util method

* code cleanup

* lint whitespace

* lint brackets for single line if blocks

* move messagehelpertests to the new unit test dir

* expose internals to the unit test project

this seems to have been breaking the build, since CI would build the merged branch, where rest wasn't exposed to the unit tests
This commit is contained in:
Chris Johnston
2019-08-25 06:28:05 -07:00
committed by Christopher F
parent e8cb031704
commit c977f2ec9c
7 changed files with 177 additions and 7 deletions

View File

@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Discord.Net.Tests")]
[assembly: InternalsVisibleTo("Discord.Net.Tests")]
[assembly: InternalsVisibleTo("Discord.Net.Tests.Unit")]

View File

@@ -6,4 +6,5 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Discord.Net.WebSocket")]
[assembly: InternalsVisibleTo("Discord.Net.Webhook")]
[assembly: InternalsVisibleTo("Discord.Net.Commands")]
[assembly: InternalsVisibleTo("Discord.Net.Tests")]
[assembly: InternalsVisibleTo("Discord.Net.Tests.Unit")]

View File

@@ -5,6 +5,7 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Discord.Net.Webhook")]
[assembly: InternalsVisibleTo("Discord.Net.Commands")]
[assembly: InternalsVisibleTo("Discord.Net.Tests")]
[assembly: InternalsVisibleTo("Discord.Net.Tests.Unit")]
[assembly: TypeForwardedTo(typeof(Discord.Embed))]
[assembly: TypeForwardedTo(typeof(Discord.EmbedBuilder))]

View File

@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Model = Discord.API.Message;
@@ -108,14 +109,56 @@ namespace Discord.Rest
public static ImmutableArray<ITag> ParseTags(string text, IMessageChannel channel, IGuild guild, IReadOnlyCollection<IUser> userMentions)
{
var tags = ImmutableArray.CreateBuilder<ITag>();
int index = 0;
var codeIndex = 0;
var inlineRegex = new Regex(@"[^\\]?(`).+?[^\\](`)", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.Singleline);
var blockRegex = new Regex(@"[^\\]?(```).+?[^\\](```)", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.Singleline);
// checks if the tag being parsed is wrapped in code blocks
bool CheckWrappedCode()
{
// util to check if the index of a tag is within the bounds of the codeblock
bool EnclosedInBlock(Match m)
=> m.Groups[1].Index < index && index < m.Groups[2].Index;
// loop through all code blocks that are before the start of the tag
while (codeIndex < index)
{
var blockMatch = blockRegex.Match(text, codeIndex);
if (blockMatch.Success)
{
if (EnclosedInBlock(blockMatch))
return true;
// continue if the end of the current code was before the start of the tag
codeIndex += blockMatch.Groups[2].Index + blockMatch.Groups[2].Length;
if (codeIndex < index)
continue;
return false;
}
var inlineMatch = inlineRegex.Match(text, codeIndex);
if (inlineMatch.Success)
{
if (EnclosedInBlock(inlineMatch))
return true;
// continue if the end of the current code was before the start of the tag
codeIndex += inlineMatch.Groups[2].Index + inlineMatch.Groups[2].Length;
if (codeIndex < index)
continue;
return false;
}
return false;
}
return false;
}
while (true)
{
index = text.IndexOf('<', index);
if (index == -1) break;
int endIndex = text.IndexOf('>', index + 1);
if (endIndex == -1) break;
if (CheckWrappedCode()) break;
string content = text.Substring(index, endIndex - index + 1);
if (MentionUtils.TryParseUser(content, out ulong id))
@@ -158,10 +201,12 @@ namespace Discord.Rest
}
index = 0;
codeIndex = 0;
while (true)
{
index = text.IndexOf("@everyone", index);
if (index == -1) break;
if (CheckWrappedCode()) break;
var tagIndex = FindIndex(tags, index);
if (tagIndex.HasValue)
tags.Insert(tagIndex.Value, new Tag<IRole>(TagType.EveryoneMention, index, "@everyone".Length, 0, guild?.EveryoneRole));
@@ -169,10 +214,12 @@ namespace Discord.Rest
}
index = 0;
codeIndex = 0;
while (true)
{
index = text.IndexOf("@here", index);
if (index == -1) break;
if (CheckWrappedCode()) break;
var tagIndex = FindIndex(tags, index);
if (tagIndex.HasValue)
tags.Insert(tagIndex.Value, new Tag<IRole>(TagType.HereMention, index, "@here".Length, 0, guild?.EveryoneRole));

View File

@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Discord.Net.Relay")]
[assembly: InternalsVisibleTo("Discord.Net.Tests")]
[assembly: InternalsVisibleTo("Discord.Net.Tests")]
[assembly: InternalsVisibleTo("Discord.Net.Tests.Unit")]

View File

@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Discord.Net.Tests")]
[assembly: InternalsVisibleTo("Discord.Net.Tests")]
[assembly: InternalsVisibleTo("Discord.Net.Tests.Unit")]