Browse Source

Merge branch 'dev' into docs/di-docs

pull/2407/head
Armano den Boef GitHub 2 years ago
parent
commit
e8353cc321
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 212 additions and 91 deletions
  1. +7
    -1
      docs/guides/deployment/deployment.md
  2. +20
    -3
      docs/guides/int_framework/intro.md
  3. +14
    -0
      docs/guides/int_framework/samples/intro/event.cs
  4. +6
    -1
      docs/guides/int_framework/samples/intro/groupmodule.cs
  5. +9
    -2
      docs/guides/int_framework/samples/intro/modal.cs
  6. +2
    -4
      docs/guides/voice/sending-voice.md
  7. +1
    -1
      experiment/Discord.Net.BuildOverrides/BuildOverrides.cs
  8. +10
    -5
      samples/ShardedClient/Services/InteractionHandlingService.cs
  9. +2
    -0
      src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
  10. +13
    -0
      src/Discord.Net.Commands/IModuleBase.cs
  11. +12
    -0
      src/Discord.Net.Commands/ModuleBase.cs
  12. +43
    -43
      src/Discord.Net.Core/Entities/Guilds/GuildFeature.cs
  13. +3
    -2
      src/Discord.Net.Interactions/TypeConverters/SlashCommands/NullableConverter.cs
  14. +1
    -1
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  15. +1
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ThreadUpdateAuditLogData.cs
  16. +1
    -1
      src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
  17. +1
    -1
      src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs
  18. +13
    -0
      src/Discord.Net.WebSocket/API/Gateway/WebhooksUpdatedEvent.cs
  19. +29
    -14
      src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
  20. +2
    -0
      src/Discord.Net.WebSocket/DiscordShardedClient.cs
  21. +17
    -3
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  22. +3
    -6
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
  23. +2
    -2
      src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketResolvableData.cs

+ 7
- 1
docs/guides/deployment/deployment.md View File

