forked from MightyPork/elefren-fork
Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "mammut"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A wrapper around the Mastodon API."
|
||||||
|
authors = ["Aaron Power <theaaronepower@gmail.com>"]
|
||||||
|
license = "MIT/Apache-2.0"
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/Aaronepower/mastodon.rs.git"
|
||||||
|
keywords = ["api", "web", "social", "mastodon", "wrapper"]
|
||||||
|
categories = ["web-programming", "http-client"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
reqwest = "0.5"
|
||||||
|
serde = "0.9"
|
||||||
|
serde_json = "0.9"
|
||||||
|
serde_derive = "0.9"
|
||||||
|
|
||||||
|
[dependencies.chrono]
|
||||||
|
version = "0.3"
|
||||||
|
features = ["serde"]
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
Copyright 2016 Aaron Power
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Aaron Power
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Mammut. A API Wrapper for the Mastodon API.
|
||||||
|
|
||||||
|
### [Documentation](https://docs.rs/mastodon)
|
||||||
|
|
||||||
|
A wrapper around the [API](https://github.com/tootsuite/mastodon/blob/master/docs/Using-the-API/API.md#tag) for [Mastodon](https://mastodon.social/)
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
#[derive(Debug, Default, Serialize)]
|
||||||
|
pub struct AppBuilder<'a> {
|
||||||
|
pub client_name: &'a str,
|
||||||
|
pub redirect_uris: &'a str,
|
||||||
|
pub scopes: Scope,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
|
pub website: Option<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize)]
|
||||||
|
pub enum Scope {
|
||||||
|
#[serde(rename = "read write follow")]
|
||||||
|
All,
|
||||||
|
#[serde(rename = "follow")]
|
||||||
|
Follow,
|
||||||
|
#[serde(rename = "read")]
|
||||||
|
Read,
|
||||||
|
#[serde(rename = "read follow")]
|
||||||
|
ReadFollow,
|
||||||
|
#[serde(rename = "read write")]
|
||||||
|
ReadWrite,
|
||||||
|
#[serde(rename = "write")]
|
||||||
|
Write,
|
||||||
|
#[serde(rename = "write follow")]
|
||||||
|
WriteFollow,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Scope {
|
||||||
|
fn default() -> Self {
|
||||||
|
Scope::Read
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
use chrono::prelude::*;
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Account {
|
||||||
|
pub id: u64,
|
||||||
|
pub username: String,
|
||||||
|
pub acct: String,
|
||||||
|
pub display_name: String,
|
||||||
|
pub note: String,
|
||||||
|
pub url: String,
|
||||||
|
pub avatar: String,
|
||||||
|
pub header: String,
|
||||||
|
pub locked: bool,
|
||||||
|
pub created_at: DateTime<UTC>,
|
||||||
|
pub followers_count: u64,
|
||||||
|
pub following_count: u64,
|
||||||
|
pub statuses_count: u64,
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Attachment {
|
||||||
|
pub id: u64,
|
||||||
|
#[serde(rename="type")]
|
||||||
|
pub media_type: MediaType,
|
||||||
|
pub url: String,
|
||||||
|
pub remote_url: String,
|
||||||
|
pub preview_url: String,
|
||||||
|
pub text_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone, Copy)]
|
||||||
|
pub enum MediaType {
|
||||||
|
#[serde(rename = "image")]
|
||||||
|
Image,
|
||||||
|
#[serde(rename = "video")]
|
||||||
|
Video,
|
||||||
|
#[serde(rename = "gifv")]
|
||||||
|
Gifv,
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Card {
|
||||||
|
pub url: String,
|
||||||
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
|
pub image: String,
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
use super::status::Status;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Context {
|
||||||
|
pub ancestors: Vec<Status>,
|
||||||
|
pub descendants: Vec<Status>,
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Instance {
|
||||||
|
pub uri: String,
|
||||||
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
|
pub email: String,
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
pub mod account;
|
||||||
|
pub mod attachment;
|
||||||
|
pub mod card;
|
||||||
|
pub mod context;
|
||||||
|
pub mod instance;
|
||||||
|
pub mod notification;
|
||||||
|
pub mod relationship;
|
||||||
|
pub mod report;
|
||||||
|
pub mod search_result;
|
||||||
|
pub mod status;
|
||||||
|
|
||||||
|
/// An empty JSON object.
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Empty {}
|
||||||
|
|
||||||
|
pub mod prelude {
|
||||||
|
pub use super::Empty;
|
||||||
|
pub use super::account::Account;
|
||||||
|
pub use super::attachment::{Attachment, MediaType};
|
||||||
|
pub use super::card::Card;
|
||||||
|
pub use super::context::Context;
|
||||||
|
pub use super::instance::Instance;
|
||||||
|
pub use super::notification::Notification;
|
||||||
|
pub use super::relationship::Relationship;
|
||||||
|
pub use super::report::Report;
|
||||||
|
pub use super::search_result::SearchResult;
|
||||||
|
pub use super::status::{Status, Application};
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
use chrono::prelude::*;
|
||||||
|
use super::account::Account;
|
||||||
|
use super::status::Status;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Notification {
|
||||||
|
pub id: u64,
|
||||||
|
pub notification_type: NotificationType,
|
||||||
|
pub created_at: DateTime<UTC>,
|
||||||
|
pub account: Account,
|
||||||
|
pub status: Option<Status>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub enum NotificationType {
|
||||||
|
Mention,
|
||||||
|
Reblog,
|
||||||
|
Favourite,
|
||||||
|
Follow,
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Relationship {
|
||||||
|
pub following: bool,
|
||||||
|
pub followed_by: bool,
|
||||||
|
pub blocking: bool,
|
||||||
|
pub muting: bool,
|
||||||
|
pub requested: bool,
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Report {
|
||||||
|
pub id: u64,
|
||||||
|
pub action_taken: String,
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
use super::prelude::{Account, Status};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct SearchResult {
|
||||||
|
pub accounts: Vec<Account>,
|
||||||
|
pub statuses: Vec<Status>,
|
||||||
|
pub hashtags: Vec<String>,
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
use chrono::prelude::*;
|
||||||
|
use super::prelude::*;
|
||||||
|
use status_builder::Visibility;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Status {
|
||||||
|
pub id: i64,
|
||||||
|
pub uri: String,
|
||||||
|
pub url: String,
|
||||||
|
pub account: Account,
|
||||||
|
pub in_reply_to_id: Option<u64>,
|
||||||
|
pub in_reply_to_account_id: Option<u64>,
|
||||||
|
pub reblog: Option<Box<Status>>,
|
||||||
|
pub content: String,
|
||||||
|
pub created_at: DateTime<UTC>,
|
||||||
|
pub reblogs_count: u64,
|
||||||
|
pub favourites_count: u64,
|
||||||
|
pub reblogged: bool,
|
||||||
|
pub favourited: bool,
|
||||||
|
pub sensitive: bool,
|
||||||
|
pub spoiler_text: String,
|
||||||
|
pub visibility: Visibility,
|
||||||
|
pub media_attachments: Vec<Attachment>,
|
||||||
|
pub mentions: Vec<Mention>,
|
||||||
|
pub tags: Vec<Tag>,
|
||||||
|
pub application: Application,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Mention {
|
||||||
|
pub url: String,
|
||||||
|
pub username: String,
|
||||||
|
pub acct: String,
|
||||||
|
pub id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Tag {
|
||||||
|
pub name: String,
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Application {
|
||||||
|
pub name: String,
|
||||||
|
pub website: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
+374
@@ -0,0 +1,374 @@
|
|||||||
|
//! # Mammut: API Wrapper around the Mastodon API.
|
||||||
|
//!
|
||||||
|
//! Most of the api is documented on [Mastodon's
|
||||||
|
//! github](https://github.com/tootsuite/mastodon/blob/master/docs/Using-the-API/API.md#tag)
|
||||||
|
#![deny(unused_must_use)]
|
||||||
|
|
||||||
|
#[cfg_attr(test, deny(warnings))]
|
||||||
|
|
||||||
|
#[macro_use] extern crate serde_derive;
|
||||||
|
#[macro_use] extern crate serde_json as json;
|
||||||
|
extern crate chrono;
|
||||||
|
extern crate reqwest;
|
||||||
|
extern crate serde;
|
||||||
|
|
||||||
|
pub mod apps;
|
||||||
|
pub mod status_builder;
|
||||||
|
pub mod entities;
|
||||||
|
|
||||||
|
use json::Error as SerdeError;
|
||||||
|
use reqwest::Error as HttpError;
|
||||||
|
use reqwest::Client;
|
||||||
|
use reqwest::header::{Authorization, Bearer, Headers};
|
||||||
|
|
||||||
|
use entities::prelude::*;
|
||||||
|
use status_builder::StatusBuilder;
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
macro_rules! methods {
|
||||||
|
($($method:ident,)+) => {
|
||||||
|
$(
|
||||||
|
fn $method<T: serde::Deserialize>(&self, url: String)
|
||||||
|
-> Result<T>
|
||||||
|
{
|
||||||
|
Ok(self.client.$method(&url)
|
||||||
|
.headers(self.access_token.clone().unwrap())
|
||||||
|
.send()?
|
||||||
|
.json()?)
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! route {
|
||||||
|
|
||||||
|
((post ($($param:ident: $typ:ty,)*)) $name:ident: $url:expr => $ret:ty, $($rest:tt)*) => {
|
||||||
|
/// Requires `access_token` or will return error.
|
||||||
|
pub fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
|
||||||
|
self.has_access_token()?;
|
||||||
|
|
||||||
|
let form_data = json!({
|
||||||
|
$(
|
||||||
|
stringify!($param): $param,
|
||||||
|
)*
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(self.client.post(&self.route(concat!("/api/v1/", $url)))
|
||||||
|
.headers(self.access_token.clone().unwrap())
|
||||||
|
.form(&form_data)
|
||||||
|
.send()?
|
||||||
|
.json()?)
|
||||||
|
}
|
||||||
|
route!{$($rest)*}
|
||||||
|
};
|
||||||
|
|
||||||
|
(($method:ident) $name:ident: $url:expr => $ret:ty, $($rest:tt)*) => {
|
||||||
|
/// Requires `access_token` or will return error.
|
||||||
|
pub fn $name(&self) -> Result<$ret> {
|
||||||
|
self.has_access_token()?;
|
||||||
|
|
||||||
|
self.$method(self.route(concat!("/api/v1/", $url)))
|
||||||
|
}
|
||||||
|
|
||||||
|
route!{$($rest)*}
|
||||||
|
};
|
||||||
|
|
||||||
|
() => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! route_id {
|
||||||
|
|
||||||
|
($(($method:ident) $name:ident: $url:expr => $ret:ty,)*) => {
|
||||||
|
$(
|
||||||
|
/// Requires `access_token` or will return error.
|
||||||
|
pub fn $name(&self, id: u64) -> Result<$ret> {
|
||||||
|
self.has_access_token()?;
|
||||||
|
|
||||||
|
|
||||||
|
self.$method(self.route(&format!(concat!("/api/v1/", $url), id)))
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Mastodon {
|
||||||
|
base_url: String,
|
||||||
|
client: Client,
|
||||||
|
client_id: Option<String>,
|
||||||
|
client_secret: Option<String>,
|
||||||
|
redirect_uri: Option<String>,
|
||||||
|
access_token: Option<Headers>,
|
||||||
|
id: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct OAuth {
|
||||||
|
client_id: String,
|
||||||
|
client_secret: String,
|
||||||
|
id: u64,
|
||||||
|
redirect_uri: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Serde(SerdeError),
|
||||||
|
Http(HttpError),
|
||||||
|
ClientIdRequired,
|
||||||
|
ClientSecretRequired,
|
||||||
|
AccessTokenRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mastodon {
|
||||||
|
/// Inits new Mastodon object. `base_url` is expected in the following
|
||||||
|
/// format `https://mastodon.social` with no leading forward slash.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use mammut::Mastodon;
|
||||||
|
///
|
||||||
|
/// let mastodon = Mastodon::new("https://mastodon.social").unwrap();
|
||||||
|
/// ```
|
||||||
|
pub fn new<I: Into<String>>(base_url: I) -> Result<Self> {
|
||||||
|
Ok(Mastodon {
|
||||||
|
base_url: base_url.into(),
|
||||||
|
client: Client::new()?,
|
||||||
|
client_id: None,
|
||||||
|
client_secret: None,
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set `access_token` required to use any method about the user.
|
||||||
|
pub fn set_access_token(&mut self, access_token: String) {
|
||||||
|
let mut headers = Headers::new();
|
||||||
|
|
||||||
|
headers.set(Authorization(Bearer { token: access_token }));
|
||||||
|
|
||||||
|
self.access_token = Some(headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
route! {
|
||||||
|
(get) verify: "accounts/verify_credentials" => Account,
|
||||||
|
(get) blocks: "blocks" => Vec<Account>,
|
||||||
|
(get) follow_requests: "follow_requests" => Vec<Account>,
|
||||||
|
(get) mutes: "mutes" => Vec<Account>,
|
||||||
|
(get) notifications: "notifications" => Vec<Notification>,
|
||||||
|
(get) reports: "reports" => Vec<Report>,
|
||||||
|
(get) get_home_timeline: "timelines/home" => Vec<Status>,
|
||||||
|
(post (id: u64,)) allow_follow_request: "accounts/follow_requests/authorize" => Empty,
|
||||||
|
(post (id: u64,)) reject_follow_request: "accounts/follow_requests/reject" => Empty,
|
||||||
|
(post (uri: String,)) follows: "follows" => Account,
|
||||||
|
(post) clear_notifications: "notifications/clear" => Empty,
|
||||||
|
(post (file: Vec<u8>,)) media: "media" => Attachment,
|
||||||
|
(post (account_id: u64, status_ids: Vec<u64>, comment: String,)) report:
|
||||||
|
"reports" => Report,
|
||||||
|
(post (q: String, resolve: bool,)) search: "search" => SearchResult,
|
||||||
|
(post (status: StatusBuilder,)) new_status: "statuses" => Status,
|
||||||
|
}
|
||||||
|
|
||||||
|
route_id! {
|
||||||
|
(get) get_account: "accounts/{}" => Account,
|
||||||
|
(get) followers: "accounts/{}/followers" => Vec<Account>,
|
||||||
|
(get) following: "accounts/{}/following" => Vec<Account>,
|
||||||
|
(get) follow: "accounts/{}/follow" => Account,
|
||||||
|
(get) unfollow: "accounts/{}/unfollow" => Account,
|
||||||
|
(get) block: "accounts/{}/block" => Account,
|
||||||
|
(get) unblock: "accounts/{}/unblock" => Account,
|
||||||
|
(get) mute: "accounts/{}/mute" => Account,
|
||||||
|
(get) unmute: "accounts/{}/unmute" => Account,
|
||||||
|
(get) get_notification: "notifications/{}" => Notification,
|
||||||
|
(get) get_status: "statuses/{}" => Status,
|
||||||
|
(get) get_context: "statuses/{}/context" => Context,
|
||||||
|
(get) get_card: "statuses/{}/card" => Card,
|
||||||
|
(get) reblogged_by: "statuses/{}/reblogged_by" => Vec<Account>,
|
||||||
|
(get) favourited_by: "statuses/{}/favourited_by" => Vec<Account>,
|
||||||
|
(post) reblog: "statuses/{}/reblog" => Status,
|
||||||
|
(post) unreblog: "statuses/{}/unreblog" => Status,
|
||||||
|
(post) favourite: "statuses/{}/favourite" => Status,
|
||||||
|
(post) unfavourite: "statuses/{}/unfavourite" => Status,
|
||||||
|
(delete) delete_status: "statuses/{}" => Empty,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_public_timeline(&self, local: bool) -> Result<Vec<Status>> {
|
||||||
|
self.has_access_token()?;
|
||||||
|
|
||||||
|
let mut url = self.route("/api/v1/timelines/public");
|
||||||
|
|
||||||
|
if local {
|
||||||
|
url += "?local=1";
|
||||||
|
}
|
||||||
|
|
||||||
|
self.get(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
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/");
|
||||||
|
url += &hashtag;
|
||||||
|
|
||||||
|
if local {
|
||||||
|
url += "?local=1";
|
||||||
|
}
|
||||||
|
|
||||||
|
self.get(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn statuses(&self, id: u64, only_media: bool, exclude_replies: bool)
|
||||||
|
-> Result<Vec<Status>>
|
||||||
|
{
|
||||||
|
self.has_access_token()?;
|
||||||
|
let mut url = format!("{}/api/v1/accounts/{}/statuses", self.base_url, id);
|
||||||
|
|
||||||
|
if only_media {
|
||||||
|
url += "?only_media=1";
|
||||||
|
}
|
||||||
|
|
||||||
|
if exclude_replies {
|
||||||
|
url += if only_media {
|
||||||
|
"&"
|
||||||
|
} else {
|
||||||
|
"?"
|
||||||
|
};
|
||||||
|
|
||||||
|
url += "exclude_replies=1";
|
||||||
|
}
|
||||||
|
|
||||||
|
self.get(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn relationships(&self, ids: &[u64]) -> Result<Vec<Relationship>> {
|
||||||
|
self.has_access_token()?;
|
||||||
|
|
||||||
|
let mut url = self.route("/api/v1/accounts/relationships?");
|
||||||
|
|
||||||
|
if ids.len() == 1 {
|
||||||
|
url += "id=";
|
||||||
|
url += &ids[0].to_string();
|
||||||
|
} else {
|
||||||
|
for id in ids {
|
||||||
|
url += "id[]=";
|
||||||
|
url += &id.to_string();
|
||||||
|
url += "&";
|
||||||
|
}
|
||||||
|
url.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.get(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add a limit fn
|
||||||
|
pub fn search_accounts(&self, query: &str) -> Result<Vec<Account>> {
|
||||||
|
self.has_access_token()?;
|
||||||
|
self.get(format!("{}/api/v1/accounts/search?q={}", self.base_url, query))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instance(&self) -> Result<Instance> {
|
||||||
|
self.is_registered()?;
|
||||||
|
|
||||||
|
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,];
|
||||||
|
|
||||||
|
fn route(&self, url: &str) -> String {
|
||||||
|
let mut s = self.base_url.clone();
|
||||||
|
s += url;
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
macro_rules! from {
|
||||||
|
($($typ:ident, $variant:ident,)*) => {
|
||||||
|
$(
|
||||||
|
impl From<$typ> for Error {
|
||||||
|
fn from(from: $typ) -> Self {
|
||||||
|
use Error::*;
|
||||||
|
$variant(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from! {
|
||||||
|
SerdeError, Serde,
|
||||||
|
HttpError, Http,
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#[derive(Debug, Default, Clone, Serialize)]
|
||||||
|
pub struct StatusBuilder {
|
||||||
|
status: String,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
|
in_reply_to_id: Option<u64>,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
|
media_ids: Option<Vec<u64>>,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
|
sensitive: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
|
spoiler_text: Option<String>,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
|
visibility: Option<Visibility>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||||
|
pub enum Visibility {
|
||||||
|
#[serde(rename = "direct")]
|
||||||
|
Direct,
|
||||||
|
#[serde(rename = "private")]
|
||||||
|
Private,
|
||||||
|
#[serde(rename = "unlisted")]
|
||||||
|
Unlisted,
|
||||||
|
#[serde(rename = "public")]
|
||||||
|
Public,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatusBuilder {
|
||||||
|
|
||||||
|
pub fn new(status: String) -> Self {
|
||||||
|
StatusBuilder {
|
||||||
|
status: status,
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Visibility {
|
||||||
|
fn default() -> Self {
|
||||||
|
Visibility::Public
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user