Skip to main content

fedi_post/
lemmy.rs

1use lemmy_client::{
2    ClientOptions, LemmyClient, LemmyRequest,
3    lemmy_api_common::{community::GetCommunity, person::Login, post::CreatePost},
4};
5use log::{info, warn};
6use thiserror::Error;
7
8use crate::{ServerCredentials, ServerPost};
9
10/// Errors that can occur when posting to Lemmy.
11#[derive(Debug, Error)]
12pub enum LemmyError {
13    /// Error while logging into Lemmy.
14    #[error("Login error: {0}")]
15    Login(lemmy_client::lemmy_api_common::lemmy_utils::error::LemmyErrorType),
16
17    /// Error while finding a community in Lemmy.
18    #[error("Find community error: {0}")]
19    Community(lemmy_client::lemmy_api_common::lemmy_utils::error::LemmyErrorType),
20
21    /// Error while finding a language in Lemmy.
22    #[error("Find language error: {0}")]
23    Language(lemmy_client::lemmy_api_common::lemmy_utils::error::LemmyErrorType),
24
25    /// Error while creating a post in Lemmy.
26    #[error("Post error: {0}")]
27    Post(lemmy_client::lemmy_api_common::lemmy_utils::error::LemmyErrorType),
28
29    /// Error indicating that login failed because no JWT was returned.
30    #[error("Login failed. No JWT returned.")]
31    LoginReturnedNoJwt,
32
33    /// Error indicating that the specified language was not found.
34    #[error(
35        "Language {language_code} not found. Availabe language codes are {available_languages:?}"
36    )]
37    LanguageNotFound {
38        /// The language code as specified in the post.
39        language_code: String,
40        /// The available language codes on the server.
41        available_languages: Vec<String>,
42    },
43}
44
45pub(super) async fn post_to_lemmy(
46    credentials: &ServerCredentials,
47    post: ServerPost,
48) -> Result<(), LemmyError> {
49    info!("Logging into lemmy at {}", credentials.domain);
50    let client = LemmyClient::new(ClientOptions {
51        domain: credentials.domain.clone(),
52        secure: true,
53    });
54    let jwt = client
55        .login(Login {
56            username_or_email: credentials.username.clone().into(),
57            password: credentials.password.clone().into(),
58            totp_2fa_token: None,
59        })
60        .await
61        .map_err(LemmyError::Login)?
62        .jwt
63        .ok_or(LemmyError::LoginReturnedNoJwt)?;
64    info!("JWT: {}", &*jwt);
65
66    info!("Getting community id for {}", post.community);
67    let community_id = client
68        .get_community(GetCommunity {
69            id: None,
70            name: Some(post.community.clone()),
71        })
72        .await
73        .map_err(LemmyError::Community)?
74        .community_view
75        .community
76        .id;
77    info!("Community id for {} is {}", post.community, community_id.0);
78
79    let language_id;
80    if let Some(language_code) = post.language.as_ref() {
81        info!("Getting language id for {}", language_code);
82        let all_languages = &client
83            .get_site(())
84            .await
85            .map_err(LemmyError::Language)?
86            .all_languages;
87        language_id = Some(
88            all_languages
89                .iter()
90                .find(|lang| &lang.code == language_code)
91                .ok_or_else(|| LemmyError::LanguageNotFound {
92                    language_code: language_code.clone(),
93                    available_languages: all_languages
94                        .iter()
95                        .map(|language| language.code.clone())
96                        .collect(),
97                })?
98                .id,
99        );
100        info!(
101            "Language id for {} is {}",
102            language_code,
103            language_id.unwrap().0,
104        );
105    } else {
106        info!("No language specified, skipping getting language id");
107        language_id = None;
108    }
109
110    info!("Posting to lemmy");
111    let ServerPost {
112        community: _community,
113        title,
114        url,
115        body,
116        language: _language,
117        alt_text,
118        nsfw,
119        nsfl,
120        ai_generated,
121        custom_thumbnail,
122    } = post;
123    let create_post = CreatePost {
124        name: title,
125        community_id,
126        url,
127        body,
128        alt_text,
129        honeypot: None,
130        nsfw,
131        language_id,
132        custom_thumbnail,
133    };
134
135    if let Some(nsfl) = nsfl {
136        warn!(
137            "The nsfl field is set to {nsfl}, but Lemmy does not support the nsfl field. Ignoring this field."
138        );
139    }
140    if let Some(ai_generated) = ai_generated {
141        warn!(
142            "The ai_generated field is set to {ai_generated}, but Lemmy does not support the ai_generated field. Ignoring this field."
143        );
144    }
145
146    info!("Posting {create_post:?}");
147    client
148        .create_post(LemmyRequest {
149            body: create_post,
150            jwt: Some(jwt.into_inner()),
151        })
152        .await
153        .map_err(LemmyError::Post)?;
154
155    info!("Post created successfully");
156    Ok(())
157}