Refactor system listeners to be a config item

This commit is contained in:
Joey Hines 2026-06-28 10:39:49 -06:00
parent dbae599c43
commit bb6a961c22
Signed by: joeyahines
GPG Key ID: E99D8FB14855100E
9 changed files with 68 additions and 68 deletions

2
Cargo.lock generated
View File

@ -1403,7 +1403,7 @@ dependencies = [
[[package]]
name = "fren"
version = "2.9.0"
version = "2.10.0"
dependencies = [
"axum 0.8.1",
"base64 0.22.1",

View File

@ -1,6 +1,6 @@
[package]
name = "fren"
version = "2.9.0"
version = "2.10.0"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,16 +1,16 @@
use std::sync::Arc;
use crate::config::GlobalData;
use crate::discord::voices::speak;
use crate::models::api_key::Apikey;
use axum::extract::State;
use axum::{http::StatusCode, response::IntoResponse, routing::post, Json, Router};
use axum::{Json, Router, http::StatusCode, response::IntoResponse, routing::post};
use log::info;
use serde::Deserialize;
use std::sync::Arc;
#[derive(Clone)]
struct ApiContext {
data: Arc<GlobalData>,
ctx: poise::serenity_prelude::Context
ctx: poise::serenity_prelude::Context,
}
pub async fn web_server(data: Arc<GlobalData>, ctx: poise::serenity_prelude::Context) {
@ -18,10 +18,7 @@ pub async fn web_server(data: Arc<GlobalData>, ctx: poise::serenity_prelude::Con
let app = Router::new()
.route("/play", post(play_sound))
.with_state(ApiContext {
data,
ctx
});
.with_state(ApiContext { data, ctx });
info!("Serving bot api on: {}", addr);
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
@ -39,9 +36,9 @@ async fn play_sound(
State(ctx): State<ApiContext>,
Json(payload): Json<SoundPayload>,
) -> impl IntoResponse {
if let Some(api_key) = Apikey::find_key_from_secret(&ctx.data.db, &payload.api_key).unwrap()
&& let Some(user_id) = api_key.user_id {
&& let Some(user_id) = api_key.user_id
{
info!("Playing audio for {user_id}");
speak(
&ctx.ctx,

View File

@ -1,6 +1,6 @@
use crate::album_manager::AlbumManager;
use crate::error::Error;
use crate::event_listener::{Action, Expiration, Listener, TriggerType};
use crate::event_listener::Listener;
use crate::migrations::{CURRENT_DB_VERSION, do_migration};
use config::{Config, File};
use cta_api::CTAClient;
@ -30,6 +30,11 @@ pub struct PicOxConfig {
token: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SystemListeners {
pub listeners: Vec<Listener>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct BotConfig {
pub bot_token: String,
@ -51,6 +56,7 @@ pub struct BotConfig {
pub music_streaming_site_urls: Vec<String>,
pub music_streaming_sites: Vec<String>,
pub picox: PicOxConfig,
pub listeners_cfg: SystemListeners,
}
impl BotConfig {
@ -93,43 +99,17 @@ pub struct GlobalData {
}
impl GlobalData {
fn setup_system_listeners(db: &Database) -> Result<(), Error> {
fn setup_system_listeners(db: &Database, cfg: &BotConfig) -> Result<(), Error> {
db.filter(|_, listener: &Listener| listener.system_listener)?
.for_each(|listener: Listener| {
let _ = db.remove::<Listener>(listener.id().unwrap()).is_ok();
});
info!("Adding system listeners...");
Listener::add_listener(
db,
Listener::new(
TriggerType::OnMessage {
channel_id: None,
content: Some("bad bot".to_string()),
},
vec![Action::React {
emoji: "😭".to_string(),
}],
1.0,
Expiration::Never,
true,
),
)?;
Listener::add_listener(
db,
Listener::new(
TriggerType::OnMessage {
channel_id: None,
content: None,
},
vec![Action::UpdateFrenCoins {
fren_coin_diff: 100,
}],
0.05,
Expiration::Never,
true,
),
)?;
for listener in &cfg.listeners_cfg.listeners {
Listener::add_listener(db, listener.clone())?;
}
Ok(())
}
@ -148,7 +128,7 @@ impl GlobalData {
do_migration(&db);
Self::setup_system_listeners(&db)?;
Self::setup_system_listeners(&db, &cfg)?;
Ok(Self {
args,

View File

@ -350,7 +350,21 @@ pub async fn list_social_credit_phrases(ctx: Context<'_>) -> Result<(), Error> {
));
}
ctx.reply(list.build()).await?;
let msg = list.build();
if msg.len() < MESSAGE_CODE_LIMIT {
ctx.reply(msg).await?;
} else {
ctx.send(
CreateReply::default()
.reply(true)
.attachment(CreateAttachment::bytes(
msg.as_bytes(),
"social_credit_phrases.md",
)),
)
.await?;
}
Ok(())
}

View File

@ -19,6 +19,7 @@ mod stonks;
mod transit;
pub(crate) mod voices;
use crate::api::web_server;
use crate::config::GlobalData;
use crate::discord::fren_coin::give_coin;
use crate::discord::joke::random;
@ -39,7 +40,6 @@ use songbird::SerenityInit;
use std::sync::Arc;
use std::time::Duration;
use url::Url;
use crate::api::web_server;
pub type Context<'a> = poise::Context<'a, Arc<GlobalData>, Error>;
@ -69,10 +69,7 @@ async fn event_handler(
let ctx_web = ctx.clone();
tokio::spawn(async move {
web_server(
data_web,
ctx_web
).await;
web_server(data_web, ctx_web).await;
});
}
}

View File

@ -1,3 +1,4 @@
use crate::config::GlobalData;
use crate::discord::Context;
use crate::error::Error;
use poise::async_trait;
@ -14,7 +15,6 @@ use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use crate::config::GlobalData;
async fn get_voice_dictionary(path: &Path) -> Result<HashMap<String, PathBuf>, tokio::io::Error> {
let mut dir = tokio::fs::read_dir(path).await?;
@ -146,7 +146,7 @@ pub async fn speak(
let word = word.to_lowercase();
if word.is_empty() || word == " " {
continue
continue;
}
let mut add_period = false;
@ -245,7 +245,16 @@ pub async fn say(
) -> Result<(), Error> {
let guild_id = ctx.guild_id().unwrap();
if let Err(err) = speak(ctx.serenity_context(), ctx.data(), guild_id, ctx.author().id, &voice, &phrase).await {
if let Err(err) = speak(
ctx.serenity_context(),
ctx.data(),
guild_id,
ctx.author().id,
&voice,
&phrase,
)
.await
{
match err {
VoiceError::VoiceNotFound(_)
| VoiceError::WordNotFound(_)

View File

@ -136,6 +136,7 @@ pub struct Listener {
pub trigger_chance: f64,
pub expiration: Expiration,
pub system_listener: bool,
#[serde(default)]
pub trigger_count: u64,
}

View File

@ -1,4 +1,5 @@
mod album_manager;
mod api;
mod config;
mod discord;
mod error;
@ -9,14 +10,13 @@ mod migrations;
mod models;
mod music;
mod user;
mod api;
use crate::config::{Args, BotConfig, GlobalData};
use crate::discord::run_bot;
use log::{error, info};
use magick_rust::magick_wand_genesis;
use std::sync::Once;
use rustls::crypto;
use std::sync::Once;
use structopt::StructOpt;
use tracing_core::LevelFilter;
use tracing_subscriber::EnvFilter;
@ -27,7 +27,9 @@ static START: Once = Once::new();
#[tokio::main]
async fn main() {
crypto::aws_lc_rs::default_provider().install_default().expect("Unable to setup default Rustls provider");
crypto::aws_lc_rs::default_provider()
.install_default()
.expect("Unable to setup default Rustls provider");
let args: Args = Args::from_args();
tracing_subscriber::fmt()