Compare commits
9 Commits
edc537366c
...
1caa36f6d4
Author | SHA1 | Date |
---|---|---|
kozabrada123 | 1caa36f6d4 | |
kozabrada123 | 2f2407467e | |
kozabrada123 | ade43f2a97 | |
kozabrada123 | fa859f616a | |
kozabrada123 | 28e3f21cbf | |
kozabrada123 | e0ae1a9ec6 | |
kozabrada123 | f63c6a7c75 | |
kozabrada123 | 7e2e85988b | |
kozabrada123 | 2f4198c0a4 |
|
@ -2,13 +2,14 @@ use futures_util::FutureExt;
|
|||
use reqwest::Client;
|
||||
|
||||
use crate::{
|
||||
errors::ChorusResult,
|
||||
errors::{ChorusError, ChorusResult},
|
||||
instance::ChorusUser,
|
||||
ratelimiter::ChorusRequest,
|
||||
types::{
|
||||
AuthorizeConnectionReturn, AuthorizeConnectionSchema, Connection, ConnectionSubreddit,
|
||||
ConnectionType, CreateConnectionCallbackSchema, CreateContactSyncConnectionSchema,
|
||||
GetConnectionAccessTokenReturn, LimitType, ModifyConnectionSchema,
|
||||
CreateDomainConnectionError, CreateDomainConnectionReturn, GetConnectionAccessTokenReturn,
|
||||
LimitType, ModifyConnectionSchema,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -121,6 +122,92 @@ impl ChorusUser {
|
|||
chorus_request.deserialize_response(self).await
|
||||
}
|
||||
|
||||
/// Creates a new domain connection for the current user.
|
||||
///
|
||||
/// This route has two possible successful return values:
|
||||
/// [CreateDomainConnectionReturn::Ok] and [CreateDomainConnectionReturn::ProofNeeded]
|
||||
///
|
||||
/// To properly handle both, please see their respective documentation pages.
|
||||
///
|
||||
/// # Notes
|
||||
/// To create normal connection types, see [Self::authorize_connection] and
|
||||
/// [Self::create_connection_callback]
|
||||
///
|
||||
/// # Examples
|
||||
/// ```no_run
|
||||
/// let domain = "example.com".to_string();
|
||||
///
|
||||
/// let result = user.create_domain_connection(&domain).await;
|
||||
///
|
||||
/// if let Ok(returned) = result {
|
||||
/// match returned {
|
||||
/// CreateDomainConnectionReturn::ProofNeeded(proof) => {
|
||||
/// println!("Additional proof needed!");
|
||||
/// println!("Either:");
|
||||
/// println!("");
|
||||
/// println!("- create a DNS TXT record with the name _discord.{domain} and content {proof}");
|
||||
/// println!("or");
|
||||
/// println!("- create a file at https://{domain}/.well-known/discord with the content {proof}");
|
||||
/// // Once the user has added the proof, retry calling the endpoint
|
||||
/// }
|
||||
/// CreateDomainConnectionReturn::Ok(connection) => {
|
||||
/// println!("Successfulyl created connection! {:?}", connection);
|
||||
/// }
|
||||
/// }
|
||||
/// } else {
|
||||
/// println!("Failed to create connection: {:?}", result);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Reference
|
||||
/// See <https://docs.discord.sex/resources/user#create-domain-connection>
|
||||
pub async fn create_domain_connection(
|
||||
&mut self,
|
||||
domain: &String,
|
||||
) -> ChorusResult<CreateDomainConnectionReturn> {
|
||||
let request = Client::new()
|
||||
.post(format!(
|
||||
"{}/users/@me/connections/domain/{}",
|
||||
self.belongs_to.read().unwrap().urls.api,
|
||||
domain
|
||||
))
|
||||
.header("Authorization", self.token());
|
||||
|
||||
let chorus_request = ChorusRequest {
|
||||
request,
|
||||
limit_type: LimitType::default(),
|
||||
};
|
||||
|
||||
let result = chorus_request
|
||||
.deserialize_response::<Connection>(self)
|
||||
.await;
|
||||
|
||||
if let Ok(connection) = result {
|
||||
return Ok(CreateDomainConnectionReturn::Ok(connection));
|
||||
}
|
||||
|
||||
let error = result.err().unwrap();
|
||||
|
||||
if let ChorusError::ReceivedErrorCode {
|
||||
error_code,
|
||||
error: ref error_string,
|
||||
} = error
|
||||
{
|
||||
if error_code == 400 {
|
||||
let try_deserialize: Result<CreateDomainConnectionError, serde_json::Error> =
|
||||
serde_json::from_str(error_string);
|
||||
|
||||
if let Ok(deserialized_error) = try_deserialize {
|
||||
return Ok(CreateDomainConnectionReturn::ProofNeeded(
|
||||
deserialized_error.proof,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(error)
|
||||
}
|
||||
|
||||
// TODO: Add create_domain_connection (<https://docs.discord.sex/resources/user#create-domain-connection>)
|
||||
// It requires changing how chorus handles errors to support properly
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
instance::{ChorusUser, Instance},
|
||||
ratelimiter::ChorusRequest,
|
||||
types::{
|
||||
AuthorizeConnectionSchema, ConnectionType, CreateUserHarvestSchema,
|
||||
AuthorizeConnectionSchema, BurstCreditsInfo, ConnectionType, CreateUserHarvestSchema,
|
||||
DeleteDisableUserSchema, GetPomeloEligibilityReturn, GetPomeloSuggestionsReturn,
|
||||
GetRecentMentionsSchema, GetUserProfileSchema, GuildAffinities, Harvest,
|
||||
HarvestBackendType, LimitType, ModifyUserNoteSchema, PremiumUsage, PublicUser, Snowflake,
|
||||
|
@ -618,10 +618,10 @@ impl ChorusUser {
|
|||
/// Fetches the current user's usage of various premium perks ([PremiumUsage] object).
|
||||
///
|
||||
/// The local user must have premium (nitro), otherwise the request will fail
|
||||
/// with a 404 NotFound error and the message {"message": "Premium usage not available", "code": 10084}.
|
||||
///
|
||||
/// # Notes
|
||||
/// As of 2024/08/16, Spacebar does not yet implement this endpoint.
|
||||
/// with a 404 NotFound error and the message {"message": "Premium usage not available", "code": 10084}.
|
||||
///
|
||||
/// # Notes
|
||||
/// As of 2024/08/16, Spacebar does not yet implement this endpoint.
|
||||
///
|
||||
/// # Reference
|
||||
/// See <https://docs.discord.sex/resources/user#get-user-premium-usage>
|
||||
|
@ -640,6 +640,29 @@ impl ChorusUser {
|
|||
|
||||
chorus_request.deserialize_response(self).await
|
||||
}
|
||||
|
||||
/// Fetches info about the current user's burst credits
|
||||
/// (how many are remaining, when they will replenish).
|
||||
///
|
||||
/// Burst credits are used to create burst reactions.
|
||||
///
|
||||
/// # Notes
|
||||
/// As of 2024/08/18, Spacebar does not yet implement this endpoint.
|
||||
pub async fn get_burst_credits(&mut self) -> ChorusResult<BurstCreditsInfo> {
|
||||
let request = Client::new()
|
||||
.get(format!(
|
||||
"{}/users/@me/burst-credits",
|
||||
self.belongs_to.read().unwrap().urls.api,
|
||||
))
|
||||
.header("Authorization", self.token());
|
||||
|
||||
let chorus_request = ChorusRequest {
|
||||
request,
|
||||
limit_type: LimitType::default(),
|
||||
};
|
||||
|
||||
chorus_request.deserialize_response(self).await
|
||||
}
|
||||
}
|
||||
|
||||
impl User {
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::errors::ChorusError;
|
|||
use crate::types::utils::Snowflake;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_aux::prelude::deserialize_option_number_from_string;
|
||||
use serde_aux::prelude::{deserialize_option_number_from_string, deserialize_default_from_null};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
use std::array::TryFromSliceError;
|
||||
use std::fmt::Debug;
|
||||
|
@ -257,6 +257,9 @@ pub struct UserProfileMetadata {
|
|||
/// The guild ID this profile applies to, if it is a guild profile.
|
||||
pub guild_id: Option<Snowflake>,
|
||||
/// The user's pronouns, up to 40 characters
|
||||
#[serde(deserialize_with = "deserialize_default_from_null")]
|
||||
// Note: spacebar will send this is as null, while it should be ""
|
||||
// See issue 1188
|
||||
pub pronouns: String,
|
||||
/// The user's bio / description, up to 190 characters
|
||||
pub bio: Option<String>,
|
||||
|
|
|
@ -30,7 +30,7 @@ pub struct Session {
|
|||
// Note: I don't think this one exists yet? Though I might've made a mistake and this might be a duplicate
|
||||
pub struct ClientInfo {
|
||||
pub client: Option<String>,
|
||||
pub os: String,
|
||||
pub os: Option<String>,
|
||||
pub version: u8,
|
||||
}
|
||||
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use chrono::{DateTime, NaiveDate, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::types::{
|
||||
GuildAffinity, HarvestBackendType, Snowflake, ThemeColors, TwoWayLinkType, UserAffinity,
|
||||
Connection, GuildAffinity, HarvestBackendType, Snowflake, ThemeColors, TwoWayLinkType,
|
||||
UserAffinity,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
|
@ -190,18 +191,22 @@ pub struct VerifyUserEmailChangeResponse {
|
|||
///
|
||||
/// See <https://docs.discord.sex/resources/user#get-user-profile>
|
||||
pub struct GetUserProfileSchema {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Whether to include the mutual guilds between the current user.
|
||||
///
|
||||
/// If unset it will default to true
|
||||
pub with_mutual_guilds: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Whether to include the mutual friends between the current user.
|
||||
///
|
||||
/// If unset it will default to false
|
||||
pub with_mutual_friends: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Whether to include the number of mutual friends between the current user
|
||||
///
|
||||
/// If unset it will default to false
|
||||
pub with_mutual_friends_count: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// The guild id to get the user's member profile in, if any.
|
||||
///
|
||||
/// Note:
|
||||
|
@ -211,6 +216,7 @@ pub struct GetUserProfileSchema {
|
|||
///
|
||||
/// This makes the request include fields such as guild_member and guild_member_profile
|
||||
pub guild_id: Option<Snowflake>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// The role id to get the user's application role connection metadata in, if any.
|
||||
pub connections_role_id: Option<Snowflake>,
|
||||
}
|
||||
|
@ -377,7 +383,7 @@ pub(crate) struct GetConnectionAccessTokenReturn {
|
|||
pub access_token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
|
||||
/// Return type for the [crate::instance::ChorusUser::get_user_affinities] endpoint.
|
||||
///
|
||||
/// See <https://docs.discord.sex/resources/user#get-user-affinities>
|
||||
|
@ -388,10 +394,67 @@ pub struct UserAffinities {
|
|||
pub inverse_user_affinities: Vec<UserAffinity>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
|
||||
/// Return type for the [crate::instance::ChorusUser::get_guild_affinities] endpoint.
|
||||
///
|
||||
/// See <https://docs.discord.sex/resources/user#get-guild-affinities>
|
||||
pub struct GuildAffinities {
|
||||
pub guild_affinities: Vec<GuildAffinity>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
/// Return type for the error in the [crate::instance::ChorusUser::create_domain_connection] endpoint.
|
||||
///
|
||||
/// This allows us to retrieve the needed proof for actually verifying the connection.
|
||||
///
|
||||
/// See <https://docs.discord.sex/resources/user#create-domain-connection>
|
||||
pub(crate) struct CreateDomainConnectionError {
|
||||
pub message: String,
|
||||
pub code: u16,
|
||||
pub proof: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
/// Return type for the [crate::instance::ChorusUser::create_domain_connection] endpoint.
|
||||
///
|
||||
/// See <https://docs.discord.sex/resources/user#create-domain-connection>
|
||||
pub enum CreateDomainConnectionReturn {
|
||||
/// Additional proof is needed to verify domain ownership.
|
||||
///
|
||||
/// The inner object is a proof string (e.g.
|
||||
/// `dh=dceaca792e3c40fcf356a9297949940af5cfe538`)
|
||||
///
|
||||
/// To verify ownership, either:
|
||||
///
|
||||
/// - add the proof string as a TXT DNS record to the domain,
|
||||
/// with the name of the record being `_discord.{domain}` or
|
||||
///
|
||||
/// - serve the proof string as a file at `https://{domain}/.well-known/discord`
|
||||
///
|
||||
/// After either of these proofs are added, the request should be retried.
|
||||
///
|
||||
ProofNeeded(String),
|
||||
/// The domain connection was successfully created, no further action is needed.
|
||||
///
|
||||
/// The inner object is the new connection.
|
||||
Ok(Connection),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
/// Return type for the [crate::instance::ChorusUser::get_burst_credits] endpoint.
|
||||
///
|
||||
/// # Reference
|
||||
/// ```json
|
||||
/// {
|
||||
/// "amount": 2,
|
||||
/// "replenished_today": false,
|
||||
/// "next_replenish_at": "2024-08-18T23:53:17+00:00"
|
||||
/// }
|
||||
/// ```
|
||||
pub struct BurstCreditsInfo {
|
||||
/// Amount of remaining burst credits the local user has
|
||||
pub amount: u16,
|
||||
pub replenished_today: bool,
|
||||
/// When the user's burst credits will automatically replenish again
|
||||
pub next_replenish_at: DateTime<Utc>,
|
||||
}
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use chorus::types::{PublicUser, Snowflake, User};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen_test::*;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
mod common;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), test)]
|
||||
|
@ -20,3 +26,20 @@ fn to_public_user() {
|
|||
let from_user = user.into_public_user();
|
||||
assert_eq!(public_user, from_user);
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
|
||||
async fn test_get_user_profile() {
|
||||
let mut bundle = common::setup().await;
|
||||
|
||||
let user_id = bundle.user.object.read().unwrap().id;
|
||||
|
||||
let user_profile = bundle
|
||||
.user
|
||||
.get_user_profile(user_id, chorus::types::GetUserProfileSchema::default())
|
||||
.await;
|
||||
|
||||
assert!(user_profile.is_ok());
|
||||
|
||||
common::teardown(bundle).await;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue