feat: add rest* of Connections api
* The only thing not added yet is create_domain_connection, because it uses errors in a funky way adds: - create_connection_callback - create_contact_sync_connection - get_connections - refresh_connection - modify_connection - delete_connection - get_connection_access_token - get_connection_subreddits + related schema for all those routes, and some supporting types
This commit is contained in:
parent
170f79bbd1
commit
a1caa38bfd
|
@ -1,15 +1,26 @@
|
|||
use futures_util::FutureExt;
|
||||
use reqwest::Client;
|
||||
|
||||
use crate::{
|
||||
errors::ChorusResult,
|
||||
instance::ChorusUser,
|
||||
ratelimiter::ChorusRequest,
|
||||
types::{AuthorizeConnectionReturn, AuthorizeConnectionSchema, ConnectionType, LimitType},
|
||||
types::{
|
||||
AuthorizeConnectionReturn, AuthorizeConnectionSchema, Connection, ConnectionSubreddit,
|
||||
ConnectionType, CreateConnectionCallbackSchema, CreateContactSyncConnectionSchema,
|
||||
GetConnectionAccessTokenReturn, LimitType, ModifyConnectionSchema,
|
||||
},
|
||||
};
|
||||
|
||||
impl ChorusUser {
|
||||
/// Fetches a url that can be used for authorizing a new connection.
|
||||
///
|
||||
/// The user should then visit the url and authenticate to create the connection.
|
||||
///
|
||||
/// # Notes
|
||||
/// This route seems to be preferred by the official infrastructure (client) to
|
||||
/// [Self::create_connection_callback].
|
||||
///
|
||||
/// # Reference
|
||||
/// See <https://docs.discord.sex/resources/user#authorize-user-connection>
|
||||
///
|
||||
|
@ -44,4 +55,249 @@ impl ChorusUser {
|
|||
.await
|
||||
.map(|response| response.url)
|
||||
}
|
||||
|
||||
/// Creates a new connection for the current user.
|
||||
///
|
||||
/// # Notes
|
||||
/// The official infrastructure (client) prefers the route
|
||||
/// [Self::authorize_connection] to this one.
|
||||
///
|
||||
/// # Reference
|
||||
/// See <https://docs.discord.sex/resources/user#create-user-connection-callback>
|
||||
// TODO: When is this called? When should it be used over authorize_connection?
|
||||
pub async fn create_connection_callback(
|
||||
&mut self,
|
||||
connection_type: ConnectionType,
|
||||
json_schema: CreateConnectionCallbackSchema,
|
||||
) -> ChorusResult<Connection> {
|
||||
let connection_type_string = serde_json::to_string(&connection_type)
|
||||
.expect("Failed to serialize connection type!")
|
||||
.replace('"', "");
|
||||
|
||||
let request = Client::new()
|
||||
.post(format!(
|
||||
"{}/connections/{}/callback",
|
||||
self.belongs_to.read().unwrap().urls.api,
|
||||
connection_type_string
|
||||
))
|
||||
.header("Authorization", self.token())
|
||||
.json(&json_schema);
|
||||
|
||||
let chorus_request = ChorusRequest {
|
||||
request,
|
||||
limit_type: LimitType::default(),
|
||||
};
|
||||
|
||||
chorus_request.deserialize_response(self).await
|
||||
}
|
||||
|
||||
/// Creates a new contact sync connection for the current user.
|
||||
///
|
||||
/// # Notes
|
||||
/// To create normal connection types, see [Self::authorize_connection] and
|
||||
/// [Self::create_connection_callback]
|
||||
///
|
||||
/// # Reference
|
||||
/// See <https://docs.discord.sex/resources/user#create-contact-sync-connection>
|
||||
pub async fn create_contact_sync_connection(
|
||||
&mut self,
|
||||
connection_account_id: &String,
|
||||
json_schema: CreateContactSyncConnectionSchema,
|
||||
) -> ChorusResult<Connection> {
|
||||
let request = Client::new()
|
||||
.put(format!(
|
||||
"{}/users/@me/connections/contacts/{}",
|
||||
self.belongs_to.read().unwrap().urls.api,
|
||||
connection_account_id
|
||||
))
|
||||
.header("Authorization", self.token())
|
||||
.json(&json_schema);
|
||||
|
||||
let chorus_request = ChorusRequest {
|
||||
request,
|
||||
limit_type: LimitType::default(),
|
||||
};
|
||||
|
||||
chorus_request.deserialize_response(self).await
|
||||
}
|
||||
|
||||
// TODO: Add create_domain_connection (<https://docs.discord.sex/resources/user#create-domain-connection>)
|
||||
// It requires changing how chorus handles errors to support properly
|
||||
|
||||
/// Fetches the current user's [Connection]s
|
||||
///
|
||||
/// # Reference
|
||||
/// See <https://docs.discord.sex/resources/user#get-user-connections>
|
||||
pub async fn get_connections(&mut self) -> ChorusResult<Vec<Connection>> {
|
||||
let request = Client::new()
|
||||
.get(format!(
|
||||
"{}/users/@me/connections",
|
||||
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
|
||||
}
|
||||
|
||||
/// Refreshes a local user's [Connection].
|
||||
///
|
||||
/// # Reference
|
||||
/// See <https://docs.discord.sex/resources/user#refresh-user-connection>
|
||||
pub async fn refresh_connection(
|
||||
&mut self,
|
||||
connection_type: ConnectionType,
|
||||
connection_account_id: &String,
|
||||
) -> ChorusResult<()> {
|
||||
let connection_type_string = serde_json::to_string(&connection_type)
|
||||
.expect("Failed to serialize connection type!")
|
||||
.replace('"', "");
|
||||
|
||||
let request = Client::new()
|
||||
.post(format!(
|
||||
"{}/users/@me/connections/{}/{}/refresh",
|
||||
self.belongs_to.read().unwrap().urls.api,
|
||||
connection_type_string,
|
||||
connection_account_id
|
||||
))
|
||||
.header("Authorization", self.token());
|
||||
|
||||
let chorus_request = ChorusRequest {
|
||||
request,
|
||||
limit_type: LimitType::default(),
|
||||
};
|
||||
|
||||
chorus_request.handle_request_as_result(self).await
|
||||
}
|
||||
|
||||
/// Changes settings on a local user's [Connection].
|
||||
///
|
||||
/// # Notes
|
||||
/// Not all connection types support all parameters.
|
||||
///
|
||||
/// # Reference
|
||||
/// See <https://docs.discord.sex/resources/user#modify-user-connection>
|
||||
pub async fn modify_connection(
|
||||
&mut self,
|
||||
connection_type: ConnectionType,
|
||||
connection_account_id: &String,
|
||||
json_schema: ModifyConnectionSchema,
|
||||
) -> ChorusResult<Connection> {
|
||||
let connection_type_string = serde_json::to_string(&connection_type)
|
||||
.expect("Failed to serialize connection type!")
|
||||
.replace('"', "");
|
||||
|
||||
let request = Client::new()
|
||||
.patch(format!(
|
||||
"{}/users/@me/connections/{}/{}",
|
||||
self.belongs_to.read().unwrap().urls.api,
|
||||
connection_type_string,
|
||||
connection_account_id
|
||||
))
|
||||
.header("Authorization", self.token())
|
||||
.json(&json_schema);
|
||||
|
||||
let chorus_request = ChorusRequest {
|
||||
request,
|
||||
limit_type: LimitType::default(),
|
||||
};
|
||||
|
||||
chorus_request.deserialize_response(self).await
|
||||
}
|
||||
|
||||
/// Deletes a local user's [Connection].
|
||||
///
|
||||
/// # Reference
|
||||
/// See <https://docs.discord.sex/resources/user#delete-user-connection>
|
||||
pub async fn delete_connection(
|
||||
&mut self,
|
||||
connection_type: ConnectionType,
|
||||
connection_account_id: &String,
|
||||
) -> ChorusResult<()> {
|
||||
let connection_type_string = serde_json::to_string(&connection_type)
|
||||
.expect("Failed to serialize connection type!")
|
||||
.replace('"', "");
|
||||
|
||||
let request = Client::new()
|
||||
.delete(format!(
|
||||
"{}/users/@me/connections/{}/{}",
|
||||
self.belongs_to.read().unwrap().urls.api,
|
||||
connection_type_string,
|
||||
connection_account_id
|
||||
))
|
||||
.header("Authorization", self.token());
|
||||
|
||||
let chorus_request = ChorusRequest {
|
||||
request,
|
||||
limit_type: LimitType::default(),
|
||||
};
|
||||
|
||||
chorus_request.handle_request_as_result(self).await
|
||||
}
|
||||
|
||||
/// Returns a new access token for the given connection.
|
||||
///
|
||||
/// Only available for [ConnectionType::Twitch], [ConnectionType::YouTube] and [ConnectionType::Spotify] connections.
|
||||
///
|
||||
/// # Reference
|
||||
/// See <https://docs.discord.sex/resources/user#get-user-connection-access-token>
|
||||
pub async fn get_connection_access_token(
|
||||
&mut self,
|
||||
connection_type: ConnectionType,
|
||||
connection_account_id: &String,
|
||||
) -> ChorusResult<String> {
|
||||
let connection_type_string = serde_json::to_string(&connection_type)
|
||||
.expect("Failed to serialize connection type!")
|
||||
.replace('"', "");
|
||||
|
||||
let request = Client::new()
|
||||
.get(format!(
|
||||
"{}/users/@me/connections/{}/{}/access-token",
|
||||
self.belongs_to.read().unwrap().urls.api,
|
||||
connection_type_string,
|
||||
connection_account_id
|
||||
))
|
||||
.header("Authorization", self.token());
|
||||
|
||||
let chorus_request = ChorusRequest {
|
||||
request,
|
||||
limit_type: LimitType::default(),
|
||||
};
|
||||
|
||||
chorus_request
|
||||
.deserialize_response::<GetConnectionAccessTokenReturn>(self)
|
||||
.await
|
||||
.map(|res| res.access_token)
|
||||
}
|
||||
|
||||
/// Fetches a list of [subreddits](crate::types::ConnectionSubreddit)
|
||||
/// the connected account moderates.
|
||||
///
|
||||
/// Only available for [ConnectionType::Reddit] connections.
|
||||
///
|
||||
/// # Reference
|
||||
/// See <https://docs.discord.sex/resources/user#get-user-connection-subreddits>
|
||||
pub async fn get_connection_subreddits(
|
||||
&mut self,
|
||||
connection_account_id: &String,
|
||||
) -> ChorusResult<Vec<ConnectionSubreddit>> {
|
||||
let request = Client::new()
|
||||
.get(format!(
|
||||
"{}/users/@me/connections/reddit/{}/subreddits",
|
||||
self.belongs_to.read().unwrap().urls.api,
|
||||
connection_account_id
|
||||
))
|
||||
.header("Authorization", self.token());
|
||||
|
||||
let chorus_request = ChorusRequest {
|
||||
request,
|
||||
limit_type: LimitType::default(),
|
||||
};
|
||||
|
||||
chorus_request.deserialize_response(self).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ pub struct Message {
|
|||
#[derive(Default, Debug)]
|
||||
pub struct User {
|
||||
pub update: Publisher<types::UserUpdate>,
|
||||
pub connections_update: Publisher<types::UserConnectionsUpdate>,
|
||||
pub note_update: Publisher<types::UserNoteUpdate>,
|
||||
pub guild_settings_update: Publisher<types::UserGuildSettingsUpdate>,
|
||||
pub presence_update: Publisher<types::PresenceUpdate>,
|
||||
|
|
|
@ -414,6 +414,7 @@ impl Gateway {
|
|||
"STAGE_INSTANCE_DELETE" => stage_instance.delete,
|
||||
"TYPING_START" => user.typing_start,
|
||||
"USER_UPDATE" => user.update, // TODO
|
||||
"USER_CONNECTIONS_UPDATE" => user.connections_update, // TODO
|
||||
"USER_NOTE_UPDATE" => user.note_update,
|
||||
"USER_GUILD_SETTINGS_UPDATE" => user.guild_settings_update,
|
||||
"VOICE_STATE_UPDATE" => voice.state_update, // TODO
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::Display,
|
||||
};
|
||||
use std::{collections::HashMap, fmt::Display};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
@ -153,6 +150,39 @@ impl Display for ConnectionType {
|
|||
}
|
||||
}
|
||||
|
||||
impl ConnectionType {
|
||||
/// Returns an array of all the connections
|
||||
pub fn array() -> [ConnectionType; 25] {
|
||||
[
|
||||
ConnectionType::AmazonMusic,
|
||||
ConnectionType::BattleNet,
|
||||
ConnectionType::Bungie,
|
||||
ConnectionType::Contacts,
|
||||
ConnectionType::Crunchyroll,
|
||||
ConnectionType::Domain,
|
||||
ConnectionType::Ebay,
|
||||
ConnectionType::EpicGames,
|
||||
ConnectionType::Facebook,
|
||||
ConnectionType::GitHub,
|
||||
ConnectionType::Instagram,
|
||||
ConnectionType::LeagueOfLegends,
|
||||
ConnectionType::PayPal,
|
||||
ConnectionType::Playstation,
|
||||
ConnectionType::Reddit,
|
||||
ConnectionType::RiotGames,
|
||||
ConnectionType::Samsung,
|
||||
ConnectionType::Spotify,
|
||||
ConnectionType::Skype,
|
||||
ConnectionType::Steam,
|
||||
ConnectionType::TikTok,
|
||||
ConnectionType::Twitch,
|
||||
ConnectionType::Twitter,
|
||||
ConnectionType::Xbox,
|
||||
ConnectionType::YouTube,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Serialize_repr, Deserialize_repr, Debug, Clone, Eq, PartialEq, Hash, Copy, PartialOrd, Ord,
|
||||
)]
|
||||
|
@ -189,3 +219,17 @@ impl Display for TwoWayLinkType {
|
|||
f.write_str(format!("{:?}", self).as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
/// Defines a subreddit as fetched through a Reddit connection.
|
||||
///
|
||||
/// # Reference
|
||||
/// See <https://docs.discord.sex/resources/user#subreddit-structure>
|
||||
pub struct ConnectionSubreddit {
|
||||
/// The subreddit's internal id, e.g. "t5_388p4"
|
||||
pub id: String,
|
||||
/// How many reddit users follow the subreddit
|
||||
pub subscribers: usize,
|
||||
/// The subreddit's relative url, e.g. "/r/discordapp/"
|
||||
pub url: String,
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::types::entities::PublicUser;
|
||||
use crate::types::events::WebSocketEvent;
|
||||
use crate::types::utils::Snowflake;
|
||||
use crate::types::Connection;
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq, WebSocketEvent)]
|
||||
/// See <https://discord.com/developers/docs/topics/gateway-events#user-update>;
|
||||
|
@ -16,6 +17,15 @@ pub struct UserUpdate {
|
|||
pub user: PublicUser,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, WebSocketEvent)]
|
||||
/// Sent to indicate updates to a user's [Connection].
|
||||
///
|
||||
/// Not documented anywhere
|
||||
pub struct UserConnectionsUpdate {
|
||||
#[serde(flatten)]
|
||||
pub connection: Connection,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq, WebSocketEvent)]
|
||||
/// See <https://docs.discord.sex/topics/gateway-events#user-note-update-structure>;
|
||||
///
|
||||
|
|
|
@ -297,3 +297,80 @@ pub struct AuthorizeConnectionSchema {
|
|||
pub(crate) struct AuthorizeConnectionReturn {
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
/// Json schema for the route POST /connections/{connection.type}/callback ([crate::instance::ChorusUser::create_connection_callback]).
|
||||
///
|
||||
/// See <https://docs.discord.sex/resources/user#create-user-connection-callback>
|
||||
pub struct CreateConnectionCallbackSchema {
|
||||
/// The authorization code for the connection
|
||||
pub code: String,
|
||||
/// The "state" used to authorize a connection
|
||||
// TODO: what is this?
|
||||
pub state: String,
|
||||
pub two_way_link_code: Option<String>,
|
||||
pub insecure: Option<bool>,
|
||||
pub friend_sync: Option<bool>,
|
||||
/// Additional parameters used for OpenID Connect
|
||||
// FIXME: Is this correct? in other connections additional info
|
||||
// is provided like this, only being string - string
|
||||
pub openid_params: Option<HashMap<String, String>>
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
/// Json schema for the route PUT /users/@me/connections/contacts/{connection.id} ([crate::instance::ChorusUser::create_contact_sync_connection]).
|
||||
///
|
||||
/// See <https://docs.discord.sex/resources/user#create-contact-sync-connection>
|
||||
pub struct CreateContactSyncConnectionSchema {
|
||||
/// The username of the connection account
|
||||
pub name: String,
|
||||
/// Whether to sync friends over the connection
|
||||
pub friend_sync: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
/// Json schema for the route PATCH /users/@me/connections/{connection.type}/{connection.id} ([crate::instance::ChorusUser::modify_connection]).
|
||||
///
|
||||
/// Note: not all connection types support all parameters.
|
||||
///
|
||||
/// See <https://docs.discord.sex/resources/user#modify-user-connection>
|
||||
pub struct ModifyConnectionSchema {
|
||||
/// The connection account's username
|
||||
///
|
||||
/// Note: We have not found which connection this could apply to
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
|
||||
/// Whether activities related to this connection will be shown in presence
|
||||
///
|
||||
/// e.g. on a Spotify connection, "Display Spotify as your status"
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub show_activity: Option<bool>,
|
||||
|
||||
/// Whether or not to sync friends from this connection
|
||||
///
|
||||
/// Note: we have not found which connections this can apply to
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub friend_sync: Option<bool>,
|
||||
|
||||
/// Whether to show additional metadata on the user's profile
|
||||
///
|
||||
/// e.g. on a Steam connection, "Display details on profile"
|
||||
/// (number of games, items, member since)
|
||||
///
|
||||
/// on a Twitter connection, number of posts / followers, member since
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub metadata_visibility: Option<bool>,
|
||||
|
||||
/// Whether to show the connection on the user's profile
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub visibility: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
/// Internal type for the [crate::instance::ChorusUser::get_connection_access_token] endpoint.
|
||||
///
|
||||
/// See <https://docs.discord.sex/resources/user#get-user-connection-access-token>
|
||||
pub(crate) struct GetConnectionAccessTokenReturn {
|
||||
pub access_token: String,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue