Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Voice channel status support #2777

Merged
merged 5 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Discord.Net.Core/DiscordConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ public class DiscordConfig
/// </summary>
public const int MaxApplicationTagCount = 5;

/// <summary>
/// Returns the maximum length of a voice channel status.
/// </summary>
public const int MaxVoiceChannelStatusLength = 500;

/// <summary>
/// Returns the maximum number of entitlements that can be gotten per-batch.
/// </summary>
Expand Down
12 changes: 11 additions & 1 deletion src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,16 @@ public enum ActionType
/// <summary>
/// Guild Onboarding was updated.
/// </summary>
OnboardingUpdated = 167
OnboardingUpdated = 167,

/// <summary>
/// A voice channel status was updated by a user.
/// </summary>
VoiceChannelStatusUpdated = 192,

/// <summary>
/// A voice channel status was deleted by a user.
/// </summary>
VoiceChannelStatusDeleted = 193
}
}
10 changes: 10 additions & 0 deletions src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,15 @@ public interface IVoiceChannel : ITextChannel, IAudioChannel
/// </returns>
/// <seealso cref="VoiceChannelProperties"/>
Task ModifyAsync(Action<VoiceChannelProperties> func, RequestOptions options = null);

/// <summary>
/// Sets the voice channel status in the current channel.
/// </summary>
/// <param name="status">The string to set as status.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous modification operation.
/// </returns>
Task SetStatusAsync(string status, RequestOptions options = null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,10 @@ public enum ChannelPermission : ulong
/// Allows sending voice messages.
/// </summary>
SendVoiceMessages = 1L << 46,

/// <summary>
/// Allows setting voice channel status.
/// </summary>
SetVoiceChannelStatus = 1L << 48,
}
}
20 changes: 14 additions & 6 deletions src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public struct ChannelPermissions
/// <summary>
/// Gets a <see cref="ChannelPermissions"/> that grants all permissions for voice channels.
/// </summary>
public static readonly ChannelPermissions Voice = new(0b10001_001010_001010_110011_111101_111111_111101_010001);
public static readonly ChannelPermissions Voice = new(0b1_010001_001010_001010_110011_111101_111111_111101_010001);

/// <summary>
/// Gets a <see cref="ChannelPermissions"/> that grants all permissions for stage channels.
Expand Down Expand Up @@ -142,6 +142,9 @@ public static ChannelPermissions All(IChannel channel)
public bool CreateEvents => Permissions.GetValue(RawValue, ChannelPermission.CreateEvents);
/// <summary> If <see langword="true"/>, a user can send voice messages in this channel.</summary>
public bool SendVoiceMessages => Permissions.GetValue(RawValue, ChannelPermission.SendVoiceMessages);
/// <summary> If <see langword="true"/>, a user can set the status of a voice channel.</summary>
public bool SetVoiceChannelStatus => Permissions.GetValue(RawValue, GuildPermission.SetVoiceChannelStatus);


/// <summary> Creates a new <see cref="ChannelPermissions"/> with the provided packed value.</summary>
public ChannelPermissions(ulong rawValue) { RawValue = rawValue; }
Expand Down Expand Up @@ -179,7 +182,8 @@ private ChannelPermissions(ulong initialValue,
bool? startEmbeddedActivities = null,
bool? useSoundboard = null,
bool? createEvents = null,
bool? sendVoiceMessages = null)
bool? sendVoiceMessages = null,
bool? setVoiceChannelStatus = null)
{
ulong value = initialValue;

Expand Down Expand Up @@ -216,6 +220,7 @@ private ChannelPermissions(ulong initialValue,
Permissions.SetValue(ref value, useSoundboard, ChannelPermission.UseSoundboard);
Permissions.SetValue(ref value, createEvents, ChannelPermission.CreateEvents);
Permissions.SetValue(ref value, sendVoiceMessages, ChannelPermission.SendVoiceMessages);
Permissions.SetValue(ref value, setVoiceChannelStatus, ChannelPermission.SetVoiceChannelStatus);

RawValue = value;
}
Expand Down Expand Up @@ -254,12 +259,13 @@ public ChannelPermissions(
bool startEmbeddedActivities = false,
bool useSoundboard = false,
bool createEvents = false,
bool sendVoiceMessages = false)
bool sendVoiceMessages = false,
bool setVoiceChannelStatus = false)
: this(0, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages,
embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect,
speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, prioritySpeaker, stream, manageRoles, manageWebhooks,
useApplicationCommands, requestToSpeak, manageThreads, createPublicThreads, createPrivateThreads, useExternalStickers, sendMessagesInThreads,
startEmbeddedActivities, useSoundboard, createEvents, sendVoiceMessages)
startEmbeddedActivities, useSoundboard, createEvents, sendVoiceMessages, setVoiceChannelStatus)
{ }

