821537186e
- Reorganized import statements in adapter tests for better readability - Replaced or_insert_with(Vec::new) with or_default() in test closures - Updated Cargo.lock with new dependency versions and checksums - Added TLS features to tonic dependency configuration - Included sqlx, chrono, and uuid dependencies with specific features - Added jsonwebtoken and arc-swap as project dependencies - Reformatted assertion statements to comply with line length limits - Adjusted base64 import order in engine codec module - Updated protobuf include statement formatting
81 lines
2.9 KiB
Rust
81 lines
2.9 KiB
Rust
//! Scheduled message dispatcher on `MessageService`.
|
|
//!
|
|
//! A background task that periodically scans for due scheduled messages
|
|
//! and sends them through the normal message path.
|
|
|
|
use std::time::Duration;
|
|
|
|
use crate::repo::CreateMessageInput;
|
|
|
|
use super::message::MessageService;
|
|
|
|
impl MessageService {
|
|
/// Start the background scheduled-message dispatcher.
|
|
/// Scans every 30 seconds for pending messages whose `scheduled_at` has passed.
|
|
pub fn start_scheduled_dispatcher(self: std::sync::Arc<Self>) {
|
|
tokio::spawn(async move {
|
|
tracing::info!("Scheduled message dispatcher started (interval: 30s)");
|
|
loop {
|
|
tokio::time::sleep(Duration::from_secs(30)).await;
|
|
|
|
match self.process_due_scheduled().await {
|
|
Ok(count) => {
|
|
if count > 0 {
|
|
tracing::info!(count, "Dispatched scheduled messages");
|
|
}
|
|
}
|
|
Err(e) => {
|
|
tracing::error!(error = %e, "Scheduled message dispatch failed");
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/// Fetch and dispatch all due scheduled messages.
|
|
async fn process_due_scheduled(&self) -> crate::ImksResult<usize> {
|
|
let due = self.repo.get_due_scheduled().await?;
|
|
let mut dispatched = 0;
|
|
|
|
for scheduled in due {
|
|
let input = CreateMessageInput {
|
|
channel_id: scheduled.channel_id,
|
|
author_id: scheduled.author_id,
|
|
thread_id: scheduled.thread_id,
|
|
reply_to_message_id: scheduled.reply_to_message_id,
|
|
message_type: "text".into(),
|
|
body: scheduled.body.clone(),
|
|
metadata: scheduled.metadata.clone(),
|
|
system: false,
|
|
};
|
|
|
|
match self.repo.create(&input).await {
|
|
Ok(message) => {
|
|
self.repo
|
|
.mark_scheduled_sent(scheduled.id, message.id)
|
|
.await?;
|
|
|
|
// Broadcast to channel
|
|
if let Some(ns) = self.namespaces.get_namespace("/") {
|
|
ns.emit_to_room(
|
|
&scheduled.channel_id.to_string(),
|
|
"message:new",
|
|
serde_json::to_value(&message).unwrap_or_default(),
|
|
)
|
|
.await;
|
|
}
|
|
dispatched += 1;
|
|
}
|
|
Err(e) => {
|
|
tracing::error!(scheduled_id = %scheduled.id, error = %e, "Failed to send scheduled message");
|
|
self.repo
|
|
.mark_scheduled_failed(scheduled.id, &e.to_string())
|
|
.await?;
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(dispatched)
|
|
}
|
|
}
|