Split IMessage into IUserMessage and ISystemMessage
This commit is contained in:
@@ -6,6 +6,6 @@ namespace Discord.Commands
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Discord.Commands
|
||||
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;
|
||||
|
||||
|
||||
@@ -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 readonly object _instance;
|
||||
private readonly Func<IMessage, IReadOnlyList<object>, Task> _action;
|
||||
private readonly Func<IUserMessage, IReadOnlyList<object>, Task> _action;
|
||||
|
||||
public MethodInfo Source { get; }
|
||||
public Module Module { get; }
|
||||
@@ -66,7 +66,7 @@ namespace Discord.Commands
|
||||
_action = BuildAction(source);
|
||||
}
|
||||
|
||||
public async Task<PreconditionResult> CheckPreconditions(IMessage context)
|
||||
public async Task<PreconditionResult> CheckPreconditions(IUserMessage context)
|
||||
{
|
||||
foreach (PreconditionAttribute precondition in Module.Preconditions)
|
||||
{
|
||||
@@ -85,7 +85,7 @@ namespace Discord.Commands
|
||||
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)
|
||||
return ParseResult.FromError(searchResult);
|
||||
@@ -104,9 +104,9 @@ namespace Discord.Commands
|
||||
|
||||
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)
|
||||
return Task.FromResult(ExecuteResult.FromError(parseResult));
|
||||
@@ -127,13 +127,13 @@ namespace Discord.Commands
|
||||
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
|
||||
{
|
||||
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();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -150,8 +150,8 @@ namespace Discord.Commands
|
||||
private IReadOnlyList<CommandParameter> BuildParameters(MethodInfo methodInfo)
|
||||
{
|
||||
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);
|
||||
for (int i = 1; i < parameters.Length; i++)
|
||||
@@ -190,7 +190,7 @@ namespace Discord.Commands
|
||||
}
|
||||
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))
|
||||
throw new InvalidOperationException("Commands must return a non-generic Task.");
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Discord.Commands
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Discord.Commands
|
||||
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;
|
||||
StringBuilder argBuilder = new StringBuilder(input.Length);
|
||||
|
||||
@@ -41,8 +41,9 @@ namespace Discord.Commands
|
||||
[typeof(DateTime)] = new SimpleTypeReader<DateTime>(),
|
||||
[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(IDMChannel)] = new ChannelTypeReader<IDMChannel>(),
|
||||
[typeof(IGroupChannel)] = new ChannelTypeReader<IGroupChannel>(),
|
||||
@@ -175,8 +176,8 @@ namespace Discord.Commands
|
||||
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();
|
||||
var matches = _map.GetCommands(input).ToImmutableArray();
|
||||
@@ -187,9 +188,9 @@ namespace Discord.Commands
|
||||
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);
|
||||
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);
|
||||
if (!searchResult.IsSuccess)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{
|
||||
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;
|
||||
if (text.Length > 0 && text[0] == c)
|
||||
@@ -12,7 +12,7 @@
|
||||
}
|
||||
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;
|
||||
if (text.StartsWith(str))
|
||||
@@ -22,7 +22,7 @@
|
||||
}
|
||||
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;
|
||||
if (text.Length <= 3 || text[0] != '<' || text[1] != '@') return false;
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Discord.Commands
|
||||
internal class ChannelTypeReader<T> : TypeReader
|
||||
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;
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Discord.Commands
|
||||
_enumsByValue = byValueBuilder.ToImmutable();
|
||||
}
|
||||
|
||||
public override Task<TypeReaderResult> Read(IMessage context, string input)
|
||||
public override Task<TypeReaderResult> Read(IUserMessage context, string input)
|
||||
{
|
||||
T baseValue;
|
||||
object enumValue;
|
||||
|
||||
@@ -3,16 +3,17 @@ using System.Threading.Tasks;
|
||||
|
||||
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;
|
||||
|
||||
//By Id (1.0)
|
||||
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)
|
||||
return Task.FromResult(TypeReaderResult.FromSuccess(msg));
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Discord.Commands
|
||||
internal class RoleTypeReader<T> : TypeReader
|
||||
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;
|
||||
ulong id;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Discord.Commands
|
||||
_tryParse = PrimitiveParsers.Get<T>();
|
||||
}
|
||||
|
||||
public override Task<TypeReaderResult> Read(IMessage context, string input)
|
||||
public override Task<TypeReaderResult> Read(IUserMessage context, string input)
|
||||
{
|
||||
T value;
|
||||
if (_tryParse(input, out value))
|
||||
|
||||
@@ -4,6 +4,6 @@ namespace Discord.Commands
|
||||
{
|
||||
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
|
||||
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 guild = (context.Channel as IGuildChannel)?.Guild;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#pragma warning disable CS1591
|
||||
using Discord.Rest;
|
||||
|
||||
namespace Discord.API.Rest
|
||||
{
|
||||
public class GetChannelMessagesParams
|
||||
|
||||
@@ -10,11 +10,11 @@ namespace Discord
|
||||
IReadOnlyCollection<IMessage> CachedMessages { get; }
|
||||
|
||||
/// <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>
|
||||
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>
|
||||
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>
|
||||
Task<IMessage> GetMessageAsync(ulong id);
|
||||
/// <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.Threading.Tasks;
|
||||
using Discord.API.Rest;
|
||||
using System.Collections.Generic;
|
||||
|
||||
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>
|
||||
bool IsTTS { get; }
|
||||
/// <summary> Returns true if this message was added to its channel's pinned messages. </summary>
|
||||
@@ -17,13 +13,14 @@ namespace Discord
|
||||
string Content { get; }
|
||||
/// <summary> Gets the time this message was sent. </summary>
|
||||
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>
|
||||
IMessageChannel Channel { get; }
|
||||
/// <summary> Gets the author of this message. </summary>
|
||||
IUser Author { get; }
|
||||
|
||||
/// <summary> Returns a collection of all attachments included in this message. </summary>
|
||||
IReadOnlyCollection<IAttachment> Attachments { get; }
|
||||
/// <summary> Returns a collection of all embeds included in this message. </summary>
|
||||
@@ -34,25 +31,5 @@ namespace Discord
|
||||
IReadOnlyCollection<IRole> MentionedRoles { get; }
|
||||
/// <summary> Returns a collection of users mentioned in this message. </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
8
src/Discord.Net/Entities/Messages/ISystemMessage.cs
Normal file
8
src/Discord.Net/Entities/Messages/ISystemMessage.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Discord
|
||||
{
|
||||
public interface ISystemMessage : IMessage
|
||||
{
|
||||
/// <summary> Gets the type of this system message. </summary>
|
||||
MessageType Type { get; }
|
||||
}
|
||||
}
|
||||
29
src/Discord.Net/Entities/Messages/IUserMessage.cs
Normal file
29
src/Discord.Net/Entities/Messages/IUserMessage.cs
Normal file
@@ -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.Threading.Tasks;
|
||||
using Model = Discord.API.Channel;
|
||||
using MessageModel = Discord.API.Message;
|
||||
|
||||
namespace Discord.Rest
|
||||
{
|
||||
@@ -62,46 +63,46 @@ namespace Discord.Rest
|
||||
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 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);
|
||||
using (var file = File.OpenRead(filePath))
|
||||
{
|
||||
var args = new UploadFileParams(file) { Filename = filename, Content = text, IsTTS = isTTS };
|
||||
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 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)
|
||||
{
|
||||
var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false);
|
||||
if (model != null)
|
||||
return new Message(this, new User(model.Author.Value), model);
|
||||
return CreateIncomingMessage(model);
|
||||
return null;
|
||||
}
|
||||
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit)
|
||||
{
|
||||
var args = new GetChannelMessagesParams { Limit = limit };
|
||||
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)
|
||||
{
|
||||
var args = new GetChannelMessagesParams { Limit = limit, RelativeMessageId = fromMessageId, RelativeDirection = dir };
|
||||
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)
|
||||
{
|
||||
@@ -110,14 +111,26 @@ namespace Discord.Rest
|
||||
public async Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync()
|
||||
{
|
||||
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()
|
||||
{
|
||||
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();
|
||||
private string DebuggerDisplay => $"@{Recipient} ({Id}, DM)";
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Model = Discord.API.Channel;
|
||||
using MessageModel = Discord.API.Message;
|
||||
|
||||
namespace Discord.Rest
|
||||
{
|
||||
@@ -87,46 +88,46 @@ namespace Discord.Rest
|
||||
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 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);
|
||||
using (var file = File.OpenRead(filePath))
|
||||
{
|
||||
var args = new UploadFileParams(file) { Filename = filename, Content = text, IsTTS = isTTS };
|
||||
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 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)
|
||||
{
|
||||
var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false);
|
||||
if (model != null)
|
||||
return new Message(this, new User(model.Author.Value), model);
|
||||
return CreateIncomingMessage(model);
|
||||
return null;
|
||||
}
|
||||
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit)
|
||||
{
|
||||
var args = new GetChannelMessagesParams { Limit = limit };
|
||||
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)
|
||||
{
|
||||
var args = new GetChannelMessagesParams { Limit = limit, RelativeMessageId = fromMessageId, RelativeDirection = dir };
|
||||
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)
|
||||
{
|
||||
@@ -135,7 +136,7 @@ namespace Discord.Rest
|
||||
public async Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync()
|
||||
{
|
||||
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()
|
||||
@@ -143,6 +144,18 @@ namespace Discord.Rest
|
||||
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;
|
||||
private string DebuggerDisplay => $"@{Name} ({Id}, Group)";
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Model = Discord.API.Channel;
|
||||
using MessageModel = Discord.API.Message;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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 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);
|
||||
using (var file = File.OpenRead(filePath))
|
||||
{
|
||||
var args = new UploadFileParams(file) { Filename = filename, Content = text, IsTTS = isTTS };
|
||||
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 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)
|
||||
{
|
||||
var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false);
|
||||
if (model != null)
|
||||
return new Message(this, new User(model.Author.Value), model);
|
||||
return CreateIncomingMessage(model);
|
||||
return null;
|
||||
}
|
||||
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit)
|
||||
{
|
||||
var args = new GetChannelMessagesParams { Limit = limit };
|
||||
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)
|
||||
{
|
||||
var args = new GetChannelMessagesParams { Limit = limit, RelativeMessageId = fromMessageId, RelativeDirection = dir };
|
||||
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)
|
||||
{
|
||||
@@ -105,7 +106,7 @@ namespace Discord.Rest
|
||||
public async Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync()
|
||||
{
|
||||
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()
|
||||
@@ -113,6 +114,18 @@ namespace Discord.Rest
|
||||
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)";
|
||||
|
||||
IMessage IMessageChannel.GetCachedMessage(ulong id) => null;
|
||||
|
||||
@@ -9,28 +9,27 @@ using Model = Discord.API.Message;
|
||||
namespace Discord.Rest
|
||||
{
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
internal class Message : SnowflakeEntity, IMessage
|
||||
internal abstract class Message : SnowflakeEntity, IMessage
|
||||
{
|
||||
private bool _isMentioningEveryone;
|
||||
private long _timestampTicks;
|
||||
private long? _editedTimestampTicks;
|
||||
|
||||
public MessageType Type { get; }
|
||||
public IMessageChannel Channel { get; }
|
||||
public IUser Author { get; }
|
||||
|
||||
public bool IsTTS { 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 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 Message(IMessageChannel channel, IUser author, Model model)
|
||||
@@ -38,86 +37,21 @@ namespace Discord.Rest
|
||||
{
|
||||
Channel = channel;
|
||||
Author = author;
|
||||
Type = model.Type;
|
||||
|
||||
MentionedUsers = ImmutableArray.Create<IUser>();
|
||||
MentionedChannelIds = ImmutableArray.Create<ulong>();
|
||||
MentionedRoles = ImmutableArray.Create<IRole>();
|
||||
|
||||
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;
|
||||
|
||||
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.Timestamp.IsSpecified)
|
||||
_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)
|
||||
{
|
||||
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()
|
||||
@@ -159,23 +93,6 @@ namespace Discord.Rest
|
||||
{
|
||||
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]" : "")}";
|
||||
|
||||
22
src/Discord.Net/Rest/Entities/Messages/SystemMessage.cs
Normal file
22
src/Discord.Net/Rest/Entities/Messages/SystemMessage.cs
Normal file
@@ -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})" : "")}";
|
||||
}
|
||||
}
|
||||
175
src/Discord.Net/Rest/Entities/Messages/UserMessage.cs
Normal file
175
src/Discord.Net/Rest/Entities/Messages/UserMessage.cs
Normal file
@@ -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;
|
||||
SocketMessage cachedMsg = channel.GetMessage(data.Id);
|
||||
ISocketMessage cachedMsg = channel.GetMessage(data.Id);
|
||||
if (cachedMsg != null)
|
||||
{
|
||||
before = cachedMsg.Clone();
|
||||
@@ -1259,7 +1259,7 @@ namespace Discord.WebSocket
|
||||
//Edited message isnt in cache, create a detached one
|
||||
var author = channel.GetUser(data.Author.Value.Id, true);
|
||||
if (author != null)
|
||||
after = new Message(channel, author, data);
|
||||
after = channel.CreateMessage(author, data);
|
||||
}
|
||||
if (after != null)
|
||||
await _messageUpdatedEvent.InvokeAsync(Optional.Create(before), after).ConfigureAwait(false);
|
||||
|
||||
@@ -7,9 +7,10 @@ namespace Discord.WebSocket
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -9,51 +9,51 @@ namespace Discord.WebSocket
|
||||
{
|
||||
internal class MessageCache : MessageManager
|
||||
{
|
||||
private readonly ConcurrentDictionary<ulong, SocketMessage> _messages;
|
||||
private readonly ConcurrentDictionary<ulong, ISocketMessage> _messages;
|
||||
private readonly ConcurrentQueue<ulong> _orderedMessages;
|
||||
private readonly int _size;
|
||||
|
||||
public override IReadOnlyCollection<SocketMessage> Messages => _messages.ToReadOnlyCollection();
|
||||
public override IReadOnlyCollection<ISocketMessage> Messages => _messages.ToReadOnlyCollection();
|
||||
|
||||
public MessageCache(DiscordSocketClient discord, ISocketMessageChannel channel)
|
||||
: base(discord, channel)
|
||||
{
|
||||
_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>();
|
||||
}
|
||||
|
||||
public override void Add(SocketMessage message)
|
||||
public override void Add(ISocketMessage message)
|
||||
{
|
||||
if (_messages.TryAdd(message.Id, message))
|
||||
{
|
||||
_orderedMessages.Enqueue(message.Id);
|
||||
|
||||
ulong msgId;
|
||||
SocketMessage msg;
|
||||
ISocketMessage msg;
|
||||
while (_orderedMessages.Count > _size && _orderedMessages.TryDequeue(out msgId))
|
||||
_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);
|
||||
return msg;
|
||||
}
|
||||
|
||||
public override SocketMessage Get(ulong id)
|
||||
public override ISocketMessage Get(ulong id)
|
||||
{
|
||||
SocketMessage result;
|
||||
ISocketMessage result;
|
||||
if (_messages.TryGetValue(id, out result))
|
||||
return result;
|
||||
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) return ImmutableArray<SocketMessage>.Empty;
|
||||
if (limit == 0) return ImmutableArray<ISocketMessage>.Empty;
|
||||
|
||||
IEnumerable<ulong> cachedMessageIds;
|
||||
if (fromMessageId == null)
|
||||
@@ -67,7 +67,7 @@ namespace Discord.WebSocket
|
||||
.Take(limit)
|
||||
.Select(x =>
|
||||
{
|
||||
SocketMessage msg;
|
||||
ISocketMessage msg;
|
||||
if (_messages.TryGetValue(x, out msg))
|
||||
return msg;
|
||||
return null;
|
||||
@@ -76,7 +76,7 @@ namespace Discord.WebSocket
|
||||
.ToImmutableArray();
|
||||
}
|
||||
|
||||
public override async Task<SocketMessage> DownloadAsync(ulong id)
|
||||
public override async Task<ISocketMessage> DownloadAsync(ulong id)
|
||||
{
|
||||
var msg = Get(id);
|
||||
if (msg != null)
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Model = Discord.API.Message;
|
||||
|
||||
namespace Discord.WebSocket
|
||||
{
|
||||
@@ -13,8 +14,8 @@ namespace Discord.WebSocket
|
||||
private readonly DiscordSocketClient _discord;
|
||||
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)
|
||||
{
|
||||
@@ -22,25 +23,25 @@ namespace Discord.WebSocket
|
||||
_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);
|
||||
if (model != null)
|
||||
return new SocketMessage(_channel, new User(model.Author.Value), model);
|
||||
return Create(new User(model.Author.Value), model);
|
||||
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
|
||||
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);
|
||||
if (cachedMessages.Count == limit)
|
||||
@@ -75,9 +76,17 @@ namespace Discord.WebSocket
|
||||
else
|
||||
user = newUser;
|
||||
}
|
||||
return new SocketMessage(_channel, user, x);
|
||||
return Create(user, x);
|
||||
})).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);
|
||||
}
|
||||
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);
|
||||
return msg;
|
||||
}
|
||||
public SocketMessage GetMessage(ulong id)
|
||||
public ISocketMessage GetMessage(ulong id)
|
||||
{
|
||||
return _messages.Get(id);
|
||||
}
|
||||
public SocketMessage RemoveMessage(ulong id)
|
||||
public ISocketMessage RemoveMessage(ulong id)
|
||||
{
|
||||
return _messages.Remove(id);
|
||||
}
|
||||
|
||||
@@ -116,17 +116,21 @@ namespace Discord.WebSocket
|
||||
{
|
||||
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);
|
||||
return msg;
|
||||
}
|
||||
public SocketMessage GetMessage(ulong id)
|
||||
public ISocketMessage GetMessage(ulong id)
|
||||
{
|
||||
return _messages.Get(id);
|
||||
}
|
||||
public SocketMessage RemoveMessage(ulong id)
|
||||
public ISocketMessage RemoveMessage(ulong id)
|
||||
{
|
||||
return _messages.Remove(id);
|
||||
}
|
||||
|
||||
@@ -58,17 +58,21 @@ namespace Discord.WebSocket
|
||||
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);
|
||||
return msg;
|
||||
}
|
||||
public SocketMessage GetMessage(ulong id)
|
||||
public ISocketMessage GetMessage(ulong id)
|
||||
{
|
||||
return _messages.Get(id);
|
||||
}
|
||||
public SocketMessage RemoveMessage(ulong id)
|
||||
public ISocketMessage RemoveMessage(ulong 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
|
||||
{
|
||||
internal class SocketMessage : Message
|
||||
internal class SocketUserMessage : UserMessage, ISocketMessage
|
||||
{
|
||||
internal override bool IsAttached => true;
|
||||
|
||||
public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient;
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
public SocketMessage Clone() => MemberwiseClone() as SocketMessage;
|
||||
public ISocketMessage Clone() => MemberwiseClone() as ISocketMessage;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user