Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ ROOT_URL=https://root.amfoss.in/
OWNER_ID=
DEBUG=true
ENABLE_DEBUG_LIBRARIES=false
RECENT_PICKS_PATH=
AMD_API_KEY=
AMD_APP_PASSWORD=
AMD_EMAIL_ID=
51 changes: 26 additions & 25 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ poise = "0.6.1"
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
imap = "3.0.0-alpha.12"
mailparse = "0.15"
html2text = "0.12"
html2text = "0.12"
rand = "0.8.5"
4 changes: 3 additions & 1 deletion src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod random;
mod set_log_level;

use crate::commands::random::random;
use crate::commands::set_log_level::set_log_level;
use serenity::all::RoleId;
use tracing::{debug, instrument};
Expand Down Expand Up @@ -31,7 +33,7 @@ async fn amdctl(ctx: Context<'_>) -> Result<(), Error> {

/// Returns a vector containg [Poise Commands][`poise::Command`]
pub fn get_commands() -> Vec<poise::Command<Data, Error>> {
let commands = vec![amdctl(), set_log_level()];
let commands = vec![amdctl(), set_log_level(), random()];
debug!(commands = ?commands.iter().map(|c| &c.name).collect::<Vec<_>>());
commands
}
112 changes: 112 additions & 0 deletions src/commands/random.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use crate::{
ids::{FIRST_YEAR_ROLE_ID, SECOND_YEAR_ROLE_ID, THIRD_YEAR_ROLE_ID},
Context, Error,
};
Comment thread
hrideshmg marked this conversation as resolved.
use rand::seq::IteratorRandom;
use serenity::all::{Mentionable as _, Role, RoleId, UserId};
use std::collections::HashSet;

#[poise::command(slash_command)]
pub async fn random(
ctx: Context<'_>,
count: Option<u32>,
role1: Option<Role>,
role2: Option<Role>,
role3: Option<Role>,
) -> Result<(), Error> {
let guild = ctx.guild_id().ok_or("No guild id")?;
let members = guild.members(ctx.http(), None, None).await?;

let count = count.unwrap_or(3) as usize;
let mut selected_roles: HashSet<RoleId> = [role1, role2, role3]
.into_iter()
.flatten()
.map(|role| role.id)
.collect();

if selected_roles.is_empty() {
selected_roles.extend([
RoleId::new(FIRST_YEAR_ROLE_ID),
RoleId::new(SECOND_YEAR_ROLE_ID),
RoleId::new(THIRD_YEAR_ROLE_ID),
]);
}
Comment thread
hrideshmg marked this conversation as resolved.

let eligible_members: Vec<_> = members // Filtering out bots and other ineligible members
.into_iter()
.filter(|m| !m.user.bot && (m.roles.iter().any(|role| selected_roles.contains(role))))
.collect();

if eligible_members.is_empty() {
ctx.say("No eligible members found.").await?;
return Ok(());
}

let recent_picks = {
// Accessing recently picked members to avoid repetition
let data = ctx.data();

@hrideshmg hrideshmg Jun 24, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is ctx.data() persisted across bot restarts? If not, it can't be relied upon as the bot service may restart due to various factors.

let recent_random_picks = data.recent_random_picks.lock().unwrap();
recent_random_picks.clone()
};

let available_members: Vec<_> = eligible_members // Members who haven't been picked recently
.iter()
.filter(|member| !recent_picks.contains(&member.user.id))
.collect();

/* Since ThreadRng is not Send, keeping it alive across an .await causes command future to become non-Send.
So, we are enclosing the selection in it's own scope so the ThreadRng is dropped before we hit any .await,
allowing the command future to remain Send
*/

let selected = {
let mut rng = rand::thread_rng();

let mut selected: Vec<_> = available_members
.into_iter()
.choose_multiple(&mut rng, count);

if selected.len() < count {
// If not enough members are available, fetch recently picked members
let remaining_needed = count - selected.len();

let selected_ids: HashSet<UserId> = selected.iter().map(|m| m.user.id).collect();

let additional: Vec<_> = eligible_members
.iter()
.filter(|m| !selected_ids.contains(&m.user.id))
.choose_multiple(&mut rng, remaining_needed);

selected.extend(additional);
}
selected
};

let ping_message = selected
.iter()
.map(|m| m.user.mention().to_string())
.collect::<Vec<_>>()
.join("\n");

{
let data = ctx.data();
let mut recent_random_picks = data.recent_random_picks.lock().unwrap();
recent_random_picks.extend(selected.iter().map(|m| m.user.id)); // Adding selected members to recently picked set

let eligible_ids: HashSet<UserId> = eligible_members.iter().map(|m| m.user.id).collect();
recent_random_picks.retain(|user_id| eligible_ids.contains(user_id));
if recent_random_picks.len() >= eligible_ids.len() {
// If all eligible members have been picked atleast once, clear the recently picked set
recent_random_picks.clear();
}
} // guard dropped here, mutex unlocked
ctx.data().save_recent_picks();
ctx.say(format!(
"Pinging {} members: {}",
selected.len(),
ping_message
))
.await?;

Ok(())
}
3 changes: 3 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub struct Config {
pub prefix_string: String,
pub root_url: String,
pub api_key: String,
pub recent_picks_path: String,
}

impl Default for Config {
Expand All @@ -48,6 +49,8 @@ impl Default for Config {
prefix_string: String::from("$"),
root_url: std::env::var("ROOT_URL").expect("ROOT_URL was not found in env"),
api_key: std::env::var("AMD_API_KEY").expect("AMD_API_KEY was not found in env"),
recent_picks_path: std::env::var("RECENT_PICKS_PATH")
.unwrap_or_else(|_| String::from("data/recent_picks.json")),
Comment thread
hrideshmg marked this conversation as resolved.
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/ids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ pub const GROUP_ONE_STATUS_UPDATE_CHANNEL_ID: u64 = 1225098248293716008;
pub const GROUP_TWO_STATUS_UPDATE_CHANNEL_ID: u64 = 1225098298935738489;
pub const GROUP_THREE_STATUS_UPDATE_CHANNEL_ID: u64 = 1225098353378070710;
pub const GROUP_FOUR_STATUS_UPDATE_CHANNEL_ID: u64 = 1225098407216156712;

pub const FIRST_YEAR_ROLE_ID: u64 = 1283689015450669108;
pub const SECOND_YEAR_ROLE_ID: u64 = 1288903184990994522;
Loading
Loading