/// <summary> Creates a new <see cref="ChannelPermissions"/> from this one, changing the provided non-null permissions.</summary>
Expand Down Expand Up @@ -296,7 +302,8 @@ public ChannelPermissions Modify(
bool? startEmbeddedActivities = null,
bool? useSoundboard = null,
bool? createEvents = null,
bool? sendVoiceMessages = null)
bool? sendVoiceMessages = null,
bool? setVoiceChannelStatus = null)
=> new ChannelPermissions(RawValue,
createInstantInvite,
manageChannel,
Expand Down Expand Up @@ -330,7 +337,8 @@ public ChannelPermissions Modify(
startEmbeddedActivities,
useSoundboard,
createEvents,
sendVoiceMessages);
sendVoiceMessages,
setVoiceChannelStatus);

public bool Has(ChannelPermission permission) => Permissions.GetValue(RawValue, permission);

Expand Down
5 changes: 5 additions & 0 deletions src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,5 +267,10 @@ public enum GuildPermission : ulong
/// Allows sending voice messages.
/// </summary>
SendVoiceMessages = 1L << 46,

/// <summary>
/// Allows setting voice channel status.
/// </summary>
SetVoiceChannelStatus = 1L << 48,
}
}
17 changes: 12 additions & 5 deletions src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ public struct GuildPermissions
public bool ViewMonetizationAnalytics => Permissions.GetValue(RawValue, GuildPermission.ViewMonetizationAnalytics);
/// <summary> If <see langword="true"/>, a user can send voice messages in this channel.</summary>
public bool SendVoiceMessages => Permissions.GetValue(RawValue, GuildPermission.SendVoiceMessages);
/// <summary> If <see langword="true"/>, a user can set the status of a voice channel.</summary>
public bool SetVoiceChannelStatus => Permissions.GetValue(RawValue, GuildPermission.SetVoiceChannelStatus);

/// <summary> Creates a new <see cref="GuildPermissions"/> with the provided packed value. </summary>
public GuildPermissions(ulong rawValue) { RawValue = rawValue; }
Expand Down Expand Up @@ -161,7 +163,8 @@ private GuildPermissions(ulong initialValue,
bool? moderateMembers = null,
bool? useSoundboard = null,
bool? viewMonetizationAnalytics = null,
bool? sendVoiceMessages = null)
bool? sendVoiceMessages = null,
bool? setVoiceChannelStatus = null)
{
ulong value = initialValue;

Expand Down Expand Up @@ -209,6 +212,7 @@ private GuildPermissions(ulong initialValue,
Permissions.SetValue(ref value, useSoundboard, GuildPermission.UseSoundboard);
Permissions.SetValue(ref value, viewMonetizationAnalytics, GuildPermission.ViewMonetizationAnalytics);
Permissions.SetValue(ref value, sendVoiceMessages, GuildPermission.SendVoiceMessages);
Permissions.SetValue(ref value, setVoiceChannelStatus, GuildPermission.SetVoiceChannelStatus);

RawValue = value;
}
Expand Down Expand Up @@ -258,7 +262,8 @@ public GuildPermissions(
bool moderateMembers = false,
bool useSoundboard = false,
bool viewMonetizationAnalytics = false,
bool sendVoiceMessages = false)
bool sendVoiceMessages = false,
bool setVoiceChannelStatus = false)
: this(0,
createInstantInvite: createInstantInvite,
manageRoles: manageRoles,
Expand Down Expand Up @@ -303,7 +308,8 @@ public GuildPermissions(
moderateMembers: moderateMembers,
useSoundboard: useSoundboard,
viewMonetizationAnalytics: viewMonetizationAnalytics,
sendVoiceMessages: sendVoiceMessages)
sendVoiceMessages: sendVoiceMessages,
setVoiceChannelStatus: setVoiceChannelStatus)
{ }

