| @@ -32,7 +32,7 @@ | |||||
| if (text.Length < endPos + 2 || text[endPos + 1] != ' ') return false; //Must end in "> " | if (text.Length < endPos + 2 || text[endPos + 1] != ' ') return false; //Must end in "> " | ||||
| ulong userId; | ulong userId; | ||||
| if (!MentionUtils.TryParseUser(text.Substring(0, endPos + 2), out userId)) return false; | |||||
| if (!MentionUtils.TryParseUser(text.Substring(0, endPos + 1), out userId)) return false; | |||||
| if (userId == user.Id) | if (userId == user.Id) | ||||
| { | { | ||||
| argPos = endPos + 2; | argPos = endPos + 2; | ||||
| @@ -5,6 +5,9 @@ namespace Discord | |||||
| { | { | ||||
| public interface IChannel : ISnowflakeEntity | public interface IChannel : ISnowflakeEntity | ||||
| { | { | ||||
| /// <summary> Gets the name of this channel. </summary> | |||||
| string Name { get; } | |||||
| /// <summary> Gets a collection of all users in this channel. </summary> | /// <summary> Gets a collection of all users in this channel. </summary> | ||||
| IAsyncEnumerable<IReadOnlyCollection<IUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | IAsyncEnumerable<IReadOnlyCollection<IUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | ||||
| @@ -7,8 +7,6 @@ namespace Discord | |||||
| { | { | ||||
| public interface IGuildChannel : IChannel, IDeletable | public interface IGuildChannel : IChannel, IDeletable | ||||
| { | { | ||||
| /// <summary> Gets the name of this channel. </summary> | |||||
| string Name { get; } | |||||
| /// <summary> Gets the position of this channel in the guild's channel list, relative to others of the same type. </summary> | /// <summary> Gets the position of this channel in the guild's channel list, relative to others of the same type. </summary> | ||||
| int Position { get; } | int Position { get; } | ||||
| @@ -1,7 +1,9 @@ | |||||
| using Model = Discord.API.EmbedProvider; | |||||
| using System.Diagnostics; | |||||
| using Model = Discord.API.EmbedProvider; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| [DebuggerDisplay("{DebuggerDisplay,nq}")] | |||||
| public struct EmbedProvider | public struct EmbedProvider | ||||
| { | { | ||||
| public string Name { get; } | public string Name { get; } | ||||
| @@ -16,5 +18,8 @@ namespace Discord | |||||
| { | { | ||||
| return new EmbedProvider(model.Name, model.Url); | return new EmbedProvider(model.Name, model.Url); | ||||
| } | } | ||||
| private string DebuggerDisplay => $"{Name} ({Url})"; | |||||
| public override string ToString() => Name; | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,7 +1,9 @@ | |||||
| using Model = Discord.API.EmbedThumbnail; | |||||
| using System.Diagnostics; | |||||
| using Model = Discord.API.EmbedThumbnail; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| [DebuggerDisplay("{DebuggerDisplay,nq}")] | |||||
| public struct EmbedThumbnail | public struct EmbedThumbnail | ||||
| { | { | ||||
| public string Url { get; } | public string Url { get; } | ||||
| @@ -22,5 +24,8 @@ namespace Discord | |||||
| model.Height.IsSpecified ? model.Height.Value : (int?)null, | model.Height.IsSpecified ? model.Height.Value : (int?)null, | ||||
| model.Width.IsSpecified ? model.Width.Value : (int?)null); | model.Width.IsSpecified ? model.Width.Value : (int?)null); | ||||
| } | } | ||||
| private string DebuggerDisplay => $"{ToString()} ({Url})"; | |||||
| public override string ToString() => Width != null && Height != null ? $"{Width}x{Height}" : "0x0"; | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,22 +1,54 @@ | |||||
| using Discord.API; | using Discord.API; | ||||
| using System; | |||||
| using System.Diagnostics; | |||||
| using System.Globalization; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| [DebuggerDisplay("{DebuggerDisplay,nq}")] | |||||
| public struct Emoji | public struct Emoji | ||||
| { | { | ||||
| public ulong Id { get; } | public ulong Id { get; } | ||||
| public string Name { get; } | public string Name { get; } | ||||
| public int Index { get; } | |||||
| public int Length { get; } | |||||
| public string Url => CDN.GetEmojiUrl(Id); | public string Url => CDN.GetEmojiUrl(Id); | ||||
| internal Emoji(ulong id, string name, int index, int length) | |||||
| internal Emoji(ulong id, string name) | |||||
| { | { | ||||
| Id = id; | Id = id; | ||||
| Name = name; | Name = name; | ||||
| Index = index; | |||||
| Length = length; | |||||
| } | } | ||||
| public static Emoji Parse(string text) | |||||
| { | |||||
| Emoji result; | |||||
| if (TryParse(text, out 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; | |||||
| ulong id; | |||||
| if (!ulong.TryParse(text.Substring(splitIndex + 1, text.Length - splitIndex - 2), NumberStyles.None, CultureInfo.InvariantCulture, out 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; | |||||
| } | } | ||||
| } | } | ||||
| @@ -27,8 +27,8 @@ namespace Discord | |||||
| IReadOnlyCollection<IAttachment> Attachments { get; } | IReadOnlyCollection<IAttachment> Attachments { get; } | ||||
| /// <summary> Returns a collection of all embeds included in this message. </summary> | /// <summary> Returns a collection of all embeds included in this message. </summary> | ||||
| IReadOnlyCollection<IEmbed> Embeds { get; } | IReadOnlyCollection<IEmbed> Embeds { get; } | ||||
| /// <summary> Returns a collection of all custom emoji included in this message. </summary> | |||||
| IReadOnlyCollection<Emoji> Emojis { get; } | |||||
| /// <summary> Returns a collection of all tags included in this message's content. </summary> | |||||
| IReadOnlyCollection<ITag> Tags { get; } | |||||
| /// <summary> Returns a collection of channel ids mentioned in this message. </summary> | /// <summary> Returns a collection of channel ids mentioned in this message. </summary> | ||||
| IReadOnlyCollection<ulong> MentionedChannelIds { get; } | IReadOnlyCollection<ulong> MentionedChannelIds { get; } | ||||
| /// <summary> Returns a collection of roles mentioned in this message. </summary> | /// <summary> Returns a collection of roles mentioned in this message. </summary> | ||||
| @@ -0,0 +1,11 @@ | |||||
| namespace Discord | |||||
| { | |||||
| public interface ITag | |||||
| { | |||||
| int Index { get; } | |||||
| int Length { get; } | |||||
| TagType Type { get; } | |||||
| ulong Key { get; } | |||||
| object Value { get; } | |||||
| } | |||||
| } | |||||
| @@ -12,18 +12,13 @@ namespace Discord | |||||
| Task PinAsync(RequestOptions options = null); | Task PinAsync(RequestOptions options = null); | ||||
| /// <summary> Removes this message from its channel's pinned messages. </summary> | /// <summary> Removes this message from its channel's pinned messages. </summary> | ||||
| Task UnpinAsync(RequestOptions options = null); | Task UnpinAsync(RequestOptions options = null); | ||||
| /// <summary> Transforms this message's text into a human readable form, resolving mentions to that object's name. </summary> | |||||
| string Resolve(int startIndex, int length, | |||||
| UserMentionHandling userHandling = UserMentionHandling.Name, | |||||
| ChannelMentionHandling channelHandling = ChannelMentionHandling.Name, | |||||
| RoleMentionHandling roleHandling = RoleMentionHandling.Name, | |||||
| EveryoneMentionHandling everyoneHandling = EveryoneMentionHandling.Ignore); | |||||
| /// <summary> Transforms this message's text into a human readable form, resolving mentions to that object's name. </summary> | |||||
| /// <summary> Transforms this message's text into a human readable form by resolving its tags. </summary> | |||||
| string Resolve( | string Resolve( | ||||
| UserMentionHandling userHandling = UserMentionHandling.Name, | |||||
| ChannelMentionHandling channelHandling = ChannelMentionHandling.Name, | |||||
| RoleMentionHandling roleHandling = RoleMentionHandling.Name, | |||||
| EveryoneMentionHandling everyoneHandling = EveryoneMentionHandling.Ignore); | |||||
| TagHandling userHandling = TagHandling.Name, | |||||
| TagHandling channelHandling = TagHandling.Name, | |||||
| TagHandling roleHandling = TagHandling.Name, | |||||
| TagHandling everyoneHandling = TagHandling.Ignore, | |||||
| TagHandling emojiHandling = TagHandling.Name); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,9 +0,0 @@ | |||||
| namespace Discord | |||||
| { | |||||
| public enum EveryoneMentionHandling | |||||
| { | |||||
| Ignore = 0, | |||||
| Remove, | |||||
| Sanitize | |||||
| } | |||||
| } | |||||
| @@ -1,10 +0,0 @@ | |||||
| namespace Discord | |||||
| { | |||||
| public enum RoleMentionHandling | |||||
| { | |||||
| Ignore = 0, | |||||
| Remove, | |||||
| Name, | |||||
| Sanitize | |||||
| } | |||||
| } | |||||
| @@ -1,11 +0,0 @@ | |||||
| namespace Discord | |||||
| { | |||||
| public enum UserMentionHandling | |||||
| { | |||||
| Ignore = 0, | |||||
| Remove, | |||||
| Name, | |||||
| NameAndDiscriminator, | |||||
| Sanitize | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,28 @@ | |||||
| using System.Diagnostics; | |||||
| namespace Discord | |||||
| { | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||||
| public class Tag<T> : ITag | |||||
| { | |||||
| public TagType Type { get; } | |||||
| public int Index { get; } | |||||
| public int Length { get; } | |||||
| public ulong Key { get; } | |||||
| public T Value { get; } | |||||
| internal Tag(TagType type, int index, int length, ulong key, T value) | |||||
| { | |||||
| Type = type; | |||||
| Index = index; | |||||
| Length = length; | |||||
| Key = key; | |||||
| Value = value; | |||||
| } | |||||
| private string DebuggerDisplay => $"{Value?.ToString() ?? "null"} ({Type})"; | |||||
| public override string ToString() => $"{Value?.ToString() ?? "null"} ({Type})"; | |||||
| object ITag.Value => Value; | |||||
| } | |||||
| } | |||||
| @@ -1,10 +1,11 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public enum ChannelMentionHandling | |||||
| public enum TagHandling | |||||
| { | { | ||||
| Ignore = 0, | Ignore = 0, | ||||
| Remove, | Remove, | ||||
| Name, | Name, | ||||
| FullName, | |||||
| Sanitize | Sanitize | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,12 @@ | |||||
| namespace Discord | |||||
| { | |||||
| public enum TagType | |||||
| { | |||||
| UserMention, | |||||
| ChannelMention, | |||||
| RoleMention, | |||||
| EveryoneMention, | |||||
| HereMention, | |||||
| Emoji | |||||
| } | |||||
| } | |||||
| @@ -1,9 +1,6 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | |||||
| using System.Globalization; | using System.Globalization; | ||||
| using System.Linq; | |||||
| using System.Text.RegularExpressions; | |||||
| using System.Text; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| @@ -11,10 +8,6 @@ namespace Discord | |||||
| { | { | ||||
| private const char SanitizeChar = '\x200b'; | 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) | //If the system can't be positive a user doesn't have a nickname, assume useNickname = true (source: Jake) | ||||
| internal static string MentionUser(string id, bool useNickname = true) => useNickname ? $"<@!{id}>" : $"<@{id}>"; | internal static string MentionUser(string id, bool useNickname = true) => useNickname ? $"<@!{id}>" : $"<@{id}>"; | ||||
| public static string MentionUser(ulong id) => MentionUser(id.ToString(), true); | public static string MentionUser(ulong id) => MentionUser(id.ToString(), true); | ||||
| @@ -24,25 +17,24 @@ namespace Discord | |||||
| public static string MentionRole(ulong id) => MentionRole(id.ToString()); | public static string MentionRole(ulong id) => MentionRole(id.ToString()); | ||||
| /// <summary> Parses a provided user mention string. </summary> | /// <summary> Parses a provided user mention string. </summary> | ||||
| public static ulong ParseUser(string mentionText) | |||||
| public static ulong ParseUser(string text) | |||||
| { | { | ||||
| ulong id; | ulong id; | ||||
| if (TryParseUser(mentionText, out id)) | |||||
| if (TryParseUser(text, out id)) | |||||
| return id; | return id; | ||||
| throw new ArgumentException("Invalid mention format", nameof(mentionText)); | |||||
| throw new ArgumentException("Invalid mention format", nameof(text)); | |||||
| } | } | ||||
| /// <summary> Tries to parse a provided user mention string. </summary> | /// <summary> Tries to parse a provided user mention string. </summary> | ||||
| public static bool TryParseUser(string mentionText, out ulong userId) | |||||
| public static bool TryParseUser(string text, out ulong userId) | |||||
| { | { | ||||
| mentionText = mentionText.Trim(); | |||||
| if (mentionText.Length >= 3 && mentionText[0] == '<' && mentionText[1] == '@' && mentionText[mentionText.Length - 1] == '>') | |||||
| if (text.Length >= 3 && text[0] == '<' && text[1] == '@' && text[text.Length - 1] == '>') | |||||
| { | { | ||||
| if (mentionText.Length >= 4 && mentionText[2] == '!') | |||||
| mentionText = mentionText.Substring(3, mentionText.Length - 4); //<@!123> | |||||
| if (text.Length >= 4 && text[2] == '!') | |||||
| text = text.Substring(3, text.Length - 4); //<@!123> | |||||
| else | else | ||||
| mentionText = mentionText.Substring(2, mentionText.Length - 3); //<@123> | |||||
| text = text.Substring(2, text.Length - 3); //<@123> | |||||
| if (ulong.TryParse(mentionText, NumberStyles.None, CultureInfo.InvariantCulture, out userId)) | |||||
| if (ulong.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out userId)) | |||||
| return true; | return true; | ||||
| } | } | ||||
| userId = 0; | userId = 0; | ||||
| @@ -50,22 +42,21 @@ namespace Discord | |||||
| } | } | ||||
| /// <summary> Parses a provided channel mention string. </summary> | /// <summary> Parses a provided channel mention string. </summary> | ||||
| public static ulong ParseChannel(string mentionText) | |||||
| public static ulong ParseChannel(string text) | |||||
| { | { | ||||
| ulong id; | ulong id; | ||||
| if (TryParseChannel(mentionText, out id)) | |||||
| if (TryParseChannel(text, out id)) | |||||
| return id; | return id; | ||||
| throw new ArgumentException("Invalid mention format", nameof(mentionText)); | |||||
| throw new ArgumentException("Invalid mention format", nameof(text)); | |||||
| } | } | ||||
| /// <summary>Tries to parse a provided channel mention string. </summary> | /// <summary>Tries to parse a provided channel mention string. </summary> | ||||
| public static bool TryParseChannel(string mentionText, out ulong channelId) | |||||
| public static bool TryParseChannel(string text, out ulong channelId) | |||||
| { | { | ||||
| mentionText = mentionText.Trim(); | |||||
| if (mentionText.Length >= 3 && mentionText[0] == '<' && mentionText[1] == '#' && mentionText[mentionText.Length - 1] == '>') | |||||
| if (text.Length >= 3 && text[0] == '<' && text[1] == '#' && text[text.Length - 1] == '>') | |||||
| { | { | ||||
| mentionText = mentionText.Substring(2, mentionText.Length - 3); //<#123> | |||||
| text = text.Substring(2, text.Length - 3); //<#123> | |||||
| if (ulong.TryParse(mentionText, NumberStyles.None, CultureInfo.InvariantCulture, out channelId)) | |||||
| if (ulong.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out channelId)) | |||||
| return true; | return true; | ||||
| } | } | ||||
| channelId = 0; | channelId = 0; | ||||
| @@ -73,212 +64,175 @@ namespace Discord | |||||
| } | } | ||||
| /// <summary> Parses a provided role mention string. </summary> | /// <summary> Parses a provided role mention string. </summary> | ||||
| public static ulong ParseRole(string mentionText) | |||||
| public static ulong ParseRole(string text) | |||||
| { | { | ||||
| ulong id; | ulong id; | ||||
| if (TryParseRole(mentionText, out id)) | |||||
| if (TryParseRole(text, out id)) | |||||
| return id; | return id; | ||||
| throw new ArgumentException("Invalid mention format", nameof(mentionText)); | |||||
| throw new ArgumentException("Invalid mention format", nameof(text)); | |||||
| } | } | ||||
| /// <summary>Tries to parse a provided role mention string. </summary> | /// <summary>Tries to parse a provided role mention string. </summary> | ||||
| public static bool TryParseRole(string mentionText, out ulong roleId) | |||||
| public static bool TryParseRole(string text, out ulong roleId) | |||||
| { | { | ||||
| mentionText = mentionText.Trim(); | |||||
| if (mentionText.Length >= 4 && mentionText[0] == '<' && mentionText[1] == '@' && mentionText[2] == '&' && mentionText[mentionText.Length - 1] == '>') | |||||
| if (text.Length >= 4 && text[0] == '<' && text[1] == '@' && text[2] == '&' && text[text.Length - 1] == '>') | |||||
| { | { | ||||
| mentionText = mentionText.Substring(3, mentionText.Length - 4); //<@&123> | |||||
| text = text.Substring(3, text.Length - 4); //<@&123> | |||||
| if (ulong.TryParse(mentionText, NumberStyles.None, CultureInfo.InvariantCulture, out roleId)) | |||||
| if (ulong.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out roleId)) | |||||
| return true; | return true; | ||||
| } | } | ||||
| roleId = 0; | roleId = 0; | ||||
| return false; | return false; | ||||
| } | } | ||||
| internal static ImmutableArray<TUser> GetUserMentions<TUser>(string text, IMessageChannel channel, IReadOnlyCollection<TUser> mentionedUsers) | |||||
| where TUser : class, IUser | |||||
| internal static string Resolve(IMessage msg, TagHandling userHandling, TagHandling channelHandling, TagHandling roleHandling, TagHandling everyoneHandling, TagHandling emojiHandling) | |||||
| { | { | ||||
| var matches = _userRegex.Matches(text); | |||||
| var builder = ImmutableArray.CreateBuilder<TUser>(matches.Count); | |||||
| foreach (var match in matches.OfType<Match>()) | |||||
| var text = new StringBuilder(msg.Content); | |||||
| var tags = msg.Tags; | |||||
| int indexOffset = 0; | |||||
| foreach (var tag in tags) | |||||
| { | { | ||||
| ulong id; | |||||
| if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | |||||
| string newText = ""; | |||||
| switch (tag.Type) | |||||
| { | { | ||||
| 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); | |||||
| case TagType.UserMention: | |||||
| if (userHandling == TagHandling.Ignore) continue; | |||||
| newText = ResolveUserMention(tag, userHandling); | |||||
| break; | |||||
| case TagType.ChannelMention: | |||||
| if (channelHandling == TagHandling.Ignore) continue; | |||||
| newText = ResolveChannelMention(tag, channelHandling); | |||||
| break; | |||||
| case TagType.RoleMention: | |||||
| if (roleHandling == TagHandling.Ignore) continue; | |||||
| newText = ResolveRoleMention(tag, roleHandling); | |||||
| break; | |||||
| case TagType.EveryoneMention: | |||||
| if (everyoneHandling == TagHandling.Ignore) continue; | |||||
| newText = ResolveEveryoneMention(tag, everyoneHandling); | |||||
| break; | |||||
| case TagType.HereMention: | |||||
| if (everyoneHandling == TagHandling.Ignore) continue; | |||||
| newText = ResolveHereMention(tag, everyoneHandling); | |||||
| break; | |||||
| case TagType.Emoji: | |||||
| if (emojiHandling == TagHandling.Ignore) continue; | |||||
| newText = ResolveEmoji(tag, emojiHandling); | |||||
| break; | |||||
| } | } | ||||
| text.Remove(tag.Index, tag.Length); | |||||
| text.Insert(tag.Index, newText); | |||||
| indexOffset += newText.Length - tag.Length; | |||||
| } | } | ||||
| return builder.ToImmutable(); | |||||
| return text.ToString(); | |||||
| } | } | ||||
| internal static ImmutableArray<ulong> GetChannelMentions(string text, IGuild guild) | |||||
| internal static string ResolveUserMention(ITag tag, TagHandling mode) | |||||
| { | { | ||||
| var matches = _channelRegex.Matches(text); | |||||
| var builder = ImmutableArray.CreateBuilder<ulong>(matches.Count); | |||||
| foreach (var match in matches.OfType<Match>()) | |||||
| if (mode != TagHandling.Remove) | |||||
| { | { | ||||
| ulong id; | |||||
| if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | |||||
| builder.Add(id); | |||||
| var user = tag.Value as IUser; | |||||
| switch (mode) | |||||
| { | |||||
| case TagHandling.Name: | |||||
| if (user != null) | |||||
| return $"@{(user as IGuildUser)?.Nickname ?? user?.Username}"; | |||||
| else | |||||
| return $"@unknown-user"; | |||||
| case TagHandling.FullName: | |||||
| if (user != null) | |||||
| return $"@{(user as IGuildUser)?.Nickname ?? user?.Username}#{user.Discriminator}"; | |||||
| else | |||||
| return $"@unknown-user"; | |||||
| case TagHandling.Sanitize: | |||||
| return MentionUser($"{SanitizeChar}{tag.Key}"); | |||||
| } | |||||
| } | } | ||||
| return builder.ToImmutable(); | |||||
| return ""; | |||||
| } | } | ||||
| internal static ImmutableArray<TRole> GetRoleMentions<TRole>(string text, IGuild guild) | |||||
| where TRole : class, IRole | |||||
| internal static string ResolveChannelMention(ITag tag, TagHandling mode) | |||||
| { | { | ||||
| 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>()) | |||||
| if (mode != TagHandling.Remove) | |||||
| { | { | ||||
| ulong id; | |||||
| if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | |||||
| var channel = tag.Value as IChannel; | |||||
| switch (mode) | |||||
| { | { | ||||
| var role = guild.GetRole(id) as TRole; | |||||
| if (role != null) | |||||
| builder.Add(role); | |||||
| case TagHandling.Name: | |||||
| case TagHandling.FullName: | |||||
| if (channel != null) | |||||
| return $"#{channel.Name}"; | |||||
| else | |||||
| return $"#deleted-channel"; | |||||
| case TagHandling.Sanitize: | |||||
| return MentionChannel($"{SanitizeChar}{tag.Key}"); | |||||
| } | } | ||||
| } | } | ||||
| return builder.ToImmutable(); | |||||
| return ""; | |||||
| } | } | ||||
| internal static string ResolveUserMentions(string text, IMessageChannel channel, IReadOnlyCollection<IUser> mentions, UserMentionHandling mode) | |||||
| internal static string ResolveRoleMention(ITag tag, TagHandling mode) | |||||
| { | { | ||||
| if (mode == UserMentionHandling.Ignore) return text; | |||||
| return _userRegex.Replace(text, new MatchEvaluator(e => | |||||
| if (mode != TagHandling.Remove) | |||||
| { | { | ||||
| ulong id; | |||||
| if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | |||||
| var role = tag.Value as IRole; | |||||
| switch (mode) | |||||
| { | { | ||||
| 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 ""; | |||||
| } | |||||
| } | |||||
| case TagHandling.Name: | |||||
| case TagHandling.FullName: | |||||
| if (role != null) | |||||
| return $"@{role.Name}"; | |||||
| else | |||||
| return $"@deleted-role"; | |||||
| case TagHandling.Sanitize: | |||||
| return MentionRole($"{SanitizeChar}{tag.Key}"); | |||||
| } | } | ||||
| return e.Value; | |||||
| })); | |||||
| } | |||||
| return ""; | |||||
| } | } | ||||
| internal static string ResolveChannelMentions(string text, IGuild guild, ChannelMentionHandling mode) | |||||
| internal static string ResolveEveryoneMention(ITag tag, TagHandling mode) | |||||
| { | { | ||||
| if (mode == ChannelMentionHandling.Ignore) return text; | |||||
| return _channelRegex.Replace(text, new MatchEvaluator(e => | |||||
| if (mode != TagHandling.Remove) | |||||
| { | { | ||||
| ulong id; | |||||
| if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | |||||
| switch (mode) | |||||
| { | { | ||||
| 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 ""; | |||||
| } | |||||
| case TagHandling.Name: | |||||
| case TagHandling.FullName: | |||||
| return "@everyone"; | |||||
| case TagHandling.Sanitize: | |||||
| return $"@{SanitizeChar}everyone"; | |||||
| } | } | ||||
| return e.Value; | |||||
| })); | |||||
| } | |||||
| return ""; | |||||
| } | } | ||||
| internal static string ResolveRoleMentions(string text, IReadOnlyCollection<IRole> mentions, RoleMentionHandling mode) | |||||
| internal static string ResolveHereMention(ITag tag, TagHandling mode) | |||||
| { | { | ||||
| if (mode == RoleMentionHandling.Ignore) return text; | |||||
| return _roleRegex.Replace(text, new MatchEvaluator(e => | |||||
| if (mode != TagHandling.Remove) | |||||
| { | { | ||||
| ulong id; | |||||
| if (ulong.TryParse(e.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | |||||
| switch (mode) | |||||
| { | { | ||||
| 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 ""; | |||||
| } | |||||
| case TagHandling.Name: | |||||
| case TagHandling.FullName: | |||||
| return "@everyone"; | |||||
| case TagHandling.Sanitize: | |||||
| return $"@{SanitizeChar}everyone"; | |||||
| } | } | ||||
| return e.Value; | |||||
| })); | |||||
| } | |||||
| return ""; | |||||
| } | } | ||||
| internal static string ResolveEveryoneMentions(string text, EveryoneMentionHandling mode) | |||||
| internal static string ResolveEmoji(ITag tag, TagHandling mode) | |||||
| { | { | ||||
| if (mode == EveryoneMentionHandling.Ignore) return text; | |||||
| switch (mode) | |||||
| if (mode != TagHandling.Remove) | |||||
| { | { | ||||
| case EveryoneMentionHandling.Sanitize: | |||||
| return text.Replace("@everyone", $"@{SanitizeChar}everyone").Replace("@here", $"@{SanitizeChar}here"); | |||||
| case EveryoneMentionHandling.Remove: | |||||
| default: | |||||
| return text.Replace("@everyone", "").Replace("@here", ""); | |||||
| Emoji emoji = (Emoji)tag.Value; | |||||
| switch (mode) | |||||
| { | |||||
| case TagHandling.Name: | |||||
| case TagHandling.FullName: | |||||
| return $":{emoji.Name}:"; | |||||
| case TagHandling.Sanitize: | |||||
| return $"<@{SanitizeChar}everyone"; | |||||
| } | |||||
| } | } | ||||
| return ""; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -35,8 +35,7 @@ | |||||
| "System.Net.WebSockets.Client": { | "System.Net.WebSockets.Client": { | ||||
| "version": "4.0.0", | "version": "4.0.0", | ||||
| "type": "build" | "type": "build" | ||||
| }, | |||||
| "System.Text.RegularExpressions": "4.1.0" | |||||
| } | |||||
| }, | }, | ||||
| "frameworks": { | "frameworks": { | ||||
| @@ -63,13 +63,13 @@ namespace Discord.Rest | |||||
| //Messages | //Messages | ||||
| public static async Task<RestMessage> GetMessageAsync(IChannel channel, BaseDiscordClient client, | public static async Task<RestMessage> GetMessageAsync(IChannel channel, BaseDiscordClient client, | ||||
| ulong id, RequestOptions options) | |||||
| ulong id, IGuild guild, RequestOptions options) | |||||
| { | { | ||||
| var model = await client.ApiClient.GetChannelMessageAsync(channel.Id, id, options).ConfigureAwait(false); | var model = await client.ApiClient.GetChannelMessageAsync(channel.Id, id, options).ConfigureAwait(false); | ||||
| return RestMessage.Create(client, model); | |||||
| return RestMessage.Create(client, guild, model); | |||||
| } | } | ||||
| public static IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IChannel channel, BaseDiscordClient client, | public static IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IChannel channel, BaseDiscordClient client, | ||||
| ulong? fromMessageId, Direction dir, int limit, RequestOptions options) | |||||
| ulong? fromMessageId, Direction dir, int limit, IGuild guild, RequestOptions options) | |||||
| { | { | ||||
| //TODO: Test this with Around direction | //TODO: Test this with Around direction | ||||
| return new PagedAsyncEnumerable<RestMessage>( | return new PagedAsyncEnumerable<RestMessage>( | ||||
| @@ -84,7 +84,7 @@ namespace Discord.Rest | |||||
| if (info.Position != null) | if (info.Position != null) | ||||
| args.RelativeMessageId = info.Position.Value; | args.RelativeMessageId = info.Position.Value; | ||||
| var models = await client.ApiClient.GetChannelMessagesAsync(channel.Id, args, options); | var models = await client.ApiClient.GetChannelMessagesAsync(channel.Id, args, options); | ||||
| return models.Select(x => RestMessage.Create(client, x)).ToImmutableArray(); ; | |||||
| return models.Select(x => RestMessage.Create(client, guild, x)).ToImmutableArray(); ; | |||||
| }, | }, | ||||
| nextPage: (info, lastPage) => | nextPage: (info, lastPage) => | ||||
| { | { | ||||
| @@ -99,34 +99,34 @@ namespace Discord.Rest | |||||
| count: limit | count: limit | ||||
| ); | ); | ||||
| } | } | ||||
| public static async Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(IChannel channel, BaseDiscordClient client, | |||||
| RequestOptions options) | |||||
| public static async Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(IChannel channel, BaseDiscordClient client, | |||||
| IGuild guild, RequestOptions options) | |||||
| { | { | ||||
| var models = await client.ApiClient.GetPinsAsync(channel.Id, options).ConfigureAwait(false); | var models = await client.ApiClient.GetPinsAsync(channel.Id, options).ConfigureAwait(false); | ||||
| return models.Select(x => RestMessage.Create(client, x)).ToImmutableArray(); | |||||
| return models.Select(x => RestMessage.Create(client, guild, x)).ToImmutableArray(); | |||||
| } | } | ||||
| public static async Task<RestUserMessage> SendMessageAsync(IChannel channel, BaseDiscordClient client, | public static async Task<RestUserMessage> SendMessageAsync(IChannel channel, BaseDiscordClient client, | ||||
| string text, bool isTTS, RequestOptions options) | |||||
| string text, bool isTTS, IGuild guild, RequestOptions options) | |||||
| { | { | ||||
| var args = new CreateMessageParams(text) { IsTTS = isTTS }; | var args = new CreateMessageParams(text) { IsTTS = isTTS }; | ||||
| var model = await client.ApiClient.CreateMessageAsync(channel.Id, args, options).ConfigureAwait(false); | var model = await client.ApiClient.CreateMessageAsync(channel.Id, args, options).ConfigureAwait(false); | ||||
| return RestUserMessage.Create(client, model); | |||||
| return RestUserMessage.Create(client, guild, model); | |||||
| } | } | ||||
| public static Task<RestUserMessage> SendFileAsync(IChannel channel, BaseDiscordClient client, | public static Task<RestUserMessage> SendFileAsync(IChannel channel, BaseDiscordClient client, | ||||
| string filePath, string text, bool isTTS, RequestOptions options) | |||||
| string filePath, string text, bool isTTS, IGuild guild, RequestOptions options) | |||||
| { | { | ||||
| string filename = Path.GetFileName(filePath); | string filename = Path.GetFileName(filePath); | ||||
| using (var file = File.OpenRead(filePath)) | using (var file = File.OpenRead(filePath)) | ||||
| return SendFileAsync(channel, client, file, filename, text, isTTS, options); | |||||
| return SendFileAsync(channel, client, file, filename, text, isTTS, guild, options); | |||||
| } | } | ||||
| public static async Task<RestUserMessage> SendFileAsync(IChannel channel, BaseDiscordClient client, | public static async Task<RestUserMessage> SendFileAsync(IChannel channel, BaseDiscordClient client, | ||||
| Stream stream, string filename, string text, bool isTTS, RequestOptions options) | |||||
| Stream stream, string filename, string text, bool isTTS, IGuild guild, RequestOptions options) | |||||
| { | { | ||||
| var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS }; | var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS }; | ||||
| var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false); | var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false); | ||||
| return RestUserMessage.Create(client, model); | |||||
| return RestUserMessage.Create(client, guild, model); | |||||
| } | } | ||||
| public static async Task DeleteMessagesAsync(IChannel channel, BaseDiscordClient client, | public static async Task DeleteMessagesAsync(IChannel channel, BaseDiscordClient client, | ||||
| @@ -43,7 +43,9 @@ namespace Discord.Rest | |||||
| public abstract Task UpdateAsync(RequestOptions options = null); | public abstract Task UpdateAsync(RequestOptions options = null); | ||||
| //IChannel | |||||
| //IChannel | |||||
| string IChannel.Name => null; | |||||
| Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| => Task.FromResult<IUser>(null); //Overriden | => Task.FromResult<IUser>(null); //Overriden | ||||
| IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | ||||
| @@ -53,22 +53,22 @@ namespace Discord.Rest | |||||
| } | } | ||||
| public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null) | public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessageAsync(this, Discord, id, options); | |||||
| => ChannelHelper.GetMessageAsync(this, Discord, id, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, options); | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, options); | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, options); | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, null, options); | |||||
| public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | ||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); | |||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, null, options); | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, options); | |||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, null, options); | |||||
| public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); | => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); | ||||
| @@ -131,7 +131,9 @@ namespace Discord.Rest | |||||
| IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | ||||
| => EnterTypingState(options); | => EnterTypingState(options); | ||||
| //IChannel | |||||
| //IChannel | |||||
| string IChannel.Name => $"@{Recipient}"; | |||||
| Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| => Task.FromResult<IUser>(GetUser(id)); | => Task.FromResult<IUser>(GetUser(id)); | ||||
| IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | ||||
| @@ -66,22 +66,22 @@ namespace Discord.Rest | |||||
| } | } | ||||
| public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null) | public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessageAsync(this, Discord, id, options); | |||||
| => ChannelHelper.GetMessageAsync(this, Discord, id, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, options); | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, options); | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, options); | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, null, options); | |||||
| public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | ||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); | |||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, null, options); | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, options); | |||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, null, options); | |||||
| public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); | => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); | ||||
| @@ -43,22 +43,22 @@ namespace Discord.Rest | |||||
| => ChannelHelper.GetUsersAsync(this, Guild, Discord, null, null, options); | => ChannelHelper.GetUsersAsync(this, Guild, Discord, null, null, options); | ||||
| public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null) | public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessageAsync(this, Discord, id, options); | |||||
| => ChannelHelper.GetMessageAsync(this, Discord, id, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, options); | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, options); | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, options); | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, null, options); | |||||
| public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | ||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); | |||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, null, options); | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, options); | |||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, null, options); | |||||
| public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); | => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); | ||||
| @@ -2,7 +2,6 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Globalization; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -11,8 +10,6 @@ namespace Discord.Rest | |||||
| { | { | ||||
| internal static class MessageHelper | internal static class MessageHelper | ||||
| { | { | ||||
| private static readonly Regex _emojiRegex = new Regex(@"<:(.+?):(\d+?)>", RegexOptions.Compiled); | |||||
| public static async Task ModifyAsync(IMessage msg, BaseDiscordClient client, Action<ModifyMessageParams> func, | public static async Task ModifyAsync(IMessage msg, BaseDiscordClient client, Action<ModifyMessageParams> func, | ||||
| RequestOptions options) | RequestOptions options) | ||||
| { | { | ||||
| @@ -37,17 +34,94 @@ namespace Discord.Rest | |||||
| await client.ApiClient.RemovePinAsync(msg.ChannelId, msg.Id, options); | await client.ApiClient.RemovePinAsync(msg.ChannelId, msg.Id, options); | ||||
| } | } | ||||
| public static ImmutableArray<Emoji> GetEmojis(string text) | |||||
| public static ImmutableArray<ITag> ParseTags(string text, IMessageChannel channel, IGuild guild, ImmutableArray<IUser> userMentions) | |||||
| { | { | ||||
| var matches = _emojiRegex.Matches(text); | |||||
| var builder = ImmutableArray.CreateBuilder<Emoji>(matches.Count); | |||||
| foreach (var match in matches.OfType<Match>()) | |||||
| var tags = new SortedList<int, ITag>(); | |||||
| int index = 0; | |||||
| while (true) | |||||
| { | { | ||||
| index = text.IndexOf('<', index); | |||||
| if (index == -1) break; | |||||
| int endIndex = text.IndexOf('>', index + 1); | |||||
| if (endIndex == -1) break; | |||||
| string content = text.Substring(index, endIndex - index + 1); | |||||
| ulong id; | ulong id; | ||||
| if (ulong.TryParse(match.Groups[2].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | |||||
| builder.Add(new Emoji(id, match.Groups[1].Value, match.Index, match.Length)); | |||||
| if (MentionUtils.TryParseUser(content, out id)) | |||||
| { | |||||
| IUser mentionedUser = null; | |||||
| foreach (var mention in userMentions) | |||||
| { | |||||
| if (mention.Id == id) | |||||
| { | |||||
| mentionedUser = channel?.GetUserAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult(); | |||||
| if (mentionedUser == null) | |||||
| mentionedUser = mention; | |||||
| break; | |||||
| } | |||||
| } | |||||
| tags.Add(index, new Tag<IUser>(TagType.UserMention, index, content.Length, id, mentionedUser)); | |||||
| } | |||||
| else if (MentionUtils.TryParseChannel(content, out id)) | |||||
| { | |||||
| IChannel mentionedChannel = null; | |||||
| if (guild != null) | |||||
| mentionedChannel = guild.GetChannelAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult(); | |||||
| tags.Add(index, new Tag<IChannel>(TagType.ChannelMention, index, content.Length, id, mentionedChannel)); | |||||
| } | |||||
| else if (MentionUtils.TryParseRole(content, out id)) | |||||
| { | |||||
| IRole mentionedRole = null; | |||||
| if (guild != null) | |||||
| mentionedRole = guild.GetRole(id); | |||||
| tags.Add(index, new Tag<IRole>(TagType.RoleMention, index, content.Length, id, mentionedRole)); | |||||
| } | |||||
| else | |||||
| { | |||||
| Emoji emoji; | |||||
| if (Emoji.TryParse(content, out emoji)) | |||||
| tags.Add(index, new Tag<Emoji>(TagType.Emoji, index, content.Length, id, emoji)); | |||||
| } | |||||
| index = endIndex + 1; | |||||
| } | |||||
| index = 0; | |||||
| while (true) | |||||
| { | |||||
| index = text.IndexOf("@everyone", index); | |||||
| if (index == -1) break; | |||||
| tags.Add(index, new Tag<object>(TagType.EveryoneMention, index, "@everyone".Length, 0, null)); | |||||
| index++; | |||||
| } | } | ||||
| return builder.ToImmutable(); | |||||
| index = 0; | |||||
| while (true) | |||||
| { | |||||
| index = text.IndexOf("@here", index); | |||||
| if (index == -1) break; | |||||
| tags.Add(index, new Tag<object>(TagType.HereMention, index, "@here".Length, 0, null)); | |||||
| index++; | |||||
| } | |||||
| return tags.Values.ToImmutableArray(); | |||||
| } | |||||
| public static ImmutableArray<ulong> FilterTagsByKey(TagType type, ImmutableArray<ITag> tags) | |||||
| { | |||||
| return tags | |||||
| .Where(x => x.Type == type) | |||||
| .Select(x => x.Key) | |||||
| .ToImmutableArray(); | |||||
| } | |||||
| public static ImmutableArray<T> FilterTagsByValue<T>(TagType type, ImmutableArray<ITag> tags) | |||||
| { | |||||
| return tags | |||||
| .Where(x => x.Type == type) | |||||
| .Select(x => (T)x.Value) | |||||
| .Where(x => x != null) | |||||
| .ToImmutableArray(); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -8,6 +8,7 @@ namespace Discord.Rest | |||||
| { | { | ||||
| public abstract class RestMessage : RestEntity<ulong>, IMessage, IUpdateable | public abstract class RestMessage : RestEntity<ulong>, IMessage, IUpdateable | ||||
| { | { | ||||
| internal readonly IGuild _guild; | |||||
| private long _timestampTicks; | private long _timestampTicks; | ||||
| public ulong ChannelId { get; } | public ulong ChannelId { get; } | ||||
| @@ -21,25 +22,26 @@ namespace Discord.Rest | |||||
| public virtual DateTimeOffset? EditedTimestamp => null; | public virtual DateTimeOffset? EditedTimestamp => null; | ||||
| public virtual IReadOnlyCollection<Attachment> Attachments => ImmutableArray.Create<Attachment>(); | public virtual IReadOnlyCollection<Attachment> Attachments => ImmutableArray.Create<Attachment>(); | ||||
| public virtual IReadOnlyCollection<Embed> Embeds => ImmutableArray.Create<Embed>(); | public virtual IReadOnlyCollection<Embed> Embeds => ImmutableArray.Create<Embed>(); | ||||
| public virtual IReadOnlyCollection<Emoji> Emojis => ImmutableArray.Create<Emoji>(); | |||||
| public virtual IReadOnlyCollection<ulong> MentionedChannelIds => ImmutableArray.Create<ulong>(); | public virtual IReadOnlyCollection<ulong> MentionedChannelIds => ImmutableArray.Create<ulong>(); | ||||
| public virtual IReadOnlyCollection<RestRole> MentionedRoles => ImmutableArray.Create<RestRole>(); | public virtual IReadOnlyCollection<RestRole> MentionedRoles => ImmutableArray.Create<RestRole>(); | ||||
| public virtual IReadOnlyCollection<RestUser> MentionedUsers => ImmutableArray.Create<RestUser>(); | public virtual IReadOnlyCollection<RestUser> MentionedUsers => ImmutableArray.Create<RestUser>(); | ||||
| public virtual IReadOnlyCollection<ITag> Tags => ImmutableArray.Create<ITag>(); | |||||
| public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | ||||
| internal RestMessage(BaseDiscordClient discord, ulong id, ulong channelId, RestUser author) | |||||
| internal RestMessage(BaseDiscordClient discord, ulong id, ulong channelId, RestUser author, IGuild guild) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| ChannelId = channelId; | ChannelId = channelId; | ||||
| Author = author; | Author = author; | ||||
| _guild = guild; | |||||
| } | } | ||||
| internal static RestMessage Create(BaseDiscordClient discord, Model model) | |||||
| internal static RestMessage Create(BaseDiscordClient discord, IGuild guild, Model model) | |||||
| { | { | ||||
| if (model.Type == MessageType.Default) | if (model.Type == MessageType.Default) | ||||
| return RestUserMessage.Create(discord, model); | |||||
| return RestUserMessage.Create(discord, guild, model); | |||||
| else | else | ||||
| return RestSystemMessage.Create(discord, model); | |||||
| return RestSystemMessage.Create(discord, guild, model); | |||||
| } | } | ||||
| internal virtual void Update(Model model) | internal virtual void Update(Model model) | ||||
| { | { | ||||
| @@ -8,13 +8,13 @@ namespace Discord.Rest | |||||
| { | { | ||||
| public MessageType Type { get; private set; } | public MessageType Type { get; private set; } | ||||
| internal RestSystemMessage(BaseDiscordClient discord, ulong id, ulong channelId, RestUser author) | |||||
| : base(discord, id, channelId, author) | |||||
| internal RestSystemMessage(BaseDiscordClient discord, ulong id, ulong channelId, RestUser author, IGuild guild) | |||||
| : base(discord, id, channelId, author, guild) | |||||
| { | { | ||||
| } | } | ||||
| internal new static RestSystemMessage Create(BaseDiscordClient discord, Model model) | |||||
| internal new static RestSystemMessage Create(BaseDiscordClient discord, IGuild guild, Model model) | |||||
| { | { | ||||
| var entity = new RestSystemMessage(discord, model.Id, model.ChannelId, RestUser.Create(discord, model.Author.Value)); | |||||
| var entity = new RestSystemMessage(discord, model.Id, model.ChannelId, RestUser.Create(discord, model.Author.Value), guild); | |||||
| entity.Update(model); | entity.Update(model); | ||||
| return entity; | return entity; | ||||
| } | } | ||||
| @@ -3,6 +3,7 @@ using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Linq; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.Message; | using Model = Discord.API.Message; | ||||
| @@ -15,10 +16,7 @@ namespace Discord.Rest | |||||
| private long? _editedTimestampTicks; | private long? _editedTimestampTicks; | ||||
| private ImmutableArray<Attachment> _attachments; | private ImmutableArray<Attachment> _attachments; | ||||
| private ImmutableArray<Embed> _embeds; | private ImmutableArray<Embed> _embeds; | ||||
| private ImmutableArray<Emoji> _emojis; | |||||
| private ImmutableArray<ulong> _mentionedChannelIds; | |||||
| private ImmutableArray<RestRole> _mentionedRoles; | |||||
| private ImmutableArray<RestUser> _mentionedUsers; | |||||
| private ImmutableArray<ITag> _tags; | |||||
| public ulong? WebhookId { get; private set; } | public ulong? WebhookId { get; private set; } | ||||
| @@ -28,18 +26,18 @@ namespace Discord.Rest | |||||
| public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); | public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); | ||||
| public override IReadOnlyCollection<Attachment> Attachments => _attachments; | public override IReadOnlyCollection<Attachment> Attachments => _attachments; | ||||
| public override IReadOnlyCollection<Embed> Embeds => _embeds; | public override IReadOnlyCollection<Embed> Embeds => _embeds; | ||||
| public override IReadOnlyCollection<Emoji> Emojis => _emojis; | |||||
| public override IReadOnlyCollection<ulong> MentionedChannelIds => _mentionedChannelIds; | |||||
| public override IReadOnlyCollection<RestRole> MentionedRoles => _mentionedRoles; | |||||
| public override IReadOnlyCollection<RestUser> MentionedUsers => _mentionedUsers; | |||||
| public override IReadOnlyCollection<ulong> MentionedChannelIds => MessageHelper.FilterTagsByKey(TagType.ChannelMention, _tags); | |||||
| public override IReadOnlyCollection<RestRole> MentionedRoles => MessageHelper.FilterTagsByValue<RestRole>(TagType.RoleMention, _tags); | |||||
| public override IReadOnlyCollection<RestUser> MentionedUsers => MessageHelper.FilterTagsByValue<RestUser>(TagType.UserMention, _tags); | |||||
| public override IReadOnlyCollection<ITag> Tags => _tags; | |||||
| internal RestUserMessage(BaseDiscordClient discord, ulong id, ulong channelId, RestUser author) | |||||
| : base(discord, id, channelId, author) | |||||
| internal RestUserMessage(BaseDiscordClient discord, ulong id, ulong channelId, RestUser author, IGuild guild) | |||||
| : base(discord, id, channelId, author, guild) | |||||
| { | { | ||||
| } | } | ||||
| internal new static RestUserMessage Create(BaseDiscordClient discord, Model model) | |||||
| internal new static RestUserMessage Create(BaseDiscordClient discord, IGuild guild, Model model) | |||||
| { | { | ||||
| var entity = new RestUserMessage(discord, model.Id, model.ChannelId, RestUser.Create(discord, model.Author.Value)); | |||||
| var entity = new RestUserMessage(discord, model.Id, model.ChannelId, RestUser.Create(discord, model.Author.Value), guild); | |||||
| entity.Update(model); | entity.Update(model); | ||||
| return entity; | return entity; | ||||
| } | } | ||||
| @@ -87,13 +85,13 @@ namespace Discord.Rest | |||||
| _embeds = ImmutableArray.Create<Embed>(); | _embeds = ImmutableArray.Create<Embed>(); | ||||
| } | } | ||||
| ImmutableArray<RestUser> mentions = ImmutableArray.Create<RestUser>(); | |||||
| ImmutableArray<IUser> mentions = ImmutableArray.Create<IUser>(); | |||||
| if (model.Mentions.IsSpecified) | if (model.Mentions.IsSpecified) | ||||
| { | { | ||||
| var value = model.Mentions.Value; | var value = model.Mentions.Value; | ||||
| if (value.Length > 0) | if (value.Length > 0) | ||||
| { | { | ||||
| var newMentions = ImmutableArray.CreateBuilder<RestUser>(value.Length); | |||||
| var newMentions = ImmutableArray.CreateBuilder<IUser>(value.Length); | |||||
| for (int i = 0; i < value.Length; i++) | for (int i = 0; i < value.Length; i++) | ||||
| newMentions.Add(RestUser.Create(Discord, value[i])); | newMentions.Add(RestUser.Create(Discord, value[i])); | ||||
| mentions = newMentions.ToImmutable(); | mentions = newMentions.ToImmutable(); | ||||
| @@ -103,11 +101,7 @@ namespace Discord.Rest | |||||
| if (model.Content.IsSpecified) | if (model.Content.IsSpecified) | ||||
| { | { | ||||
| var text = model.Content.Value; | var text = model.Content.Value; | ||||
| _mentionedUsers = MentionUtils.GetUserMentions(text, null, mentions); | |||||
| _mentionedChannelIds = MentionUtils.GetChannelMentions(text, null); | |||||
| _mentionedRoles = MentionUtils.GetRoleMentions<RestRole>(text, null); | |||||
| _emojis = MessageHelper.GetEmojis(text); | |||||
| _tags = MessageHelper.ParseTags(text, null, _guild, mentions); | |||||
| model.Content = text; | model.Content = text; | ||||
| } | } | ||||
| } | } | ||||
| @@ -122,21 +116,9 @@ namespace Discord.Rest | |||||
| public Task UnpinAsync(RequestOptions options) | public Task UnpinAsync(RequestOptions options) | ||||
| => MessageHelper.UnpinAsync(this, Discord, options); | => MessageHelper.UnpinAsync(this, Discord, options); | ||||
| public string Resolve(UserMentionHandling userHandling = UserMentionHandling.Name, ChannelMentionHandling channelHandling = ChannelMentionHandling.Name, | |||||
| RoleMentionHandling roleHandling = RoleMentionHandling.Name, EveryoneMentionHandling everyoneHandling = EveryoneMentionHandling.Ignore) | |||||
| => Resolve(Content, userHandling, channelHandling, roleHandling, everyoneHandling); | |||||
| public string Resolve(int startIndex, int length, UserMentionHandling userHandling = UserMentionHandling.Name, ChannelMentionHandling channelHandling = ChannelMentionHandling.Name, | |||||
| RoleMentionHandling roleHandling = RoleMentionHandling.Name, EveryoneMentionHandling everyoneHandling = EveryoneMentionHandling.Ignore) | |||||
| => Resolve(Content.Substring(startIndex, length), userHandling, channelHandling, roleHandling, everyoneHandling); | |||||
| public string Resolve(string text, UserMentionHandling userHandling, ChannelMentionHandling channelHandling, | |||||
| RoleMentionHandling roleHandling, EveryoneMentionHandling 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; | |||||
| } | |||||
| public string Resolve(TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | |||||
| TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | |||||
| => MentionUtils.Resolve(this, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); | |||||
| private string DebuggerDisplay => $"{Author}: {Content} ({Id}{(Attachments.Count > 0 ? $", {Attachments.Count} Attachments" : "")})"; | private string DebuggerDisplay => $"{Author}: {Content} ({Id}{(Attachments.Count > 0 ? $", {Attachments.Count} Attachments" : "")})"; | ||||
| } | } | ||||
| @@ -38,7 +38,9 @@ namespace Discord.WebSocket | |||||
| internal SocketChannel Clone() => MemberwiseClone() as SocketChannel; | internal SocketChannel Clone() => MemberwiseClone() as SocketChannel; | ||||
| //IChannel | |||||
| //IChannel | |||||
| string IChannel.Name => null; | |||||
| Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| => Task.FromResult<IUser>(null); //Overridden | => Task.FromResult<IUser>(null); //Overridden | ||||
| IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | ||||
| @@ -9,7 +9,7 @@ namespace Discord.WebSocket | |||||
| internal static class SocketChannelHelper | internal static class SocketChannelHelper | ||||
| { | { | ||||
| public static IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(SocketChannel channel, DiscordSocketClient discord, MessageCache messages, | public static IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(SocketChannel channel, DiscordSocketClient discord, MessageCache messages, | ||||
| ulong? fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) | |||||
| ulong? fromMessageId, Direction dir, int limit, CacheMode mode, IGuild guild, RequestOptions options) | |||||
| { | { | ||||
| if (dir == Direction.Around) | if (dir == Direction.Around) | ||||
| throw new NotImplementedException(); //TODO: Impl | throw new NotImplementedException(); //TODO: Impl | ||||
| @@ -36,7 +36,7 @@ namespace Discord.WebSocket | |||||
| return result; | return result; | ||||
| //Download remaining messages | //Download remaining messages | ||||
| var downloadedMessages = ChannelHelper.GetMessagesAsync(channel, discord, cachedMessages.Min(x => x.Id), dir, limit, options); | |||||
| var downloadedMessages = ChannelHelper.GetMessagesAsync(channel, discord, cachedMessages.Min(x => x.Id), dir, limit, guild, options); | |||||
| return result.Concat(downloadedMessages); | return result.Concat(downloadedMessages); | ||||
| } | } | ||||
| else | else | ||||
| @@ -45,7 +45,7 @@ namespace Discord.WebSocket | |||||
| return result; | return result; | ||||
| //Dont use cache in this case | //Dont use cache in this case | ||||
| return ChannelHelper.GetMessagesAsync(channel, discord, fromMessageId, dir, limit, options); | |||||
| return ChannelHelper.GetMessagesAsync(channel, discord, fromMessageId, dir, limit, guild, options); | |||||
| } | } | ||||
| } | } | ||||
| public static IReadOnlyCollection<SocketMessage> GetCachedMessages(SocketChannel channel, DiscordSocketClient discord, MessageCache messages, | public static IReadOnlyCollection<SocketMessage> GetCachedMessages(SocketChannel channel, DiscordSocketClient discord, MessageCache messages, | ||||
| @@ -48,15 +48,15 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| IMessage msg = _messages?.Get(id); | IMessage msg = _messages?.Get(id); | ||||
| if (msg == null) | if (msg == null) | ||||
| msg = await ChannelHelper.GetMessageAsync(this, Discord, id, options); | |||||
| msg = await ChannelHelper.GetMessageAsync(this, Discord, id, null, options); | |||||
| return msg; | return msg; | ||||
| } | } | ||||
| public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, CacheMode.AllowDownload, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, CacheMode.AllowDownload, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, CacheMode.AllowDownload, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, CacheMode.AllowDownload, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, CacheMode.AllowDownload, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, CacheMode.AllowDownload, null, options); | |||||
| public IReadOnlyCollection<SocketMessage> GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch) | public IReadOnlyCollection<SocketMessage> GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, null, Direction.Before, limit); | => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, null, Direction.Before, limit); | ||||
| public IReadOnlyCollection<SocketMessage> GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | public IReadOnlyCollection<SocketMessage> GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| @@ -64,14 +64,14 @@ namespace Discord.WebSocket | |||||
| public IReadOnlyCollection<SocketMessage> GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | public IReadOnlyCollection<SocketMessage> GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessage.Id, dir, limit); | => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessage.Id, dir, limit); | ||||
| public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | ||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); | |||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, null, options); | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, options); | |||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, null, options); | |||||
| public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); | => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); | ||||
| @@ -123,11 +123,11 @@ namespace Discord.WebSocket | |||||
| return GetCachedMessage(id); | return GetCachedMessage(id); | ||||
| } | } | ||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, mode, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, mode, null, options); | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, mode, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, mode, null, options); | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, null, options); | |||||
| async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | ||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | ||||
| @@ -140,6 +140,8 @@ namespace Discord.WebSocket | |||||
| => EnterTypingState(options); | => EnterTypingState(options); | ||||
| //IChannel | //IChannel | ||||
| string IChannel.Name => $"@{Recipient}"; | |||||
| Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| => Task.FromResult<IUser>(GetUser(id)); | => Task.FromResult<IUser>(GetUser(id)); | ||||
| IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | ||||
| @@ -71,15 +71,15 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| IMessage msg = _messages?.Get(id); | IMessage msg = _messages?.Get(id); | ||||
| if (msg == null) | if (msg == null) | ||||
| msg = await ChannelHelper.GetMessageAsync(this, Discord, id, options); | |||||
| msg = await ChannelHelper.GetMessageAsync(this, Discord, id, null, options); | |||||
| return msg; | return msg; | ||||
| } | } | ||||
| public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, CacheMode.AllowDownload, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, CacheMode.AllowDownload, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, CacheMode.AllowDownload, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, CacheMode.AllowDownload, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, CacheMode.AllowDownload, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, CacheMode.AllowDownload, null, options); | |||||
| public IReadOnlyCollection<SocketMessage> GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch) | public IReadOnlyCollection<SocketMessage> GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, null, Direction.Before, limit); | => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, null, Direction.Before, limit); | ||||
| public IReadOnlyCollection<SocketMessage> GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | public IReadOnlyCollection<SocketMessage> GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| @@ -87,14 +87,14 @@ namespace Discord.WebSocket | |||||
| public IReadOnlyCollection<SocketMessage> GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | public IReadOnlyCollection<SocketMessage> GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessage.Id, dir, limit); | => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessage.Id, dir, limit); | ||||
| public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | ||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); | |||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, null, options); | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, options); | |||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, null, options); | |||||
| public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); | => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); | ||||
| @@ -186,11 +186,11 @@ namespace Discord.WebSocket | |||||
| return GetCachedMessage(id); | return GetCachedMessage(id); | ||||
| } | } | ||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, mode, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, mode, null, options); | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, mode, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, mode, null, options); | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, null, options); | |||||
| async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | ||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | ||||
| @@ -54,15 +54,15 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| IMessage msg = _messages?.Get(id); | IMessage msg = _messages?.Get(id); | ||||
| if (msg == null) | if (msg == null) | ||||
| msg = await ChannelHelper.GetMessageAsync(this, Discord, id, options); | |||||
| msg = await ChannelHelper.GetMessageAsync(this, Discord, id, Guild, options); | |||||
| return msg; | return msg; | ||||
| } | } | ||||
| public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, CacheMode.AllowDownload, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, CacheMode.AllowDownload, Guild, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, CacheMode.AllowDownload, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, CacheMode.AllowDownload, Guild, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, CacheMode.AllowDownload, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, CacheMode.AllowDownload, Guild, options); | |||||
| public IReadOnlyCollection<SocketMessage> GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch) | public IReadOnlyCollection<SocketMessage> GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, null, Direction.Before, limit); | => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, null, Direction.Before, limit); | ||||
| public IReadOnlyCollection<SocketMessage> GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | public IReadOnlyCollection<SocketMessage> GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| @@ -70,14 +70,14 @@ namespace Discord.WebSocket | |||||
| public IReadOnlyCollection<SocketMessage> GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | public IReadOnlyCollection<SocketMessage> GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessage.Id, dir, limit); | => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessage.Id, dir, limit); | ||||
| public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | ||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); | |||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, Guild, options); | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, options); | |||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, Guild, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, Guild, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | ||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, Guild, options); | |||||
| public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); | => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); | ||||
| @@ -124,11 +124,11 @@ namespace Discord.WebSocket | |||||
| return GetCachedMessage(id); | return GetCachedMessage(id); | ||||
| } | } | ||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, mode, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, mode, Guild, options); | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, mode, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, mode, Guild, options); | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, options); | |||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, Guild, options); | |||||
| async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | ||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | ||||
| @@ -22,10 +22,10 @@ namespace Discord.WebSocket | |||||
| public virtual IReadOnlyCollection<Attachment> Attachments => ImmutableArray.Create<Attachment>(); | public virtual IReadOnlyCollection<Attachment> Attachments => ImmutableArray.Create<Attachment>(); | ||||
| public virtual IReadOnlyCollection<Embed> Embeds => ImmutableArray.Create<Embed>(); | public virtual IReadOnlyCollection<Embed> Embeds => ImmutableArray.Create<Embed>(); | ||||
| public virtual IReadOnlyCollection<Emoji> Emojis => ImmutableArray.Create<Emoji>(); | |||||
| public virtual IReadOnlyCollection<SocketGuildChannel> MentionedChannels => ImmutableArray.Create<SocketGuildChannel>(); | public virtual IReadOnlyCollection<SocketGuildChannel> MentionedChannels => ImmutableArray.Create<SocketGuildChannel>(); | ||||
| public virtual IReadOnlyCollection<SocketRole> MentionedRoles => ImmutableArray.Create<SocketRole>(); | public virtual IReadOnlyCollection<SocketRole> MentionedRoles => ImmutableArray.Create<SocketRole>(); | ||||
| public virtual IReadOnlyCollection<SocketUser> MentionedUsers => ImmutableArray.Create<SocketUser>(); | public virtual IReadOnlyCollection<SocketUser> MentionedUsers => ImmutableArray.Create<SocketUser>(); | ||||
| public virtual IReadOnlyCollection<ITag> Tags => ImmutableArray.Create<ITag>(); | |||||
| public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | ||||
| @@ -4,7 +4,6 @@ using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Linq; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.Message; | using Model = Discord.API.Message; | ||||
| @@ -17,10 +16,7 @@ namespace Discord.WebSocket | |||||
| private long? _editedTimestampTicks; | private long? _editedTimestampTicks; | ||||
| private ImmutableArray<Attachment> _attachments; | private ImmutableArray<Attachment> _attachments; | ||||
| private ImmutableArray<Embed> _embeds; | private ImmutableArray<Embed> _embeds; | ||||
| private ImmutableArray<Emoji> _emojis; | |||||
| private ImmutableArray<SocketGuildChannel> _mentionedChannels; | |||||
| private ImmutableArray<SocketRole> _mentionedRoles; | |||||
| private ImmutableArray<SocketUser> _mentionedUsers; | |||||
| private ImmutableArray<ITag> _tags; | |||||
| public ulong? WebhookId { get; private set; } | public ulong? WebhookId { get; private set; } | ||||
| @@ -31,10 +27,10 @@ namespace Discord.WebSocket | |||||
| public override IReadOnlyCollection<Attachment> Attachments => _attachments; | public override IReadOnlyCollection<Attachment> Attachments => _attachments; | ||||
| public override IReadOnlyCollection<Embed> Embeds => _embeds; | public override IReadOnlyCollection<Embed> Embeds => _embeds; | ||||
| public override IReadOnlyCollection<Emoji> Emojis => _emojis; | |||||
| public override IReadOnlyCollection<SocketGuildChannel> MentionedChannels => _mentionedChannels; | |||||
| public override IReadOnlyCollection<SocketRole> MentionedRoles => _mentionedRoles; | |||||
| public override IReadOnlyCollection<SocketUser> MentionedUsers => _mentionedUsers; | |||||
| public override IReadOnlyCollection<ITag> Tags => _tags; | |||||
| public override IReadOnlyCollection<SocketGuildChannel> MentionedChannels => MessageHelper.FilterTagsByValue<SocketGuildChannel>(TagType.ChannelMention, _tags); | |||||
| public override IReadOnlyCollection<SocketRole> MentionedRoles => MessageHelper.FilterTagsByValue<SocketRole>(TagType.RoleMention, _tags); | |||||
| public override IReadOnlyCollection<SocketUser> MentionedUsers => MessageHelper.FilterTagsByValue<SocketUser>(TagType.UserMention, _tags); | |||||
| internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author) | internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author) | ||||
| : base(discord, id, channel, author) | : base(discord, id, channel, author) | ||||
| @@ -90,13 +86,13 @@ namespace Discord.WebSocket | |||||
| _embeds = ImmutableArray.Create<Embed>(); | _embeds = ImmutableArray.Create<Embed>(); | ||||
| } | } | ||||
| ImmutableArray<SocketUser> mentions = ImmutableArray.Create<SocketUser>(); | |||||
| ImmutableArray<IUser> mentions = ImmutableArray.Create<IUser>(); | |||||
| if (model.Mentions.IsSpecified) | if (model.Mentions.IsSpecified) | ||||
| { | { | ||||
| var value = model.Mentions.Value; | var value = model.Mentions.Value; | ||||
| if (value.Length > 0) | if (value.Length > 0) | ||||
| { | { | ||||
| var newMentions = ImmutableArray.CreateBuilder<SocketUser>(value.Length); | |||||
| var newMentions = ImmutableArray.CreateBuilder<IUser>(value.Length); | |||||
| for (int i = 0; i < value.Length; i++) | for (int i = 0; i < value.Length; i++) | ||||
| newMentions.Add(SocketSimpleUser.Create(Discord, Discord.State, value[i])); | newMentions.Add(SocketSimpleUser.Create(Discord, Discord.State, value[i])); | ||||
| mentions = newMentions.ToImmutable(); | mentions = newMentions.ToImmutable(); | ||||
| @@ -107,13 +103,7 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| var text = model.Content.Value; | var text = model.Content.Value; | ||||
| var guild = (Channel as SocketGuildChannel)?.Guild; | var guild = (Channel as SocketGuildChannel)?.Guild; | ||||
| _mentionedUsers = MentionUtils.GetUserMentions(text, Channel, mentions); | |||||
| _mentionedChannels = MentionUtils.GetChannelMentions(text, guild) | |||||
| .Select(x => guild?.GetChannel(x)) | |||||
| .Where(x => x != null).ToImmutableArray(); | |||||
| _mentionedRoles = MentionUtils.GetRoleMentions<SocketRole>(text, guild); | |||||
| _emojis = MessageHelper.GetEmojis(text); | |||||
| _tags = MessageHelper.ParseTags(text, Channel, guild, mentions); | |||||
| model.Content = text; | model.Content = text; | ||||
| } | } | ||||
| } | } | ||||
| @@ -128,21 +118,9 @@ namespace Discord.WebSocket | |||||
| public Task UnpinAsync(RequestOptions options = null) | public Task UnpinAsync(RequestOptions options = null) | ||||
| => MessageHelper.UnpinAsync(this, Discord, options); | => MessageHelper.UnpinAsync(this, Discord, options); | ||||
| public string Resolve(UserMentionHandling userHandling = UserMentionHandling.Name, ChannelMentionHandling channelHandling = ChannelMentionHandling.Name, | |||||
| RoleMentionHandling roleHandling = RoleMentionHandling.Name, EveryoneMentionHandling everyoneHandling = EveryoneMentionHandling.Ignore) | |||||
| => Resolve(Content, userHandling, channelHandling, roleHandling, everyoneHandling); | |||||
| public string Resolve(int startIndex, int length, UserMentionHandling userHandling = UserMentionHandling.Name, ChannelMentionHandling channelHandling = ChannelMentionHandling.Name, | |||||
| RoleMentionHandling roleHandling = RoleMentionHandling.Name, EveryoneMentionHandling everyoneHandling = EveryoneMentionHandling.Ignore) | |||||
| => Resolve(Content.Substring(startIndex, length), userHandling, channelHandling, roleHandling, everyoneHandling); | |||||
| public string Resolve(string text, UserMentionHandling userHandling, ChannelMentionHandling channelHandling, | |||||
| RoleMentionHandling roleHandling, EveryoneMentionHandling 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; | |||||
| } | |||||
| public string Resolve(TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | |||||
| TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | |||||
| => MentionUtils.Resolve(this, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); | |||||
| private string DebuggerDisplay => $"{Author}: {Content} ({Id}{(Attachments.Count > 0 ? $", {Attachments.Count} Attachments" : "")})"; | private string DebuggerDisplay => $"{Author}: {Content} ({Id}{(Attachments.Count > 0 ? $", {Attachments.Count} Attachments" : "")})"; | ||||
| internal new SocketUserMessage Clone() => MemberwiseClone() as SocketUserMessage; | internal new SocketUserMessage Clone() => MemberwiseClone() as SocketUserMessage; | ||||