@@ -4,4 +4,13 @@ | |||||
<TargetFramework>netstandard2.0</TargetFramework> | <TargetFramework>netstandard2.0</TargetFramework> | ||||
</PropertyGroup> | </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> | </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" /> | <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | |||||
<ProjectReference Include="..\..\src\Discord.Net\Discord.Net.csproj" /> | |||||
</ItemGroup> | |||||
</Project> | </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))); | |||||
} | |||||
} | |||||
} |