diff --git a/src/Discord.Net.Core/Entities/Messages/IMessage.cs b/src/Discord.Net.Core/Entities/Messages/IMessage.cs
index 6bb44368b..4266f893a 100644
--- a/src/Discord.Net.Core/Entities/Messages/IMessage.cs
+++ b/src/Discord.Net.Core/Entities/Messages/IMessage.cs
@@ -7,12 +7,12 @@ namespace Discord
{
/// Gets the type of this system message.
MessageType Type { get; }
+ /// Gets the source of this message.
+ MessageSource Source { get; }
/// Returns true if this message was sent as a text-to-speech message.
bool IsTTS { get; }
/// Returns true if this message was added to its channel's pinned messages.
bool IsPinned { get; }
- /// Returns true if this message was created using a webhook.
- bool IsWebhook { get; }
/// Returns the content for this message.
string Content { get; }
/// Gets the time this message was sent.
@@ -24,8 +24,6 @@ namespace Discord
IMessageChannel Channel { get; }
/// Gets the author of this message.
IUser Author { get; }
- /// Gets the id of the webhook used to created this message, if any.
- ulong? WebhookId { get; }
/// Returns all attachments included in this message.
IReadOnlyCollection Attachments { get; }
diff --git a/src/Discord.Net.Core/Entities/Messages/MessageSource.cs b/src/Discord.Net.Core/Entities/Messages/MessageSource.cs
new file mode 100644
index 000000000..1cb2f8b94
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Messages/MessageSource.cs
@@ -0,0 +1,10 @@
+namespace Discord
+{
+ public enum MessageSource
+ {
+ System,
+ User,
+ Bot,
+ Webhook
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
index 2824a1426..054b80119 100644
--- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
+++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
@@ -7,22 +7,24 @@ namespace Discord
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public struct ChannelPermissions
{
- //TODO: C#7 Candidate for binary literals
- private static ChannelPermissions _allDM { get; } = new ChannelPermissions(Convert.ToUInt64("00000000000001011100110000000000", 2));
- private static ChannelPermissions _allVoice { get; } = new ChannelPermissions(Convert.ToUInt64("00010011111100000000000000010001", 2));
- private static ChannelPermissions _allText { get; } = new ChannelPermissions(Convert.ToUInt64("00010000000001111111110001010001", 2));
- private static ChannelPermissions _allGroup { get; } = new ChannelPermissions(Convert.ToUInt64("00000000000001111110110000000000", 2));
-
/// Gets a blank ChannelPermissions that grants no permissions.
- public static ChannelPermissions None { get; } = new ChannelPermissions();
+ public static readonly ChannelPermissions None = new ChannelPermissions();
+ /// Gets a ChannelPermissions that grants all permissions for text channels.
+ public static readonly ChannelPermissions Text = new ChannelPermissions(0b00100_0000000_1111111110001_010001);
+ /// Gets a ChannelPermissions that grants all permissions for voice channels.
+ public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000000000_010001);
+ /// Gets a ChannelPermissions that grants all permissions for direct message channels.
+ public static readonly ChannelPermissions DM = new ChannelPermissions(0b00000_1000110_1011100110000_000000);
+ /// Gets a ChannelPermissions that grants all permissions for group channels.
+ public static readonly ChannelPermissions Group = new ChannelPermissions(0b00000_1000110_0001101100000_000000);
/// Gets a ChannelPermissions that grants all permissions for a given channelType.
public static ChannelPermissions All(IChannel channel)
{
//TODO: C#7 Candidate for typeswitch
- if (channel is ITextChannel) return _allText;
- if (channel is IVoiceChannel) return _allVoice;
- if (channel is IDMChannel) return _allDM;
- if (channel is IGroupChannel) return _allGroup;
+ if (channel is ITextChannel) return Text;
+ if (channel is IVoiceChannel) return Voice;
+ if (channel is IDMChannel) return DM;
+ if (channel is IGroupChannel) return Group;
throw new ArgumentException("Unknown channel type", nameof(channel));
}
@@ -77,7 +79,7 @@ namespace Discord
/// Creates a new ChannelPermissions with the provided packed value.
public ChannelPermissions(ulong rawValue) { RawValue = rawValue; }
- private ChannelPermissions(ulong initialValue, bool? createInstantInvite = null, bool? manageChannel = null,
+ private ChannelPermissions(ulong initialValue, bool? createInstantInvite = null, bool? manageChannel = null,
bool? addReactions = null,
bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
@@ -111,25 +113,26 @@ namespace Discord
}
/// Creates a new ChannelPermissions with the provided permissions.
- public ChannelPermissions(bool createInstantInvite = false, bool manageChannel = false,
+ public ChannelPermissions(bool createInstantInvite = false, bool manageChannel = false,
bool addReactions = false,
bool readMessages = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false,
bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false,
bool useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false,
bool moveMembers = false, bool useVoiceActivation = false, bool managePermissions = false, bool manageWebhooks = false)
- : this(0, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages,
- embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect,
- speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions, manageWebhooks) { }
+ : this(0, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages,
+ embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect,
+ speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions, manageWebhooks)
+ { }
/// Creates a new ChannelPermissions from this one, changing the provided non-null permissions.
- public ChannelPermissions Modify(bool? createInstantInvite = null, bool? manageChannel = null,
+ public ChannelPermissions Modify(bool? createInstantInvite = null, bool? manageChannel = null,
bool? addReactions = null,
bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
bool useExternalEmojis = false, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null,
bool? moveMembers = null, bool? useVoiceActivation = null, bool? managePermissions = null, bool? manageWebhooks = null)
=> new ChannelPermissions(RawValue, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages,
- embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect,
+ embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect,
speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions, manageWebhooks);
public bool Has(ChannelPermission permission) => Permissions.GetValue(RawValue, permission);
diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
index e7461915c..c5f1efab0 100644
--- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
+++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Diagnostics;
namespace Discord
@@ -9,9 +8,10 @@ namespace Discord
{
/// Gets a blank GuildPermissions that grants no permissions.
public static readonly GuildPermissions None = new GuildPermissions();
- /// Gets a GuildPermissions that grants all permissions.
- //TODO: C#7 Candidate for binary literals
- public static readonly GuildPermissions All = new GuildPermissions(Convert.ToUInt64("01111111111100111111110001111111", 2));
+ /// Gets a GuildPermissions that grants all guild permissions for webhook users.
+ public static readonly GuildPermissions Webhook = new GuildPermissions(0b00000_0000000_0001101100000_000000);
+ /// Gets a GuildPermissions that grants all guild permissions.
+ public static readonly GuildPermissions All = new GuildPermissions(0b11111_1111110_0111111110001_111111);
/// Gets a packed value representing all the permissions in this GuildPermissions.
public ulong RawValue { get; }
diff --git a/src/Discord.Net.Core/Entities/Users/IUser.cs b/src/Discord.Net.Core/Entities/Users/IUser.cs
index 62060da22..45d8862f1 100644
--- a/src/Discord.Net.Core/Entities/Users/IUser.cs
+++ b/src/Discord.Net.Core/Entities/Users/IUser.cs
@@ -12,8 +12,10 @@ namespace Discord
string Discriminator { get; }
/// Gets the per-username unique id for this user.
ushort DiscriminatorValue { get; }
- /// Returns true if this user is a bot account.
+ /// Returns true if this user is a bot user.
bool IsBot { get; }
+ /// Returns true if this user is a webhook user.
+ bool IsWebhook { get; }
/// Gets the username for this user.
string Username { get; }
diff --git a/src/Discord.Net.Core/Entities/Users/IWebhookUser.cs b/src/Discord.Net.Core/Entities/Users/IWebhookUser.cs
new file mode 100644
index 000000000..8f4d42187
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Users/IWebhookUser.cs
@@ -0,0 +1,8 @@
+namespace Discord
+{
+ //TODO: Add webhook endpoints
+ public interface IWebhookUser : IGuildUser
+ {
+ ulong WebhookId { get; }
+ }
+}
diff --git a/src/Discord.Net.Core/Utils/AsyncEvent.cs b/src/Discord.Net.Core/Utils/AsyncEvent.cs
index a7fdeddf2..12a1fba9c 100644
--- a/src/Discord.Net.Core/Utils/AsyncEvent.cs
+++ b/src/Discord.Net.Core/Utils/AsyncEvent.cs
@@ -37,11 +37,8 @@ namespace Discord
public static async Task InvokeAsync(this AsyncEvent> eventHandler)
{
var subscribers = eventHandler.Subscriptions;
- if (subscribers.Count > 0)
- {
- for (int i = 0; i < subscribers.Count; i++)
- await subscribers[i].Invoke().ConfigureAwait(false);
- }
+ for (int i = 0; i < subscribers.Count; i++)
+ await subscribers[i].Invoke().ConfigureAwait(false);
}
public static async Task InvokeAsync(this AsyncEvent> eventHandler, T arg)
{
diff --git a/src/Discord.Net.Core/Utils/Optional.cs b/src/Discord.Net.Core/Utils/Optional.cs
index e2d55cf7f..df927b7ea 100644
--- a/src/Discord.Net.Core/Utils/Optional.cs
+++ b/src/Discord.Net.Core/Utils/Optional.cs
@@ -51,5 +51,9 @@ namespace Discord
{
public static Optional Create() => Optional.Unspecified;
public static Optional Create(T value) => new Optional(value);
+
+ public static T? ToNullable(this Optional val)
+ where T : struct
+ => val.IsSpecified ? val.Value : (T?)null;
}
}
diff --git a/src/Discord.Net.Core/Utils/Permissions.cs b/src/Discord.Net.Core/Utils/Permissions.cs
index a99c64094..b69b103e1 100644
--- a/src/Discord.Net.Core/Utils/Permissions.cs
+++ b/src/Discord.Net.Core/Utils/Permissions.cs
@@ -86,12 +86,16 @@ namespace Discord
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnsetBit(ref ulong value, byte bit) => value &= ~(1U << bit);
+ public static ChannelPermissions ToChannelPerms(IGuildChannel channel, ulong guildPermissions)
+ => new ChannelPermissions(guildPermissions & ChannelPermissions.All(channel).RawValue);
public static ulong ResolveGuild(IGuild guild, IGuildUser user)
{
ulong resolvedPermissions = 0;
if (user.Id == guild.OwnerId)
resolvedPermissions = GuildPermissions.All.RawValue; //Owners always have all permissions
+ else if (user.IsWebhook)
+ resolvedPermissions = GuildPermissions.Webhook.RawValue;
else
{
foreach (var roleId in user.RoleIds)
diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
index 07bdfe0eb..efcadac0d 100644
--- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
@@ -91,7 +91,7 @@ namespace Discord.Rest
var guildId = (channel as IGuildChannel)?.GuildId;
var guild = guildId != null ? await (client as IDiscordClient).GetGuildAsync(guildId.Value, CacheMode.CacheOnly).ConfigureAwait(false) : null;
var model = await client.ApiClient.GetChannelMessageAsync(channel.Id, id, options).ConfigureAwait(false);
- var author = GetAuthor(client, guild, model.Author.Value);
+ var author = GetAuthor(client, guild, model.Author.Value, model.WebhookId.ToNullable());
return RestMessage.Create(client, channel, author, model);
}
public static IAsyncEnumerable> GetMessagesAsync(IMessageChannel channel, BaseDiscordClient client,
@@ -119,7 +119,7 @@ namespace Discord.Rest
var builder = ImmutableArray.CreateBuilder();
foreach (var model in models)
{
- var author = GetAuthor(client, guild, model.Author.Value);
+ var author = GetAuthor(client, guild, model.Author.Value, model.WebhookId.ToNullable());
builder.Add(RestMessage.Create(client, channel, author, model));
}
return builder.ToImmutable();
@@ -147,7 +147,7 @@ namespace Discord.Rest
var builder = ImmutableArray.CreateBuilder();
foreach (var model in models)
{
- var author = GetAuthor(client, guild, model.Author.Value);
+ var author = GetAuthor(client, guild, model.Author.Value, model.WebhookId.ToNullable());
builder.Add(RestMessage.Create(client, channel, author, model));
}
return builder.ToImmutable();
@@ -264,13 +264,13 @@ namespace Discord.Rest
=> new TypingNotifier(client, channel, options);
//Helpers
- private static IUser GetAuthor(BaseDiscordClient client, IGuild guild, UserModel model)
+ private static IUser GetAuthor(BaseDiscordClient client, IGuild guild, UserModel model, ulong? webhookId)
{
IUser author = null;
if (guild != null)
author = guild.GetUserAsync(model.Id, CacheMode.CacheOnly).Result;
if (author == null)
- author = RestUser.Create(client, model);
+ author = RestUser.Create(client, guild, model, webhookId);
return author;
}
}
diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
index 2c6f67477..f347563ad 100644
--- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
@@ -67,7 +67,7 @@ namespace Discord.Rest
await client.ApiClient.RemovePinAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false);
}
- public static ImmutableArray ParseTags(string text, IMessageChannel channel, IGuild guild, ImmutableArray userMentions)
+ public static ImmutableArray ParseTags(string text, IMessageChannel channel, IGuild guild, IReadOnlyCollection userMentions)
{
var tags = ImmutableArray.CreateBuilder();
@@ -156,5 +156,16 @@ namespace Discord.Rest
.Where(x => x != null)
.ToImmutableArray();
}
+
+ public static MessageSource GetSource(Model msg)
+ {
+ if (msg.Type != MessageType.Default)
+ return MessageSource.System;
+ else if (msg.WebhookId.IsSpecified)
+ return MessageSource.Webhook;
+ else if (msg.Author.GetValueOrDefault()?.Bot.GetValueOrDefault(false) == true)
+ return MessageSource.Bot;
+ return MessageSource.User;
+ }
}
}
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
index b50edf03b..bdd4800c1 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
@@ -13,6 +13,7 @@ namespace Discord.Rest
public IMessageChannel Channel { get; }
public IUser Author { get; }
+ public MessageSource Source { get; }
public string Content { get; private set; }
@@ -26,16 +27,15 @@ namespace Discord.Rest
public virtual IReadOnlyCollection MentionedRoleIds => ImmutableArray.Create();
public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create();
public virtual IReadOnlyCollection Tags => ImmutableArray.Create();
- public virtual ulong? WebhookId => null;
- public bool IsWebhook => WebhookId != null;
public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks);
- internal RestMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author)
+ internal RestMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source)
: base(discord, id)
{
Channel = channel;
Author = author;
+ Source = source;
}
internal static RestMessage Create(BaseDiscordClient discord, IMessageChannel channel, IUser author, Model model)
{
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs
index a5ced8c8f..b9dda08ae 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs
@@ -9,7 +9,7 @@ namespace Discord.Rest
public MessageType Type { get; private set; }
internal RestSystemMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author)
- : base(discord, id, channel, author)
+ : base(discord, id, channel, author, MessageSource.System)
{
}
internal new static RestSystemMessage Create(BaseDiscordClient discord, IMessageChannel channel, IUser author, Model model)
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
index a9197188e..00ab0c299 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
@@ -13,7 +13,6 @@ namespace Discord.Rest
{
private bool _isMentioningEveryone, _isTTS, _isPinned;
private long? _editedTimestampTicks;
- private ulong? _webhookId;
private ImmutableArray _attachments;
private ImmutableArray