/// <summary> Creates a new <see cref="GuildPermissions"/> from this one, changing the provided non-null permissions. </summary>
Expand Down Expand Up @@ -351,13 +357,14 @@ public GuildPermissions Modify(
bool? moderateMembers = null,
bool? useSoundboard = null,
bool? viewMonetizationAnalytics = null,
bool? sendVoiceMessages = null)
bool? sendVoiceMessages = null,
bool? setVoiceChannelStatus = null)
=> new GuildPermissions(RawValue, createInstantInvite, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions,
viewAuditLog, viewGuildInsights, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles,
readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers,
useVoiceActivation, prioritySpeaker, stream, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojisAndStickers,
useApplicationCommands, requestToSpeak, manageEvents, manageThreads, createPublicThreads, createPrivateThreads, useExternalStickers, sendMessagesInThreads,
startEmbeddedActivities, moderateMembers, useSoundboard, viewMonetizationAnalytics, sendVoiceMessages);
startEmbeddedActivities, moderateMembers, useSoundboard, viewMonetizationAnalytics, sendVoiceMessages, setVoiceChannelStatus);

/// <summary>
/// Returns a value that indicates if a specific <see cref="GuildPermission"/> is enabled
Expand Down
3 changes: 3 additions & 0 deletions src/Discord.Net.Rest/API/Common/AuditLogOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ internal class AuditLogOptions

[JsonProperty("auto_moderation_rule_trigger_type")]
public AutoModTriggerType? AutoModRuleTriggerType { get; set; }

[JsonProperty("status")]
public string Status { get; set; }
}
3 changes: 3 additions & 0 deletions src/Discord.Net.Rest/API/Common/Channel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ internal class Channel
[JsonProperty("video_quality_mode")]
public Optional<VideoQualityMode> VideoQualityMode { get; set; }

[JsonProperty("status")]
public Optional<string> Status { get; set; }

//PrivateChannel
[JsonProperty("recipients")]
public Optional<User[]> Recipients { get; set; }
Expand Down
9 changes: 9 additions & 0 deletions src/Discord.Net.Rest/API/Rest/ModifyVoiceStatusParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Newtonsoft.Json;

namespace Discord.API.Rest;

internal class ModifyVoiceStatusParams
{
[JsonProperty("status")]
public string Status { get; set; }
}
11 changes: 11 additions & 0 deletions src/Discord.Net.Rest/DiscordRestApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,17 @@ public async Task ModifyGuildChannelsAsync(ulong guildId, IEnumerable<Rest.Modif
break;
}
}

public async Task ModifyVoiceChannelStatusAsync(ulong channelId, string status, RequestOptions options = null)
{
Preconditions.NotEqual(channelId, 0, nameof(channelId));

var payload = new ModifyVoiceStatusParams { Status = status };
var ids = new BucketIds();

await SendJsonAsync("PUT", () => $"channels/{channelId}/voice-status", payload, ids, options: options);
}

#endregion

#region Threads
Expand Down
3 changes: 3 additions & 0 deletions src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ private static readonly Dictionary<ActionType, Func<BaseDiscordClient, EntryMode
[ActionType.OnboardingQuestionCreated] = OnboardingPromptCreatedAuditLogData.Create,
[ActionType.OnboardingQuestionUpdated] = OnboardingPromptUpdatedAuditLogData.Create,
[ActionType.OnboardingUpdated] = OnboardingUpdatedAuditLogData.Create,

[ActionType.VoiceChannelStatusUpdated] = VoiceChannelStatusUpdateAuditLogData.Create,
[ActionType.VoiceChannelStatusDeleted] = VoiceChannelStatusDeletedAuditLogData.Create
};

