Allow multiple reminder dates for a single chat group

This commit is contained in:
Manuel Vögele
2024-01-17 17:02:20 +01:00
parent bc2f647243
commit a4479e6a9d
8 changed files with 160 additions and 73 deletions

View File

@@ -14,13 +14,15 @@ use bot::fetch_and_announce_appointment;
use chrono::{DateTime, Days, NaiveTime, TimeZone, Utc};
use chrono_tz::Europe;
use db::ChatInfo;
use diesel::result::Error::NotFound;
use diesel::{Connection, RunQueryDsl, SqliteConnection};
use diesel::result::Error::{self, NotFound};
use diesel::{
BelongingToDsl, Connection, GroupedBy, RunQueryDsl, SelectableHelper, SqliteConnection,
};
use diesel::{ExpressionMethods, QueryDsl};
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
use error::ConfigLoadError;
use log::*;
use serde::{de::Error, Deserialize, Deserializer};
use serde::{Deserialize, Deserializer};
use teloxide::adaptors::Throttle;
use teloxide::prelude::RequesterExt;
use teloxide::requests::Requester;
@@ -29,7 +31,7 @@ use teloxide::{adaptors::throttle::Limits, Bot};
use tokio::time::sleep;
use crate::bot::Command;
use crate::db::DbChat;
use crate::db::{DbChat, DbReminder};
#[macro_use]
extern crate rust_i18n;
@@ -49,7 +51,7 @@ pub struct Config {
fn deserialize_time<'de, D: Deserializer<'de>>(deserializer: D) -> Result<NaiveTime, D::Error> {
let s: String = Deserialize::deserialize(deserializer)?;
NaiveTime::parse_from_str(&s, "%H:%M").map_err(D::Error::custom)
NaiveTime::parse_from_str(&s, "%H:%M").map_err(serde::de::Error::custom)
}
impl Config {
@@ -146,15 +148,29 @@ struct Reminder<Tz: TimeZone> {
// Checks if the date of the next appointment has changed (and announces if so)
// Additionally, checks if it is time for a reminder and sends that reminder if necessary
async fn check_task(bot: &Throttle<Bot>, config: &Config, db: &Database) -> Result<()> {
let chats = db.lock().await.transaction(|db| {
let chats = db.lock().await.transaction::<_, Error, _>(|db| {
use schema::chat::dsl::*;
chat.load::<DbChat>(db)
use schema::reminder::dsl::*;
let chats = chat.load::<DbChat>(db)?;
let reminders: Vec<DbReminder> = DbReminder::belonging_to(&chats)
.select(DbReminder::as_select())
.order(days_ahead.asc())
.load(db)?;
let reminders_per_chat = reminders
.grouped_by(&chats)
.into_iter()
.zip(chats)
.collect::<Vec<_>>();
Ok(reminders_per_chat)
})?;
let now = Utc::now().with_timezone(&Europe::Berlin);
for chat in chats {
let mut chat_info = ChatInfo::from(chat);
for (reminders, chat) in chats {
let mut chat_info = ChatInfo::from_db(chat, reminders);
fetch_and_announce_appointment(bot, &mut chat_info, db).await?;
let appointment = match chat_info.next_appointment {
@@ -170,17 +186,24 @@ async fn check_task(bot: &Throttle<Bot>, config: &Config, db: &Database) -> Resu
text: t!("messages.starting_now", locale = &chat_info.locale),
});
} else {
let reminder_day =
appointment.start.date_naive() - Days::new(chat_info.remind_days_ahead);
let reminder_date_time = if chat_info.remind_days_ahead == 0 {
reminder_day.and_time(config.reminder_time)
} else {
reminder_day.and_time(config.preceeding_day_reminder_time)
};
let reminder_date_time = reminder_date_time
.and_local_timezone(now.timezone())
.unwrap();
if now >= reminder_date_time {
// This assumes that remind_days_ahead is sorted in ascending order
let most_recent_active_reminder = chat_info
.remind_days_ahead
.iter()
.map(|days_ahead| {
let reminder_day = appointment.start.date_naive() - Days::new(*days_ahead);
let reminder_date_time = if *days_ahead == 0 {
reminder_day.and_time(config.reminder_time)
} else {
reminder_day.and_time(config.preceeding_day_reminder_time)
};
reminder_date_time
.and_local_timezone(now.timezone())
.unwrap()
})
.find(|reminder_datetime| now >= *reminder_datetime);
if let Some(reminder_date_time) = most_recent_active_reminder {
// TODO This can have weird effects if it's happenig around midnight, since it's not timezone aware (and may even mix multiple timezones)
let remaining_time = appointment.start.date_naive() - now.date_naive();
let remaining_days = remaining_time.num_days();
@@ -203,7 +226,7 @@ async fn check_task(bot: &Throttle<Bot>, config: &Config, db: &Database) -> Resu
reminder = Some(Reminder {
time: reminder_date_time,
text: reminder_text,
})
});
}
}