| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
ac9b575230 | Clean up Emoji validation | 8 years ago |
|
|
40ba298331 | Added emoji validation | 8 years ago |
| @@ -11,4 +11,20 @@ | |||
| <PackageReference Include="System.Collections.Immutable" Version="1.3.1" /> | |||
| <PackageReference Include="System.Interactive.Async" Version="3.1.1" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <None Update="Entities\Emotes\Emoji.Codepoints.tt"> | |||
| <Generator>TextTemplatingFileGenerator</Generator> | |||
| <LastGenOutput>Emoji.Codepoints.cs</LastGenOutput> | |||
| </None> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Compile Update="Entities\Emotes\Emoji.Codepoints.cs"> | |||
| <DesignTime>True</DesignTime> | |||
| <AutoGen>True</AutoGen> | |||
| <DependentUpon>Emoji.Codepoints.tt</DependentUpon> | |||
| </Compile> | |||
| </ItemGroup> | |||
| </Project> | |||
| @@ -0,0 +1,59 @@ | |||
| <#@ output extension=".cs" #> | |||
| <#@ template language="C#" #> | |||
| <#@ assembly name="System.Net.Http.dll" #> | |||
| <#@ import namespace="System.Net.Http" #> | |||
| <#@ assembly name="System.Core.dll" #> | |||
| <#@ import namespace="System.Collections.Generic" #> | |||
| <#@ import namespace="System.Linq" #> | |||
| <# | |||
| const string DataUrl = "http://www.unicode.org/Public/emoji/6.0/emoji-data.txt"; | |||
| List<int> codepoints = new List<int>(); | |||
| void FetchData() | |||
| { | |||
| var client = new HttpClient(); | |||
| try | |||
| { | |||
| var response = client.GetAsync(DataUrl).GetAwaiter().GetResult(); | |||
| response.EnsureSuccessStatusCode(); | |||
| string body = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); | |||
| foreach (var line in body.Split('\n')) | |||
| { | |||
| if (line.StartsWith("#")) continue; | |||
| var split = line.Split(';'); | |||
| if (split.Length < 1) continue; | |||
| var code = split[0].Trim(); | |||
| if (string.IsNullOrEmpty(code)) continue; | |||
| var ranges = code.Split("..".ToCharArray()); | |||
| if (ranges.Length == 3) | |||
| { | |||
| var lower = Convert.ToInt32(ranges[0], 16); | |||
| var upper = Convert.ToInt32(ranges[2], 16); | |||
| codepoints.AddRange(Enumerable.Range(lower, (upper-lower)+1)); | |||
| } | |||
| else | |||
| { | |||
| var point = Convert.ToInt32(code, 16); | |||
| codepoints.Add(point); | |||
| } | |||
| } | |||
| } | |||
| catch | |||
| { | |||
| } | |||
| } | |||
| FetchData(); | |||
| #> | |||
| namespace Discord | |||
| { | |||
| public partial class Emoji | |||
| { | |||
| internal readonly int[] Codepoints = new int[] | |||
| { | |||
| <# foreach (var codepoint in codepoints) { #><#= codepoint #>, <# } #> | |||
| }; | |||
| } | |||
| } | |||
| @@ -1,9 +1,12 @@ | |||
| namespace Discord | |||
| using System; | |||
| using System.Text; | |||
| namespace Discord | |||
| { | |||
| /// <summary> | |||
| /// A unicode emoji | |||
| /// </summary> | |||
| public class Emoji : IEmote | |||
| public partial class Emoji : IEmote | |||
| { | |||
| // TODO: need to constrain this to unicode-only emojis somehow | |||
| @@ -20,6 +23,29 @@ | |||
| /// <param name="unicode">The pure UTF-8 encoding of an emoji</param> | |||
| public Emoji(string unicode) | |||
| { | |||
| // NETStandard1.1 doesn't support UTF32 | |||
| #if !NETSTANDARD1_1 | |||
| byte[] utf32 = Encoding.UTF32.GetBytes(unicode); | |||
| for (var i = 0; i < utf32.Length; i += 4) | |||
| { | |||
| int codepoint = BitConverter.ToInt32(utf32, i); | |||
| bool any = false; | |||
| for (var j = 0; j < Codepoints.Length; j++) | |||
| { | |||
| if (Codepoints[j] == codepoint) | |||
| { | |||
| any = true; | |||
| break; | |||
| } | |||
| } | |||
| if (any) continue; | |||
| else | |||
| throw new ArgumentException("One or more characters was not a valid Emoji", nameof(unicode)); | |||
| } | |||
| #endif | |||
| Name = unicode; | |||
| } | |||
| @@ -10,6 +10,9 @@ | |||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |||
| </Content> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <None Include="Tests.Emotes.cs" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ProjectReference Include="../../src/Discord.Net.Commands/Discord.Net.Commands.csproj" /> | |||
| <ProjectReference Include="../../src/Discord.Net.Core/Discord.Net.Core.csproj" /> | |||
| @@ -0,0 +1,54 @@ | |||
| using System; | |||
| using Xunit; | |||
| namespace Discord | |||
| { | |||
| public class EmoteTests | |||
| { | |||
| const string Smiley = "\U0001F603"; | |||
| const string Man = "\U0001F468"; | |||
| const string Woman = "\U0001F469"; | |||
| const string Girl = "\U0001F467"; | |||
| const string Boy = "\U0001F466"; | |||
| const string Join = "\u200D"; | |||
| [Fact] | |||
| public void Single_Emoji() | |||
| { | |||
| Assert.Equal(Smiley, new Emoji(Smiley).Name); | |||
| Assert.Equal(Man, new Emoji(Man).Name); | |||
| Assert.Equal(Woman, new Emoji(Woman).Name); | |||
| Assert.Equal(Girl, new Emoji(Girl).Name); | |||
| Assert.Equal(Boy, new Emoji(Boy).Name); | |||
| } | |||
| [Fact] | |||
| public void Multipart_Emoji() | |||
| { | |||
| string family = string.Concat(Man, Join, Woman, Join, Girl, Join, Boy); | |||
| Assert.Equal(family, new Emoji(family).Name); | |||
| } | |||
| [Fact] | |||
| public void Emoji_Fail() | |||
| { | |||
| Assert.Throws<ArgumentException>(() => new Emoji("foxDab")); | |||
| } | |||
| [Fact] | |||
| public void Emote() | |||
| { | |||
| Assert.Equal(true, Discord.Emote.TryParse("<:foxDab:280494667093508096>", out var emote)); | |||
| Assert.NotNull(emote); | |||
| Assert.Equal("foxDab", emote.Name); | |||
| Assert.Equal(280494667093508096UL, emote.Id); | |||
| Assert.Equal(DateTimeOffset.FromUnixTimeMilliseconds(1486945539974), emote.CreatedAt); | |||
| } | |||
| [Fact] | |||
| public void Emote_Parse_Fail() | |||
| { | |||
| Assert.Equal(false, Discord.Emote.TryParse("", out _)); | |||
| Assert.Equal(false, Discord.Emote.TryParse(":foxDab", out _)); | |||
| Assert.Equal(false, Discord.Emote.TryParse(":foxDab:", out _)); | |||
| Assert.Throws<ArgumentException>(() => Discord.Emote.Parse(":foxDab:")); | |||
| } | |||
| } | |||
| } | |||