Restructure and replace emojis with a new emote system (#619)

This commit is contained in:
Christopher F
2017-05-04 11:52:48 -04:00
committed by RogueException
parent ba1982a3f9
commit 576a52cdc6
18 changed files with 170 additions and 140 deletions

View File

@@ -0,0 +1,23 @@
namespace Discord
{
/// <summary>
/// A unicode emoji
/// </summary>
public class Emoji : IEmote
{
// TODO: need to constrain this to unicode-only emojis somehow
/// <summary>
/// Creates a unciode emoji.
/// </summary>
/// <param name="unicode">The pure UTF-8 encoding of an emoji</param>
public Emoji(string unicode)
{
Name = unicode;
}
/// <summary>
/// The unicode representation of this emote.
/// </summary>
public string Name { get; }
}
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Globalization;
namespace Discord
{
/// <summary>
/// A custom image-based emote
/// </summary>
public class Emote : IEmote, ISnowflakeEntity
{
/// <summary>
/// The display name (tooltip) of this emote
/// </summary>
public string Name { get; }
/// <summary>
/// The ID of this emote
/// </summary>
public ulong Id { get; }
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
public string Url => CDN.GetEmojiUrl(Id);
internal Emote(ulong id, string name)
{
Id = id;
Name = name;
}
/// <summary>
/// Parse an Emote from its raw format
/// </summary>
/// <param name="text">The raw encoding of an emote; for example, &lt;:dab:277855270321782784&gt;</param>
/// <returns>An emote</returns>
public static Emote Parse(string text)
{
if (TryParse(text, out Emote result))
return result;
throw new ArgumentException("Invalid emote format", nameof(text));
}
public static bool TryParse(string text, out Emote result)
{
result = null;
if (text.Length >= 4 && text[0] == '<' && text[1] == ':' && text[text.Length - 1] == '>')
{
int splitIndex = text.IndexOf(':', 2);
if (splitIndex == -1)
return false;
if (!ulong.TryParse(text.Substring(splitIndex + 1, text.Length - splitIndex - 2), NumberStyles.None, CultureInfo.InvariantCulture, out ulong id))
return false;
string name = text.Substring(2, splitIndex - 2);
result = new Emote(id, name);
return true;
}
return false;
}
private string DebuggerDisplay => $"{Name} ({Id})";
public override string ToString() => Name;
}
}

View File

@@ -3,19 +3,18 @@ using System.Diagnostics;
namespace Discord
{
/// <summary>
/// An image-based emote that is attached to a guild
/// </summary>
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public struct GuildEmoji
public class GuildEmote : Emote
{
public ulong Id { get; }
public string Name { get; }
public bool IsManaged { get; }
public bool RequireColons { get; }
public IReadOnlyList<ulong> RoleIds { get; }
internal GuildEmoji(ulong id, string name, bool isManaged, bool requireColons, IReadOnlyList<ulong> roleIds)
internal GuildEmote(ulong id, string name, bool isManaged, bool requireColons, IReadOnlyList<ulong> roleIds) : base(id, name)
{
Id = id;
Name = name;
IsManaged = isManaged;
RequireColons = requireColons;
RoleIds = roleIds;

View File

@@ -0,0 +1,13 @@
namespace Discord
{
/// <summary>
/// A general container for any type of emote in a message.
/// </summary>
public interface IEmote
{
/// <summary>
/// The display name or unicode representation of this emote
/// </summary>
string Name { get; }
}
}

View File

@@ -45,7 +45,7 @@ namespace Discord
/// <summary> Gets the built-in role containing all users in this guild. </summary>
IRole EveryoneRole { get; }
/// <summary> Gets a collection of all custom emojis for this guild. </summary>
IReadOnlyCollection<GuildEmoji> Emojis { get; }
IReadOnlyCollection<GuildEmote> Emotes { get; }
/// <summary> Gets a collection of all extra features added to this guild. </summary>
IReadOnlyCollection<string> Features { get; }
/// <summary> Gets a collection of all roles in this guild. </summary>

View File

@@ -1,51 +0,0 @@
using System;
using System.Diagnostics;
using System.Globalization;
namespace Discord
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public struct Emoji
{
public ulong? Id { get; }
public string Name { get; }
public string Url => Id != null ? CDN.GetEmojiUrl(Id.Value) : null;
internal Emoji(ulong? id, string name)
{
Id = id;
Name = name;
}
public static Emoji Parse(string text)
{
if (TryParse(text, out Emoji result))
return result;
throw new ArgumentException("Invalid emoji format", nameof(text));
}
public static bool TryParse(string text, out Emoji result)
{
result = default(Emoji);
if (text.Length >= 4 && text[0] == '<' && text[1] == ':' && text[text.Length - 1] == '>')
{
int splitIndex = text.IndexOf(':', 2);
if (splitIndex == -1)
return false;
if (!ulong.TryParse(text.Substring(splitIndex + 1, text.Length - splitIndex - 2), NumberStyles.None, CultureInfo.InvariantCulture, out ulong id))
return false;
string name = text.Substring(2, splitIndex - 2);
result = new Emoji(id, name);
return true;
}
return false;
}
private string DebuggerDisplay => $"{Name} ({Id})";
public override string ToString() => Name;
}
}

View File

@@ -2,6 +2,6 @@
{
public interface IReaction
{
Emoji Emoji { get; }
IEmote Emote { get; }
}
}

View File

@@ -14,16 +14,12 @@ namespace Discord
Task UnpinAsync(RequestOptions options = null);
/// <summary> Returns all reactions included in this message. </summary>
IReadOnlyDictionary<Emoji, ReactionMetadata> Reactions { get; }
IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions { get; }
/// <summary> Adds a reaction to this message. </summary>
Task AddReactionAsync(Emoji emoji, RequestOptions options = null);
/// <summary> Adds a reaction to this message. </summary>
Task AddReactionAsync(string emoji, RequestOptions options = null);
Task AddReactionAsync(IEmote emote, RequestOptions options = null);
/// <summary> Removes a reaction from message. </summary>
Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null);
/// <summary> Removes a reaction from this message. </summary>
Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null);
Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options = null);
/// <summary> Removes all reactions from this message. </summary>
Task RemoveAllReactionsAsync(RequestOptions options = null);
Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null);

View File

@@ -252,7 +252,7 @@ namespace Discord
{
if (mode != TagHandling.Remove)
{
Emoji emoji = (Emoji)tag.Value;
Emote emoji = (Emote)tag.Value;
//Remove if its name contains any bad chars (prevents a few tag exploits)
for (int i = 0; i < emoji.Name.Length; i++)