@@ -47,6 +47,12 @@ enough. Here is a list of recommended VPS provider.
* Location(s):
* Europe: Lithuania
* Based in: Europe
* [ServerStarter.Host](https://serverstarter.host/clients/store/discord-bots)
* Description: Bot hosting with a panel for quick deployment and
no Linux knowledge required.
* Location(s):
* America: United States
* Based in: United States

## .NET Core Deployment

@@ -100,4 +106,4 @@ Windows 10 x64 based machine:
* `dotnet publish -c Release -r win10-x64`

[.NET Core application deployment]: https://docs.microsoft.com/en-us/dotnet/core/deploying/
[Runtime ID]: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog
[Runtime ID]: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog

+ 20
- 3
docs/guides/int_framework/intro.md View File

@@ -279,8 +279,8 @@ Meaning, the constructor parameters and public settable properties of a module w
For more information on dependency injection, read the [DependencyInjection] guides.

> [!NOTE]
> On every command execution, module dependencies are resolved using a new service scope which allows you to utilize scoped service instances, just like in Asp.Net.
> Including the precondition checks, every module method is executed using the same service scope and service scopes are disposed right after the `AfterExecute` method returns.
> On every command execution, if the 'AutoServiceScopes' option is enabled in the config , module dependencies are resolved using a new service scope which allows you to utilize scoped service instances, just like in Asp.Net.
> Including the precondition checks, every module method is executed using the same service scope and service scopes are disposed right after the `AfterExecute` method returns. This doesn't apply to methods other than `ExecuteAsync()`.

## Module Groups

@@ -291,6 +291,11 @@ By nesting commands inside a module that is tagged with [GroupAttribute] you can
> Although creating nested module stuctures are allowed,
> you are not permitted to use more than 2 [GroupAttribute]'s in module hierarchy.

> [!NOTE]
> To not use the command group's name as a prefix for component or modal interaction's custom id set `ignoreGroupNames` parameter to `true` in classes with [GroupAttribute]
>
> However, you have to be careful to prevent overlapping ids of buttons and modals

[!code-csharp[Command Group Example](samples/intro/groupmodule.cs)]

## Executing Commands
@@ -303,8 +308,19 @@ Any of the following socket events can be used to execute commands:
- [AutocompleteExecuted]
- [UserCommandExecuted]
- [MessageCommandExecuted]
- [ModalExecuted]

These events will trigger for the specific type of interaction they inherit their name from. The [InteractionCreated] event will trigger for all.
An example of executing a command from an event can be seen here:

Commands can be either executed on the gateway thread or on a seperate thread from the thread pool. This behaviour can be configured by changing the *RunMode* property of `InteractionServiceConfig` or by setting the *runMode* parameter of a command attribute.
[!code-csharp[Command Event Example](samples/intro/event.cs)]

Commands can be either executed on the gateway thread or on a seperate thread from the thread pool.
This behaviour can be configured by changing the `RunMode` property of `InteractionServiceConfig` or by setting the *runMode* parameter of a command attribute.

> [!WARNING]
> In the example above, no form of post-execution is presented.
> Please carefully read the [Post Execution Documentation] for the best approach in resolving the result based on your `RunMode`.

You can also configure the way [InteractionService] executes the commands.
By default, commands are executed using `ConstructorInfo.Invoke()` to create module instances and
@@ -371,6 +387,7 @@ delegate can be used to create HTTP responses from a deserialized json object st
[AutocompleteExecuted]: xref:Discord.WebSocket.BaseSocketClient
[UserCommandExecuted]: xref:Discord.WebSocket.BaseSocketClient
[MessageCommandExecuted]: xref:Discord.WebSocket.BaseSocketClient
[ModalExecuted]: xref:Discord.WebSocket.BaseSocketClient
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient
[DiscordRestClient]: xref:Discord.Rest.DiscordRestClient
[SocketInteractionContext]: xref:Discord.Interactions.SocketInteractionContext


+ 14
- 0
docs/guides/int_framework/samples/intro/event.cs View File

@@ -0,0 +1,14 @@
// Theres multiple ways to subscribe to the event, depending on your application. Please use the approach fit to your type of client.
// DiscordSocketClient:
_socketClient.InteractionCreated += async (x) =>
{
var ctx = new SocketInteractionContext(_socketClient, x);
await _interactionService.ExecuteCommandAsync(ctx, _serviceProvider);
}

// DiscordShardedClient:
_shardedClient.InteractionCreated += async (x) =>
{
var ctx = new ShardedInteractionContext(_shardedClient, x);
await _interactionService.ExecuteCommandAsync(ctx, _serviceProvider);
}

+ 6
- 1
docs/guides/int_framework/samples/intro/groupmodule.cs View File

@@ -16,6 +16,11 @@ public class CommandGroupModule : InteractionModuleBase<SocketInteractionContext
// group-name subcommand-group-name echo
[SlashCommand("echo", "Echo an input")]
public async Task EchoSubcommand(string input)
=> await RespondAsync(input);
=> await RespondAsync(input, components: new ComponentBuilder().WithButton("Echo", $"echoButton_{input}").Build());

// Component interaction with ignoreGroupNames set to true
[ComponentInteraction("echoButton_*", true)]
public async Task EchoButton(string input)
=> await RespondAsync(input);
}
}

+ 9
- 2
docs/guides/int_framework/samples/intro/modal.cs View File

@@ -12,7 +12,9 @@ public class FoodModal : IModal
[ModalTextInput("food_name", placeholder: "Pizza", maxLength: 20)]
public string Food { get; set; }

