Initial event/session management work
parent
b2c9213bab
commit
4bddb8371c
|
@ -1,2 +1,4 @@
|
|||
/target
|
||||
/.idea
|
||||
config.toml
|
||||
session_config.toml
|
|
@ -342,6 +342,27 @@ dependencies = [
|
|||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd6dd8046d00723a59a2f8c5f295c515b9bb9a331ee4f8f3d4dd49e428acd3b6"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz-build",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz-build"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7"
|
||||
dependencies = [
|
||||
"parse-zoneinfo",
|
||||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
|
@ -814,7 +835,7 @@ dependencies = [
|
|||
"futures-sink",
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"indexmap 2.2.6",
|
||||
"indexmap 2.5.0",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
|
@ -1021,9 +1042,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.5",
|
||||
|
@ -1274,6 +1295,15 @@ dependencies = [
|
|||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse-zoneinfo"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.1"
|
||||
|
@ -1338,7 +1368,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap 2.2.6",
|
||||
"indexmap 2.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1701,9 +1769,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rpb"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"config",
|
||||
"env_logger",
|
||||
"log",
|
||||
|
@ -1713,6 +1782,7 @@ dependencies = [
|
|||
"serde",
|
||||
"structopt",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tonic",
|
||||
]
|
||||
|
||||
|
@ -1905,9 +1975,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.6"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
|
||||
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -1979,6 +2049,12 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||
|
||||
[[package]]
|
||||
name = "skeptic"
|
||||
version = "0.13.7"
|
||||
|
@ -2319,9 +2395,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.14"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
|
@ -2331,20 +2407,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.6"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.14"
|
||||
version = "0.22.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
||||
checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf"
|
||||
dependencies = [
|
||||
"indexmap 2.2.6",
|
||||
"indexmap 2.5.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
|
@ -2935,9 +3011,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.13"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
|
||||
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rpb"
|
||||
description = "Role Playing Bot!"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
@ -15,6 +15,8 @@ raas_types = {version = "0.0.9", registry = "jojo-dev"}
|
|||
tonic = "0.11.0"
|
||||
roll-rs = "0.3.0"
|
||||
chrono = "0.4.38"
|
||||
chrono-tz = "0.10.0"
|
||||
toml = "0.8.19"
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "1.38.0"
|
||||
|
|
146
src/bot.rs
146
src/bot.rs
|
@ -1,8 +1,13 @@
|
|||
use crate::config::BotConfig;
|
||||
use crate::context::Context;
|
||||
use crate::model::session::{Session, SessionConfig};
|
||||
use chrono::Utc;
|
||||
use log::{debug, info};
|
||||
use poise::serenity_prelude::{CreateAttachment, MessageBuilder};
|
||||
use poise::futures_util::{Stream, StreamExt};
|
||||
use poise::serenity_prelude::{
|
||||
futures, AutocompleteChoice, CreateAttachment, EditScheduledEvent, MessageBuilder,
|
||||
ScheduledEventId, ScheduledEventStatus,
|
||||
};
|
||||
use poise::CreateReply;
|
||||
use raas_types::raas::bot::roll::{roll_response, Roll, RollCmd};
|
||||
use raas_types::raas::resp::response::Resp;
|
||||
|
@ -91,6 +96,128 @@ fn real_roll_help() -> String {
|
|||
"Roll a real D20!".to_string()
|
||||
}
|
||||
|
||||
#[poise::command(slash_command)]
|
||||
async fn create_session(
|
||||
ctx: BotContext<'_>,
|
||||
#[description = "Create session from event"] event_name: String,
|
||||
) -> Result<(), Error> {
|
||||
let guild = ctx.guild_id().unwrap();
|
||||
|
||||
let events = guild.scheduled_events(&ctx.http(), false).await?;
|
||||
|
||||
let event = events
|
||||
.iter()
|
||||
.find(|e| e.name.eq_ignore_ascii_case(&event_name))
|
||||
.unwrap();
|
||||
|
||||
let path = ctx.data().config.session_config_path.clone();
|
||||
let mut session_config = ctx.data().session_config.lock().await;
|
||||
|
||||
session_config.create_session(event.id).await;
|
||||
|
||||
session_config.save_config(path).await.unwrap();
|
||||
|
||||
ctx.reply(format!("Created session from event '{}'", event.name))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[poise::command(slash_command)]
|
||||
async fn start_session(
|
||||
ctx: BotContext<'_>,
|
||||
#[description = "Session Name"]
|
||||
#[autocomplete = "autocomplete_session"]
|
||||
session: ScheduledEventId,
|
||||
) -> Result<(), Error> {
|
||||
let sessions = &mut ctx.data().session_config.lock().await;
|
||||
|
||||
let session = sessions
|
||||
.sessions
|
||||
.iter()
|
||||
.find(|s| s.event == session)
|
||||
.unwrap();
|
||||
|
||||
let event = ctx
|
||||
.guild_id()
|
||||
.unwrap()
|
||||
.edit_scheduled_event(
|
||||
ctx.http(),
|
||||
session.event,
|
||||
EditScheduledEvent::new().status(ScheduledEventStatus::Active),
|
||||
)
|
||||
.await?;
|
||||
|
||||
ctx.reply(format!("Started session '{}'", event.name))
|
||||
.await?;
|
||||
|
||||
sessions.active_session = Some(event.id);
|
||||
|
||||
sessions.save_config(ctx.data().config.session_config_path.clone()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[poise::command(slash_command)]
|
||||
async fn stop_session(ctx: BotContext<'_>) -> Result<(), Error> {
|
||||
let sessions = &mut ctx.data().session_config.lock().await;
|
||||
|
||||
if let Some(session) = sessions.active_session {
|
||||
match ctx
|
||||
.guild_id()
|
||||
.unwrap()
|
||||
.edit_scheduled_event(
|
||||
&ctx.http(),
|
||||
session,
|
||||
EditScheduledEvent::new().status(ScheduledEventStatus::Completed),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(e) => {
|
||||
ctx.reply(format!("Ending session '{}'", e.name)).await?;
|
||||
}
|
||||
Err(err) => {
|
||||
ctx.reply(format!("Failed to end session: '{}'", err))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ctx.reply("No sessions in progress!").await?;
|
||||
}
|
||||
|
||||
sessions.active_session = None;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn autocomplete_session<'a>(
|
||||
ctx: BotContext<'a>,
|
||||
partial: &'a str,
|
||||
) -> impl Stream<Item = poise::serenity_prelude::AutocompleteChoice> + 'a {
|
||||
let sessions: Vec<Session> = { ctx.data().session_config.lock().await.sessions.clone() };
|
||||
|
||||
futures::stream::iter(sessions).filter_map(move |s| async move {
|
||||
let event = ctx
|
||||
.guild_id()
|
||||
.unwrap()
|
||||
.scheduled_event(ctx.http(), s.event, false)
|
||||
.await;
|
||||
|
||||
if let Ok(event) = event {
|
||||
if event.status == ScheduledEventStatus::Scheduled
|
||||
&& event.name.matches(partial).count() > 0
|
||||
{
|
||||
Some(AutocompleteChoice::new(event.name, event.id.to_string()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn start_bot(config: BotConfig) {
|
||||
info!("Starting bot!");
|
||||
|
||||
|
@ -100,13 +227,26 @@ pub async fn start_bot(config: BotConfig) {
|
|||
|
||||
let framework = poise::Framework::builder()
|
||||
.options(poise::FrameworkOptions {
|
||||
commands: vec![roll(), real_roll()],
|
||||
commands: vec![
|
||||
roll(),
|
||||
real_roll(),
|
||||
create_session(),
|
||||
start_session(),
|
||||
stop_session(),
|
||||
],
|
||||
..Default::default()
|
||||
})
|
||||
.setup(|ctx, _ready, framework| {
|
||||
Box::pin(async move {
|
||||
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
||||
Ok(Context { config })
|
||||
Ok(Context {
|
||||
session_config: tokio::sync::Mutex::new(
|
||||
SessionConfig::init(config.session_config_path.clone())
|
||||
.await
|
||||
.unwrap(),
|
||||
),
|
||||
config,
|
||||
})
|
||||
})
|
||||
})
|
||||
.build();
|
||||
|
|
|
@ -12,6 +12,7 @@ pub struct Args {
|
|||
pub struct BotConfig {
|
||||
pub bot_token: String,
|
||||
pub raas_url: String,
|
||||
pub session_config_path: PathBuf,
|
||||
}
|
||||
|
||||
impl BotConfig {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use crate::config::BotConfig;
|
||||
use crate::model::session::SessionConfig;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
pub config: BotConfig,
|
||||
pub session_config: Mutex<SessionConfig>,
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use structopt::StructOpt;
|
|||
mod bot;
|
||||
mod config;
|
||||
mod context;
|
||||
mod model;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
pub mod playlist;
|
||||
pub mod scene;
|
||||
pub mod session;
|
|
@ -0,0 +1,14 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum Mode {
|
||||
InOrder,
|
||||
Shuffle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Playlist {
|
||||
pub name: String,
|
||||
pub playlist: String,
|
||||
pub mode: Mode,
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(tag = "adv_type")]
|
||||
pub enum SceneAdvancement {
|
||||
Manual,
|
||||
Timed { time_s: f32 },
|
||||
SequenceComplete,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum NextScene {
|
||||
NextInSequence,
|
||||
Previous,
|
||||
JumpTo { scene_name: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Scene {
|
||||
pub name: String,
|
||||
pub playlist: String,
|
||||
pub adv: SceneAdvancement,
|
||||
pub next_scene: NextScene,
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
use crate::model::playlist::Playlist;
|
||||
use crate::model::scene::Scene;
|
||||
use log::info;
|
||||
use poise::serenity_prelude::ScheduledEventId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct Session {
|
||||
pub event: ScheduledEventId,
|
||||
pub scenes: Vec<Scene>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct SessionConfig {
|
||||
pub active_session: Option<ScheduledEventId>,
|
||||
pub playlists: Vec<Playlist>,
|
||||
pub global_scenes: Vec<Scene>,
|
||||
pub sessions: Vec<Session>,
|
||||
}
|
||||
|
||||
impl SessionConfig {
|
||||
pub async fn init(path: PathBuf) -> Result<Self, std::io::Error> {
|
||||
if path.exists() {
|
||||
info!("Initializing with config at {:?}...", path);
|
||||
Self::load_config(path).await
|
||||
} else {
|
||||
info!("Initializing with a blank config...");
|
||||
let config = Self::default();
|
||||
|
||||
config.save_config(path).await?;
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn load_config(path: PathBuf) -> Result<Self, std::io::Error> {
|
||||
let file = tokio::fs::read(path).await?;
|
||||
|
||||
let toml_string = String::from_utf8(file).unwrap();
|
||||
|
||||
Ok(toml::from_str(&toml_string).unwrap())
|
||||
}
|
||||
|
||||
pub async fn save_config(&self, path: PathBuf) -> Result<(), std::io::Error> {
|
||||
tokio::fs::write(path, toml::to_string(self).unwrap()).await
|
||||
}
|
||||
|
||||
pub async fn create_session(&mut self, event: ScheduledEventId) {
|
||||
self.sessions.push(Session {
|
||||
event,
|
||||
scenes: vec![],
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue