Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ jobs:
rustup default stable
rustup override set stable

- uses: Swatinem/rust-cache@v2

- name: 🌱 - Install dependencies
run: cargo install cross --git https://github.com/cross-rs/cross

Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ serenity = "0.12.4"
uuid = { version = "1.11.1", features = ["v4"] }
chrono = "0.4.40"
itertools = "0.14.0"
mockall = "0.13.1"
mockall_double = "0.3.1"
2 changes: 2 additions & 0 deletions src/agenda_cultural/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ impl AgendaCulturalAPI {
}
}

info!("Parsed events");

events_by_date
}

Expand Down
7 changes: 6 additions & 1 deletion src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@ use crate::discord::api::{DiscordAPI, EventsThread};
use chrono::NaiveDate;
use serenity::all::{ChannelId, GuildChannel, Message, PartialGuild};
use std::collections::BTreeMap;
use tracing::info;
use tracing::{info, instrument, trace};

#[instrument(skip_all)]
pub async fn filter_new_events_by_thread(
discord: &DiscordAPI,
guild: &PartialGuild,
events_by_month: BTreeMap<NaiveDate, Vec<Event>>,
channel_id: ChannelId,
) -> BTreeMap<EventsThread, Vec<Event>> {
trace!("Getting threads");
let threads = discord.get_channel_threads(guild, channel_id).await;

trace!("Sorting threads by month");
let threads_by_month =
get_threads_by_month(discord, channel_id, &events_by_month, &threads).await;

trace!("Getting sent events");
let sent_events = get_sent_events(discord, &threads).await;

threads_by_month
Expand Down
102 changes: 93 additions & 9 deletions src/discord/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use serenity::prelude::SerenityError;
use serenity::Client;
use std::env;
use std::fmt::Debug;
use mockall::automock;
use tracing::field::debug;
use tracing::{debug, error, info, instrument, trace, warn};

Expand All @@ -40,27 +41,31 @@ const PORTUGUESE_MONTHS: [&str; 12] = [
];

const CHILDREN_LABEL: &str = "🧸 para crianças";
const COMMENT_APPLIED_REACTION: char = '✅';

lazy_static! {
static ref USER_MENTION_REGEX: Regex =
Regex::new("<@(\\d+)>").expect("Failed to create mention regex");

static ref COMMENT_IN_USER_REVIEW_REGEX: Regex = Regex::new(r"\*\*Comentários:\*\*.*").unwrap();
}

pub struct DiscordAPI {
pub client: Client,
pub own_user: CurrentUser,
}

#[automock]
impl DiscordAPI {
pub async fn default() -> Self {
DiscordAPI::new(
DiscordAPI::new_with_token(
&env::var("DISCORD_TOKEN").expect("DISCORD_TOKEN not set"),
true,
)
.await
}

pub async fn new(token: &str, cache_flag: bool) -> Self {
pub async fn new_with_token(token: &str, cache_flag: bool) -> Self {
let intents = GatewayIntents::GUILD_MESSAGES
| GatewayIntents::MESSAGE_CONTENT
| GatewayIntents::GUILD_MESSAGE_REACTIONS;
Expand Down Expand Up @@ -188,12 +193,8 @@ impl DiscordAPI {
.react(&self.client.http, ReactionType::from(emoji_char))
.await;

if let Err(e) = react_result {
let msg = &format!("Failed to add reaction {} to message", emoji_char);
error!(
msg,
error = %e
);
if let Err(error) = react_result {
error!("Failed to add reaction {} to message. Error: {:?}", emoji_char, error);
}
}

Expand Down Expand Up @@ -285,6 +286,66 @@ impl DiscordAPI {
}
}

#[instrument(skip_all)]
pub async fn add_reply_comments_on_dms(&self) {
let dms = self
.client
.http
.get_user_dm_channels()
.await
.expect("Failed to get DM channels");

for dm in dms {
let messages: Vec<Message> = dm
.messages(&self.client.http, GetMessages::new())
.await
.expect("Failed to get messages")
.into_iter()
.filter(|msg| self.is_message_reply_to_own(msg))
.collect();

debug!("Found {} review edit messages", messages.len());

for msg in messages {
let mut referenced_msg = msg.referenced_message.clone().unwrap();

if referenced_msg.embeds.is_empty() {
warn!("Referenced message is not a review (does not have an embed)");
continue
}

self.edit_user_review_comment(&msg, &mut referenced_msg).await;

info!("Edited user review with new comment");
}
}
}

#[instrument(skip_all)]
async fn edit_user_review_comment(&self, msg: &Message, referenced_msg: &mut Message) {
let new_comment = msg.content.as_str();
let embed = referenced_msg.embeds.first().unwrap().to_owned();

debug!(event = embed.url, "Editing user review comment");

let embed_edit = Self::edit_event_embed_comment(embed, &new_comment);

let edit_embed_result = referenced_msg
.edit(&self.client.http, EditMessage::new().embed(embed_edit))
.await;

if let Err(error) = edit_embed_result
{
error!("Failed editing review to new comment. Error: {:?}", error);
}

self.add_reaction_to_message(&msg, COMMENT_APPLIED_REACTION)
.await;

info!(comment = new_comment, "Edited user review with new comment");
}

// TODO: instrument
async fn send_user_review(
&self,
user: &User,
Expand Down Expand Up @@ -312,6 +373,13 @@ impl DiscordAPI {
}
}

// TODO: unit test this
fn is_message_reply_to_own(&self, msg: &Message) -> bool {
msg.author != *self.own_user
&& msg.referenced_message.is_some()
&& msg.referenced_message.clone().unwrap().author == *self.own_user
}

async fn get_user_votes(
&self,
event_message: &Message,
Expand Down Expand Up @@ -422,7 +490,8 @@ impl DiscordAPI {
.expect("Failed to send message");

if let Some(comment) = comment {
self.add_reaction_to_message(&comment, '✅').await;
self.add_reaction_to_message(&comment, COMMENT_APPLIED_REACTION)
.await;
}
}

Expand All @@ -448,6 +517,21 @@ impl DiscordAPI {
}
}

#[instrument(skip(event_embed), fields(event_name = %event_embed.title.clone().unwrap_or_default()))]
fn edit_event_embed_comment(event_embed: Embed, comment: &str) -> CreateEmbed {
if event_embed.description == None {
warn!("Event review does not have a description!");
}

let original_description = event_embed.description.clone().unwrap();
let new_description = COMMENT_IN_USER_REVIEW_REGEX.replace(
&original_description,
format!("**Comentários: {}**", comment),
);

CreateEmbed::from(event_embed).description(new_description)
}

#[instrument(skip(self, dm), fields(user_name = %dm.recipient.name.to_string()))]
async fn get_user_last_comment(&self, dm: &PrivateChannel) -> Option<Message> {
match dm.last_message_id {
Expand Down
4 changes: 3 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ async fn run(config: &Config, discord: &DiscordAPI, category: Category, channel_

let new_events = filter_new_events_by_thread(discord, &guild, events, channel_id).await;

info!("Filtered new events");

send_new_events(
discord,
new_events,
Expand All @@ -75,7 +77,7 @@ async fn run(config: &Config, discord: &DiscordAPI, category: Category, channel_
)
.await;

info!("Finished for {}", category);
info!("Finished sending new events for {}", category);
}

#[instrument(skip(discord, threads, vote_emojis))]
Expand Down
6 changes: 3 additions & 3 deletions tests/discord_api_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ mod discord {
}

pub async fn build_api() -> DiscordAPI {
let api = DiscordAPI::new(&token, false).await;
let api = DiscordAPI::new_with_token(&token, false).await;

let _ = INIT
.get_or_init(|| async {
Expand All @@ -419,7 +419,7 @@ mod discord {
}

pub async fn build_api_without_cache() -> DiscordAPI {
let api = DiscordAPI::new(&token, false).await;
let api = DiscordAPI::new_with_token(&token, false).await;

let _ = INIT
.get_or_init(|| async {
Expand All @@ -445,7 +445,7 @@ mod discord {
}

pub async fn build_tester_api() -> DiscordAPI {
DiscordAPI::new(&tester_token, false).await
DiscordAPI::new_with_token(&tester_token, false).await
}
}
}
Loading