// Additional paremeters can be specified to further customize the input.
// Additional paremeters can be specified to further customize the input.
// Parameters can be optional
[RequiredInput(false)]
[InputLabel("Why??")]
[ModalTextInput("food_reason", TextInputStyle.Paragraph, "Kuz it's tasty", maxLength: 500)]
public string Reason { get; set; }
@@ -22,10 +24,15 @@ public class FoodModal : IModal
[ModalInteraction("food_menu")]
public async Task ModalResponse(FoodModal modal)
{
// Check if "Why??" field is populated
string reason = string.IsNullOrWhiteSpace(modal.Reason)
? "."
: $" because {modal.Reason}";

// Build the message to send.
string message = "hey @everyone, I just learned " +
$"{Context.User.Mention}'s favorite food is " +
$"{modal.Food} because {modal.Reason}.";
$"{modal.Food}{reason}";

// Specify the AllowedMentions so we don't actually ping everyone.
AllowedMentions mentions = new();


+ 2
- 4
docs/guides/voice/sending-voice.md View File

@@ -17,11 +17,9 @@ bot. (When developing on .NET Framework, this would be `bin/debug`,
when developing on .NET Core, this is where you execute `dotnet run`
from; typically the same directory as your csproj).

For Windows Users, precompiled binaries are available for your
convienence [here](https://github.com/discord-net/Discord.Net/tree/dev/voice-natives).
**For Windows users, precompiled binaries are available for your convienence [here](https://github.com/discord-net/Discord.Net/tree/dev/voice-natives).**

For Linux Users, you will need to compile [Sodium] and [Opus] from
source, or install them from your package manager.
**For Linux users, you will need to compile [Sodium] and [Opus] from source, or install them from your package manager.**

[Sodium]: https://download.libsodium.org/libsodium/releases/
[Opus]: http://downloads.xiph.org/releases/opus/


+ 1
- 1
experiment/Discord.Net.BuildOverrides/BuildOverrides.cs View File

@@ -251,7 +251,7 @@ namespace Discord
private static Assembly _overrideDomain_Resolving(AssemblyLoadContext arg1, AssemblyName arg2)
{
// resolve the override id
var v = _loadedOverrides.FirstOrDefault(x => x.Value.Any(x => x.Assembly.FullName == arg1.Assemblies.FirstOrDefault().FullName));
var v = _loadedOverrides.FirstOrDefault(x => x.Value.Any(x => x.Assembly.FullName == arg1.Assemblies.First().FullName));

return GetDependencyAsync(v.Key.Id, $"{arg2}").GetAwaiter().GetResult();
}


+ 10
- 5
samples/ShardedClient/Services/InteractionHandlingService.cs View File

@@ -22,6 +22,7 @@ namespace ShardedClient.Services

_service.Log += LogAsync;
_client.InteractionCreated += OnInteractionAsync;
_client.ShardReady += ReadyAsync;
// For examples on how to handle post execution,
// see the InteractionFramework samples.
}
@@ -30,11 +31,6 @@ namespace ShardedClient.Services
public async Task InitializeAsync()
{
await _service.AddModulesAsync(typeof(InteractionHandlingService).Assembly, _provider);
#if DEBUG
await _service.RegisterCommandsToGuildAsync(1 /* implement */);
#else
await _service.RegisterCommandsGloballyAsync();
#endif
}

private async Task OnInteractionAsync(SocketInteraction interaction)
@@ -53,5 +49,14 @@ namespace ShardedClient.Services

return Task.CompletedTask;
}

private async Task ReadyAsync(DiscordSocketClient _)
{
#if DEBUG
await _service.RegisterCommandsToGuildAsync(1 /* implement */);
#else
await _service.RegisterCommandsGloballyAsync();
#endif
}
}
}

+ 2
- 0
src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs View File

@@ -206,6 +206,7 @@ namespace Discord.Commands

try
{
await instance.BeforeExecuteAsync(cmd).ConfigureAwait(false);
instance.BeforeExecute(cmd);

var task = method.Invoke(instance, args) as Task ?? Task.Delay(0);
@@ -221,6 +222,7 @@ namespace Discord.Commands
}
finally
{
await instance.AfterExecuteAsync(cmd).ConfigureAwait(false);
instance.AfterExecute(cmd);
(instance as IDisposable)?.Dispose();
}


+ 13
- 0
src/Discord.Net.Commands/IModuleBase.cs View File

@@ -1,4 +1,5 @@
using Discord.Commands.Builders;
using System.Threading.Tasks;

