megalodon/pixelfed/
pixelfed.rs

1use std::collections::HashMap;
2
3use super::api_client::APIClient;
4use super::entities;
5use super::oauth;
6use super::web_socket::WebSocket;
7use crate::megalodon::FollowRequestOutput;
8use crate::{
9    default, entities as MegalodonEntities, error::Error, megalodon, oauth as MegalodonOAuth,
10    response::Response,
11};
12use crate::{error, Streaming};
13use rand::RngCore;
14
15use async_trait::async_trait;
16use chrono::{DateTime, Utc};
17use oauth2::basic::BasicClient;
18use oauth2::{
19    AuthUrl, ClientId, ClientSecret, CsrfToken, RedirectUrl, ResponseType, Scope, TokenUrl,
20};
21use serde_json::Value;
22use sha1::{Digest, Sha1};
23use std::ops::Sub;
24use tokio::{fs::File, io::AsyncRead};
25use tokio_util::codec::{BytesCodec, FramedRead};
26
27/// Pixelfed API Client which satisfies megalodon trait.
28#[derive(Debug, Clone)]
29pub struct Pixelfed {
30    client: APIClient,
31    base_url: String,
32}
33
34impl Pixelfed {
35    /// Create a new [`Pixelfed`].
36    pub fn new(
37        base_url: String,
38        access_token: Option<String>,
39        user_agent: Option<String>,
40    ) -> Result<Pixelfed, Error> {
41        let client = APIClient::new(base_url.clone(), access_token.clone(), user_agent.clone())?;
42        Ok(Self { client, base_url })
43    }
44
45    async fn generate_auth_url(
46        &self,
47        client_id: String,
48        client_secret: String,
49        scope: Vec<&str>,
50        redirect_uri: String,
51    ) -> Result<String, Error> {
52        let client = BasicClient::new(
53            ClientId::new(client_id),
54            Some(ClientSecret::new(client_secret)),
55            AuthUrl::new(format!("{}{}", self.base_url, "/oauth/authorize").to_string())?,
56            Some(TokenUrl::new(
57                format!("{}{}", self.base_url, "/oauth/token").to_string(),
58            )?),
59        )
60        .set_redirect_uri(RedirectUrl::new(redirect_uri)?);
61
62        let scopes: Vec<Scope> = scope.iter().map(|s| Scope::new(s.to_string())).collect();
63
64        let (auth_url, _) = client
65            .authorize_url(CsrfToken::new_random)
66            .add_scopes(scopes)
67            .set_response_type(&ResponseType::new("code".to_string()))
68            .url();
69        Ok(auth_url.to_string())
70    }
71}
72
73#[async_trait]
74impl megalodon::Megalodon for Pixelfed {
75    async fn register_app(
76        &self,
77        client_name: String,
78        options: &megalodon::AppInputOptions,
79    ) -> Result<MegalodonOAuth::AppData, Error> {
80        let mut scope = default::DEFAULT_SCOPES.to_vec();
81        if let Some(scopes) = &options.scopes {
82            scope = scopes.iter().map(|s| s.as_ref()).collect();
83        }
84
85        let mut app = self.create_app(client_name, options).await?;
86        let url = self
87            .generate_auth_url(
88                app.client_id.clone(),
89                app.client_secret.clone(),
90                scope,
91                app.redirect_uri.clone().unwrap(),
92            )
93            .await?;
94        app.url = Some(url);
95        Ok(app)
96    }
97
98    async fn create_app(
99        &self,
100        client_name: String,
101        options: &megalodon::AppInputOptions,
102    ) -> Result<MegalodonOAuth::AppData, Error> {
103        let mut scope = default::DEFAULT_SCOPES.to_vec();
104        if let Some(scopes) = &options.scopes {
105            scope = scopes.iter().map(|s| s.as_ref()).collect();
106        }
107        let mut redirect_uris = default::NO_REDIRECT;
108        if let Some(uris) = &options.redirect_uris {
109            redirect_uris = uris.as_ref();
110        }
111
112        let mut params = HashMap::<&str, Value>::new();
113        params.insert("client_name", serde_json::Value::String(client_name));
114        params.insert(
115            "redirect_uris",
116            serde_json::Value::String(redirect_uris.to_string()),
117        );
118        params.insert("scopes", serde_json::Value::String(scope.join(" ")));
119        if let Some(website) = &options.website {
120            params.insert("website", serde_json::Value::String(website.clone()));
121        }
122
123        let res = self
124            .client
125            .post::<oauth::AppDataFromServer>("/api/v1/apps", &params, None)
126            .await?;
127        Ok(res.json.into())
128    }
129
130    async fn fetch_access_token(
131        &self,
132        client_id: String,
133        client_secret: String,
134        code: String,
135        redirect_uri: String,
136    ) -> Result<MegalodonOAuth::TokenData, Error> {
137        let mut params = HashMap::<&str, Value>::new();
138        params.insert("client_id", serde_json::Value::String(client_id));
139        params.insert("client_secret", serde_json::Value::String(client_secret));
140        params.insert("code", serde_json::Value::String(code));
141        params.insert("redirect_uri", serde_json::Value::String(redirect_uri));
142        params.insert(
143            "grant_type",
144            serde_json::Value::String("authorization_code".to_string()),
145        );
146
147        let res = self
148            .client
149            .post::<oauth::TokenDataFromServer>("/oauth/token", &params, None)
150            .await?;
151        Ok(res.json.into())
152    }
153
154    async fn refresh_access_token(
155        &self,
156        client_id: String,
157        client_secret: String,
158        refresh_token: String,
159    ) -> Result<MegalodonOAuth::TokenData, Error> {
160        let mut params = HashMap::<&str, Value>::new();
161        params.insert("client_id", serde_json::Value::String(client_id));
162        params.insert("client_secret", serde_json::Value::String(client_secret));
163        params.insert("refresh_token", serde_json::Value::String(refresh_token));
164        params.insert(
165            "grant_type",
166            serde_json::Value::String("authorization_code".to_string()),
167        );
168
169        let res = self
170            .client
171            .post::<oauth::TokenDataFromServer>("/oauth/token", &params, None)
172            .await?;
173        Ok(res.json.into())
174    }
175
176    async fn revoke_access_token(
177        &self,
178        client_id: String,
179        client_secret: String,
180        access_token: String,
181    ) -> Result<Response<()>, Error> {
182        let mut params = HashMap::<&str, Value>::new();
183        params.insert("client_id", serde_json::Value::String(client_id));
184        params.insert("client_secret", serde_json::Value::String(client_secret));
185        params.insert("token", serde_json::Value::String(access_token));
186
187        let res = self
188            .client
189            .post::<()>("/oauth/revoke", &params, None)
190            .await?;
191        Ok(res)
192    }
193
194    async fn verify_app_credentials(
195        &self,
196    ) -> Result<Response<MegalodonEntities::Application>, Error> {
197        let res = self
198            .client
199            .get::<entities::Application>("/api/v1/apps/verify_credentials", None)
200            .await?;
201
202        Ok(Response::<MegalodonEntities::Application>::new(
203            res.json.into(),
204            res.status,
205            res.status_text,
206            res.header,
207        ))
208    }
209
210    async fn register_account(
211        &self,
212        username: String,
213        email: String,
214        password: String,
215        agreement: String,
216        locale: String,
217        reason: Option<String>,
218    ) -> Result<Response<MegalodonEntities::Token>, Error> {
219        let mut params = HashMap::<&str, Value>::from([
220            ("username", serde_json::Value::String(username)),
221            ("email", serde_json::Value::String(email)),
222            ("password", serde_json::Value::String(password)),
223            ("agreement", serde_json::Value::String(agreement)),
224            ("locale", serde_json::Value::String(locale)),
225        ]);
226        if let Some(reason) = reason {
227            params.insert("reason", serde_json::Value::String(reason));
228        }
229
230        let res = self
231            .client
232            .post::<entities::Token>("/api/v1/accounts", &params, None)
233            .await?;
234
235        Ok(Response::<MegalodonEntities::Token>::new(
236            res.json.into(),
237            res.status,
238            res.status_text,
239            res.header,
240        ))
241    }
242
243    async fn verify_account_credentials(
244        &self,
245    ) -> Result<Response<MegalodonEntities::Account>, Error> {
246        let res = self
247            .client
248            .get::<entities::Account>("/api/v1/accounts/verify_credentials", None)
249            .await?;
250        Ok(Response::<MegalodonEntities::Account>::new(
251            res.json.into(),
252            res.status,
253            res.status_text,
254            res.header,
255        ))
256    }
257
258    async fn update_credentials(
259        &self,
260        options: Option<&megalodon::UpdateCredentialsInputOptions>,
261    ) -> Result<Response<MegalodonEntities::Account>, Error> {
262        let mut params = HashMap::<&str, Value>::new();
263        if let Some(options) = options {
264            if let Some(discoverable) = options.discoverable {
265                params.insert(
266                    "discoverable",
267                    serde_json::Value::String(discoverable.to_string()),
268                );
269            }
270            if let Some(bot) = options.bot {
271                params.insert("bot", serde_json::Value::String(bot.to_string()));
272            }
273            if let Some(display_name) = &options.display_name {
274                params.insert(
275                    "display_name",
276                    serde_json::Value::String(display_name.clone()),
277                );
278            }
279            if let Some(note) = &options.note {
280                params.insert("note", serde_json::Value::String(note.clone()));
281            }
282            if let Some(avatar) = &options.avatar {
283                params.insert("avatar", serde_json::Value::String(avatar.clone()));
284            }
285            if let Some(header) = &options.header {
286                params.insert("header", serde_json::Value::String(header.clone()));
287            }
288            if let Some(locked) = options.locked {
289                params.insert("locked", serde_json::Value::String(locked.to_string()));
290            }
291            if let Some(source) = &options.source {
292                if let Some(json_source) = serde_json::to_value(&source).ok() {
293                    params.insert("source", json_source);
294                }
295            }
296            if let Some(fields_attributes) = &options.fields_attributes {
297                let json_fields_attributes = serde_json::map::Map::from_iter(
298                    fields_attributes
299                        .iter()
300                        .enumerate()
301                        .map(|(x, y)| (x.to_string(), serde_json::to_value(y).ok().into())),
302                );
303
304                if let Ok(json_fields_attributes) = serde_json::to_value(json_fields_attributes) {
305                    params.insert("fields_attributes", json_fields_attributes);
306                }
307            }
308        }
309
310        let res = self
311            .client
312            .patch::<entities::Account>("/api/v1/accounts/update_credentials", &params, None)
313            .await?;
314
315        Ok(Response::<MegalodonEntities::Account>::new(
316            res.json.into(),
317            res.status,
318            res.status_text,
319            res.header,
320        ))
321    }
322
323    async fn get_account(&self, id: String) -> Result<Response<MegalodonEntities::Account>, Error> {
324        let res = self
325            .client
326            .get::<entities::Account>(format!("/api/v1/accounts/{}", id).as_str(), None)
327            .await?;
328
329        Ok(Response::<MegalodonEntities::Account>::new(
330            res.json.into(),
331            res.status,
332            res.status_text,
333            res.header,
334        ))
335    }
336
337    async fn get_account_statuses(
338        &self,
339        id: String,
340        options: Option<&megalodon::GetAccountStatusesInputOptions>,
341    ) -> Result<Response<Vec<MegalodonEntities::Status>>, Error> {
342        let mut params = Vec::<String>::new();
343        if let Some(options) = options {
344            if let Some(limit) = options.limit {
345                params.push(format!("limit={}", limit));
346            }
347            if let Some(max_id) = &options.max_id {
348                params.push(format!("max_id={}", max_id));
349            }
350            if let Some(since_id) = &options.since_id {
351                params.push(format!("since_id={}", since_id));
352            }
353            if let Some(pinned) = options.pinned {
354                params.push(format!("pinned={}", pinned));
355            }
356            if let Some(exclude_replies) = options.exclude_replies {
357                params.push(format!("exclude_replies={}", exclude_replies));
358            }
359            if let Some(exclude_reblogs) = options.exclude_reblogs {
360                params.push(format!("exclude_reblogs={}", exclude_reblogs));
361            }
362            if let Some(only_media) = options.only_media {
363                params.push(format!("only_media={}", only_media));
364            }
365        }
366        let mut url = format!("/api/v1/accounts/{}/statuses", id);
367        if params.len() > 0 {
368            url = url + "?" + params.join("&").as_str();
369        }
370        let res = self
371            .client
372            .get::<Vec<entities::Status>>(url.as_str(), None)
373            .await?;
374
375        Ok(Response::<Vec<MegalodonEntities::Status>>::new(
376            res.json.into_iter().map(|s| s.into()).collect(),
377            res.status,
378            res.status_text,
379            res.header,
380        ))
381    }
382
383    async fn get_account_favourites(
384        &self,
385        _id: String,
386        _options: Option<&megalodon::GetAccountFavouritesInputOptions>,
387    ) -> Result<Response<Vec<MegalodonEntities::Status>>, Error> {
388        Err(Error::new_own(
389            "Pixelfed doest not support".to_string(),
390            error::Kind::NoImplementedError,
391            None,
392            None,
393            None,
394        ))
395    }
396
397    async fn subscribe_account(
398        &self,
399        id: String,
400    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
401        let params = HashMap::<&str, Value>::from([("notify", serde_json::Value::Bool(true))]);
402        let res = self
403            .client
404            .post::<entities::Relationship>(
405                format!("/api/v1/accounts/{}/follow", id).as_str(),
406                &params,
407                None,
408            )
409            .await?;
410
411        Ok(Response::<MegalodonEntities::Relationship>::new(
412            res.json.into(),
413            res.status,
414            res.status_text,
415            res.header,
416        ))
417    }
418
419    async fn unsubscribe_account(
420        &self,
421        id: String,
422    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
423        let params = HashMap::<&str, Value>::from([("notify", serde_json::Value::Bool(false))]);
424        let res = self
425            .client
426            .post::<entities::Relationship>(
427                format!("/api/v1/accounts/{}/follow", id).as_str(),
428                &params,
429                None,
430            )
431            .await?;
432
433        Ok(Response::<MegalodonEntities::Relationship>::new(
434            res.json.into(),
435            res.status,
436            res.status_text,
437            res.header,
438        ))
439    }
440
441    async fn get_account_followers(
442        &self,
443        id: String,
444        options: Option<&megalodon::AccountFollowersInputOptions>,
445    ) -> Result<Response<Vec<MegalodonEntities::Account>>, Error> {
446        let mut params = Vec::<String>::new();
447        if let Some(options) = options {
448            if let Some(limit) = options.limit {
449                params.push(format!("limit={}", limit));
450            }
451            if let Some(max_id) = &options.max_id {
452                params.push(format!("max_id={}", max_id));
453            }
454            if let Some(since_id) = &options.since_id {
455                params.push(format!("since_id={}", since_id));
456            }
457        }
458        let mut url = format!("/api/v1/accounts/{}/followers", id);
459        if params.len() > 0 {
460            url = url + "?" + params.join("&").as_str();
461        }
462        let res = self
463            .client
464            .get::<Vec<entities::Account>>(&url, None)
465            .await?;
466
467        Ok(Response::<Vec<MegalodonEntities::Account>>::new(
468            res.json.into_iter().map(|j| j.into()).collect(),
469            res.status,
470            res.status_text,
471            res.header,
472        ))
473    }
474
475    async fn get_account_following(
476        &self,
477        id: String,
478        options: Option<&megalodon::AccountFollowersInputOptions>,
479    ) -> Result<Response<Vec<MegalodonEntities::Account>>, Error> {
480        let mut params = Vec::<String>::new();
481        if let Some(options) = options {
482            if let Some(limit) = options.limit {
483                params.push(format!("limit={}", limit));
484            }
485            if let Some(max_id) = &options.max_id {
486                params.push(format!("max_id={}", max_id));
487            }
488            if let Some(since_id) = &options.since_id {
489                params.push(format!("since_id={}", since_id));
490            }
491        }
492        let mut url = format!("/api/v1/accounts/{}/following", id);
493        if params.len() > 0 {
494            url = url + "?" + params.join("&").as_str();
495        }
496        let res = self
497            .client
498            .get::<Vec<entities::Account>>(&url, None)
499            .await?;
500
501        Ok(Response::<Vec<MegalodonEntities::Account>>::new(
502            res.json.into_iter().map(|j| j.into()).collect(),
503            res.status,
504            res.status_text,
505            res.header,
506        ))
507    }
508
509    async fn get_account_lists(
510        &self,
511        _id: String,
512    ) -> Result<Response<Vec<MegalodonEntities::List>>, Error> {
513        Err(Error::new_own(
514            "Pixelfed doest not support".to_string(),
515            error::Kind::NoImplementedError,
516            None,
517            None,
518            None,
519        ))
520    }
521
522    async fn get_identity_proofs(
523        &self,
524        _id: String,
525    ) -> Result<Response<Vec<MegalodonEntities::IdentityProof>>, Error> {
526        Err(Error::new_own(
527            "Pixelfed doest not support".to_string(),
528            error::Kind::NoImplementedError,
529            None,
530            None,
531            None,
532        ))
533    }
534
535    async fn follow_account(
536        &self,
537        id: String,
538        options: Option<&megalodon::FollowAccountInputOptions>,
539    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
540        let mut params = HashMap::<&str, Value>::new();
541        if let Some(options) = options {
542            if let Some(reblog) = options.reblog {
543                params.insert("reblog", serde_json::Value::String(reblog.to_string()));
544            }
545        }
546
547        let res = self
548            .client
549            .post::<entities::Relationship>(
550                format!("/api/v1/accounts/{}/follow", id).as_ref(),
551                &params,
552                None,
553            )
554            .await?;
555
556        Ok(Response::<MegalodonEntities::Relationship>::new(
557            res.json.into(),
558            res.status,
559            res.status_text,
560            res.header,
561        ))
562    }
563
564    async fn unfollow_account(
565        &self,
566        id: String,
567    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
568        let params = HashMap::<&str, Value>::new();
569        let res = self
570            .client
571            .post::<entities::Relationship>(
572                format!("/api/v1/accounts/{}/unfollow", id).as_ref(),
573                &params,
574                None,
575            )
576            .await?;
577
578        Ok(Response::<MegalodonEntities::Relationship>::new(
579            res.json.into(),
580            res.status,
581            res.status_text,
582            res.header,
583        ))
584    }
585
586    async fn block_account(
587        &self,
588        id: String,
589    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
590        let params = HashMap::<&str, Value>::new();
591        let res = self
592            .client
593            .post::<entities::Relationship>(
594                format!("/api/v1/accounts/{}/block", id).as_ref(),
595                &params,
596                None,
597            )
598            .await?;
599
600        Ok(Response::<MegalodonEntities::Relationship>::new(
601            res.json.into(),
602            res.status,
603            res.status_text,
604            res.header,
605        ))
606    }
607
608    async fn unblock_account(
609        &self,
610        id: String,
611    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
612        let params = HashMap::<&str, Value>::new();
613        let res = self
614            .client
615            .post::<entities::Relationship>(
616                format!("/api/v1/accounts/{}/unblock", id).as_ref(),
617                &params,
618                None,
619            )
620            .await?;
621
622        Ok(Response::<MegalodonEntities::Relationship>::new(
623            res.json.into(),
624            res.status,
625            res.status_text,
626            res.header,
627        ))
628    }
629
630    async fn mute_account(
631        &self,
632        id: String,
633        notifications: bool,
634    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
635        let params = HashMap::<&str, Value>::from([(
636            "notifications",
637            serde_json::Value::String(notifications.to_string()),
638        )]);
639        let res = self
640            .client
641            .post::<entities::Relationship>(
642                format!("/api/v1/accounts/{}/mute", id).as_ref(),
643                &params,
644                None,
645            )
646            .await?;
647
648        Ok(Response::<MegalodonEntities::Relationship>::new(
649            res.json.into(),
650            res.status,
651            res.status_text,
652            res.header,
653        ))
654    }
655
656    async fn unmute_account(
657        &self,
658        id: String,
659    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
660        let params = HashMap::<&str, Value>::new();
661        let res = self
662            .client
663            .post::<entities::Relationship>(
664                format!("/api/v1/accounts{}/unmute", id).as_ref(),
665                &params,
666                None,
667            )
668            .await?;
669
670        Ok(Response::<MegalodonEntities::Relationship>::new(
671            res.json.into(),
672            res.status,
673            res.status_text,
674            res.header,
675        ))
676    }
677
678    async fn pin_account(
679        &self,
680        _id: String,
681    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
682        Err(Error::new_own(
683            "Pixelfed doest not support".to_string(),
684            error::Kind::NoImplementedError,
685            None,
686            None,
687            None,
688        ))
689    }
690
691    async fn unpin_account(
692        &self,
693        _id: String,
694    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
695        Err(Error::new_own(
696            "Pixelfed doest not support".to_string(),
697            error::Kind::NoImplementedError,
698            None,
699            None,
700            None,
701        ))
702    }
703
704    async fn set_account_note(
705        &self,
706        _id: String,
707        _note: Option<String>,
708    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
709        Err(Error::new_own(
710            "Pixelfed doest not support".to_string(),
711            error::Kind::NoImplementedError,
712            None,
713            None,
714            None,
715        ))
716    }
717
718    async fn get_relationships(
719        &self,
720        ids: Vec<String>,
721    ) -> Result<Response<Vec<MegalodonEntities::Relationship>>, Error> {
722        let mut params = Vec::<String>::new();
723        for id in ids.iter() {
724            params.push(format!("id[]={}", id));
725        }
726        let path = "/api/v1/accounts/relationships?".to_string() + params.join("&").as_str();
727        let res = self
728            .client
729            .get::<Vec<entities::Relationship>>(path.as_ref(), None)
730            .await?;
731
732        Ok(Response::<Vec<MegalodonEntities::Relationship>>::new(
733            res.json.into_iter().map(|j| j.into()).collect(),
734            res.status,
735            res.status_text,
736            res.header,
737        ))
738    }
739
740    async fn search_account(
741        &self,
742        q: String,
743        options: Option<&megalodon::SearchAccountInputOptions>,
744    ) -> Result<Response<Vec<MegalodonEntities::Account>>, Error> {
745        let mut params = Vec::<String>::from([format!("q={}", q)]);
746        if let Some(options) = options {
747            if let Some(following) = options.following {
748                params.push(format!("following={}", following));
749            }
750            if let Some(resolve) = options.resolve {
751                params.push(format!("resolve={}", resolve));
752            }
753            if let Some(limit) = options.limit {
754                params.push(format!("limit={}", limit));
755            }
756            if let Some(max_id) = &options.max_id {
757                params.push(format!("max_id={}", max_id));
758            }
759            if let Some(since_id) = &options.since_id {
760                params.push(format!("since_id={}", since_id));
761            }
762        }
763        let mut path = "/api/v1/accounts/search".to_string();
764        if params.len() > 0 {
765            path = path + "?" + params.join("&").as_str();
766        }
767        let res = self
768            .client
769            .get::<Vec<entities::Account>>(path.as_str(), None)
770            .await?;
771
772        Ok(Response::<Vec<MegalodonEntities::Account>>::new(
773            res.json.into_iter().map(|j| j.into()).collect(),
774            res.status,
775            res.status_text,
776            res.header,
777        ))
778    }
779
780    async fn lookup_account(
781        &self,
782        _acct: String,
783    ) -> Result<Response<MegalodonEntities::Account>, Error> {
784        Err(Error::new_own(
785            "Pixelfed doest not support".to_string(),
786            error::Kind::NoImplementedError,
787            None,
788            None,
789            None,
790        ))
791    }
792
793    async fn get_bookmarks(
794        &self,
795        options: Option<&megalodon::GetBookmarksInputOptions>,
796    ) -> Result<Response<Vec<MegalodonEntities::Status>>, Error> {
797        let mut params = Vec::<String>::new();
798        if let Some(options) = options {
799            if let Some(limit) = options.limit {
800                params.push(format!("limit={}", limit));
801            }
802            if let Some(max_id) = &options.max_id {
803                params.push(format!("max_id={}", max_id));
804            }
805            if let Some(since_id) = &options.since_id {
806                params.push(format!("since_id={}", since_id));
807            }
808            if let Some(min_id) = &options.min_id {
809                params.push(format!("min_id={}", min_id));
810            }
811        }
812        let mut path = "/api/v1/bookmarks".to_string();
813        if params.len() > 0 {
814            path = path + "?" + params.join("&").as_str();
815        }
816        let res = self
817            .client
818            .get::<Vec<entities::Status>>(path.as_str(), None)
819            .await?;
820
821        Ok(Response::<Vec<MegalodonEntities::Status>>::new(
822            res.json.into_iter().map(|j| j.into()).collect(),
823            res.status,
824            res.status_text,
825            res.header,
826        ))
827    }
828
829    async fn get_favourites(
830        &self,
831        options: Option<&megalodon::GetFavouritesInputOptions>,
832    ) -> Result<Response<Vec<MegalodonEntities::Status>>, Error> {
833        let mut params = Vec::<String>::new();
834        if let Some(options) = options {
835            if let Some(limit) = options.limit {
836                params.push(format!("limit={}", limit));
837            }
838            if let Some(max_id) = &options.max_id {
839                params.push(format!("max_id={}", max_id));
840            }
841            if let Some(min_id) = &options.min_id {
842                params.push(format!("min_id={}", min_id));
843            }
844        }
845        let mut path = "/api/v1/favourites".to_string();
846        if params.len() > 0 {
847            path = path + "?" + params.join("&").as_str();
848        }
849        let res = self
850            .client
851            .get::<Vec<entities::Status>>(path.as_str(), None)
852            .await?;
853
854        Ok(Response::<Vec<MegalodonEntities::Status>>::new(
855            res.json.into_iter().map(|j| j.into()).collect(),
856            res.status,
857            res.status_text,
858            res.header,
859        ))
860    }
861
862    async fn get_mutes(
863        &self,
864        options: Option<&megalodon::GetMutesInputOptions>,
865    ) -> Result<Response<Vec<MegalodonEntities::Account>>, Error> {
866        let mut params = Vec::<String>::new();
867        if let Some(options) = options {
868            if let Some(limit) = options.limit {
869                params.push(format!("limit={}", limit));
870            }
871            if let Some(max_id) = &options.max_id {
872                params.push(format!("max_id={}", max_id));
873            }
874            if let Some(min_id) = &options.min_id {
875                params.push(format!("min_id={}", min_id));
876            }
877        }
878        let mut path = "/api/v1/mutes".to_string();
879        if params.len() > 0 {
880            path = path + "?" + params.join("&").as_str();
881        }
882        let res = self
883            .client
884            .get::<Vec<entities::Account>>(path.as_str(), None)
885            .await?;
886
887        Ok(Response::<Vec<MegalodonEntities::Account>>::new(
888            res.json.into_iter().map(|j| j.into()).collect(),
889            res.status,
890            res.status_text,
891            res.header,
892        ))
893    }
894
895    async fn get_blocks(
896        &self,
897        options: Option<&megalodon::GetBlocksInputOptions>,
898    ) -> Result<Response<Vec<MegalodonEntities::Account>>, Error> {
899        let mut params = Vec::<String>::new();
900        if let Some(options) = options {
901            if let Some(limit) = options.limit {
902                params.push(format!("limit={}", limit));
903            }
904            if let Some(max_id) = &options.max_id {
905                params.push(format!("max_id={}", max_id));
906            }
907            if let Some(min_id) = &options.min_id {
908                params.push(format!("min_id={}", min_id));
909            }
910        }
911        let mut path = "/api/v1/blocks".to_string();
912        if params.len() > 0 {
913            path = path + "?" + params.join("&").as_str();
914        }
915        let res = self
916            .client
917            .get::<Vec<entities::Account>>(path.as_str(), None)
918            .await?;
919
920        Ok(Response::<Vec<MegalodonEntities::Account>>::new(
921            res.json.into_iter().map(|j| j.into()).collect(),
922            res.status,
923            res.status_text,
924            res.header,
925        ))
926    }
927
928    async fn get_domain_blocks(
929        &self,
930        options: Option<&megalodon::GetDomainBlocksInputOptions>,
931    ) -> Result<Response<Vec<String>>, Error> {
932        let mut params = Vec::<String>::new();
933        if let Some(options) = options {
934            if let Some(limit) = options.limit {
935                params.push(format!("limit={}", limit));
936            }
937            if let Some(max_id) = &options.max_id {
938                params.push(format!("max_id={}", max_id));
939            }
940            if let Some(min_id) = &options.min_id {
941                params.push(format!("min_id={}", min_id));
942            }
943        }
944        let mut path = "/api/v1/domain_blocks".to_string();
945        if params.len() > 0 {
946            path = path + "?" + params.join("&").as_str();
947        }
948        let res = self.client.get::<Vec<String>>(path.as_str(), None).await?;
949
950        Ok(Response::<Vec<String>>::new(
951            res.json,
952            res.status,
953            res.status_text,
954            res.header,
955        ))
956    }
957
958    async fn block_domain(&self, domain: String) -> Result<Response<()>, Error> {
959        let params = HashMap::<&str, Value>::from([("domain", serde_json::Value::String(domain))]);
960        let res = self
961            .client
962            .post::<()>("/api/v1/domain_blocks", &params, None)
963            .await?;
964
965        Ok(res)
966    }
967
968    async fn unblock_domain(&self, domain: String) -> Result<Response<()>, Error> {
969        let params = HashMap::<&str, Value>::from([("domain", serde_json::Value::String(domain))]);
970        let res = self
971            .client
972            .delete::<()>("/api/v1/domain_blocks", &params, None)
973            .await?;
974
975        Ok(res)
976    }
977
978    async fn get_filters(&self) -> Result<Response<Vec<MegalodonEntities::Filter>>, Error> {
979        let res = self
980            .client
981            .get::<Vec<entities::Filter>>("/api/v1/filters", None)
982            .await?;
983
984        Ok(Response::<Vec<MegalodonEntities::Filter>>::new(
985            res.json.into_iter().map(|j| j.into()).collect(),
986            res.status,
987            res.status_text,
988            res.header,
989        ))
990    }
991
992    async fn get_filter(&self, id: String) -> Result<Response<MegalodonEntities::Filter>, Error> {
993        let res = self
994            .client
995            .get::<entities::Filter>(format!("/api/v1/filters/{}", id).as_str(), None)
996            .await?;
997
998        Ok(Response::<MegalodonEntities::Filter>::new(
999            res.json.into(),
1000            res.status,
1001            res.status_text,
1002            res.header,
1003        ))
1004    }
1005
1006    async fn create_filter(
1007        &self,
1008        phrase: String,
1009        context: Vec<MegalodonEntities::filter::FilterContext>,
1010        options: Option<&megalodon::FilterInputOptions>,
1011    ) -> Result<Response<MegalodonEntities::Filter>, Error> {
1012        let mut params = HashMap::<&str, Value>::from([
1013            ("phrase", serde_json::Value::String(phrase)),
1014            (
1015                "context",
1016                serde_json::to_value(&context).ok().unwrap_or_default(),
1017            ),
1018        ]);
1019        if let Some(options) = options {
1020            if let Some(irreversible) = options.irreversible {
1021                params.insert(
1022                    "irreversible",
1023                    serde_json::Value::String(irreversible.to_string()),
1024                );
1025            }
1026            if let Some(whole_word) = options.whole_word {
1027                params.insert(
1028                    "whole_word",
1029                    serde_json::Value::String(whole_word.to_string()),
1030                );
1031            }
1032            if let Some(expires_in) = options.expires_in {
1033                params.insert(
1034                    "expires_in",
1035                    serde_json::Value::String(expires_in.to_string()),
1036                );
1037            }
1038        }
1039        let res = self
1040            .client
1041            .post::<entities::Filter>("/api/v1/filters", &params, None)
1042            .await?;
1043
1044        Ok(Response::<MegalodonEntities::Filter>::new(
1045            res.json.into(),
1046            res.status,
1047            res.status_text,
1048            res.header,
1049        ))
1050    }
1051
1052    async fn update_filter(
1053        &self,
1054        id: String,
1055        phrase: String,
1056        context: Vec<MegalodonEntities::filter::FilterContext>,
1057        options: Option<&megalodon::FilterInputOptions>,
1058    ) -> Result<Response<MegalodonEntities::Filter>, Error> {
1059        let mut params = HashMap::<&str, Value>::from([
1060            ("phrase", serde_json::Value::String(phrase)),
1061            (
1062                "context",
1063                serde_json::to_value(&context).ok().unwrap_or_default(),
1064            ),
1065        ]);
1066        if let Some(options) = options {
1067            if let Some(irreversible) = options.irreversible {
1068                params.insert(
1069                    "irreversible",
1070                    serde_json::Value::String(irreversible.to_string()),
1071                );
1072            }
1073            if let Some(whole_word) = options.whole_word {
1074                params.insert(
1075                    "whole_word",
1076                    serde_json::Value::String(whole_word.to_string()),
1077                );
1078            }
1079            if let Some(expires_in) = options.expires_in {
1080                params.insert(
1081                    "expires_in",
1082                    serde_json::Value::String(expires_in.to_string()),
1083                );
1084            }
1085        }
1086        let res = self
1087            .client
1088            .put::<entities::Filter>(format!("/api/v1/filters/{}", id).as_str(), &params, None)
1089            .await?;
1090
1091        Ok(Response::<MegalodonEntities::Filter>::new(
1092            res.json.into(),
1093            res.status,
1094            res.status_text,
1095            res.header,
1096        ))
1097    }
1098
1099    async fn delete_filter(&self, id: String) -> Result<Response<()>, Error> {
1100        let params = HashMap::<&str, Value>::new();
1101        let res = self
1102            .client
1103            .delete::<()>(format!("/api/v1/filters/{}", id).as_str(), &params, None)
1104            .await?;
1105
1106        Ok(res)
1107    }
1108
1109    async fn report(
1110        &self,
1111        account_id: String,
1112        options: Option<&megalodon::ReportInputOptions>,
1113    ) -> Result<Response<MegalodonEntities::Report>, Error> {
1114        let mut params =
1115            HashMap::<&str, Value>::from([("account_id", serde_json::Value::String(account_id))]);
1116        if let Some(options) = options {
1117            if let Some(status_ids) = &options.status_ids {
1118                if let Some(json_status_ids) = serde_json::to_value(&status_ids).ok() {
1119                    params.insert("status_ids", json_status_ids);
1120                }
1121            }
1122            if let Some(comment) = &options.comment {
1123                params.insert("comment", Value::String(comment.to_string()));
1124            }
1125            if let Some(forward) = &options.forward {
1126                params.insert("forward", Value::String(forward.to_string()));
1127            }
1128            if let Some(category) = &options.category {
1129                params.insert("category", Value::String(category.to_string()));
1130            }
1131            if let Some(rule_ids) = &options.rule_ids {
1132                if let Some(json_rule_ids) = serde_json::to_value(&rule_ids).ok() {
1133                    params.insert("rule_ids", json_rule_ids);
1134                }
1135            }
1136        }
1137        let res = self
1138            .client
1139            .post::<entities::Report>("/api/v1/reports", &params, None)
1140            .await?;
1141
1142        Ok(Response::<MegalodonEntities::Report>::new(
1143            res.json.into(),
1144            res.status,
1145            res.status_text,
1146            res.header,
1147        ))
1148    }
1149
1150    async fn get_follow_requests(
1151        &self,
1152        limit: Option<u32>,
1153    ) -> Result<Response<Vec<FollowRequestOutput>>, Error> {
1154        let mut params = Vec::<String>::new();
1155        if let Some(limit) = limit {
1156            params.push(format!("limit={}", limit));
1157        }
1158        let mut path = "/api/v1/follow_requests".to_string();
1159        if params.len() > 0 {
1160            path = path + "?" + params.join("&").as_str();
1161        }
1162
1163        let res = self
1164            .client
1165            .get::<Vec<entities::Account>>(path.as_str(), None)
1166            .await?;
1167
1168        Ok(Response::<Vec<FollowRequestOutput>>::new(
1169            res.json.into_iter().map(|j| j.into()).collect(),
1170            res.status,
1171            res.status_text,
1172            res.header,
1173        ))
1174    }
1175
1176    async fn accept_follow_request(
1177        &self,
1178        id: String,
1179    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
1180        let params = HashMap::new();
1181        let res = self
1182            .client
1183            .post::<entities::Relationship>(
1184                format!("/api/v1/follow_requests/{}/authorize", id).as_str(),
1185                &params,
1186                None,
1187            )
1188            .await?;
1189
1190        Ok(Response::<MegalodonEntities::Relationship>::new(
1191            res.json.into(),
1192            res.status,
1193            res.status_text,
1194            res.header,
1195        ))
1196    }
1197
1198    async fn reject_follow_request(
1199        &self,
1200        id: String,
1201    ) -> Result<Response<MegalodonEntities::Relationship>, Error> {
1202        let params = HashMap::new();
1203        let res = self
1204            .client
1205            .post::<entities::Relationship>(
1206                format!("/api/v1/follow_requests/{}/reject", id).as_str(),
1207                &params,
1208                None,
1209            )
1210            .await?;
1211
1212        Ok(Response::<MegalodonEntities::Relationship>::new(
1213            res.json.into(),
1214            res.status,
1215            res.status_text,
1216            res.header,
1217        ))
1218    }
1219
1220    async fn get_endorsements(
1221        &self,
1222        options: Option<&megalodon::GetEndorsementsInputOptions>,
1223    ) -> Result<Response<Vec<MegalodonEntities::Account>>, Error> {
1224        let mut params = Vec::<String>::new();
1225        if let Some(options) = options {
1226            if let Some(limit) = options.limit {
1227                params.push(format!("limit={}", limit));
1228            }
1229            if let Some(max_id) = &options.max_id {
1230                params.push(format!("max_id={}", max_id));
1231            }
1232            if let Some(since_id) = &options.since_id {
1233                params.push(format!("since_id={}", since_id));
1234            }
1235        }
1236        let mut path = "/api/v1/endorsements".to_string();
1237        if params.len() > 0 {
1238            path = path + "?" + params.join("&").as_str();
1239        }
1240        let res = self
1241            .client
1242            .get::<Vec<entities::Account>>(path.as_str(), None)
1243            .await?;
1244
1245        Ok(Response::<Vec<MegalodonEntities::Account>>::new(
1246            res.json.into_iter().map(|j| j.into()).collect(),
1247            res.status,
1248            res.status_text,
1249            res.header,
1250        ))
1251    }
1252
1253    async fn get_featured_tags(
1254        &self,
1255    ) -> Result<Response<Vec<MegalodonEntities::FeaturedTag>>, Error> {
1256        Err(Error::new_own(
1257            "Pixelfed doest not support".to_string(),
1258            error::Kind::NoImplementedError,
1259            None,
1260            None,
1261            None,
1262        ))
1263    }
1264
1265    async fn create_featured_tag(
1266        &self,
1267        _name: String,
1268    ) -> Result<Response<MegalodonEntities::FeaturedTag>, Error> {
1269        Err(Error::new_own(
1270            "Pixelfed doest not support".to_string(),
1271            error::Kind::NoImplementedError,
1272            None,
1273            None,
1274            None,
1275        ))
1276    }
1277
1278    async fn delete_featured_tag(&self, _id: String) -> Result<Response<()>, Error> {
1279        Err(Error::new_own(
1280            "Pixelfed doest not support".to_string(),
1281            error::Kind::NoImplementedError,
1282            None,
1283            None,
1284            None,
1285        ))
1286    }
1287
1288    async fn get_suggested_tags(&self) -> Result<Response<Vec<MegalodonEntities::Tag>>, Error> {
1289        Err(Error::new_own(
1290            "Pixelfed doest not support".to_string(),
1291            error::Kind::NoImplementedError,
1292            None,
1293            None,
1294            None,
1295        ))
1296    }
1297
1298    async fn get_preferences(&self) -> Result<Response<MegalodonEntities::Preferences>, Error> {
1299        let res = self
1300            .client
1301            .get::<entities::Preferences>("/api/v1/preferences", None)
1302            .await?;
1303
1304        Ok(Response::<MegalodonEntities::Preferences>::new(
1305            res.json.into(),
1306            res.status,
1307            res.status_text,
1308            res.header,
1309        ))
1310    }
1311
1312    async fn get_followed_tags(&self) -> Result<Response<Vec<MegalodonEntities::Tag>>, Error> {
1313        let res = self
1314            .client
1315            .get::<Vec<entities::Tag>>("/api/v1/followed_tags", None)
1316            .await?;
1317
1318        Ok(Response::<Vec<MegalodonEntities::Tag>>::new(
1319            res.json.into_iter().map(|j| j.into()).collect(),
1320            res.status,
1321            res.status_text,
1322            res.header,
1323        ))
1324    }
1325
1326    async fn get_suggestions(
1327        &self,
1328        _limit: Option<u32>,
1329    ) -> Result<Response<Vec<MegalodonEntities::Account>>, Error> {
1330        Err(Error::new_own(
1331            "Pixelfed doest not support".to_string(),
1332            error::Kind::NoImplementedError,
1333            None,
1334            None,
1335            None,
1336        ))
1337    }
1338
1339    async fn get_tag(&self, id: String) -> Result<Response<MegalodonEntities::Tag>, Error> {
1340        let res = self
1341            .client
1342            .get::<entities::Tag>(format!("/api/v1/tags/{}", id).as_str(), None)
1343            .await?;
1344
1345        Ok(Response::<MegalodonEntities::Tag>::new(
1346            res.json.into(),
1347            res.status,
1348            res.status_text,
1349            res.header,
1350        ))
1351    }
1352
1353    async fn follow_tag(&self, id: String) -> Result<Response<MegalodonEntities::Tag>, Error> {
1354        let params = HashMap::<&str, Value>::default();
1355        let res = self
1356            .client
1357            .post::<entities::Tag>(
1358                format!("/api/v1/tags/{}/follow", id).as_str(),
1359                &params,
1360                None,
1361            )
1362            .await?;
1363
1364        Ok(Response::<MegalodonEntities::Tag>::new(
1365            res.json.into(),
1366            res.status,
1367            res.status_text,
1368            res.header,
1369        ))
1370    }
1371
1372    async fn unfollow_tag(&self, id: String) -> Result<Response<MegalodonEntities::Tag>, Error> {
1373        let params = HashMap::<&str, Value>::default();
1374        let res = self
1375            .client
1376            .post::<entities::Tag>(
1377                format!("/api/v1/tags/{}/unfollow", id).as_str(),
1378                &params,
1379                None,
1380            )
1381            .await?;
1382
1383        Ok(Response::<MegalodonEntities::Tag>::new(
1384            res.json.into(),
1385            res.status,
1386            res.status_text,
1387            res.header,
1388        ))
1389    }
1390
1391    async fn post_status(
1392        &self,
1393        status: String,
1394        options: Option<&megalodon::PostStatusInputOptions>,
1395    ) -> Result<Response<megalodon::PostStatusOutput>, Error> {
1396        let mut params =
1397            HashMap::<&str, Value>::from([("status", serde_json::Value::String(status))]);
1398
1399        let mut is_scheduled = false;
1400
1401        if let Some(options) = options {
1402            if let Some(media_ids) = &options.media_ids {
1403                if let Some(json_media_ids) = serde_json::to_value(media_ids).ok() {
1404                    params.insert("media_ids", json_media_ids);
1405                }
1406            }
1407            if let Some(in_reply_to_id) = &options.in_reply_to_id {
1408                params.insert(
1409                    "in_reply_to_id",
1410                    serde_json::Value::String(in_reply_to_id.to_string()),
1411                );
1412            }
1413            if let Some(sensitive) = options.sensitive {
1414                params.insert(
1415                    "sensitive",
1416                    serde_json::Value::String(sensitive.to_string()),
1417                );
1418            }
1419            if let Some(spoiler_text) = &options.spoiler_text {
1420                params.insert(
1421                    "spoiler_text",
1422                    serde_json::Value::String(spoiler_text.clone()),
1423                );
1424            }
1425            if let Some(visibility) = &options.visibility {
1426                params.insert(
1427                    "visibility",
1428                    serde_json::to_value(visibility.to_string()).unwrap(),
1429                );
1430            }
1431            if let Some(scheduled_at) = options.scheduled_at {
1432                // https://docs.joinmastodon.org/methods/statuses/#form-data-parameters
1433                // scheduled_at must be at least 5 mins in the futur
1434                if scheduled_at.sub(Utc::now()).num_minutes() > 5 {
1435                    is_scheduled = true;
1436                    params.insert(
1437                        "scheduled_at",
1438                        serde_json::to_value(scheduled_at.to_rfc3339()).unwrap(),
1439                    );
1440                }
1441            }
1442            if let Some(language) = &options.language {
1443                params.insert("language", serde_json::Value::String(language.clone()));
1444            }
1445            if let Some(quote_id) = &options.quote_id {
1446                params.insert("quote_id", serde_json::Value::String(quote_id.clone()));
1447            }
1448            if let Some(poll) = &options.poll {
1449                params.insert("poll", serde_json::to_value(&poll).unwrap());
1450            }
1451        }
1452
1453        if is_scheduled {
1454            let res = self
1455                .client
1456                .post::<entities::ScheduledStatus>("/api/v1/statuses", &params, None)
1457                .await?;
1458
1459            Ok(Response::<megalodon::PostStatusOutput>::new(
1460                res.json.into(),
1461                res.status,
1462                res.status_text,
1463                res.header,
1464            ))
1465        } else {
1466            let res = self
1467                .client
1468                .post::<entities::Status>("/api/v1/statuses", &params, None)
1469                .await?;
1470
1471            Ok(Response::<megalodon::PostStatusOutput>::new(
1472                res.json.into(),
1473                res.status,
1474                res.status_text,
1475                res.header,
1476            ))
1477        }
1478    }
1479
1480    async fn get_status(&self, id: String) -> Result<Response<MegalodonEntities::Status>, Error> {
1481        let res = self
1482            .client
1483            .get::<entities::Status>(format!("/api/v1/statuses/{}", id).as_str(), None)
1484            .await?;
1485
1486        Ok(Response::<MegalodonEntities::Status>::new(
1487            res.json.into(),
1488            res.status,
1489            res.status_text,
1490            res.header,
1491        ))
1492    }
1493
1494    async fn get_status_source(
1495        &self,
1496        _id: String,
1497    ) -> Result<Response<MegalodonEntities::StatusSource>, Error> {
1498        Err(Error::new_own(
1499            "Pixelfed doest not support".to_string(),
1500            error::Kind::NoImplementedError,
1501            None,
1502            None,
1503            None,
1504        ))
1505    }
1506
1507    async fn edit_status(
1508        &self,
1509        id: String,
1510        options: &megalodon::EditStatusInputOptions,
1511    ) -> Result<Response<MegalodonEntities::Status>, Error> {
1512        let mut params = HashMap::<&str, Value>::default();
1513        if let Some(status) = &options.status {
1514            params.insert("status", serde_json::Value::String(status.clone()));
1515        }
1516        if let Some(spoiler_text) = &options.spoiler_text {
1517            params.insert(
1518                "spoiler_text",
1519                serde_json::Value::String(spoiler_text.clone()),
1520            );
1521        }
1522        if let Some(sensitive) = options.sensitive {
1523            params.insert(
1524                "sensitive",
1525                serde_json::Value::String(sensitive.to_string()),
1526            );
1527        }
1528        if let Some(language) = &options.language {
1529            params.insert("language", serde_json::Value::String(language.clone()));
1530        }
1531        if let Some(media_ids) = &options.media_ids {
1532            if let Some(json_media_ids) = serde_json::to_value(media_ids).ok() {
1533                params.insert("media_ids", json_media_ids);
1534            }
1535        }
1536        if let Some(poll) = &options.poll {
1537            params.insert("poll", serde_json::to_value(&poll).unwrap());
1538        }
1539
1540        let res = self
1541            .client
1542            .put::<entities::Status>(format!("/api/v1/statuses/{}", id).as_str(), &params, None)
1543            .await?;
1544
1545        Ok(Response::<MegalodonEntities::Status>::new(
1546            res.json.into(),
1547            res.status,
1548            res.status_text,
1549            res.header,
1550        ))
1551    }
1552
1553    async fn delete_status(&self, id: String) -> Result<Response<()>, Error> {
1554        let params = HashMap::new();
1555        let Response {
1556            json: _,
1557            status,
1558            status_text,
1559            header,
1560        } = self
1561            .client
1562            .delete::<Value>(format!("/api/v1/statuses/{}", id).as_str(), &params, None)
1563            .await?;
1564
1565        Ok(Response::new((), status, status_text, header))
1566    }
1567
1568    async fn get_status_context(
1569        &self,
1570        id: String,
1571        options: Option<&megalodon::GetStatusContextInputOptions>,
1572    ) -> Result<Response<MegalodonEntities::Context>, Error> {
1573        let mut params = Vec::<String>::new();
1574        if let Some(options) = options {
1575            if let Some(limit) = options.limit {
1576                params.push(format!("limit={}", limit));
1577            }
1578            if let Some(max_id) = &options.max_id {
1579                params.push(format!("max_id={}", max_id));
1580            }
1581            if let Some(since_id) = &options.since_id {
1582                params.push(format!("sinde_id={}", since_id));
1583            }
1584        }
1585        let mut path = format!("/api/v1/statuses/{}/context", id).to_string();
1586        if params.len() > 0 {
1587            path = path + "?" + params.join("&").as_str();
1588        }
1589        let res = self
1590            .client
1591            .get::<entities::Context>(path.as_str(), None)
1592            .await?;
1593
1594        Ok(Response::<MegalodonEntities::Context>::new(
1595            res.json.into(),
1596            res.status,
1597            res.status_text,
1598            res.header,
1599        ))
1600    }
1601
1602    async fn get_status_reblogged_by(
1603        &self,
1604        id: String,
1605    ) -> Result<Response<Vec<MegalodonEntities::Account>>, Error> {
1606        let res = self
1607            .client
1608            .get::<Vec<entities::Account>>(
1609                format!("/api/v1/statuses/{}/reblogged_by", id).as_str(),
1610                None,
1611            )
1612            .await?;
1613
1614        Ok(Response::<Vec<MegalodonEntities::Account>>::new(
1615            res.json.into_iter().map(|j| j.into()).collect(),
1616            res.status,
1617            res.status_text,
1618            res.header,
1619        ))
1620    }
1621
1622    async fn get_status_favourited_by(
1623        &self,
1624        id: String,
1625    ) -> Result<Response<Vec<MegalodonEntities::Account>>, Error> {
1626        let res = self
1627            .client
1628            .get::<Vec<entities::Account>>(
1629                format!("/api/v1/statuses/{}/favourited_by", id).as_str(),
1630                None,
1631            )
1632            .await?;
1633
1634        Ok(Response::<Vec<MegalodonEntities::Account>>::new(
1635            res.json.into_iter().map(|j| j.into()).collect(),
1636            res.status,
1637            res.status_text,
1638            res.header,
1639        ))
1640    }
1641
1642    async fn favourite_status(
1643        &self,
1644        id: String,
1645    ) -> Result<Response<MegalodonEntities::Status>, Error> {
1646        let params = HashMap::new();
1647        let res = self
1648            .client
1649            .post::<entities::Status>(
1650                format!("/api/v1/statuses/{}/favourite", id).as_str(),
1651                &params,
1652                None,
1653            )
1654            .await?;
1655
1656        Ok(Response::<MegalodonEntities::Status>::new(
1657            res.json.into(),
1658            res.status,
1659            res.status_text,
1660            res.header,
1661        ))
1662    }
1663
1664    async fn unfavourite_status(
1665        &self,
1666        id: String,
1667    ) -> Result<Response<MegalodonEntities::Status>, Error> {
1668        let params = HashMap::new();
1669        let res = self
1670            .client
1671            .post::<entities::Status>(
1672                format!("/api/v1/statuses/{}/unfavourite", id).as_str(),
1673                &params,
1674                None,
1675            )
1676            .await?;
1677
1678        Ok(Response::<MegalodonEntities::Status>::new(
1679            res.json.into(),
1680            res.status,
1681            res.status_text,
1682            res.header,
1683        ))
1684    }
1685
1686    async fn reblog_status(
1687        &self,
1688        id: String,
1689    ) -> Result<Response<MegalodonEntities::Status>, Error> {
1690        let params = HashMap::new();
1691        let res = self
1692            .client
1693            .post::<entities::Status>(
1694                format!("/api/v1/statuses/{}/reblog", id).as_str(),
1695                &params,
1696                None,
1697            )
1698            .await?;
1699
1700        Ok(Response::<MegalodonEntities::Status>::new(
1701            res.json.into(),
1702            res.status,
1703            res.status_text,
1704            res.header,
1705        ))
1706    }
1707
1708    async fn unreblog_status(
1709        &self,
1710        id: String,
1711    ) -> Result<Response<MegalodonEntities::Status>, Error> {
1712        let params = HashMap::new();
1713        let res = self
1714            .client
1715            .post::<entities::Status>(
1716                format!("/api/v1/statuses/{}/unreblog", id).as_str(),
1717                &params,
1718                None,
1719            )
1720            .await?;
1721
1722        Ok(Response::<MegalodonEntities::Status>::new(
1723            res.json.into(),
1724            res.status,
1725            res.status_text,
1726            res.header,
1727        ))
1728    }
1729
1730    async fn bookmark_status(
1731        &self,
1732        id: String,
1733    ) -> Result<Response<MegalodonEntities::Status>, Error> {
1734        let params = HashMap::new();
1735        let res = self
1736            .client
1737            .post::<entities::Status>(
1738                format!("/api/v1/statuses/{}/bookmark", id).as_str(),
1739                &params,
1740                None,
1741            )
1742            .await?;
1743
1744        Ok(Response::<MegalodonEntities::Status>::new(
1745            res.json.into(),
1746            res.status,
1747            res.status_text,
1748            res.header,
1749        ))
1750    }
1751
1752    async fn unbookmark_status(
1753        &self,
1754        id: String,
1755    ) -> Result<Response<MegalodonEntities::Status>, Error> {
1756        let params = HashMap::new();
1757        let res = self
1758            .client
1759            .post::<entities::Status>(
1760                format!("/api/v1/statuses/{}/unbookmark", id).as_str(),
1761                &params,
1762                None,
1763            )
1764            .await?;
1765
1766        Ok(Response::<MegalodonEntities::Status>::new(
1767            res.json.into(),
1768            res.status,
1769            res.status_text,
1770            res.header,
1771        ))
1772    }
1773
1774    async fn mute_status(&self, _id: String) -> Result<Response<MegalodonEntities::Status>, Error> {
1775        Err(Error::new_own(
1776            "Pixelfed doest not support".to_string(),
1777            error::Kind::NoImplementedError,
1778            None,
1779            None,
1780            None,
1781        ))
1782    }
1783
1784    async fn unmute_status(
1785        &self,
1786        _id: String,
1787    ) -> Result<Response<MegalodonEntities::Status>, Error> {
1788        Err(Error::new_own(
1789            "Pixelfed doest not support".to_string(),
1790            error::Kind::NoImplementedError,
1791            None,
1792            None,
1793            None,
1794        ))
1795    }
1796
1797    async fn pin_status(&self, _id: String) -> Result<Response<MegalodonEntities::Status>, Error> {
1798        Err(Error::new_own(
1799            "Pixelfed doest not support".to_string(),
1800            error::Kind::NoImplementedError,
1801            None,
1802            None,
1803            None,
1804        ))
1805    }
1806
1807    async fn unpin_status(
1808        &self,
1809        _id: String,
1810    ) -> Result<Response<MegalodonEntities::Status>, Error> {
1811        Err(Error::new_own(
1812            "Pixelfed doest not support".to_string(),
1813            error::Kind::NoImplementedError,
1814            None,
1815            None,
1816            None,
1817        ))
1818    }
1819
1820    async fn upload_media_reader(
1821        &self,
1822        reader: Box<dyn AsyncRead + Sync + Send + Unpin>,
1823        options: Option<&megalodon::UploadMediaInputOptions>,
1824        file_name: Option<String>,
1825    ) -> Result<Response<MegalodonEntities::UploadMedia>, Error> {
1826        // Generate a random filename if not provided
1827        let mut file_name_unhash = [0; 32];
1828        rand::thread_rng().fill_bytes(&mut file_name_unhash);
1829        let random_file_name = hex::encode(Sha1::digest(file_name_unhash));
1830
1831        let stream = FramedRead::new(reader, BytesCodec::new());
1832        let file_body = reqwest::Body::wrap_stream(stream);
1833
1834        // Determine MIME type from original filename if available
1835        let mime_type = if let Some(ref original_name) = file_name {
1836            mime_guess::from_path(original_name)
1837                .first_or_octet_stream()
1838                .to_string()
1839        } else {
1840            "application/octet-stream".to_string()
1841        };
1842
1843        let part = reqwest::multipart::Part::stream(file_body)
1844            .file_name(random_file_name)
1845            .mime_str(&mime_type)
1846            .map_err(|e| {
1847                Error::new_own(
1848                    e.to_string(),
1849                    crate::error::Kind::ParseError,
1850                    None,
1851                    None,
1852                    None,
1853                )
1854            })?;
1855
1856        let mut form = reqwest::multipart::Form::new().part("file", part);
1857        if let Some(options) = options {
1858            if let Some(description) = &options.description {
1859                form = form.text("description", description.clone());
1860            }
1861            if let Some(focus) = &options.focus {
1862                form = form.text("focus", focus.clone());
1863            }
1864        }
1865
1866        let res = self
1867            .client
1868            .post_multipart::<entities::Attachment>("/api/v2/media", form, None)
1869            .await?;
1870
1871        Ok(Response::<MegalodonEntities::UploadMedia>::new(
1872            res.json.into(),
1873            res.status,
1874            res.status_text,
1875            res.header,
1876        ))
1877    }
1878
1879    async fn get_media(
1880        &self,
1881        id: String,
1882    ) -> Result<Response<MegalodonEntities::Attachment>, Error> {
1883        let res = self
1884            .client
1885            .get::<entities::Attachment>(format!("/api/v1/media/{}", id).as_str(), None)
1886            .await?;
1887
1888        Ok(Response::<MegalodonEntities::Attachment>::new(
1889            res.json.into(),
1890            res.status,
1891            res.status_text,
1892            res.header,
1893        ))
1894    }
1895
1896    async fn update_media(
1897        &self,
1898        id: String,
1899        options: Option<&megalodon::UpdateMediaInputOptions>,
1900    ) -> Result<Response<MegalodonEntities::Attachment>, Error> {
1901        let mut form = reqwest::multipart::Form::new();
1902        if let Some(options) = options {
1903            if let Some(description) = &options.description {
1904                form = form.text("description", description.clone());
1905            }
1906            if let Some(focus) = &options.focus {
1907                form = form.text("focus", focus.clone());
1908            }
1909            if let Some(file_path) = &options.file_path {
1910                let file = File::open(file_path).await?;
1911
1912                let file_name = hex::encode(Sha1::digest(file_path.as_bytes()));
1913
1914                let stream = FramedRead::new(file, BytesCodec::new());
1915                let file_body = reqwest::Body::wrap_stream(stream);
1916                let part = reqwest::multipart::Part::stream(file_body).file_name(file_name);
1917                form = form.part("file", part);
1918            }
1919        }
1920
1921        let res = self
1922            .client
1923            .put_multipart::<entities::Attachment>(
1924                format!("/api/v1/media/{}", id).as_str(),
1925                form,
1926                None,
1927            )
1928            .await?;
1929
1930        Ok(Response::<MegalodonEntities::Attachment>::new(
1931            res.json.into(),
1932            res.status,
1933            res.status_text,
1934            res.header,
1935        ))
1936    }
1937
1938    async fn get_poll(&self, id: String) -> Result<Response<MegalodonEntities::Poll>, Error> {
1939        let res = self
1940            .client
1941            .get::<entities::Poll>(format!("/api/v1/polls/{}", id).as_str(), None)
1942            .await?;
1943
1944        Ok(Response::<MegalodonEntities::Poll>::new(
1945            res.json.into(),
1946            res.status,
1947            res.status_text,
1948            res.header,
1949        ))
1950    }
1951
1952    async fn vote_poll(
1953        &self,
1954        id: String,
1955        choices: Vec<u32>,
1956        _status_id: Option<String>,
1957    ) -> Result<Response<MegalodonEntities::Poll>, Error> {
1958        let params = HashMap::<&str, Value>::from([(
1959            "choices",
1960            serde_json::to_value(&choices).ok().unwrap_or_default(),
1961        )]);
1962        let res = self
1963            .client
1964            .post::<entities::Poll>(
1965                format!("/api/v1/polls/{}/votes", id).as_str(),
1966                &params,
1967                None,
1968            )
1969            .await?;
1970
1971        Ok(Response::<MegalodonEntities::Poll>::new(
1972            res.json.into(),
1973            res.status,
1974            res.status_text,
1975            res.header,
1976        ))
1977    }
1978
1979    async fn get_scheduled_statuses(
1980        &self,
1981        options: Option<&megalodon::GetScheduledStatusesInputOptions>,
1982    ) -> Result<Response<Vec<MegalodonEntities::ScheduledStatus>>, Error> {
1983        let mut params = Vec::<String>::new();
1984        if let Some(options) = options {
1985            if let Some(limit) = options.limit {
1986                params.push(format!("limit={}", limit));
1987            }
1988            if let Some(max_id) = &options.max_id {
1989                params.push(format!("max_id={}", max_id));
1990            }
1991            if let Some(since_id) = &options.since_id {
1992                params.push(format!("since_id={}", since_id));
1993            }
1994            if let Some(min_id) = &options.min_id {
1995                params.push(format!("min_id={}", min_id));
1996            }
1997        }
1998        let mut path = "/api/v1/scheduled_statuses".to_string();
1999        if params.len() > 0 {
2000            path = path + "?" + params.join("&").as_str();
2001        }
2002        let res = self
2003            .client
2004            .get::<Vec<entities::ScheduledStatus>>(path.as_str(), None)
2005            .await?;
2006
2007        Ok(Response::<Vec<MegalodonEntities::ScheduledStatus>>::new(
2008            res.json.into_iter().map(|j| j.into()).collect(),
2009            res.status,
2010            res.status_text,
2011            res.header,
2012        ))
2013    }
2014
2015    async fn get_scheduled_status(
2016        &self,
2017        id: String,
2018    ) -> Result<Response<MegalodonEntities::ScheduledStatus>, Error> {
2019        let res = self
2020            .client
2021            .get::<entities::ScheduledStatus>(
2022                format!("/api/v1/scheduled_statuses/{}", id).as_str(),
2023                None,
2024            )
2025            .await?;
2026
2027        Ok(Response::<MegalodonEntities::ScheduledStatus>::new(
2028            res.json.into(),
2029            res.status,
2030            res.status_text,
2031            res.header,
2032        ))
2033    }
2034
2035    async fn schedule_status(
2036        &self,
2037        id: String,
2038        scheduled_at: Option<DateTime<Utc>>,
2039    ) -> Result<Response<MegalodonEntities::ScheduledStatus>, Error> {
2040        let mut params = HashMap::<&str, Value>::new();
2041        if let Some(scheduled_at) = scheduled_at {
2042            params.insert(
2043                "scheduled_at",
2044                serde_json::Value::String(scheduled_at.to_rfc3339()),
2045            );
2046        }
2047        let res = self
2048            .client
2049            .put::<entities::ScheduledStatus>(
2050                format!("/api/v1/scheduled_statuses/{}", id).as_str(),
2051                &params,
2052                None,
2053            )
2054            .await?;
2055
2056        Ok(Response::<MegalodonEntities::ScheduledStatus>::new(
2057            res.json.into(),
2058            res.status,
2059            res.status_text,
2060            res.header,
2061        ))
2062    }
2063
2064    async fn cancel_scheduled_status(&self, id: String) -> Result<Response<()>, Error> {
2065        let params = HashMap::new();
2066        let res = self
2067            .client
2068            .delete::<()>(
2069                format!("/api/v1/scheduled_statuses/{}", id).as_str(),
2070                &params,
2071                None,
2072            )
2073            .await?;
2074
2075        Ok(res)
2076    }
2077
2078    async fn get_public_timeline(
2079        &self,
2080        options: Option<&megalodon::GetPublicTimelineInputOptions>,
2081    ) -> Result<Response<Vec<MegalodonEntities::Status>>, Error> {
2082        let mut params =
2083            Vec::<String>::from([format!("local={}", false), format!("remote={}", true)]);
2084        if let Some(options) = options {
2085            if let Some(only_media) = options.only_media {
2086                params.push(format!("only_media={}", only_media));
2087            }
2088            if let Some(limit) = options.limit {
2089                params.push(format!("limit={}", limit));
2090            }
2091            if let Some(max_id) = &options.max_id {
2092                params.push(format!("max_id={}", max_id));
2093            }
2094            if let Some(since_id) = &options.since_id {
2095                params.push(format!("since_id={}", since_id));
2096            }
2097            if let Some(min_id) = &options.min_id {
2098                params.push(format!("min_id={}", min_id));
2099            }
2100        }
2101        let mut path = "/api/v1/timelines/public".to_string();
2102        if params.len() > 0 {
2103            path = path + "?" + params.join("&").as_str();
2104        }
2105        let res = self
2106            .client
2107            .get::<Vec<entities::Status>>(path.as_str(), None)
2108            .await?;
2109
2110        Ok(Response::<Vec<MegalodonEntities::Status>>::new(
2111            res.json.into_iter().map(|j| j.into()).collect(),
2112            res.status,
2113            res.status_text,
2114            res.header,
2115        ))
2116    }
2117
2118    async fn get_local_timeline(
2119        &self,
2120        options: Option<&megalodon::GetLocalTimelineInputOptions>,
2121    ) -> Result<Response<Vec<MegalodonEntities::Status>>, Error> {
2122        let mut params = Vec::<String>::from([format!("local={}", true)]);
2123        if let Some(options) = options {
2124            if let Some(only_media) = options.only_media {
2125                params.push(format!("only_media={}", only_media));
2126            }
2127            if let Some(limit) = options.limit {
2128                params.push(format!("limit={}", limit));
2129            }
2130            if let Some(max_id) = &options.max_id {
2131                params.push(format!("max_id={}", max_id));
2132            }
2133            if let Some(since_id) = &options.since_id {
2134                params.push(format!("since_id={}", since_id));
2135            }
2136            if let Some(min_id) = &options.min_id {
2137                params.push(format!("min_id={}", min_id));
2138            }
2139        }
2140        let mut path = "/api/v1/timelines/public".to_string();
2141        if params.len() > 0 {
2142            path = path + "?" + params.join("&").as_str();
2143        }
2144        let res = self
2145            .client
2146            .get::<Vec<entities::Status>>(path.as_str(), None)
2147            .await?;
2148
2149        Ok(Response::<Vec<MegalodonEntities::Status>>::new(
2150            res.json.into_iter().map(|j| j.into()).collect(),
2151            res.status,
2152            res.status_text,
2153            res.header,
2154        ))
2155    }
2156
2157    async fn get_tag_timeline(
2158        &self,
2159        hashtag: String,
2160        options: Option<&megalodon::GetTagTimelineInputOptions>,
2161    ) -> Result<Response<Vec<MegalodonEntities::Status>>, Error> {
2162        let mut params = Vec::<String>::new();
2163        if let Some(options) = options {
2164            if let Some(only_media) = options.only_media {
2165                params.push(format!("only_media={}", only_media));
2166            }
2167            if let Some(limit) = options.limit {
2168                params.push(format!("limit={}", limit));
2169            }
2170            if let Some(max_id) = &options.max_id {
2171                params.push(format!("max_id={}", max_id));
2172            }
2173            if let Some(since_id) = &options.since_id {
2174                params.push(format!("since_id={}", since_id));
2175            }
2176            if let Some(min_id) = &options.min_id {
2177                params.push(format!("min_id={}", min_id));
2178            }
2179            if let Some(local) = options.local {
2180                params.push(format!("local={}", local));
2181            }
2182        }
2183        let mut path = format!("/api/v1/timelines/tag/{}", hashtag);
2184        if params.len() > 0 {
2185            path = path + "?" + params.join("&").as_str();
2186        }
2187        let res = self
2188            .client
2189            .get::<Vec<entities::Status>>(path.as_str(), None)
2190            .await?;
2191
2192        Ok(Response::<Vec<MegalodonEntities::Status>>::new(
2193            res.json.into_iter().map(|j| j.into()).collect(),
2194            res.status,
2195            res.status_text,
2196            res.header,
2197        ))
2198    }
2199
2200    async fn get_home_timeline(
2201        &self,
2202        options: Option<&megalodon::GetHomeTimelineInputOptions>,
2203    ) -> Result<Response<Vec<MegalodonEntities::Status>>, Error> {
2204        let mut params = Vec::<String>::new();
2205        if let Some(options) = options {
2206            if let Some(only_media) = options.only_media {
2207                params.push(format!("only_media={}", only_media));
2208            }
2209            if let Some(limit) = options.limit {
2210                params.push(format!("limit={}", limit));
2211            }
2212            if let Some(max_id) = &options.max_id {
2213                params.push(format!("max_id={}", max_id));
2214            }
2215            if let Some(since_id) = &options.since_id {
2216                params.push(format!("since_id={}", since_id));
2217            }
2218            if let Some(min_id) = &options.min_id {
2219                params.push(format!("min_id={}", min_id));
2220            }
2221            if let Some(local) = options.local {
2222                params.push(format!("local={}", local));
2223            }
2224        }
2225        let mut path = "/api/v1/timelines/home".to_string();
2226        if params.len() > 0 {
2227            path = path + "?" + params.join("&").as_str();
2228        }
2229        let res = self
2230            .client
2231            .get::<Vec<entities::Status>>(path.as_str(), None)
2232            .await?;
2233
2234        Ok(Response::<Vec<MegalodonEntities::Status>>::new(
2235            res.json.into_iter().map(|j| j.into()).collect(),
2236            res.status,
2237            res.status_text,
2238            res.header,
2239        ))
2240    }
2241
2242    async fn get_list_timeline(
2243        &self,
2244        _list_id: String,
2245        _options: Option<&megalodon::GetListTimelineInputOptions>,
2246    ) -> Result<Response<Vec<MegalodonEntities::Status>>, Error> {
2247        Err(Error::new_own(
2248            "Pixelfed doest not support".to_string(),
2249            error::Kind::NoImplementedError,
2250            None,
2251            None,
2252            None,
2253        ))
2254    }
2255
2256    async fn get_conversation_timeline(
2257        &self,
2258        options: Option<&megalodon::GetConversationTimelineInputOptions>,
2259    ) -> Result<Response<Vec<MegalodonEntities::Conversation>>, Error> {
2260        let mut params = Vec::<String>::new();
2261        if let Some(options) = options {
2262            if let Some(limit) = options.limit {
2263                params.push(format!("limit={}", limit));
2264            }
2265            if let Some(max_id) = &options.max_id {
2266                params.push(format!("max_id={}", max_id));
2267            }
2268            if let Some(since_id) = &options.since_id {
2269                params.push(format!("since_id={}", since_id));
2270            }
2271            if let Some(min_id) = &options.min_id {
2272                params.push(format!("min_id={}", min_id));
2273            }
2274        }
2275        let mut path = "/api/v1/conversations".to_string();
2276        if params.len() > 0 {
2277            path = path + "?" + params.join("&").as_str();
2278        }
2279        let res = self
2280            .client
2281            .get::<Vec<entities::Conversation>>(path.as_str(), None)
2282            .await?;
2283
2284        Ok(Response::<Vec<MegalodonEntities::Conversation>>::new(
2285            res.json.into_iter().map(|j| j.into()).collect(),
2286            res.status,
2287            res.status_text,
2288            res.header,
2289        ))
2290    }
2291
2292    async fn delete_conversation(&self, id: String) -> Result<Response<()>, Error> {
2293        let params = HashMap::new();
2294        let res = self
2295            .client
2296            .delete::<()>(
2297                format!("/api/v1/conversations/{}", id).as_str(),
2298                &params,
2299                None,
2300            )
2301            .await?;
2302
2303        Ok(res)
2304    }
2305
2306    async fn read_conversation(
2307        &self,
2308        id: String,
2309    ) -> Result<Response<MegalodonEntities::Conversation>, Error> {
2310        let params = HashMap::new();
2311        let res = self
2312            .client
2313            .post::<entities::Conversation>(
2314                format!("/api/v1/conversations/{}/read", id).as_str(),
2315                &params,
2316                None,
2317            )
2318            .await?;
2319
2320        Ok(Response::<MegalodonEntities::Conversation>::new(
2321            res.json.into(),
2322            res.status,
2323            res.status_text,
2324            res.header,
2325        ))
2326    }
2327
2328    async fn get_lists(&self) -> Result<Response<Vec<MegalodonEntities::List>>, Error> {
2329        let res = self.client.get::<()>("/api/v1/lists", None).await?;
2330
2331        Ok(Response::<Vec<MegalodonEntities::List>>::new(
2332            vec![],
2333            res.status,
2334            res.status_text,
2335            res.header,
2336        ))
2337    }
2338
2339    async fn get_list(&self, _id: String) -> Result<Response<MegalodonEntities::List>, Error> {
2340        Err(Error::new_own(
2341            "Pixelfed doest not support".to_string(),
2342            error::Kind::NoImplementedError,
2343            None,
2344            None,
2345            None,
2346        ))
2347    }
2348
2349    async fn create_list(
2350        &self,
2351        _title: String,
2352    ) -> Result<Response<MegalodonEntities::List>, Error> {
2353        Err(Error::new_own(
2354            "Pixelfed doest not support".to_string(),
2355            error::Kind::NoImplementedError,
2356            None,
2357            None,
2358            None,
2359        ))
2360    }
2361
2362    async fn update_list(
2363        &self,
2364        _id: String,
2365        _title: String,
2366    ) -> Result<Response<MegalodonEntities::List>, Error> {
2367        Err(Error::new_own(
2368            "Pixelfed doest not support".to_string(),
2369            error::Kind::NoImplementedError,
2370            None,
2371            None,
2372            None,
2373        ))
2374    }
2375
2376    async fn delete_list(&self, _id: String) -> Result<Response<()>, Error> {
2377        Err(Error::new_own(
2378            "Pixelfed doest not support".to_string(),
2379            error::Kind::NoImplementedError,
2380            None,
2381            None,
2382            None,
2383        ))
2384    }
2385
2386    async fn get_accounts_in_list(
2387        &self,
2388        _id: String,
2389        _options: Option<&megalodon::GetAccountsInListInputOptions>,
2390    ) -> Result<Response<Vec<MegalodonEntities::Account>>, Error> {
2391        Err(Error::new_own(
2392            "Pixelfed doest not support".to_string(),
2393            error::Kind::NoImplementedError,
2394            None,
2395            None,
2396            None,
2397        ))
2398    }
2399
2400    async fn add_accounts_to_list(
2401        &self,
2402        _id: String,
2403        _account_ids: Vec<String>,
2404    ) -> Result<Response<MegalodonEntities::List>, Error> {
2405        Err(Error::new_own(
2406            "Pixelfed doest not support".to_string(),
2407            error::Kind::NoImplementedError,
2408            None,
2409            None,
2410            None,
2411        ))
2412    }
2413
2414    async fn delete_accounts_from_list(
2415        &self,
2416        _id: String,
2417        _account_ids: Vec<String>,
2418    ) -> Result<Response<()>, Error> {
2419        Err(Error::new_own(
2420            "Pixelfed doest not support".to_string(),
2421            error::Kind::NoImplementedError,
2422            None,
2423            None,
2424            None,
2425        ))
2426    }
2427
2428    async fn get_markers(
2429        &self,
2430        timeline: Vec<String>,
2431    ) -> Result<Response<MegalodonEntities::Marker>, Error> {
2432        let params: Vec<String> = timeline
2433            .into_iter()
2434            .map(|t| format!("timeline[]={}", t))
2435            .collect();
2436
2437        let mut path = "/api/v1/markers".to_string();
2438        if params.len() > 0 {
2439            path = path + "?" + params.join("&").as_str();
2440        }
2441        let res = self
2442            .client
2443            .get::<entities::Marker>(path.as_str(), None)
2444            .await?;
2445
2446        Ok(Response::<MegalodonEntities::Marker>::new(
2447            res.json.into(),
2448            res.status,
2449            res.status_text,
2450            res.header,
2451        ))
2452    }
2453
2454    async fn save_markers(
2455        &self,
2456        options: Option<&megalodon::SaveMarkersInputOptions>,
2457    ) -> Result<Response<MegalodonEntities::Marker>, Error> {
2458        let mut params = HashMap::<&str, Value>::new();
2459        if let Some(options) = options {
2460            if let Some(home) = &options.home {
2461                if let Some(json_home) = serde_json::to_value(&home).ok() {
2462                    params.insert("home", json_home);
2463                }
2464            }
2465            if let Some(notifications) = &options.notifications {
2466                if let Some(json_notifications) = serde_json::to_value(&notifications).ok() {
2467                    params.insert("notifications", json_notifications);
2468                }
2469            }
2470        }
2471        let res = self
2472            .client
2473            .post::<entities::Marker>("/api/v1/makers", &params, None)
2474            .await?;
2475
2476        Ok(Response::<MegalodonEntities::Marker>::new(
2477            res.json.into(),
2478            res.status,
2479            res.status_text,
2480            res.header,
2481        ))
2482    }
2483
2484    async fn get_notifications(
2485        &self,
2486        options: Option<&megalodon::GetNotificationsInputOptions>,
2487    ) -> Result<Response<Vec<MegalodonEntities::Notification>>, Error> {
2488        let mut params = Vec::<String>::new();
2489        if let Some(options) = options {
2490            if let Some(limit) = options.limit {
2491                params.push(format!("limit={}", limit));
2492            }
2493            if let Some(max_id) = &options.max_id {
2494                params.push(format!("max_id={}", max_id));
2495            }
2496            if let Some(since_id) = &options.since_id {
2497                params.push(format!("since_id={}", since_id));
2498            }
2499            if let Some(min_id) = &options.min_id {
2500                params.push(format!("min_id={}", min_id));
2501            }
2502            if let Some(exclude_types) = &options.exclude_types {
2503                params.push(format!(
2504                    "exclude_types={}",
2505                    serde_json::to_string(exclude_types).unwrap()
2506                ));
2507            }
2508            if let Some(account_id) = &options.account_id {
2509                params.push(format!("account_id={}", account_id));
2510            }
2511        }
2512        let mut path = "/api/v1/notifications".to_string();
2513        if params.len() > 0 {
2514            path = path + "?" + params.join("&").as_str();
2515        }
2516        let res = self
2517            .client
2518            .get::<Vec<entities::Notification>>(path.as_str(), None)
2519            .await?;
2520
2521        Ok(Response::<Vec<MegalodonEntities::Notification>>::new(
2522            res.json.into_iter().map(|j| j.into()).collect(),
2523            res.status,
2524            res.status_text,
2525            res.header,
2526        ))
2527    }
2528
2529    async fn get_notification(
2530        &self,
2531        _id: String,
2532    ) -> Result<Response<MegalodonEntities::Notification>, Error> {
2533        Err(Error::new_own(
2534            "Pixelfed doest not support".to_string(),
2535            error::Kind::NoImplementedError,
2536            None,
2537            None,
2538            None,
2539        ))
2540    }
2541
2542    async fn dismiss_notifications(&self) -> Result<Response<()>, Error> {
2543        Err(Error::new_own(
2544            "Pixelfed doest not support".to_string(),
2545            error::Kind::NoImplementedError,
2546            None,
2547            None,
2548            None,
2549        ))
2550    }
2551
2552    async fn dismiss_notification(&self, _id: String) -> Result<Response<()>, Error> {
2553        Err(Error::new_own(
2554            "Pixelfed doest not support".to_string(),
2555            error::Kind::NoImplementedError,
2556            None,
2557            None,
2558            None,
2559        ))
2560    }
2561
2562    async fn read_notifications(
2563        &self,
2564        _options: &megalodon::ReadNotificationsInputOptions,
2565    ) -> Result<Response<()>, Error> {
2566        Err(Error::new_own(
2567            "Mastodon doest not support".to_string(),
2568            error::Kind::NoImplementedError,
2569            None,
2570            None,
2571            None,
2572        ))
2573    }
2574
2575    async fn subscribe_push_notification(
2576        &self,
2577        _subscription: &megalodon::SubscribePushNotificationInputSubscription,
2578        _data: Option<&megalodon::SubscribePushNotificationInputData>,
2579    ) -> Result<Response<MegalodonEntities::PushSubscription>, Error> {
2580        Err(Error::new_own(
2581            "Pixelfed doest not support".to_string(),
2582            error::Kind::NoImplementedError,
2583            None,
2584            None,
2585            None,
2586        ))
2587    }
2588
2589    async fn get_push_subscription(
2590        &self,
2591    ) -> Result<Response<MegalodonEntities::PushSubscription>, Error> {
2592        Err(Error::new_own(
2593            "Pixelfed doest not support".to_string(),
2594            error::Kind::NoImplementedError,
2595            None,
2596            None,
2597            None,
2598        ))
2599    }
2600
2601    async fn update_push_subscription(
2602        &self,
2603        _data: Option<&megalodon::SubscribePushNotificationInputData>,
2604    ) -> Result<Response<MegalodonEntities::PushSubscription>, Error> {
2605        Err(Error::new_own(
2606            "Pixelfed doest not support".to_string(),
2607            error::Kind::NoImplementedError,
2608            None,
2609            None,
2610            None,
2611        ))
2612    }
2613
2614    async fn delete_push_subscription(&self) -> Result<Response<()>, Error> {
2615        Err(Error::new_own(
2616            "Pixelfed doest not support".to_string(),
2617            error::Kind::NoImplementedError,
2618            None,
2619            None,
2620            None,
2621        ))
2622    }
2623
2624    async fn search(
2625        &self,
2626        q: String,
2627        options: Option<&megalodon::SearchInputOptions>,
2628    ) -> Result<Response<MegalodonEntities::Results>, Error> {
2629        let mut params = Vec::<String>::from([format!("q={}", q)]);
2630        if let Some(options) = options {
2631            if let Some(t) = &options.r#type {
2632                params.push(format!("type={}", t));
2633            }
2634            if let Some(limit) = options.limit {
2635                params.push(format!("limit={}", limit));
2636            }
2637            if let Some(max_id) = &options.max_id {
2638                params.push(format!("max_id={}", max_id));
2639            }
2640            if let Some(min_id) = &options.min_id {
2641                params.push(format!("min_id={}", min_id));
2642            }
2643            if let Some(resolve) = options.resolve {
2644                params.push(format!("resolve={}", resolve));
2645            }
2646            if let Some(offset) = options.offset {
2647                params.push(format!("offset={}", offset));
2648            }
2649            if let Some(following) = options.following {
2650                params.push(format!("following={}", following));
2651            }
2652            if let Some(account_id) = &options.account_id {
2653                params.push(format!("account_id={}", account_id));
2654            }
2655            if let Some(exclude_unreviewed) = options.exclude_unreviewed {
2656                params.push(format!("exclude_unreviewed={}", exclude_unreviewed));
2657            }
2658        }
2659        let mut path = "/api/v2/search".to_string();
2660        if params.len() > 0 {
2661            path = path + "?" + params.join("&").as_str();
2662        }
2663        let res = self
2664            .client
2665            .get::<entities::Results>(path.as_str(), None)
2666            .await?;
2667
2668        Ok(Response::<MegalodonEntities::Results>::new(
2669            res.json.into(),
2670            res.status,
2671            res.status_text,
2672            res.header,
2673        ))
2674    }
2675
2676    async fn get_instance(&self) -> Result<Response<MegalodonEntities::Instance>, Error> {
2677        let res = self
2678            .client
2679            .get::<entities::Instance>("/api/v1/instance", None)
2680            .await?;
2681
2682        Ok(Response::<MegalodonEntities::Instance>::new(
2683            res.json.into(),
2684            res.status,
2685            res.status_text,
2686            res.header,
2687        ))
2688    }
2689
2690    async fn get_instance_peers(&self) -> Result<Response<Vec<String>>, Error> {
2691        let res = self
2692            .client
2693            .get::<Vec<String>>("/api/v1/instance/peers", None)
2694            .await?;
2695        Ok(res)
2696    }
2697
2698    async fn get_instance_activity(
2699        &self,
2700    ) -> Result<Response<Vec<MegalodonEntities::Activity>>, Error> {
2701        Err(Error::new_own(
2702            "Pixelfed doest not support".to_string(),
2703            error::Kind::NoImplementedError,
2704            None,
2705            None,
2706            None,
2707        ))
2708    }
2709
2710    async fn get_instance_trends(
2711        &self,
2712        limit: Option<u32>,
2713    ) -> Result<Response<Vec<MegalodonEntities::Tag>>, Error> {
2714        let mut params = Vec::<String>::new();
2715        if let Some(limit) = limit {
2716            params.push(format!("limit={}", limit));
2717        }
2718        let mut path = "/api/v1/trends".to_string();
2719        if params.len() > 0 {
2720            path = path + "?" + params.join("&").as_str();
2721        }
2722        let res = self
2723            .client
2724            .get::<Vec<entities::Tag>>(path.as_str(), None)
2725            .await?;
2726
2727        Ok(Response::<Vec<MegalodonEntities::Tag>>::new(
2728            res.json.into_iter().map(|j| j.into()).collect(),
2729            res.status,
2730            res.status_text,
2731            res.header,
2732        ))
2733    }
2734
2735    async fn get_instance_directory(
2736        &self,
2737        _options: Option<&megalodon::GetInstanceDirectoryInputOptions>,
2738    ) -> Result<Response<Vec<MegalodonEntities::Account>>, Error> {
2739        Err(Error::new_own(
2740            "Pixelfed doest not support".to_string(),
2741            error::Kind::NoImplementedError,
2742            None,
2743            None,
2744            None,
2745        ))
2746    }
2747
2748    async fn get_instance_custom_emojis(
2749        &self,
2750    ) -> Result<Response<Vec<MegalodonEntities::Emoji>>, Error> {
2751        let res = self
2752            .client
2753            .get::<Vec<entities::Emoji>>("/api/v1/custom_emojis", None)
2754            .await?;
2755
2756        Ok(Response::<Vec<MegalodonEntities::Emoji>>::new(
2757            res.json.into_iter().map(|j| j.into()).collect(),
2758            res.status,
2759            res.status_text,
2760            res.header,
2761        ))
2762    }
2763
2764    async fn get_instance_announcements(
2765        &self,
2766    ) -> Result<Response<Vec<MegalodonEntities::Announcement>>, Error> {
2767        let res = self
2768            .client
2769            .get::<Vec<entities::Announcement>>("/api/v1/announcements", None)
2770            .await?;
2771
2772        Ok(Response::<Vec<MegalodonEntities::Announcement>>::new(
2773            res.json.into_iter().map(|j| j.into()).collect(),
2774            res.status,
2775            res.status_text,
2776            res.header,
2777        ))
2778    }
2779
2780    async fn dismiss_instance_announcement(&self, id: String) -> Result<Response<()>, Error> {
2781        let params = HashMap::<&str, Value>::new();
2782        let res = self
2783            .client
2784            .post::<()>(
2785                format!("/api/v1/announcements/{}/dismiss", id).as_str(),
2786                &params,
2787                None,
2788            )
2789            .await?;
2790
2791        Ok(Response::<()>::new(
2792            (),
2793            res.status,
2794            res.status_text,
2795            res.header,
2796        ))
2797    }
2798
2799    async fn add_reaction_to_announcement(
2800        &self,
2801        id: String,
2802        name: String,
2803    ) -> Result<Response<()>, Error> {
2804        let params = HashMap::<&str, Value>::new();
2805        let res = self
2806            .client
2807            .put::<()>(
2808                format!("/api/v1/announcements/{}/reactions/{}", id, name).as_str(),
2809                &params,
2810                None,
2811            )
2812            .await?;
2813
2814        Ok(Response::<()>::new(
2815            (),
2816            res.status,
2817            res.status_text,
2818            res.header,
2819        ))
2820    }
2821
2822    async fn remove_reaction_from_announcement(
2823        &self,
2824        id: String,
2825        name: String,
2826    ) -> Result<Response<()>, Error> {
2827        let params = HashMap::<&str, Value>::new();
2828        let res = self
2829            .client
2830            .delete::<()>(
2831                format!("/api/v1/announcements/{}/reactions/{}", id, name).as_str(),
2832                &params,
2833                None,
2834            )
2835            .await?;
2836
2837        Ok(Response::<()>::new(
2838            (),
2839            res.status,
2840            res.status_text,
2841            res.header,
2842        ))
2843    }
2844
2845    async fn create_emoji_reaction(
2846        &self,
2847        _id: String,
2848        _emoji: String,
2849    ) -> Result<Response<MegalodonEntities::Status>, Error> {
2850        Err(Error::new_own(
2851            "Pixelfed doest not support".to_string(),
2852            error::Kind::NoImplementedError,
2853            None,
2854            None,
2855            None,
2856        ))
2857    }
2858
2859    async fn delete_emoji_reaction(
2860        &self,
2861        _id: String,
2862        _emoji: String,
2863    ) -> Result<Response<MegalodonEntities::Status>, Error> {
2864        Err(Error::new_own(
2865            "Pixelfed doest not support".to_string(),
2866            error::Kind::NoImplementedError,
2867            None,
2868            None,
2869            None,
2870        ))
2871    }
2872
2873    async fn get_emoji_reactions(
2874        &self,
2875        _id: String,
2876    ) -> Result<Response<Vec<MegalodonEntities::Reaction>>, Error> {
2877        Err(Error::new_own(
2878            "Pixelfed doest not support".to_string(),
2879            error::Kind::NoImplementedError,
2880            None,
2881            None,
2882            None,
2883        ))
2884    }
2885
2886    async fn get_emoji_reaction(
2887        &self,
2888        _id: String,
2889        _emoji: String,
2890    ) -> Result<Response<MegalodonEntities::Reaction>, Error> {
2891        Err(Error::new_own(
2892            "Pixelfed doest not support".to_string(),
2893            error::Kind::NoImplementedError,
2894            None,
2895            None,
2896            None,
2897        ))
2898    }
2899
2900    async fn streaming_url(&self) -> String {
2901        let instance = self.get_instance().await;
2902        if let Ok(instance) = instance {
2903            match instance.json.urls {
2904                Some(urls) => return urls.streaming_api,
2905                _ => {}
2906            };
2907        }
2908
2909        self.base_url.clone()
2910    }
2911
2912    async fn user_streaming(&self) -> Box<dyn Streaming + Send + Sync> {
2913        let c = WebSocket::new();
2914
2915        Box::new(c)
2916    }
2917
2918    async fn public_streaming(&self) -> Box<dyn Streaming + Send + Sync> {
2919        let c = WebSocket::new();
2920
2921        Box::new(c)
2922    }
2923
2924    async fn local_streaming(&self) -> Box<dyn Streaming + Send + Sync> {
2925        let c = WebSocket::new();
2926
2927        Box::new(c)
2928    }
2929
2930    async fn direct_streaming(&self) -> Box<dyn Streaming + Send + Sync> {
2931        let c = WebSocket::new();
2932
2933        Box::new(c)
2934    }
2935
2936    async fn tag_streaming(&self, _tag: String) -> Box<dyn Streaming + Send + Sync> {
2937        let c = WebSocket::new();
2938
2939        Box::new(c)
2940    }
2941
2942    async fn list_streaming(&self, _list_id: String) -> Box<dyn Streaming + Send + Sync> {
2943        let c = WebSocket::new();
2944
2945        Box::new(c)
2946    }
2947}