diff --git a/src/agenda_cultural/api.rs b/src/agenda_cultural/api.rs index 525801b..52433ac 100644 --- a/src/agenda_cultural/api.rs +++ b/src/agenda_cultural/api.rs @@ -28,7 +28,7 @@ lazy_static! { ExponentialBackoff::builder() .jitter(Bounded) .retry_bounds(Duration::from_millis(50), Duration::from_millis(1000)) - .build_with_total_retry_duration_and_max_retries(Duration::from_secs(10)) + .build_with_total_retry_duration_and_max_retries(Duration::from_secs(30)) )) .build(); static ref EVENT_ID_SELECTOR: Selector = Selector::parse(&format!( diff --git a/src/config/env_loader.rs b/src/config/env_loader.rs index 7cf6b35..1b4a45a 100644 --- a/src/config/env_loader.rs +++ b/src/config/env_loader.rs @@ -1,5 +1,6 @@ use crate::config::model::{Config, DebugConfig, EmojiConfig}; use serenity::all::ChannelId; +use std::collections::HashMap; use std::env; pub fn load_config() -> Config { @@ -7,6 +8,8 @@ pub fn load_config() -> Config { let artes_channel_id: ChannelId = load_channel_id_config("DISCORD_ARTES_CHANNEL_ID"); let voting_emojis: [EmojiConfig; 5] = load_voting_emojis_config("VOTING_EMOJIS"); let gather_new_events: bool = load_bool_config("GATHER_NEW_EVENTS", true); + let venue_ticket_shop_url: HashMap = + load_venue_ticket_shop_config("VENUE_TICKET_SHOP_URLS"); let debug_config = DebugConfig { clear_channel: load_bool_config("DEBUG_CLEAR_CHANNEL", false), @@ -23,6 +26,7 @@ pub fn load_config() -> Config { artes_channel_id, voting_emojis, gather_new_events, + venue_ticket_shop_url, } } @@ -33,6 +37,28 @@ fn load_channel_id_config(name: &str) -> ChannelId { .unwrap_or_else(|_| panic!("{} is not a valid Discord channel ID", name)) } +pub fn load_venue_ticket_shop_config(name: &str) -> HashMap { + let config = env::var(name).unwrap_or_else(|_| panic!("{} must be set.", name)); + + let venue_ticket_shop: Vec<&str> = config + .split(";") + .collect(); + + venue_ticket_shop + .iter() + .map(|v| { + let venue_to_ticket_shop = v + .split_once(":") + .expect("Emojis must be comma-separated in the Name:ID format"); + + ( + venue_to_ticket_shop.0.into(), + venue_to_ticket_shop.1.into(), + ) + }) + .collect() +} + pub fn load_voting_emojis_config(name: &str) -> [EmojiConfig; 5] { let config = env::var(name).unwrap_or_else(|_| panic!("{} must be set.", name)); diff --git a/src/config/model.rs b/src/config/model.rs index 1fd19b2..eb987ee 100644 --- a/src/config/model.rs +++ b/src/config/model.rs @@ -1,4 +1,5 @@ use serenity::all::ChannelId; +use std::collections::HashMap; use std::fmt::Display; #[derive(Debug)] @@ -7,6 +8,7 @@ pub struct Config { pub teatro_channel_id: ChannelId, pub artes_channel_id: ChannelId, pub voting_emojis: [EmojiConfig; 5], + pub venue_ticket_shop_url: HashMap, pub gather_new_events: bool, } diff --git a/src/discord/api.rs b/src/discord/api.rs index ed977ba..718054a 100644 --- a/src/discord/api.rs +++ b/src/discord/api.rs @@ -22,8 +22,6 @@ use std::fmt::Debug; use tracing::field::debug; use tracing::{debug, error, info, instrument, trace, warn}; -const AUTHOR_NAME: &str = "AlertaEmCena"; - const PORTUGUESE_MONTHS: [&str; 12] = [ "Janeiro", "Fevereiro", @@ -93,7 +91,7 @@ impl DiscordAPI { #[instrument(skip(self, channel_id), fields(channel_id = %channel_id.to_string(), event = %event.title.to_string() ))] - pub async fn send_event(&self, channel_id: ChannelId, event: Event) -> Message { + pub async fn send_event(&self, channel_id: ChannelId, event: Event, ticket_shop_url: Option) -> Message { info!("Sending event"); let mut description = event.details.description; @@ -102,14 +100,19 @@ impl DiscordAPI { description = format!("{}\n\n{}", description.clone(), CHILDREN_LABEL); } + let mut author = CreateEmbedAuthor::new(&event.venue); + + if let Some(ticket_shop_url) = ticket_shop_url { + author = author.url(ticket_shop_url); + } + let embed = CreateEmbed::new() .title(event.title) .url(event.link) .description(description.clone()) - .author(CreateEmbedAuthor::new(AUTHOR_NAME)) + .author(author) .color(Colour::new(0x005eeb)) .field("Datas", event.occurring_at.dates, true) - .field("Onde", event.venue, true) .image(event.details.image_url); let message_builder = CreateMessage::new().add_embed(embed.clone()); @@ -396,7 +399,7 @@ impl DiscordAPI { }); if let Some(reaction) = reaction { - Self::has_no_user_reactions(reaction) + Self::has_someone_reacted(reaction) } else { warn!( "Message does not have reaction emoji '{}'!", @@ -416,14 +419,14 @@ impl DiscordAPI { }); if let Some(reaction) = reaction { - Self::has_no_user_reactions(reaction) + Self::has_someone_reacted(reaction) } else { warn!("Message does not have saved for later emoji!"); false } } - fn has_no_user_reactions(reaction: &MessageReaction) -> bool { + fn has_someone_reacted(reaction: &MessageReaction) -> bool { if reaction.count == 1 { // No one has voted if reaction.me { @@ -716,7 +719,7 @@ mod tests { "#, ) .unwrap(); - let has_no_user_reactions = DiscordAPI::has_no_user_reactions(&reaction); + let has_no_user_reactions = DiscordAPI::has_someone_reacted(&reaction); assert!(has_no_user_reactions); } @@ -739,7 +742,7 @@ mod tests { "#, ) .unwrap(); - let has_no_user_reactions = DiscordAPI::has_no_user_reactions(&reaction); + let has_no_user_reactions = DiscordAPI::has_someone_reacted(&reaction); assert!(!has_no_user_reactions); } @@ -762,7 +765,7 @@ mod tests { "#, ) .unwrap(); - let has_no_user_reactions = DiscordAPI::has_no_user_reactions(&reaction); + let has_no_user_reactions = DiscordAPI::has_someone_reacted(&reaction); assert!(!has_no_user_reactions); } diff --git a/src/main.rs b/src/main.rs index c867bdd..82d5ff6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use alertaemcena::agenda_cultural::api::AgendaCulturalAPI; use alertaemcena::agenda_cultural::model::{Category, Event}; use alertaemcena::api::*; use alertaemcena::config::env_loader::load_config; -use alertaemcena::config::model::{Config, DebugConfig, EmojiConfig}; +use alertaemcena::config::model::{Config, EmojiConfig}; use alertaemcena::discord::api::{DiscordAPI, EventsThread}; use alertaemcena::discord::backup::{backup_user_votes, VoteRecord}; use alertaemcena::tracing::setup_loki; @@ -126,8 +126,7 @@ async fn run( send_new_events( discord, new_events, - &config.debug_config, - &config.voting_emojis, + config, ) .await; @@ -270,20 +269,19 @@ async fn handle_reaction_features( users_with_reactions } -#[instrument(skip(discord, new_events, emojis, debug_config), fields(new_events_count = %new_events.len() +#[instrument(skip_all, fields(new_events_count = %new_events.len() ))] async fn send_new_events( discord: &DiscordAPI, new_events: BTreeMap>, - debug_config: &DebugConfig, - emojis: &[EmojiConfig; 5], + config: &Config ) { if new_events.is_empty() { info!("No new events to send"); return; } - if debug_config.skip_sending { + if config.debug_config.skip_sending { info!("Skipping sending events"); return; } @@ -300,14 +298,18 @@ async fn send_new_events( ); for event in events { - let message = discord.send_event(thread.thread_id, event).await; + let ticket_url = config + .venue_ticket_shop_url + .get(&event.venue) + .cloned(); + let message = discord.send_event(thread.thread_id, event, ticket_url).await; - if debug_config.skip_feature_reactions { + if config.debug_config.skip_feature_reactions { info!("Skipping feature reactions"); continue; } - add_feature_reactions(discord, &message, emojis, *SAVE_FOR_LATER_EMOJI).await; + add_feature_reactions(discord, &message, &config.voting_emojis, *SAVE_FOR_LATER_EMOJI).await; } } } diff --git a/tests/discord_api_tests.rs b/tests/discord_api_tests.rs index f126c81..8c0dcdc 100644 --- a/tests/discord_api_tests.rs +++ b/tests/discord_api_tests.rs @@ -403,7 +403,7 @@ mod discord { let thread = api .get_date_thread(&active_threads, *channel_id, date) .await; - let message = api.send_event(thread.thread_id, event).await; + let message = api.send_event(thread.thread_id, event, None).await; (thread, message) }