namespace Discord.Commands
{
@@ -13,12 +14,24 @@ namespace Discord.Commands
/// <param name="context">The context to set.</param>
void SetContext(ICommandContext context);

/// <summary>
/// Executed asynchronously before a command is run in this module base.
/// </summary>
/// <param name="command">The command thats about to run.</param>
Task BeforeExecuteAsync(CommandInfo command);

/// <summary>
/// Executed before a command is run in this module base.
/// </summary>
/// <param name="command">The command thats about to run.</param>
void BeforeExecute(CommandInfo command);

/// <summary>
/// Executed asynchronously after a command is run in this module base.
/// </summary>
/// <param name="command">The command thats about to run.</param>
Task AfterExecuteAsync(CommandInfo command);

/// <summary>
/// Executed after a command is ran in this module base.
/// </summary>


+ 12
- 0
src/Discord.Net.Commands/ModuleBase.cs View File

@@ -46,6 +46,11 @@ namespace Discord.Commands
return await Context.Channel.SendMessageAsync(message, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false);
}
/// <summary>
/// The method to execute asynchronously before executing the command.
/// </summary>
/// <param name="command">The <see cref="CommandInfo"/> of the command to be executed.</param>
protected virtual Task BeforeExecuteAsync(CommandInfo command) => Task.CompletedTask;
/// <summary>
/// The method to execute before executing the command.
/// </summary>
/// <param name="command">The <see cref="CommandInfo"/> of the command to be executed.</param>
@@ -53,6 +58,11 @@ namespace Discord.Commands
{
}
/// <summary>
/// The method to execute asynchronously after executing the command.
/// </summary>
/// <param name="command">The <see cref="CommandInfo"/> of the command to be executed.</param>
protected virtual Task AfterExecuteAsync(CommandInfo command) => Task.CompletedTask;
/// <summary>
/// The method to execute after executing the command.
/// </summary>
/// <param name="command">The <see cref="CommandInfo"/> of the command to be executed.</param>
@@ -76,7 +86,9 @@ namespace Discord.Commands
var newValue = context as T;
Context = newValue ?? throw new InvalidOperationException($"Invalid context type. Expected {typeof(T).Name}, got {context.GetType().Name}.");
}
Task IModuleBase.BeforeExecuteAsync(CommandInfo command) => BeforeExecuteAsync(command);
void IModuleBase.BeforeExecute(CommandInfo command) => BeforeExecute(command);
Task IModuleBase.AfterExecuteAsync(CommandInfo command) => AfterExecuteAsync(command);
void IModuleBase.AfterExecute(CommandInfo command) => AfterExecute(command);
void IModuleBase.OnModuleBuilding(CommandService commandService, ModuleBuilder builder) => OnModuleBuilding(commandService, builder);
#endregion


+ 43
- 43
src/Discord.Net.Core/Entities/Guilds/GuildFeature.cs View File

@@ -12,174 +12,174 @@ namespace Discord
/// <summary>
/// The guild has no features.
/// </summary>
None = 0,
None = 0L,
/// <summary>
/// The guild has access to animated banners.
/// </summary>
AnimatedBanner = 1 << 0,
AnimatedBanner = 1L << 0,
/// <summary>
/// The guild has access to set an animated guild icon.
/// </summary>
AnimatedIcon = 1 << 1,
AnimatedIcon = 1L << 1,
/// <summary>
/// The guild has access to set a guild banner image.
/// </summary>
Banner = 1 << 2,
Banner = 1L << 2,
/// <summary>
/// The guild has access to channel banners.
/// </summary>
ChannelBanner = 1 << 3,
ChannelBanner = 1L << 3,
/// <summary>
/// The guild has access to use commerce features (i.e. create store channels).
/// </summary>
Commerce = 1 << 4,
Commerce = 1L << 4,
/// <summary>
/// The guild can enable welcome screen, Membership Screening, stage channels and discovery, and receives community updates.
/// </summary>
Community = 1 << 5,
Community = 1L << 5,
/// <summary>
/// The guild is able to be discovered in the directory.
/// </summary>
Discoverable = 1 << 6,
Discoverable = 1L << 6,
/// <summary>
/// The guild has discoverable disabled.
/// </summary>
DiscoverableDisabled = 1 << 7,
DiscoverableDisabled = 1L << 7,
/// <summary>
/// The guild has enabled discoverable before.
/// </summary>
EnabledDiscoverableBefore = 1 << 8,
EnabledDiscoverableBefore = 1L << 8,
/// <summary>
/// The guild is able to be featured in the directory.
/// </summary>
Featureable = 1 << 9,
Featureable = 1L << 9,
/// <summary>
/// The guild has a force relay.
/// </summary>
ForceRelay = 1 << 10,
ForceRelay = 1L << 10,
/// <summary>
/// The guild has a directory entry.
/// </summary>
HasDirectoryEntry = 1 << 11,
HasDirectoryEntry = 1L << 11,
/// <summary>
/// The guild is a hub.
/// </summary>
Hub = 1 << 12,
Hub = 1L << 12,
/// <summary>
/// You shouldn't be here...
/// </summary>
InternalEmployeeOnly = 1 << 13,
InternalEmployeeOnly = 1L << 13,
/// <summary>
/// The guild has access to set an invite splash background.
/// </summary>
InviteSplash = 1 << 14,
InviteSplash = 1L << 14,
/// <summary>
/// The guild is linked to a hub.
/// </summary>
LinkedToHub = 1 << 15,
LinkedToHub = 1L << 15,
/// <summary>
/// The guild has member profiles.
/// </summary>
MemberProfiles = 1 << 16,
MemberProfiles = 1L << 16,
/// <summary>
/// The guild has enabled <seealso href="https://discord.com/developers/docs/resources/guild#membership-screening-object">Membership Screening</seealso>.
/// </summary>
MemberVerificationGateEnabled = 1 << 17,
MemberVerificationGateEnabled = 1L << 17,
/// <summary>
/// The guild has enabled monetization.
/// </summary>
MonetizationEnabled = 1 << 18,
MonetizationEnabled = 1L << 18,
/// <summary>
/// The guild has more emojis.
/// </summary>
MoreEmoji = 1 << 19,
MoreEmoji = 1L << 19,
/// <summary>
/// The guild has increased custom sticker slots.
/// </summary>
MoreStickers = 1 << 20,
MoreStickers = 1L << 20,
/// <summary>
/// The guild has access to create news channels.
/// </summary>
News = 1 << 21,
News = 1L << 21,
/// <summary>
/// The guild has new thread permissions.
/// </summary>
NewThreadPermissions = 1 << 22,
NewThreadPermissions = 1L << 22,
/// <summary>
/// The guild is partnered.
/// </summary>
Partnered = 1 << 23,
Partnered = 1L << 23,
/// <summary>
/// The guild has a premium tier three override; guilds made by Discord usually have this.
/// </summary>
PremiumTier3Override = 1 << 24,
PremiumTier3Override = 1L << 24,
/// <summary>
/// The guild can be previewed before joining via Membership Screening or the directory.
/// </summary>
PreviewEnabled = 1 << 25,
PreviewEnabled = 1L << 25,
/// <summary>
/// The guild has access to create private threads.
/// </summary>
PrivateThreads = 1 << 26,
PrivateThreads = 1L << 26,
/// <summary>
/// The guild has relay enabled.
/// </summary>
RelayEnabled = 1 << 27,
RelayEnabled = 1L << 27,
/// <summary>
/// The guild is able to set role icons.
/// </summary>
RoleIcons = 1 << 28,
RoleIcons = 1L << 28,
/// <summary>
/// The guild has role subscriptions available for purchase.
/// </summary>
RoleSubscriptionsAvailableForPurchase = 1 << 29,
RoleSubscriptionsAvailableForPurchase = 1L << 29,
/// <summary>
/// The guild has role subscriptions enabled.
/// </summary>
RoleSubscriptionsEnabled = 1 << 30,
RoleSubscriptionsEnabled = 1L << 30,
/// <summary>
/// The guild has access to the seven day archive time for threads.
/// </summary>
SevenDayThreadArchive = 1 << 31,
SevenDayThreadArchive = 1L << 31,
/// <summary>
/// The guild has text in voice enabled.
/// </summary>
TextInVoiceEnabled = 1 << 32,
TextInVoiceEnabled = 1L << 32,
/// <summary>
/// The guild has threads enabled.
/// </summary>
ThreadsEnabled = 1 << 33,
ThreadsEnabled = 1L << 33,
/// <summary>
/// The guild has testing threads enabled.
/// </summary>
ThreadsEnabledTesting = 1 << 34,
ThreadsEnabledTesting = 1L << 34,
/// <summary>
/// The guild has the default thread auto archive.
/// </summary>
ThreadsDefaultAutoArchiveDuration = 1 << 35,
ThreadsDefaultAutoArchiveDuration = 1L << 35,
/// <summary>
/// The guild has access to the three day archive time for threads.
/// </summary>
ThreeDayThreadArchive = 1 << 36,
ThreeDayThreadArchive = 1L << 36,
/// <summary>
/// The guild has enabled ticketed events.
/// </summary>
TicketedEventsEnabled = 1 << 37,
TicketedEventsEnabled = 1L << 37,
/// <summary>
/// The guild has access to set a vanity URL.
/// </summary>
VanityUrl = 1 << 38,
VanityUrl = 1L << 38,
/// <summary>
/// The guild is verified.
/// </summary>
Verified = 1 << 39,
Verified = 1L << 39,
/// <summary>
/// The guild has access to set 384kbps bitrate in voice (previously VIP voice servers).
/// </summary>
VIPRegions = 1 << 40,
VIPRegions = 1L << 40,
/// <summary>
/// The guild has enabled the welcome screen.
/// </summary>
WelcomeScreenEnabled = 1 << 41,
WelcomeScreenEnabled = 1L << 41,
}
}

