commitpull/1923/head73740e4169
Author: quin lynch <lynchquin@gmail.com> Date: Wed Aug 4 23:17:04 2021 -0300 add sharded events commitdac6ba3603
Merge: 22e3a529 39b7e715 Author: quin lynch <lynchquin@gmail.com> Date: Wed Aug 4 22:16:12 2021 -0300 Merge branch 'feature/stage-channels' of https://github.com/Discord-Net-Labs/Discord.Net-Labs into feature/stage-channels commit22e3a529e0
Author: quin lynch <lynchquin@gmail.com> Date: Wed Aug 4 22:15:03 2021 -0300 Threads pre 2 commit39b7e715f3
Author: Quin Lynch <49576606+quinchs@users.noreply.github.com> Date: Tue Aug 3 20:43:21 2021 -0300 Update README.md commit56536f6448
Author: Quin Lynch <49576606+quinchs@users.noreply.github.com> Date: Tue Aug 3 20:28:27 2021 -0300 Update README.md commitd98e79452c
Author: quin lynch <lynchquin@gmail.com> Date: Sat Jul 31 21:22:56 2021 -0300 Request to speak stuff commit950fe80cec
Author: quin lynch <lynchquin@gmail.com> Date: Sat Jul 31 20:59:11 2021 -0300 Rest stage channels commit360b9436bb
Author: quin lynch <lynchquin@gmail.com> Date: Sat Jul 31 20:29:38 2021 -0300 Stage channel socket side commit928edaa03b
Author: quin lynch <lynchquin@gmail.com> Date: Sat Jul 31 19:19:40 2021 -0300 Add models commitec58a9b7e8
Author: quin lynch <lynchquin@gmail.com> Date: Sat Jul 31 18:59:09 2021 -0300 initial stage channel events
@@ -1914,6 +1914,70 @@ | |||||
A read-only collection of users that can access this channel. | A read-only collection of users that can access this channel. | ||||
</returns> | </returns> | ||||
</member> | </member> | ||||
<member name="T:Discord.IStageChannel"> | |||||
<summary> | |||||
Represents a generic Stage Channel. | |||||
</summary> | |||||
</member> | |||||
<member name="P:Discord.IStageChannel.Topic"> | |||||
<summary> | |||||
Gets the topic of the Stage instance. | |||||
</summary> | |||||
<remarks> | |||||
If the stage isn't live then this property will be set to <see langword="null"/>. | |||||
</remarks> | |||||
</member> | |||||
<member name="P:Discord.IStageChannel.PrivacyLevel"> | |||||
<summary> | |||||
The <see cref="T:Discord.StagePrivacyLevel"/> of the current stage. | |||||
</summary> | |||||
<remarks> | |||||
If the stage isn't live then this property will be set to <see langword="null"/>. | |||||
</remarks> | |||||
</member> | |||||
<member name="P:Discord.IStageChannel.DiscoverableDisabled"> | |||||
<summary> | |||||
<see langword="true"/> if stage discovery is disabled, otherwise <see langword="false"/>. | |||||
</summary> | |||||
</member> | |||||
<member name="P:Discord.IStageChannel.Live"> | |||||
<summary> | |||||
<see langword="true"/> when the stage is live, otherwise <see langword="false"/>. | |||||
</summary> | |||||
<remarks> | |||||
If the stage isn't live then this property will be set to <see langword="null"/>. | |||||
</remarks> | |||||
</member> | |||||
<member name="M:Discord.IStageChannel.StartStageAsync(System.String,Discord.StagePrivacyLevel,Discord.RequestOptions)"> | |||||
<summary> | |||||
Starts the stage, creating a stage instance. | |||||
</summary> | |||||
<param name="topic">The topic for the stage/</param> | |||||
<param name="privacyLevel">The privacy level of the stage</param> | |||||
<param name="options">The options to be used when sending the request.</param> | |||||
<returns> | |||||
A task that represents the asynchronous start operation. | |||||
</returns> | |||||
</member> | |||||
<member name="M:Discord.IStageChannel.ModifyInstanceAsync(System.Action{Discord.StageInstanceProperties},Discord.RequestOptions)"> | |||||
<summary> | |||||
Modifies the current stage instance. | |||||
</summary> | |||||
<param name="func">The properties to modify the stage instance with.</param> | |||||
<param name="options">The options to be used when sending the request.</param> | |||||
<returns> | |||||
A task that represents the asynchronous modify operation. | |||||
</returns> | |||||
</member> | |||||
<member name="M:Discord.IStageChannel.StopStageAsync(Discord.RequestOptions)"> | |||||
<summary> | |||||
Stops the stage, deleting the stage instance. | |||||
</summary> | |||||
<param name="options">The options to be used when sending the request.</param> | |||||
<returns> | |||||
A task that represents the asynchronous stop operation. | |||||
</returns> | |||||
</member> | |||||
<member name="T:Discord.ITextChannel"> | <member name="T:Discord.ITextChannel"> | ||||
<summary> | <summary> | ||||
Represents a generic channel in a guild that can send and receive messages. | Represents a generic channel in a guild that can send and receive messages. | ||||
@@ -2202,6 +2266,21 @@ | |||||
<param name="id"> Sets the ID of the channel to apply this position to. </param> | <param name="id"> Sets the ID of the channel to apply this position to. </param> | ||||
<param name="position"> Sets the new zero-based position of this channel. </param> | <param name="position"> Sets the new zero-based position of this channel. </param> | ||||
</member> | </member> | ||||
<member name="T:Discord.StageInstanceProperties"> | |||||
<summary> | |||||
Represents properties to use when modifying a stage instance. | |||||
</summary> | |||||
</member> | |||||
<member name="P:Discord.StageInstanceProperties.Topic"> | |||||
<summary> | |||||
Gets or sets the topic of the stage. | |||||
</summary> | |||||
</member> | |||||
<member name="P:Discord.StageInstanceProperties.PrivacyLevel"> | |||||
<summary> | |||||
Gets or sets the privacy level of the stage. | |||||
</summary> | |||||
</member> | |||||
<member name="T:Discord.TextChannelProperties"> | <member name="T:Discord.TextChannelProperties"> | ||||
<summary> | <summary> | ||||
Provides properties that are used to modify an <see cref="T:Discord.ITextChannel"/> with the specified changes. | Provides properties that are used to modify an <see cref="T:Discord.ITextChannel"/> with the specified changes. | ||||
@@ -3344,6 +3423,29 @@ | |||||
with the specified <paramref name="id"/>; <see langword="null" /> if none is found. | with the specified <paramref name="id"/>; <see langword="null" /> if none is found. | ||||
</returns> | </returns> | ||||
</member> | </member> | ||||
<member name="M:Discord.IGuild.GetStageChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)"> | |||||
<summary> | |||||
Gets a stage channel in this guild | |||||
</summary> | |||||
<param name="id">The snowflake identifier for the stage channel.</param> | |||||
<param name="mode">The <see cref="T:Discord.CacheMode"/> that determines whether the object should be fetched from cache.</param> | |||||
<param name="options">The options to be used when sending the request.</param> | |||||
<returns> | |||||
A task that represents the asynchronous get operation. The task result contains the stage channel associated | |||||
with the specified <paramref name="id"/>; <see langword="null" /> if none is found. | |||||
</returns> | |||||
</member> | |||||
<member name="M:Discord.IGuild.GetStageChannelsAsync(Discord.CacheMode,Discord.RequestOptions)"> | |||||
<summary> | |||||
Gets a collection of all stage channels in this guild. | |||||
</summary> | |||||
<param name="mode">The <see cref="T:Discord.CacheMode"/> that determines whether the object should be fetched from cache.</param> | |||||
<param name="options">The options to be used when sending the request.</param> | |||||
<returns> | |||||
A task that represents the asynchronous get operation. The task result contains a read-only collection of | |||||
stage channels found within this guild. | |||||
</returns> | |||||
</member> | |||||
<member name="M:Discord.IGuild.GetAFKChannelAsync(Discord.CacheMode,Discord.RequestOptions)"> | <member name="M:Discord.IGuild.GetAFKChannelAsync(Discord.CacheMode,Discord.RequestOptions)"> | ||||
<summary> | <summary> | ||||
Gets the AFK voice channel in this guild. | Gets the AFK voice channel in this guild. | ||||
@@ -9780,6 +9882,11 @@ | |||||
<c>true</c> if the user is streaming; otherwise <c>false</c>. | <c>true</c> if the user is streaming; otherwise <c>false</c>. | ||||
</returns> | </returns> | ||||
</member> | </member> | ||||
<member name="P:Discord.IVoiceState.RequestToSpeakTimestamp"> | |||||
<summary> | |||||
Gets the time on which the user requested to speak. | |||||
</summary> | |||||
</member> | |||||
<member name="T:Discord.IWebhookUser"> | <member name="T:Discord.IWebhookUser"> | ||||
<summary> Represents a Webhook Discord user. </summary> | <summary> Represents a Webhook Discord user. </summary> | ||||
</member> | </member> | ||||
@@ -0,0 +1,75 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Discord | |||||
{ | |||||
/// <summary> | |||||
/// Represents a generic Stage Channel. | |||||
/// </summary> | |||||
public interface IStageChannel : IVoiceChannel | |||||
{ | |||||
/// <summary> | |||||
/// Gets the topic of the Stage instance. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// If the stage isn't live then this property will be set to <see langword="null"/>. | |||||
/// </remarks> | |||||
string Topic { get; } | |||||
/// <summary> | |||||
/// The <see cref="StagePrivacyLevel"/> of the current stage. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// If the stage isn't live then this property will be set to <see langword="null"/>. | |||||
/// </remarks> | |||||
StagePrivacyLevel? PrivacyLevel { get; } | |||||
/// <summary> | |||||
/// <see langword="true"/> if stage discovery is disabled, otherwise <see langword="false"/>. | |||||
/// </summary> | |||||
bool? DiscoverableDisabled { get; } | |||||
/// <summary> | |||||
/// <see langword="true"/> when the stage is live, otherwise <see langword="false"/>. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// If the stage isn't live then this property will be set to <see langword="null"/>. | |||||
/// </remarks> | |||||
bool Live { get; } | |||||
/// <summary> | |||||
/// Starts the stage, creating a stage instance. | |||||
/// </summary> | |||||
/// <param name="topic">The topic for the stage/</param> | |||||
/// <param name="privacyLevel">The privacy level of the stage</param> | |||||
/// <param name="options">The options to be used when sending the request.</param> | |||||
/// <returns> | |||||
/// A task that represents the asynchronous start operation. | |||||
/// </returns> | |||||
Task StartStageAsync(string topic, StagePrivacyLevel privacyLevel = StagePrivacyLevel.GuildOnly, RequestOptions options = null); | |||||
/// <summary> | |||||
/// Modifies the current stage instance. | |||||
/// </summary> | |||||
/// <param name="func">The properties to modify the stage instance with.</param> | |||||
/// <param name="options">The options to be used when sending the request.</param> | |||||
/// <returns> | |||||
/// A task that represents the asynchronous modify operation. | |||||
/// </returns> | |||||
Task ModifyInstanceAsync(Action<StageInstanceProperties> func, RequestOptions options = null); | |||||
/// <summary> | |||||
/// Stops the stage, deleting the stage instance. | |||||
/// </summary> | |||||
/// <param name="options">The options to be used when sending the request.</param> | |||||
/// <returns> | |||||
/// A task that represents the asynchronous stop operation. | |||||
/// </returns> | |||||
Task StopStageAsync(RequestOptions options = null); | |||||
Task RequestToSpeak(RequestOptions options = null); | |||||
} | |||||
} |
@@ -0,0 +1,24 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Discord | |||||
{ | |||||
/// <summary> | |||||
/// Represents properties to use when modifying a stage instance. | |||||
/// </summary> | |||||
public class StageInstanceProperties | |||||
{ | |||||
/// <summary> | |||||
/// Gets or sets the topic of the stage. | |||||
/// </summary> | |||||
public Optional<string> Topic { get; set; } | |||||
/// <summary> | |||||
/// Gets or sets the privacy level of the stage. | |||||
/// </summary> | |||||
public Optional<StagePrivacyLevel> PrivacyLevel { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,14 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Discord | |||||
{ | |||||
public enum StagePrivacyLevel | |||||
{ | |||||
Public = 1, | |||||
GuildOnly = 2, | |||||
} | |||||
} |
@@ -530,6 +530,27 @@ namespace Discord | |||||
/// </returns> | /// </returns> | ||||
Task<IVoiceChannel> GetVoiceChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | Task<IVoiceChannel> GetVoiceChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | ||||
/// <summary> | /// <summary> | ||||
/// Gets a stage channel in this guild | |||||
/// </summary> | |||||
/// <param name="id">The snowflake identifier for the stage channel.</param> | |||||
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param> | |||||
/// <param name="options">The options to be used when sending the request.</param> | |||||
/// <returns> | |||||
/// A task that represents the asynchronous get operation. The task result contains the stage channel associated | |||||
/// with the specified <paramref name="id"/>; <see langword="null" /> if none is found. | |||||
/// </returns> | |||||
Task<IStageChannel> GetStageChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||||
/// <summary> | |||||
/// Gets a collection of all stage channels in this guild. | |||||
/// </summary> | |||||
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param> | |||||
/// <param name="options">The options to be used when sending the request.</param> | |||||
/// <returns> | |||||
/// A task that represents the asynchronous get operation. The task result contains a read-only collection of | |||||
/// stage channels found within this guild. | |||||
/// </returns> | |||||
Task<IReadOnlyCollection<IStageChannel>> GetStageChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||||
/// <summary> | |||||
/// Gets the AFK voice channel in this guild. | /// Gets the AFK voice channel in this guild. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param> | /// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param> | ||||
@@ -1,3 +1,5 @@ | |||||
using System; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
@@ -62,5 +64,9 @@ namespace Discord | |||||
/// <c>true</c> if the user is streaming; otherwise <c>false</c>. | /// <c>true</c> if the user is streaming; otherwise <c>false</c>. | ||||
/// </returns> | /// </returns> | ||||
bool IsStreaming { get; } | bool IsStreaming { get; } | ||||
/// <summary> | |||||
/// Gets the time on which the user requested to speak. | |||||
/// </summary> | |||||
DateTimeOffset? RequestToSpeakTimestamp { get; } | |||||
} | } | ||||
} | } |
@@ -0,0 +1,30 @@ | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Discord.API | |||||
{ | |||||
internal class StageInstance | |||||
{ | |||||
[JsonProperty("id")] | |||||
public ulong Id { get; set; } | |||||
[JsonProperty("guild_id")] | |||||
public ulong GuildId { get; set; } | |||||
[JsonProperty("channel_id")] | |||||
public ulong ChannelId { get; set; } | |||||
[JsonProperty("topic")] | |||||
public string Topic { get; set; } | |||||
[JsonProperty("privacy_level")] | |||||
public StagePrivacyLevel PrivacyLevel { get; set; } | |||||
[JsonProperty("discoverable_disabled")] | |||||
public bool DiscoverableDisabled { get; set; } | |||||
} | |||||
} |
@@ -1,5 +1,6 @@ | |||||
#pragma warning disable CS1591 | #pragma warning disable CS1591 | ||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System; | |||||
namespace Discord.API | namespace Discord.API | ||||
{ | { | ||||
@@ -28,5 +29,7 @@ namespace Discord.API | |||||
public bool Suppress { get; set; } | public bool Suppress { get; set; } | ||||
[JsonProperty("self_stream")] | [JsonProperty("self_stream")] | ||||
public bool SelfStream { get; set; } | public bool SelfStream { get; set; } | ||||
[JsonProperty("request_to_speak_timestamp")] | |||||
public Optional<DateTimeOffset?> RequestToSpeakTimestamp { get; set; } | |||||
} | } | ||||
} | } |
@@ -0,0 +1,21 @@ | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
internal class CreateStageInstanceParams | |||||
{ | |||||
[JsonProperty("channel_id")] | |||||
public ulong ChannelId { get; set; } | |||||
[JsonProperty("topic")] | |||||
public string Topic { get; set; } | |||||
[JsonProperty("privacy_level")] | |||||
public Optional<StagePrivacyLevel> PrivacyLevel { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,19 @@ | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
internal class ModifyStageInstanceParams | |||||
{ | |||||
[JsonProperty("topic")] | |||||
public Optional<string> Topic { get; set; } | |||||
[JsonProperty("privacy_level")] | |||||
public Optional<StagePrivacyLevel> PrivacyLevel { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,17 @@ | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
internal class ModifyVoiceStateParams | |||||
{ | |||||
[JsonProperty("channel_id")] | |||||
public ulong ChannelId { get; set; } | |||||
[JsonProperty("suppress")] | |||||
public Optional<bool> Suppressed { get; set; } | |||||
[JsonProperty("request_to_speak_timestamp")] | |||||
public Optional<DateTimeOffset> RequestToSpeakTimestamp { get; set; } | |||||
} | |||||
} |
@@ -2281,6 +2281,38 @@ | |||||
Represents a REST-based news channel in a guild that has the same properties as a <see cref="T:Discord.Rest.RestTextChannel"/>. | Represents a REST-based news channel in a guild that has the same properties as a <see cref="T:Discord.Rest.RestTextChannel"/>. | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="T:Discord.Rest.RestStageChannel"> | |||||
<summary> | |||||
Represents a REST-based stage channel in a guild. | |||||
</summary> | |||||
</member> | |||||
<member name="P:Discord.Rest.RestStageChannel.Topic"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="P:Discord.Rest.RestStageChannel.PrivacyLevel"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="P:Discord.Rest.RestStageChannel.DiscoverableDisabled"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="P:Discord.Rest.RestStageChannel.Live"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="M:Discord.Rest.RestStageChannel.ModifyInstanceAsync(System.Action{Discord.StageInstanceProperties},Discord.RequestOptions)"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="M:Discord.Rest.RestStageChannel.StartStageAsync(System.String,Discord.StagePrivacyLevel,Discord.RequestOptions)"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="M:Discord.Rest.RestStageChannel.StopStageAsync(Discord.RequestOptions)"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="M:Discord.Rest.RestStageChannel.UpdateAsync(Discord.RequestOptions)"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="M:Discord.Rest.RestStageChannel.RequestToSpeak(Discord.RequestOptions)"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="T:Discord.Rest.RestTextChannel"> | <member name="T:Discord.Rest.RestTextChannel"> | ||||
<summary> | <summary> | ||||
Represents a REST-based channel in a guild that can send and receive messages. | Represents a REST-based channel in a guild that can send and receive messages. | ||||
@@ -3130,6 +3162,28 @@ | |||||
voice channels found within this guild. | voice channels found within this guild. | ||||
</returns> | </returns> | ||||
</member> | </member> | ||||
<member name="M:Discord.Rest.RestGuild.GetStageChannelAsync(System.UInt64,Discord.RequestOptions)"> | |||||
<summary> | |||||
Gets a stage channel in this guild | |||||
</summary> | |||||
<param name="id">The snowflake identifier for the stage channel.</param> | |||||
<param name="options">The options to be used when sending the request.</param> | |||||
<returns> | |||||
A task that represents the asynchronous get operation. The task result contains the stage channel associated | |||||
with the specified <paramref name="id"/>; <see langword="null" /> if none is found. | |||||
</returns> | |||||
</member> | |||||
<member name="M:Discord.Rest.RestGuild.GetStageChannelsAsync(Discord.RequestOptions)"> | |||||
<summary> | |||||
Gets a collection of all stage channels in this guild. | |||||
</summary> | |||||
<param name="mode">The <see cref="T:Discord.CacheMode"/> that determines whether the object should be fetched from cache.</param> | |||||
<param name="options">The options to be used when sending the request.</param> | |||||
<returns> | |||||
A task that represents the asynchronous get operation. The task result contains a read-only collection of | |||||
stage channels found within this guild. | |||||
</returns> | |||||
</member> | |||||
<member name="M:Discord.Rest.RestGuild.GetCategoryChannelsAsync(Discord.RequestOptions)"> | <member name="M:Discord.Rest.RestGuild.GetCategoryChannelsAsync(Discord.RequestOptions)"> | ||||
<summary> | <summary> | ||||
Gets a collection of all category channels in this guild. | Gets a collection of all category channels in this guild. | ||||
@@ -3496,6 +3550,12 @@ | |||||
<member name="M:Discord.Rest.RestGuild.Discord#IGuild#GetCategoriesAsync(Discord.CacheMode,Discord.RequestOptions)"> | <member name="M:Discord.Rest.RestGuild.Discord#IGuild#GetCategoriesAsync(Discord.CacheMode,Discord.RequestOptions)"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
<member name="M:Discord.Rest.RestGuild.Discord#IGuild#GetStageChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)"> | |||||
<inheritdoc /> | |||||
</member> | |||||
<member name="M:Discord.Rest.RestGuild.Discord#IGuild#GetStageChannelsAsync(Discord.CacheMode,Discord.RequestOptions)"> | |||||
<inheritdoc /> | |||||
</member> | |||||
<member name="M:Discord.Rest.RestGuild.Discord#IGuild#GetVoiceChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)"> | <member name="M:Discord.Rest.RestGuild.Discord#IGuild#GetVoiceChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
@@ -4365,6 +4425,9 @@ | |||||
<member name="P:Discord.Rest.RestGroupUser.Discord#IVoiceState#IsStreaming"> | <member name="P:Discord.Rest.RestGroupUser.Discord#IVoiceState#IsStreaming"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
<member name="P:Discord.Rest.RestGroupUser.Discord#IVoiceState#RequestToSpeakTimestamp"> | |||||
<inheritdoc /> | |||||
</member> | |||||
<member name="T:Discord.Rest.RestGuildUser"> | <member name="T:Discord.Rest.RestGuildUser"> | ||||
<summary> | <summary> | ||||
Represents a REST-based guild user. | Represents a REST-based guild user. | ||||
@@ -4456,6 +4519,9 @@ | |||||
<member name="P:Discord.Rest.RestGuildUser.Discord#IVoiceState#IsStreaming"> | <member name="P:Discord.Rest.RestGuildUser.Discord#IVoiceState#IsStreaming"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
<member name="P:Discord.Rest.RestGuildUser.Discord#IVoiceState#RequestToSpeakTimestamp"> | |||||
<inheritdoc /> | |||||
</member> | |||||
<member name="T:Discord.Rest.RestSelfUser"> | <member name="T:Discord.Rest.RestSelfUser"> | ||||
<summary> | <summary> | ||||
Represents the logged-in REST-based user. | Represents the logged-in REST-based user. | ||||
@@ -4679,6 +4745,9 @@ | |||||
<member name="P:Discord.Rest.RestWebhookUser.Discord#IVoiceState#IsStreaming"> | <member name="P:Discord.Rest.RestWebhookUser.Discord#IVoiceState#IsStreaming"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
<member name="P:Discord.Rest.RestWebhookUser.Discord#IVoiceState#RequestToSpeakTimestamp"> | |||||
<inheritdoc /> | |||||
</member> | |||||
<member name="P:Discord.Rest.RestWebhook.Token"> | <member name="P:Discord.Rest.RestWebhook.Token"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
@@ -577,6 +577,70 @@ namespace Discord.API | |||||
return await SendAsync<ChannelThreads>("GET", () => $"channels/{channelId}/users/@me/threads/archived/private{query}", bucket, options: options); | return await SendAsync<ChannelThreads>("GET", () => $"channels/{channelId}/users/@me/threads/archived/private{query}", bucket, options: options); | ||||
} | } | ||||
// stage | |||||
public async Task<StageInstance> CreateStageInstanceAsync(CreateStageInstanceParams args, RequestOptions options = null) | |||||
{ | |||||
options = RequestOptions.CreateOrClone(options); | |||||
var bucket = new BucketIds(); | |||||
return await SendJsonAsync<StageInstance>("POST", () => $"stage-instances", args, bucket, options: options).ConfigureAwait(false); | |||||
} | |||||
public async Task<StageInstance> ModifyStageInstanceAsync(ulong channelId, ModifyStageInstanceParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
options = RequestOptions.CreateOrClone(options); | |||||
var bucket = new BucketIds(channelId: channelId); | |||||
return await SendJsonAsync<StageInstance>("PATCH", () => $"stage-instances/{channelId}", args, bucket, options: options).ConfigureAwait(false); | |||||
} | |||||
public async Task DeleteStageInstanceAsync(ulong channelId, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
options = RequestOptions.CreateOrClone(options); | |||||
try | |||||
{ | |||||
await SendAsync("DELETE", $"stage-instances/{channelId}", options: options).ConfigureAwait(false); | |||||
} | |||||
catch (HttpException httpEx) when (httpEx.HttpCode == HttpStatusCode.NotFound) { } | |||||
} | |||||
public async Task<StageInstance> GetStageInstanceAsync(ulong channelId, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
options = RequestOptions.CreateOrClone(options); | |||||
var bucket = new BucketIds(channelId: channelId); | |||||
try | |||||
{ | |||||
return await SendAsync<StageInstance>("POST", () => $"stage-instances/{channelId}", bucket, options: options).ConfigureAwait(false); | |||||
} | |||||
catch(HttpException httpEx) when (httpEx.HttpCode == HttpStatusCode.NotFound) | |||||
{ | |||||
return null; | |||||
} | |||||
} | |||||
public async Task ModifyMyVoiceState(ulong guildId, ModifyVoiceStateParams args, RequestOptions options = null) | |||||
{ | |||||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
options = RequestOptions.CreateOrClone(options); | |||||
var bucket = new BucketIds(); | |||||
await SendJsonAsync("PATCH", () => $"guilds/{guildId}/voice-states/@me", args, bucket, options: options).ConfigureAwait(false); | |||||
} | |||||
// roles | // roles | ||||
public async Task AddRoleAsync(ulong guildId, ulong userId, ulong roleId, RequestOptions options = null) | public async Task AddRoleAsync(ulong guildId, ulong userId, ulong roleId, RequestOptions options = null) | ||||
{ | { | ||||
@@ -6,6 +6,7 @@ using System.IO; | |||||
using System.Linq; | using System.Linq; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Model = Discord.API.Channel; | using Model = Discord.API.Channel; | ||||
using StageInstance = Discord.API.StageInstance; | |||||
namespace Discord.Rest | namespace Discord.Rest | ||||
{ | { | ||||
@@ -92,6 +93,21 @@ namespace Discord.Rest | |||||
return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); | return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); | ||||
} | } | ||||
public static async Task<StageInstance> ModifyAsync(IStageChannel channel, BaseDiscordClient client, | |||||
Action<StageInstanceProperties> func, RequestOptions options = null) | |||||
{ | |||||
var args = new StageInstanceProperties(); | |||||
func(args); | |||||
var apiArgs = new ModifyStageInstanceParams() | |||||
{ | |||||
PrivacyLevel = args.PrivacyLevel, | |||||
Topic = args.Topic | |||||
}; | |||||
return await client.ApiClient.ModifyStageInstanceAsync(channel.Id, apiArgs, options); | |||||
} | |||||
//Invites | //Invites | ||||
public static async Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(IGuildChannel channel, BaseDiscordClient client, | public static async Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(IGuildChannel channel, BaseDiscordClient client, | ||||
RequestOptions options) | RequestOptions options) | ||||
@@ -40,6 +40,8 @@ namespace Discord.Rest | |||||
return RestTextChannel.Create(discord, guild, model); | return RestTextChannel.Create(discord, guild, model); | ||||
case ChannelType.Voice: | case ChannelType.Voice: | ||||
return RestVoiceChannel.Create(discord, guild, model); | return RestVoiceChannel.Create(discord, guild, model); | ||||
case ChannelType.Stage: | |||||
return RestStageChannel.Create(discord, guild, model); | |||||
case ChannelType.Category: | case ChannelType.Category: | ||||
return RestCategoryChannel.Create(discord, guild, model); | return RestCategoryChannel.Create(discord, guild, model); | ||||
case ChannelType.PublicThread or ChannelType.PrivateThread or ChannelType.NewsThread: | case ChannelType.PublicThread or ChannelType.PrivateThread or ChannelType.NewsThread: | ||||
@@ -0,0 +1,109 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using Model = Discord.API.Channel; | |||||
using StageInstance = Discord.API.StageInstance; | |||||
namespace Discord.Rest | |||||
{ | |||||
/// <summary> | |||||
/// Represents a REST-based stage channel in a guild. | |||||
/// </summary> | |||||
public class RestStageChannel : RestVoiceChannel, IStageChannel | |||||
{ | |||||
/// <inheritdoc/> | |||||
public string Topic { get; private set; } | |||||
/// <inheritdoc/> | |||||
public StagePrivacyLevel? PrivacyLevel { get; private set; } | |||||
/// <inheritdoc/> | |||||
public bool? DiscoverableDisabled { get; private set; } | |||||
/// <inheritdoc/> | |||||
public bool Live { get; private set; } | |||||
internal RestStageChannel(BaseDiscordClient discord, IGuild guild, ulong id) | |||||
: base(discord, guild, id) | |||||
{ | |||||
} | |||||
internal static new RestStageChannel Create(BaseDiscordClient discord, IGuild guild, Model model) | |||||
{ | |||||
var entity = new RestStageChannel(discord, guild, model.Id); | |||||
entity.Update(model); | |||||
return entity; | |||||
} | |||||
internal void Update(StageInstance model, bool isLive = false) | |||||
{ | |||||
this.Live = isLive; | |||||
if(isLive) | |||||
{ | |||||
this.Topic = model.Topic; | |||||
this.PrivacyLevel = model.PrivacyLevel; | |||||
this.DiscoverableDisabled = model.DiscoverableDisabled; | |||||
} | |||||
else | |||||
{ | |||||
this.Topic = null; | |||||
this.PrivacyLevel = null; | |||||
this.DiscoverableDisabled = null; | |||||
} | |||||
} | |||||
/// <inheritdoc/> | |||||
public async Task ModifyInstanceAsync(Action<StageInstanceProperties> func, RequestOptions options = null) | |||||
{ | |||||
var model = await ChannelHelper.ModifyAsync(this, Discord, func, options); | |||||
Update(model, true); | |||||
} | |||||
/// <inheritdoc/> | |||||
public async Task StartStageAsync(string topic, StagePrivacyLevel privacyLevel = StagePrivacyLevel.GuildOnly, RequestOptions options = null) | |||||
{ | |||||
var args = new API.Rest.CreateStageInstanceParams() | |||||
{ | |||||
ChannelId = this.Id, | |||||
PrivacyLevel = privacyLevel, | |||||
Topic = topic | |||||
}; | |||||
var model = await Discord.ApiClient.CreateStageInstanceAsync(args, options); | |||||
Update(model, true); | |||||
} | |||||
/// <inheritdoc/> | |||||
public async Task StopStageAsync(RequestOptions options = null) | |||||
{ | |||||
await Discord.ApiClient.DeleteStageInstanceAsync(this.Id, options); | |||||
Update(null, false); | |||||
} | |||||
/// <inheritdoc/> | |||||
public override async Task UpdateAsync(RequestOptions options = null) | |||||
{ | |||||
await base.UpdateAsync(options); | |||||
var model = await Discord.ApiClient.GetStageInstanceAsync(this.Id, options); | |||||
Update(model, model != null); | |||||
} | |||||
/// <inheritdoc/> | |||||
public Task RequestToSpeak(RequestOptions options = null) | |||||
{ | |||||
var args = new API.Rest.ModifyVoiceStateParams() | |||||
{ | |||||
ChannelId = this.Id, | |||||
RequestToSpeakTimestamp = DateTimeOffset.UtcNow | |||||
}; | |||||
return Discord.ApiClient.ModifyMyVoiceState(this.Guild.Id, args, options); | |||||
} | |||||
} | |||||
} |
@@ -451,6 +451,35 @@ namespace Discord.Rest | |||||
var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false); | var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false); | ||||
return channels.OfType<RestVoiceChannel>().ToImmutableArray(); | return channels.OfType<RestVoiceChannel>().ToImmutableArray(); | ||||
} | } | ||||
/// <summary> | |||||
/// Gets a stage channel in this guild | |||||
/// </summary> | |||||
/// <param name="id">The snowflake identifier for the stage channel.</param> | |||||
/// <param name="options">The options to be used when sending the request.</param> | |||||
/// <returns> | |||||
/// A task that represents the asynchronous get operation. The task result contains the stage channel associated | |||||
/// with the specified <paramref name="id"/>; <see langword="null" /> if none is found. | |||||
/// </returns> | |||||
public async Task<RestStageChannel> GetStageChannelAsync(ulong id, RequestOptions options = null) | |||||
{ | |||||
var channel = await GuildHelper.GetChannelAsync(this, Discord, id, options).ConfigureAwait(false); | |||||
return channel as RestStageChannel; | |||||
} | |||||
/// <summary> | |||||
/// Gets a collection of all stage channels in this guild. | |||||
/// </summary> | |||||
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param> | |||||
/// <param name="options">The options to be used when sending the request.</param> | |||||
/// <returns> | |||||
/// A task that represents the asynchronous get operation. The task result contains a read-only collection of | |||||
/// stage channels found within this guild. | |||||
/// </returns> | |||||
public async Task<IReadOnlyCollection<RestStageChannel>> GetStageChannelsAsync(RequestOptions options = null) | |||||
{ | |||||
var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false); | |||||
return channels.OfType<RestStageChannel>().ToImmutableArray(); | |||||
} | |||||
/// <summary> | /// <summary> | ||||
/// Gets a collection of all category channels in this guild. | /// Gets a collection of all category channels in this guild. | ||||
@@ -952,6 +981,22 @@ namespace Discord.Rest | |||||
return null; | return null; | ||||
} | } | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
async Task<IStageChannel> IGuild.GetStageChannelAsync(ulong id, CacheMode mode, RequestOptions options ) | |||||
{ | |||||
if (mode == CacheMode.AllowDownload) | |||||
return await GetStageChannelAsync(id, options).ConfigureAwait(false); | |||||
else | |||||
return null; | |||||
} | |||||
/// <inheritdoc /> | |||||
async Task<IReadOnlyCollection<IStageChannel>> IGuild.GetStageChannelsAsync(CacheMode mode, RequestOptions options) | |||||
{ | |||||
if (mode == CacheMode.AllowDownload) | |||||
return await GetStageChannelsAsync(options).ConfigureAwait(false); | |||||
else | |||||
return null; | |||||
} | |||||
/// <inheritdoc /> | |||||
async Task<IVoiceChannel> IGuild.GetVoiceChannelAsync(ulong id, CacheMode mode, RequestOptions options) | async Task<IVoiceChannel> IGuild.GetVoiceChannelAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
{ | { | ||||
if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
@@ -1109,5 +1154,6 @@ namespace Discord.Rest | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
async Task<IReadOnlyCollection<IWebhook>> IGuild.GetWebhooksAsync(RequestOptions options) | async Task<IReadOnlyCollection<IWebhook>> IGuild.GetWebhooksAsync(RequestOptions options) | ||||
=> await GetWebhooksAsync(options).ConfigureAwait(false); | => await GetWebhooksAsync(options).ConfigureAwait(false); | ||||
} | } | ||||
} | } |
@@ -1,3 +1,4 @@ | |||||
using System; | |||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using Model = Discord.API.User; | using Model = Discord.API.User; | ||||
@@ -37,5 +38,7 @@ namespace Discord.Rest | |||||
string IVoiceState.VoiceSessionId => null; | string IVoiceState.VoiceSessionId => null; | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
bool IVoiceState.IsStreaming => false; | bool IVoiceState.IsStreaming => false; | ||||
/// <inheritdoc /> | |||||
DateTimeOffset? IVoiceState.RequestToSpeakTimestamp => null; | |||||
} | } | ||||
} | } |
@@ -169,5 +169,7 @@ namespace Discord.Rest | |||||
string IVoiceState.VoiceSessionId => null; | string IVoiceState.VoiceSessionId => null; | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
bool IVoiceState.IsStreaming => false; | bool IVoiceState.IsStreaming => false; | ||||
/// <inheritdoc /> | |||||
DateTimeOffset? IVoiceState.RequestToSpeakTimestamp => null; | |||||
} | } | ||||
} | } |
@@ -107,5 +107,7 @@ namespace Discord.Rest | |||||
string IVoiceState.VoiceSessionId => null; | string IVoiceState.VoiceSessionId => null; | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
bool IVoiceState.IsStreaming => false; | bool IVoiceState.IsStreaming => false; | ||||
/// <inheritdoc /> | |||||
DateTimeOffset? IVoiceState.RequestToSpeakTimestamp => null; | |||||
} | } | ||||
} | } |
@@ -1,10 +1,14 @@ | |||||
#pragma warning disable CS1591 | |||||
#pragma warning disable CS1591 | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System; | |||||
namespace Discord.API.Gateway | namespace Discord.API.Gateway | ||||
{ | { | ||||
internal class GuildMemberUpdateEvent : GuildMember | internal class GuildMemberUpdateEvent : GuildMember | ||||
{ | { | ||||
[JsonProperty("joined_at")] | |||||
public new DateTimeOffset? JoinedAt { get; set; } | |||||
[JsonProperty("guild_id")] | [JsonProperty("guild_id")] | ||||
public ulong GuildId { get; set; } | public ulong GuildId { get; set; } | ||||
} | } | ||||
@@ -592,5 +592,64 @@ namespace Discord.WebSocket | |||||
} | } | ||||
internal readonly AsyncEvent<Func<SocketThreadUser, Task>> _threadMemberLeft = new AsyncEvent<Func<SocketThreadUser, Task>>(); | internal readonly AsyncEvent<Func<SocketThreadUser, Task>> _threadMemberLeft = new AsyncEvent<Func<SocketThreadUser, Task>>(); | ||||
/// <summary> | |||||
/// Fired when a stage is started. | |||||
/// </summary> | |||||
public event Func<SocketStageChannel, Task> StageStarted | |||||
{ | |||||
add { _stageStarted.Add(value); } | |||||
remove { _stageStarted.Remove(value); } | |||||
} | |||||
internal readonly AsyncEvent<Func<SocketStageChannel, Task>> _stageStarted = new AsyncEvent<Func<SocketStageChannel, Task>>(); | |||||
/// <summary> | |||||
/// Fired when a stage ends. | |||||
/// </summary> | |||||
public event Func<SocketStageChannel, Task> StageEnded | |||||
{ | |||||
add { _stageEnded.Add(value); } | |||||
remove { _stageEnded.Remove(value); } | |||||
} | |||||
internal readonly AsyncEvent<Func<SocketStageChannel, Task>> _stageEnded = new AsyncEvent<Func<SocketStageChannel, Task>>(); | |||||
/// <summary> | |||||
/// Fired when a stage is updated. | |||||
/// </summary> | |||||
public event Func<SocketStageChannel, SocketStageChannel, Task> StageUpdated | |||||
{ | |||||
add { _stageUpdated.Add(value); } | |||||
remove { _stageUpdated.Remove(value); } | |||||
} | |||||
internal readonly AsyncEvent<Func<SocketStageChannel, SocketStageChannel, Task>> _stageUpdated = new AsyncEvent<Func<SocketStageChannel, SocketStageChannel, Task>>(); | |||||
/// <summary> | |||||
/// Fired when a user requests to speak within a stage channel. | |||||
/// </summary> | |||||
public event Func<SocketStageChannel, SocketGuildUser, Task> RequestToSpeak | |||||
{ | |||||
add { _requestToSpeak.Add(value); } | |||||
remove { _requestToSpeak.Remove(value); } | |||||
} | |||||
internal readonly AsyncEvent<Func<SocketStageChannel, SocketGuildUser, Task>> _requestToSpeak = new AsyncEvent<Func<SocketStageChannel, SocketGuildUser, Task>>(); | |||||
/// <summary> | |||||
/// Fired when a speaker is added in a stage channel. | |||||
/// </summary> | |||||
public event Func<SocketStageChannel, SocketGuildUser, Task> SpeakerAdded | |||||
{ | |||||
add { _speakerAdded.Add(value); } | |||||
remove { _speakerAdded.Remove(value); } | |||||
} | |||||
internal readonly AsyncEvent<Func<SocketStageChannel, SocketGuildUser, Task>> _speakerAdded = new AsyncEvent<Func<SocketStageChannel, SocketGuildUser, Task>>(); | |||||
/// <summary> | |||||
/// Fired when a speaker is removed from a stage channel. | |||||
/// </summary> | |||||
public event Func<SocketStageChannel, SocketGuildUser, Task> SpeakerRemoved | |||||
{ | |||||
add { _speakerRemoved.Add(value); } | |||||
remove { _speakerRemoved.Remove(value); } | |||||
} | |||||
internal readonly AsyncEvent<Func<SocketStageChannel, SocketGuildUser, Task>> _speakerRemoved = new AsyncEvent<Func<SocketStageChannel, SocketGuildUser, Task>>(); | |||||
} | } | ||||
} | } |
@@ -1,4 +1,4 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<Import Project="../../Discord.Net.targets" /> | <Import Project="../../Discord.Net.targets" /> | ||||
<Import Project="../../StyleAnalyzer.targets" /> | <Import Project="../../StyleAnalyzer.targets" /> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
@@ -17,6 +17,9 @@ | |||||
<PropertyGroup> | <PropertyGroup> | ||||
<DocumentationFile>..\Discord.Net.WebSocket\Discord.Net.WebSocket.xml</DocumentationFile> | <DocumentationFile>..\Discord.Net.WebSocket\Discord.Net.WebSocket.xml</DocumentationFile> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net461|AnyCPU'"> | |||||
<DefineConstants>DEBUG;TRACE;DEBUG_LIMITS</DefineConstants> | |||||
</PropertyGroup> | |||||
<ItemGroup> | <ItemGroup> | ||||
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> | <ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> | ||||
<ProjectReference Include="..\Discord.Net.Rest\Discord.Net.Rest.csproj" /> | <ProjectReference Include="..\Discord.Net.Rest\Discord.Net.Rest.csproj" /> | ||||
@@ -781,6 +781,36 @@ | |||||
Fired when a user leaves a thread | Fired when a user leaves a thread | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="E:Discord.WebSocket.BaseSocketClient.StageStarted"> | |||||
<summary> | |||||
Fired when a stage is started. | |||||
</summary> | |||||
</member> | |||||
<member name="E:Discord.WebSocket.BaseSocketClient.StageEnded"> | |||||
<summary> | |||||
Fired when a stage ends. | |||||
</summary> | |||||
</member> | |||||
<member name="E:Discord.WebSocket.BaseSocketClient.StageUpdated"> | |||||
<summary> | |||||
Fired when a stage is updated. | |||||
</summary> | |||||
</member> | |||||
<member name="E:Discord.WebSocket.BaseSocketClient.RequestToSpeak"> | |||||
<summary> | |||||
Fired when a user requests to speak within a stage channel. | |||||
</summary> | |||||
</member> | |||||
<member name="E:Discord.WebSocket.BaseSocketClient.SpeakerAdded"> | |||||
<summary> | |||||
Fired when a speaker is added in a stage channel. | |||||
</summary> | |||||
</member> | |||||
<member name="E:Discord.WebSocket.BaseSocketClient.SpeakerRemoved"> | |||||
<summary> | |||||
Fired when a speaker is removed from a stage channel. | |||||
</summary> | |||||
</member> | |||||
<member name="P:Discord.WebSocket.DiscordShardedClient.Latency"> | <member name="P:Discord.WebSocket.DiscordShardedClient.Latency"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
@@ -2142,6 +2172,40 @@ | |||||
</note> | </note> | ||||
</remarks> | </remarks> | ||||
</member> | </member> | ||||
<member name="T:Discord.WebSocket.SocketStageChannel"> | |||||
<summary> | |||||
Represents a stage channel recieved over the gateway. | |||||
</summary> | |||||
</member> | |||||
<member name="P:Discord.WebSocket.SocketStageChannel.Topic"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="P:Discord.WebSocket.SocketStageChannel.PrivacyLevel"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="P:Discord.WebSocket.SocketStageChannel.DiscoverableDisabled"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="P:Discord.WebSocket.SocketStageChannel.Live"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="P:Discord.WebSocket.SocketStageChannel.Speakers"> | |||||
<summary> | |||||
Gets a collection of users who are speakers within the stage. | |||||
</summary> | |||||
</member> | |||||
<member name="M:Discord.WebSocket.SocketStageChannel.StartStageAsync(System.String,Discord.StagePrivacyLevel,Discord.RequestOptions)"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="M:Discord.WebSocket.SocketStageChannel.ModifyInstanceAsync(System.Action{Discord.StageInstanceProperties},Discord.RequestOptions)"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="M:Discord.WebSocket.SocketStageChannel.StopStageAsync(Discord.RequestOptions)"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="M:Discord.WebSocket.SocketStageChannel.RequestToSpeak(Discord.RequestOptions)"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="T:Discord.WebSocket.SocketTextChannel"> | <member name="T:Discord.WebSocket.SocketTextChannel"> | ||||
<summary> | <summary> | ||||
Represents a WebSocket-based channel in a guild that can send and receive messages. | Represents a WebSocket-based channel in a guild that can send and receive messages. | ||||
@@ -2901,6 +2965,14 @@ | |||||
A read-only collection of voice channels found within this guild. | A read-only collection of voice channels found within this guild. | ||||
</returns> | </returns> | ||||
</member> | </member> | ||||
<member name="P:Discord.WebSocket.SocketGuild.StageChannels"> | |||||
<summary> | |||||
Gets a collection of all stage channels in this guild. | |||||
</summary> | |||||
<returns> | |||||
A read-only collection of stage channels found within this guild. | |||||
</returns> | |||||
</member> | |||||
<member name="P:Discord.WebSocket.SocketGuild.CategoryChannels"> | <member name="P:Discord.WebSocket.SocketGuild.CategoryChannels"> | ||||
<summary> | <summary> | ||||
Gets a collection of all category channels in this guild. | Gets a collection of all category channels in this guild. | ||||
@@ -3076,6 +3148,15 @@ | |||||
A voice channel associated with the specified <paramref name="id" />; <see langword="null"/> if none is found. | A voice channel associated with the specified <paramref name="id" />; <see langword="null"/> if none is found. | ||||
</returns> | </returns> | ||||
</member> | </member> | ||||
<member name="M:Discord.WebSocket.SocketGuild.GetStageChannel(System.UInt64)"> | |||||
<summary> | |||||
Gets a stage channel in this guild. | |||||
</summary> | |||||
<param name="id">The snowflake identifier for the stage channel.</param> | |||||
<returns> | |||||
A stage channel associated with the specified <paramref name="id" />; <see langword="null"/> if none is found. | |||||
</returns> | |||||
</member> | |||||
<member name="M:Discord.WebSocket.SocketGuild.GetCategoryChannel(System.UInt64)"> | <member name="M:Discord.WebSocket.SocketGuild.GetCategoryChannel(System.UInt64)"> | ||||
<summary> | <summary> | ||||
Gets a category channel in this guild. | Gets a category channel in this guild. | ||||
@@ -3417,6 +3498,12 @@ | |||||
<member name="M:Discord.WebSocket.SocketGuild.Discord#IGuild#GetVoiceChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)"> | <member name="M:Discord.WebSocket.SocketGuild.Discord#IGuild#GetVoiceChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
<member name="M:Discord.WebSocket.SocketGuild.Discord#IGuild#GetStageChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)"> | |||||
<inheritdoc /> | |||||
</member> | |||||
<member name="M:Discord.WebSocket.SocketGuild.Discord#IGuild#GetStageChannelsAsync(Discord.CacheMode,Discord.RequestOptions)"> | |||||
<inheritdoc /> | |||||
</member> | |||||
<member name="M:Discord.WebSocket.SocketGuild.Discord#IGuild#GetAFKChannelAsync(Discord.CacheMode,Discord.RequestOptions)"> | <member name="M:Discord.WebSocket.SocketGuild.Discord#IGuild#GetAFKChannelAsync(Discord.CacheMode,Discord.RequestOptions)"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
@@ -4358,6 +4445,9 @@ | |||||
<member name="P:Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#IsStreaming"> | <member name="P:Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#IsStreaming"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
<member name="P:Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#RequestToSpeakTimestamp"> | |||||
<inheritdoc /> | |||||
</member> | |||||
<member name="T:Discord.WebSocket.SocketGuildUser"> | <member name="T:Discord.WebSocket.SocketGuildUser"> | ||||
<summary> | <summary> | ||||
Represents a WebSocket-based guild user. | Represents a WebSocket-based guild user. | ||||
@@ -4407,6 +4497,9 @@ | |||||
<member name="P:Discord.WebSocket.SocketGuildUser.IsStreaming"> | <member name="P:Discord.WebSocket.SocketGuildUser.IsStreaming"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
<member name="P:Discord.WebSocket.SocketGuildUser.RequestToSpeakTimestamp"> | |||||
<inheritdoc /> | |||||
</member> | |||||
<member name="P:Discord.WebSocket.SocketGuildUser.IsPending"> | <member name="P:Discord.WebSocket.SocketGuildUser.IsPending"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
@@ -4654,6 +4747,9 @@ | |||||
<member name="P:Discord.WebSocket.SocketThreadUser.IsStreaming"> | <member name="P:Discord.WebSocket.SocketThreadUser.IsStreaming"> | ||||
<inheritdoc/> | <inheritdoc/> | ||||
</member> | </member> | ||||
<member name="P:Discord.WebSocket.SocketThreadUser.RequestToSpeakTimestamp"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="M:Discord.WebSocket.SocketThreadUser.GetPermissions(Discord.IGuildChannel)"> | <member name="M:Discord.WebSocket.SocketThreadUser.GetPermissions(Discord.IGuildChannel)"> | ||||
<inheritdoc/> | <inheritdoc/> | ||||
</member> | </member> | ||||
@@ -4819,6 +4915,9 @@ | |||||
<member name="P:Discord.WebSocket.SocketVoiceState.VoiceSessionId"> | <member name="P:Discord.WebSocket.SocketVoiceState.VoiceSessionId"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
<member name="P:Discord.WebSocket.SocketVoiceState.RequestToSpeakTimestamp"> | |||||
<inheritdoc/> | |||||
</member> | |||||
<member name="P:Discord.WebSocket.SocketVoiceState.IsMuted"> | <member name="P:Discord.WebSocket.SocketVoiceState.IsMuted"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
@@ -4968,6 +5067,9 @@ | |||||
<member name="P:Discord.WebSocket.SocketWebhookUser.Discord#IVoiceState#IsStreaming"> | <member name="P:Discord.WebSocket.SocketWebhookUser.Discord#IVoiceState#IsStreaming"> | ||||
<inheritdoc /> | <inheritdoc /> | ||||
</member> | </member> | ||||
<member name="P:Discord.WebSocket.SocketWebhookUser.Discord#IVoiceState#RequestToSpeakTimestamp"> | |||||
<inheritdoc /> | |||||
</member> | |||||
<member name="T:Discord.WebSocket.SocketVoiceServer"> | <member name="T:Discord.WebSocket.SocketVoiceServer"> | ||||
<summary> | <summary> | ||||
Represents a WebSocket-based voice server. | Represents a WebSocket-based voice server. | ||||
@@ -1,4 +1,4 @@ | |||||
using System; | |||||
using System; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
@@ -35,4 +35,4 @@ namespace Discord.WebSocket | |||||
} | } | ||||
private readonly AsyncEvent<Func<int, int, DiscordSocketClient, Task>> _shardLatencyUpdatedEvent = new AsyncEvent<Func<int, int, DiscordSocketClient, Task>>(); | private readonly AsyncEvent<Func<int, int, DiscordSocketClient, Task>> _shardLatencyUpdatedEvent = new AsyncEvent<Func<int, int, DiscordSocketClient, Task>>(); | ||||
} | } | ||||
} | |||||
} |
@@ -386,6 +386,13 @@ namespace Discord.WebSocket | |||||
client.ThreadMemberJoined += (user) => _threadMemberJoined.InvokeAsync(user); | client.ThreadMemberJoined += (user) => _threadMemberJoined.InvokeAsync(user); | ||||
client.ThreadMemberLeft += (user) => _threadMemberLeft.InvokeAsync(user); | client.ThreadMemberLeft += (user) => _threadMemberLeft.InvokeAsync(user); | ||||
client.StageEnded += (stage) => _stageEnded.InvokeAsync(stage); | |||||
client.StageStarted += (stage) => _stageStarted.InvokeAsync(stage); | |||||
client.StageUpdated += (stage1, stage2) => _stageUpdated.InvokeAsync(stage1, stage2); | |||||
client.RequestToSpeak += (stage, user) => _requestToSpeak.InvokeAsync(stage, user); | |||||
client.SpeakerAdded += (stage, user) => _speakerAdded.InvokeAsync(stage, user); | |||||
client.SpeakerRemoved += (stage, user) => _speakerRemoved.InvokeAsync(stage, user); | |||||
} | } | ||||
//IDiscordClient | //IDiscordClient | ||||
@@ -1770,6 +1770,29 @@ namespace Discord.WebSocket | |||||
} | } | ||||
} | } | ||||
if (user is SocketGuildUser guildUser && data.ChannelId.HasValue) | |||||
{ | |||||
SocketStageChannel stage = guildUser.Guild.GetStageChannel(data.ChannelId.Value); | |||||
if (stage != null && before.VoiceChannel != null && after.VoiceChannel != null) | |||||
{ | |||||
if (!before.RequestToSpeakTimestamp.HasValue && after.RequestToSpeakTimestamp.HasValue) | |||||
{ | |||||
await TimedInvokeAsync(_requestToSpeak, nameof(RequestToSpeak), stage, guildUser); | |||||
return; | |||||
} | |||||
if(before.IsSuppressed && !after.IsSuppressed) | |||||
{ | |||||
await TimedInvokeAsync(_speakerAdded, nameof(SpeakerAdded), stage, guildUser); | |||||
return; | |||||
} | |||||
if(!before.IsSuppressed && after.IsSuppressed) | |||||
{ | |||||
await TimedInvokeAsync(_speakerRemoved, nameof(SpeakerRemoved), stage, guildUser); | |||||
} | |||||
} | |||||
} | |||||
await TimedInvokeAsync(_userVoiceStateUpdatedEvent, nameof(UserVoiceStateUpdated), user, before, after).ConfigureAwait(false); | await TimedInvokeAsync(_userVoiceStateUpdatedEvent, nameof(UserVoiceStateUpdated), user, before, after).ConfigureAwait(false); | ||||
} | } | ||||
break; | break; | ||||
@@ -2181,6 +2204,47 @@ namespace Discord.WebSocket | |||||
break; | break; | ||||
case "STAGE_INSTANCE_CREATE" or "STAGE_INSTANCE_UPDATE" or "STAGE_INSTANCE_DELETE": | |||||
{ | |||||
await _gatewayLogger.DebugAsync($"Received Dispatch ({type})").ConfigureAwait(false); | |||||
var data = (payload as JToken).ToObject<StageInstance>(_serializer); | |||||
var guild = State.GetGuild(data.GuildId); | |||||
if(guild == null) | |||||
{ | |||||
await UnknownGuildAsync(type, data.GuildId).ConfigureAwait(false); | |||||
return; | |||||
} | |||||
var stageChannel = guild.GetStageChannel(data.ChannelId); | |||||
if(stageChannel == null) | |||||
{ | |||||
await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false); | |||||
return; | |||||
} | |||||
SocketStageChannel before = type == "STAGE_INSTANCE_UPDATE" ? stageChannel.Clone() : null; | |||||
stageChannel.Update(data, type == "STAGE_INSTANCE_CREATE" ? true : type == "STAGE_INSTANCE_DELETE" ? false : false); | |||||
switch (type) | |||||
{ | |||||
case "STAGE_INSTANCE_CREATE": | |||||
await TimedInvokeAsync(_stageStarted, nameof(StageStarted), stageChannel); | |||||
return; | |||||
case "STAGE_INSTANCE_DELETE": | |||||
await TimedInvokeAsync(_stageEnded, nameof(StageEnded), stageChannel); | |||||
return; | |||||
case "STAGE_INSTANCE_UPDATE": | |||||
await TimedInvokeAsync(_stageUpdated, nameof(StageUpdated), before, stageChannel); | |||||
return; | |||||
} | |||||
} | |||||
break; | |||||
//Ignored (User only) | //Ignored (User only) | ||||
case "CHANNEL_PINS_ACK": | case "CHANNEL_PINS_ACK": | ||||
await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_ACK)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_ACK)").ConfigureAwait(false); | ||||
@@ -58,6 +58,8 @@ namespace Discord.WebSocket | |||||
return SocketCategoryChannel.Create(guild, state, model); | return SocketCategoryChannel.Create(guild, state, model); | ||||
case ChannelType.PrivateThread or ChannelType.PublicThread or ChannelType.NewsThread: | case ChannelType.PrivateThread or ChannelType.PublicThread or ChannelType.NewsThread: | ||||
return SocketThreadChannel.Create(guild, state, model); | return SocketThreadChannel.Create(guild, state, model); | ||||
case ChannelType.Stage: | |||||
return SocketStageChannel.Create(guild, state, model); | |||||
default: | default: | ||||
return new SocketGuildChannel(guild.Discord, model.Id, guild); | return new SocketGuildChannel(guild.Discord, model.Id, guild); | ||||
} | } | ||||
@@ -0,0 +1,116 @@ | |||||
using Discord.Rest; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Collections.Immutable; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using Model = Discord.API.Channel; | |||||
using StageInstance = Discord.API.StageInstance; | |||||
namespace Discord.WebSocket | |||||
{ | |||||
/// <summary> | |||||
/// Represents a stage channel recieved over the gateway. | |||||
/// </summary> | |||||
public class SocketStageChannel : SocketVoiceChannel, IStageChannel | |||||
{ | |||||
/// <inheritdoc/> | |||||
public string Topic { get; private set; } | |||||
/// <inheritdoc/> | |||||
public StagePrivacyLevel? PrivacyLevel { get; private set; } | |||||
/// <inheritdoc/> | |||||
public bool? DiscoverableDisabled { get; private set; } | |||||
/// <inheritdoc/> | |||||
public bool Live { get; private set; } = false; | |||||
/// <summary> | |||||
/// Gets a collection of users who are speakers within the stage. | |||||
/// </summary> | |||||
public IReadOnlyCollection<SocketGuildUser> Speakers | |||||
=> this.Users.Where(x => !x.IsSuppressed).ToImmutableArray(); | |||||
internal new SocketStageChannel Clone() => MemberwiseClone() as SocketStageChannel; | |||||
internal SocketStageChannel(DiscordSocketClient discord, ulong id, SocketGuild guild) | |||||
: base(discord, id, guild) | |||||
{ | |||||
} | |||||
internal new static SocketStageChannel Create(SocketGuild guild, ClientState state, Model model) | |||||
{ | |||||
var entity = new SocketStageChannel(guild.Discord, model.Id, guild); | |||||
entity.Update(state, model); | |||||
return entity; | |||||
} | |||||
internal override void Update(ClientState state, Model model) | |||||
{ | |||||
base.Update(state, model); | |||||
} | |||||
internal void Update(StageInstance model, bool isLive = false) | |||||
{ | |||||
this.Live = isLive; | |||||
if (isLive) | |||||
{ | |||||
this.Topic = model.Topic; | |||||
this.PrivacyLevel = model.PrivacyLevel; | |||||
this.DiscoverableDisabled = model.DiscoverableDisabled; | |||||
} | |||||
else | |||||
{ | |||||
this.Topic = null; | |||||
this.PrivacyLevel = null; | |||||
this.DiscoverableDisabled = null; | |||||
} | |||||
} | |||||
/// <inheritdoc/> | |||||
public async Task StartStageAsync(string topic, StagePrivacyLevel privacyLevel = StagePrivacyLevel.GuildOnly, RequestOptions options = null) | |||||
{ | |||||
var args = new API.Rest.CreateStageInstanceParams() | |||||
{ | |||||
ChannelId = this.Id, | |||||
Topic = topic, | |||||
PrivacyLevel = privacyLevel, | |||||
}; | |||||
var model = await Discord.ApiClient.CreateStageInstanceAsync(args, options).ConfigureAwait(false); | |||||
this.Update(model, true); | |||||
} | |||||
/// <inheritdoc/> | |||||
public async Task ModifyInstanceAsync(Action<StageInstanceProperties> func, RequestOptions options = null) | |||||
{ | |||||
var model = await ChannelHelper.ModifyAsync(this, Discord, func, options); | |||||
this.Update(model, true); | |||||
} | |||||
/// <inheritdoc/> | |||||
public async Task StopStageAsync(RequestOptions options = null) | |||||
{ | |||||
await Discord.ApiClient.DeleteStageInstanceAsync(this.Id, options); | |||||
Update(null, false); | |||||
} | |||||
/// <inheritdoc/> | |||||
public Task RequestToSpeak(RequestOptions options = null) | |||||
{ | |||||
var args = new API.Rest.ModifyVoiceStateParams() | |||||
{ | |||||
ChannelId = this.Id, | |||||
RequestToSpeakTimestamp = DateTimeOffset.UtcNow | |||||
}; | |||||
return Discord.ApiClient.ModifyMyVoiceState(this.Guild.Id, args, options); | |||||
} | |||||
} | |||||
} |
@@ -271,6 +271,14 @@ namespace Discord.WebSocket | |||||
public IReadOnlyCollection<SocketVoiceChannel> VoiceChannels | public IReadOnlyCollection<SocketVoiceChannel> VoiceChannels | ||||
=> Channels.OfType<SocketVoiceChannel>().ToImmutableArray(); | => Channels.OfType<SocketVoiceChannel>().ToImmutableArray(); | ||||
/// <summary> | /// <summary> | ||||
/// Gets a collection of all stage channels in this guild. | |||||
/// </summary> | |||||
/// <returns> | |||||
/// A read-only collection of stage channels found within this guild. | |||||
/// </returns> | |||||
public IReadOnlyCollection<SocketStageChannel> StageChannels | |||||
=> Channels.OfType<SocketStageChannel>().ToImmutableArray(); | |||||
/// <summary> | |||||
/// Gets a collection of all category channels in this guild. | /// Gets a collection of all category channels in this guild. | ||||
/// </summary> | /// </summary> | ||||
/// <returns> | /// <returns> | ||||
@@ -652,6 +660,15 @@ namespace Discord.WebSocket | |||||
public SocketVoiceChannel GetVoiceChannel(ulong id) | public SocketVoiceChannel GetVoiceChannel(ulong id) | ||||
=> GetChannel(id) as SocketVoiceChannel; | => GetChannel(id) as SocketVoiceChannel; | ||||
/// <summary> | /// <summary> | ||||
/// Gets a stage channel in this guild. | |||||
/// </summary> | |||||
/// <param name="id">The snowflake identifier for the stage channel.</param> | |||||
/// <returns> | |||||
/// A stage channel associated with the specified <paramref name="id" />; <see langword="null"/> if none is found. | |||||
/// </returns> | |||||
public SocketStageChannel GetStageChannel(ulong id) | |||||
=> GetChannel(id) as SocketStageChannel; | |||||
/// <summary> | |||||
/// Gets a category channel in this guild. | /// Gets a category channel in this guild. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="id">The snowflake identifier for the category channel.</param> | /// <param name="id">The snowflake identifier for the category channel.</param> | ||||
@@ -1354,6 +1371,12 @@ namespace Discord.WebSocket | |||||
Task<IVoiceChannel> IGuild.GetVoiceChannelAsync(ulong id, CacheMode mode, RequestOptions options) | Task<IVoiceChannel> IGuild.GetVoiceChannelAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
=> Task.FromResult<IVoiceChannel>(GetVoiceChannel(id)); | => Task.FromResult<IVoiceChannel>(GetVoiceChannel(id)); | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
Task<IStageChannel> IGuild.GetStageChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null) | |||||
=> Task.FromResult<IStageChannel>(GetStageChannel(id)); | |||||
/// <inheritdoc /> | |||||
Task<IReadOnlyCollection<IStageChannel>> IGuild.GetStageChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null) | |||||
=> Task.FromResult<IReadOnlyCollection<IStageChannel>>(StageChannels); | |||||
/// <inheritdoc /> | |||||
Task<IVoiceChannel> IGuild.GetAFKChannelAsync(CacheMode mode, RequestOptions options) | Task<IVoiceChannel> IGuild.GetAFKChannelAsync(CacheMode mode, RequestOptions options) | ||||
=> Task.FromResult<IVoiceChannel>(AFKChannel); | => Task.FromResult<IVoiceChannel>(AFKChannel); | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
@@ -1462,5 +1485,7 @@ namespace Discord.WebSocket | |||||
_audioLock?.Dispose(); | _audioLock?.Dispose(); | ||||
_audioClient?.Dispose(); | _audioClient?.Dispose(); | ||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,3 +1,4 @@ | |||||
using System; | |||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using Model = Discord.API.User; | using Model = Discord.API.User; | ||||
@@ -66,5 +67,7 @@ namespace Discord.WebSocket | |||||
string IVoiceState.VoiceSessionId => null; | string IVoiceState.VoiceSessionId => null; | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
bool IVoiceState.IsStreaming => false; | bool IVoiceState.IsStreaming => false; | ||||
/// <inheritdoc /> | |||||
DateTimeOffset? IVoiceState.RequestToSpeakTimestamp => null; | |||||
} | } | ||||
} | } |
@@ -57,7 +57,11 @@ namespace Discord.WebSocket | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public bool IsStreaming => VoiceState?.IsStreaming ?? false; | public bool IsStreaming => VoiceState?.IsStreaming ?? false; | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public DateTimeOffset? RequestToSpeakTimestamp => VoiceState?.RequestToSpeakTimestamp ?? null; | |||||
/// <inheritdoc /> | |||||
public bool? IsPending { get; private set; } | public bool? IsPending { get; private set; } | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); | public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); | ||||
/// <summary> | /// <summary> | ||||
@@ -110,6 +110,10 @@ namespace Discord.WebSocket | |||||
public bool IsStreaming | public bool IsStreaming | ||||
=> GuildUser.IsStreaming; | => GuildUser.IsStreaming; | ||||
/// <inheritdoc/> | |||||
public DateTimeOffset? RequestToSpeakTimestamp | |||||
=> GuildUser.RequestToSpeakTimestamp; | |||||
private SocketGuildUser GuildUser { get; set; } | private SocketGuildUser GuildUser { get; set; } | ||||
internal SocketThreadUser(SocketGuild guild, SocketThreadChannel thread, SocketGuildUser member) | internal SocketThreadUser(SocketGuild guild, SocketThreadChannel thread, SocketGuildUser member) | ||||
@@ -13,7 +13,7 @@ namespace Discord.WebSocket | |||||
/// <summary> | /// <summary> | ||||
/// Initializes a default <see cref="SocketVoiceState"/> with everything set to <c>null</c> or <c>false</c>. | /// Initializes a default <see cref="SocketVoiceState"/> with everything set to <c>null</c> or <c>false</c>. | ||||
/// </summary> | /// </summary> | ||||
public static readonly SocketVoiceState Default = new SocketVoiceState(null, null, false, false, false, false, false, false); | |||||
public static readonly SocketVoiceState Default = new SocketVoiceState(null, null, null, false, false, false, false, false, false); | |||||
[Flags] | [Flags] | ||||
private enum Flags : byte | private enum Flags : byte | ||||
@@ -35,6 +35,8 @@ namespace Discord.WebSocket | |||||
public SocketVoiceChannel VoiceChannel { get; } | public SocketVoiceChannel VoiceChannel { get; } | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public string VoiceSessionId { get; } | public string VoiceSessionId { get; } | ||||
/// <inheritdoc/> | |||||
public DateTimeOffset? RequestToSpeakTimestamp { get; private set; } | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public bool IsMuted => (_voiceStates & Flags.Muted) != 0; | public bool IsMuted => (_voiceStates & Flags.Muted) != 0; | ||||
@@ -48,11 +50,13 @@ namespace Discord.WebSocket | |||||
public bool IsSelfDeafened => (_voiceStates & Flags.SelfDeafened) != 0; | public bool IsSelfDeafened => (_voiceStates & Flags.SelfDeafened) != 0; | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public bool IsStreaming => (_voiceStates & Flags.SelfStream) != 0; | public bool IsStreaming => (_voiceStates & Flags.SelfStream) != 0; | ||||
internal SocketVoiceState(SocketVoiceChannel voiceChannel, string sessionId, bool isSelfMuted, bool isSelfDeafened, bool isMuted, bool isDeafened, bool isSuppressed, bool isStream) | |||||
internal SocketVoiceState(SocketVoiceChannel voiceChannel, DateTimeOffset? requestToSpeak, string sessionId, bool isSelfMuted, bool isSelfDeafened, bool isMuted, bool isDeafened, bool isSuppressed, bool isStream) | |||||
{ | { | ||||
VoiceChannel = voiceChannel; | VoiceChannel = voiceChannel; | ||||
VoiceSessionId = sessionId; | VoiceSessionId = sessionId; | ||||
RequestToSpeakTimestamp = requestToSpeak; | |||||
Flags voiceStates = Flags.Normal; | Flags voiceStates = Flags.Normal; | ||||
if (isSelfMuted) | if (isSelfMuted) | ||||
@@ -71,7 +75,7 @@ namespace Discord.WebSocket | |||||
} | } | ||||
internal static SocketVoiceState Create(SocketVoiceChannel voiceChannel, Model model) | internal static SocketVoiceState Create(SocketVoiceChannel voiceChannel, Model model) | ||||
{ | { | ||||
return new SocketVoiceState(voiceChannel, model.SessionId, model.SelfMute, model.SelfDeaf, model.Mute, model.Deaf, model.Suppress, model.SelfStream); | |||||
return new SocketVoiceState(voiceChannel, model.RequestToSpeakTimestamp.IsSpecified ? model.RequestToSpeakTimestamp.Value : null, model.SessionId, model.SelfMute, model.SelfDeaf, model.Mute, model.Deaf, model.Suppress, model.SelfStream); | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
@@ -138,5 +138,7 @@ namespace Discord.WebSocket | |||||
string IVoiceState.VoiceSessionId => null; | string IVoiceState.VoiceSessionId => null; | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
bool IVoiceState.IsStreaming => false; | bool IVoiceState.IsStreaming => false; | ||||
/// <inheritdoc /> | |||||
DateTimeOffset? IVoiceState.RequestToSpeakTimestamp => null; | |||||
} | } | ||||
} | } |