feat: live auto-updating dashboard in /watch with top stealth and new models

This commit is contained in:
katR 2026-09-27 18:19:25 +05:30
parent 7425895931
commit bd3c655a80
4 changed files with 274 additions and 22 deletions

View file

@ -94,6 +94,14 @@ impl Model {
}
self.pricing.values().all(|value| is_decimal_zero(value))
}
pub fn is_stealth(&self) -> bool {
let id_lower = self.id.to_lowercase();
let name_lower = self.name.to_lowercase();
id_lower.starts_with("stealth/")
|| id_lower.contains("stealth")
|| name_lower.contains("stealth")
}
}
pub(crate) fn validate(catalog: &BTreeMap<String, Model>) -> Result<(), Error> {
@ -253,6 +261,16 @@ mod tests {
);
}
#[test]
fn stealth_detection_test() {
assert!(model("stealth/space-bunny", &[]).is_stealth());
assert!(model("vendor/model-stealth-preview", &[]).is_stealth());
let mut named = model("vendor/xyz", &[]);
named.name = "Stealth Model V1".into();
assert!(named.is_stealth());
assert!(!model("meta-llama/llama-3", &[]).is_stealth());
}
#[test]
fn changes_detects_new_and_paid_to_free() {
let prev = map(vec![

View file

@ -339,8 +339,12 @@ fn patch_subscription(
min_context: 0,
ping_enabled: false,
compact: false,
message_id: None,
});
sub.channel_id = channel_id;
if sub.channel_id != channel_id {
sub.channel_id = channel_id;
sub.message_id = None;
}
if patch.clear_role {
sub.role_id = None;
sub.ping_enabled = false;
@ -490,7 +494,7 @@ async fn watch(
}
let roles = guild.roles(ctx.serenity_context()).await?;
let mut guard = ctx.data().store.lock().await;
let sub = match patch_subscription(
let mut sub = match patch_subscription(
guard.state.subscriptions.get(&guild.get()),
channel.id.get(),
&patch,
@ -514,8 +518,40 @@ async fn watch(
drop(guard);
return reply(ctx, embed("Invalid role").description("The configured role is missing. Set ping_role to a valid role or @everyone, or use ping_enabled:false or clear_role:true.")).await;
}
let result = embed("Alerts configured here")
.description(WATCH_HELP)
// Post or update the public live dashboard in the channel
let dashboard_embed = presentation::live_dashboard(&guard.state, &sub, ctx.data().poll_seconds);
let mut updated_msg_id = None;
if let Some(existing_msg_id) = sub.message_id
&& channel
.id
.edit_message(
ctx.serenity_context(),
serenity::MessageId::new(existing_msg_id),
serenity::EditMessage::new().embed(dashboard_embed.clone()),
)
.await
.is_ok()
{
updated_msg_id = Some(existing_msg_id);
}
if updated_msg_id.is_none()
&& let Ok(sent) = channel
.id
.send_message(
ctx.serenity_context(),
serenity::CreateMessage::new()
.embed(dashboard_embed)
.allowed_mentions(no_mentions()),
)
.await
{
updated_msg_id = Some(sent.id.get());
}
sub.message_id = updated_msg_id;
let result = embed("Live Radar Dashboard & Alerts Configured")
.description("Posted the live auto-updating dashboard in this channel. It will automatically refresh at every poll interval.\n\n".to_owned() + WATCH_HELP)
.field(
"Complete settings",
settings(&sub, guild.get(), ctx.data().poll_seconds),
@ -787,7 +823,11 @@ fn acknowledge(state: &mut State, guild: u64, snapshot: &Subscription, count: us
}
}
async fn deliver(store: &Mutex<Store>, http: &serenity::Http) -> Result<(), Error> {
async fn deliver(
store: &Mutex<Store>,
http: &serenity::Http,
poll_seconds: u64,
) -> Result<(), Error> {
let guilds: Vec<_> = store
.lock()
.await
@ -797,26 +837,53 @@ async fn deliver(store: &Mutex<Store>, http: &serenity::Http) -> Result<(), Erro
.copied()
.collect();
for guild in guilds {
let snapshot = store.lock().await.state.subscriptions.get(&guild).cloned();
let (snapshot, state) = {
let guard = store.lock().await;
(
guard.state.subscriptions.get(&guild).cloned(),
guard.state.clone(),
)
};
let Some(sub) = snapshot else {
continue;
};
if sub.pending.is_empty() {
continue;
let channel = serenity::ChannelId::new(sub.channel_id);
// 1. Auto-update the live dashboard embed if message_id exists
if let Some(msg_id) = sub.message_id {
let dashboard = presentation::live_dashboard(&state, &sub, poll_seconds);
let edit_res = channel
.edit_message(
http,
serenity::MessageId::new(msg_id),
serenity::EditMessage::new().embed(dashboard),
)
.await;
if edit_res.is_err() {
tracing::info!(
guild,
"Live dashboard message edit failed (may have been deleted or moved)"
);
}
}
let count = sub.pending.len().min(8);
if serenity::ChannelId::new(sub.channel_id)
.send_message(http, notification_message(&sub, guild, count))
.await
.is_err()
{
tracing::warn!(guild, "Alert delivery failed; retained for next poll");
continue;
}
let mut guard = store.lock().await;
let mut next = guard.state.clone();
if acknowledge(&mut next, guild, &sub, count) {
guard.commit(next).await?;
// 2. Deliver new model drop notification role pings if any are pending
if !sub.pending.is_empty() {
let count = sub.pending.len().min(8);
if channel
.send_message(http, notification_message(&sub, guild, count))
.await
.is_err()
{
tracing::warn!(guild, "Alert delivery failed; retained for next poll");
continue;
}
let mut guard = store.lock().await;
let mut next = guard.state.clone();
if acknowledge(&mut next, guild, &sub, count) {
guard.commit(next).await?;
}
}
}
Ok(())
@ -973,7 +1040,7 @@ async fn main() -> Result<(), Error> {
if let Err(error) = refresh(&poll_store, &http_client).await {
tracing::warn!(%error, "Catalog refresh failed; preserving previous snapshot");
}
if let Err(error) = deliver(&poll_store, &http).await {
if let Err(error) = deliver(&poll_store, &http, poll_seconds).await {
tracing::warn!(%error, "Saving delivery progress failed; alerts may retry");
}
}

View file

@ -173,6 +173,112 @@ pub fn snapshot(state: &State, options: &Browse, poll_seconds: u64) -> serenity:
.field("Poll interval", format!("{formatted_poll} seconds · run /status again to refresh this snapshot"), false)
}
pub fn live_dashboard(
state: &State,
sub: &crate::store::Subscription,
poll_seconds: u64,
) -> serenity::CreateEmbed {
let freshness = state
.last_success
.map(|time| {
format!(
"Synced <t:{time}:R> (<t:{time}:f>). Auto-updates every {}s.",
format_number(poll_seconds)
)
})
.unwrap_or_else(|| "Waiting for initial catalog sync...".into());
let mut result = embed("Model Radar · Live Dashboard").description(freshness);
// 1. 🔥 New & Recently Dropped Models (at the top)
let recent_drops: Vec<_> = sub
.pending
.iter()
.take(if sub.compact { 6 } else { 3 })
.map(|event| {
format!(
"**{}** · {}",
if event.kind == "paid_to_free" {
"Now free"
} else {
"New in catalog"
},
model_line(&event.model, sub.compact)
)
})
.collect();
if !recent_drops.is_empty() {
let text_block = recent_drops.join("\n\n");
result = result.field("🔥 New & Recent Drops", text(&text_block, 1024), false);
}
// 2. 🕵️ Stealth & Experimental Models (First page section)
let stealth_models: Vec<_> = state
.catalog
.values()
.filter(|m| m.is_stealth() && (!sub.free_only || m.is_free()))
.take(if sub.compact { 5 } else { 3 })
.map(|m| model_line(m, sub.compact))
.collect();
let stealth_text = if stealth_models.is_empty() {
"No stealth models currently active in catalog.".to_string()
} else {
stealth_models.join("\n\n")
};
result = result.field(
"🕵️ Stealth & Experimental Models",
text(&stealth_text, 1024),
false,
);
// 3. ✨ Active Models Snapshot
let active_models: Vec<_> = state
.catalog
.values()
.filter(|m| sub.matches(m))
.take(if sub.compact { 6 } else { 4 })
.map(|m| model_line(m, sub.compact))
.collect();
let active_text = if active_models.is_empty() {
"No matching models found with current filters.".to_string()
} else {
active_models.join("\n\n")
};
result = result.field("✨ Active Models Snapshot", text(&active_text, 1024), false);
// 4. Overview & Filters
let total_count = format_number(state.catalog.len());
let free_count = format_number(state.catalog.values().filter(|m| m.is_free()).count());
let stealth_count = format_number(state.catalog.values().filter(|m| m.is_stealth()).count());
result = result.field(
"Overview",
format!("{total_count} total · {free_count} free · {stealth_count} stealth"),
true,
);
let scope_text = if sub.free_only {
"Free only"
} else {
"All models"
};
let search_desc = if sub.search.is_empty() {
"None"
} else {
&sub.search
};
let min_ctx = format_number(sub.min_context);
result = result.field(
"Active Filters",
format!("Scope: {scope_text}\nSearch: {search_desc}\nMin Context: {min_ctx} tokens"),
true,
);
result
}
fn format_price_usd(val: f64) -> String {
if val == 0.0 {
return "$0.00".into();
@ -498,4 +604,61 @@ pub mod tests {
"tiered pricing"
);
}
#[test]
fn live_dashboard_includes_stealth_and_new_models() {
let state = State {
catalog: [
model("stealth/space-bunny", true, 32000),
model("qwen/qwen-2.5", true, 64000),
model("meta-llama/llama-3", false, 8000),
]
.into_iter()
.map(|m| (m.id.clone(), m))
.collect(),
last_success: Some(12345),
..Default::default()
};
let sub = crate::store::Subscription {
channel_id: 1,
role_id: None,
free_only: true,
search: String::new(),
min_context: 0,
ping_enabled: false,
compact: false,
message_id: None,
pending: vec![crate::catalog::Event {
model: model("qwen/qwen-2.5", true, 64000),
kind: "paid_to_free".into(),
}],
};
let embed = live_dashboard(&state, &sub, 600);
let value = serde_json::to_value(&embed).unwrap();
assert_embed_bounds(&value);
// Verify "🔥 New & Recent Drops" at top
assert_eq!(value["fields"][0]["name"], "🔥 New & Recent Drops");
assert!(
value["fields"][0]["value"]
.as_str()
.unwrap()
.contains("Now free")
);
// Verify "🕵️ Stealth & Experimental Models" section
assert_eq!(
value["fields"][1]["name"],
"🕵️ Stealth & Experimental Models"
);
assert!(
value["fields"][1]["value"]
.as_str()
.unwrap()
.contains("stealth/space")
);
// Verify "✨ Active Models Snapshot"
assert_eq!(value["fields"][2]["name"], "✨ Active Models Snapshot");
}
}

View file

@ -32,6 +32,8 @@ pub struct Subscription {
pub ping_enabled: bool,
#[serde(default)]
pub compact: bool,
#[serde(default)]
pub message_id: Option<u64>,
pub pending: Vec<Event>,
}
@ -183,6 +185,7 @@ mod tests {
min_context: 8192,
ping_enabled: false,
compact: true,
message_id: None,
pending: vec![Event {
model,
kind: "new".into(),
@ -199,6 +202,7 @@ mod tests {
min_context: 0,
ping_enabled: true,
compact: false,
message_id: None,
pending: Vec::new(),
},
),