refactor(send): apply ponytail audit cuts 2, 4, 6

- updated_sequence_task: clone the Task and mutate the two fields
  instead of rebuilding all 12 by hand (-22 lines; new fields no
  longer need a sync here)
- unify unix_now with db::now_f64 (unix_now() = now_f64() as i64),
  moved to db.rs next to its clock source
- classify_to_send_error takes the MediaFetchFailure label, folding
  the duplicated inline match in send_batch_via_upload (-8 lines)
This commit is contained in:
2026-09-07 19:19:56 +08:00
parent 89c4642e1c
commit 2f741e5f4b
4 changed files with 40 additions and 57 deletions
+6
View File
@@ -160,3 +160,9 @@ pub fn now_f64() -> f64 {
.map(|d| d.as_secs_f64())
.unwrap_or(0.0)
}
/// Unix timestamp in whole seconds. Same clock as [`now_f64`], for fields
/// that store integer seconds (chat-state expiry, edit prompts).
pub fn unix_now() -> i64 {
now_f64() as i64
}
+1 -1
View File
@@ -3,8 +3,8 @@
use super::urls::enqueue_retry;
use super::{CHAT_STORE, CONFIG, TASK_QUEUE};
use crate::db::unix_now;
use crate::send::{self, Task};
use crate::state::unix_now;
use teloxide::RequestError;
use teloxide::prelude::*;
use teloxide::types::{CallbackQuery, ChatId, MessageId, ParseMode};
+31 -48
View File
@@ -3,12 +3,13 @@
//! URL is blocked by hotlink protection; the bot downloads the file itself
//! and uploads it via multipart).
use crate::db::unix_now;
use crate::handlers::{CHAT_STORE, LINK_CACHE, TASK_QUEUE, log_key};
use crate::link_cache::{CachedMedia, CachedMediaKind, CachedPost, LinkCache};
use crate::media_sender::MediaSender;
use crate::photo::{self, MAX_UPLOAD_BYTES, PhotoPrep};
use crate::queue::QueueError;
use crate::state::{EditMessage, unix_now};
use crate::state::EditMessage;
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
@@ -392,8 +393,7 @@ pub enum SendError {
Retryable { delay_seconds: f64, task: Box<Task> },
Permanent { message: String, task: Box<Task> },
}
fn classify_to_send_error(e: &RequestError, task: Task) -> SendError {
fn classify_to_send_error(e: &RequestError, task: Task, fetch_failure_label: &str) -> SendError {
match classify_request_error(e) {
Classification::Retryable { delay_seconds } => SendError::Retryable {
delay_seconds,
@@ -404,7 +404,7 @@ fn classify_to_send_error(e: &RequestError, task: Task) -> SendError {
task: Box::new(task),
},
Classification::MediaFetchFailure => SendError::Permanent {
message: "media fetch failed".into(),
message: fetch_failure_label.into(),
task: Box::new(task),
},
}
@@ -879,54 +879,24 @@ async fn send_batch_via_upload(
drop(keep_alive);
match result {
Ok(messages) => Ok(messages),
Err(e) => Err(match classify_request_error(&e) {
Classification::Retryable { delay_seconds } => SendError::Retryable {
delay_seconds,
task: Box::new(task.clone()),
},
Classification::Permanent { message } => SendError::Permanent {
message,
task: Box::new(task),
},
Classification::MediaFetchFailure => SendError::Permanent {
message: "upload failed".into(),
task: Box::new(task),
},
}),
Err(e) => Err(classify_to_send_error(&e, task, "upload failed")),
}
}
fn updated_sequence_task(task: &Task, batch_index: usize, sent_message_ids: Vec<i64>) -> Task {
match task {
let mut updated = task.clone();
match &mut updated {
Task::SendMediaSequence {
chat_id,
reply_to_message_id,
caption,
media_batches,
batch_index: _,
sent_message_ids: _,
source_url,
edit_before_forward,
forward_channel_id,
notify_chat_id,
notify_message_id,
cache_data,
} => Task::SendMediaSequence {
chat_id: *chat_id,
reply_to_message_id: *reply_to_message_id,
caption: caption.clone(),
media_batches: media_batches.clone(),
batch_index,
sent_message_ids,
source_url: source_url.clone(),
edit_before_forward: *edit_before_forward,
forward_channel_id: *forward_channel_id,
notify_chat_id: *notify_chat_id,
notify_message_id: *notify_message_id,
cache_data: cache_data.clone(),
},
batch_index: index,
sent_message_ids: ids,
..
} => {
*index = batch_index;
*ids = sent_message_ids;
}
_ => unreachable!("updated_sequence_task requires a SendMediaSequence task"),
}
updated
}
/// Sends the media batches starting at `task.batch_index`, extending
@@ -1014,6 +984,7 @@ pub async fn send_media_sequence(
return Err(classify_to_send_error(
&e,
updated_sequence_task(task, idx, sent),
"media fetch failed",
));
}
}
@@ -1115,13 +1086,21 @@ pub async fn send_animation(sender: &dyn MediaSender, task: &Task) -> Result<Vec
cache_animation_send(task, &message).await;
Ok(vec![id])
}
Err(e) => Err(classify_to_send_error(&e, task.clone())),
Err(e) => Err(classify_to_send_error(
&e,
task.clone(),
"media fetch failed",
)),
}
}
Err(e) => Err(SendError::from_fallback(e, task.clone())),
}
}
Err(e) => Err(classify_to_send_error(&e, task.clone())),
Err(e) => Err(classify_to_send_error(
&e,
task.clone(),
"media fetch failed",
)),
}
}
@@ -1158,7 +1137,11 @@ pub async fn forward_messages(sender: &dyn MediaSender, task: &Task) -> Result<(
);
Ok(())
}
Err(e) => Err(classify_to_send_error(&e, task.clone())),
Err(e) => Err(classify_to_send_error(
&e,
task.clone(),
"media fetch failed",
)),
}
}
+2 -8
View File
@@ -1,12 +1,13 @@
//! Per-chat state with SQLite persistence (table `chat_state` in
//! `data/task_queue.db`, shared with the task queue).
use crate::db::unix_now;
use parking_lot::Mutex;
use rusqlite::params;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::time::Duration;
#[derive(Serialize, Deserialize, Default, Clone, Debug)]
pub struct ChatData {
@@ -40,13 +41,6 @@ pub struct ChatStore {
pool: Arc<crate::db::DbPool>,
}
pub fn unix_now() -> i64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs() as i64)
.unwrap_or(0)
}
impl ChatStore {
/// Wraps the shared DB pool (schema initialized once by
/// [`crate::db::open_store`]; the `chat_state` table lives in the merged