use reqwest::Method;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{client::Sendry, error::Error};
#[derive(Debug, Clone)]
pub struct Deliverability {
client: Sendry,
}
impl Deliverability {
pub(crate) fn new(client: Sendry) -> Self {
Self { client }
}
pub async fn get_reputation(
&self,
params: ReputationQuery,
) -> Result<ReputationResponse, Error> {
let q = params.to_query();
self.client
.request(self.client.build::<()>(
Method::GET,
"/v1/deliverability/reputation",
&q,
None,
))
.await
}
pub async fn get_reputation_history(
&self,
domain_id: &str,
params: ReputationHistoryQuery,
) -> Result<Value, Error> {
let q = params.to_query();
self.client
.request(self.client.build::<()>(
Method::GET,
&format!("/v1/deliverability/reputation/{domain_id}/history"),
&q,
None,
))
.await
}
pub async fn get_blocklist(&self, params: BlocklistQuery) -> Result<BlocklistResponse, Error> {
let q = params.to_query();
self.client
.request(self.client.build::<()>(
Method::GET,
"/v1/deliverability/blocklist",
&q,
None,
))
.await
}
pub async fn run_blocklist_check(&self, params: BlocklistCheckBody) -> Result<Value, Error> {
self.client
.request(self.client.build(
Method::POST,
"/v1/deliverability/blocklist/check",
&[],
Some(¶ms),
))
.await
}
pub async fn dismiss_alert(&self, alert_id: &str) -> Result<Value, Error> {
#[derive(Serialize)]
struct Body<'a> {
status: &'a str,
}
self.client
.request(self.client.build(
Method::PATCH,
&format!("/v1/deliverability/blocklist/alerts/{alert_id}"),
&[],
Some(&Body { status: "dismissed" }),
))
.await
}
pub async fn get_report(
&self,
params: DeliverabilityReportQuery,
) -> Result<DeliverabilityReport, Error> {
let q = params.to_query();
self.client
.request(self.client.build::<()>(
Method::GET,
"/v1/deliverability/report",
&q,
None,
))
.await
}
}
#[derive(Debug, Clone, Default)]
pub struct ReputationQuery {
pub domain_id: Option<String>,
pub days: Option<u32>,
}
impl ReputationQuery {
fn to_query(&self) -> Vec<(&'static str, String)> {
let mut q = Vec::new();
if let Some(v) = &self.domain_id {
q.push(("domain_id", v.clone()));
}
if let Some(v) = self.days {
q.push(("days", v.to_string()));
}
q
}
}
#[derive(Debug, Clone, Default)]
pub struct ReputationHistoryQuery {
pub from: Option<String>,
pub to: Option<String>,
}
impl ReputationHistoryQuery {
fn to_query(&self) -> Vec<(&'static str, String)> {
let mut q = Vec::new();
if let Some(v) = &self.from {
q.push(("from", v.clone()));
}
if let Some(v) = &self.to {
q.push(("to", v.clone()));
}
q
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct ReputationSnapshot {
pub date: String,
pub total_sent: u64,
pub total_delivered: u64,
pub total_bounced: u64,
pub total_complained: u64,
pub total_opened: u64,
pub total_clicked: u64,
pub delivery_rate: f64,
pub bounce_rate: f64,
pub complaint_rate: f64,
pub open_rate: f64,
pub click_rate: f64,
pub reputation_score: f64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ReputationFactors {
#[serde(rename = "bounceRate")]
pub bounce_rate: f64,
#[serde(rename = "complaintRate")]
pub complaint_rate: f64,
#[serde(rename = "deliveryRate")]
pub delivery_rate: f64,
#[serde(rename = "engagementRate")]
pub engagement_rate: f64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ReputationCurrent {
pub score: f64,
pub rating: String,
pub factors: ReputationFactors,
pub recommendations: Vec<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ReputationDomain {
pub domain_id: String,
pub domain_name: String,
pub score: f64,
pub rating: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ReputationResponse {
pub current: ReputationCurrent,
pub history: Vec<ReputationSnapshot>,
pub domains: Vec<ReputationDomain>,
}
#[derive(Debug, Clone, Default)]
pub struct BlocklistQuery {
pub target: Option<String>,
pub target_type: Option<String>,
}
impl BlocklistQuery {
fn to_query(&self) -> Vec<(&'static str, String)> {
let mut q = Vec::new();
if let Some(v) = &self.target {
q.push(("target", v.clone()));
}
if let Some(v) = &self.target_type {
q.push(("target_type", v.clone()));
}
q
}
}
#[derive(Debug, Clone, Serialize)]
pub struct BlocklistCheckBody {
pub target: String,
pub target_type: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BlocklistCheckItem {
pub id: String,
pub target: String,
pub target_type: String,
pub provider: String,
pub listed: bool,
pub listing_reason: Option<String>,
pub response_time_ms: Option<u32>,
pub checked_at: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BlocklistAlertItem {
pub id: String,
pub target: String,
pub target_type: String,
pub provider: String,
pub status: String,
pub listed_at: String,
pub resolved_at: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BlocklistSummary {
pub total_targets: u32,
pub listed_count: u32,
pub clean_count: u32,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BlocklistResponse {
pub checks: Vec<BlocklistCheckItem>,
pub alerts: Vec<BlocklistAlertItem>,
pub summary: BlocklistSummary,
}
#[derive(Debug, Clone, Default)]
pub struct DeliverabilityReportQuery {
pub domain_id: Option<String>,
pub days: Option<u32>,
}
impl DeliverabilityReportQuery {
fn to_query(&self) -> Vec<(&'static str, String)> {
let mut q = Vec::new();
if let Some(v) = &self.domain_id {
q.push(("domain_id", v.clone()));
}
if let Some(v) = self.days {
q.push(("days", v.to_string()));
}
q
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct ReportPeriod {
pub from: String,
pub to: String,
pub days: u32,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ReportMetrics {
pub total_sent: u64,
pub total_delivered: u64,
pub total_bounced: u64,
pub total_complained: u64,
pub delivery_rate: f64,
pub bounce_rate: f64,
pub complaint_rate: f64,
pub open_rate: f64,
pub click_rate: f64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ReportReputation {
pub score: f64,
pub rating: String,
pub trend: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ReportBlocklistStatus {
pub total_lists_checked: u32,
pub active_listings: u32,
pub clean: bool,
}
#[derive(Debug, Clone, Deserialize)]
pub struct InboxPlacementEstimate {
pub inbox_pct: f64,
pub spam_pct: f64,
pub missing_pct: f64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AuthenticationStatus {
pub spf: bool,
pub dkim: bool,
pub dmarc: bool,
pub bimi: bool,
}
#[derive(Debug, Clone, Deserialize)]
pub struct DeliverabilityReport {
pub period: ReportPeriod,
pub metrics: ReportMetrics,
pub reputation: ReportReputation,
pub blocklist_status: ReportBlocklistStatus,
pub inbox_placement_estimate: InboxPlacementEstimate,
pub recommendations: Vec<String>,
pub authentication: AuthenticationStatus,
}