-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement scheduler system and a prototype task to check status update
- Loading branch information
1 parent
c222bef
commit 0960398
Showing
6 changed files
with
278 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
amFOSS Daemon: A discord bot for the amFOSS Discord server. | ||
Copyright (C) 2024 amFOSS | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
use serde_json::Value; | ||
|
||
const REQUEST_URL: &str = "https://root.shuttleapp.rs/"; | ||
|
||
pub async fn fetch_members() -> Result<Vec<String>, reqwest::Error> { | ||
let client = reqwest::Client::new(); | ||
let query = r#" | ||
query { | ||
getMember { | ||
name | ||
} | ||
}"#; | ||
|
||
let response = client | ||
.post(REQUEST_URL) | ||
.json(&serde_json::json!({"query": query})) | ||
.send() | ||
.await?; | ||
|
||
let json: Value = response.json().await?; | ||
|
||
let member_names: Vec<String> = json["data"]["getMember"] | ||
.as_array() | ||
.unwrap() | ||
.iter() | ||
.map(|member| member["name"].as_str().unwrap().to_string()) | ||
.collect(); | ||
|
||
Ok(member_names) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
amFOSS Daemon: A discord bot for the amFOSS Discord server. | ||
Copyright (C) 2024 amFOSS | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
use crate::tasks::{get_tasks, Task}; | ||
use serenity::client::Context as SerenityContext; | ||
|
||
use tokio::spawn; | ||
|
||
pub async fn run_scheduler(ctx: SerenityContext) { | ||
let tasks = get_tasks(); | ||
|
||
for task in tasks { | ||
spawn(schedule_task(ctx.clone(), task)); | ||
} | ||
} | ||
|
||
async fn schedule_task(ctx: SerenityContext, task: Box<dyn Task>) { | ||
loop { | ||
let next_run_in = task.interval(); | ||
tokio::time::sleep(next_run_in).await; | ||
|
||
task.run(ctx.clone()).await; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* | ||
amFOSS Daemon: A discord bot for the amFOSS Discord server. | ||
Copyright (C) 2024 amFOSS | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
use crate::{ | ||
graphql::fetch_members, | ||
utils::{get_five_am_timestamp, time_until}, | ||
}; | ||
use async_trait::async_trait; | ||
use serenity::{ | ||
all::{ChannelId, Message}, | ||
client::Context, | ||
}; | ||
|
||
use tokio::time::Duration; | ||
|
||
const GROUP_ONE_CHANNEL_ID: u64 = 1225098248293716008; | ||
const GROUP_TWO_CHANNEL_ID: u64 = 1225098298935738489; | ||
const GROUP_THREE_CHANNEL_ID: u64 = 1225098353378070710; | ||
const GROUP_FOUR_CHANNEL_ID: u64 = 1225098407216156712; | ||
const STATUS_UPDATE_CHANNEL_ID: u64 = 764575524127244318; | ||
|
||
#[async_trait] | ||
pub trait Task: Send + Sync { | ||
fn name(&self) -> &'static str; | ||
fn interval(&self) -> Duration; | ||
async fn run(&self, ctx: Context); | ||
} | ||
|
||
pub struct StatusUpdateCheck; | ||
|
||
#[async_trait] | ||
impl Task for StatusUpdateCheck { | ||
fn name(&self) -> &'static str { | ||
"StatusUpdateCheck" | ||
} | ||
|
||
fn interval(&self) -> Duration { | ||
time_until(5, 0) | ||
} | ||
|
||
async fn run(&self, ctx: Context) { | ||
let members = fetch_members().await.expect("Root must be up."); | ||
|
||
let channel_ids: Vec<ChannelId> = vec![ | ||
ChannelId::new(GROUP_ONE_CHANNEL_ID), | ||
ChannelId::new(GROUP_TWO_CHANNEL_ID), | ||
ChannelId::new(GROUP_THREE_CHANNEL_ID), | ||
ChannelId::new(GROUP_FOUR_CHANNEL_ID), | ||
]; | ||
|
||
let time = chrono::Local::now().with_timezone(&chrono_tz::Asia::Kolkata); | ||
let today_five_am = get_five_am_timestamp(time); | ||
let yesterday_five_am = today_five_am - chrono::Duration::hours(24); | ||
|
||
let mut valid_updates: Vec<Message> = vec![]; | ||
|
||
for &channel_id in &channel_ids { | ||
let builder = serenity::builder::GetMessages::new().limit(50); | ||
match channel_id.messages(&ctx.http, builder).await { | ||
Ok(messages) => { | ||
let filtered_messages: Vec<Message> = messages | ||
.into_iter() | ||
.filter(|msg| { | ||
msg.timestamp >= yesterday_five_am.into() | ||
&& msg.timestamp < today_five_am.into() | ||
&& msg.content.to_lowercase().contains("namah shivaya") | ||
&& msg.content.to_lowercase().contains("regards") | ||
}) | ||
.collect(); | ||
|
||
valid_updates.extend(filtered_messages); | ||
} | ||
Err(e) => println!("ERROR: {:?}", e), | ||
} | ||
} | ||
|
||
let mut naughty_list: Vec<String> = vec![]; | ||
|
||
for member in &members { | ||
let name_parts: Vec<&str> = member.split_whitespace().collect(); | ||
let first_name = name_parts.get(0).unwrap_or(&""); | ||
let last_name = name_parts.get(1).unwrap_or(&""); | ||
let has_sent_update = valid_updates | ||
.iter() | ||
.any(|msg| msg.content.contains(first_name) || msg.content.contains(last_name)); | ||
|
||
if !has_sent_update { | ||
naughty_list.push(member.to_string()); | ||
} | ||
} | ||
|
||
let status_update_channel = ChannelId::new(STATUS_UPDATE_CHANNEL_ID); | ||
|
||
if naughty_list.is_empty() { | ||
status_update_channel | ||
.say(ctx.http, "Everyone sent their update today!") | ||
.await; | ||
} else { | ||
let formatted_list = naughty_list | ||
.iter() | ||
.enumerate() | ||
.map(|(i, member)| format!("{}. {:?}", i + 1, member)) | ||
.collect::<Vec<String>>() | ||
.join("\n"); | ||
status_update_channel | ||
.say( | ||
ctx.http, | ||
format!( | ||
"These members did not send their updates:\n{}", | ||
formatted_list | ||
), | ||
) | ||
.await; | ||
} | ||
} | ||
} | ||
|
||
pub fn get_tasks() -> Vec<Box<dyn Task>> { | ||
vec![Box::new(StatusUpdateCheck)] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
amFOSS Daemon: A discord bot for the amFOSS Discord server. | ||
Copyright (C) 2024 amFOSS | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
use chrono::{DateTime, Datelike, Local, TimeZone}; | ||
use chrono_tz::Tz; | ||
use tokio::time::Duration; | ||
|
||
pub fn time_until(hour: u32, minute: u32) -> Duration { | ||
let now = chrono::Local::now().with_timezone(&chrono_tz::Asia::Kolkata); | ||
let today_run = now.date().and_hms(hour, minute, 0); | ||
|
||
let next_run = if now < today_run { | ||
today_run | ||
} else { | ||
today_run + chrono::Duration::days(1) | ||
}; | ||
|
||
let time_until = (next_run - now).to_std().unwrap(); | ||
Duration::from_secs(time_until.as_secs()) | ||
} | ||
|
||
pub fn get_five_am_timestamp(now: DateTime<Tz>) -> DateTime<Local> { | ||
chrono::Local | ||
.ymd(now.year(), now.month(), now.day()) | ||
.and_hms_opt(5, 0, 0) | ||
.expect("Chrono must work.") | ||
} |