Implement announcement merging and filter recent events by mode

This commit is contained in:
Natsu Kagami 2024-08-05 13:31:13 +02:00
parent 6458454ba9
commit 26b72d90ef
Signed by: nki
GPG key ID: 55A032EB38B49ADB

View file

@ -1,6 +1,7 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use future::Future; use future::Future;
use futures_util::try_join; use futures_util::try_join;
use serenity::all::Member;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use stream::FuturesUnordered; use stream::FuturesUnordered;
@ -145,7 +146,7 @@ impl Announcer {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.await .await
.into_iter(); .into_iter();
top.chain(recents) CollectedScore::merge(top.chain(recents))
.map(|v| v.send_message(&ctx, &env, mention, &broadcast_to)) .map(|v| v.send_message(&ctx, &env, mention, &broadcast_to))
.collect::<FuturesUnordered<_>>() .collect::<FuturesUnordered<_>>()
.filter_map(|v| future::ready(v.pls_ok().map(|_| ()))) .filter_map(|v| future::ready(v.pls_ok().map(|_| ())))
@ -206,6 +207,7 @@ impl Announcer {
let events = std::mem::take(&mut user.events) let events = std::mem::take(&mut user.events)
.into_iter() .into_iter()
.filter_map(|v| v.to_event_rank()) .filter_map(|v| v.to_event_rank())
.filter(|s| s.mode == mode)
.filter(|s| Self::is_announceable_date(s.date, last_update, now)) .filter(|s| Self::is_announceable_date(s.date, last_update, now))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
Ok((user, top_scores, events)) Ok((user, top_scores, events))
@ -220,12 +222,27 @@ struct CollectedScore<'a> {
} }
impl<'a> CollectedScore<'a> { impl<'a> CollectedScore<'a> {
fn merge(scores: impl IntoIterator<Item = Self>) -> impl Iterator<Item = Self> {
let mut mp = std::collections::HashMap::<u64, Self>::new();
scores
.into_iter()
.filter_map(|s| s.score.id.map(|id| (id, s)))
.for_each(|(id, s)| {
mp.entry(id)
.and_modify(|v| {
v.kind = v.kind.merge(s.kind);
})
.or_insert(s);
});
mp.into_values()
}
fn from_top_score(user: &'a User, score: Score, mode: Mode, rank: u8) -> Self { fn from_top_score(user: &'a User, score: Score, mode: Mode, rank: u8) -> Self {
Self { Self {
user, user,
score, score,
mode, mode,
kind: ScoreType::TopRecord(rank), kind: ScoreType::top(rank),
} }
} }
@ -255,7 +272,7 @@ impl<'a> CollectedScore<'a> {
user, user,
score, score,
mode: event.mode, mode: event.mode,
kind: ScoreType::WorldRecord(event.rank), kind: ScoreType::world(event.rank),
}) })
} }
} }
@ -314,31 +331,20 @@ impl<'a> CollectedScore<'a> {
.send_message( .send_message(
&ctx, &ctx,
CreateMessage::new() CreateMessage::new()
.content(match self.kind { .content(self.kind.announcement_msg(self.mode, &member))
ScoreType::TopRecord(rank) => {
if rank <= 25 {
format!("New leaderboard record from {}!", mention.mention())
} else {
format!("New leaderboard record from **{}**!", member.distinct())
}
}
ScoreType::WorldRecord(rank) => {
if (self.mode == Mode::Std && rank <= 100)
|| (self.mode != Mode::Std && rank <= 50)
{
format!("New leaderboard record from {}!", mention.mention())
} else {
format!("New leaderboard record from **{}**!", member.distinct())
}
}
})
.embed({ .embed({
let mut b = score_embed(&self.score, bm, content, self.user); let mut b = score_embed(&self.score, bm, content, self.user);
match self.kind { let b = if let Some(rank) = self.kind.top_record {
ScoreType::TopRecord(rank) => b.top_record(rank), b.top_record(rank)
ScoreType::WorldRecord(rank) => b.world_record(rank), } else {
} &mut b
.build() };
let b = if let Some(rank) = self.kind.world_record {
b.world_record(rank)
} else {
b
};
b.build()
}) })
.components(vec![score_components(Some(guild))]), .components(vec![score_components(Some(guild))]),
) )
@ -349,7 +355,49 @@ impl<'a> CollectedScore<'a> {
} }
} }
enum ScoreType { #[derive(Debug, Clone, Copy, PartialEq, Eq)]
TopRecord(u8), struct ScoreType {
WorldRecord(u16), pub top_record: Option<u8>,
pub world_record: Option<u16>,
}
impl ScoreType {
fn top(rank: u8) -> Self {
Self {
top_record: Some(rank),
world_record: None,
}
}
fn world(rank: u16) -> Self {
Self {
top_record: None,
world_record: Some(rank),
}
}
fn merge(self, other: Self) -> Self {
Self {
top_record: self.top_record.or(other.top_record),
world_record: self.world_record.or(other.world_record),
}
}
fn announcement_msg(&self, mode: Mode, mention: &Member) -> String {
let mention_user = self.top_record.is_some_and(|r| r <= 25)
|| self
.world_record
.is_some_and(|w| if mode == Mode::Std { w <= 100 } else { w <= 50 });
let title = if self.top_record.is_some() {
"New top record"
} else if self.world_record.is_some() {
"New leaderboard record"
} else {
"New record"
};
if mention_user {
format!("{} from {}!", title, mention.mention())
} else {
format!("{} from **{}**!", title, mention.distinct())
}
}
} }