forge_notify: add issue/PR number to header, drop repo: line, show assignee on new items (closes #225)
This commit is contained in:
parent
c99261b042
commit
20bb343f27
1 changed files with 42 additions and 26 deletions
|
|
@ -154,6 +154,12 @@ fn truncate(s: &str, max: usize) -> String {
|
||||||
/// Build a human-readable wake message for one Forgejo notification.
|
/// Build a human-readable wake message for one Forgejo notification.
|
||||||
/// Fetches the subject and (if present) the latest comment to include
|
/// Fetches the subject and (if present) the latest comment to include
|
||||||
/// author and body. Falls back gracefully when API calls fail.
|
/// author and body. Falls back gracefully when API calls fail.
|
||||||
|
///
|
||||||
|
/// Format: `[kind #N] title\nurl: <url>\n\nauthor: body` (comments)
|
||||||
|
/// `[kind #N] title\nurl: <url>\nassignee: <login|unassigned>` (new items)
|
||||||
|
///
|
||||||
|
/// Number is extracted from `html_url` (last path segment before any `#`).
|
||||||
|
/// `repo:` line is omitted — single-repo deployments don't benefit from the noise.
|
||||||
async fn format_notification(
|
async fn format_notification(
|
||||||
client: &reqwest::Client,
|
client: &reqwest::Client,
|
||||||
token: &str,
|
token: &str,
|
||||||
|
|
@ -164,7 +170,16 @@ async fn format_notification(
|
||||||
let html_url = notif["subject"]["html_url"]
|
let html_url = notif["subject"]["html_url"]
|
||||||
.as_str()
|
.as_str()
|
||||||
.unwrap_or_else(|| notif["subject"]["url"].as_str().unwrap_or(""));
|
.unwrap_or_else(|| notif["subject"]["url"].as_str().unwrap_or(""));
|
||||||
let repo = notif["repository"]["full_name"].as_str().unwrap_or("?");
|
|
||||||
|
// Extract issue/PR number from the html_url. URL ends with /issues/N or
|
||||||
|
// /pulls/N (possibly followed by #anchor for comments). Best-effort.
|
||||||
|
let num = html_url
|
||||||
|
.split('#')
|
||||||
|
.next()
|
||||||
|
.and_then(|u| u.rsplit('/').next())
|
||||||
|
.and_then(|s| s.parse::<u64>().ok())
|
||||||
|
.map(|n| format!(" #{n}"))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
// API URLs for fetching content
|
// API URLs for fetching content
|
||||||
let subject_api_url = notif["subject"]["url"].as_str().unwrap_or("");
|
let subject_api_url = notif["subject"]["url"].as_str().unwrap_or("");
|
||||||
|
|
@ -190,12 +205,9 @@ async fn format_notification(
|
||||||
.unwrap_or("")
|
.unwrap_or("")
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
let kind = format!("comment on {}", notif_type_label(notif_type));
|
let kind = format!("comment on {}{num}", notif_type_label(notif_type));
|
||||||
let mut out = format!(
|
let url = if comment_html_url.is_empty() { html_url } else { comment_html_url };
|
||||||
"[{kind}] {title}\nrepo: {repo}\nurl: {}\n\n{author}: {}\n",
|
let mut out = format!("[{kind}] {title}\nurl: {url}\n\n{author}: {}", truncate(body, BODY_TRUNCATE));
|
||||||
if comment_html_url.is_empty() { html_url } else { comment_html_url },
|
|
||||||
truncate(body, BODY_TRUNCATE),
|
|
||||||
);
|
|
||||||
if out.ends_with('\n') {
|
if out.ends_with('\n') {
|
||||||
out.pop();
|
out.pop();
|
||||||
}
|
}
|
||||||
|
|
@ -213,29 +225,33 @@ async fn format_notification(
|
||||||
|
|
||||||
let label = notif_type_label(notif_type);
|
let label = notif_type_label(notif_type);
|
||||||
let kind = match notif_state {
|
let kind = match notif_state {
|
||||||
"merged" => format!("{label} merged"),
|
"merged" => format!("{label} merged{num}"),
|
||||||
"closed" => format!("{label} closed"),
|
"closed" => format!("{label} closed{num}"),
|
||||||
"open" | "" => format!("new {label}"),
|
"open" | "" => format!("new {label}{num}"),
|
||||||
other => format!("{label}: {other}"),
|
other => format!("{label}{num}: {other}"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fetch subject only for body/author on new (open) items — not
|
let mut out = format!("[{kind}] {title}\nurl: {html_url}");
|
||||||
// worth an extra HTTP round-trip for already-closed/merged ones.
|
|
||||||
let is_open = notif_state == "open" || notif_state.is_empty();
|
// For new (open) items: fetch assignee(s). Skip body — agents can
|
||||||
let mut out = format!("[{kind}] {title}\nrepo: {repo}\nurl: {html_url}");
|
// run `hive-forge view <n>` for full content. One extra API call
|
||||||
if is_open {
|
// only on creation events, not state changes.
|
||||||
|
let is_new = notif_state == "open" || notif_state.is_empty();
|
||||||
|
if is_new {
|
||||||
let subject = fetch_json(client, subject_api_url, token).await;
|
let subject = fetch_json(client, subject_api_url, token).await;
|
||||||
let author = subject
|
let assignees: Vec<&str> = subject
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|s| s["user"]["login"].as_str())
|
.and_then(|s| s["assignees"].as_array())
|
||||||
.unwrap_or("?");
|
.map(|arr| {
|
||||||
let body = subject
|
arr.iter()
|
||||||
.as_ref()
|
.filter_map(|a| a["login"].as_str())
|
||||||
.and_then(|s| s["body"].as_str())
|
.collect()
|
||||||
.unwrap_or("")
|
})
|
||||||
.trim();
|
.unwrap_or_default();
|
||||||
if !body.is_empty() {
|
if assignees.is_empty() {
|
||||||
out.push_str(&format!("\n\n{author}: {}", truncate(body, BODY_TRUNCATE)));
|
out.push_str("\nassignee: unassigned");
|
||||||
|
} else {
|
||||||
|
out.push_str(&format!("\nassignee: {}", assignees.join(", ")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue