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