0.3.0 Redone registration api, added debug/clone
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mammut"
|
name = "mammut"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
description = "A wrapper around the Mastodon API."
|
description = "A wrapper around the Mastodon API."
|
||||||
authors = ["Aaron Power <theaaronepower@gmail.com>"]
|
authors = ["Aaron Power <theaaronepower@gmail.com>"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
|
|||||||
@@ -3,3 +3,32 @@
|
|||||||
### [Documentation](https://docs.rs/mammut/)
|
### [Documentation](https://docs.rs/mammut/)
|
||||||
|
|
||||||
A wrapper around the [API](https://github.com/tootsuite/mastodon/blob/master/docs/Using-the-API/API.md#tag) for [Mastodon](https://mastodon.social/)
|
A wrapper around the [API](https://github.com/tootsuite/mastodon/blob/master/docs/Using-the-API/API.md#tag) for [Mastodon](https://mastodon.social/)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# extern crate mammut;
|
||||||
|
# fn main() {
|
||||||
|
# try().unwrap();
|
||||||
|
# }
|
||||||
|
# fn try() -> mammut::Result<()> {
|
||||||
|
use mammut::Registration;
|
||||||
|
use mammut::apps::{AppBuilder, Scope};
|
||||||
|
|
||||||
|
let app = AppBuilder {
|
||||||
|
client_name: "mammut_test",
|
||||||
|
redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
scopes: Scope::Read,
|
||||||
|
website: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut registration = Registration::new("https://mastodon.social")?;
|
||||||
|
registration.register(app)?;
|
||||||
|
let url = registration.authorise()?;
|
||||||
|
// Here you now need to open the url in the browser
|
||||||
|
// And handle a the redirect url coming back with the code.
|
||||||
|
let code = String::from("RETURNED_FROM_BROWSER");
|
||||||
|
let mastodon = registration.create_access_token(code)?;
|
||||||
|
|
||||||
|
println!("{:?}", mastodon.get_home_timeline()?);
|
||||||
|
# Ok(())
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Account {
|
pub struct Account {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Card {
|
pub struct Card {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::status::Status;
|
use super::status::Status;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub ancestors: Vec<Status>,
|
pub ancestors: Vec<Status>,
|
||||||
pub descendants: Vec<Status>,
|
pub descendants: Vec<Status>,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
pub uri: String,
|
pub uri: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use chrono::prelude::*;
|
|||||||
use super::account::Account;
|
use super::account::Account;
|
||||||
use super::status::Status;
|
use super::status::Status;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Notification {
|
pub struct Notification {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub notification_type: NotificationType,
|
pub notification_type: NotificationType,
|
||||||
@@ -11,7 +11,7 @@ pub struct Notification {
|
|||||||
pub status: Option<Status>,
|
pub status: Option<Status>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub enum NotificationType {
|
pub enum NotificationType {
|
||||||
Mention,
|
Mention,
|
||||||
Reblog,
|
Reblog,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Relationship {
|
pub struct Relationship {
|
||||||
pub following: bool,
|
pub following: bool,
|
||||||
pub followed_by: bool,
|
pub followed_by: bool,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Report {
|
pub struct Report {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub action_taken: String,
|
pub action_taken: String,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::prelude::{Account, Status};
|
use super::prelude::{Account, Status};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct SearchResult {
|
pub struct SearchResult {
|
||||||
pub accounts: Vec<Account>,
|
pub accounts: Vec<Account>,
|
||||||
pub statuses: Vec<Status>,
|
pub statuses: Vec<Status>,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use chrono::prelude::*;
|
|||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
use status_builder::Visibility;
|
use status_builder::Visibility;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
pub uri: String,
|
pub uri: String,
|
||||||
@@ -26,7 +26,7 @@ pub struct Status {
|
|||||||
pub application: Application,
|
pub application: Application,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Mention {
|
pub struct Mention {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
@@ -34,16 +34,14 @@ pub struct Mention {
|
|||||||
pub id: u64,
|
pub id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub url: String,
|
pub url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Application {
|
pub struct Application {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub website: String,
|
pub website: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+90
-150
@@ -2,6 +2,35 @@
|
|||||||
//!
|
//!
|
||||||
//! Most of the api is documented on [Mastodon's
|
//! Most of the api is documented on [Mastodon's
|
||||||
//! github](https://github.com/tootsuite/mastodon/blob/master/docs/Using-the-API/API.md#tag)
|
//! github](https://github.com/tootsuite/mastodon/blob/master/docs/Using-the-API/API.md#tag)
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! # extern crate mammut;
|
||||||
|
//! # fn main() {
|
||||||
|
//! # try().unwrap();
|
||||||
|
//! # }
|
||||||
|
//! # fn try() -> mammut::Result<()> {
|
||||||
|
//! use mammut::Registration;
|
||||||
|
//! use mammut::apps::{AppBuilder, Scope};
|
||||||
|
//!
|
||||||
|
//! let app = AppBuilder {
|
||||||
|
//! client_name: "mammut_test",
|
||||||
|
//! redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
//! scopes: Scope::Read,
|
||||||
|
//! website: None,
|
||||||
|
//! };
|
||||||
|
//!
|
||||||
|
//! let mut registration = Registration::new("https://mastodon.social")?;
|
||||||
|
//! registration.register(app)?;
|
||||||
|
//! let url = registration.authorise()?;
|
||||||
|
//! // Here you now need to open the url in the browser
|
||||||
|
//! // And handle a the redirect url coming back with the code.
|
||||||
|
//! let code = String::from("RETURNED_FROM_BROWSER");
|
||||||
|
//! let mastodon = registration.create_access_token(code)?;
|
||||||
|
//!
|
||||||
|
//! println!("{:?}", mastodon.get_home_timeline()?);
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
|
||||||
#[cfg_attr(test, deny(warnings))]
|
#[cfg_attr(test, deny(warnings))]
|
||||||
|
|
||||||
@@ -17,6 +46,10 @@ pub mod apps;
|
|||||||
pub mod status_builder;
|
pub mod status_builder;
|
||||||
/// Entities returned from the API
|
/// Entities returned from the API
|
||||||
pub mod entities;
|
pub mod entities;
|
||||||
|
/// Registering your app.
|
||||||
|
pub mod registration;
|
||||||
|
|
||||||
|
use std::ops;
|
||||||
|
|
||||||
use json::Error as SerdeError;
|
use json::Error as SerdeError;
|
||||||
use reqwest::Error as HttpError;
|
use reqwest::Error as HttpError;
|
||||||
@@ -26,6 +59,7 @@ use reqwest::header::{Authorization, Bearer, Headers};
|
|||||||
use entities::prelude::*;
|
use entities::prelude::*;
|
||||||
use status_builder::StatusBuilder;
|
use status_builder::StatusBuilder;
|
||||||
|
|
||||||
|
pub use registration::Registration;
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
macro_rules! methods {
|
macro_rules! methods {
|
||||||
@@ -35,7 +69,7 @@ macro_rules! methods {
|
|||||||
-> Result<T>
|
-> Result<T>
|
||||||
{
|
{
|
||||||
Ok(self.client.$method(&url)
|
Ok(self.client.$method(&url)
|
||||||
.headers(self.access_token.clone().unwrap())
|
.headers(self.headers.clone())
|
||||||
.send()?
|
.send()?
|
||||||
.json()?)
|
.json()?)
|
||||||
}
|
}
|
||||||
@@ -53,7 +87,6 @@ macro_rules! route {
|
|||||||
#[doc = "# Errors"]
|
#[doc = "# Errors"]
|
||||||
/// If `access_token` is not set.
|
/// If `access_token` is not set.
|
||||||
pub fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
|
pub fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
|
||||||
self.has_access_token()?;
|
|
||||||
|
|
||||||
let form_data = json!({
|
let form_data = json!({
|
||||||
$(
|
$(
|
||||||
@@ -62,7 +95,7 @@ macro_rules! route {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Ok(self.client.post(&self.route(concat!("/api/v1/", $url)))
|
Ok(self.client.post(&self.route(concat!("/api/v1/", $url)))
|
||||||
.headers(self.access_token.clone().unwrap())
|
.headers(self.headers.clone())
|
||||||
.form(&form_data)
|
.form(&form_data)
|
||||||
.send()?
|
.send()?
|
||||||
.json()?)
|
.json()?)
|
||||||
@@ -78,8 +111,6 @@ macro_rules! route {
|
|||||||
#[doc = "# Errors"]
|
#[doc = "# Errors"]
|
||||||
/// If `access_token` is not set.
|
/// If `access_token` is not set.
|
||||||
pub fn $name(&self) -> Result<$ret> {
|
pub fn $name(&self) -> Result<$ret> {
|
||||||
self.has_access_token()?;
|
|
||||||
|
|
||||||
self.$method(self.route(concat!("/api/v1/", $url)))
|
self.$method(self.route(concat!("/api/v1/", $url)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,9 +131,6 @@ macro_rules! route_id {
|
|||||||
#[doc = "# Errors"]
|
#[doc = "# Errors"]
|
||||||
/// If `access_token` is not set.
|
/// If `access_token` is not set.
|
||||||
pub fn $name(&self, id: u64) -> Result<$ret> {
|
pub fn $name(&self, id: u64) -> Result<$ret> {
|
||||||
self.has_access_token()?;
|
|
||||||
|
|
||||||
|
|
||||||
self.$method(self.route(&format!(concat!("/api/v1/", $url), id)))
|
self.$method(self.route(&format!(concat!("/api/v1/", $url), id)))
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
@@ -112,26 +140,21 @@ macro_rules! route_id {
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Mastodon {
|
pub struct Mastodon {
|
||||||
base_url: String,
|
|
||||||
client: Client,
|
client: Client,
|
||||||
client_id: Option<String>,
|
headers: Headers,
|
||||||
client_secret: Option<String>,
|
/// Raw data about your mastodon instance.
|
||||||
redirect_uri: Option<String>,
|
pub data: Data
|
||||||
access_token: Option<Headers>,
|
|
||||||
id: Option<u64>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
/// Raw data about mastodon app. Save `Data` using `serde` to prevent needing
|
||||||
struct OAuth {
|
/// to authenticate on every run.
|
||||||
client_id: String,
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
client_secret: String,
|
pub struct Data {
|
||||||
id: u64,
|
pub base: String,
|
||||||
redirect_uri: String,
|
pub client_id: String,
|
||||||
}
|
pub client_secret: String,
|
||||||
|
pub redirect: String,
|
||||||
#[derive(Deserialize)]
|
pub token: String,
|
||||||
struct AccessToken {
|
|
||||||
access_token: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -144,104 +167,43 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Mastodon {
|
impl Mastodon {
|
||||||
/// Inits new Mastodon object. `base_url` is expected in the following
|
fn from_registration(base: String,
|
||||||
/// format `https://mastodon.social` with no leading forward slash.
|
client_id: String,
|
||||||
///
|
client_secret: String,
|
||||||
/// ```
|
redirect: String,
|
||||||
/// use mammut::Mastodon;
|
token: String,
|
||||||
///
|
client: Client)
|
||||||
/// let mastodon = Mastodon::new("https://mastodon.social").unwrap();
|
-> Self
|
||||||
/// ```
|
{
|
||||||
pub fn new<I: Into<String>>(base_url: I) -> Result<Self> {
|
let data = Data {
|
||||||
Ok(Mastodon {
|
base: base,
|
||||||
base_url: base_url.into(),
|
client_id: client_id,
|
||||||
client: Client::new()?,
|
client_secret: client_secret,
|
||||||
client_id: None,
|
redirect: redirect,
|
||||||
client_secret: None,
|
token: token,
|
||||||
redirect_uri: None,
|
|
||||||
access_token: None,
|
|
||||||
id: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the application with the server from `base_url`.
|
};
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # extern crate mammut;
|
|
||||||
/// # fn main() {
|
|
||||||
/// # try().unwrap();
|
|
||||||
/// # }
|
|
||||||
///
|
|
||||||
/// # fn try() -> mammut::Result<()> {
|
|
||||||
/// use mammut::Mastodon;
|
|
||||||
/// use mammut::apps::{AppBuilder, Scope};
|
|
||||||
///
|
|
||||||
/// let app = AppBuilder {
|
|
||||||
/// client_name: "mammut_test",
|
|
||||||
/// redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
|
|
||||||
/// scopes: Scope::Read,
|
|
||||||
/// website: None,
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// let mut mastodon = Mastodon::new("https://mastodon.social")?;
|
|
||||||
/// mastodon.register(app)?;
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub fn register(&mut self, app_builder: apps::AppBuilder) -> Result<()> {
|
|
||||||
let url = self.route("/api/v1/apps");
|
|
||||||
|
|
||||||
let app: OAuth = self.client.post(&url).form(&app_builder).send()?.json()?;
|
|
||||||
|
|
||||||
self.id = Some(app.id);
|
|
||||||
self.client_id = Some(app.client_id);
|
|
||||||
self.client_secret = Some(app.client_secret);
|
|
||||||
self.redirect_uri = Some(app.redirect_uri);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the full url needed for authorisation. This needs to be opened
|
|
||||||
/// in a browser.
|
|
||||||
pub fn authorise(&mut self) -> Result<String> {
|
|
||||||
self.is_registered()?;
|
|
||||||
|
|
||||||
let url = format!(
|
|
||||||
"{}/oauth/authorize?client_id={}&redirect_uri={}&response_type=code",
|
|
||||||
self.base_url,
|
|
||||||
self.client_id.clone().unwrap(),
|
|
||||||
self.redirect_uri.clone().unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Requires the authorisation code returned from the `redirect_url`
|
|
||||||
pub fn get_access_token(&mut self, code: String) -> Result<()> {
|
|
||||||
self.is_registered()?;
|
|
||||||
|
|
||||||
let url = format!(
|
|
||||||
"{}/oauth/token?client_id={}&client_secret={}&code={}&grant_type=authorization_code&redirect_uri={}",
|
|
||||||
self.base_url,
|
|
||||||
self.client_id.clone().unwrap(),
|
|
||||||
self.client_secret.clone().unwrap(),
|
|
||||||
code,
|
|
||||||
self.redirect_uri.clone().unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let access_token: AccessToken = self.client.post(&url).send()?.json()?;
|
|
||||||
|
|
||||||
self.set_access_token(access_token.access_token);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set `access_token` required to use any method about the user.
|
|
||||||
fn set_access_token(&mut self, access_token: String) {
|
|
||||||
let mut headers = Headers::new();
|
let mut headers = Headers::new();
|
||||||
|
headers.set(Authorization(Bearer { token: data.token.clone() }));
|
||||||
|
|
||||||
headers.set(Authorization(Bearer { token: access_token }));
|
Mastodon {
|
||||||
|
client: client,
|
||||||
|
headers: headers,
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.access_token = Some(headers);
|
/// Creates a mastodon instance from the data struct.
|
||||||
|
pub fn from_data(data: Data) -> Result<Self> {
|
||||||
|
let mut headers = Headers::new();
|
||||||
|
headers.set(Authorization(Bearer { token: data.token.clone() }));
|
||||||
|
|
||||||
|
Ok(Mastodon {
|
||||||
|
client: Client::new()?,
|
||||||
|
headers: headers,
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
route! {
|
route! {
|
||||||
@@ -287,8 +249,6 @@ impl Mastodon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_public_timeline(&self, local: bool) -> Result<Vec<Status>> {
|
pub fn get_public_timeline(&self, local: bool) -> Result<Vec<Status>> {
|
||||||
self.has_access_token()?;
|
|
||||||
|
|
||||||
let mut url = self.route("/api/v1/timelines/public");
|
let mut url = self.route("/api/v1/timelines/public");
|
||||||
|
|
||||||
if local {
|
if local {
|
||||||
@@ -299,8 +259,6 @@ impl Mastodon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tagged_timeline(&self, hashtag: String, local: bool) -> Result<Vec<Status>> {
|
pub fn get_tagged_timeline(&self, hashtag: String, local: bool) -> Result<Vec<Status>> {
|
||||||
self.has_access_token()?;
|
|
||||||
|
|
||||||
let mut url = self.route("/api/v1/timelines/tag/");
|
let mut url = self.route("/api/v1/timelines/tag/");
|
||||||
url += &hashtag;
|
url += &hashtag;
|
||||||
|
|
||||||
@@ -314,8 +272,7 @@ impl Mastodon {
|
|||||||
pub fn statuses(&self, id: u64, only_media: bool, exclude_replies: bool)
|
pub fn statuses(&self, id: u64, only_media: bool, exclude_replies: bool)
|
||||||
-> Result<Vec<Status>>
|
-> Result<Vec<Status>>
|
||||||
{
|
{
|
||||||
self.has_access_token()?;
|
let mut url = format!("{}/api/v1/accounts/{}/statuses", self.base, id);
|
||||||
let mut url = format!("{}/api/v1/accounts/{}/statuses", self.base_url, id);
|
|
||||||
|
|
||||||
if only_media {
|
if only_media {
|
||||||
url += "?only_media=1";
|
url += "?only_media=1";
|
||||||
@@ -336,8 +293,6 @@ impl Mastodon {
|
|||||||
|
|
||||||
|
|
||||||
pub fn relationships(&self, ids: &[u64]) -> Result<Vec<Relationship>> {
|
pub fn relationships(&self, ids: &[u64]) -> Result<Vec<Relationship>> {
|
||||||
self.has_access_token()?;
|
|
||||||
|
|
||||||
let mut url = self.route("/api/v1/accounts/relationships?");
|
let mut url = self.route("/api/v1/accounts/relationships?");
|
||||||
|
|
||||||
if ids.len() == 1 {
|
if ids.len() == 1 {
|
||||||
@@ -357,44 +312,29 @@ impl Mastodon {
|
|||||||
|
|
||||||
// TODO: Add a limit fn
|
// TODO: Add a limit fn
|
||||||
pub fn search_accounts(&self, query: &str) -> Result<Vec<Account>> {
|
pub fn search_accounts(&self, query: &str) -> Result<Vec<Account>> {
|
||||||
self.has_access_token()?;
|
self.get(format!("{}/api/v1/accounts/search?q={}", self.base, query))
|
||||||
self.get(format!("{}/api/v1/accounts/search?q={}", self.base_url, query))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn instance(&self) -> Result<Instance> {
|
pub fn instance(&self) -> Result<Instance> {
|
||||||
self.is_registered()?;
|
|
||||||
|
|
||||||
self.get(self.route("/api/v1/instance"))
|
self.get(self.route("/api/v1/instance"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn has_access_token(&self) -> Result<()> {
|
|
||||||
if self.access_token.is_none() {
|
|
||||||
Err(Error::AccessTokenRequired)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_registered(&self) -> Result<()> {
|
|
||||||
if self.client_id.is_none() {
|
|
||||||
Err(Error::ClientIdRequired)
|
|
||||||
} else if self.client_secret.is_none() {
|
|
||||||
Err(Error::ClientSecretRequired)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
methods![get, post, delete,];
|
methods![get, post, delete,];
|
||||||
|
|
||||||
fn route(&self, url: &str) -> String {
|
fn route(&self, url: &str) -> String {
|
||||||
let mut s = self.base_url.clone();
|
let mut s = self.base.clone();
|
||||||
s += url;
|
s += url;
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ops::Deref for Mastodon {
|
||||||
|
type Target = Data;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! from {
|
macro_rules! from {
|
||||||
($($typ:ident, $variant:ident,)*) => {
|
($($typ:ident, $variant:ident,)*) => {
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
use reqwest::Client;
|
||||||
|
|
||||||
|
use super::{Error, Mastodon, Result};
|
||||||
|
use apps::AppBuilder;
|
||||||
|
|
||||||
|
/// Handles registering your mastodon app to your instance. It is recommended
|
||||||
|
/// you cache your data struct to avoid registering on every run.
|
||||||
|
pub struct Registration {
|
||||||
|
base: String,
|
||||||
|
client: Client,
|
||||||
|
client_id: Option<String>,
|
||||||
|
client_secret: Option<String>,
|
||||||
|
redirect: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct OAuth {
|
||||||
|
client_id: String,
|
||||||
|
client_secret: String,
|
||||||
|
redirect_uri: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct AccessToken {
|
||||||
|
access_token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Registration {
|
||||||
|
pub fn new<I: Into<String>>(base: I) -> Result<Self> {
|
||||||
|
Ok(Registration {
|
||||||
|
base: base.into(),
|
||||||
|
client: Client::new()?,
|
||||||
|
client_id: None,
|
||||||
|
client_secret: None,
|
||||||
|
redirect: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register the application with the server from the `base` url.
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # extern crate mammut;
|
||||||
|
/// # fn main() {
|
||||||
|
/// # try().unwrap();
|
||||||
|
/// # }
|
||||||
|
/// # fn try() -> mammut::Result<()> {
|
||||||
|
/// use mammut::Registration;
|
||||||
|
/// use mammut::apps::{AppBuilder, Scope};
|
||||||
|
///
|
||||||
|
/// let app = AppBuilder {
|
||||||
|
/// client_name: "mammut_test",
|
||||||
|
/// redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
/// scopes: Scope::Read,
|
||||||
|
/// website: None,
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// let mut registration = Registration::new("https://mastodon.social")?;
|
||||||
|
/// registration.register(app)?;
|
||||||
|
/// let url = registration.authorise()?;
|
||||||
|
/// // Here you now need to open the url in the browser
|
||||||
|
/// // And handle a the redirect url coming back with the code.
|
||||||
|
/// let code = String::from("RETURNED_FROM_BROWSER");
|
||||||
|
/// let mastodon = registration.create_access_token(code)?;
|
||||||
|
///
|
||||||
|
/// println!("{:?}", mastodon.get_home_timeline()?);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn register(&mut self, app_builder: AppBuilder) -> Result<()> {
|
||||||
|
let url = format!("{}/api/v1/apps", self.base);
|
||||||
|
|
||||||
|
let app: OAuth = self.client.post(&url).form(&app_builder).send()?.json()?;
|
||||||
|
|
||||||
|
self.client_id = Some(app.client_id);
|
||||||
|
self.client_secret = Some(app.client_secret);
|
||||||
|
self.redirect = Some(app.redirect_uri);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the full url needed for authorisation. This needs to be opened
|
||||||
|
/// in a browser.
|
||||||
|
pub fn authorise(&mut self) -> Result<String> {
|
||||||
|
self.is_registered()?;
|
||||||
|
|
||||||
|
let url = format!(
|
||||||
|
"{}/oauth/authorize?client_id={}&redirect_uri={}&response_type=code",
|
||||||
|
self.base,
|
||||||
|
self.client_id.clone().unwrap(),
|
||||||
|
self.redirect.clone().unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_registered(&self) -> Result<()> {
|
||||||
|
if self.client_id.is_none() {
|
||||||
|
Err(Error::ClientIdRequired)
|
||||||
|
} else if self.client_secret.is_none() {
|
||||||
|
Err(Error::ClientSecretRequired)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_access_token(self, code: String) -> Result<Mastodon> {
|
||||||
|
self.is_registered()?;
|
||||||
|
let url = format!(
|
||||||
|
"{}/oauth/token?client_id={}&client_secret={}&code={}&grant_type=authorization_code&redirect_uri={}",
|
||||||
|
self.base,
|
||||||
|
self.client_id.clone().unwrap(),
|
||||||
|
self.client_secret.clone().unwrap(),
|
||||||
|
code,
|
||||||
|
self.redirect.clone().unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
let token: AccessToken = self.client.post(&url).send()?.json()?;
|
||||||
|
|
||||||
|
Ok(Mastodon::from_registration(self.base,
|
||||||
|
self.client_id.unwrap(),
|
||||||
|
self.client_secret.unwrap(),
|
||||||
|
self.redirect.unwrap(),
|
||||||
|
token.access_token,
|
||||||
|
self.client))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user