Skip to content

755: Write Codebloom standup bot in Rust#745

Open
tahminator wants to merge 1 commit intomainfrom
755
Open

755: Write Codebloom standup bot in Rust#745
tahminator wants to merge 1 commit intomainfrom
755

Conversation

@tahminator
Copy link
Owner

@tahminator tahminator commented Feb 7, 2026

755

Description of changes

Checklist before review

  • I have done a thorough self-review of the PR
  • Copilot has reviewed my latest changes, and all comments have been fixed and/or closed.
  • If I have made database changes, I have made sure I followed all the db repo rules listed in the wiki here. (check if no db changes)
  • All tests have passed
  • I have successfully deployed this PR to staging
  • I have done manual QA in both dev (and staging if possible) and attached screenshots below.

Screenshots

Dev

Staging

@github-actions
Copy link
Contributor

github-actions bot commented Feb 7, 2026

Available PR Commands

  • /ai - Triggers all AI review commands at once
  • /review - AI review of the PR changes
  • /describe - AI-powered description of the PR
  • /improve - AI-powered suggestions
  • /deploy - Deploy to staging

See: https://github.com/tahminator/codebloom/wiki/CI-Commands

@github-actions
Copy link
Contributor

github-actions bot commented Feb 7, 2026

Title

755: Write Codebloom standup bot in Rust


PR Type

Enhancement, Documentation


Description

  • Add Rust Discord standup bot

  • Schedule Mon/Wed 12pm ET via Redis

  • Send embed; add slash command

  • Env templates and git-crypt config


Diagram Walkthrough

flowchart LR
  A["Tokio interval (15 min)"]
  B["Check Redis last standup"]
  C["Send Discord 'Codebloom Standup' embed"]
  D["Set last standup in Redis"]
  E["No-op"]
  F["Load env credentials"]
  G1["Init Redis client"]
  H1["Init Discord client (background)"]

  A --> B
  B -- "due today?" --> C
  C --> D
  B -- "not due" --> E
  F --> G1
  F --> H1
Loading

File Walkthrough

Relevant files
Configuration changes
5 files
.gitattributes
Encrypt standup-bot production env with git-crypt               
+2/-0     
example.env
Provide development environment variable template               
+9/-0     
example.env.production
Provide production environment variable template                 
+9/-0     
credentials.rs
Load Discord credentials from environment                               
+32/-0   
credentials.rs
Load Redis URI credentials                                                             
+11/-0   
Documentation
2 files
README.md
Introduce internal services directory overview                     
+3/-0     
README
Document Rust Discord standup bot purpose                               
+6/-0     
Dependencies
1 files
Cargo.toml
Create crate and declare dependencies                                       
+12/-0   
Enhancement
6 files
client.rs
Initialize Discord client and embed sender                             
+54/-0   
ping.rs
Implement hello slash command                                                       
+10/-0   
handlers.rs
Handle interactions and register guild commands                   
+49/-0   
main.rs
Schedule standup checks and dispatch messages                       
+65/-0   
client.rs
Create Redis connection and timestamp helpers                       
+43/-0   
utils.rs
Compute Monday/Wednesday noon ET trigger                                 
+21/-0   
Miscellaneous
3 files
mod.rs
Add commands module wiring                                                             
+1/-0     
mod.rs
Expose Discord submodules                                                               
+4/-0     
mod.rs
Expose Redis submodules                                                                   
+3/-0     
Error handling
1 files
error.rs
Define Redis client error types                                                   
+21/-0   
Additional files
1 files
.env.production [link]   

@github-actions
Copy link
Contributor

github-actions bot commented Feb 7, 2026

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Issue

When no prior standup timestamp exists in Redis, the bot passes the current time to is_time_to_send_standup_message, which makes it think a standup was already sent today, preventing the first message from ever sending. Initialize with a past timestamp or accept Option<DateTime> in the check.

None => {
    if !is_time_to_send_standup_message(Utc::now()) {
        return;
    }
}
Misconfigured Intents

The Discord gateway intents are set to GUILD_MEMBERS only, which is privileged and likely unnecessary. Use minimal required intents (e.g., GUILDS or non_privileged) to reliably receive interactions and reduce permission scope.

static INTENTS: GatewayIntents = GatewayIntents::GUILD_MEMBERS;
static HTTP: OnceLock<Arc<Http>> = OnceLock::new();

pub async fn init_in_bg(discord_creds: &DiscordCredentials) -> Result<(), Error> {
    let mut client = Client::builder(&discord_creds.token, INTENTS)
        .event_handler(Handler)
        .await?;
Staging Config Gap

Only a production env file is encrypted (internal/standup-bot/.env.production). To meet AC (use staging credentials and deploy via Coolify), add a staging env (internal/standup-bot/.env.staging) and ensure Coolify wiring references it; otherwise risk using production creds.

internal/standup-bot/.env.production filter=git-crypt-l3 diff=git-crypt-l3

Comment on lines +44 to +48
None => {
if !is_time_to_send_standup_message(Utc::now()) {
return;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: When there is no prior standup time in Redis, passing Utc::now() makes the function think a message was already sent today, blocking the first send. Treat the "no record" case as "not sent today" by passing a timestamp from a prior day. This ensures the bot sends on first eligible tick. [possible issue, importance: 9]

Suggested change
None => {
if !is_time_to_send_standup_message(Utc::now()) {
return;
}
}
None => {
// No prior send recorded; treat as not sent today and allow send if it's the right window.
if !is_time_to_send_standup_message(Utc::now() - chrono::Duration::days(1)) {
return;
}
}


use crate::discord::{credentials::DiscordCredentials, handlers::Handler};

static INTENTS: GatewayIntents = GatewayIntents::GUILD_MEMBERS;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Using the privileged GUILD_MEMBERS intent is unnecessary here and can prevent the gateway from working if not enabled in the Discord portal. Switch to the non-privileged GUILDS intent to reliably receive readiness and interaction events without extra configuration. [general, importance: 8]

Suggested change
static INTENTS: GatewayIntents = GatewayIntents::GUILD_MEMBERS;
static INTENTS: GatewayIntents = GatewayIntents::GUILDS;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant