diff --git a/src/main.rs b/src/main.rs index c3edc88..cb21865 100644 --- a/src/main.rs +++ b/src/main.rs @@ -466,20 +466,28 @@ async fn watch( .await? .guild() .ok_or("Server channel required")?; - if channel.kind != serenity::ChannelType::Text || channel.guild_id != guild { + if !matches!( + channel.kind, + serenity::ChannelType::Text | serenity::ChannelType::News + ) || channel.guild_id != guild + { return reply( ctx, embed("Text channel required") - .description("Run /watch in a regular server text channel."), + .description("Run /watch in a regular server text or announcement channel."), ) .await; } let bot_id = ctx.serenity_context().cache.current_user().id; let member = guild.member(ctx.serenity_context(), bot_id).await?; - let permissions = ctx - .guild() - .ok_or("Server cache unavailable")? - .user_permissions_in(&channel, &member); + #[allow(deprecated)] + let permissions = if let Some(guild_cache) = ctx.guild() { + guild_cache.user_permissions_in(&channel, &member) + } else { + channel + .permissions_for_user(ctx.serenity_context(), bot_id) + .unwrap_or_else(|_| serenity::Permissions::all()) + }; if !permissions.contains( serenity::Permissions::VIEW_CHANNEL | serenity::Permissions::SEND_MESSAGES @@ -1018,7 +1026,31 @@ async fn main() -> Result<(), Error> { commands, on_error: |error| Box::pin(async move { if let Some(ctx) = error.ctx() { - let _ = reply(ctx, embed("Request failed").description("Check your permissions and arguments, or retry shortly. The operator can check bot logs.")).await; + let err_msg = match &error { + poise::FrameworkError::Command { error, .. } => error.to_string(), + poise::FrameworkError::MissingBotPermissions { + missing_permissions, + .. + } => { + format!("The bot is missing required Discord permissions: {missing_permissions}") + } + poise::FrameworkError::MissingUserPermissions { + missing_permissions, + .. + } => match missing_permissions { + Some(perms) => { + format!("You need `{perms}` permission in this server to run this command.") + } + None => { + "You lack the required permissions to run this command in this server.".into() + } + }, + poise::FrameworkError::ArgumentParse { error, .. } => { + format!("Invalid command argument: {error}") + } + other => other.to_string(), + }; + let _ = reply(ctx, embed("Command Error").description(err_msg)).await; } tracing::warn!(%error, "Discord command/framework error"); }), diff --git a/src/presentation.rs b/src/presentation.rs index 28011e9..60ae46a 100644 --- a/src/presentation.rs +++ b/src/presentation.rs @@ -8,6 +8,28 @@ pub fn no_mentions() -> serenity::CreateAllowedMentions { .replied_user(false) } +pub fn clamp_str(value: &str, limit: usize) -> String { + if limit == 0 { + return String::new(); + } + let mut result = String::new(); + let mut count = 0; + for ch in value.chars() { + let w = ch.len_utf16(); + if count + w > limit.saturating_sub(1) { + result.push('…'); + break; + } + result.push(ch); + count += w; + } + if result.is_empty() { + "—".into() + } else { + result + } +} + pub fn text(value: &str, limit: usize) -> String { if limit == 0 { return String::new(); @@ -210,7 +232,7 @@ pub fn live_dashboard( if !recent_drops.is_empty() { let text_block = recent_drops.join("\n\n"); - result = result.field("🔥 New & Recent Drops", text(&text_block, 1024), false); + result = result.field("🔥 New & Recent Drops", clamp_str(&text_block, 1024), false); } // 2. 🕵️ Stealth & Experimental Models (First page section) @@ -229,7 +251,7 @@ pub fn live_dashboard( }; result = result.field( "🕵️ Stealth & Experimental Models", - text(&stealth_text, 1024), + clamp_str(&stealth_text, 1024), false, ); @@ -247,7 +269,11 @@ pub fn live_dashboard( } else { active_models.join("\n\n") }; - result = result.field("✨ Active Models Snapshot", text(&active_text, 1024), false); + result = result.field( + "✨ Active Models Snapshot", + clamp_str(&active_text, 1024), + false, + ); // 4. Overview & Filters let total_count = format_number(state.catalog.len());