use std::sync::Arc;
use std::time::Duration;
use bingle_core::api::bingle_api::{BingleApi, BingleError, ProgressCallback};
use bingle_local::api::pending_sender::{
MessageDelivery, NotSentReason, SendOutcome, SendReport, ShutdownReport,
};
use serde_json::Value as JsonValue;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SendTarget {
Handle(String),
Id(String),
}
impl SendTarget {
pub fn label(&self) -> &str {
match self {
SendTarget::Handle(h) => h,
SendTarget::Id(id) => id,
}
}
}
pub fn is_account_id(label: &str) -> bool {
label.len() == 58
&& label
.bytes()
.all(|b| b.is_ascii_uppercase() || (b'2'..=b'7').contains(&b))
}
pub struct ChatDelivery<A: BingleApi> {
api: Arc<A>,
}
impl<A: BingleApi> ChatDelivery<A> {
pub fn new(api: Arc<A>) -> Self {
Self { api }
}
}
impl<A: BingleApi> MessageDelivery for ChatDelivery<A> {
fn deliver(
&self,
recipient: &str,
message: JsonValue,
progress: Option<Arc<ProgressCallback>>,
) -> Result<bool, BingleError> {
let recipient = recipient.to_string();
if is_account_id(&recipient) {
self.api.send_message_to_id(&recipient, message, progress)
} else {
self.api
.send_message_to_handle(&recipient, message, progress)
}
}
}
pub const EXIT_FLUSH_DEADLINE: Duration = Duration::from_secs(15);
pub fn shutdown_lines(report: &ShutdownReport) -> Vec<String> {
let retry = "it will be retried next time you start chat";
let forwarded = report.forwarded.iter().map(|entry| {
format!(
"↪ {} is offline; queued to their mailbox — they'll get it when they reconnect",
entry.recipients.join(", ")
)
});
let not_sent = report.not_sent.iter().map(|(entry, reason)| {
let to = entry.recipients.join(", ");
match reason {
NotSentReason::StoreForwardOff => {
format!("! message to {to} not delivered (store-and-forward is off); {retry}")
}
NotSentReason::PostFailed => {
format!("! message to {to} could not be posted to the mailbox; {retry}")
}
NotSentReason::InFlight => {
format!("! message to {to} was still sending at exit; {retry}")
}
NotSentReason::DeadlineReached => {
format!("! message to {to} not delivered before exit; {retry}")
}
}
});
forwarded.chain(not_sent).collect()
}
pub fn report_line(report: &SendReport) -> Option<String> {
let to = report.recipients.join(", ");
match (&report.outcome, report.previously_failed) {
(SendOutcome::Delivered, false) => None,
(SendOutcome::Delivered, true) => Some(format!("✓ delivered to {to}")),
(SendOutcome::Forwarded(_), _) => Some(format!(
"↪ {to} is offline; queued to their mailbox — they'll get it when they reconnect"
)),
(SendOutcome::Retrying(reason), false) => Some(format!(
"! send to {to} not delivered ({reason}); will keep retrying…"
)),
(SendOutcome::Retrying(_), true) => None,
(SendOutcome::Failed(reason), _) => Some(format!("! send to {to} failed: {reason}")),
}
}