storkit: merge 404_bug_whatsapp_and_slack_missing_assign_command_handler

This commit is contained in:
dave
2026-03-27 15:07:15 +00:00
parent b4eeb499e9
commit a9cdd3a354
2 changed files with 166 additions and 0 deletions
+83
View File
@@ -939,6 +939,32 @@ async fn handle_incoming_message(
return; return;
} }
if let Some(assign_cmd) = crate::chat::transport::matrix::assign::extract_assign_command(
message,
&ctx.bot_name,
&ctx.bot_user_id,
) {
let response = match assign_cmd {
crate::chat::transport::matrix::assign::AssignCommand::Assign { story_number, model } => {
slog!("[slack] Handling assign command from {user} in {channel}: story {story_number} model {model}");
crate::chat::transport::matrix::assign::handle_assign(
&ctx.bot_name,
&story_number,
&model,
&ctx.project_root,
&ctx.agents,
)
.await
}
crate::chat::transport::matrix::assign::AssignCommand::BadArgs => {
format!("Usage: `{} assign <number> <model>`", ctx.bot_name)
}
};
let response = markdown_to_slack(&response);
let _ = ctx.transport.send_message(channel, &response, "").await;
return;
}
// No command matched — forward to LLM for conversational response. // No command matched — forward to LLM for conversational response.
slog!("[slack] No command matched, forwarding to LLM for {user} in {channel}"); slog!("[slack] No command matched, forwarding to LLM for {user} in {channel}");
handle_llm_message(ctx, channel, user, message).await; handle_llm_message(ctx, channel, user, message).await;
@@ -1899,4 +1925,61 @@ mod tests {
); );
assert!(result.is_none(), "'help' should not be recognised as start"); assert!(result.is_none(), "'help' should not be recognised as start");
} }
// ── assign command extraction ──────────────────────────────────────
#[test]
fn assign_command_extracted_from_plain_message_slack() {
let result = crate::chat::transport::matrix::assign::extract_assign_command(
"assign 42 opus",
"Timmy",
"@timmy:home.local",
);
assert!(
matches!(
result,
Some(crate::chat::transport::matrix::assign::AssignCommand::Assign { .. })
),
"plain 'assign 42 opus' should be recognised on Slack"
);
}
#[test]
fn assign_command_extracted_with_bot_name_prefix_slack() {
let result = crate::chat::transport::matrix::assign::extract_assign_command(
"Timmy assign 42 sonnet",
"Timmy",
"@timmy:home.local",
);
assert!(
matches!(
result,
Some(crate::chat::transport::matrix::assign::AssignCommand::Assign { .. })
),
"'Timmy assign 42 sonnet' should be recognised on Slack"
);
}
#[test]
fn assign_command_returns_bad_args_without_model_slack() {
let result = crate::chat::transport::matrix::assign::extract_assign_command(
"assign 42",
"Timmy",
"@timmy:home.local",
);
assert_eq!(
result,
Some(crate::chat::transport::matrix::assign::AssignCommand::BadArgs)
);
}
#[test]
fn non_assign_slack_message_not_extracted() {
let result = crate::chat::transport::matrix::assign::extract_assign_command(
"status",
"Timmy",
"@timmy:home.local",
);
assert!(result.is_none(), "'status' should not be recognised as assign on Slack");
}
} }
@@ -220,6 +220,32 @@ pub(super) async fn handle_incoming_message(ctx: &WhatsAppWebhookContext, sender
return; return;
} }
if let Some(assign_cmd) = crate::chat::transport::matrix::assign::extract_assign_command(
message,
&ctx.bot_name,
&ctx.bot_user_id,
) {
let response = match assign_cmd {
crate::chat::transport::matrix::assign::AssignCommand::Assign { story_number, model } => {
slog!("[whatsapp] Handling assign command from {sender}: story {story_number} model {model}");
crate::chat::transport::matrix::assign::handle_assign(
&ctx.bot_name,
&story_number,
&model,
&ctx.project_root,
&ctx.agents,
)
.await
}
crate::chat::transport::matrix::assign::AssignCommand::BadArgs => {
format!("Usage: `{} assign <number> <model>`", ctx.bot_name)
}
};
let formatted = markdown_to_whatsapp(&response);
let _ = ctx.transport.send_message(sender, &formatted, "").await;
return;
}
// No command matched — forward to LLM for conversational response. // No command matched — forward to LLM for conversational response.
slog!("[whatsapp] No command matched, forwarding to LLM for {sender}"); slog!("[whatsapp] No command matched, forwarding to LLM for {sender}");
handle_llm_message(ctx, sender, message).await; handle_llm_message(ctx, sender, message).await;
@@ -707,4 +733,61 @@ mod tests {
); );
assert!(result.is_none(), "'status' should not be recognised as rmtree"); assert!(result.is_none(), "'status' should not be recognised as rmtree");
} }
// ── assign command extraction ──────────────────────────────────────
#[test]
fn assign_command_extracted_from_plain_message() {
let result = crate::chat::transport::matrix::assign::extract_assign_command(
"assign 42 opus",
"Timmy",
"@timmy:home.local",
);
assert!(
matches!(
result,
Some(crate::chat::transport::matrix::assign::AssignCommand::Assign { .. })
),
"plain 'assign 42 opus' should be recognised"
);
}
#[test]
fn assign_command_extracted_with_bot_name_prefix() {
let result = crate::chat::transport::matrix::assign::extract_assign_command(
"Timmy assign 42 sonnet",
"Timmy",
"@timmy:home.local",
);
assert!(
matches!(
result,
Some(crate::chat::transport::matrix::assign::AssignCommand::Assign { .. })
),
"'Timmy assign 42 sonnet' should be recognised"
);
}
#[test]
fn assign_command_returns_bad_args_without_model() {
let result = crate::chat::transport::matrix::assign::extract_assign_command(
"assign 42",
"Timmy",
"@timmy:home.local",
);
assert_eq!(
result,
Some(crate::chat::transport::matrix::assign::AssignCommand::BadArgs)
);
}
#[test]
fn non_assign_whatsapp_message_not_extracted() {
let result = crate::chat::transport::matrix::assign::extract_assign_command(
"status",
"Timmy",
"@timmy:home.local",
);
assert!(result.is_none(), "'status' should not be recognised as assign");
}
} }