Fixing code warnings

This commit is contained in:
Timmy
2026-03-24 21:26:48 +00:00
parent df6d2db327
commit 721d12bcfe
3 changed files with 45 additions and 52 deletions

View File

@@ -15,9 +15,6 @@ use async_trait::async_trait;
/// producing and consuming these identifiers. /// producing and consuming these identifiers.
pub type MessageId = String; pub type MessageId = String;
/// A platform-agnostic identifier for a chat room / channel / conversation.
pub type RoomId = String;
/// Abstraction over a chat platform's message-sending capabilities. /// Abstraction over a chat platform's message-sending capabilities.
/// ///
/// Implementations must be `Send + Sync` so they can be shared across /// Implementations must be `Send + Sync` so they can be shared across

View File

@@ -15,8 +15,8 @@ use tokio::sync::Mutex as TokioMutex;
use crate::agents::AgentPool; use crate::agents::AgentPool;
use crate::chat::transport::matrix::{ConversationEntry, ConversationRole, RoomConversation}; use crate::chat::transport::matrix::{ConversationEntry, ConversationRole, RoomConversation};
use crate::slog;
use crate::chat::{ChatTransport, MessageId}; use crate::chat::{ChatTransport, MessageId};
use crate::slog;
// ── API base URLs (overridable for tests) ──────────────────────────────── // ── API base URLs (overridable for tests) ────────────────────────────────
@@ -46,6 +46,7 @@ const OUTSIDE_WINDOW_ERR: &str = "OUTSIDE_MESSAGING_WINDOW";
/// between free-form text and a template message. /// between free-form text and a template message.
pub struct MessagingWindowTracker { pub struct MessagingWindowTracker {
last_message: std::sync::Mutex<HashMap<String, std::time::Instant>>, last_message: std::sync::Mutex<HashMap<String, std::time::Instant>>,
#[allow(dead_code)] // Used by Meta provider path (is_within_window → send_notification)
window_duration: std::time::Duration, window_duration: std::time::Duration,
} }
@@ -83,6 +84,7 @@ impl MessagingWindowTracker {
/// Returns `true` when the last inbound message from `phone` arrived within /// Returns `true` when the last inbound message from `phone` arrived within
/// the 24-hour window, meaning free-form replies are still permitted. /// the 24-hour window, meaning free-form replies are still permitted.
#[allow(dead_code)] // Used by Meta provider path (send_notification)
pub fn is_within_window(&self, phone: &str) -> bool { pub fn is_within_window(&self, phone: &str) -> bool {
let map = self.last_message.lock().unwrap(); let map = self.last_message.lock().unwrap();
match map.get(phone) { match map.get(phone) {
@@ -105,6 +107,7 @@ pub struct WhatsAppTransport {
client: reqwest::Client, client: reqwest::Client,
/// Name of the approved Meta message template used for notifications /// Name of the approved Meta message template used for notifications
/// outside the 24-hour messaging window. /// outside the 24-hour messaging window.
#[allow(dead_code)] // Used by Meta provider path (send_template_notification)
notification_template_name: String, notification_template_name: String,
/// Optional base URL override for tests. /// Optional base URL override for tests.
api_base: String, api_base: String,
@@ -126,11 +129,7 @@ impl WhatsAppTransport {
} }
#[cfg(test)] #[cfg(test)]
fn with_api_base( fn with_api_base(phone_number_id: String, access_token: String, api_base: String) -> Self {
phone_number_id: String,
access_token: String,
api_base: String,
) -> Self {
Self { Self {
phone_number_id, phone_number_id,
access_token, access_token,
@@ -209,6 +208,7 @@ impl WhatsAppTransport {
/// ///
/// The template body is expected to accept two positional parameters: /// The template body is expected to accept two positional parameters:
/// `{{1}}` = story name, `{{2}}` = pipeline stage. /// `{{1}}` = story name, `{{2}}` = pipeline stage.
#[allow(dead_code)] // Meta provider path — template fallback for expired 24h window
pub async fn send_template_notification( pub async fn send_template_notification(
&self, &self,
to: &str, to: &str,
@@ -282,6 +282,7 @@ impl WhatsAppTransport {
/// ///
/// This method never crashes on a messaging-window error — it always /// This method never crashes on a messaging-window error — it always
/// attempts the template fallback and logs what happened. /// attempts the template fallback and logs what happened.
#[allow(dead_code)] // Meta provider path — window-aware notification dispatch
pub async fn send_notification( pub async fn send_notification(
&self, &self,
to: &str, to: &str,
@@ -423,7 +424,11 @@ impl TwilioWhatsAppTransport {
format!("whatsapp:{}", to) format!("whatsapp:{}", to)
}; };
let params = [("From", from.as_str()), ("To", to_wa.as_str()), ("Body", body)]; let params = [
("From", from.as_str()),
("To", to_wa.as_str()),
("Body", body),
];
let resp = self let resp = self
.client .client
@@ -444,9 +449,8 @@ impl TwilioWhatsAppTransport {
return Err(format!("Twilio API returned {status}: {resp_text}")); return Err(format!("Twilio API returned {status}: {resp_text}"));
} }
let parsed: TwilioSendResponse = serde_json::from_str(&resp_text).map_err(|e| { let parsed: TwilioSendResponse = serde_json::from_str(&resp_text)
format!("Failed to parse Twilio API response: {e} — body: {resp_text}") .map_err(|e| format!("Failed to parse Twilio API response: {e} — body: {resp_text}"))?;
})?;
Ok(parsed.sid.unwrap_or_default()) Ok(parsed.sid.unwrap_or_default())
} }
@@ -472,7 +476,9 @@ impl ChatTransport for TwilioWhatsAppTransport {
html: &str, html: &str,
) -> Result<(), String> { ) -> Result<(), String> {
// Twilio does not support message editing — send a new message. // Twilio does not support message editing — send a new message.
slog!("[whatsapp/twilio] edit_message — Twilio does not support edits, sending new message"); slog!(
"[whatsapp/twilio] edit_message — Twilio does not support edits, sending new message"
);
self.send_message(recipient, plain, html).await.map(|_| ()) self.send_message(recipient, plain, html).await.map(|_| ())
} }
@@ -525,10 +531,7 @@ pub fn extract_twilio_text_messages(bytes: &[u8]) -> Vec<(String, String)> {
}; };
// Strip the "whatsapp:" prefix so the sender is stored as a plain phone number. // Strip the "whatsapp:" prefix so the sender is stored as a plain phone number.
let sender = from let sender = from.strip_prefix("whatsapp:").unwrap_or(&from).to_string();
.strip_prefix("whatsapp:")
.unwrap_or(&from)
.to_string();
vec![(sender, body)] vec![(sender, body)]
} }
@@ -575,6 +578,7 @@ struct GraphApiError {
// ── Template message types ────────────────────────────────────────────── // ── Template message types ──────────────────────────────────────────────
#[allow(dead_code)] // Meta provider path — template message types
#[derive(Serialize)] #[derive(Serialize)]
struct GraphTemplateMessage<'a> { struct GraphTemplateMessage<'a> {
messaging_product: &'a str, messaging_product: &'a str,
@@ -583,6 +587,7 @@ struct GraphTemplateMessage<'a> {
template: GraphTemplate<'a>, template: GraphTemplate<'a>,
} }
#[allow(dead_code)]
#[derive(Serialize)] #[derive(Serialize)]
struct GraphTemplate<'a> { struct GraphTemplate<'a> {
name: &'a str, name: &'a str,
@@ -590,17 +595,20 @@ struct GraphTemplate<'a> {
components: Vec<GraphTemplateComponent>, components: Vec<GraphTemplateComponent>,
} }
#[allow(dead_code)]
#[derive(Serialize)] #[derive(Serialize)]
struct GraphLanguage { struct GraphLanguage {
code: &'static str, code: &'static str,
} }
#[allow(dead_code)]
#[derive(Serialize)] #[derive(Serialize)]
struct GraphTemplateComponent { struct GraphTemplateComponent {
r#type: &'static str, r#type: &'static str,
parameters: Vec<GraphTemplateParameter>, parameters: Vec<GraphTemplateParameter>,
} }
#[allow(dead_code)]
#[derive(Serialize)] #[derive(Serialize)]
struct GraphTemplateParameter { struct GraphTemplateParameter {
r#type: &'static str, r#type: &'static str,
@@ -631,11 +639,13 @@ pub struct WebhookChange {
pub struct WebhookValue { pub struct WebhookValue {
#[serde(default)] #[serde(default)]
pub messages: Vec<WebhookMessage>, pub messages: Vec<WebhookMessage>,
#[allow(dead_code)] // Present in Meta webhook JSON, kept for deserialization
pub metadata: Option<WebhookMetadata>, pub metadata: Option<WebhookMetadata>,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct WebhookMetadata { pub struct WebhookMetadata {
#[allow(dead_code)]
pub phone_number_id: Option<String>, pub phone_number_id: Option<String>,
} }
@@ -733,9 +743,7 @@ struct PersistedWhatsAppHistory {
const WHATSAPP_HISTORY_FILE: &str = ".storkit/whatsapp_history.json"; const WHATSAPP_HISTORY_FILE: &str = ".storkit/whatsapp_history.json";
/// Load WhatsApp conversation history from disk. /// Load WhatsApp conversation history from disk.
pub fn load_whatsapp_history( pub fn load_whatsapp_history(project_root: &std::path::Path) -> HashMap<String, RoomConversation> {
project_root: &std::path::Path,
) -> HashMap<String, RoomConversation> {
let path = project_root.join(WHATSAPP_HISTORY_FILE); let path = project_root.join(WHATSAPP_HISTORY_FILE);
let data = match std::fs::read_to_string(&path) { let data = match std::fs::read_to_string(&path) {
Ok(d) => d, Ok(d) => d,
@@ -828,9 +836,7 @@ pub async fn webhook_verify(
&& let Some(challenge) = q.hub_challenge && let Some(challenge) = q.hub_challenge
{ {
slog!("[whatsapp] Webhook verification succeeded"); slog!("[whatsapp] Webhook verification succeeded");
return Response::builder() return Response::builder().status(StatusCode::OK).body(challenge);
.status(StatusCode::OK)
.body(challenge);
} }
slog!("[whatsapp] Webhook verification failed"); slog!("[whatsapp] Webhook verification failed");
Response::builder() Response::builder()
@@ -897,17 +903,11 @@ pub async fn webhook_receive(
} }
}); });
Response::builder() Response::builder().status(StatusCode::OK).body("ok")
.status(StatusCode::OK)
.body("ok")
} }
/// Dispatch an incoming WhatsApp message to bot commands. /// Dispatch an incoming WhatsApp message to bot commands.
async fn handle_incoming_message( async fn handle_incoming_message(ctx: &WhatsAppWebhookContext, sender: &str, message: &str) {
ctx: &WhatsAppWebhookContext,
sender: &str,
message: &str,
) {
use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command}; use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command};
// Record this inbound message to keep the 24-hour window open. // Record this inbound message to keep the 24-hour window open.
@@ -950,12 +950,12 @@ async fn handle_incoming_message(
HtopCommand::Start { duration_secs } => { HtopCommand::Start { duration_secs } => {
// On WhatsApp, send a single snapshot instead of a live-updating // On WhatsApp, send a single snapshot instead of a live-updating
// dashboard since we can't edit messages. // dashboard since we can't edit messages.
let snapshot = let snapshot = crate::chat::transport::matrix::htop::build_htop_message(
crate::chat::transport::matrix::htop::build_htop_message(&ctx.agents, 0, duration_secs); &ctx.agents,
let _ = ctx 0,
.transport duration_secs,
.send_message(sender, &snapshot, "") );
.await; let _ = ctx.transport.send_message(sender, &snapshot, "").await;
} }
} }
return; return;
@@ -991,22 +991,16 @@ async fn handle_incoming_message(
} }
/// Forward a message to Claude Code and send the response back via WhatsApp. /// Forward a message to Claude Code and send the response back via WhatsApp.
async fn handle_llm_message( async fn handle_llm_message(ctx: &WhatsAppWebhookContext, sender: &str, user_message: &str) {
ctx: &WhatsAppWebhookContext,
sender: &str,
user_message: &str,
) {
use crate::llm::providers::claude_code::{ClaudeCodeProvider, ClaudeCodeResult};
use crate::chat::transport::matrix::drain_complete_paragraphs; use crate::chat::transport::matrix::drain_complete_paragraphs;
use crate::llm::providers::claude_code::{ClaudeCodeProvider, ClaudeCodeResult};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use tokio::sync::watch; use tokio::sync::watch;
// Look up existing session ID for this sender. // Look up existing session ID for this sender.
let resume_session_id: Option<String> = { let resume_session_id: Option<String> = {
let guard = ctx.history.lock().await; let guard = ctx.history.lock().await;
guard guard.get(sender).and_then(|conv| conv.session_id.clone())
.get(sender)
.and_then(|conv| conv.session_id.clone())
}; };
let bot_name = &ctx.bot_name; let bot_name = &ctx.bot_name;
@@ -1077,9 +1071,7 @@ async fn handle_llm_message(
let last_text = messages let last_text = messages
.iter() .iter()
.rev() .rev()
.find(|m| { .find(|m| m.role == crate::llm::types::Role::Assistant && !m.content.is_empty())
m.role == crate::llm::types::Role::Assistant && !m.content.is_empty()
})
.map(|m| m.content.clone()) .map(|m| m.content.clone())
.unwrap_or_default(); .unwrap_or_default();
if !last_text.is_empty() { if !last_text.is_empty() {
@@ -1220,7 +1212,10 @@ mod tests {
let result = transport.send_message("15551234567", "hello", "").await; let result = transport.send_message("15551234567", "hello", "").await;
assert!(result.is_err()); assert!(result.is_err());
let msg = result.unwrap_err(); let msg = result.unwrap_err();
assert!(msg.contains("24-hour messaging window"), "unexpected: {msg}"); assert!(
msg.contains("24-hour messaging window"),
"unexpected: {msg}"
);
} }
// ── send_template_notification ──────────────────────────────────── // ── send_template_notification ────────────────────────────────────

View File

@@ -414,7 +414,8 @@ async fn main() -> Result<(), std::io::Error> {
startup_agents.auto_assign_available_work(&root).await; startup_agents.auto_assign_available_work(&root).await;
}); });
} }
let addr = format!("127.0.0.1:{port}"); let host = std::env::var("STORKIT_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
let addr = format!("{host}:{port}");
println!( println!(
"\x1b[95;1m ____ _ _ ___ _ \n / ___|| |_ ___ _ __| | _|_ _| |_ \n \\___ \\| __/ _ \\| '__| |/ /| || __|\n ___) | || (_) | | | < | || |_ \n |____/ \\__\\___/|_| |_|\\_\\___|\\__|\n\x1b[0m" "\x1b[95;1m ____ _ _ ___ _ \n / ___|| |_ ___ _ __| | _|_ _| |_ \n \\___ \\| __/ _ \\| '__| |/ /| || __|\n ___) | || (_) | | | < | || |_ \n |____/ \\__\\___/|_| |_|\\_\\___|\\__|\n\x1b[0m"