+ 3
- 2
src/Discord.Net.Interactions/TypeConverters/SlashCommands/NullableConverter.cs View File

@@ -9,10 +9,11 @@ namespace Discord.Interactions

public NullableConverter(InteractionService interactionService, IServiceProvider services)
{
var type = Nullable.GetUnderlyingType(typeof(T));
var nullableType = typeof(T);
var type = Nullable.GetUnderlyingType(nullableType);

if (type is null)
throw new ArgumentException($"No type {nameof(TypeConverter)} is defined for this {type.FullName}", "type");
throw new ArgumentException($"No type {nameof(TypeConverter)} is defined for this {nullableType.FullName}", nameof(type));

_typeConverter = interactionService.GetTypeConverter(type, services);
}


+ 1
- 1
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -1756,7 +1756,7 @@ namespace Discord.API
if (args.TargetType.Value == TargetUserType.Stream)
Preconditions.GreaterThan(args.TargetUserId, 0, nameof(args.TargetUserId));
if (args.TargetType.Value == TargetUserType.EmbeddedApplication)
Preconditions.GreaterThan(args.TargetApplicationId, 0, nameof(args.TargetUserId));
Preconditions.GreaterThan(args.TargetApplicationId, 0, nameof(args.TargetApplicationId));
}
options = RequestOptions.CreateOrClone(options);



+ 1
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ThreadUpdateAuditLogData.cs View File

@@ -15,7 +15,7 @@ namespace Discord.Rest
Thread = thread;
ThreadType = type;
Before = before;
After = After;
After = after;
}

internal static ThreadUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)


+ 1
- 1
src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs View File

@@ -428,7 +428,7 @@ namespace Discord.Rest
var ids = args.Roles.Value.Select(r => r.Id);

if (args.RoleIds.IsSpecified)
args.RoleIds.Value.Concat(ids);
args.RoleIds = Optional.Create(args.RoleIds.Value.Concat(ids));
else
args.RoleIds = Optional.Create(ids);
}


+ 1
- 1
src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs View File

@@ -426,7 +426,7 @@ namespace Discord.Rest
AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
=> await FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
/// <inheritdoc/>
async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(string filePath, string text, string fileName, Embed[] embeds, bool isTTS, bool ephemeral,
async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(string filePath, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral,
AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options)
=> await FollowupWithFileAsync(filePath, text, fileName, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false);
/// <inheritdoc/>


+ 13
- 0
src/Discord.Net.WebSocket/API/Gateway/WebhooksUpdatedEvent.cs View File

@@ -0,0 +1,13 @@
using Newtonsoft.Json;

namespace Discord.API.Gateway
{
internal class WebhooksUpdatedEvent
{
[JsonProperty("guild_id")]
public ulong GuildId { get; set; }

[JsonProperty("channel_id")]
public ulong ChannelId { get; set; }
}
}

+ 29
- 14
src/Discord.Net.WebSocket/BaseSocketClient.Events.cs View File

@@ -55,7 +55,7 @@ namespace Discord.WebSocket
/// <summary> Fired when a channel is updated. </summary>
/// <remarks>
/// <para>
/// This event is fired when a generic channel has been destroyed. The event handler must return a
/// This event is fired when a generic channel has been updated. The event handler must return a
/// <see cref="Task"/> and accept 2 <see cref="SocketChannel"/> as its parameters.
/// </para>
/// <para>
@@ -106,7 +106,7 @@ namespace Discord.WebSocket
/// <remarks>
/// <para>
/// This event is fired when a message is deleted. The event handler must return a
/// <see cref="Task"/> and accept a <see cref="Cacheable{TEntity,TId}"/> and
/// <see cref="Task"/> and accept a <see cref="Cacheable{TEntity,TId}"/> and
/// <see cref="ISocketMessageChannel"/> as its parameters.
/// </para>
/// <para>
@@ -117,11 +117,11 @@ namespace Discord.WebSocket
/// </note>
/// If caching is enabled via <see cref="DiscordSocketConfig"/>, the
/// <see cref="Cacheable{TEntity,TId}"/> entity will contain the deleted message; otherwise, in event
/// that the message cannot be retrieved, the snowflake ID of the message is preserved in the
/// that the message cannot be retrieved, the snowflake ID of the message is preserved in the
/// <see cref="ulong"/>.
/// </para>
/// <para>
/// The source channel of the removed message will be passed into the
/// The source channel of the removed message will be passed into the
/// <see cref="ISocketMessageChannel"/> parameter.
/// </para>
/// </remarks>
@@ -143,7 +143,7 @@ namespace Discord.WebSocket
/// </note>
/// <para>
/// This event is fired when multiple messages are bulk deleted. The event handler must return a
/// <see cref="Task"/> and accept an <see cref="IReadOnlyCollection{Cacheable}"/> and
/// <see cref="Task"/> and accept an <see cref="IReadOnlyCollection{Cacheable}"/> and
/// <see cref="ISocketMessageChannel"/> as its parameters.
/// </para>
/// <para>
@@ -154,11 +154,11 @@ namespace Discord.WebSocket
/// </note>
/// If caching is enabled via <see cref="DiscordSocketConfig"/>, the
/// <see cref="Cacheable{TEntity,TId}"/> entity will contain the deleted message; otherwise, in event
/// that the message cannot be retrieved, the snowflake ID of the message is preserved in the
/// that the message cannot be retrieved, the snowflake ID of the message is preserved in the
/// <see cref="ulong"/>.
/// </para>
/// <para>
/// The source channel of the removed message will be passed into the
/// The source channel of the removed message will be passed into the
/// <see cref="ISocketMessageChannel"/> parameter.
/// </para>
/// </remarks>
@@ -178,14 +178,14 @@ namespace Discord.WebSocket
/// <para>
/// If caching is enabled via <see cref="DiscordSocketConfig"/>, the
/// <see cref="Cacheable{TEntity,TId}"/> entity will contain the original message; otherwise, in event
/// that the message cannot be retrieved, the snowflake ID of the message is preserved in the
/// that the message cannot be retrieved, the snowflake ID of the message is preserved in the
/// <see cref="ulong"/>.
/// </para>
/// <para>
/// The updated message will be passed into the <see cref="SocketMessage"/> parameter.
/// </para>
/// <para>
/// The source channel of the updated message will be passed into the
/// The source channel of the updated message will be passed into the
/// <see cref="ISocketMessageChannel"/> parameter.
/// </para>
/// </remarks>
@@ -199,24 +199,24 @@ namespace Discord.WebSocket
/// <remarks>
/// <para>
/// This event is fired when a reaction is added to a user message. The event handler must return a
/// <see cref="Task"/> and accept a <see cref="Cacheable{TEntity,TId}"/>, an
/// <see cref="Task"/> and accept a <see cref="Cacheable{TEntity,TId}"/>, an
/// <see cref="ISocketMessageChannel"/>, and a <see cref="SocketReaction"/> as its parameter.
/// </para>
/// <para>
/// If caching is enabled via <see cref="DiscordSocketConfig"/>, the
/// <see cref="Cacheable{TEntity,TId}"/> entity will contain the original message; otherwise, in event
/// that the message cannot be retrieved, the snowflake ID of the message is preserved in the
/// that the message cannot be retrieved, the snowflake ID of the message is preserved in the
/// <see cref="ulong"/>.
/// </para>
/// <para>
/// The source channel of the reaction addition will be passed into the
/// The source channel of the reaction addition will be passed into the
/// <see cref="ISocketMessageChannel"/> parameter.
/// </para>
/// <para>
/// The reaction that was added will be passed into the <see cref="SocketReaction"/> parameter.
/// </para>
/// <note>
/// When fetching the reaction from this event, a user may not be provided under
/// When fetching the reaction from this event, a user may not be provided under
/// <see cref="SocketReaction.User"/>. Please see the documentation of the property for more
/// information.
/// </note>
@@ -367,7 +367,7 @@ namespace Discord.WebSocket
}
internal readonly AsyncEvent<Func<Cacheable<SocketGuildEvent, ulong>, SocketGuildEvent, Task>> _guildScheduledEventUpdated = new AsyncEvent<Func<Cacheable<SocketGuildEvent, ulong>, SocketGuildEvent, Task>>();

