Merge pull request #2 from Posthuman-Community/bugfix

Fixed: handle invalid timezone input to prevent crash
This commit is contained in:
Yuki.N 2024-11-28 09:31:05 +00:00 committed by GitHub
commit 0c357bcec0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 92 additions and 35 deletions

1
Cargo.lock generated
View file

@ -12,6 +12,7 @@ dependencies = [
"log",
"pretty_env_logger",
"rand",
"regex",
"teloxide",
"tokio",
]

View file

@ -12,6 +12,7 @@ diesel = { version = "2.2.4", features = ["sqlite"] }
chrono = "0.4.38"
dotenvy = "0.15.7"
rand = "0.8.5"
regex = "1.11.1"
[profile.release]
debug = false

View file

@ -2,6 +2,7 @@ use crate::{
bot::commands::Command,
db::action::{clear_reminder_time, set_reminder_time, set_user_timezone},
};
use regex::Regex;
use teloxide::{adaptors::DefaultParseMode, prelude::*, utils::html};
use chrono::naive::NaiveTime;
@ -54,14 +55,28 @@ pub async fn reply(bot: Bot, msg: Message, command: Command) -> ResponseResult<(
}
}
Command::SetTimezone(timezone) => {
set_user_timezone(conn, user_id, msg.chat.id, timezone.as_str());
bot.send_message(
msg.chat.id,
format!(
"Hi, {mentioned_user}, your timezone is set to <code>UTC{timezone}</code>."
),
)
.await?;
let tz_pattern =
Regex::new(r"^(\+|-)\d{2}:\d{2}$").expect("Failed to initialize regex pattern");
match tz_pattern.is_match(&timezone) {
true => {
set_user_timezone(conn, user_id, msg.chat.id, &username, timezone.as_str());
bot.send_message(
msg.chat.id,
format!(
"Hi, {mentioned_user}, your timezone is set to <code>UTC{timezone}</code>."
),
)
.await?;
}
false => {
bot.send_message(
msg.chat.id,
"Invalid timezone format. Please use the format <code>±HH:MM</code>.",
)
.await?;
}
}
}
Command::Stop => {
clear_reminder_time(conn, msg.chat.id, user_id);

View file

@ -12,6 +12,10 @@ pub fn set_reminder_time(
_username: &str,
time: &str,
) {
println!(
"set_reminder_time -> UserId: {}, ChatId: {}, Username: {} , time: {}",
_user_id.0, _chat_id.0, _username, time
);
diesel::insert_into(users)
.values((
chat_id.eq(_chat_id.0),
@ -30,22 +34,46 @@ pub fn set_user_timezone(
conn: &mut SqliteConnection,
_user_id: UserId,
_chat_id: ChatId,
_username: &str,
_user_timezone: &str,
) {
println!(
"UserId: {}, ChatId: {}, UserTimezone: {}",
_user_id.0, _chat_id.0, _user_timezone
"set_user_timezone -> UserId: {}, ChatId: {}, Username: {} ,UserTimezone: {}",
_user_id.0, _chat_id.0, _username, _user_timezone
);
diesel::update(
users.filter(
let user_exists = users
.filter(
chat_id
.eq(_chat_id.0)
.and(user_id.eq(i64::try_from(_user_id.0).unwrap())),
),
)
.set(tz_offset.eq(_user_timezone))
.execute(conn)
.expect("Error update user timezone");
)
.select((chat_id, user_id))
.first::<(i64, i64)>(conn)
.optional()
.expect("Error checking if user exists");
if user_exists.is_some() {
diesel::update(
users.filter(
chat_id
.eq(_chat_id.0)
.and(user_id.eq(i64::try_from(_user_id.0).unwrap())),
),
)
.set(tz_offset.eq(_user_timezone))
.execute(conn)
.expect("Error updating user timezone");
} else {
diesel::insert_into(users)
.values((
chat_id.eq(_chat_id.0),
user_id.eq(i64::try_from(_user_id.0).unwrap()),
username.eq(_username),
tz_offset.eq(_user_timezone),
))
.execute(conn)
.expect("Error inserting new user with timezone");
}
}
pub fn clear_reminder_time(conn: &mut SqliteConnection, _chat_id: ChatId, _user_id: UserId) {
@ -63,6 +91,7 @@ pub fn clear_reminder_time(conn: &mut SqliteConnection, _chat_id: ChatId, _user_
pub fn get_user_reminders(conn: &mut SqliteConnection) -> Vec<Users> {
// Only work for group members
users
.select((chat_id, user_id, username, reminder_time, tz_offset))
.filter(chat_id.ne(user_id))
.load::<Users>(conn)
.expect("Error loading user")

View file

@ -1,14 +1,14 @@
use crate::db::schema::{quotes, users};
use diesel::prelude::*;
#[derive(Queryable, Selectable)]
#[derive(Debug, Queryable, Selectable)]
#[diesel(table_name = users)]
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
pub struct Users {
pub chat_id: i64,
pub user_id: i64,
pub username: String,
pub reminder_time: String,
pub reminder_time: Option<String>,
pub tz_offset: Option<String>,
}

View file

@ -12,8 +12,8 @@ diesel::table! {
chat_id -> BigInt,
user_id -> BigInt,
username -> Text,
reminder_time -> Text,
tz_offset -> Nullable<Text>,
reminder_time -> Nullable<Text>,
}
}

View file

@ -22,23 +22,34 @@ pub async fn schedule_reminders(bot: &Arc<Bot>) {
for user in users_to_remind {
if let Some(tzoffset) = user.tz_offset.as_ref() {
let user_timezone: TimezoneOffest = tzoffset.parse().unwrap();
if let Ok(user_timezone) = tzoffset.parse::<TimezoneOffest>() {
let duration_secs = user_timezone.to_duration();
let user_utc_time = Utc::now()
.with_timezone(&FixedOffset::east_opt(duration_secs).unwrap())
.format("%H:%M")
.to_string();
let duration_secs = user_timezone.to_duration();
let user_utc_time = Utc::now()
.with_timezone(&FixedOffset::east_opt(duration_secs).unwrap())
.format("%H:%M")
.to_string();
if user_utc_time == user.reminder_time {
let _user_id = UserId(u64::try_from(user.user_id).unwrap());
let _username = user.username;
let mentioned_user = html::user_mention(_user_id, &_username);
let notification = format!("{mentioned_user} {selected_quote}");
bot.send_message(ChatId(user.chat_id), notification)
.await
.unwrap();
if let Some(reminder) = &user.reminder_time {
if user_utc_time == *reminder {
let _user_id = UserId(u64::try_from(user.user_id).unwrap());
let _username = user.username;
let mentioned_user = html::user_mention(_user_id, &_username);
let notification = format!("{mentioned_user} {selected_quote}");
bot.send_message(ChatId(user.chat_id), notification)
.await
.unwrap();
}
} else {
eprintln!("Invalid reminder: {:?}", &user.reminder_time)
}
} else {
eprintln!("Failed to parse timezone offset: {}", tzoffset)
}
} else {
eprintln!(
"User {} does not have a timezone offset set.",
user.username
)
}
}
}