Files
imks/svc/scheduled.rs
T
zhenyi 821537186e refactor(tests): reformat code and update dependency management
- 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
2026-06-11 12:11:05 +08:00

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)
}
}