Compare commits
10 commits
2b5e114926
...
bd8a224797
Author | SHA1 | Date | |
---|---|---|---|
bd8a224797 | |||
33c4f45b35 | |||
|
0c357bcec0 | ||
059f9650ef | |||
7f046a5aa5 | |||
7278bf249f | |||
9a18c33afb | |||
1bec737eee | |||
2abffa695a | |||
121a65d12f |
10 changed files with 360 additions and 47 deletions
68
Cargo.lock
generated
68
Cargo.lock
generated
|
@ -11,6 +11,8 @@ dependencies = [
|
|||
"dotenvy",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"rand",
|
||||
"regex",
|
||||
"teloxide",
|
||||
"tokio",
|
||||
]
|
||||
|
@ -113,6 +115,12 @@ version = "3.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.8.0"
|
||||
|
@ -1113,6 +1121,15 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.5.0"
|
||||
|
@ -1165,6 +1182,36 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rc-box"
|
||||
version = "1.2.0"
|
||||
|
@ -2147,6 +2194,27 @@ dependencies = [
|
|||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.4"
|
||||
|
|
|
@ -11,3 +11,10 @@ tokio = { version = "1.8", features = ["rt-multi-thread", "macros"] }
|
|||
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
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# ApollousaBot
|
||||
|
||||
A Telegram bot to remind members in Posthuman-Community to take breaks and exercise.
|
100
db_helper.py
Normal file
100
db_helper.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
import sqlite3
|
||||
|
||||
conn = sqlite3.connect("database.db")
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS quotes (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
msg TEXT NOT NULL
|
||||
)
|
||||
""")
|
||||
|
||||
messages = [
|
||||
"自然が呼んでいる、外に出て歩こう!🌿",
|
||||
"椅子は動けないけど、あなたは動ける!立ってストレッチ!🚀",
|
||||
"宠猫喊你起身活动,来吧喵 🐱",
|
||||
"Your pet cat says it’s time to move, meow! 🐱",
|
||||
"ネコが「動く時間だよ」、ニャー!🐱",
|
||||
"锻炼身体,心情也会变好哦! 🌞",
|
||||
"Exercise your body, uplift your mood! 🌈",
|
||||
"体を動かして、気分もスッキリ!🌞",
|
||||
"别只是动动手指,起来扭扭腰吧! 💃",
|
||||
"Don't only move your fingers, get up and wiggle your waist! 💃",
|
||||
"指だけ動かさないで、腰を振ろう!💃",
|
||||
"起身运动一下,犒劳自己一片小饼干! 🍪",
|
||||
"Move around and reward yourself with a cookie! 🍪",
|
||||
"動いたら、クッキーを自分にご褒美!🍪",
|
||||
"再多努力五分钟,然后起来活动一下吧! ✊",
|
||||
"Work hard for another five minutes, then get up and move! ✊",
|
||||
"あと 5 分頑張って、そして動こう!✊",
|
||||
"用挤牙膏的劲去运动,身体会感谢你! 🏋️",
|
||||
"Squeeze in some exercise like you squeeze toothpaste, your body will thank you! 🏋️",
|
||||
"歯磨き粉を絞るように運動して、体が感謝してくれるよ!🏋️",
|
||||
"会健身的椅子才是好椅子!让你的动起来! 🪑",
|
||||
"A good chair gets some exercise too! Let's move yours! 🪑",
|
||||
"健康な椅子は運動する椅子だ!動かそう!🪑",
|
||||
"保持健康,今天走一万步啦! 💪",
|
||||
"Keep fit, hit 10,000 steps today! 💪",
|
||||
"健康を保つために、今日は 1 万歩歩こう!👟",
|
||||
"左脚,右脚,一步两步,动起来! 🚶",
|
||||
"Left foot, right foot, one two step, let's move! 🚶♀️",
|
||||
"左右の足、一歩二歩、動こう!🚶",
|
||||
"玩个小游戏,站起来跳十次! 🎮",
|
||||
"Play a little game, jump up ten times! 🎮",
|
||||
"ゲームしよう、10 回ジャンプ!🎮",
|
||||
"起来动一动,心情棒棒哒! 🌟",
|
||||
"Get up and move, feel super awesome! 🌟",
|
||||
"立ち上がって動くと、気分が最高!✨",
|
||||
"给自己来个 “动感时刻” 吧! 🕺",
|
||||
"自分に「動の時間」を贈ろう!🕺",
|
||||
"收拾收拾,活动一下筋骨! 🚶",
|
||||
"Tidy up and stretch your muscles! 🚶♂️",
|
||||
"身の回りを整理して、筋肉を伸ばそう!🧹",
|
||||
"休息片刻,能量满满! ✨",
|
||||
"Take a break, feel energized! ✨",
|
||||
"休憩して、エネルギーを補充しよう!🔋",
|
||||
"运动一下,神清气爽! 🌿",
|
||||
"A quick workout, clear mind! 🌿",
|
||||
"少し運動して、頭をリフレッシュ!🍃",
|
||||
"每天一小步,健康一大步! 🤸",
|
||||
"A little step each day, a big leap to health! 🤸",
|
||||
"毎日少し動いて、大きな健康を得よう!🏃♂️",
|
||||
"起身喝口水,再散步五分钟! 🚶",
|
||||
"Stand up, have a sip of water, and take a five-minute walk! 🚶♀️",
|
||||
"立ち上がって水を飲んで、5 分歩こう!🚶♂️",
|
||||
"时间到!该起来动动啦! ⏰",
|
||||
"Time's up! Get up and move around! ⏰",
|
||||
"時間だよ!立ち上がって動こう!⏳",
|
||||
"只需几步,就能解锁健康! 🚶",
|
||||
"Just a few steps to unlock health! 🚶♀️",
|
||||
"健康へのカギは、数歩歩くだけ!🔑",
|
||||
"活动一下,用笑容迎接每一天! 😊",
|
||||
"Move around, greet each day with a smile! 😊",
|
||||
"動いて、毎日を笑顔で迎えよう!😊",
|
||||
"别让自己 “长在” 椅子上,起来动动吧! 🌟",
|
||||
"椅子に「根を張らない」ように、立ち上がって動こう!🌿",
|
||||
"让你的小腿跑一跑,充电完毕!⚡",
|
||||
"Let your legs run a bit, recharge complete! ⚡",
|
||||
"少し足を動かして、充電完了!⚡",
|
||||
"记得活动哦!短暂休息,长久健康! 🚶",
|
||||
"Don't forget to move! Short breaks, long health! 🚶♂️",
|
||||
"動くのを忘れないで!短い休憩、長い健康!🚶♀️",
|
||||
"起身摇摆,工作更自在! 💃",
|
||||
"Stand up and sway, work feels like play! 💃",
|
||||
"立ち上がって揺れよう、仕事が楽しくなるよ!💃 ",
|
||||
"久坐不好,起来活动活动吧! 🚶",
|
||||
"记得起身活动活动,松松背拉拉筋。😊",
|
||||
]
|
||||
|
||||
|
||||
for idx, msg in enumerate(messages, start=1):
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO quotes (ID, msg) VALUES (?, ?)
|
||||
""",
|
||||
(idx, msg),
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
|
@ -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;
|
||||
|
@ -20,7 +21,7 @@ pub async fn reply(bot: Bot, msg: Message, command: Command) -> ResponseResult<(
|
|||
Command::Help => {
|
||||
bot.send_message(
|
||||
msg.chat.id,
|
||||
format!("Hi, {mentioned_user}, Welcome to the Exercise Reminder Bot! 😉\n\nPlease use <code>/settime HH:MM</code> to set the reminder time.\n\nAnd use <code>/settimezone +01:00</code> <b>(according to your location)</b> to accurate the reminder.\n\nIf you are not sure your timezone, please check this <a href=\"https://en.wikipedia.org/wiki/List_of_UTC_offsets\">page</a>.",
|
||||
format!("Hi, {mentioned_user}, Welcome to the Exercise Reminder Bot! 😉\n\nPlease use <code>/settime HH:MM</code> to set the reminder time. And use <code>/settimezone +01:00</code> <b>(according to your location)</b> to accurate the reminder.\n\nIf you are not sure your timezone, please check this <a href=\"https://en.wikipedia.org/wiki/List_of_UTC_offsets\">page</a>.\n\n<b>Please <code>settime</code> first before <code>settimezone</code></b>.",
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
@ -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);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::db::schema::quotes::dsl::*;
|
||||
use crate::db::schema::users::dsl::*;
|
||||
use diesel::prelude::*;
|
||||
use teloxide::types::{ChatId, UserId};
|
||||
|
@ -11,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),
|
||||
|
@ -18,7 +23,7 @@ pub fn set_reminder_time(
|
|||
username.eq(_username),
|
||||
reminder_time.eq(time),
|
||||
))
|
||||
.on_conflict(chat_id)
|
||||
.on_conflict((chat_id, user_id))
|
||||
.do_update()
|
||||
.set(reminder_time.eq(time))
|
||||
.execute(conn)
|
||||
|
@ -29,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) {
|
||||
|
@ -60,5 +89,17 @@ pub fn clear_reminder_time(conn: &mut SqliteConnection, _chat_id: ChatId, _user_
|
|||
}
|
||||
|
||||
pub fn get_user_reminders(conn: &mut SqliteConnection) -> Vec<Users> {
|
||||
users.load::<Users>(conn).expect("Error loading user")
|
||||
// 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")
|
||||
}
|
||||
|
||||
pub fn get_quotes(conn: &mut SqliteConnection) -> Vec<String> {
|
||||
quotes
|
||||
.select(msg)
|
||||
.load::<String>(conn)
|
||||
.expect("Error loading Quotes")
|
||||
}
|
||||
|
|
|
@ -1,13 +1,23 @@
|
|||
use crate::db::schema::users;
|
||||
use crate::db::schema::{quotes, users};
|
||||
use diesel::prelude::*;
|
||||
|
||||
#[derive(Queryable, Selectable)]
|
||||
#[derive(Debug, Queryable, Selectable, QueryableByName, Insertable)]
|
||||
#[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>,
|
||||
}
|
||||
|
||||
#[derive(Queryable, Selectable)]
|
||||
#[diesel(table_name = quotes)]
|
||||
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||
pub struct Quotes {
|
||||
#[allow(dead_code)]
|
||||
pub id: i32,
|
||||
#[allow(dead_code)]
|
||||
pub msg: String,
|
||||
}
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
// @generated automatically by Diesel CLI.
|
||||
|
||||
diesel::table! {
|
||||
users (chat_id) {
|
||||
quotes (id) {
|
||||
id -> Integer,
|
||||
msg -> Text,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
users (chat_id, user_id) {
|
||||
chat_id -> BigInt,
|
||||
user_id -> BigInt,
|
||||
username -> Text,
|
||||
reminder_time -> Text,
|
||||
tz_offset -> Nullable<Text>,
|
||||
reminder_time -> Nullable<Text>,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::allow_tables_to_appear_in_same_query!(
|
||||
quotes,
|
||||
users,
|
||||
);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::db::action as operator;
|
||||
use crate::utils::TimezoneOffest;
|
||||
use crate::db::model::Users;
|
||||
use crate::utils::{get_random_quote, TimezoneOffest};
|
||||
use chrono::{FixedOffset, Utc};
|
||||
use teloxide::adaptors::DefaultParseMode;
|
||||
use teloxide::prelude::*;
|
||||
|
@ -12,32 +13,52 @@ use crate::db::establish_connection;
|
|||
|
||||
type Bot = DefaultParseMode<teloxide::Bot>;
|
||||
|
||||
async fn handle_user_reminder(
|
||||
user: &Users,
|
||||
bot: &Arc<Bot>,
|
||||
selected_quote: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let tzoffset = user.tz_offset.as_ref().ok_or(format!(
|
||||
"User {} does not have a timezone offset set.",
|
||||
user.username
|
||||
))?;
|
||||
let user_timezone = tzoffset
|
||||
.parse::<TimezoneOffest>()
|
||||
.map_err(|e| format!("Failed to parse timezone offset: {}", e))?;
|
||||
let duration_secs = user_timezone.to_duration();
|
||||
let user_utc_time = FixedOffset::east_opt(duration_secs).map_or(
|
||||
"Invalid timezone offset".to_string(),
|
||||
|offset| {
|
||||
Utc::now()
|
||||
.with_timezone(&offset)
|
||||
.format("%H:%M")
|
||||
.to_string()
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(reminder) = &user.reminder_time {
|
||||
if user_utc_time == *reminder {
|
||||
let _user_id = UserId(u64::try_from(user.user_id)?);
|
||||
let mentioned_user = html::user_mention(_user_id, &user.username);
|
||||
let notification = format!("{mentioned_user} {selected_quote}");
|
||||
bot.send_message(ChatId(user.chat_id), notification).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn schedule_reminders(bot: &Arc<Bot>) {
|
||||
let conn = &mut establish_connection();
|
||||
let users_to_remind = operator::get_user_reminders(conn);
|
||||
|
||||
let all_quotes = operator::get_quotes(conn);
|
||||
let selected_quote =
|
||||
get_random_quote(&all_quotes).unwrap_or("记得起身活动活动,松松背拉拉筋。");
|
||||
|
||||
for user in users_to_remind {
|
||||
if let Some(tzoffset) = user.tz_offset.as_ref() {
|
||||
let user_timezone: TimezoneOffest = tzoffset.parse().unwrap();
|
||||
|
||||
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();
|
||||
|
||||
// println!("User UTC time: {}", user_utc_time);
|
||||
|
||||
if user_utc_time == user.reminder_time {
|
||||
// println!("{}: {}", user.reminder_time, user.username);
|
||||
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},记得起身活动活动,松松背拉拉筋。");
|
||||
bot.send_message(ChatId(user.chat_id), notification)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
if let Err(e) = handle_user_reminder(&user, bot, selected_quote).await {
|
||||
eprintln!("Error handling reminder for user {}: {}", user.username, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
36
src/utils.rs
36
src/utils.rs
|
@ -1,3 +1,5 @@
|
|||
use rand::Rng;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct TimezoneOffest {
|
||||
offset_hours: i32,
|
||||
|
@ -47,6 +49,19 @@ impl TimezoneOffest {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_random_quote(quotes: &[String]) -> Option<&str> {
|
||||
if !quotes.is_empty() {
|
||||
let length = quotes.len();
|
||||
let mut rng = rand::thread_rng();
|
||||
let random_index = rng.gen_range(0..length);
|
||||
let random_quote = "es[random_index];
|
||||
|
||||
Some(random_quote)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
@ -87,4 +102,25 @@ mod tests {
|
|||
let ew_offset = Utc::now().with_timezone(&FixedOffset::east_opt(duration).unwrap());
|
||||
println!("UTC+8:00 -> {}", ew_offset);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_random_quote() {
|
||||
let quotes = vec![
|
||||
String::from("wwww"),
|
||||
String::from("vvvv"),
|
||||
String::from("xxxx"),
|
||||
];
|
||||
|
||||
let random_quote = get_random_quote("es);
|
||||
assert!(random_quote.is_some());
|
||||
|
||||
if let Some(quote) = random_quote {
|
||||
println!("{quote}");
|
||||
}
|
||||
|
||||
let empty_quotes: Vec<String> = vec![];
|
||||
|
||||
let random_quote = get_random_quote(&empty_quotes);
|
||||
assert!(random_quote.is_none());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue