diff --git a/docs/guides/bearer_token/bearer_token_guide.md b/docs/guides/bearer_token/bearer_token_guide.md new file mode 100644 index 000000000..edaf61374 --- /dev/null +++ b/docs/guides/bearer_token/bearer_token_guide.md @@ -0,0 +1,68 @@ +--- +uid: Guides.BearerToken +title: Working with Bearer token +--- + +# Working with Bearer token + +Some endpoints in Discord API require a Bearer token, which can be obtained through [OAuth2 flow](https://discord.com/developers/docs/topics/oauth2). Discord.Net allows you to interact with these endpoints using the [DiscordRestClient]. + +## Initializing a new instance of the client +[!code-csharp[Initialize DiscordRestClient](samples/rest_client_init.cs)] + +## Getting current user + +The [DiscordRestClient] gets the current user when `LoginAsync()` is called. The user object can be found in the `CurrentUser` property. + +If you need to fetch the user again, the `GetGetCurrentUserAsync()` method can be used. + +[!code-csharp[Get current user](samples/current_user.cs)] + +> [!NOTE] +> Some properties might be `null` depending on which scopes users authorized your app with. +> For example: `email` scope is required to fetch current user's email address. + +## Fetching current user's guilds + +The `GetGuildSummariesAsync()` method is used to fetch current user's guilds. Since it returns an `IAsyncEnumerable` you need to call `FlattenAsync()` to get a plain `IEnumerable` containing [RestUserGuild] objects. + +[!code-csharp[Get current user's guilds](samples/current_user_guilds.cs)] + +> [!WARNING] +> This method requires `guilds` scope + +## Fetching current user's guild member object + +To fetch the current user's guild member object, the `GetCurrentUserGuildMemberAsync()` method can be used. + +[!code-csharp[Get current user's guild member](samples/current_user_guild_member.cs)] + +> [!WARNING] +> This method requires `guilds.members.read` scope + +## Get user connections + +The `GetConnectionsAsync` method can be used to fetch current user's connections to other platforms. + +[!code-csharp[Get current user's connections](samples/current_user_connections.cs)] + +> [!WARNING] +> This method requires `connections` scope + +## Application role connection + +In addition to previous features, Discord.Net supports fetching & updating user's application role connection metadata values. `GetUserApplicationRoleConnectionAsync()` returns a [RoleConnection] object of the current user for the given application id. + +The `ModifyUserApplicationRoleConnectionAsync()` method is used to update current user's role connection metadata values. A new set of values can be created with [RoleConnectionProperties] object. + +[!code-csharp[Get current user's connections](samples/app_role_connection.cs)] + +> [!WARNING] +> This method requires `role_connections.write` scope + + + +[DiscordRestClient]: xref:Discord.Rest.DiscordRestClient +[RestUserGuild]: xref:Discord.Rest.RestUserGuild +[RoleConnection]: xref:Discord.RoleConnection +[RoleConnectionProperties]: xref:Discord.RoleConnectionProperties diff --git a/docs/guides/bearer_token/samples/app_role_connection.cs b/docs/guides/bearer_token/samples/app_role_connection.cs new file mode 100644 index 000000000..cff57a894 --- /dev/null +++ b/docs/guides/bearer_token/samples/app_role_connection.cs @@ -0,0 +1,11 @@ +// fetch application role connection of the current user for the app with provided id. +var roleConnection = await client.GetUserApplicationRoleConnectionAsync(applicationid); + +// create a new role connection metadata properties object & set some values. +var properties = new RoleConnectionProperties("Discord.Net Docs", "Cool Coding Guy") + .WithNumber("eaten_cookies", 69) + .WithBool("loves_cookies", true) + .WithDate("last_eaten_cookie", DateTimeOffset.UtcNow); + +// update current user's values with the given properties. +await client.ModifyUserApplicationRoleConnectionAsync(applicationId, properties); diff --git a/docs/guides/bearer_token/samples/current_user.cs b/docs/guides/bearer_token/samples/current_user.cs new file mode 100644 index 000000000..1b7337d71 --- /dev/null +++ b/docs/guides/bearer_token/samples/current_user.cs @@ -0,0 +1,5 @@ +// gets the user object stored in the DiscordRestClient. +var user = client.CurrentUser; + +// fetches the current user with a REST call & updates the CurrentUser property. +var refreshedUser = await client.GetCurrentUserAsync(); \ No newline at end of file diff --git a/docs/guides/bearer_token/samples/current_user_connections.cs b/docs/guides/bearer_token/samples/current_user_connections.cs new file mode 100644 index 000000000..be339753d --- /dev/null +++ b/docs/guides/bearer_token/samples/current_user_connections.cs @@ -0,0 +1,2 @@ +// fetches the current user's connections. +var connections = await client.GetConnectionsAsync(); \ No newline at end of file diff --git a/docs/guides/bearer_token/samples/current_user_guild_member.cs b/docs/guides/bearer_token/samples/current_user_guild_member.cs new file mode 100644 index 000000000..bfbe3632f --- /dev/null +++ b/docs/guides/bearer_token/samples/current_user_guild_member.cs @@ -0,0 +1,6 @@ +// fetches the current user's guild member object in a guild with provided id. +var member = await client.GetCurrentUserGuildMemberAsync(guildId); + +// fetches the current user's guild member object in a RestUserGuild. +var guild = await client.GetGuildSummariesAsync().FlattenAsync().First(); +var member = await guild.GetCurrentUserGuildMemberAsync(); \ No newline at end of file diff --git a/docs/guides/bearer_token/samples/current_user_guilds.cs b/docs/guides/bearer_token/samples/current_user_guilds.cs new file mode 100644 index 000000000..f98e36096 --- /dev/null +++ b/docs/guides/bearer_token/samples/current_user_guilds.cs @@ -0,0 +1,2 @@ +// fetches the guilds the current user participate in. +var guilds = await client.GetGuildSummariesAsync().FlattenAsync(); \ No newline at end of file diff --git a/docs/guides/bearer_token/samples/rest_client_init.cs b/docs/guides/bearer_token/samples/rest_client_init.cs new file mode 100644 index 000000000..e810a4fb2 --- /dev/null +++ b/docs/guides/bearer_token/samples/rest_client_init.cs @@ -0,0 +1,5 @@ +using Discord; +using Discord.Rest; + +await using var client = new DiscordRestClient(); +await client.LoginAsync(TokenType.Bearer, "bearer token obtained through oauth2 flow"); \ No newline at end of file diff --git a/docs/guides/toc.yml b/docs/guides/toc.yml index c892eb8c4..f2cefda8d 100644 --- a/docs/guides/toc.yml +++ b/docs/guides/toc.yml @@ -126,6 +126,8 @@ topicUid: Guides.OtherLibs.MediatR - name: Emoji topicUid: Guides.Emoji +- name: Bearer Tokens + topicUid: Guides.BearerToken - name: Voice topicUid: Guides.Voice.SendingVoice - name: Deployment diff --git a/src/Discord.Net.Core/Entities/Guilds/IUserGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IUserGuild.cs index b6685edf6..741dc335c 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IUserGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IUserGuild.cs @@ -18,5 +18,13 @@ namespace Discord /// Returns the current user's permissions for this guild. /// GuildPermissions Permissions { get; } + + /// + /// Gets the features for this guild. + /// + /// + /// A flags enum containing all the features for the guild. + /// + GuildFeatures Features { get; } } } diff --git a/src/Discord.Net.Rest/API/Common/UserGuild.cs b/src/Discord.Net.Rest/API/Common/UserGuild.cs index fc1fe833d..a501604a0 100644 --- a/src/Discord.Net.Rest/API/Common/UserGuild.cs +++ b/src/Discord.Net.Rest/API/Common/UserGuild.cs @@ -14,5 +14,7 @@ namespace Discord.API public bool Owner { get; set; } [JsonProperty("permissions"), Int53] public string Permissions { get; set; } + [JsonProperty("features")] + public GuildFeatures Features { get; set; } } } diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 7d015d912..c759119c2 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -1,4 +1,3 @@ - using Discord.API.Rest; using Discord.Net; using Discord.Net.Converters; @@ -2204,6 +2203,14 @@ namespace Discord.API return await SendJsonAsync("POST", () => "users/@me/channels", args, new BucketIds(), options: options).ConfigureAwait(false); } + + public async Task GetCurrentUserGuildMember(ulong guildId, RequestOptions options = null) + { + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(); + return await SendAsync("GET", () => $"users/@me/guilds/{guildId}/member", ids, options: options).ConfigureAwait(false); + } #endregion #region Voice Regions diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index 0778e69ad..dbb2b9918 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -152,6 +152,19 @@ namespace Discord.Rest #endregion + public async Task GetCurrentUserAsync(RequestOptions options = null) + { + var user = await ApiClient.GetMyUserAsync(options); + CurrentUser.Update(user); + return CurrentUser; + } + + public async Task GetCurrentUserGuildMemberAsync(ulong guildId, RequestOptions options = null) + { + var user = await ApiClient.GetCurrentUserGuildMember(guildId, options); + return RestGuildUser.Create(this, null, user, guildId); + } + public async Task GetApplicationInfoAsync(RequestOptions options = null) { return _applicationInfo ??= await ClientHelper.GetApplicationInfoAsync(this, options).ConfigureAwait(false); diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs index b75d6288e..6131c6520 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs @@ -21,6 +21,8 @@ namespace Discord.Rest public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); /// public string IconUrl => CDN.GetGuildIconUrl(Id, _iconId); + /// + public GuildFeatures Features { get; private set; } internal RestUserGuild(BaseDiscordClient discord, ulong id) : base(discord, id) @@ -39,12 +41,20 @@ namespace Discord.Rest IsOwner = model.Owner; Name = model.Name; Permissions = new GuildPermissions(model.Permissions); + Features = model.Features; } public async Task LeaveAsync(RequestOptions options = null) { await Discord.ApiClient.LeaveGuildAsync(Id, options).ConfigureAwait(false); } + + public async Task GetCurrentUserGuildMemberAsync(RequestOptions options = null) + { + var user = await Discord.ApiClient.GetCurrentUserGuildMember(Id, options); + return RestGuildUser.Create(Discord, null, user, Id); + } + /// public async Task DeleteAsync(RequestOptions options = null) {