forked from MightyPork/elefren-fork
Keyword/filtering API
This adds the 5 methods for the mastodon API that deal with keyword filtering: GET /api/v1/filters POST /api/v1/filters GET /api/v1/filters/:id PUT /api/v1/filters/:id DELETE /api/v1/filters/:id Closes #71
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
/// Represents a single Filter
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Filter {
|
||||||
|
id: String,
|
||||||
|
phrase: String,
|
||||||
|
context: Vec<FilterContext>,
|
||||||
|
expires_at: Option<String>, // TODO: timestamp
|
||||||
|
irreversible: bool,
|
||||||
|
whole_word: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the various types of Filter contexts
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum FilterContext {
|
||||||
|
/// Represents the "home" context
|
||||||
|
#[serde(rename = "home")]
|
||||||
|
Home,
|
||||||
|
/// Represents the "notifications" context
|
||||||
|
#[serde(rename = "notifications")]
|
||||||
|
Notifications,
|
||||||
|
/// Represents the "public" context
|
||||||
|
#[serde(rename = "public")]
|
||||||
|
Public,
|
||||||
|
/// Represents the "thread" context
|
||||||
|
#[serde(rename = "thread")]
|
||||||
|
Thread,
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ pub mod attachment;
|
|||||||
pub mod card;
|
pub mod card;
|
||||||
/// Data structures for ser/de of contetx-related resources
|
/// Data structures for ser/de of contetx-related resources
|
||||||
pub mod context;
|
pub mod context;
|
||||||
|
/// Data structures for ser/de of filter-related resources
|
||||||
|
pub mod filter;
|
||||||
/// Data structures for ser/de of instance-related resources
|
/// Data structures for ser/de of instance-related resources
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub(crate) mod itemsiter;
|
pub(crate) mod itemsiter;
|
||||||
@@ -39,6 +41,7 @@ pub mod prelude {
|
|||||||
attachment::{Attachment, MediaType},
|
attachment::{Attachment, MediaType},
|
||||||
card::Card,
|
card::Card,
|
||||||
context::Context,
|
context::Context,
|
||||||
|
filter::{Filter, FilterContext},
|
||||||
instance::*,
|
instance::*,
|
||||||
list::List,
|
list::List,
|
||||||
mention::Mention,
|
mention::Mention,
|
||||||
|
|||||||
+51
-21
@@ -89,7 +89,13 @@ pub use errors::{ApiError, Error, Result};
|
|||||||
pub use isolang::Language;
|
pub use isolang::Language;
|
||||||
pub use mastodon_client::MastodonClient;
|
pub use mastodon_client::MastodonClient;
|
||||||
pub use registration::Registration;
|
pub use registration::Registration;
|
||||||
pub use requests::{AddPushRequest, StatusesRequest, UpdateCredsRequest, UpdatePushRequest};
|
pub use requests::{
|
||||||
|
AddFilterRequest,
|
||||||
|
AddPushRequest,
|
||||||
|
StatusesRequest,
|
||||||
|
UpdateCredsRequest,
|
||||||
|
UpdatePushRequest,
|
||||||
|
};
|
||||||
pub use status_builder::StatusBuilder;
|
pub use status_builder::StatusBuilder;
|
||||||
|
|
||||||
/// Registering your App
|
/// Registering your App
|
||||||
@@ -193,11 +199,13 @@ impl<H: HttpSend> MastodonClient<H> for Mastodon<H> {
|
|||||||
(post (id: &str,)) authorize_follow_request: "accounts/follow_requests/authorize" => Empty,
|
(post (id: &str,)) authorize_follow_request: "accounts/follow_requests/authorize" => Empty,
|
||||||
(post (id: &str,)) reject_follow_request: "accounts/follow_requests/reject" => Empty,
|
(post (id: &str,)) reject_follow_request: "accounts/follow_requests/reject" => Empty,
|
||||||
(get (q: &'a str, resolve: bool,)) search: "search" => SearchResult,
|
(get (q: &'a str, resolve: bool,)) search: "search" => SearchResult,
|
||||||
|
(get (local: bool,)) get_public_timeline: "timelines/public" => Vec<Status>,
|
||||||
(post (uri: Cow<'static, str>,)) follows: "follows" => Account,
|
(post (uri: Cow<'static, str>,)) follows: "follows" => Account,
|
||||||
(post multipart (file: Cow<'static, str>,)) media: "media" => Attachment,
|
(post multipart (file: Cow<'static, str>,)) media: "media" => Attachment,
|
||||||
(post) clear_notifications: "notifications/clear" => Empty,
|
(post) clear_notifications: "notifications/clear" => Empty,
|
||||||
(get) get_push_subscription: "push/subscription" => Subscription,
|
(get) get_push_subscription: "push/subscription" => Subscription,
|
||||||
(delete) delete_push_subscription: "push/subscription" => Empty,
|
(delete) delete_push_subscription: "push/subscription" => Empty,
|
||||||
|
(get) get_filters: "filters" => Vec<Filter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
route_v2! {
|
route_v2! {
|
||||||
@@ -221,6 +229,39 @@ impl<H: HttpSend> MastodonClient<H> for Mastodon<H> {
|
|||||||
(post) favourite: "statuses/{}/favourite" => Status,
|
(post) favourite: "statuses/{}/favourite" => Status,
|
||||||
(post) unfavourite: "statuses/{}/unfavourite" => Status,
|
(post) unfavourite: "statuses/{}/unfavourite" => Status,
|
||||||
(delete) delete_status: "statuses/{}" => Empty,
|
(delete) delete_status: "statuses/{}" => Empty,
|
||||||
|
(get) get_filter: "filters/{}" => Filter,
|
||||||
|
(delete) delete_filter: "filters/{}" => Empty,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_filter(&self, request: &mut AddFilterRequest) -> Result<Filter> {
|
||||||
|
let url = self.route("/api/v1/filters");
|
||||||
|
let response = self.send(self.client.post(&url).json(&request))?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
|
||||||
|
if status.is_client_error() {
|
||||||
|
return Err(Error::Client(status.clone()));
|
||||||
|
} else if status.is_server_error() {
|
||||||
|
return Err(Error::Server(status.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialise(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PUT /api/v1/filters/:id
|
||||||
|
fn update_filter(&self, id: u64, request: &mut AddFilterRequest) -> Result<Filter> {
|
||||||
|
let url = self.route(&format!("/api/v1/filters/{}", id));
|
||||||
|
let response = self.send(self.client.put(&url).json(&request))?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
|
||||||
|
if status.is_client_error() {
|
||||||
|
return Err(Error::Client(status.clone()));
|
||||||
|
} else if status.is_server_error() {
|
||||||
|
return Err(Error::Server(status.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialise(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_credentials(&self, builder: &mut UpdateCredsRequest) -> Result<Account> {
|
fn update_credentials(&self, builder: &mut UpdateCredsRequest) -> Result<Account> {
|
||||||
@@ -228,12 +269,12 @@ impl<H: HttpSend> MastodonClient<H> for Mastodon<H> {
|
|||||||
let url = self.route("/api/v1/accounts/update_credentials");
|
let url = self.route("/api/v1/accounts/update_credentials");
|
||||||
let response = self.send(self.client.patch(&url).json(&changes))?;
|
let response = self.send(self.client.patch(&url).json(&changes))?;
|
||||||
|
|
||||||
let status = response.status().clone();
|
let status = response.status();
|
||||||
|
|
||||||
if status.is_client_error() {
|
if status.is_client_error() {
|
||||||
return Err(Error::Client(status));
|
return Err(Error::Client(status.clone()));
|
||||||
} else if status.is_server_error() {
|
} else if status.is_server_error() {
|
||||||
return Err(Error::Server(status));
|
return Err(Error::Server(status.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialise(response)
|
deserialise(response)
|
||||||
@@ -250,26 +291,15 @@ impl<H: HttpSend> MastodonClient<H> for Mastodon<H> {
|
|||||||
deserialise(response)
|
deserialise(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the federated timeline for the instance.
|
|
||||||
fn get_public_timeline(&self, local: bool) -> Result<Vec<Status>> {
|
|
||||||
let mut url = self.route("/api/v1/timelines/public");
|
|
||||||
|
|
||||||
if local {
|
|
||||||
url += "?local=1";
|
|
||||||
}
|
|
||||||
|
|
||||||
self.get(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get timeline filtered by a hashtag(eg. `#coffee`) either locally or
|
/// Get timeline filtered by a hashtag(eg. `#coffee`) either locally or
|
||||||
/// federated.
|
/// federated.
|
||||||
fn get_tagged_timeline(&self, hashtag: String, local: bool) -> Result<Vec<Status>> {
|
fn get_tagged_timeline(&self, hashtag: String, local: bool) -> Result<Vec<Status>> {
|
||||||
let mut url = self.route("/api/v1/timelines/tag/");
|
let base = "/api/v1/timelines/tag/";
|
||||||
url += &hashtag;
|
let url = if local {
|
||||||
|
self.route(&format!("{}{}?local=1", base, hashtag))
|
||||||
if local {
|
} else {
|
||||||
url += "?local=1";
|
self.route(&format!("{}{}", base, hashtag))
|
||||||
}
|
};
|
||||||
|
|
||||||
self.get(url)
|
self.get(url)
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-1
@@ -4,7 +4,13 @@ use entities::prelude::*;
|
|||||||
use errors::Result;
|
use errors::Result;
|
||||||
use http_send::{HttpSend, HttpSender};
|
use http_send::{HttpSend, HttpSender};
|
||||||
use page::Page;
|
use page::Page;
|
||||||
use requests::{AddPushRequest, StatusesRequest, UpdateCredsRequest, UpdatePushRequest};
|
use requests::{
|
||||||
|
AddFilterRequest,
|
||||||
|
AddPushRequest,
|
||||||
|
StatusesRequest,
|
||||||
|
UpdateCredsRequest,
|
||||||
|
UpdatePushRequest,
|
||||||
|
};
|
||||||
use status_builder::StatusBuilder;
|
use status_builder::StatusBuilder;
|
||||||
|
|
||||||
/// Represents the set of methods that a Mastodon Client can do, so that
|
/// Represents the set of methods that a Mastodon Client can do, so that
|
||||||
@@ -227,4 +233,24 @@ pub trait MastodonClient<H: HttpSend = HttpSender> {
|
|||||||
fn delete_push_subscription(&self) -> Result<Empty> {
|
fn delete_push_subscription(&self) -> Result<Empty> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
|
/// GET /api/v1/filters
|
||||||
|
fn get_filters(&self) -> Result<Vec<Filter>> {
|
||||||
|
unimplemented!("This method was not implemented");
|
||||||
|
}
|
||||||
|
/// POST /api/v1/filters
|
||||||
|
fn add_filter(&self, request: &mut AddFilterRequest) -> Result<Filter> {
|
||||||
|
unimplemented!("This method was not implemented");
|
||||||
|
}
|
||||||
|
/// GET /api/v1/filters/:id
|
||||||
|
fn get_filter(&self, id: u64) -> Result<Filter> {
|
||||||
|
unimplemented!("This method was not implemented");
|
||||||
|
}
|
||||||
|
/// PUT /api/v1/filters/:id
|
||||||
|
fn update_filter(&self, id: u64, request: &mut AddFilterRequest) -> Result<Filter> {
|
||||||
|
unimplemented!("This method was not implemented");
|
||||||
|
}
|
||||||
|
/// DELETE /api/v1/filters/:id
|
||||||
|
fn delete_filter(&self, id: u64) -> Result<Empty> {
|
||||||
|
unimplemented!("This method was not implemented");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
use entities::filter::FilterContext;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
/// Form used to create a filter
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # extern crate elefren;
|
||||||
|
/// # use std::error::Error;
|
||||||
|
/// use elefren::{entities::filter::FilterContext, requests::AddFilterRequest};
|
||||||
|
/// # fn main() -> Result<(), Box<Error>> {
|
||||||
|
/// let request = AddFilterRequest::new("foo", FilterContext::Home);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||||
|
pub struct AddFilterRequest {
|
||||||
|
phrase: String,
|
||||||
|
context: FilterContext,
|
||||||
|
irreversible: Option<bool>,
|
||||||
|
whole_word: Option<bool>,
|
||||||
|
#[serde(serialize_with = "serialize_duration::ser")]
|
||||||
|
expires_in: Option<Duration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddFilterRequest {
|
||||||
|
/// Create a new AddFilterRequest
|
||||||
|
pub fn new(phrase: &str, context: FilterContext) -> AddFilterRequest {
|
||||||
|
AddFilterRequest {
|
||||||
|
phrase: phrase.to_string(),
|
||||||
|
context,
|
||||||
|
irreversible: None,
|
||||||
|
whole_word: None,
|
||||||
|
expires_in: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set `irreversible` to `true`
|
||||||
|
pub fn irreversible(&mut self) -> &mut Self {
|
||||||
|
self.irreversible = Some(true);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set `whole_word` to `true`
|
||||||
|
pub fn whole_word(&mut self) -> &mut Self {
|
||||||
|
self.whole_word = Some(true);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set `expires_in` to a duration
|
||||||
|
pub fn expires_in(&mut self, d: Duration) -> &mut Self {
|
||||||
|
self.expires_in = Some(d);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod serialize_duration {
|
||||||
|
use serde::ser::Serializer;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub(crate) fn ser<S>(duration: &Option<Duration>, s: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
if let Some(d) = duration {
|
||||||
|
let sec = d.as_secs();
|
||||||
|
s.serialize_u64(sec)
|
||||||
|
} else {
|
||||||
|
s.serialize_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use serde_json;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new() {
|
||||||
|
let request = AddFilterRequest::new("foo", FilterContext::Home);
|
||||||
|
assert_eq!(
|
||||||
|
request,
|
||||||
|
AddFilterRequest {
|
||||||
|
phrase: "foo".to_string(),
|
||||||
|
context: FilterContext::Home,
|
||||||
|
irreversible: None,
|
||||||
|
whole_word: None,
|
||||||
|
expires_in: None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_irreversible() {
|
||||||
|
let mut request = AddFilterRequest::new("foo", FilterContext::Home);
|
||||||
|
request.irreversible();
|
||||||
|
assert_eq!(
|
||||||
|
request,
|
||||||
|
AddFilterRequest {
|
||||||
|
phrase: "foo".to_string(),
|
||||||
|
context: FilterContext::Home,
|
||||||
|
irreversible: Some(true),
|
||||||
|
whole_word: None,
|
||||||
|
expires_in: None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_whole_word() {
|
||||||
|
let mut request = AddFilterRequest::new("foo", FilterContext::Home);
|
||||||
|
request.whole_word();
|
||||||
|
assert_eq!(
|
||||||
|
request,
|
||||||
|
AddFilterRequest {
|
||||||
|
phrase: "foo".to_string(),
|
||||||
|
context: FilterContext::Home,
|
||||||
|
irreversible: None,
|
||||||
|
whole_word: Some(true),
|
||||||
|
expires_in: None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expires_in() {
|
||||||
|
let mut request = AddFilterRequest::new("foo", FilterContext::Home);
|
||||||
|
request.expires_in(Duration::from_secs(300));
|
||||||
|
assert_eq!(
|
||||||
|
request,
|
||||||
|
AddFilterRequest {
|
||||||
|
phrase: "foo".to_string(),
|
||||||
|
context: FilterContext::Home,
|
||||||
|
irreversible: None,
|
||||||
|
whole_word: None,
|
||||||
|
expires_in: Some(Duration::from_secs(300)),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serialize_request() {
|
||||||
|
let mut request = AddFilterRequest::new("foo", FilterContext::Home);
|
||||||
|
request.expires_in(Duration::from_secs(300));
|
||||||
|
let ser = serde_json::to_string(&request).expect("Couldn't serialize");
|
||||||
|
assert_eq!(
|
||||||
|
ser,
|
||||||
|
r#"{"phrase":"foo","context":"home","irreversible":null,"whole_word":null,"expires_in":300}"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
/// Data structure for the MastodonClient::add_filter method
|
||||||
|
pub use self::filter::AddFilterRequest;
|
||||||
/// Data structure for the MastodonClient::add_push_subscription method
|
/// Data structure for the MastodonClient::add_push_subscription method
|
||||||
pub use self::push::{AddPushRequest, Keys, UpdatePushRequest};
|
pub use self::push::{AddPushRequest, Keys, UpdatePushRequest};
|
||||||
/// Data structure for the MastodonClient::statuses method
|
/// Data structure for the MastodonClient::statuses method
|
||||||
@@ -5,6 +7,7 @@ pub use self::statuses::StatusesRequest;
|
|||||||
/// Data structure for the MastodonClient::update_credentials method
|
/// Data structure for the MastodonClient::update_credentials method
|
||||||
pub use self::update_credentials::UpdateCredsRequest;
|
pub use self::update_credentials::UpdateCredsRequest;
|
||||||
|
|
||||||
|
mod filter;
|
||||||
mod push;
|
mod push;
|
||||||
mod statuses;
|
mod statuses;
|
||||||
mod update_credentials;
|
mod update_credentials;
|
||||||
|
|||||||
Reference in New Issue
Block a user