diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs
index 5b3b5bd47..042b0330b 100644
--- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs
@@ -71,10 +71,11 @@ namespace Discord.Commands
if (ChannelPermission.HasValue)
{
ChannelPermissions perms;
- if (context.Channel is IGuildChannel guildChannel)
+ IMessageChannel channel = await context.Channel.GetOrDownloadAsync().ConfigureAwait(false);
+ if (channel is IGuildChannel guildChannel)
perms = guildUser.GetPermissions(guildChannel);
else
- perms = ChannelPermissions.All(context.Channel);
+ perms = ChannelPermissions.All(channel);
if (!perms.Has(ChannelPermission.Value))
return PreconditionResult.FromError(ErrorMessage ?? $"Bot requires channel permission {ChannelPermission.Value}.");
diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs
index a27469c88..9d68b3aab 100644
--- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs
@@ -17,10 +17,6 @@ namespace Discord.Commands
/// Specifies the command to be executed within a DM.
///
DM = 0x02,
- ///
- /// Specifies the command to be executed within a group.
- ///
- Group = 0x04
}
///
@@ -59,11 +55,9 @@ namespace Discord.Commands
bool isValid = false;
if ((Contexts & ContextType.Guild) != 0)
- isValid = context.Channel is IGuildChannel;
+ isValid = context.GuildId != null;
if ((Contexts & ContextType.DM) != 0)
- isValid = isValid || context.Channel is IDMChannel;
- if ((Contexts & ContextType.Group) != 0)
- isValid = isValid || context.Channel is IGroupChannel;
+ isValid = context.GuildId == null;
if (isValid)
return Task.FromResult(PreconditionResult.FromSuccess());
diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireNsfwAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireNsfwAttribute.cs
index 2a9647cd2..6ee296071 100644
--- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireNsfwAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireNsfwAttribute.cs
@@ -36,7 +36,7 @@ namespace Discord.Commands
///
public override Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
- if (context.Channel is ITextChannel text && text.IsNsfw)
+ if (context.Channel.HasValue && context.Channel.Value is ITextChannel text && text.IsNsfw)
return Task.FromResult(PreconditionResult.FromSuccess());
else
return Task.FromResult(PreconditionResult.FromError(ErrorMessage ?? "This command may only be invoked in an NSFW channel."));
diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs
index 2908a18c1..447363d87 100644
--- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs
@@ -54,31 +54,32 @@ namespace Discord.Commands
}
///
- public override Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
+ public override async Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
var guildUser = context.User as IGuildUser;
if (GuildPermission.HasValue)
{
if (guildUser == null)
- return Task.FromResult(PreconditionResult.FromError(NotAGuildErrorMessage ?? "Command must be used in a guild channel."));
+ return PreconditionResult.FromError(NotAGuildErrorMessage ?? "Command must be used in a guild channel.");
if (!guildUser.GuildPermissions.Has(GuildPermission.Value))
- return Task.FromResult(PreconditionResult.FromError(ErrorMessage ?? $"User requires guild permission {GuildPermission.Value}."));
+ return PreconditionResult.FromError(ErrorMessage ?? $"User requires guild permission {GuildPermission.Value}.");
}
if (ChannelPermission.HasValue)
{
ChannelPermissions perms;
- if (context.Channel is IGuildChannel guildChannel)
+ IMessageChannel channel = await context.Channel.GetOrDownloadAsync().ConfigureAwait(false);
+ if (channel is IGuildChannel guildChannel)
perms = guildUser.GetPermissions(guildChannel);
else
- perms = ChannelPermissions.All(context.Channel);
+ perms = ChannelPermissions.All(channel);
if (!perms.Has(ChannelPermission.Value))
- return Task.FromResult(PreconditionResult.FromError(ErrorMessage ?? $"User requires channel permission {ChannelPermission.Value}."));
+ return PreconditionResult.FromError(ErrorMessage ?? $"User requires channel permission {ChannelPermission.Value}.");
}
- return Task.FromResult(PreconditionResult.FromSuccess());
+ return PreconditionResult.FromSuccess();
}
}
}
diff --git a/src/Discord.Net.Commands/CommandContext.cs b/src/Discord.Net.Commands/CommandContext.cs
index 393cdf97a..200d7bc48 100644
--- a/src/Discord.Net.Commands/CommandContext.cs
+++ b/src/Discord.Net.Commands/CommandContext.cs
@@ -8,24 +8,27 @@ namespace Discord.Commands
///
public IGuild Guild { get; }
///
- public IMessageChannel Channel { get; }
+ public ulong? GuildId { get; }
+ ///
+ public Cacheable Channel { get; }
///
public IUser User { get; }
///
public IUserMessage Message { get; }
/// Indicates whether the channel that the command is executed in is a private channel.
- public bool IsPrivate => Channel is IPrivateChannel;
+ public bool IsPrivate => GuildId == null;
///
/// Initializes a new class with the provided client and message.
///
/// The underlying client.
/// The underlying message.
- public CommandContext(IDiscordClient client, IUserMessage msg)
+ public CommandContext(IDiscordClient client, IUserMessage msg, IGuild guild)
{
Client = client;
- Guild = (msg.Channel as IGuildChannel)?.Guild;
+ GuildId = msg.GuildId;
+ Guild = guild;
Channel = msg.Channel;
User = msg.Author;
Message = msg;
diff --git a/src/Discord.Net.Commands/ModuleBase.cs b/src/Discord.Net.Commands/ModuleBase.cs
index 6ec2db54d..1a527298e 100644
--- a/src/Discord.Net.Commands/ModuleBase.cs
+++ b/src/Discord.Net.Commands/ModuleBase.cs
@@ -36,9 +36,9 @@ namespace Discord.Commands
/// If null, all mentioned roles and users will be notified.
///
/// The message references to be included. Used to reply to specific messages.
- protected virtual async Task ReplyAsync(string message = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null)
+ protected virtual async Task ReplyAsync(string message = null, bool isTTS = false, Embed embed = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, RequestOptions options = null)
{
- return await Context.Channel.SendMessageAsync(message, isTTS, embed, options, allowedMentions, messageReference).ConfigureAwait(false);
+ return await Context.Client.SendMessageAsync(Context.Channel.Id, message, isTTS, embed, allowedMentions, messageReference, options).ConfigureAwait(false);
}
///
/// The method to execute before executing the command.
diff --git a/src/Discord.Net.Commands/Readers/MessageTypeReader.cs b/src/Discord.Net.Commands/Readers/MessageTypeReader.cs
index acec2f12d..515901221 100644
--- a/src/Discord.Net.Commands/Readers/MessageTypeReader.cs
+++ b/src/Discord.Net.Commands/Readers/MessageTypeReader.cs
@@ -17,7 +17,7 @@ namespace Discord.Commands
//By Id (1.0)
if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out ulong id))
{
- if (await context.Channel.GetMessageAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) is T msg)
+ if (context.Channel.HasValue && await context.Channel.Value.GetMessageAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) is T msg)
return TypeReaderResult.FromSuccess(msg);
}
diff --git a/src/Discord.Net.Commands/Readers/UserTypeReader.cs b/src/Discord.Net.Commands/Readers/UserTypeReader.cs
index c0104e341..b1fbd0bdd 100644
--- a/src/Discord.Net.Commands/Readers/UserTypeReader.cs
+++ b/src/Discord.Net.Commands/Readers/UserTypeReader.cs
@@ -18,7 +18,9 @@ namespace Discord.Commands
public override async Task ReadAsync(ICommandContext context, string input, IServiceProvider services)
{
var results = new Dictionary();
- IAsyncEnumerable channelUsers = context.Channel.GetUsersAsync(CacheMode.CacheOnly).Flatten(); // it's better
+ IAsyncEnumerable channelUsers = context.Channel.HasValue
+ ? context.Channel.Value.GetUsersAsync(CacheMode.CacheOnly).Flatten()
+ : AsyncEnumerable.Empty();
IReadOnlyCollection guildUsers = ImmutableArray.Create();
if (context.Guild != null)
@@ -29,8 +31,8 @@ namespace Discord.Commands
{
if (context.Guild != null)
AddResult(results, await context.Guild.GetUserAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 1.00f);
- else
- AddResult(results, await context.Channel.GetUserAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 1.00f);
+ else if (context.Channel.HasValue)
+ AddResult(results, await context.Channel.Value.GetUserAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 1.00f);
}
//By Id (0.9)
@@ -38,8 +40,8 @@ namespace Discord.Commands
{
if (context.Guild != null)
AddResult(results, await context.Guild.GetUserAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 0.90f);
- else
- AddResult(results, await context.Channel.GetUserAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 0.90f);
+ else if (context.Channel.HasValue)
+ AddResult(results, await context.Channel.Value.GetUserAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 0.90f);
}
//By Username + Discriminator (0.7-0.85)
diff --git a/src/Discord.Net.Core/Commands/ICommandContext.cs b/src/Discord.Net.Core/Commands/ICommandContext.cs
index d56eb38a0..bd85e343f 100644
--- a/src/Discord.Net.Core/Commands/ICommandContext.cs
+++ b/src/Discord.Net.Core/Commands/ICommandContext.cs
@@ -14,9 +14,14 @@ namespace Discord.Commands
///
IGuild Guild { get; }
///
- /// Gets the that the command is executed in.
+ /// Gets the guild id that the command is executed in if it was in one.
///
- IMessageChannel Channel { get; }
+ ulong? GuildId { get; }
+ ///
+ /// Gets the that the command is executed in if cached;
+ /// otherwise just the channel id.
+ ///
+ Cacheable Channel { get; }
///
/// Gets the who executed the command.
///
diff --git a/src/Discord.Net.Core/Entities/Messages/IMessage.cs b/src/Discord.Net.Core/Entities/Messages/IMessage.cs
index b5023eb59..85bd90f38 100644
--- a/src/Discord.Net.Core/Entities/Messages/IMessage.cs
+++ b/src/Discord.Net.Core/Entities/Messages/IMessage.cs
@@ -66,11 +66,15 @@ namespace Discord
/// Time of when the message was last edited; null if the message is never edited.
///
DateTimeOffset? EditedTimestamp { get; }
-
+
///
/// Gets the source channel of the message.
///
- IMessageChannel Channel { get; }
+ Cacheable Channel { get; }
+ ///
+ /// Gets the source guild id of the message if it was sent in one.
+ ///
+ ulong? GuildId { get; }
///
/// Gets the author of this message.
///
@@ -171,7 +175,7 @@ namespace Discord
/// A read-only collection of sticker objects.
///
IReadOnlyCollection Stickers { get; }
-
+
///
/// Gets the flags related to this message.
///
diff --git a/src/Discord.Net.Core/Extensions/MessageExtensions.cs b/src/Discord.Net.Core/Extensions/MessageExtensions.cs
index b043d7b77..6adae7233 100644
--- a/src/Discord.Net.Core/Extensions/MessageExtensions.cs
+++ b/src/Discord.Net.Core/Extensions/MessageExtensions.cs
@@ -17,7 +17,7 @@ namespace Discord
public static string GetJumpUrl(this IMessage msg)
{
var channel = msg.Channel;
- return $"https://discord.com/channels/{(channel is IDMChannel ? "@me" : $"{(channel as ITextChannel).GuildId}")}/{channel.Id}/{msg.Id}";
+ return $"https://discord.com/channels/{(msg.GuildId.HasValue ? $"{msg.GuildId}" : "@me")}/{channel.Id}/{msg.Id}";
}
///
@@ -89,7 +89,8 @@ namespace Discord
///
public static async Task ReplyAsync(this IUserMessage msg, string text = null, bool isTTS = false, Embed embed = null, AllowedMentions allowedMentions = null, RequestOptions options = null)
{
- return await msg.Channel.SendMessageAsync(text, isTTS, embed, options, allowedMentions, new MessageReference(messageId: msg.Id)).ConfigureAwait(false);
+ IMessageChannel channel = await msg.Channel.GetOrDownloadAsync().ConfigureAwait(false);
+ return await channel.SendMessageAsync(text, isTTS, embed, options, allowedMentions, new MessageReference(messageId: msg.Id)).ConfigureAwait(false);
}
}
}
diff --git a/src/Discord.Net.Core/IDiscordClient.cs b/src/Discord.Net.Core/IDiscordClient.cs
index d7d6d2856..e0dfa96e7 100644
--- a/src/Discord.Net.Core/IDiscordClient.cs
+++ b/src/Discord.Net.Core/IDiscordClient.cs
@@ -284,5 +284,24 @@ namespace Discord
/// that represents the gateway information related to the bot.
///
Task GetBotGatewayAsync(RequestOptions options = null);
+
+ ///
+ /// Sends a message to a channel.
+ ///
+ /// The channel to send the message.
+ /// The message to be sent.
+ /// Determines whether the message should be read aloud by Discord or not.
+ /// The to be sent.
+ ///
+ /// Specifies if notifications are sent for mentioned users and roles in the message .
+ /// If null, all mentioned roles and users will be notified.
+ ///
+ /// The reference for this message.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents an asynchronous send operation for delivering the message. The task result
+ /// contains the sent message.
+ ///
+ Task SendMessageAsync(ulong channelId, string text = null, bool isTTS = false, Embed embed = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, RequestOptions options = null);
}
}
diff --git a/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs b/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs
index 27d393c07..7f7f0a5f1 100644
--- a/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs
+++ b/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs
@@ -84,12 +84,12 @@ namespace Discord.Net.Examples.WebSocket
// check if the message is a user message as opposed to a system message (e.g. Clyde, pins, etc.)
if (!(message is SocketUserMessage userMessage)) return Task.CompletedTask;
// check if the message origin is a guild message channel
- if (!(userMessage.Channel is SocketTextChannel textChannel)) return Task.CompletedTask;
+ if (userMessage.GuildId == null) return Task.CompletedTask;
// check if the target user was mentioned
var targetUsers = userMessage.MentionedUsers.Where(x => _targetUserIds.Contains(x.Id));
foreach (var targetUser in targetUsers)
Console.WriteLine(
- $"{targetUser} was mentioned in the message '{message.Content}' by {message.Author} in {textChannel.Name}.");
+ $"{targetUser} was mentioned in the message '{message.Content}' by {message.Author} in {MentionUtils.MentionChannel(message.Channel.Id)}.");
return Task.CompletedTask;
}
diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs
index 68589a4f1..044f3f6b3 100644
--- a/src/Discord.Net.Rest/BaseDiscordClient.cs
+++ b/src/Discord.Net.Rest/BaseDiscordClient.cs
@@ -216,6 +216,10 @@ namespace Discord.Rest
Task IDiscordClient.GetWebhookAsync(ulong id, RequestOptions options)
=> Task.FromResult(null);
+ ///
+ Task IDiscordClient.SendMessageAsync(ulong channelId, string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, RequestOptions options)
+ => Task.FromResult(null);
+
///
Task IDiscordClient.StartAsync()
=> Task.Delay(0);
diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs
index b5bdc4235..237132cb5 100644
--- a/src/Discord.Net.Rest/DiscordRestClient.cs
+++ b/src/Discord.Net.Rest/DiscordRestClient.cs
@@ -114,11 +114,36 @@ namespace Discord.Rest
=> MessageHelper.RemoveAllReactionsAsync(channelId, messageId, this, options);
public Task RemoveAllReactionsForEmoteAsync(ulong channelId, ulong messageId, IEmote emote, RequestOptions options = null)
=> MessageHelper.RemoveAllReactionsForEmoteAsync(channelId, messageId, emote, this, options);
+
+ ///
+ /// Sends a message to a channel.
+ ///
+ /// The channel to send the message.
+ /// The message to be sent.
+ /// Determines whether the message should be read aloud by Discord or not.
+ /// The to be sent.
+ ///
+ /// Specifies if notifications are sent for mentioned users and roles in the message .
+ /// If null, all mentioned roles and users will be notified.
+ ///
+ /// The reference for this message.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents an asynchronous send operation for delivering the message. The task result
+ /// contains the sent message.
+ ///
+ public Task SendMessageAsync(ulong channelId, string text = null, bool isTTS = false, Embed embed = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, RequestOptions options = null)
+ => ChannelHelper.SendMessageAsync(channelId, this, text, isTTS, embed, allowedMentions, messageReference, options ?? RequestOptions.Default);
+
//IDiscordClient
///
async Task IDiscordClient.GetApplicationInfoAsync(RequestOptions options)
=> await GetApplicationInfoAsync(options).ConfigureAwait(false);
+ ///
+ async Task IDiscordClient.SendMessageAsync(ulong channelId, string message, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, RequestOptions options)
+ => await SendMessageAsync(channelId, message, isTTS, embed, allowedMentions, messageReference, options ?? RequestOptions.Default).ConfigureAwait(false);
+
///
async Task IDiscordClient.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options)
{
diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
index 22395ab3a..3b395cf95 100644
--- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
@@ -223,7 +223,34 @@ namespace Discord.Rest
var args = new CreateMessageParams(text) { IsTTS = isTTS, Embed = embed?.ToModel(), AllowedMentions = allowedMentions?.ToModel(), MessageReference = messageReference?.ToModel() };
var model = await client.ApiClient.CreateMessageAsync(channel.Id, args, options).ConfigureAwait(false);
- return RestUserMessage.Create(client, channel, client.CurrentUser, model);
+ return (RestUserMessage)RestMessage.Create(client, channel, client.CurrentUser, model);
+ }
+ /// Message content is too long, length must be less or equal to .
+ public static async Task SendMessageAsync(ulong channelId, BaseDiscordClient client,
+ string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, RequestOptions options)
+ {
+ Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed.");
+ Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed.");
+
+ // check that user flag and user Id list are exclusive, same with role flag and role Id list
+ if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue)
+ {
+ if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) &&
+ allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
+ {
+ throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
+ }
+
+ if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
+ allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
+ {
+ throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
+ }
+ }
+
+ var args = new CreateMessageParams(text) { IsTTS = isTTS, Embed = embed?.ToModel(), AllowedMentions = allowedMentions?.ToModel(), MessageReference = messageReference?.ToModel() };
+ var model = await client.ApiClient.CreateMessageAsync(channelId, args, options).ConfigureAwait(false);
+ return (RestUserMessage)RestMessage.Create(client, null, client.CurrentUser, model);
}
///
@@ -283,14 +310,14 @@ namespace Discord.Rest
var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS, Embed = embed?.ToModel() ?? Optional.Unspecified, AllowedMentions = allowedMentions?.ToModel() ?? Optional.Unspecified, MessageReference = messageReference?.ToModel() ?? Optional.Unspecified, IsSpoiler = isSpoiler };
var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false);
- return RestUserMessage.Create(client, channel, client.CurrentUser, model);
+ return (RestUserMessage)RestMessage.Create(client, channel, client.CurrentUser, model);
}
public static async Task ModifyMessageAsync(IMessageChannel channel, ulong messageId, Action func,
BaseDiscordClient client, RequestOptions options)
{
var msgModel = await MessageHelper.ModifyAsync(channel.Id, messageId, client, func, options).ConfigureAwait(false);
- return RestUserMessage.Create(client, channel, msgModel.Author.IsSpecified ? RestUser.Create(client, msgModel.Author.Value) : client.CurrentUser, msgModel);
+ return (RestUserMessage)RestMessage.Create(client, channel, msgModel.Author.IsSpecified ? RestUser.Create(client, msgModel.Author.Value) : client.CurrentUser, msgModel);
}
public static Task DeleteMessageAsync(IMessageChannel channel, ulong messageId, BaseDiscordClient client,
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
index 0c54743a6..4cff43463 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
@@ -16,7 +16,9 @@ namespace Discord.Rest
private ImmutableArray _reactions = ImmutableArray.Create();
///
- public IMessageChannel Channel { get; }
+ public Cacheable Channel { get; }
+ ///
+ public ulong? GuildId { get; private set; }
///
/// Gets the Author of the message.
///
@@ -74,7 +76,7 @@ namespace Discord.Rest
///
public MessageType Type { get; private set; }
- internal RestMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source)
+ internal RestMessage(BaseDiscordClient discord, ulong id, Cacheable channel, IUser author, MessageSource source)
: base(discord, id)
{
Channel = channel;
@@ -83,15 +85,24 @@ namespace Discord.Rest
}
internal static RestMessage Create(BaseDiscordClient discord, IMessageChannel channel, IUser author, Model model)
{
+ var cacheableChannel = new Cacheable(
+ channel,
+ model.ChannelId,
+ channel != null,
+ async () => (IMessageChannel)await ClientHelper.GetChannelAsync(discord, model.ChannelId, RequestOptions.Default).ConfigureAwait(false));
+
if (model.Type == MessageType.Default || model.Type == MessageType.Reply)
- return RestUserMessage.Create(discord, channel, author, model);
+ return RestUserMessage.Create(discord, cacheableChannel, author, model);
else
- return RestSystemMessage.Create(discord, channel, author, model);
+ return RestSystemMessage.Create(discord, cacheableChannel, author, model);
}
internal virtual void Update(Model model)
{
Type = model.Type;
+ if (model.GuildId.IsSpecified)
+ GuildId = model.GuildId.Value;
+
if (model.Timestamp.IsSpecified)
_timestampTicks = model.Timestamp.Value.UtcTicks;
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs
index 1c59d4f45..b5e21109e 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs
@@ -9,11 +9,11 @@ namespace Discord.Rest
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestSystemMessage : RestMessage, ISystemMessage
{
- internal RestSystemMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author)
+ internal RestSystemMessage(BaseDiscordClient discord, ulong id, Cacheable channel, IUser author)
: base(discord, id, channel, author, MessageSource.System)
{
}
- internal new static RestSystemMessage Create(BaseDiscordClient discord, IMessageChannel channel, IUser author, Model model)
+ internal static RestSystemMessage Create(BaseDiscordClient discord, Cacheable channel, IUser author, Model model)
{
var entity = new RestSystemMessage(discord, model.Id, channel, author);
entity.Update(model);
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
index aa6b44da6..629f14fca 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
@@ -50,11 +50,11 @@ namespace Discord.Rest
///
public IUserMessage ReferencedMessage => _referencedMessage;
- internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source)
+ internal RestUserMessage(BaseDiscordClient discord, ulong id, Cacheable channel, IUser author, MessageSource source)
: base(discord, id, channel, author, source)
{
}
- internal new static RestUserMessage Create(BaseDiscordClient discord, IMessageChannel channel, IUser author, Model model)
+ internal static RestUserMessage Create(BaseDiscordClient discord, Cacheable channel, IUser author, Model model)
{
var entity = new RestUserMessage(discord, model.Id, channel, author, MessageHelper.GetSource(model));
entity.Update(model);
@@ -120,7 +120,7 @@ namespace Discord.Rest
}
}
- var guildId = (Channel as IGuildChannel)?.GuildId;
+ ulong? guildId = model.GuildId.IsSpecified ? model.GuildId.Value : null;
var guild = guildId != null ? (Discord as IDiscordClient).GetGuildAsync(guildId.Value, CacheMode.CacheOnly).Result : null;
if (model.Content.IsSpecified)
{
@@ -177,7 +177,7 @@ namespace Discord.Rest
/// This operation may only be called on a channel.
public async Task CrosspostAsync(RequestOptions options = null)
{
- if (!(Channel is INewsChannel))
+ if (Channel.HasValue && !(Channel.Value is INewsChannel))
{
throw new InvalidOperationException("Publishing (crossposting) is only valid in news channels.");
}
diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.cs b/src/Discord.Net.WebSocket/BaseSocketClient.cs
index 1cfe6c8bf..6704225c1 100644
--- a/src/Discord.Net.WebSocket/BaseSocketClient.cs
+++ b/src/Discord.Net.WebSocket/BaseSocketClient.cs
@@ -268,12 +268,35 @@ namespace Discord.WebSocket
///
public Task GetInviteAsync(string inviteId, RequestOptions options = null)
=> ClientHelper.GetInviteAsync(this, inviteId, options ?? RequestOptions.Default);
-
+ ///
+ /// Sends a message to a channel.
+ ///
+ /// The channel to send the message.
+ /// The message to be sent.
+ /// Determines whether the message should be read aloud by Discord or not.
+ /// The to be sent.
+ ///
+ /// Specifies if notifications are sent for mentioned users and roles in the message .
+ /// If null, all mentioned roles and users will be notified.
+ ///
+ /// The reference for this message.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents an asynchronous send operation for delivering the message. The task result
+ /// contains the sent message.
+ ///
+ public Task SendMessageAsync(ulong channelId, string text = null, bool isTTS = false, Embed embed = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, RequestOptions options = null)
+ => ChannelHelper.SendMessageAsync(channelId, this, text, isTTS, embed, allowedMentions, messageReference, options ?? RequestOptions.Default);
+
// IDiscordClient
///
async Task IDiscordClient.GetApplicationInfoAsync(RequestOptions options)
=> await GetApplicationInfoAsync(options).ConfigureAwait(false);
+ ///
+ async Task IDiscordClient.SendMessageAsync(ulong channelId, string message, bool isTTS, Embed embed, AllowedMentions allowedMentions, MessageReference messageReference, RequestOptions options)
+ => await SendMessageAsync(channelId, message, isTTS, embed, allowedMentions, messageReference, options ?? RequestOptions.Default).ConfigureAwait(false);
+
///
Task IDiscordClient.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options)
=> Task.FromResult(GetChannel(id));
diff --git a/src/Discord.Net.WebSocket/Commands/ShardedCommandContext.cs b/src/Discord.Net.WebSocket/Commands/ShardedCommandContext.cs
index f970c32fc..6eb8e2639 100644
--- a/src/Discord.Net.WebSocket/Commands/ShardedCommandContext.cs
+++ b/src/Discord.Net.WebSocket/Commands/ShardedCommandContext.cs
@@ -9,7 +9,7 @@ namespace Discord.Commands
public new DiscordShardedClient Client { get; }
public ShardedCommandContext(DiscordShardedClient client, SocketUserMessage msg)
- : base(client.GetShard(GetShardId(client, (msg.Channel as SocketGuildChannel)?.Guild)), msg)
+ : base(client.GetShardFor(msg.GuildId ?? 0), msg)
{
Client = client;
}
diff --git a/src/Discord.Net.WebSocket/Commands/SocketCommandContext.cs b/src/Discord.Net.WebSocket/Commands/SocketCommandContext.cs
index f4d517909..440aaa323 100644
--- a/src/Discord.Net.WebSocket/Commands/SocketCommandContext.cs
+++ b/src/Discord.Net.WebSocket/Commands/SocketCommandContext.cs
@@ -15,10 +15,10 @@ namespace Discord.Commands
/// Gets the that the command is executed in.
///
public SocketGuild Guild { get; }
- ///
- /// Gets the that the command is executed in.
- ///
- public ISocketMessageChannel Channel { get; }
+ ///
+ public ulong? GuildId { get; }
+ ///
+ public Cacheable Channel { get; }
///
/// Gets the who executed the command.
///
@@ -31,7 +31,7 @@ namespace Discord.Commands
///
/// Indicates whether the channel that the command is executed in is a private channel.
///
- public bool IsPrivate => Channel is IPrivateChannel;
+ public bool IsPrivate => GuildId == null;
///
/// Initializes a new class with the provided client and message.
@@ -41,7 +41,9 @@ namespace Discord.Commands
public SocketCommandContext(DiscordSocketClient client, SocketUserMessage msg)
{
Client = client;
- Guild = (msg.Channel as SocketGuildChannel)?.Guild;
+ GuildId = msg.GuildId;
+ if (msg.GuildId != null)
+ Guild = client.GetGuild(msg.GuildId.Value);
Channel = msg.Channel;
User = msg.Author;
Message = msg;
@@ -53,7 +55,7 @@ namespace Discord.Commands
///
IGuild ICommandContext.Guild => Guild;
///
- IMessageChannel ICommandContext.Channel => Channel;
+ Cacheable ICommandContext.Channel => Channel;
///
IUser ICommandContext.User => User;
///
diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
index e6d05b525..ce5f59016 100644
--- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
@@ -179,11 +179,11 @@ namespace Discord.WebSocket
return _shards[id];
return null;
}
- private int GetShardIdFor(ulong guildId)
+ public int GetShardIdFor(ulong guildId)
=> (int)((guildId >> 22) % (uint)_totalShards);
public int GetShardIdFor(IGuild guild)
=> GetShardIdFor(guild?.Id ?? 0);
- private DiscordSocketClient GetShardFor(ulong guildId)
+ public DiscordSocketClient GetShardFor(ulong guildId)
=> GetShard(GetShardIdFor(guildId));
public DiscordSocketClient GetShardFor(IGuild guild)
=> GetShardFor(guild?.Id ?? 0);
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
index 2cdff662c..08781484f 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
@@ -1263,24 +1263,16 @@ namespace Discord.WebSocket
var channel = GetChannel(data.ChannelId) as ISocketMessageChannel;
var guild = (channel as SocketGuildChannel)?.Guild;
+ if (channel == null && data.GuildId.IsSpecified)
+ guild = State.GetGuild(data.GuildId.Value);
if (guild != null && !guild.IsSynced)
{
await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
return;
}
- if (channel == null)
- {
- if (!data.GuildId.IsSpecified) // assume it is a DM
- {
- channel = CreateDMChannel(data.ChannelId, data.Author.Value, State);
- }
- else
- {
- await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
- return;
- }
- }
+ if (channel == null && !data.GuildId.IsSpecified) // assume it is a DM
+ channel = CreateDMChannel(data.ChannelId, data.Author.Value, State);
SocketUser author;
if (guild != null)
@@ -1291,7 +1283,7 @@ namespace Discord.WebSocket
author = guild.GetUser(data.Author.Value.Id);
}
else
- author = (channel as SocketChannel).GetUser(data.Author.Value.Id);
+ author = (channel as SocketChannel)?.GetUser(data.Author.Value.Id);
if (author == null)
{
@@ -1309,13 +1301,14 @@ namespace Discord.WebSocket
author = groupChannel.GetOrAddUser(data.Author.Value);
else
{
- await UnknownChannelUserAsync(type, data.Author.Value.Id, channel.Id).ConfigureAwait(false);
+ await UnknownChannelUserAsync(type, data.Author.Value.Id, data.ChannelId).ConfigureAwait(false);
return;
}
}
var msg = SocketMessage.Create(this, State, author, channel, data);
- SocketChannelHelper.AddMessage(channel, this, msg);
+ if (channel != null)
+ SocketChannelHelper.AddMessage(channel, this, msg);
await TimedInvokeAsync(_messageReceivedEvent, nameof(MessageReceived), msg).ConfigureAwait(false);
}
break;
@@ -1327,6 +1320,8 @@ namespace Discord.WebSocket
var channel = GetChannel(data.ChannelId) as ISocketMessageChannel;
var guild = (channel as SocketGuildChannel)?.Guild;
+ if (channel == null && data.GuildId.IsSpecified)
+ guild = State.GetGuild(data.GuildId.Value);
if (guild != null && !guild.IsSynced)
{
await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
@@ -1391,11 +1386,6 @@ namespace Discord.WebSocket
else
channel = CreateDMChannel(data.ChannelId, author, State);
}
- else
- {
- await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
- return;
- }
}
after = SocketMessage.Create(this, State, author, channel, data);
diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
index 353c26fb8..b3103d534 100644
--- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
+++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
@@ -23,13 +23,10 @@ namespace Discord.WebSocket
/// A WebSocket-based user object.
///
public SocketUser Author { get; }
- ///
- /// Gets the source channel of the message.
- ///
- ///
- /// A WebSocket-based message channel.
- ///
- public ISocketMessageChannel Channel { get; }
+ ///
+ public Cacheable Channel { get; }
+ ///
+ public ulong? GuildId { get; private set; }
///
public MessageSource Source { get; }
@@ -85,13 +82,8 @@ namespace Discord.WebSocket
/// Collection of WebSocket-based guild channels.
///
public virtual IReadOnlyCollection MentionedChannels => ImmutableArray.Create();
- ///
- /// Returns the roles mentioned in this message.
- ///
- ///
- /// Collection of WebSocket-based roles.
- ///
- public virtual IReadOnlyCollection MentionedRoles => ImmutableArray.Create();
+ ///
+ public virtual IReadOnlyCollection MentionedRoleIds => ImmutableArray.Create();
///
/// Returns the users mentioned in this message.
///
@@ -109,24 +101,33 @@ namespace Discord.WebSocket
///
public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks);
- internal SocketMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source)
+ internal SocketMessage(DiscordSocketClient discord, ulong id, Cacheable channel, SocketUser author, MessageSource source)
: base(discord, id)
{
Channel = channel;
Author = author;
Source = source;
}
- internal static SocketMessage Create(DiscordSocketClient discord, ClientState state, SocketUser author, ISocketMessageChannel channel, Model model)
+ internal static SocketMessage Create(DiscordSocketClient discord, ClientState state, SocketUser author, IMessageChannel channel, Model model)
{
+ var cacheableChannel = new Cacheable(
+ channel,
+ model.ChannelId,
+ channel != null,
+ async () => (IMessageChannel)await ClientHelper.GetChannelAsync(discord, model.ChannelId, RequestOptions.Default).ConfigureAwait(false));
+
if (model.Type == MessageType.Default || model.Type == MessageType.Reply)
- return SocketUserMessage.Create(discord, state, author, channel, model);
+ return SocketUserMessage.Create(discord, state, author, cacheableChannel, model);
else
- return SocketSystemMessage.Create(discord, state, author, channel, model);
+ return SocketSystemMessage.Create(discord, state, author, cacheableChannel, model);
}
internal virtual void Update(ClientState state, Model model)
{
Type = model.Type;
+ if (model.GuildId.IsSpecified)
+ GuildId = model.GuildId.Value;
+
if (model.Timestamp.IsSpecified)
_timestampTicks = model.Timestamp.Value.UtcTicks;
@@ -188,16 +189,12 @@ namespace Discord.WebSocket
///
IUser IMessage.Author => Author;
///
- IMessageChannel IMessage.Channel => Channel;
- ///
IReadOnlyCollection IMessage.Attachments => Attachments;
///
IReadOnlyCollection IMessage.Embeds => Embeds;
///
IReadOnlyCollection IMessage.MentionedChannelIds => MentionedChannels.Select(x => x.Id).ToImmutableArray();
///
- IReadOnlyCollection IMessage.MentionedRoleIds => MentionedRoles.Select(x => x.Id).ToImmutableArray();
- ///
IReadOnlyCollection IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray();
///
IReadOnlyCollection IMessage.Stickers => Stickers;
diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs
index ec22a7703..41f9578fd 100644
--- a/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs
+++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs
@@ -9,11 +9,11 @@ namespace Discord.WebSocket
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class SocketSystemMessage : SocketMessage, ISystemMessage
{
- internal SocketSystemMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author)
+ internal SocketSystemMessage(DiscordSocketClient discord, ulong id, Cacheable channel, SocketUser author)
: base(discord, id, channel, author, MessageSource.System)
{
}
- internal new static SocketSystemMessage Create(DiscordSocketClient discord, ClientState state, SocketUser author, ISocketMessageChannel channel, Model model)
+ internal static SocketSystemMessage Create(DiscordSocketClient discord, ClientState state, SocketUser author, Cacheable channel, Model model)
{
var entity = new SocketSystemMessage(discord, model.Id, channel, author);
entity.Update(state, model);
diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs
index 597544f4d..f47709725 100644
--- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs
+++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs
@@ -21,7 +21,7 @@ namespace Discord.WebSocket
private ImmutableArray _attachments = ImmutableArray.Create();
private ImmutableArray