/// <summary>
/// Fired when a guild event is cancelled.
/// </summary>
@@ -877,5 +877,20 @@ namespace Discord.WebSocket
}
internal readonly AsyncEvent<Func<SocketCustomSticker, Task>> _guildStickerDeleted = new AsyncEvent<Func<SocketCustomSticker, Task>>();
#endregion

#region Webhooks

/// <summary>
/// Fired when a webhook is modified, moved, or deleted. If the webhook was
/// moved the channel represents the destination channel, not the source.
/// </summary>
public event Func<SocketGuild, SocketChannel, Task> WebhooksUpdated
{
add { _webhooksUpdated.Add(value); }
remove { _webhooksUpdated.Remove(value); }
}
internal readonly AsyncEvent<Func<SocketGuild, SocketChannel, Task>> _webhooksUpdated = new AsyncEvent<Func<SocketGuild, SocketChannel, Task>>();

#endregion
}
}

+ 2
- 0
src/Discord.Net.WebSocket/DiscordShardedClient.cs View File

@@ -496,6 +496,8 @@ namespace Discord.WebSocket
client.GuildScheduledEventStarted += (arg) => _guildScheduledEventStarted.InvokeAsync(arg);
client.GuildScheduledEventUserAdd += (arg1, arg2) => _guildScheduledEventUserAdd.InvokeAsync(arg1, arg2);
client.GuildScheduledEventUserRemove += (arg1, arg2) => _guildScheduledEventUserRemove.InvokeAsync(arg1, arg2);

client.WebhooksUpdated += (arg1, arg2) => _webhooksUpdated.InvokeAsync(arg1, arg2);
}
#endregion



+ 17
- 3
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -2839,6 +2839,23 @@ namespace Discord.WebSocket

#endregion

#region Webhooks

case "WEBHOOKS_UPDATE":
{
var data = (payload as JToken).ToObject<WebhooksUpdatedEvent>(_serializer);
type = "WEBHOOKS_UPDATE";
await _gatewayLogger.DebugAsync("Received Dispatch (WEBHOOKS_UPDATE)").ConfigureAwait(false);

var guild = State.GetGuild(data.GuildId);
var channel = State.GetChannel(data.ChannelId);

await TimedInvokeAsync(_webhooksUpdated, nameof(WebhooksUpdated), guild, channel);
}
break;

#endregion

#region Ignored (User only)
case "CHANNEL_PINS_ACK":
await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_ACK)").ConfigureAwait(false);
@@ -2858,9 +2875,6 @@ namespace Discord.WebSocket
case "USER_SETTINGS_UPDATE":
await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false);
break;
case "WEBHOOKS_UPDATE":
await _gatewayLogger.DebugAsync("Ignored Dispatch (WEBHOOKS_UPDATE)").ConfigureAwait(false);
break;
#endregion

#region Others


+ 3
- 6
src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs View File

@@ -532,13 +532,10 @@ namespace Discord.WebSocket
Features = model.Features;

var roles = new ConcurrentDictionary<ulong, SocketRole>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.Roles.Length * 1.05));
if (model.Roles != null)
for (int i = 0; i < model.Roles.Length; i++)
{
for (int i = 0; i < model.Roles.Length; i++)
{
var role = SocketRole.Create(this, state, model.Roles[i]);
roles.TryAdd(role.Id, role);
}
var role = SocketRole.Create(this, state, model.Roles[i]);
roles.TryAdd(role.Id, role);
}
_roles = roles;



+ 2
- 2
src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketResolvableData.cs View File

@@ -39,7 +39,7 @@ namespace Discord.WebSocket
{
foreach (var channel in resolved.Channels.Value)
{
SocketChannel socketChannel = guild != null
var socketChannel = guild != null
? guild.GetChannel(channel.Value.Id)
: discord.GetChannel(channel.Value.Id);

@@ -69,7 +69,7 @@ namespace Discord.WebSocket
}
}

if (resolved.Roles.IsSpecified)
if (resolved.Roles.IsSpecified && guild != null)
{
foreach (var role in resolved.Roles.Value)
{


Loading…
Cancel
Save