public static IAuditLogData CreateData(BaseDiscordClient discord, EntryModel entry, Model log = null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using EntryModel = Discord.API.AuditLogEntry;
using Model = Discord.API.AuditLog;

namespace Discord.Rest;

/// <summary>
/// Contains a piece of audit log data related to a voice channel status delete.
/// </summary>
public class VoiceChannelStatusDeletedAuditLogData : IAuditLogData
{
private VoiceChannelStatusDeletedAuditLogData(ulong channelId)
{
ChannelId = channelId;
}

internal static VoiceChannelStatusDeletedAuditLogData Create(BaseDiscordClient discord, EntryModel entry, Model log = null)
{
return new (entry.TargetId!.Value);
}

/// <summary>
/// Get the id of the channel status was removed in.
/// </summary>
public ulong ChannelId { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using EntryModel = Discord.API.AuditLogEntry;
using Model = Discord.API.AuditLog;

namespace Discord.Rest;

/// <summary>
/// Contains a piece of audit log data related to a voice channel status update.
/// </summary>
public class VoiceChannelStatusUpdateAuditLogData : IAuditLogData
{
private VoiceChannelStatusUpdateAuditLogData(string status, ulong channelId)
{
Status = status;
ChannelId = channelId;
}

internal static VoiceChannelStatusUpdateAuditLogData Create(BaseDiscordClient discord, EntryModel entry, Model log = null)
{
return new (entry.Options.Status, entry.TargetId!.Value);
}

/// <summary>
/// Gets the status that was set in the voice channel.
/// </summary>
public string Status { get; }

/// <summary>
/// Get the id of the channel status was updated in.
/// </summary>
public ulong ChannelId { get; }
}
11 changes: 11 additions & 0 deletions src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -636,5 +636,16 @@ public static async Task SyncPermissionsAsync(INestedChannel channel, BaseDiscor
await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false);
}
#endregion

#region Voice

public static async Task ModifyVoiceChannelStatusAsync(IVoiceChannel channel, string status, BaseDiscordClient client, RequestOptions options)
{
Preconditions.AtMost(status.Length, DiscordConfig.MaxVoiceChannelStatusLength, $"Voice channel status length must be less than {DiscordConfig.MaxVoiceChannelStatusLength}.");

await client.ApiClient.ModifyVoiceChannelStatusAsync(channel.Id, status, options).ConfigureAwait(false);
}

#endregion
}
}
8 changes: 8 additions & 0 deletions src/Discord.Net.Rest/Entities/Channels/RestStageChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,13 @@ public Task RemoveFromSpeakerAsync(IGuildUser user, RequestOptions options = nul

return Discord.ApiClient.ModifyUserVoiceState(Guild.Id, user.Id, args);
}

/// <inheritdoc />
/// <remarks>
/// Setting voice channel status is not supported in stage channels.
/// </remarks>
/// <exception cref="NotSupportedException">Setting voice channel status is not supported in stage channels.</exception>
public override Task SetStatusAsync(string status, RequestOptions options = null)
=> throw new NotSupportedException("Setting voice channel status is not supported in stage channels.");
}
}
4 changes: 4 additions & 0 deletions src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ public async Task ModifyAsync(Action<VoiceChannelProperties> func, RequestOption
public override Task<RestThreadChannel> CreateThreadAsync(string name, ThreadType type = ThreadType.PublicThread, ThreadArchiveDuration autoArchiveDuration = ThreadArchiveDuration.OneDay, IMessage message = null, bool? invitable = null, int? slowmode = null, RequestOptions options = null)
=> throw new InvalidOperationException("Cannot create a thread within a voice channel");

/// <inheritdoc />
public virtual Task SetStatusAsync(string status, RequestOptions options = null)
=> ChannelHelper.ModifyVoiceChannelStatusAsync(this, status, Discord, options);

#endregion

private string DebuggerDisplay => $"{Name} ({Id}, Voice)";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Newtonsoft.Json;

namespace Discord.API.Gateway;

internal class VoiceChannelStatusUpdateEvent
{
[JsonProperty("id")]
public ulong Id { get; set; }

[JsonProperty("guild_id")]
public ulong GuildId { get; set; }

[JsonProperty("status")]
public string Status { get; set; }
}
16 changes: 16 additions & 0 deletions src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@ public event Func<SocketChannel, SocketChannel, Task> ChannelUpdated
remove { _channelUpdatedEvent.Remove(value); }
}
internal readonly AsyncEvent<Func<SocketChannel, SocketChannel, Task>> _channelUpdatedEvent = new AsyncEvent<Func<SocketChannel, SocketChannel, Task>>();

/// <summary>
/// Fired when status of a voice channel is updated.
/// </summary>
/// <remarks>
/// The previous state of the channel is passed into the first parameter; the updated channel is passed into the second one.
/// </remarks>
public event Func<SocketVoiceChannel, SocketVoiceChannel, Task> VoiceChannelStatusUpdated
{
add { _voiceChannelStatusUpdated.Add(value); }
remove { _voiceChannelStatusUpdated.Remove(value); }
}

internal readonly AsyncEvent<Func<SocketVoiceChannel, SocketVoiceChannel, Task>> _voiceChannelStatusUpdated = new();


#endregion

#region Messages
Expand Down
Loading