use crate::cli::parse_tweet_id;
use crate::context::AppContext;
use crate::errors::XmasterError;
use crate::output::{self, CsvRenderable, OutputFormat, Tableable};
use crate::providers::xapi::XApi;
use reqwest::Method;
use serde::Serialize;
use serde_json::{json, Value};
use std::sync::Arc;
const BASE: &str = "https://api.x.com/2";
async fn signed_request(
ctx: &Arc<AppContext>,
method: Method,
url: &str,
body: Option<Value>,
) -> Result<(), XmasterError> {
let _val = XApi::new(ctx.clone()).request(method, url, body).await?;
Ok(())
}
#[derive(Serialize)]
struct ModerationResult {
action: String,
target: String,
success: bool,
}
impl Tableable for ModerationResult {
fn to_table(&self) -> comfy_table::Table {
let mut table = comfy_table::Table::new();
table.set_header(vec!["Action", "Target", "Status"]);
table.add_row(vec![
self.action.as_str(),
self.target.as_str(),
if self.success { "OK" } else { "Failed" },
]);
table
}
}
impl CsvRenderable for ModerationResult {}
fn render_success(format: OutputFormat, action: &str, target: String) {
let display = ModerationResult {
action: action.to_string(),
target,
success: true,
};
output::render(format, &display, None);
}
pub async fn hide_reply(
ctx: Arc<AppContext>,
format: OutputFormat,
id: &str,
) -> Result<(), XmasterError> {
let tweet_id = parse_tweet_id(id);
signed_request(
&ctx,
Method::PUT,
&format!("{BASE}/tweets/{tweet_id}/hidden"),
Some(json!({ "hidden": true })),
)
.await?;
render_success(format, "hide_reply", tweet_id);
Ok(())
}
pub async fn unhide_reply(
ctx: Arc<AppContext>,
format: OutputFormat,
id: &str,
) -> Result<(), XmasterError> {
let tweet_id = parse_tweet_id(id);
signed_request(
&ctx,
Method::PUT,
&format!("{BASE}/tweets/{tweet_id}/hidden"),
Some(json!({ "hidden": false })),
)
.await?;
render_success(format, "unhide_reply", tweet_id);
Ok(())
}
pub async fn block(
ctx: Arc<AppContext>,
format: OutputFormat,
username: &str,
) -> Result<(), XmasterError> {
let api = XApi::new(ctx.clone());
let uid = api.get_authenticated_user_id().await?;
let target = api.get_user_by_username(username).await?;
signed_request(
&ctx,
Method::POST,
&format!("{BASE}/users/{uid}/blocking"),
Some(json!({ "target_user_id": target.id })),
)
.await?;
render_success(format, "block", format!("@{username}"));
Ok(())
}
pub async fn unblock(
ctx: Arc<AppContext>,
format: OutputFormat,
username: &str,
) -> Result<(), XmasterError> {
let api = XApi::new(ctx.clone());
let uid = api.get_authenticated_user_id().await?;
let target = api.get_user_by_username(username).await?;
signed_request(
&ctx,
Method::DELETE,
&format!("{BASE}/users/{uid}/blocking/{}", target.id),
None,
)
.await?;
render_success(format, "unblock", format!("@{username}"));
Ok(())
}
pub async fn mute(
ctx: Arc<AppContext>,
format: OutputFormat,
username: &str,
) -> Result<(), XmasterError> {
let api = XApi::new(ctx.clone());
let uid = api.get_authenticated_user_id().await?;
let target = api.get_user_by_username(username).await?;
signed_request(
&ctx,
Method::POST,
&format!("{BASE}/users/{uid}/muting"),
Some(json!({ "target_user_id": target.id })),
)
.await?;
render_success(format, "mute", format!("@{username}"));
Ok(())
}
pub async fn unmute(
ctx: Arc<AppContext>,
format: OutputFormat,
username: &str,
) -> Result<(), XmasterError> {
let api = XApi::new(ctx.clone());
let uid = api.get_authenticated_user_id().await?;
let target = api.get_user_by_username(username).await?;
signed_request(
&ctx,
Method::DELETE,
&format!("{BASE}/users/{uid}/muting/{}", target.id),
None,
)
.await?;
render_success(format, "unmute", format!("@{username}"));
Ok(())
}