add discord membercount service & endpoint

a background worker is added that sends an approximate member count
request every 60 seconds, caches it into memory, and serves the cache on
a public endpoint
This commit is contained in:
2025-07-09 00:06:56 +02:00
parent fc458143a9
commit 6a12495686
5 changed files with 78 additions and 5 deletions

View File

@@ -1,3 +1,4 @@
use crate::discordbot::MAIN_GUILD_ID;
use chrono::{Datelike, Utc}; use chrono::{Datelike, Utc};
use chrono_tz::Europe::Warsaw; use chrono_tz::Europe::Warsaw;
use rand::{SeedableRng, seq::IndexedRandom}; use rand::{SeedableRng, seq::IndexedRandom};
@@ -21,8 +22,6 @@ pub fn init_service(ctx: &Context, guild_id: &GuildId) {
}); });
} }
const MAIN_GUILD_ID: GuildId = GuildId::new(447075692664979466);
pub enum Event { pub enum Event {
Normal, Normal,
PolskaGórą, PolskaGórą,

View File

@@ -0,0 +1,22 @@
use crate::discordbot::MAIN_GUILD_ID;
use serenity::all::{Context, GuildId};
use service::run_membercount_service;
use tracing::info;
mod service;
pub use service::get_member_count;
pub fn init_service(ctx: &Context, guild_id: &GuildId) {
let (ctx, guild_id) = (ctx.clone(), guild_id.clone());
info!("Initialising discord member count service...");
if guild_id != MAIN_GUILD_ID {
info!("Guild member count service not initialised; Bot not running on main guild.");
return;
}
tokio::spawn(async move {
run_membercount_service(ctx, guild_id).await;
});
}

View File

@@ -0,0 +1,33 @@
use serenity::all::{Context, GuildId};
use std::{
sync::{Arc, LazyLock},
time::Duration,
};
use tokio::{sync::RwLock, time::sleep};
use tracing::error;
static MEMBER_COUNT: LazyLock<Arc<RwLock<Option<u64>>>> =
LazyLock::new(|| Arc::new(RwLock::new(None)));
pub async fn run_membercount_service(ctx: Context, guild_id: GuildId) {
loop {
update_member_count(&ctx, guild_id).await;
sleep(Duration::from_secs(60)).await;
}
}
pub async fn get_member_count() -> Option<u64> {
*MEMBER_COUNT.read().await
}
async fn update_member_count(ctx: &Context, guild_id: GuildId) {
let count = match ctx.http.get_guild_with_counts(guild_id).await {
Ok(guild) => guild.approximate_member_count,
Err(e) => {
error!("Could not fetch guild member count: {e}");
None
}
};
let mut member_count = MEMBER_COUNT.write().await;
*member_count = count;
}

View File

@@ -1,5 +1,7 @@
use serenity::{ use serenity::{
all::{CreateInteractionResponse, CreateInteractionResponseMessage, Interaction, Ready}, all::{
CreateInteractionResponse, CreateInteractionResponseMessage, GuildId, Interaction, Ready,
},
async_trait, async_trait,
prelude::*, prelude::*,
}; };
@@ -12,8 +14,12 @@ struct Handler;
mod commands; mod commands;
mod events; mod events;
mod member_count;
mod status; mod status;
const MAIN_GUILD_ID: GuildId = GuildId::new(447075692664979466);
pub use member_count::get_member_count;
#[async_trait] #[async_trait]
impl EventHandler for Handler { impl EventHandler for Handler {
async fn ready(&self, ctx: Context, ready: Ready) { async fn ready(&self, ctx: Context, ready: Ready) {
@@ -37,6 +43,7 @@ impl EventHandler for Handler {
status::init_service(&ctx); status::init_service(&ctx);
events::init_service(&ctx, &guild_id); events::init_service(&ctx, &guild_id);
member_count::init_service(&ctx, &guild_id);
} }
async fn interaction_create(&self, ctx: Context, interaction: Interaction) { async fn interaction_create(&self, ctx: Context, interaction: Interaction) {

View File

@@ -1,5 +1,17 @@
use axum::{Router, routing::get}; use axum::{Router, http::StatusCode, routing::get};
pub fn init() -> Router { pub fn init() -> Router {
Router::new().route("/", get(async || "root")) Router::new()
.route("/", get(async || "root"))
.route("/discord/member-count", get(get_member_count))
}
async fn get_member_count() -> (StatusCode, String) {
match crate::discordbot::get_member_count().await {
Some(count) => (StatusCode::OK, format!("{count}")),
None => (
StatusCode::INTERNAL_SERVER_ERROR,
format!("An error occured - could not fetch discord member count."),
),
}
} }