feat: add authorize_connection
Also adds: src/types/entities/connection.rs, Connection and PublicConnection, src/api/users/connections.rs
This commit is contained in:
parent
6c6a87ce5b
commit
170f79bbd1
|
@ -0,0 +1,47 @@
|
||||||
|
use reqwest::Client;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
errors::ChorusResult,
|
||||||
|
instance::ChorusUser,
|
||||||
|
ratelimiter::ChorusRequest,
|
||||||
|
types::{AuthorizeConnectionReturn, AuthorizeConnectionSchema, ConnectionType, LimitType},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl ChorusUser {
|
||||||
|
/// Fetches a url that can be used for authorizing a new connection.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://docs.discord.sex/resources/user#authorize-user-connection>
|
||||||
|
///
|
||||||
|
/// Note: it doesn't seem to be actually unauthenticated
|
||||||
|
pub async fn authorize_connection(
|
||||||
|
&mut self,
|
||||||
|
connection_type: ConnectionType,
|
||||||
|
query_parameters: AuthorizeConnectionSchema,
|
||||||
|
) -> 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!(
|
||||||
|
"{}/connections/{}/authorize",
|
||||||
|
self.belongs_to.read().unwrap().urls.api,
|
||||||
|
connection_type_string
|
||||||
|
))
|
||||||
|
// Note: ommiting this header causes a 401 Unauthorized,
|
||||||
|
// even though discord.sex mentions it as unauthenticated
|
||||||
|
.header("Authorization", self.token())
|
||||||
|
.query(&query_parameters);
|
||||||
|
|
||||||
|
let chorus_request = ChorusRequest {
|
||||||
|
request,
|
||||||
|
limit_type: LimitType::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
chorus_request
|
||||||
|
.deserialize_response::<AuthorizeConnectionReturn>(self)
|
||||||
|
.await
|
||||||
|
.map(|response| response.url)
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,11 +4,13 @@
|
||||||
|
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
pub use channels::*;
|
pub use channels::*;
|
||||||
|
pub use connections::*;
|
||||||
pub use guilds::*;
|
pub use guilds::*;
|
||||||
pub use relationships::*;
|
pub use relationships::*;
|
||||||
pub use users::*;
|
pub use users::*;
|
||||||
|
|
||||||
pub mod channels;
|
pub mod channels;
|
||||||
|
pub mod connections;
|
||||||
pub mod guilds;
|
pub mod guilds;
|
||||||
pub mod relationships;
|
pub mod relationships;
|
||||||
pub mod users;
|
pub mod users;
|
||||||
|
|
|
@ -15,11 +15,12 @@ use crate::{
|
||||||
instance::{ChorusUser, Instance},
|
instance::{ChorusUser, Instance},
|
||||||
ratelimiter::ChorusRequest,
|
ratelimiter::ChorusRequest,
|
||||||
types::{
|
types::{
|
||||||
CreateUserHarvestSchema, DeleteDisableUserSchema, GetPomeloEligibilityReturn,
|
AuthorizeConnectionSchema, ConnectionType, CreateUserHarvestSchema,
|
||||||
GetPomeloSuggestionsReturn, GetRecentMentionsSchema, GetUserProfileSchema, Harvest,
|
DeleteDisableUserSchema, GetPomeloEligibilityReturn, GetPomeloSuggestionsReturn,
|
||||||
HarvestBackendType, LimitType, ModifyUserNoteSchema, PublicUser, Snowflake, User,
|
GetRecentMentionsSchema, GetUserProfileSchema, Harvest, HarvestBackendType, LimitType,
|
||||||
UserModifyProfileSchema, UserModifySchema, UserNote, UserProfile, UserProfileMetadata,
|
ModifyUserNoteSchema, PublicUser, Snowflake, User, UserModifyProfileSchema,
|
||||||
UserSettings, VerifyUserEmailChangeResponse, VerifyUserEmailChangeSchema,
|
UserModifySchema, UserNote, UserProfile, UserProfileMetadata, UserSettings,
|
||||||
|
VerifyUserEmailChangeResponse, VerifyUserEmailChangeSchema,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fmt::Display,
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
|
||||||
|
/// A 3rd party service connection to a user's account.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://docs.discord.sex/resources/user#connection-object>
|
||||||
|
// TODO: Should (could) this type be Updateable and Composite?
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
pub struct Connection {
|
||||||
|
/// The id of the account on the 3rd party service
|
||||||
|
#[serde(rename = "id")]
|
||||||
|
pub connected_account_id: String,
|
||||||
|
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub connection_type: ConnectionType,
|
||||||
|
|
||||||
|
/// The username of the connection account
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// If the connection is verified
|
||||||
|
pub verified: bool,
|
||||||
|
|
||||||
|
/// Service specific metadata about the connection / connected account
|
||||||
|
// FIXME: Is there a better type? As far as I see the value is always encoded as a string
|
||||||
|
pub metadata: Option<HashMap<String, String>>,
|
||||||
|
pub metadata_visibility: ConnectionVisibilityType,
|
||||||
|
|
||||||
|
/// If the connection if revoked
|
||||||
|
pub revoked: bool,
|
||||||
|
|
||||||
|
// TODO: Add integrations
|
||||||
|
pub friend_sync: bool,
|
||||||
|
|
||||||
|
/// Whether activities related to this connection will be shown in presence
|
||||||
|
pub show_activity: bool,
|
||||||
|
|
||||||
|
/// Whether this connection has a corresponding 3rd party OAuth2 token
|
||||||
|
pub two_way_link: bool,
|
||||||
|
|
||||||
|
pub visibility: ConnectionVisibilityType,
|
||||||
|
|
||||||
|
/// The access token for the connection account
|
||||||
|
///
|
||||||
|
/// Note: not included when fetching a user's connections via OAuth2
|
||||||
|
pub access_token: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A partial / public [Connection] type.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://docs.discord.sex/resources/user#partial-connection-structure>
|
||||||
|
// FIXME: Should (could) this type also be Updateable and Composite?
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct PublicConnection {
|
||||||
|
/// The id of the account on the 3rd party service
|
||||||
|
#[serde(rename = "id")]
|
||||||
|
pub connected_account_id: String,
|
||||||
|
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub connection_type: ConnectionType,
|
||||||
|
|
||||||
|
/// The username of the connection account
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// If the connection is verified
|
||||||
|
pub verified: bool,
|
||||||
|
|
||||||
|
/// Service specific metadata about the connection / connected account
|
||||||
|
// FIXME: Is there a better type? As far as I see the value is always encoded as a string
|
||||||
|
pub metadata: Option<HashMap<String, String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Connection> for PublicConnection {
|
||||||
|
fn from(value: Connection) -> Self {
|
||||||
|
Self {
|
||||||
|
connected_account_id: value.connected_account_id,
|
||||||
|
connection_type: value.connection_type,
|
||||||
|
name: value.name,
|
||||||
|
verified: value.verified,
|
||||||
|
metadata: value.metadata,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, Hash, Copy, PartialOrd, Ord)]
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
/// A type of connection; the service the connection is for
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://docs.discord.sex/resources/user#connection-type>
|
||||||
|
pub enum ConnectionType {
|
||||||
|
#[serde(rename = "amazon-music")]
|
||||||
|
AmazonMusic,
|
||||||
|
/// Battle.net
|
||||||
|
BattleNet,
|
||||||
|
/// Bungie.net
|
||||||
|
Bungie,
|
||||||
|
/// Discord?'s contact sync
|
||||||
|
///
|
||||||
|
/// (Not returned in Get User Profile or when fetching connections)
|
||||||
|
Contacts,
|
||||||
|
Crunchyroll,
|
||||||
|
Domain,
|
||||||
|
Ebay,
|
||||||
|
EpicGames,
|
||||||
|
Facebook,
|
||||||
|
GitHub,
|
||||||
|
Instagram,
|
||||||
|
LeagueOfLegends,
|
||||||
|
PayPal,
|
||||||
|
/// Playstation network
|
||||||
|
Playstation,
|
||||||
|
Reddit,
|
||||||
|
Roblox,
|
||||||
|
RiotGames,
|
||||||
|
/// Samsung Galaxy
|
||||||
|
///
|
||||||
|
/// Users can no longer add this service
|
||||||
|
Samsung,
|
||||||
|
Spotify,
|
||||||
|
/// Users can no longer add this service
|
||||||
|
Skype,
|
||||||
|
Steam,
|
||||||
|
TikTok,
|
||||||
|
Twitch,
|
||||||
|
Twitter,
|
||||||
|
Xbox,
|
||||||
|
YouTube,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ConnectionType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Self::AmazonMusic => f.write_str("Amazon Music"),
|
||||||
|
Self::BattleNet => f.write_str("Battle.net"),
|
||||||
|
Self::Bungie => f.write_str("Bungie.net"),
|
||||||
|
Self::Ebay => f.write_str("eBay"),
|
||||||
|
Self::EpicGames => f.write_str("Epic Games"),
|
||||||
|
Self::LeagueOfLegends => f.write_str("League of Legends"),
|
||||||
|
Self::Playstation => f.write_str("PlayStation Network"),
|
||||||
|
Self::RiotGames => f.write_str("Riot Games"),
|
||||||
|
Self::Samsung => f.write_str("Samsung Galaxy"),
|
||||||
|
_ => f.write_str(format!("{:?}", self).as_str()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Serialize_repr, Deserialize_repr, Debug, Clone, Eq, PartialEq, Hash, Copy, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://docs.discord.sex/resources/user#visibility-type>
|
||||||
|
pub enum ConnectionVisibilityType {
|
||||||
|
/// Invisible to everyone except the user themselves
|
||||||
|
None = 0,
|
||||||
|
/// Visible to everyone
|
||||||
|
Everyone = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, Hash, Copy, PartialOrd, Ord)]
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
/// A type of two-way connection link
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://docs.discord.sex/resources/user#two-way-link-type>
|
||||||
|
pub enum TwoWayLinkType {
|
||||||
|
/// The connection is linked via web
|
||||||
|
Web,
|
||||||
|
/// The connection is linked via mobile
|
||||||
|
Mobile,
|
||||||
|
/// The connection is linked via desktop
|
||||||
|
Desktop,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TwoWayLinkType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(format!("{:?}", self).as_str())
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ pub use audit_log::*;
|
||||||
pub use auto_moderation::*;
|
pub use auto_moderation::*;
|
||||||
pub use channel::*;
|
pub use channel::*;
|
||||||
pub use config::*;
|
pub use config::*;
|
||||||
|
pub use connection::*;
|
||||||
pub use emoji::*;
|
pub use emoji::*;
|
||||||
pub use guild::*;
|
pub use guild::*;
|
||||||
pub use guild_member::*;
|
pub use guild_member::*;
|
||||||
|
@ -50,6 +51,7 @@ mod audit_log;
|
||||||
mod auto_moderation;
|
mod auto_moderation;
|
||||||
mod channel;
|
mod channel;
|
||||||
mod config;
|
mod config;
|
||||||
|
mod connection;
|
||||||
mod emoji;
|
mod emoji;
|
||||||
mod guild;
|
mod guild;
|
||||||
mod guild_member;
|
mod guild_member;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::collections::HashMap;
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{HarvestBackendType, Snowflake, ThemeColors};
|
use crate::types::{HarvestBackendType, Snowflake, ThemeColors, TwoWayLinkType};
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
@ -275,3 +275,25 @@ pub(crate) struct CreateUserHarvestSchema {
|
||||||
pub(crate) struct ModifyUserNoteSchema {
|
pub(crate) struct ModifyUserNoteSchema {
|
||||||
pub note: Option<String>,
|
pub note: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
|
/// Query string parameters for the route GET /connections/{connection.type}/authorize
|
||||||
|
/// ([crate::instance::ChorusUser::authorize_connection])
|
||||||
|
///
|
||||||
|
/// See <https://docs.discord.sex/resources/user#authorize-user-connection>
|
||||||
|
pub struct AuthorizeConnectionSchema {
|
||||||
|
/// The type of two-way link ([TwoWayLinkType]) to create
|
||||||
|
pub two_way_link_type: Option<TwoWayLinkType>,
|
||||||
|
/// The device code to use for the two-way link
|
||||||
|
pub two_way_user_code: Option<String>,
|
||||||
|
/// If this is a continuation of a previous authorization
|
||||||
|
pub continuation: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
|
/// Internal type for the [crate::instance::ChorusUser::authorize_connection] endpoint.
|
||||||
|
///
|
||||||
|
/// See <https://docs.discord.sex/resources/user#authorize-user-connection>
|
||||||
|
pub(crate) struct AuthorizeConnectionReturn {
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue