mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
305 lines
9.1 KiB
Rust
305 lines
9.1 KiB
Rust
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
use reqwest::Method;
|
|
|
|
use crate::api::generated::{
|
|
snowflake,
|
|
types::{
|
|
ReportNoteRequest, ReportNoteResponse, UpdateReportRequest, UpdateReportRequestOutcome,
|
|
UpdateReportRequestResolution, UpdateReportRequestStatus,
|
|
},
|
|
};
|
|
|
|
use super::client::{AdminApiClient, ApiError, ApiResult};
|
|
use super::types::{
|
|
ReportEntry, ReportLegalHoldResponse, ReportReasonListResponse, ResolveReportResponse,
|
|
SearchReportsResponse,
|
|
};
|
|
|
|
#[derive(Default)]
|
|
pub struct SearchReportsParams<'a> {
|
|
pub query: Option<&'a str>,
|
|
pub status: Option<i32>,
|
|
pub report_type: Option<i32>,
|
|
pub category: Option<&'a str>,
|
|
pub reason: Option<&'a str>,
|
|
pub reporter_id: Option<&'a str>,
|
|
pub reported_user_id: Option<&'a str>,
|
|
pub reported_webhook_id: Option<&'a str>,
|
|
pub reported_guild_id: Option<&'a str>,
|
|
pub reported_channel_id: Option<&'a str>,
|
|
pub guild_context_id: Option<&'a str>,
|
|
pub resolved_by_admin_id: Option<&'a str>,
|
|
pub sort_by: Option<&'a str>,
|
|
pub sort_order: Option<&'a str>,
|
|
pub limit: u32,
|
|
pub offset: u64,
|
|
}
|
|
|
|
impl AdminApiClient {
|
|
pub async fn get_report(&self, report_id: &str) -> ApiResult<ReportEntry> {
|
|
let response = self
|
|
.generated()
|
|
.get_admin_report(&snowflake(report_id))
|
|
.await
|
|
.map_err(|e| self.generated_error(e))?;
|
|
self.generated_value(response.into_inner())
|
|
}
|
|
|
|
pub async fn resolve_report(
|
|
&self,
|
|
report_id: &str,
|
|
resolution: UpdateReportRequestResolution,
|
|
outcome: Option<UpdateReportRequestOutcome>,
|
|
duplicate_of: Option<&str>,
|
|
public_comment: Option<&str>,
|
|
notify_reporter: bool,
|
|
) -> ApiResult<ResolveReportResponse> {
|
|
let body = UpdateReportRequest {
|
|
duplicate_of: duplicate_of.map(snowflake),
|
|
notify_reporter,
|
|
outcome,
|
|
public_comment: public_comment.map(str::to_owned),
|
|
resolution,
|
|
status: UpdateReportRequestStatus::Resolved,
|
|
};
|
|
self.send(
|
|
Method::PATCH,
|
|
&format!("/admin/reports/{}", urlencoding::encode(report_id)),
|
|
Some(&body),
|
|
None,
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub async fn set_report_legal_hold(
|
|
&self,
|
|
report_id: &str,
|
|
legal_hold_until: Option<&str>,
|
|
legal_hold_reason: Option<&str>,
|
|
) -> ApiResult<ReportLegalHoldResponse> {
|
|
let body = serde_json::json!({
|
|
"legal_hold_until": legal_hold_until,
|
|
"legal_hold_reason": legal_hold_until.and(legal_hold_reason),
|
|
});
|
|
self.send(
|
|
Method::POST,
|
|
&format!(
|
|
"/admin/reports/{}/legal-hold",
|
|
urlencoding::encode(report_id)
|
|
),
|
|
Some(&body),
|
|
None,
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub async fn annotate_report(
|
|
&self,
|
|
report_id: &str,
|
|
body: String,
|
|
) -> ApiResult<ReportNoteResponse> {
|
|
self.send(
|
|
Method::POST,
|
|
&format!("/admin/reports/{}/notes", urlencoding::encode(report_id)),
|
|
Some(&ReportNoteRequest { body }),
|
|
None,
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub async fn delete_report(
|
|
&self,
|
|
report_id: &str,
|
|
audit_log_reason: Option<&str>,
|
|
) -> ApiResult<()> {
|
|
self.send_void(
|
|
Method::DELETE,
|
|
&format!("/admin/reports/{}", urlencoding::encode(report_id)),
|
|
None::<&serde_json::Value>,
|
|
audit_log_reason,
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub async fn search_reports(
|
|
&self,
|
|
params: &SearchReportsParams<'_>,
|
|
) -> ApiResult<SearchReportsResponse> {
|
|
let status = params
|
|
.status
|
|
.map(report_status)
|
|
.transpose()?
|
|
.unwrap_or_default();
|
|
let report_type = params
|
|
.report_type
|
|
.map(report_type_name)
|
|
.transpose()?
|
|
.unwrap_or_default();
|
|
let sort_by = params
|
|
.sort_by
|
|
.map(report_sort_by)
|
|
.transpose()?
|
|
.unwrap_or_default();
|
|
let limit = params.limit.to_string();
|
|
let offset = params.offset.to_string();
|
|
let query_params = [
|
|
("q", params.query.unwrap_or_default()),
|
|
("status", status),
|
|
("report_type", report_type),
|
|
("category", params.category.unwrap_or_default()),
|
|
("reason", params.reason.unwrap_or_default()),
|
|
("reporter_id", params.reporter_id.unwrap_or_default()),
|
|
(
|
|
"reported_user_id",
|
|
params.reported_user_id.unwrap_or_default(),
|
|
),
|
|
(
|
|
"reported_webhook_id",
|
|
params.reported_webhook_id.unwrap_or_default(),
|
|
),
|
|
(
|
|
"reported_guild_id",
|
|
params.reported_guild_id.unwrap_or_default(),
|
|
),
|
|
(
|
|
"reported_channel_id",
|
|
params.reported_channel_id.unwrap_or_default(),
|
|
),
|
|
(
|
|
"guild_context_id",
|
|
params.guild_context_id.unwrap_or_default(),
|
|
),
|
|
(
|
|
"resolved_by_admin_id",
|
|
params.resolved_by_admin_id.unwrap_or_default(),
|
|
),
|
|
("sort_by", sort_by),
|
|
("sort_order", params.sort_order.unwrap_or_default()),
|
|
("limit", limit.as_str()),
|
|
("offset", offset.as_str()),
|
|
];
|
|
self.get("/admin/reports", Some(&query_params)).await
|
|
}
|
|
|
|
pub async fn list_report_reasons(&self) -> ApiResult<ReportReasonListResponse> {
|
|
self.get("/admin/report-reasons", None).await
|
|
}
|
|
|
|
pub async fn search_reports_by_reporter(
|
|
&self,
|
|
reporter_id: &str,
|
|
limit: u32,
|
|
offset: u64,
|
|
) -> ApiResult<SearchReportsResponse> {
|
|
self.search_reports(&SearchReportsParams {
|
|
reporter_id: Some(reporter_id),
|
|
limit,
|
|
offset,
|
|
..Default::default()
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn search_reports_by_reported_user(
|
|
&self,
|
|
reported_user_id: &str,
|
|
limit: u32,
|
|
offset: u64,
|
|
) -> ApiResult<SearchReportsResponse> {
|
|
self.search_reports(&SearchReportsParams {
|
|
reported_user_id: Some(reported_user_id),
|
|
limit,
|
|
offset,
|
|
..Default::default()
|
|
})
|
|
.await
|
|
}
|
|
}
|
|
|
|
fn report_status(value: i32) -> ApiResult<&'static str> {
|
|
match value {
|
|
0 => Ok("pending"),
|
|
1 => Ok("resolved"),
|
|
other => Err(ApiError::Parse(format!("unknown report status: {other}"))),
|
|
}
|
|
}
|
|
|
|
fn report_type_name(value: i32) -> ApiResult<&'static str> {
|
|
match value {
|
|
0 => Ok("message"),
|
|
1 => Ok("user"),
|
|
2 => Ok("guild"),
|
|
other => Err(ApiError::Parse(format!("unknown report type: {other}"))),
|
|
}
|
|
}
|
|
|
|
fn report_sort_by(value: &str) -> ApiResult<&'static str> {
|
|
match value {
|
|
"created_at" | "createdAt" => Ok("created_at"),
|
|
"reported_at" | "reportedAt" => Ok("reported_at"),
|
|
"resolved_at" | "resolvedAt" => Ok("resolved_at"),
|
|
other => Err(ApiError::Parse(format!(
|
|
"unknown report sort field: {other}"
|
|
))),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn report_statuses_preserve_the_closed_wire_mapping() {
|
|
for (value, expected) in [(0, "pending"), (1, "resolved")] {
|
|
assert_eq!(report_status(value).expect("supported status"), expected);
|
|
}
|
|
for value in [-1, 2] {
|
|
assert_eq!(
|
|
report_status(value)
|
|
.expect_err("unknown status")
|
|
.to_string(),
|
|
format!("parse error: unknown report status: {value}")
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn report_types_preserve_the_closed_wire_mapping() {
|
|
for (value, expected) in [(0, "message"), (1, "user"), (2, "guild")] {
|
|
assert_eq!(report_type_name(value).expect("supported type"), expected);
|
|
}
|
|
for value in [-1, 3] {
|
|
assert_eq!(
|
|
report_type_name(value)
|
|
.expect_err("unknown type")
|
|
.to_string(),
|
|
format!("parse error: unknown report type: {value}")
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn report_sort_fields_accept_only_the_existing_aliases() {
|
|
for (field, expected) in [
|
|
("createdAt", "created_at"),
|
|
("created_at", "created_at"),
|
|
("reportedAt", "reported_at"),
|
|
("reported_at", "reported_at"),
|
|
("resolvedAt", "resolved_at"),
|
|
("resolved_at", "resolved_at"),
|
|
] {
|
|
assert_eq!(
|
|
report_sort_by(field).expect("supported sort field"),
|
|
expected
|
|
);
|
|
}
|
|
assert_eq!(
|
|
report_sort_by("unknown")
|
|
.expect_err("unknown sort field")
|
|
.to_string(),
|
|
"parse error: unknown report sort field: unknown"
|
|
);
|
|
}
|
|
}
|