@@ -4,4 +4,13 @@ | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Wumpus.Net.Gateway" Version="0.2.2-build-00031" /> | |||
<PackageReference Include="Wumpus.Net.Rest" Version="0.2.2-build-00031" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Folder Include="Models\Guilds\" /> | |||
</ItemGroup> | |||
</Project> |
@@ -0,0 +1,19 @@ | |||
using Model = Wumpus.Entities.DefaultMessageNotifications; | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Specifies the default message notification behavior the guild uses. | |||
/// </summary> | |||
public enum DefaultMessageNotifications | |||
{ | |||
/// <summary> | |||
/// By default, all messages will trigger notifications. | |||
/// </summary> | |||
AllMessages = 0, | |||
/// <summary> | |||
/// By default, only mentions will trigger notifications. | |||
/// </summary> | |||
MentionsOnly = 1 | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
namespace Discord | |||
{ | |||
public enum ExplicitContentFilterLevel | |||
{ | |||
/// <summary> No messages will be scanned. </summary> | |||
Disabled = 0, | |||
/// <summary> Scans messages from all guild members that do not have a role. </summary> | |||
/// <remarks> Recommented option for servers that use roles for trusted membership. </remarks> | |||
MembersWithoutRoles = 1, | |||
/// <summary> Scan messages sent by all guild members. </summary> | |||
AllMembers = 2 | |||
} | |||
} |
@@ -0,0 +1,146 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace Discord | |||
{ | |||
public interface IGuild : IDeletable, ISnowflakeEntity | |||
{ | |||
/// <summary> | |||
/// Gets the name of this guild. | |||
/// </summary> | |||
/// <returns> | |||
/// A string containing the name of this guild. | |||
/// </returns> | |||
string Name { get; } | |||
/// <summary> | |||
/// Gets the amount of time (in seconds) a user must be inactive in a voice channel for until they are | |||
/// automatically moved to the AFK voice channel. | |||
/// </summary> | |||
/// <returns> | |||
/// An <see cref="int"/> representing the amount of time in seconds for a user to be marked as inactive | |||
/// and moved into the AFK voice channel. | |||
/// </returns> | |||
int AFKTimeout { get; } | |||
/// <summary> | |||
/// Gets a value that indicates whether this guild is embeddable (i.e. can use widget). | |||
/// </summary> | |||
/// <returns> | |||
/// <c>true</c> if this guild can be embedded via widgets; otherwise <c>false</c>. | |||
/// </returns> | |||
bool IsEmbeddable { get; } | |||
/// <summary> | |||
/// Gets the default message notifications for users who haven't explicitly set their notification settings. | |||
/// </summary> | |||
DefaultMessageNotifications DefaultMessageNotifications { get; } | |||
/// <summary> | |||
/// Gets the level of Multi-Factor Authentication requirements a user must fulfill before being allowed to | |||
/// perform administrative actions in this guild. | |||
/// </summary> | |||
/// <returns> | |||
/// The level of MFA requirement. | |||
/// </returns> | |||
MfaLevel MfaLevel { get; } | |||
/// <summary> | |||
/// Gets the level of requirements a user must fulfill before being allowed to post messages in this guild. | |||
/// </summary> | |||
/// <returns> | |||
/// The level of requirements. | |||
/// </returns> | |||
VerificationLevel VerificationLevel { get; } | |||
/// <summary> | |||
/// Gets the level of content filtering applied to user's content in a Guild. | |||
/// </summary> | |||
/// <returns> | |||
/// The level of explicit content filtering. | |||
/// </returns> | |||
ExplicitContentFilterLevel ExplicitContentFilter { get; } | |||
/// <summary> | |||
/// Gets the ID of this guild's icon. | |||
/// </summary> | |||
/// <returns> | |||
/// An identifier for the splash image; <c>null</c> if none is set. | |||
/// </returns> | |||
string IconId { get; } | |||
/// <summary> | |||
/// Gets the URL of this guild's icon. | |||
/// </summary> | |||
/// <returns> | |||
/// A URL pointing to the guild's icon; <c>null</c> if none is set. | |||
/// </returns> | |||
string IconUrl { get; } | |||
/// <summary> | |||
/// Gets the ID of this guild's splash image. | |||
/// </summary> | |||
/// <returns> | |||
/// An identifier for the splash image; <c>null</c> if none is set. | |||
/// </returns> | |||
string SplashId { get; } | |||
/// <summary> | |||
/// Gets the URL of this guild's splash image. | |||
/// </summary> | |||
/// <returns> | |||
/// A URL pointing to the guild's splash image; <c>null</c> if none is set. | |||
/// </returns> | |||
string SplashUrl { get; } | |||
/// <summary> | |||
/// Determines if this guild is currently connected and ready to be used. | |||
/// </summary> | |||
/// <remarks> | |||
/// <note> | |||
/// This property only applies to a guild fetched via the gateway; it will always return false on guilds fetched via REST. | |||
/// </note> | |||
/// This boolean is used to determine if the guild is currently connected to the WebSocket and is ready to be used/accessed. | |||
/// </remarks> | |||
/// <returns> | |||
/// <c>true</c> if this guild is currently connected and ready to be used; otherwise <c>false</c>. | |||
/// </returns> | |||
bool Available { get; } | |||
/// <summary> | |||
/// Gets the ID of the AFK voice channel for this guild. | |||
/// </summary> | |||
/// <returns> | |||
/// A <see cref="ulong"/> representing the snowflake identifier of the AFK voice channel; <c>null</c> if | |||
/// none is set. | |||
/// </returns> | |||
ulong? AFKChannelId { get; } | |||
/// <summary> | |||
/// Gets the ID of the widget embed channel of this guild. | |||
/// </summary> | |||
/// <returns> | |||
/// A <see cref="ulong"/> representing the snowflake identifier of the embedded channel found within the | |||
/// widget settings of this guild; <c>null</c> if none is set. | |||
/// </returns> | |||
ulong? EmbedChannelId { get; } | |||
/// <summary> | |||
/// Gets the ID of the channel where randomized welcome messages are sent. | |||
/// </summary> | |||
/// <returns> | |||
/// A <see cref="ulong"/> representing the snowflake identifier of the system channel where randomized | |||
/// welcome messages are sent; <c>null</c> if none is set. | |||
/// </returns> | |||
ulong? SystemChannelId { get; } | |||
/// <summary> | |||
/// Gets the ID of the user that owns this guild. | |||
/// </summary> | |||
/// <returns> | |||
/// A <see cref="ulong"/> representing the snowflake identifier of the user that owns this guild. | |||
/// </returns> | |||
ulong OwnerId { get; } | |||
/// <summary> | |||
/// Gets the application ID of the guild creator if it is bot-created. | |||
/// </summary> | |||
/// <returns> | |||
/// A <see cref="ulong"/> representing the snowflake identifier of the application ID that created this guild, or <c>null</c> if it was not bot-created. | |||
/// </returns> | |||
ulong? ApplicationId { get; } | |||
/// <summary> | |||
/// Gets the ID of the region hosting this guild's voice channels. | |||
/// </summary> | |||
/// <returns> | |||
/// A string containing the identifier for the voice region that this guild uses (e.g. <c>eu-central</c>). | |||
/// </returns> | |||
string VoiceRegionId { get; } | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Specifies the guild's Multi-Factor Authentication (MFA) level requirement. | |||
/// </summary> | |||
public enum MfaLevel | |||
{ | |||
/// <summary> | |||
/// Users have no additional MFA restriction on this guild. | |||
/// </summary> | |||
Disabled = 0, | |||
/// <summary> | |||
/// Users must have MFA enabled on their account to perform administrative actions. | |||
/// </summary> | |||
Enabled = 1 | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Specifies the verification level the guild uses. | |||
/// </summary> | |||
public enum VerificationLevel | |||
{ | |||
/// <summary> | |||
/// Users have no additional restrictions on sending messages to this guild. | |||
/// </summary> | |||
None = 0, | |||
/// <summary> | |||
/// Users must have a verified email on their account. | |||
/// </summary> | |||
Low = 1, | |||
/// <summary> | |||
/// Users must fulfill the requirements of Low and be registered on Discord for at least 5 minutes. | |||
/// </summary> | |||
Medium = 2, | |||
/// <summary> | |||
/// Users must fulfill the requirements of Medium and be a member of this guild for at least 10 minutes. | |||
/// </summary> | |||
High = 3, | |||
/// <summary> | |||
/// Users must fulfill the requirements of High and must have a verified phone on their Discord account. | |||
/// </summary> | |||
Extreme = 4 | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
using System.Threading.Tasks; | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Determines whether the object is deletable or not. | |||
/// </summary> | |||
public interface IDeletable | |||
{ | |||
/// <summary> | |||
/// Deletes this object and all its children. | |||
/// </summary> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
Task DeleteAsync(/*RequestOptions options = null*/); | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
using System; | |||
namespace Discord | |||
{ | |||
public interface IEntity<TId> | |||
where TId : IEquatable<TId> | |||
{ | |||
/// <summary> | |||
/// Gets the <see cref="IDiscordClient"/> that created this object. | |||
/// </summary> | |||
IDiscordClient Discord { get; } | |||
/// <summary> | |||
/// Gets the unique identifier for this object. | |||
/// </summary> | |||
TId Id { get; } | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
using System; | |||
namespace Discord | |||
{ | |||
/// <summary> Represents a Discord snowflake entity. </summary> | |||
public interface ISnowflakeEntity : IEntity<ulong> | |||
{ | |||
/// <summary> | |||
/// Gets when the snowflake was created. | |||
/// </summary> | |||
/// <returns> | |||
/// A <see cref="DateTimeOffset"/> representing when the entity was first created. | |||
/// </returns> | |||
DateTimeOffset CreatedAt { get; } | |||
} | |||
} |
@@ -0,0 +1,66 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Wumpus.Entities; | |||
using Model = Wumpus.Entities.Guild; | |||
namespace Discord | |||
{ | |||
internal class Guild : SnowflakeEntity, IGuild | |||
{ | |||
public Guild(Model model, IDiscordClient client) : base(client) | |||
{ | |||
Name = model.Name.ToString(); | |||
AFKTimeout = model.AfkTimeout; | |||
IsEmbeddable = model.EmbedEnabled.GetValueOrDefault(false); | |||
// FYI: when casting these, make sure Wumpus is using the same schema as us, otherwise these values will not match up. | |||
DefaultMessageNotifications = (DefaultMessageNotifications)model.DefaultMessageNotifications; | |||
MfaLevel = (MfaLevel)model.MfaLevel; | |||
VerificationLevel = (VerificationLevel)model.VerificationLevel; | |||
ExplicitContentFilter = (ExplicitContentFilterLevel)model.ExplicitContentFilter; | |||
IconId = model.Icon?.Hash.ToString(); | |||
IconUrl = null; // TODO: port CDN | |||
SplashId = model.Splash?.Hash.ToString(); | |||
SplashUrl = null; // TODO: port CDN | |||
Available = model is GatewayGuild; | |||
AFKChannelId = model.AfkChannelId; | |||
EmbedChannelId = model.EmbedChannelId.IsSpecified ? model.EmbedChannelId.Value : null; | |||
SystemChannelId = model.SystemChannelId; | |||
OwnerId = model.OwnerId; | |||
ApplicationId = model.ApplicationId; | |||
VoiceRegionId = null; // TODO? | |||
} | |||
public string Name { get; set; } | |||
public int AFKTimeout { get; set; } | |||
public bool IsEmbeddable { get; set; } | |||
public DefaultMessageNotifications DefaultMessageNotifications { get; set; } | |||
public MfaLevel MfaLevel { get; set; } | |||
public VerificationLevel VerificationLevel { get; set; } | |||
public ExplicitContentFilterLevel ExplicitContentFilter { get; set; } | |||
public string IconId { get; set; } | |||
public string IconUrl { get; set; } | |||
public string SplashId { get; set; } | |||
public string SplashUrl { get; set; } | |||
public bool Available { get; set; } | |||
public ulong? AFKChannelId { get; set; } | |||
public ulong? EmbedChannelId { get; set; } | |||
public ulong? SystemChannelId { get; set; } | |||
public ulong OwnerId { get; set; } | |||
public ulong? ApplicationId { get; set; } | |||
public string VoiceRegionId { get; set; } | |||
public Task DeleteAsync() => throw new NotImplementedException(); | |||
} | |||
} |
@@ -0,0 +1,28 @@ | |||
using System; | |||
namespace Discord | |||
{ | |||
internal abstract class SnowflakeEntity : ISnowflakeEntity | |||
{ | |||
private DateTimeOffset? _createdAt; | |||
public SnowflakeEntity(IDiscordClient discord) | |||
{ | |||
Discord = discord; | |||
} | |||
public IDiscordClient Discord { get; set; } | |||
public ulong Id { get; set; } | |||
public DateTimeOffset CreatedAt | |||
{ | |||
get | |||
{ | |||
if (_createdAt.HasValue) | |||
return _createdAt.Value; | |||
_createdAt = SnowflakeUtilities.FromSnowflake(Id); | |||
return _createdAt.Value; | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Provides a series of helper methods for handling snowflake identifiers. | |||
/// </summary> | |||
public static class SnowflakeUtilities | |||
{ | |||
/// <summary> | |||
/// Resolves the time of which the snowflake is generated. | |||
/// </summary> | |||
/// <param name="value">The snowflake identifier to resolve.</param> | |||
/// <returns> | |||
/// A <see cref="DateTimeOffset" /> representing the time for when the object is geenrated. | |||
/// </returns> | |||
public static DateTimeOffset FromSnowflake(ulong value) | |||
=> DateTimeOffset.FromUnixTimeMilliseconds((long)((value >> 22) + 1420070400000UL)); | |||
/// <summary> | |||
/// Generates a pseudo-snowflake identifier with a <see cref="DateTimeOffset"/>. | |||
/// </summary> | |||
/// <param name="value">The time to be used in the new snowflake.</param> | |||
/// <returns> | |||
/// A <see cref="UInt64" /> representing the newly generated snowflake identifier. | |||
/// </returns> | |||
public static ulong ToSnowflake(DateTimeOffset value) | |||
=> ((ulong)value.ToUnixTimeMilliseconds() - 1420070400000UL) << 22; | |||
} | |||
} |
@@ -12,4 +12,8 @@ | |||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\..\src\Discord.Net\Discord.Net.csproj" /> | |||
</ItemGroup> | |||
</Project> |
@@ -1,14 +0,0 @@ | |||
using System; | |||
using Xunit; | |||
namespace Discord.Tests.Unit | |||
{ | |||
public class UnitTest1 | |||
{ | |||
[Fact] | |||
public void Test1() | |||
{ | |||
} | |||
} | |||
} |
@@ -0,0 +1,22 @@ | |||
using System; | |||
using Xunit; | |||
namespace Discord.Tests.Unit | |||
{ | |||
public class SnowflakeTests | |||
{ | |||
[Fact] | |||
public void FromSnowflake() | |||
{ | |||
Assert.Equal(DateTimeOffset.FromUnixTimeMilliseconds(1420070400000), SnowflakeUtilities.FromSnowflake(0)); | |||
Assert.Equal(DateTimeOffset.FromUnixTimeMilliseconds(1439474045698), SnowflakeUtilities.FromSnowflake(81384788765712384)); | |||
} | |||
[Fact] | |||
public void ToSnowflake() | |||
{ | |||
Assert.Equal(0UL, SnowflakeUtilities.ToSnowflake(DateTimeOffset.FromUnixTimeMilliseconds(1420070400000))); | |||
Assert.Equal(81384788765704192UL, SnowflakeUtilities.ToSnowflake(DateTimeOffset.FromUnixTimeMilliseconds(1439474045698))); | |||
} | |||
} | |||
} |