Compare commits

..

2 Commits

Author SHA1 Message Date
kozabrada123 04f5c9082d
Merge 170f79bbd1 into ec9541f38e 2024-08-10 09:35:46 +02:00
kozabrada123 170f79bbd1 feat: add authorize_connection
Also adds: src/types/entities/connection.rs, Connection and PublicConnection,
src/api/users/connections.rs
2024-08-10 11:34:56 +02:00
6 changed files with 271 additions and 6 deletions

View File

@ -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)
}
}

View File

@ -4,11 +4,13 @@
#![allow(unused_imports)]
pub use channels::*;
pub use connections::*;
pub use guilds::*;
pub use relationships::*;
pub use users::*;
pub mod channels;
pub mod connections;
pub mod guilds;
pub mod relationships;
pub mod users;

View File

@ -15,11 +15,12 @@ use crate::{
instance::{ChorusUser, Instance},
ratelimiter::ChorusRequest,
types::{
CreateUserHarvestSchema, DeleteDisableUserSchema, GetPomeloEligibilityReturn,
GetPomeloSuggestionsReturn, GetRecentMentionsSchema, GetUserProfileSchema, Harvest,
HarvestBackendType, LimitType, ModifyUserNoteSchema, PublicUser, Snowflake, User,
UserModifyProfileSchema, UserModifySchema, UserNote, UserProfile, UserProfileMetadata,
UserSettings, VerifyUserEmailChangeResponse, VerifyUserEmailChangeSchema,
AuthorizeConnectionSchema, ConnectionType, CreateUserHarvestSchema,
DeleteDisableUserSchema, GetPomeloEligibilityReturn, GetPomeloSuggestionsReturn,
GetRecentMentionsSchema, GetUserProfileSchema, Harvest, HarvestBackendType, LimitType,
ModifyUserNoteSchema, PublicUser, Snowflake, User, UserModifyProfileSchema,
UserModifySchema, UserNote, UserProfile, UserProfileMetadata, UserSettings,
VerifyUserEmailChangeResponse, VerifyUserEmailChangeSchema,
},
};

View File

@ -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())
}
}

View File

@ -8,6 +8,7 @@ pub use audit_log::*;
pub use auto_moderation::*;
pub use channel::*;
pub use config::*;
pub use connection::*;
pub use emoji::*;
pub use guild::*;
pub use guild_member::*;
@ -50,6 +51,7 @@ mod audit_log;
mod auto_moderation;
mod channel;
mod config;
mod connection;
mod emoji;
mod guild;
mod guild_member;

View File

@ -7,7 +7,7 @@ use std::collections::HashMap;
use chrono::NaiveDate;
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)]
#[serde(rename_all = "snake_case")]
@ -275,3 +275,25 @@ pub(crate) struct CreateUserHarvestSchema {
pub(crate) struct ModifyUserNoteSchema {
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,
}