| @@ -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))); | |||
| } | |||
| } | |||
| } | |||