@@ -6,6 +6,6 @@ namespace Discord.Commands | |||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)] | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)] | ||||
public abstract class PreconditionAttribute : Attribute | public abstract class PreconditionAttribute : Attribute | ||||
{ | { | ||||
public abstract Task<PreconditionResult> CheckPermissions(IMessage context, Command executingCommand, object moduleInstance); | |||||
public abstract Task<PreconditionResult> CheckPermissions(IUserMessage context, Command executingCommand, object moduleInstance); | |||||
} | } | ||||
} | } |
@@ -21,7 +21,7 @@ namespace Discord.Commands | |||||
Contexts = contexts; | Contexts = contexts; | ||||
} | } | ||||
public override Task<PreconditionResult> CheckPermissions(IMessage context, Command executingCommand, object moduleInstance) | |||||
public override Task<PreconditionResult> CheckPermissions(IUserMessage context, Command executingCommand, object moduleInstance) | |||||
{ | { | ||||
bool isValid = false; | bool isValid = false; | ||||
@@ -20,7 +20,7 @@ namespace Discord.Commands | |||||
GuildPermission = null; | GuildPermission = null; | ||||
} | } | ||||
public override Task<PreconditionResult> CheckPermissions(IMessage context, Command executingCommand, object moduleInstance) | |||||
public override Task<PreconditionResult> CheckPermissions(IUserMessage context, Command executingCommand, object moduleInstance) | |||||
{ | { | ||||
var guildUser = context.Author as IGuildUser; | var guildUser = context.Author as IGuildUser; | ||||
@@ -16,7 +16,7 @@ namespace Discord.Commands | |||||
private static readonly ConcurrentDictionary<Type, Func<IEnumerable<object>, object>> _arrayConverters = new ConcurrentDictionary<Type, Func<IEnumerable<object>, object>>(); | private static readonly ConcurrentDictionary<Type, Func<IEnumerable<object>, object>> _arrayConverters = new ConcurrentDictionary<Type, Func<IEnumerable<object>, object>>(); | ||||
private readonly object _instance; | private readonly object _instance; | ||||
private readonly Func<IMessage, IReadOnlyList<object>, Task> _action; | |||||
private readonly Func<IUserMessage, IReadOnlyList<object>, Task> _action; | |||||
public MethodInfo Source { get; } | public MethodInfo Source { get; } | ||||
public Module Module { get; } | public Module Module { get; } | ||||
@@ -66,7 +66,7 @@ namespace Discord.Commands | |||||
_action = BuildAction(source); | _action = BuildAction(source); | ||||
} | } | ||||
public async Task<PreconditionResult> CheckPreconditions(IMessage context) | |||||
public async Task<PreconditionResult> CheckPreconditions(IUserMessage context) | |||||
{ | { | ||||
foreach (PreconditionAttribute precondition in Module.Preconditions) | foreach (PreconditionAttribute precondition in Module.Preconditions) | ||||
{ | { | ||||
@@ -85,7 +85,7 @@ namespace Discord.Commands | |||||
return PreconditionResult.FromSuccess(); | return PreconditionResult.FromSuccess(); | ||||
} | } | ||||
public async Task<ParseResult> Parse(IMessage msg, SearchResult searchResult, PreconditionResult? preconditionResult = null) | |||||
public async Task<ParseResult> Parse(IUserMessage context, SearchResult searchResult, PreconditionResult? preconditionResult = null) | |||||
{ | { | ||||
if (!searchResult.IsSuccess) | if (!searchResult.IsSuccess) | ||||
return ParseResult.FromError(searchResult); | return ParseResult.FromError(searchResult); | ||||
@@ -104,9 +104,9 @@ namespace Discord.Commands | |||||
input = input.Substring(matchingAlias.Length); | input = input.Substring(matchingAlias.Length); | ||||
return await CommandParser.ParseArgs(this, msg, input, 0).ConfigureAwait(false); | |||||
return await CommandParser.ParseArgs(this, context, input, 0).ConfigureAwait(false); | |||||
} | } | ||||
public Task<ExecuteResult> Execute(IMessage msg, ParseResult parseResult) | |||||
public Task<ExecuteResult> Execute(IUserMessage context, ParseResult parseResult) | |||||
{ | { | ||||
if (!parseResult.IsSuccess) | if (!parseResult.IsSuccess) | ||||
return Task.FromResult(ExecuteResult.FromError(parseResult)); | return Task.FromResult(ExecuteResult.FromError(parseResult)); | ||||
@@ -127,13 +127,13 @@ namespace Discord.Commands | |||||
paramList[i] = parseResult.ParamValues[i].Values.First().Value; | paramList[i] = parseResult.ParamValues[i].Values.First().Value; | ||||
} | } | ||||
return Execute(msg, argList, paramList); | |||||
return Execute(context, argList, paramList); | |||||
} | } | ||||
public async Task<ExecuteResult> Execute(IMessage msg, IEnumerable<object> argList, IEnumerable<object> paramList) | |||||
public async Task<ExecuteResult> Execute(IUserMessage context, IEnumerable<object> argList, IEnumerable<object> paramList) | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
await _action.Invoke(msg, GenerateArgs(argList, paramList)).ConfigureAwait(false);//Note: This code may need context | |||||
await _action.Invoke(context, GenerateArgs(argList, paramList)).ConfigureAwait(false);//Note: This code may need context | |||||
return ExecuteResult.FromSuccess(); | return ExecuteResult.FromSuccess(); | ||||
} | } | ||||
catch (Exception ex) | catch (Exception ex) | ||||
@@ -150,8 +150,8 @@ namespace Discord.Commands | |||||
private IReadOnlyList<CommandParameter> BuildParameters(MethodInfo methodInfo) | private IReadOnlyList<CommandParameter> BuildParameters(MethodInfo methodInfo) | ||||
{ | { | ||||
var parameters = methodInfo.GetParameters(); | var parameters = methodInfo.GetParameters(); | ||||
if (parameters.Length == 0 || parameters[0].ParameterType != typeof(IMessage)) | |||||
throw new InvalidOperationException("The first parameter of a command must be IMessage."); | |||||
if (parameters.Length == 0 || parameters[0].ParameterType != typeof(IUserMessage)) | |||||
throw new InvalidOperationException($"The first parameter of a command must be {nameof(IUserMessage)}."); | |||||
var paramBuilder = ImmutableArray.CreateBuilder<CommandParameter>(parameters.Length - 1); | var paramBuilder = ImmutableArray.CreateBuilder<CommandParameter>(parameters.Length - 1); | ||||
for (int i = 1; i < parameters.Length; i++) | for (int i = 1; i < parameters.Length; i++) | ||||
@@ -190,7 +190,7 @@ namespace Discord.Commands | |||||
} | } | ||||
return paramBuilder.ToImmutable(); | return paramBuilder.ToImmutable(); | ||||
} | } | ||||
private Func<IMessage, IReadOnlyList<object>, Task> BuildAction(MethodInfo methodInfo) | |||||
private Func<IUserMessage, IReadOnlyList<object>, Task> BuildAction(MethodInfo methodInfo) | |||||
{ | { | ||||
if (methodInfo.ReturnType != typeof(Task)) | if (methodInfo.ReturnType != typeof(Task)) | ||||
throw new InvalidOperationException("Commands must return a non-generic Task."); | throw new InvalidOperationException("Commands must return a non-generic Task."); | ||||
@@ -32,7 +32,7 @@ namespace Discord.Commands | |||||
DefaultValue = defaultValue; | DefaultValue = defaultValue; | ||||
} | } | ||||
public async Task<TypeReaderResult> Parse(IMessage context, string input) | |||||
public async Task<TypeReaderResult> Parse(IUserMessage context, string input) | |||||
{ | { | ||||
return await _reader.Read(context, input).ConfigureAwait(false); | return await _reader.Read(context, input).ConfigureAwait(false); | ||||
} | } | ||||
@@ -13,7 +13,7 @@ namespace Discord.Commands | |||||
QuotedParameter | QuotedParameter | ||||
} | } | ||||
public static async Task<ParseResult> ParseArgs(Command command, IMessage context, string input, int startPos) | |||||
public static async Task<ParseResult> ParseArgs(Command command, IUserMessage context, string input, int startPos) | |||||
{ | { | ||||
CommandParameter curParam = null; | CommandParameter curParam = null; | ||||
StringBuilder argBuilder = new StringBuilder(input.Length); | StringBuilder argBuilder = new StringBuilder(input.Length); | ||||
@@ -41,8 +41,9 @@ namespace Discord.Commands | |||||
[typeof(DateTime)] = new SimpleTypeReader<DateTime>(), | [typeof(DateTime)] = new SimpleTypeReader<DateTime>(), | ||||
[typeof(DateTimeOffset)] = new SimpleTypeReader<DateTimeOffset>(), | [typeof(DateTimeOffset)] = new SimpleTypeReader<DateTimeOffset>(), | ||||
[typeof(IMessage)] = new MessageTypeReader(), | |||||
[typeof(IMessage)] = new MessageTypeReader<IMessage>(), | |||||
[typeof(IUserMessage)] = new MessageTypeReader<IUserMessage>(), | |||||
//[typeof(ISystemMessage)] = new MessageTypeReader<ISystemMessage>(), | |||||
[typeof(IChannel)] = new ChannelTypeReader<IChannel>(), | [typeof(IChannel)] = new ChannelTypeReader<IChannel>(), | ||||
[typeof(IDMChannel)] = new ChannelTypeReader<IDMChannel>(), | [typeof(IDMChannel)] = new ChannelTypeReader<IDMChannel>(), | ||||
[typeof(IGroupChannel)] = new ChannelTypeReader<IGroupChannel>(), | [typeof(IGroupChannel)] = new ChannelTypeReader<IGroupChannel>(), | ||||
@@ -175,8 +176,8 @@ namespace Discord.Commands | |||||
return false; | return false; | ||||
} | } | ||||
public SearchResult Search(IMessage message, int argPos) => Search(message, message.Content.Substring(argPos)); | |||||
public SearchResult Search(IMessage message, string input) | |||||
public SearchResult Search(IUserMessage message, int argPos) => Search(message, message.Content.Substring(argPos)); | |||||
public SearchResult Search(IUserMessage message, string input) | |||||
{ | { | ||||
string lowerInput = input.ToLowerInvariant(); | string lowerInput = input.ToLowerInvariant(); | ||||
var matches = _map.GetCommands(input).ToImmutableArray(); | var matches = _map.GetCommands(input).ToImmutableArray(); | ||||
@@ -187,9 +188,9 @@ namespace Discord.Commands | |||||
return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command."); | return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command."); | ||||
} | } | ||||
public Task<IResult> Execute(IMessage message, int argPos, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) | |||||
public Task<IResult> Execute(IUserMessage message, int argPos, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) | |||||
=> Execute(message, message.Content.Substring(argPos), multiMatchHandling); | => Execute(message, message.Content.Substring(argPos), multiMatchHandling); | ||||
public async Task<IResult> Execute(IMessage message, string input, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) | |||||
public async Task<IResult> Execute(IUserMessage message, string input, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) | |||||
{ | { | ||||
var searchResult = Search(message, input); | var searchResult = Search(message, input); | ||||
if (!searchResult.IsSuccess) | if (!searchResult.IsSuccess) | ||||
@@ -2,7 +2,7 @@ | |||||
{ | { | ||||
public static class MessageExtensions | public static class MessageExtensions | ||||
{ | { | ||||
public static bool HasCharPrefix(this IMessage msg, char c, ref int argPos) | |||||
public static bool HasCharPrefix(this IUserMessage msg, char c, ref int argPos) | |||||
{ | { | ||||
var text = msg.Content; | var text = msg.Content; | ||||
if (text.Length > 0 && text[0] == c) | if (text.Length > 0 && text[0] == c) | ||||
@@ -12,7 +12,7 @@ | |||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
public static bool HasStringPrefix(this IMessage msg, string str, ref int argPos) | |||||
public static bool HasStringPrefix(this IUserMessage msg, string str, ref int argPos) | |||||
{ | { | ||||
var text = msg.Content; | var text = msg.Content; | ||||
if (text.StartsWith(str)) | if (text.StartsWith(str)) | ||||
@@ -22,7 +22,7 @@ | |||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
public static bool HasMentionPrefix(this IMessage msg, IUser user, ref int argPos) | |||||
public static bool HasMentionPrefix(this IUserMessage msg, IUser user, ref int argPos) | |||||
{ | { | ||||
var text = msg.Content; | var text = msg.Content; | ||||
if (text.Length <= 3 || text[0] != '<' || text[1] != '@') return false; | if (text.Length <= 3 || text[0] != '<' || text[1] != '@') return false; | ||||
@@ -9,7 +9,7 @@ namespace Discord.Commands | |||||
internal class ChannelTypeReader<T> : TypeReader | internal class ChannelTypeReader<T> : TypeReader | ||||
where T : class, IChannel | where T : class, IChannel | ||||
{ | { | ||||
public override async Task<TypeReaderResult> Read(IMessage context, string input) | |||||
public override async Task<TypeReaderResult> Read(IUserMessage context, string input) | |||||
{ | { | ||||
var guild = (context.Channel as IGuildChannel)?.Guild; | var guild = (context.Channel as IGuildChannel)?.Guild; | ||||
@@ -42,7 +42,7 @@ namespace Discord.Commands | |||||
_enumsByValue = byValueBuilder.ToImmutable(); | _enumsByValue = byValueBuilder.ToImmutable(); | ||||
} | } | ||||
public override Task<TypeReaderResult> Read(IMessage context, string input) | |||||
public override Task<TypeReaderResult> Read(IUserMessage context, string input) | |||||
{ | { | ||||
T baseValue; | T baseValue; | ||||
object enumValue; | object enumValue; | ||||
@@ -3,16 +3,17 @@ using System.Threading.Tasks; | |||||
namespace Discord.Commands | namespace Discord.Commands | ||||
{ | { | ||||
internal class MessageTypeReader : TypeReader | |||||
internal class MessageTypeReader<T> : TypeReader | |||||
where T : class, IMessage | |||||
{ | { | ||||
public override Task<TypeReaderResult> Read(IMessage context, string input) | |||||
public override Task<TypeReaderResult> Read(IUserMessage context, string input) | |||||
{ | { | ||||
ulong id; | ulong id; | ||||
//By Id (1.0) | //By Id (1.0) | ||||
if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out id)) | ||||
{ | { | ||||
var msg = context.Channel.GetCachedMessage(id); | |||||
var msg = context.Channel.GetCachedMessage(id) as T; | |||||
if (msg != null) | if (msg != null) | ||||
return Task.FromResult(TypeReaderResult.FromSuccess(msg)); | return Task.FromResult(TypeReaderResult.FromSuccess(msg)); | ||||
} | } | ||||
@@ -9,7 +9,7 @@ namespace Discord.Commands | |||||
internal class RoleTypeReader<T> : TypeReader | internal class RoleTypeReader<T> : TypeReader | ||||
where T : class, IRole | where T : class, IRole | ||||
{ | { | ||||
public override Task<TypeReaderResult> Read(IMessage context, string input) | |||||
public override Task<TypeReaderResult> Read(IUserMessage context, string input) | |||||
{ | { | ||||
var guild = (context.Channel as IGuildChannel)?.Guild; | var guild = (context.Channel as IGuildChannel)?.Guild; | ||||
ulong id; | ulong id; | ||||
@@ -11,7 +11,7 @@ namespace Discord.Commands | |||||
_tryParse = PrimitiveParsers.Get<T>(); | _tryParse = PrimitiveParsers.Get<T>(); | ||||
} | } | ||||
public override Task<TypeReaderResult> Read(IMessage context, string input) | |||||
public override Task<TypeReaderResult> Read(IUserMessage context, string input) | |||||
{ | { | ||||
T value; | T value; | ||||
if (_tryParse(input, out value)) | if (_tryParse(input, out value)) | ||||
@@ -4,6 +4,6 @@ namespace Discord.Commands | |||||
{ | { | ||||
public abstract class TypeReader | public abstract class TypeReader | ||||
{ | { | ||||
public abstract Task<TypeReaderResult> Read(IMessage context, string input); | |||||
public abstract Task<TypeReaderResult> Read(IUserMessage context, string input); | |||||
} | } | ||||
} | } |
@@ -9,7 +9,7 @@ namespace Discord.Commands | |||||
internal class UserTypeReader<T> : TypeReader | internal class UserTypeReader<T> : TypeReader | ||||
where T : class, IUser | where T : class, IUser | ||||
{ | { | ||||
public override async Task<TypeReaderResult> Read(IMessage context, string input) | |||||
public override async Task<TypeReaderResult> Read(IUserMessage context, string input) | |||||
{ | { | ||||
var results = new Dictionary<ulong, TypeReaderValue>(); | var results = new Dictionary<ulong, TypeReaderValue>(); | ||||
var guild = (context.Channel as IGuildChannel)?.Guild; | var guild = (context.Channel as IGuildChannel)?.Guild; | ||||
@@ -1,6 +1,4 @@ | |||||
#pragma warning disable CS1591 | #pragma warning disable CS1591 | ||||
using Discord.Rest; | |||||
namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
{ | { | ||||
public class GetChannelMessagesParams | public class GetChannelMessagesParams | ||||
@@ -10,11 +10,11 @@ namespace Discord | |||||
IReadOnlyCollection<IMessage> CachedMessages { get; } | IReadOnlyCollection<IMessage> CachedMessages { get; } | ||||
/// <summary> Sends a message to this message channel. </summary> | /// <summary> Sends a message to this message channel. </summary> | ||||
Task<IMessage> SendMessageAsync(string text, bool isTTS = false); | |||||
Task<IUserMessage> SendMessageAsync(string text, bool isTTS = false); | |||||
/// <summary> Sends a file to this text channel, with an optional caption. </summary> | /// <summary> Sends a file to this text channel, with an optional caption. </summary> | ||||
Task<IMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false); | |||||
Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false); | |||||
/// <summary> Sends a file to this text channel, with an optional caption. </summary> | /// <summary> Sends a file to this text channel, with an optional caption. </summary> | ||||
Task<IMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false); | |||||
Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false); | |||||
/// <summary> Gets a message from this message channel with the given id, or null if not found. </summary> | /// <summary> Gets a message from this message channel with the given id, or null if not found. </summary> | ||||
Task<IMessage> GetMessageAsync(ulong id); | Task<IMessage> GetMessageAsync(ulong id); | ||||
/// <summary> Gets the message from this channel's cache with the given id, or null if not found. </summary> | /// <summary> Gets the message from this channel's cache with the given id, or null if not found. </summary> | ||||
@@ -1,14 +1,10 @@ | |||||
using System; | using System; | ||||
using System.Threading.Tasks; | |||||
using Discord.API.Rest; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
public interface IMessage : IDeletable, ISnowflakeEntity, IUpdateable | |||||
public interface IMessage : ISnowflakeEntity, IUpdateable | |||||
{ | { | ||||
/// <summary> Gets the time of this message's last edit, if any. </summary> | |||||
DateTimeOffset? EditedTimestamp { get; } | |||||
/// <summary> Returns true if this message was sent as a text-to-speech message. </summary> | /// <summary> Returns true if this message was sent as a text-to-speech message. </summary> | ||||
bool IsTTS { get; } | bool IsTTS { get; } | ||||
/// <summary> Returns true if this message was added to its channel's pinned messages. </summary> | /// <summary> Returns true if this message was added to its channel's pinned messages. </summary> | ||||
@@ -17,13 +13,14 @@ namespace Discord | |||||
string Content { get; } | string Content { get; } | ||||
/// <summary> Gets the time this message was sent. </summary> | /// <summary> Gets the time this message was sent. </summary> | ||||
DateTimeOffset Timestamp { get; } | DateTimeOffset Timestamp { get; } | ||||
/// <summary> Gets the type of this message. </summary> | |||||
MessageType Type { get; } | |||||
/// <summary> Gets the time of this message's last edit, if any. </summary> | |||||
DateTimeOffset? EditedTimestamp { get; } | |||||
/// <summary> Gets the channel this message was sent to. </summary> | /// <summary> Gets the channel this message was sent to. </summary> | ||||
IMessageChannel Channel { get; } | IMessageChannel Channel { get; } | ||||
/// <summary> Gets the author of this message. </summary> | /// <summary> Gets the author of this message. </summary> | ||||
IUser Author { get; } | IUser Author { get; } | ||||
/// <summary> Returns a collection of all attachments included in this message. </summary> | /// <summary> Returns a collection of all attachments included in this message. </summary> | ||||
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> | ||||
@@ -34,25 +31,5 @@ namespace Discord | |||||
IReadOnlyCollection<IRole> MentionedRoles { get; } | IReadOnlyCollection<IRole> MentionedRoles { get; } | ||||
/// <summary> Returns a collection of users mentioned in this message. </summary> | /// <summary> Returns a collection of users mentioned in this message. </summary> | ||||
IReadOnlyCollection<IUser> MentionedUsers { get; } | IReadOnlyCollection<IUser> MentionedUsers { get; } | ||||
/// <summary> Modifies this message. </summary> | |||||
Task ModifyAsync(Action<ModifyMessageParams> func); | |||||
/// <summary> Adds this message to its channel's pinned messages. </summary> | |||||
Task PinAsync(); | |||||
/// <summary> Removes this message from its channel's pinned messages. </summary> | |||||
Task UnpinAsync(); | |||||
/// <summary> Transforms this message's text into a human readable form, resolving things like 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 things like mentions to that object's name. </summary> | |||||
string Resolve( | |||||
UserMentionHandling userHandling = UserMentionHandling.Name, | |||||
ChannelMentionHandling channelHandling = ChannelMentionHandling.Name, | |||||
RoleMentionHandling roleHandling = RoleMentionHandling.Name, | |||||
EveryoneMentionHandling everyoneHandling = EveryoneMentionHandling.Ignore); | |||||
} | } | ||||
} | } |
@@ -0,0 +1,8 @@ | |||||
namespace Discord | |||||
{ | |||||
public interface ISystemMessage : IMessage | |||||
{ | |||||
/// <summary> Gets the type of this system message. </summary> | |||||
MessageType Type { get; } | |||||
} | |||||
} |
@@ -0,0 +1,29 @@ | |||||
using Discord.API.Rest; | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
namespace Discord | |||||
{ | |||||
public interface IUserMessage : IMessage, IDeletable | |||||
{ | |||||
/// <summary> Modifies this message. </summary> | |||||
Task ModifyAsync(Action<ModifyMessageParams> func); | |||||
/// <summary> Adds this message to its channel's pinned messages. </summary> | |||||
Task PinAsync(); | |||||
/// <summary> Removes this message from its channel's pinned messages. </summary> | |||||
Task UnpinAsync(); | |||||
/// <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> | |||||
string Resolve( | |||||
UserMentionHandling userHandling = UserMentionHandling.Name, | |||||
ChannelMentionHandling channelHandling = ChannelMentionHandling.Name, | |||||
RoleMentionHandling roleHandling = RoleMentionHandling.Name, | |||||
EveryoneMentionHandling everyoneHandling = EveryoneMentionHandling.Ignore); | |||||
} | |||||
} |
@@ -7,6 +7,7 @@ using System.IO; | |||||
using System.Linq; | using System.Linq; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Model = Discord.API.Channel; | using Model = Discord.API.Channel; | ||||
using MessageModel = Discord.API.Message; | |||||
namespace Discord.Rest | namespace Discord.Rest | ||||
{ | { | ||||
@@ -62,46 +63,46 @@ namespace Discord.Rest | |||||
return ImmutableArray.Create(currentUser, Recipient); | return ImmutableArray.Create(currentUser, Recipient); | ||||
} | } | ||||
public async Task<IMessage> SendMessageAsync(string text, bool isTTS) | |||||
public async Task<IUserMessage> SendMessageAsync(string text, bool isTTS) | |||||
{ | { | ||||
var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; | var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; | ||||
var model = await Discord.ApiClient.CreateDMMessageAsync(Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.CreateDMMessageAsync(Id, args).ConfigureAwait(false); | ||||
return new Message(this, new User(model.Author.Value), model); | |||||
return CreateOutgoingMessage(model); | |||||
} | } | ||||
public async Task<IMessage> SendFileAsync(string filePath, string text, bool isTTS) | |||||
public async Task<IUserMessage> SendFileAsync(string filePath, string text, bool isTTS) | |||||
{ | { | ||||
string filename = Path.GetFileName(filePath); | string filename = Path.GetFileName(filePath); | ||||
using (var file = File.OpenRead(filePath)) | using (var file = File.OpenRead(filePath)) | ||||
{ | { | ||||
var args = new UploadFileParams(file) { Filename = filename, Content = text, IsTTS = isTTS }; | var args = new UploadFileParams(file) { Filename = filename, Content = text, IsTTS = isTTS }; | ||||
var model = await Discord.ApiClient.UploadDMFileAsync(Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.UploadDMFileAsync(Id, args).ConfigureAwait(false); | ||||
return new Message(this, new User(model.Author.Value), model); | |||||
return CreateOutgoingMessage(model); | |||||
} | } | ||||
} | } | ||||
public async Task<IMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS) | |||||
public async Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS) | |||||
{ | { | ||||
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 Discord.ApiClient.UploadDMFileAsync(Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.UploadDMFileAsync(Id, args).ConfigureAwait(false); | ||||
return new Message(this, new User(model.Author.Value), model); | |||||
return CreateOutgoingMessage(model); | |||||
} | } | ||||
public virtual async Task<IMessage> GetMessageAsync(ulong id) | public virtual async Task<IMessage> GetMessageAsync(ulong id) | ||||
{ | { | ||||
var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); | var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); | ||||
if (model != null) | if (model != null) | ||||
return new Message(this, new User(model.Author.Value), model); | |||||
return CreateIncomingMessage(model); | |||||
return null; | return null; | ||||
} | } | ||||
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit) | public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit) | ||||
{ | { | ||||
var args = new GetChannelMessagesParams { Limit = limit }; | var args = new GetChannelMessagesParams { Limit = limit }; | ||||
var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); | var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); | ||||
return models.Select(x => new Message(this, new User(x.Author.Value), x)).ToImmutableArray(); | |||||
return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); | |||||
} | } | ||||
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) | public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) | ||||
{ | { | ||||
var args = new GetChannelMessagesParams { Limit = limit, RelativeMessageId = fromMessageId, RelativeDirection = dir }; | var args = new GetChannelMessagesParams { Limit = limit, RelativeMessageId = fromMessageId, RelativeDirection = dir }; | ||||
var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); | var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); | ||||
return models.Select(x => new Message(this, new User(x.Author.Value), x)).ToImmutableArray(); | |||||
return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); | |||||
} | } | ||||
public async Task DeleteMessagesAsync(IEnumerable<IMessage> messages) | public async Task DeleteMessagesAsync(IEnumerable<IMessage> messages) | ||||
{ | { | ||||
@@ -110,14 +111,26 @@ namespace Discord.Rest | |||||
public async Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync() | public async Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync() | ||||
{ | { | ||||
var models = await Discord.ApiClient.GetPinsAsync(Id); | var models = await Discord.ApiClient.GetPinsAsync(Id); | ||||
return models.Select(x => new Message(this, new User(x.Author.Value), x)).ToImmutableArray(); | |||||
return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); | |||||
} | } | ||||
public async Task TriggerTypingAsync() | public async Task TriggerTypingAsync() | ||||
{ | { | ||||
await Discord.ApiClient.TriggerTypingIndicatorAsync(Id).ConfigureAwait(false); | await Discord.ApiClient.TriggerTypingIndicatorAsync(Id).ConfigureAwait(false); | ||||
} | |||||
} | |||||
private UserMessage CreateOutgoingMessage(MessageModel model) | |||||
{ | |||||
return new UserMessage(this, new User(model.Author.Value), model); | |||||
} | |||||
private Message CreateIncomingMessage(MessageModel model) | |||||
{ | |||||
if (model.Type == MessageType.Default) | |||||
return new UserMessage(this, new User(model.Author.Value), model); | |||||
else | |||||
return new SystemMessage(this, new User(model.Author.Value), model); | |||||
} | |||||
public override string ToString() => '@' + Recipient.ToString(); | public override string ToString() => '@' + Recipient.ToString(); | ||||
private string DebuggerDisplay => $"@{Recipient} ({Id}, DM)"; | private string DebuggerDisplay => $"@{Recipient} ({Id}, DM)"; | ||||
@@ -8,6 +8,7 @@ using System.IO; | |||||
using System.Linq; | using System.Linq; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Model = Discord.API.Channel; | using Model = Discord.API.Channel; | ||||
using MessageModel = Discord.API.Message; | |||||
namespace Discord.Rest | namespace Discord.Rest | ||||
{ | { | ||||
@@ -87,46 +88,46 @@ namespace Discord.Rest | |||||
return _users.Select(x => x.Value).Concat<IUser>(ImmutableArray.Create(currentUser)).ToReadOnlyCollection(_users); | return _users.Select(x => x.Value).Concat<IUser>(ImmutableArray.Create(currentUser)).ToReadOnlyCollection(_users); | ||||
} | } | ||||
public async Task<IMessage> SendMessageAsync(string text, bool isTTS) | |||||
public async Task<IUserMessage> SendMessageAsync(string text, bool isTTS) | |||||
{ | { | ||||
var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; | var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; | ||||
var model = await Discord.ApiClient.CreateDMMessageAsync(Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.CreateDMMessageAsync(Id, args).ConfigureAwait(false); | ||||
return new Message(this, new User(model.Author.Value), model); | |||||
return CreateOutgoingMessage(model); | |||||
} | } | ||||
public async Task<IMessage> SendFileAsync(string filePath, string text, bool isTTS) | |||||
public async Task<IUserMessage> SendFileAsync(string filePath, string text, bool isTTS) | |||||
{ | { | ||||
string filename = Path.GetFileName(filePath); | string filename = Path.GetFileName(filePath); | ||||
using (var file = File.OpenRead(filePath)) | using (var file = File.OpenRead(filePath)) | ||||
{ | { | ||||
var args = new UploadFileParams(file) { Filename = filename, Content = text, IsTTS = isTTS }; | var args = new UploadFileParams(file) { Filename = filename, Content = text, IsTTS = isTTS }; | ||||
var model = await Discord.ApiClient.UploadDMFileAsync(Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.UploadDMFileAsync(Id, args).ConfigureAwait(false); | ||||
return new Message(this, new User(model.Author.Value), model); | |||||
return CreateOutgoingMessage(model); | |||||
} | } | ||||
} | } | ||||
public async Task<IMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS) | |||||
public async Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS) | |||||
{ | { | ||||
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 Discord.ApiClient.UploadDMFileAsync(Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.UploadDMFileAsync(Id, args).ConfigureAwait(false); | ||||
return new Message(this, new User(model.Author.Value), model); | |||||
return CreateOutgoingMessage(model); | |||||
} | } | ||||
public virtual async Task<IMessage> GetMessageAsync(ulong id) | public virtual async Task<IMessage> GetMessageAsync(ulong id) | ||||
{ | { | ||||
var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); | var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); | ||||
if (model != null) | if (model != null) | ||||
return new Message(this, new User(model.Author.Value), model); | |||||
return CreateIncomingMessage(model); | |||||
return null; | return null; | ||||
} | } | ||||
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit) | public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit) | ||||
{ | { | ||||
var args = new GetChannelMessagesParams { Limit = limit }; | var args = new GetChannelMessagesParams { Limit = limit }; | ||||
var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); | var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); | ||||
return models.Select(x => new Message(this, new User(x.Author.Value), x)).ToImmutableArray(); | |||||
return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); | |||||
} | } | ||||
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) | public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) | ||||
{ | { | ||||
var args = new GetChannelMessagesParams { Limit = limit, RelativeMessageId = fromMessageId, RelativeDirection = dir }; | var args = new GetChannelMessagesParams { Limit = limit, RelativeMessageId = fromMessageId, RelativeDirection = dir }; | ||||
var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); | var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); | ||||
return models.Select(x => new Message(this, new User(x.Author.Value), x)).ToImmutableArray(); | |||||
return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); | |||||
} | } | ||||
public async Task DeleteMessagesAsync(IEnumerable<IMessage> messages) | public async Task DeleteMessagesAsync(IEnumerable<IMessage> messages) | ||||
{ | { | ||||
@@ -135,7 +136,7 @@ namespace Discord.Rest | |||||
public async Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync() | public async Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync() | ||||
{ | { | ||||
var models = await Discord.ApiClient.GetPinsAsync(Id); | var models = await Discord.ApiClient.GetPinsAsync(Id); | ||||
return models.Select(x => new Message(this, new User(x.Author.Value), x)).ToImmutableArray(); | |||||
return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); | |||||
} | } | ||||
public async Task TriggerTypingAsync() | public async Task TriggerTypingAsync() | ||||
@@ -143,6 +144,18 @@ namespace Discord.Rest | |||||
await Discord.ApiClient.TriggerTypingIndicatorAsync(Id).ConfigureAwait(false); | await Discord.ApiClient.TriggerTypingIndicatorAsync(Id).ConfigureAwait(false); | ||||
} | } | ||||
private UserMessage CreateOutgoingMessage(MessageModel model) | |||||
{ | |||||
return new UserMessage(this, new User(model.Author.Value), model); | |||||
} | |||||
private Message CreateIncomingMessage(MessageModel model) | |||||
{ | |||||
if (model.Type == MessageType.Default) | |||||
return new UserMessage(this, new User(model.Author.Value), model); | |||||
else | |||||
return new SystemMessage(this, new User(model.Author.Value), model); | |||||
} | |||||
public override string ToString() => Name; | public override string ToString() => Name; | ||||
private string DebuggerDisplay => $"@{Name} ({Id}, Group)"; | private string DebuggerDisplay => $"@{Name} ({Id}, Group)"; | ||||
@@ -7,6 +7,7 @@ using System.IO; | |||||
using System.Linq; | using System.Linq; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Model = Discord.API.Channel; | using Model = Discord.API.Channel; | ||||
using MessageModel = Discord.API.Message; | |||||
namespace Discord.Rest | namespace Discord.Rest | ||||
{ | { | ||||
@@ -57,46 +58,46 @@ namespace Discord.Rest | |||||
return users.Where(x => Permissions.GetValue(Permissions.ResolveChannel(x, this, x.GuildPermissions.RawValue), ChannelPermission.ReadMessages)).ToImmutableArray(); | return users.Where(x => Permissions.GetValue(Permissions.ResolveChannel(x, this, x.GuildPermissions.RawValue), ChannelPermission.ReadMessages)).ToImmutableArray(); | ||||
} | } | ||||
public async Task<IMessage> SendMessageAsync(string text, bool isTTS) | |||||
public async Task<IUserMessage> SendMessageAsync(string text, bool isTTS) | |||||
{ | { | ||||
var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; | var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; | ||||
var model = await Discord.ApiClient.CreateMessageAsync(Guild.Id, Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.CreateMessageAsync(Guild.Id, Id, args).ConfigureAwait(false); | ||||
return new Message(this, new User(model.Author.Value), model); | |||||
return CreateOutgoingMessage(model); | |||||
} | } | ||||
public async Task<IMessage> SendFileAsync(string filePath, string text, bool isTTS) | |||||
public async Task<IUserMessage> SendFileAsync(string filePath, string text, bool isTTS) | |||||
{ | { | ||||
string filename = Path.GetFileName(filePath); | string filename = Path.GetFileName(filePath); | ||||
using (var file = File.OpenRead(filePath)) | using (var file = File.OpenRead(filePath)) | ||||
{ | { | ||||
var args = new UploadFileParams(file) { Filename = filename, Content = text, IsTTS = isTTS }; | var args = new UploadFileParams(file) { Filename = filename, Content = text, IsTTS = isTTS }; | ||||
var model = await Discord.ApiClient.UploadFileAsync(Guild.Id, Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.UploadFileAsync(Guild.Id, Id, args).ConfigureAwait(false); | ||||
return new Message(this, new User(model.Author.Value), model); | |||||
return CreateOutgoingMessage(model); | |||||
} | } | ||||
} | } | ||||
public async Task<IMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS) | |||||
public async Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS) | |||||
{ | { | ||||
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 Discord.ApiClient.UploadFileAsync(Guild.Id, Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.UploadFileAsync(Guild.Id, Id, args).ConfigureAwait(false); | ||||
return new Message(this, new User(model.Author.Value), model); | |||||
return CreateOutgoingMessage(model); | |||||
} | } | ||||
public virtual async Task<IMessage> GetMessageAsync(ulong id) | public virtual async Task<IMessage> GetMessageAsync(ulong id) | ||||
{ | { | ||||
var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); | var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); | ||||
if (model != null) | if (model != null) | ||||
return new Message(this, new User(model.Author.Value), model); | |||||
return CreateIncomingMessage(model); | |||||
return null; | return null; | ||||
} | } | ||||
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit) | public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit) | ||||
{ | { | ||||
var args = new GetChannelMessagesParams { Limit = limit }; | var args = new GetChannelMessagesParams { Limit = limit }; | ||||
var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); | var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); | ||||
return models.Select(x => new Message(this, new User(x.Author.Value), x)).ToImmutableArray(); | |||||
return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); | |||||
} | } | ||||
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) | public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) | ||||
{ | { | ||||
var args = new GetChannelMessagesParams { Limit = limit, RelativeMessageId = fromMessageId, RelativeDirection = dir }; | var args = new GetChannelMessagesParams { Limit = limit, RelativeMessageId = fromMessageId, RelativeDirection = dir }; | ||||
var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); | var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); | ||||
return models.Select(x => new Message(this, new User(x.Author.Value), x)).ToImmutableArray(); | |||||
return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); | |||||
} | } | ||||
public async Task DeleteMessagesAsync(IEnumerable<IMessage> messages) | public async Task DeleteMessagesAsync(IEnumerable<IMessage> messages) | ||||
{ | { | ||||
@@ -105,7 +106,7 @@ namespace Discord.Rest | |||||
public async Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync() | public async Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync() | ||||
{ | { | ||||
var models = await Discord.ApiClient.GetPinsAsync(Id); | var models = await Discord.ApiClient.GetPinsAsync(Id); | ||||
return models.Select(x => new Message(this, new User(x.Author.Value), x)).ToImmutableArray(); | |||||
return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); | |||||
} | } | ||||
public async Task TriggerTypingAsync() | public async Task TriggerTypingAsync() | ||||
@@ -113,6 +114,18 @@ namespace Discord.Rest | |||||
await Discord.ApiClient.TriggerTypingIndicatorAsync(Id).ConfigureAwait(false); | await Discord.ApiClient.TriggerTypingIndicatorAsync(Id).ConfigureAwait(false); | ||||
} | } | ||||
private UserMessage CreateOutgoingMessage(MessageModel model) | |||||
{ | |||||
return new UserMessage(this, new User(model.Author.Value), model); | |||||
} | |||||
private Message CreateIncomingMessage(MessageModel model) | |||||
{ | |||||
if (model.Type == MessageType.Default) | |||||
return new UserMessage(this, new User(model.Author.Value), model); | |||||
else | |||||
return new SystemMessage(this, new User(model.Author.Value), model); | |||||
} | |||||
private string DebuggerDisplay => $"{Name} ({Id}, Text)"; | private string DebuggerDisplay => $"{Name} ({Id}, Text)"; | ||||
IMessage IMessageChannel.GetCachedMessage(ulong id) => null; | IMessage IMessageChannel.GetCachedMessage(ulong id) => null; | ||||
@@ -9,28 +9,27 @@ using Model = Discord.API.Message; | |||||
namespace Discord.Rest | namespace Discord.Rest | ||||
{ | { | ||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
internal class Message : SnowflakeEntity, IMessage | |||||
internal abstract class Message : SnowflakeEntity, IMessage | |||||
{ | { | ||||
private bool _isMentioningEveryone; | |||||
private long _timestampTicks; | private long _timestampTicks; | ||||
private long? _editedTimestampTicks; | |||||
public MessageType Type { get; } | |||||
public IMessageChannel Channel { get; } | public IMessageChannel Channel { get; } | ||||
public IUser Author { get; } | public IUser Author { get; } | ||||
public bool IsTTS { get; private set; } | |||||
public string Content { get; private set; } | public string Content { get; private set; } | ||||
public bool IsPinned { get; private set; } | |||||
public IReadOnlyCollection<IAttachment> Attachments { get; private set; } | |||||
public IReadOnlyCollection<IEmbed> Embeds { get; private set; } | |||||
public IReadOnlyCollection<ulong> MentionedChannelIds { get; private set; } | |||||
public IReadOnlyCollection<IRole> MentionedRoles { get; private set; } | |||||
public IReadOnlyCollection<IUser> MentionedUsers { get; private set; } | |||||
public override DiscordRestClient Discord => (Channel as Entity<ulong>).Discord; | public override DiscordRestClient Discord => (Channel as Entity<ulong>).Discord; | ||||
public DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); | |||||
public virtual bool IsTTS => false; | |||||
public virtual bool IsPinned => false; | |||||
public virtual DateTimeOffset? EditedTimestamp => null; | |||||
public virtual IReadOnlyCollection<IAttachment> Attachments => ImmutableArray.Create<IAttachment>(); | |||||
public virtual IReadOnlyCollection<IEmbed> Embeds => ImmutableArray.Create<IEmbed>(); | |||||
public virtual IReadOnlyCollection<ulong> MentionedChannelIds => ImmutableArray.Create<ulong>(); | |||||
public virtual IReadOnlyCollection<IRole> MentionedRoles => ImmutableArray.Create<IRole>(); | |||||
public virtual IReadOnlyCollection<IUser> MentionedUsers => ImmutableArray.Create<IUser>(); | |||||
public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | ||||
public Message(IMessageChannel channel, IUser author, Model model) | public Message(IMessageChannel channel, IUser author, Model model) | ||||
@@ -38,86 +37,21 @@ namespace Discord.Rest | |||||
{ | { | ||||
Channel = channel; | Channel = channel; | ||||
Author = author; | Author = author; | ||||
Type = model.Type; | |||||
MentionedUsers = ImmutableArray.Create<IUser>(); | |||||
MentionedChannelIds = ImmutableArray.Create<ulong>(); | |||||
MentionedRoles = ImmutableArray.Create<IRole>(); | |||||
Update(model, UpdateSource.Creation); | Update(model, UpdateSource.Creation); | ||||
} | } | ||||
public void Update(Model model, UpdateSource source) | |||||
public virtual void Update(Model model, UpdateSource source) | |||||
{ | { | ||||
if (source == UpdateSource.Rest && IsAttached) return; | if (source == UpdateSource.Rest && IsAttached) return; | ||||
var guildChannel = Channel as GuildChannel; | var guildChannel = Channel as GuildChannel; | ||||
var guild = guildChannel?.Guild; | var guild = guildChannel?.Guild; | ||||
if (model.IsTextToSpeech.IsSpecified) | |||||
IsTTS = model.IsTextToSpeech.Value; | |||||
if (model.Pinned.IsSpecified) | |||||
IsPinned = model.Pinned.Value; | |||||
if (model.Timestamp.IsSpecified) | if (model.Timestamp.IsSpecified) | ||||
_timestampTicks = model.Timestamp.Value.UtcTicks; | _timestampTicks = model.Timestamp.Value.UtcTicks; | ||||
if (model.EditedTimestamp.IsSpecified) | |||||
_editedTimestampTicks = model.EditedTimestamp.Value?.UtcTicks; | |||||
if (model.MentionEveryone.IsSpecified) | |||||
_isMentioningEveryone = model.MentionEveryone.Value; | |||||
if (model.Attachments.IsSpecified) | |||||
{ | |||||
var value = model.Attachments.Value; | |||||
if (value.Length > 0) | |||||
{ | |||||
var attachments = new Attachment[value.Length]; | |||||
for (int i = 0; i < attachments.Length; i++) | |||||
attachments[i] = new Attachment(value[i]); | |||||
Attachments = ImmutableArray.Create(attachments); | |||||
} | |||||
else | |||||
Attachments = ImmutableArray.Create<Attachment>(); | |||||
} | |||||
if (model.Embeds.IsSpecified) | |||||
{ | |||||
var value = model.Embeds.Value; | |||||
if (value.Length > 0) | |||||
{ | |||||
var embeds = new Embed[value.Length]; | |||||
for (int i = 0; i < embeds.Length; i++) | |||||
embeds[i] = new Embed(value[i]); | |||||
Embeds = ImmutableArray.Create(embeds); | |||||
} | |||||
else | |||||
Embeds = ImmutableArray.Create<Embed>(); | |||||
} | |||||
if (model.Mentions.IsSpecified) | |||||
{ | |||||
var value = model.Mentions.Value; | |||||
if (value.Length > 0) | |||||
{ | |||||
var mentions = new User[value.Length]; | |||||
for (int i = 0; i < value.Length; i++) | |||||
mentions[i] = new User(value[i]); | |||||
MentionedUsers = ImmutableArray.Create(mentions); | |||||
} | |||||
else | |||||
MentionedUsers = ImmutableArray.Create<IUser>(); | |||||
} | |||||
if (model.Content.IsSpecified) | if (model.Content.IsSpecified) | ||||
{ | |||||
var text = model.Content.Value; | |||||
if (guildChannel != null) | |||||
{ | |||||
MentionedUsers = MentionUtils.GetUserMentions(text, Channel, MentionedUsers); | |||||
MentionedChannelIds = MentionUtils.GetChannelMentions(text, guildChannel.Guild); | |||||
MentionedRoles = MentionUtils.GetRoleMentions(text, guildChannel.Guild); | |||||
} | |||||
Content = text; | |||||
} | |||||
Content = model.Content.Value; | |||||
} | } | ||||
public async Task UpdateAsync() | public async Task UpdateAsync() | ||||
@@ -159,23 +93,6 @@ namespace Discord.Rest | |||||
{ | { | ||||
await Discord.ApiClient.RemovePinAsync(Channel.Id, Id).ConfigureAwait(false); | await Discord.ApiClient.RemovePinAsync(Channel.Id, Id).ConfigureAwait(false); | ||||
} | } | ||||
public string Resolve(int startIndex, int length, UserMentionHandling userHandling, ChannelMentionHandling channelHandling, | |||||
RoleMentionHandling roleHandling, EveryoneMentionHandling everyoneHandling) | |||||
=> Resolve(Content.Substring(startIndex, length), userHandling, channelHandling, roleHandling, everyoneHandling); | |||||
public string Resolve(UserMentionHandling userHandling, ChannelMentionHandling channelHandling, | |||||
RoleMentionHandling roleHandling, EveryoneMentionHandling everyoneHandling) | |||||
=> Resolve(Content, userHandling, channelHandling, roleHandling, everyoneHandling); | |||||
private string Resolve(string text, UserMentionHandling userHandling, ChannelMentionHandling channelHandling, | |||||
RoleMentionHandling roleHandling, EveryoneMentionHandling everyoneHandling) | |||||
{ | |||||
text = MentionUtils.ResolveUserMentions(text, Channel, MentionedUsers, userHandling); | |||||
text = MentionUtils.ResolveChannelMentions(text, (Channel as IGuildChannel)?.Guild, channelHandling); | |||||
text = MentionUtils.ResolveRoleMentions(text, MentionedRoles, roleHandling); | |||||
text = MentionUtils.ResolveEveryoneMentions(text, everyoneHandling); | |||||
return text; | |||||
} | |||||
public override string ToString() => Content; | public override string ToString() => Content; | ||||
private string DebuggerDisplay => $"{Author}: {Content}{(Attachments.Count > 0 ? $" [{Attachments.Count} Attachments]" : "")}"; | private string DebuggerDisplay => $"{Author}: {Content}{(Attachments.Count > 0 ? $" [{Attachments.Count} Attachments]" : "")}"; | ||||
@@ -0,0 +1,22 @@ | |||||
using System.Diagnostics; | |||||
using Model = Discord.API.Message; | |||||
namespace Discord.Rest | |||||
{ | |||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||||
internal class SystemMessage : Message, ISystemMessage | |||||
{ | |||||
public MessageType Type { get; } | |||||
public override DiscordRestClient Discord => (Channel as Entity<ulong>).Discord; | |||||
public SystemMessage(IMessageChannel channel, IUser author, Model model) | |||||
: base(channel, author, model) | |||||
{ | |||||
Type = model.Type; | |||||
} | |||||
public override string ToString() => Content; | |||||
private string DebuggerDisplay => $"[{Type}] {Author}{(!string.IsNullOrEmpty(Content) ? $": ({Content})" : "")}"; | |||||
} | |||||
} |
@@ -0,0 +1,175 @@ | |||||
using Discord.API.Rest; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Collections.Immutable; | |||||
using System.Diagnostics; | |||||
using System.Threading.Tasks; | |||||
using Model = Discord.API.Message; | |||||
namespace Discord.Rest | |||||
{ | |||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||||
internal class UserMessage : Message, IUserMessage | |||||
{ | |||||
private bool _isMentioningEveryone, _isTTS, _isPinned; | |||||
private long? _editedTimestampTicks; | |||||
private IReadOnlyCollection<IAttachment> _attachments; | |||||
private IReadOnlyCollection<IEmbed> _embeds; | |||||
private IReadOnlyCollection<ulong> _mentionedChannelIds; | |||||
private IReadOnlyCollection<IRole> _mentionedRoles; | |||||
private IReadOnlyCollection<IUser> _mentionedUsers; | |||||
public override DiscordRestClient Discord => (Channel as Entity<ulong>).Discord; | |||||
public override bool IsTTS => _isTTS; | |||||
public override bool IsPinned => _isPinned; | |||||
public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); | |||||
public override IReadOnlyCollection<IAttachment> Attachments => _attachments; | |||||
public override IReadOnlyCollection<IEmbed> Embeds => _embeds; | |||||
public override IReadOnlyCollection<ulong> MentionedChannelIds => _mentionedChannelIds; | |||||
public override IReadOnlyCollection<IRole> MentionedRoles => _mentionedRoles; | |||||
public override IReadOnlyCollection<IUser> MentionedUsers => _mentionedUsers; | |||||
public UserMessage(IMessageChannel channel, IUser author, Model model) | |||||
: base(channel, author, model) | |||||
{ | |||||
_mentionedChannelIds = ImmutableArray.Create<ulong>(); | |||||
_mentionedRoles = ImmutableArray.Create<IRole>(); | |||||
_mentionedUsers = ImmutableArray.Create<IUser>(); | |||||
Update(model, UpdateSource.Creation); | |||||
} | |||||
public override void Update(Model model, UpdateSource source) | |||||
{ | |||||
if (source == UpdateSource.Rest && IsAttached) return; | |||||
var guildChannel = Channel as GuildChannel; | |||||
var guild = guildChannel?.Guild; | |||||
if (model.IsTextToSpeech.IsSpecified) | |||||
_isTTS = model.IsTextToSpeech.Value; | |||||
if (model.Pinned.IsSpecified) | |||||
_isPinned = model.Pinned.Value; | |||||
if (model.EditedTimestamp.IsSpecified) | |||||
_editedTimestampTicks = model.EditedTimestamp.Value?.UtcTicks; | |||||
if (model.MentionEveryone.IsSpecified) | |||||
_isMentioningEveryone = model.MentionEveryone.Value; | |||||
if (model.Attachments.IsSpecified) | |||||
{ | |||||
var value = model.Attachments.Value; | |||||
if (value.Length > 0) | |||||
{ | |||||
var attachments = new Attachment[value.Length]; | |||||
for (int i = 0; i < attachments.Length; i++) | |||||
attachments[i] = new Attachment(value[i]); | |||||
_attachments = ImmutableArray.Create(attachments); | |||||
} | |||||
else | |||||
_attachments = ImmutableArray.Create<Attachment>(); | |||||
} | |||||
if (model.Embeds.IsSpecified) | |||||
{ | |||||
var value = model.Embeds.Value; | |||||
if (value.Length > 0) | |||||
{ | |||||
var embeds = new Embed[value.Length]; | |||||
for (int i = 0; i < embeds.Length; i++) | |||||
embeds[i] = new Embed(value[i]); | |||||
_embeds = ImmutableArray.Create(embeds); | |||||
} | |||||
else | |||||
_embeds = ImmutableArray.Create<Embed>(); | |||||
} | |||||
ImmutableArray<IUser> mentions = ImmutableArray.Create<IUser>(); | |||||
if (model.Mentions.IsSpecified) | |||||
{ | |||||
var value = model.Mentions.Value; | |||||
if (value.Length > 0) | |||||
{ | |||||
var newMentions = new IUser[value.Length]; | |||||
for (int i = 0; i < value.Length; i++) | |||||
newMentions[i] = new User(value[i]); | |||||
mentions = ImmutableArray.Create(newMentions); | |||||
} | |||||
} | |||||
if (model.Content.IsSpecified) | |||||
{ | |||||
var text = model.Content.Value; | |||||
if (guildChannel != null) | |||||
{ | |||||
_mentionedUsers = MentionUtils.GetUserMentions(text, Channel, mentions); | |||||
_mentionedChannelIds = MentionUtils.GetChannelMentions(text, guildChannel.Guild); | |||||
_mentionedRoles = MentionUtils.GetRoleMentions(text, guildChannel.Guild); | |||||
} | |||||
model.Content = text; | |||||
} | |||||
base.Update(model, source); | |||||
} | |||||
public async Task UpdateAsync() | |||||
{ | |||||
if (IsAttached) throw new NotSupportedException(); | |||||
var model = await Discord.ApiClient.GetChannelMessageAsync(Channel.Id, Id).ConfigureAwait(false); | |||||
Update(model, UpdateSource.Rest); | |||||
} | |||||
public async Task ModifyAsync(Action<ModifyMessageParams> func) | |||||
{ | |||||
if (func == null) throw new NullReferenceException(nameof(func)); | |||||
var args = new ModifyMessageParams(); | |||||
func(args); | |||||
var guildChannel = Channel as GuildChannel; | |||||
Model model; | |||||
if (guildChannel != null) | |||||
model = await Discord.ApiClient.ModifyMessageAsync(guildChannel.Guild.Id, Channel.Id, Id, args).ConfigureAwait(false); | |||||
else | |||||
model = await Discord.ApiClient.ModifyDMMessageAsync(Channel.Id, Id, args).ConfigureAwait(false); | |||||
Update(model, UpdateSource.Rest); | |||||
} | |||||
public async Task DeleteAsync() | |||||
{ | |||||
var guildChannel = Channel as GuildChannel; | |||||
if (guildChannel != null) | |||||
await Discord.ApiClient.DeleteMessageAsync(guildChannel.Id, Channel.Id, Id).ConfigureAwait(false); | |||||
else | |||||
await Discord.ApiClient.DeleteDMMessageAsync(Channel.Id, Id).ConfigureAwait(false); | |||||
} | |||||
public async Task PinAsync() | |||||
{ | |||||
await Discord.ApiClient.AddPinAsync(Channel.Id, Id).ConfigureAwait(false); | |||||
} | |||||
public async Task UnpinAsync() | |||||
{ | |||||
await Discord.ApiClient.RemovePinAsync(Channel.Id, Id).ConfigureAwait(false); | |||||
} | |||||
public string Resolve(int startIndex, int length, UserMentionHandling userHandling, ChannelMentionHandling channelHandling, | |||||
RoleMentionHandling roleHandling, EveryoneMentionHandling everyoneHandling) | |||||
=> Resolve(Content.Substring(startIndex, length), userHandling, channelHandling, roleHandling, everyoneHandling); | |||||
public string Resolve(UserMentionHandling userHandling, ChannelMentionHandling channelHandling, | |||||
RoleMentionHandling roleHandling, EveryoneMentionHandling everyoneHandling) | |||||
=> Resolve(Content, userHandling, channelHandling, roleHandling, everyoneHandling); | |||||
private string Resolve(string text, UserMentionHandling userHandling, ChannelMentionHandling channelHandling, | |||||
RoleMentionHandling roleHandling, EveryoneMentionHandling everyoneHandling) | |||||
{ | |||||
text = MentionUtils.ResolveUserMentions(text, Channel, MentionedUsers, userHandling); | |||||
text = MentionUtils.ResolveChannelMentions(text, (Channel as IGuildChannel)?.Guild, channelHandling); | |||||
text = MentionUtils.ResolveRoleMentions(text, MentionedRoles, roleHandling); | |||||
text = MentionUtils.ResolveEveryoneMentions(text, everyoneHandling); | |||||
return text; | |||||
} | |||||
public override string ToString() => Content; | |||||
private string DebuggerDisplay => $"{Author}: {Content}{(Attachments.Count > 0 ? $" [{Attachments.Count} Attachments]" : "")}"; | |||||
} | |||||
} |
@@ -1247,7 +1247,7 @@ namespace Discord.WebSocket | |||||
} | } | ||||
IMessage before = null, after = null; | IMessage before = null, after = null; | ||||
SocketMessage cachedMsg = channel.GetMessage(data.Id); | |||||
ISocketMessage cachedMsg = channel.GetMessage(data.Id); | |||||
if (cachedMsg != null) | if (cachedMsg != null) | ||||
{ | { | ||||
before = cachedMsg.Clone(); | before = cachedMsg.Clone(); | ||||
@@ -1259,7 +1259,7 @@ namespace Discord.WebSocket | |||||
//Edited message isnt in cache, create a detached one | //Edited message isnt in cache, create a detached one | ||||
var author = channel.GetUser(data.Author.Value.Id, true); | var author = channel.GetUser(data.Author.Value.Id, true); | ||||
if (author != null) | if (author != null) | ||||
after = new Message(channel, author, data); | |||||
after = channel.CreateMessage(author, data); | |||||
} | } | ||||
if (after != null) | if (after != null) | ||||
await _messageUpdatedEvent.InvokeAsync(Optional.Create(before), after).ConfigureAwait(false); | await _messageUpdatedEvent.InvokeAsync(Optional.Create(before), after).ConfigureAwait(false); | ||||
@@ -7,9 +7,10 @@ namespace Discord.WebSocket | |||||
{ | { | ||||
IReadOnlyCollection<ISocketUser> Users { get; } | IReadOnlyCollection<ISocketUser> Users { get; } | ||||
SocketMessage AddMessage(ISocketUser author, MessageModel model); | |||||
SocketMessage GetMessage(ulong id); | |||||
SocketMessage RemoveMessage(ulong id); | |||||
ISocketMessage CreateMessage(ISocketUser author, MessageModel model); | |||||
ISocketMessage AddMessage(ISocketUser author, MessageModel model); | |||||
ISocketMessage GetMessage(ulong id); | |||||
ISocketMessage RemoveMessage(ulong id); | |||||
ISocketUser GetUser(ulong id, bool skipCheck = false); | ISocketUser GetUser(ulong id, bool skipCheck = false); | ||||
} | } | ||||
@@ -9,51 +9,51 @@ namespace Discord.WebSocket | |||||
{ | { | ||||
internal class MessageCache : MessageManager | internal class MessageCache : MessageManager | ||||
{ | { | ||||
private readonly ConcurrentDictionary<ulong, SocketMessage> _messages; | |||||
private readonly ConcurrentDictionary<ulong, ISocketMessage> _messages; | |||||
private readonly ConcurrentQueue<ulong> _orderedMessages; | private readonly ConcurrentQueue<ulong> _orderedMessages; | ||||
private readonly int _size; | private readonly int _size; | ||||
public override IReadOnlyCollection<SocketMessage> Messages => _messages.ToReadOnlyCollection(); | |||||
public override IReadOnlyCollection<ISocketMessage> Messages => _messages.ToReadOnlyCollection(); | |||||
public MessageCache(DiscordSocketClient discord, ISocketMessageChannel channel) | public MessageCache(DiscordSocketClient discord, ISocketMessageChannel channel) | ||||
: base(discord, channel) | : base(discord, channel) | ||||
{ | { | ||||
_size = discord.MessageCacheSize; | _size = discord.MessageCacheSize; | ||||
_messages = new ConcurrentDictionary<ulong, SocketMessage>(1, (int)(_size * 1.05)); | |||||
_messages = new ConcurrentDictionary<ulong, ISocketMessage>(1, (int)(_size * 1.05)); | |||||
_orderedMessages = new ConcurrentQueue<ulong>(); | _orderedMessages = new ConcurrentQueue<ulong>(); | ||||
} | } | ||||
public override void Add(SocketMessage message) | |||||
public override void Add(ISocketMessage message) | |||||
{ | { | ||||
if (_messages.TryAdd(message.Id, message)) | if (_messages.TryAdd(message.Id, message)) | ||||
{ | { | ||||
_orderedMessages.Enqueue(message.Id); | _orderedMessages.Enqueue(message.Id); | ||||
ulong msgId; | ulong msgId; | ||||
SocketMessage msg; | |||||
ISocketMessage msg; | |||||
while (_orderedMessages.Count > _size && _orderedMessages.TryDequeue(out msgId)) | while (_orderedMessages.Count > _size && _orderedMessages.TryDequeue(out msgId)) | ||||
_messages.TryRemove(msgId, out msg); | _messages.TryRemove(msgId, out msg); | ||||
} | } | ||||
} | } | ||||
public override SocketMessage Remove(ulong id) | |||||
public override ISocketMessage Remove(ulong id) | |||||
{ | { | ||||
SocketMessage msg; | |||||
ISocketMessage msg; | |||||
_messages.TryRemove(id, out msg); | _messages.TryRemove(id, out msg); | ||||
return msg; | return msg; | ||||
} | } | ||||
public override SocketMessage Get(ulong id) | |||||
public override ISocketMessage Get(ulong id) | |||||
{ | { | ||||
SocketMessage result; | |||||
ISocketMessage result; | |||||
if (_messages.TryGetValue(id, out result)) | if (_messages.TryGetValue(id, out result)) | ||||
return result; | return result; | ||||
return null; | return null; | ||||
} | } | ||||
public override IImmutableList<SocketMessage> GetMany(ulong? fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | |||||
public override IImmutableList<ISocketMessage> GetMany(ulong? fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | |||||
{ | { | ||||
if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit)); | if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit)); | ||||
if (limit == 0) return ImmutableArray<SocketMessage>.Empty; | |||||
if (limit == 0) return ImmutableArray<ISocketMessage>.Empty; | |||||
IEnumerable<ulong> cachedMessageIds; | IEnumerable<ulong> cachedMessageIds; | ||||
if (fromMessageId == null) | if (fromMessageId == null) | ||||
@@ -67,7 +67,7 @@ namespace Discord.WebSocket | |||||
.Take(limit) | .Take(limit) | ||||
.Select(x => | .Select(x => | ||||
{ | { | ||||
SocketMessage msg; | |||||
ISocketMessage msg; | |||||
if (_messages.TryGetValue(x, out msg)) | if (_messages.TryGetValue(x, out msg)) | ||||
return msg; | return msg; | ||||
return null; | return null; | ||||
@@ -76,7 +76,7 @@ namespace Discord.WebSocket | |||||
.ToImmutableArray(); | .ToImmutableArray(); | ||||
} | } | ||||
public override async Task<SocketMessage> DownloadAsync(ulong id) | |||||
public override async Task<ISocketMessage> DownloadAsync(ulong id) | |||||
{ | { | ||||
var msg = Get(id); | var msg = Get(id); | ||||
if (msg != null) | if (msg != null) | ||||
@@ -5,6 +5,7 @@ using System.Collections.Generic; | |||||
using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Model = Discord.API.Message; | |||||
namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
{ | { | ||||
@@ -13,8 +14,8 @@ namespace Discord.WebSocket | |||||
private readonly DiscordSocketClient _discord; | private readonly DiscordSocketClient _discord; | ||||
private readonly ISocketMessageChannel _channel; | private readonly ISocketMessageChannel _channel; | ||||
public virtual IReadOnlyCollection<SocketMessage> Messages | |||||
=> ImmutableArray.Create<SocketMessage>(); | |||||
public virtual IReadOnlyCollection<ISocketMessage> Messages | |||||
=> ImmutableArray.Create<ISocketMessage>(); | |||||
public MessageManager(DiscordSocketClient discord, ISocketMessageChannel channel) | public MessageManager(DiscordSocketClient discord, ISocketMessageChannel channel) | ||||
{ | { | ||||
@@ -22,25 +23,25 @@ namespace Discord.WebSocket | |||||
_channel = channel; | _channel = channel; | ||||
} | } | ||||
public virtual void Add(SocketMessage message) { } | |||||
public virtual SocketMessage Remove(ulong id) => null; | |||||
public virtual SocketMessage Get(ulong id) => null; | |||||
public virtual void Add(ISocketMessage message) { } | |||||
public virtual ISocketMessage Remove(ulong id) => null; | |||||
public virtual ISocketMessage Get(ulong id) => null; | |||||
public virtual IImmutableList<SocketMessage> GetMany(ulong? fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | |||||
=> ImmutableArray.Create<SocketMessage>(); | |||||
public virtual IImmutableList<ISocketMessage> GetMany(ulong? fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | |||||
=> ImmutableArray.Create<ISocketMessage>(); | |||||
public virtual async Task<SocketMessage> DownloadAsync(ulong id) | |||||
public virtual async Task<ISocketMessage> DownloadAsync(ulong id) | |||||
{ | { | ||||
var model = await _discord.ApiClient.GetChannelMessageAsync(_channel.Id, id).ConfigureAwait(false); | var model = await _discord.ApiClient.GetChannelMessageAsync(_channel.Id, id).ConfigureAwait(false); | ||||
if (model != null) | if (model != null) | ||||
return new SocketMessage(_channel, new User(model.Author.Value), model); | |||||
return Create(new User(model.Author.Value), model); | |||||
return null; | return null; | ||||
} | } | ||||
public async Task<IReadOnlyCollection<SocketMessage>> DownloadAsync(ulong? fromId, Direction dir, int limit) | |||||
public async Task<IReadOnlyCollection<ISocketMessage>> DownloadAsync(ulong? fromId, Direction dir, int limit) | |||||
{ | { | ||||
//TODO: Test heavily, especially the ordering of messages | //TODO: Test heavily, especially the ordering of messages | ||||
if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit)); | if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit)); | ||||
if (limit == 0) return ImmutableArray<SocketMessage>.Empty; | |||||
if (limit == 0) return ImmutableArray<ISocketMessage>.Empty; | |||||
var cachedMessages = GetMany(fromId, dir, limit); | var cachedMessages = GetMany(fromId, dir, limit); | ||||
if (cachedMessages.Count == limit) | if (cachedMessages.Count == limit) | ||||
@@ -75,9 +76,17 @@ namespace Discord.WebSocket | |||||
else | else | ||||
user = newUser; | user = newUser; | ||||
} | } | ||||
return new SocketMessage(_channel, user, x); | |||||
return Create(user, x); | |||||
})).ToImmutableArray(); | })).ToImmutableArray(); | ||||
} | } | ||||
} | } | ||||
public ISocketMessage Create(IUser author, Model model) | |||||
{ | |||||
if (model.Type == MessageType.Default) | |||||
return new SocketUserMessage(_channel, author, model); | |||||
else | |||||
return new SocketSystemMessage(_channel, author, model); | |||||
} | |||||
} | } | ||||
} | } |
@@ -52,17 +52,21 @@ namespace Discord.WebSocket | |||||
{ | { | ||||
return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false); | return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false); | ||||
} | } | ||||
public SocketMessage AddMessage(ISocketUser author, MessageModel model) | |||||
public ISocketMessage CreateMessage(ISocketUser author, MessageModel model) | |||||
{ | { | ||||
var msg = new SocketMessage(this, author, model); | |||||
return _messages.Create(author, model); | |||||
} | |||||
public ISocketMessage AddMessage(ISocketUser author, MessageModel model) | |||||
{ | |||||
var msg = _messages.Create(author, model); | |||||
_messages.Add(msg); | _messages.Add(msg); | ||||
return msg; | return msg; | ||||
} | } | ||||
public SocketMessage GetMessage(ulong id) | |||||
public ISocketMessage GetMessage(ulong id) | |||||
{ | { | ||||
return _messages.Get(id); | return _messages.Get(id); | ||||
} | } | ||||
public SocketMessage RemoveMessage(ulong id) | |||||
public ISocketMessage RemoveMessage(ulong id) | |||||
{ | { | ||||
return _messages.Remove(id); | return _messages.Remove(id); | ||||
} | } | ||||
@@ -116,17 +116,21 @@ namespace Discord.WebSocket | |||||
{ | { | ||||
return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false); | return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false); | ||||
} | } | ||||
public SocketMessage AddMessage(ISocketUser author, MessageModel model) | |||||
public ISocketMessage CreateMessage(ISocketUser author, MessageModel model) | |||||
{ | { | ||||
var msg = new SocketMessage(this, author, model); | |||||
return _messages.Create(author, model); | |||||
} | |||||
public ISocketMessage AddMessage(ISocketUser author, MessageModel model) | |||||
{ | |||||
var msg = _messages.Create(author, model); | |||||
_messages.Add(msg); | _messages.Add(msg); | ||||
return msg; | return msg; | ||||
} | } | ||||
public SocketMessage GetMessage(ulong id) | |||||
public ISocketMessage GetMessage(ulong id) | |||||
{ | { | ||||
return _messages.Get(id); | return _messages.Get(id); | ||||
} | } | ||||
public SocketMessage RemoveMessage(ulong id) | |||||
public ISocketMessage RemoveMessage(ulong id) | |||||
{ | { | ||||
return _messages.Remove(id); | return _messages.Remove(id); | ||||
} | } | ||||
@@ -58,17 +58,21 @@ namespace Discord.WebSocket | |||||
return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false); | return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false); | ||||
} | } | ||||
public SocketMessage AddMessage(ISocketUser author, MessageModel model) | |||||
public ISocketMessage CreateMessage(ISocketUser author, MessageModel model) | |||||
{ | { | ||||
var msg = new SocketMessage(this, author, model); | |||||
return _messages.Create(author, model); | |||||
} | |||||
public ISocketMessage AddMessage(ISocketUser author, MessageModel model) | |||||
{ | |||||
var msg = _messages.Create(author, model); | |||||
_messages.Add(msg); | _messages.Add(msg); | ||||
return msg; | return msg; | ||||
} | } | ||||
public SocketMessage GetMessage(ulong id) | |||||
public ISocketMessage GetMessage(ulong id) | |||||
{ | { | ||||
return _messages.Get(id); | return _messages.Get(id); | ||||
} | } | ||||
public SocketMessage RemoveMessage(ulong id) | |||||
public ISocketMessage RemoveMessage(ulong id) | |||||
{ | { | ||||
return _messages.Remove(id); | return _messages.Remove(id); | ||||
} | } | ||||
@@ -0,0 +1,13 @@ | |||||
using Model = Discord.API.Message; | |||||
namespace Discord.WebSocket | |||||
{ | |||||
internal interface ISocketMessage : IMessage | |||||
{ | |||||
DiscordSocketClient Discord { get; } | |||||
new ISocketMessageChannel Channel { get; } | |||||
void Update(Model model, UpdateSource source); | |||||
ISocketMessage Clone(); | |||||
} | |||||
} |
@@ -0,0 +1,20 @@ | |||||
using Discord.Rest; | |||||
using Model = Discord.API.Message; | |||||
namespace Discord.WebSocket | |||||
{ | |||||
internal class SocketSystemMessage : SystemMessage, ISocketMessage | |||||
{ | |||||
internal override bool IsAttached => true; | |||||
public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | |||||
public new ISocketMessageChannel Channel => base.Channel as ISocketMessageChannel; | |||||
public SocketSystemMessage(ISocketMessageChannel channel, IUser author, Model model) | |||||
: base(channel, author, model) | |||||
{ | |||||
} | |||||
public ISocketMessage Clone() => MemberwiseClone() as ISocketMessage; | |||||
} | |||||
} |
@@ -3,18 +3,18 @@ using Model = Discord.API.Message; | |||||
namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
{ | { | ||||
internal class SocketMessage : Message | |||||
internal class SocketUserMessage : UserMessage, ISocketMessage | |||||
{ | { | ||||
internal override bool IsAttached => true; | internal override bool IsAttached => true; | ||||
public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | ||||
public new ISocketMessageChannel Channel => base.Channel as ISocketMessageChannel; | public new ISocketMessageChannel Channel => base.Channel as ISocketMessageChannel; | ||||
public SocketMessage(ISocketMessageChannel channel, IUser author, Model model) | |||||
public SocketUserMessage(ISocketMessageChannel channel, IUser author, Model model) | |||||
: base(channel, author, model) | : base(channel, author, model) | ||||
{ | { | ||||
} | } | ||||
public SocketMessage Clone() => MemberwiseClone() as SocketMessage; | |||||
public ISocketMessage Clone() => MemberwiseClone() as ISocketMessage; | |||||
} | } | ||||
} | } |