Initial commit

main
Joey Hines 2024-06-30 10:23:45 -06:00
commit fb4e245034
Signed by: joeyahines
GPG Key ID: 995E531F7A569DDB
10 changed files with 3226 additions and 0 deletions

View File

@ -0,0 +1,2 @@
[registries.jojo-dev]
index = "https://git.jojodev.com/joeyahines/_cargo-index.git"

2
.gitignore vendored 100644
View File

@ -0,0 +1,2 @@
/target
/.idea

3022
Cargo.lock generated 100644

File diff suppressed because it is too large Load Diff

21
Cargo.toml 100644
View File

@ -0,0 +1,21 @@
[package]
name = "rpb"
description = "Role Playing Bot!"
version = "0.1.0"
edition = "2021"
[dependencies]
poise = "0.6.1"
config = "0.14.0"
serde = "1.0.203"
structopt = "0.3.26"
log = "0.4.22"
env_logger = "0.11.3"
raas_types = {version = "0.0.9", registry = "jojo-dev"}
tonic = "0.11.0"
rpg-dice-rust = "1.1.0"
chrono = "0.4.38"
[dependencies.tokio]
version = "1.38.0"
features = ["rt-multi-thread"]

2
README.md 100644
View File

@ -0,0 +1,2 @@
# Role Playing Bot
Simple D&D/TTRPG Bot Aid

2
config.toml 100644
View File

@ -0,0 +1,2 @@
bot_token = "NzEyNzE4NjQzNTkwNDYzNTIx.GWN1BZ.WqkDyZPB6OqZt1LL41lypjDKN0vPxDPZnkemZM"
raas_url = "http://158.69.121.46:12000"

118
src/bot.rs 100644
View File

@ -0,0 +1,118 @@
use crate::config::BotConfig;
use crate::context::Context;
use chrono::Utc;
use dicelib::solve_dice_expression;
use log::{debug, info};
use poise::serenity_prelude::{CreateAttachment, MessageBuilder};
use poise::CreateReply;
use raas_types::raas::bot::roll::{roll_response, Roll, RollCmd};
use raas_types::raas::resp::response::Resp;
use raas_types::raas::service::raas_client::RaasClient;
type Error = Box<dyn std::error::Error + Send + Sync>;
type BotContext<'a> = poise::Context<'a, Context, Error>;
#[poise::command(slash_command, help_text_fn = "roll_help")]
async fn roll(
ctx: BotContext<'_>,
#[description = "Dice String eg 1d20"] roll: String,
) -> Result<(), Error> {
let result = solve_dice_expression(&roll, None)?;
let mut msg = MessageBuilder::new();
msg.push("Result of: ");
msg.push_mono_line_safe(roll);
msg.push_bold_line_safe(result.to_string());
ctx.reply(msg.build()).await?;
Ok(())
}
fn roll_help() -> String {
"Example Usage:\n`/roll d20+2`".to_string()
}
#[poise::command(slash_command, help_text_fn = "real_roll_help")]
async fn real_roll(ctx: BotContext<'_>) -> Result<(), Error> {
let addr = ctx.data().config.raas_url.clone();
let mut client = RaasClient::connect(addr).await?;
let rolls = 3;
let roll_request = raas_types::raas::cmd::Request {
timestamp: Utc::now().timestamp() as u64,
cmd: Some(raas_types::raas::cmd::request::Cmd::RollCmd(RollCmd {
cmd: Some(raas_types::raas::bot::roll::roll_cmd::Cmd::Roll(Roll {
rotations: rolls,
})),
})),
};
debug!("Request: {:?}", roll_request);
let grpc_request = tonic::Request::new(roll_request);
ctx.reply("Sending roll request, please hang on!").await?;
let response = client.send_request(grpc_request).await?;
let raas_response = response.into_inner();
debug!(
"Got RaaS: {} @ {}",
raas_response.id, raas_response.timestamp
);
match raas_response.resp.unwrap() {
Resp::RollResp(roll_resp) => {
if let roll_response::Response::RollImage(img) = roll_resp.response.unwrap() {
ctx.send(
CreateReply::default()
.content("Your roll:")
.attachment(CreateAttachment::bytes(img.img, "roll.jpg"))
.reply(true),
)
.await?;
}
}
Resp::Error(err) => {
ctx.reply(format!("Roll Bot encountered an error: `{}`", err.msg))
.await?;
}
}
Ok(())
}
fn real_roll_help() -> String {
"Roll a real D20!".to_string()
}
pub async fn start_bot(config: BotConfig) {
info!("Starting bot!");
let intents = poise::serenity_prelude::GatewayIntents::non_privileged();
let token = config.bot_token.clone();
let framework = poise::Framework::builder()
.options(poise::FrameworkOptions {
commands: vec![roll(), real_roll()],
..Default::default()
})
.setup(|ctx, _ready, framework| {
Box::pin(async move {
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
Ok(Context { config })
})
})
.build();
let client = poise::serenity_prelude::ClientBuilder::new(token, intents)
.framework(framework)
.await;
client.unwrap().start().await.unwrap();
}

25
src/config.rs 100644
View File

@ -0,0 +1,25 @@
use config::{Config, File};
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
pub struct Args {
pub config_path: PathBuf,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BotConfig {
pub bot_token: String,
pub raas_url: String,
}
impl BotConfig {
pub fn new(config_path: &Path) -> Result<Self, config::ConfigError> {
let cfg = Config::builder()
.add_source(File::from(config_path))
.build()?;
cfg.try_deserialize()
}
}

6
src/context.rs 100644
View File

@ -0,0 +1,6 @@
use crate::config::BotConfig;
#[derive(Clone)]
pub struct Context {
pub config: BotConfig,
}

26
src/main.rs 100644
View File

@ -0,0 +1,26 @@
use crate::bot::start_bot;
use crate::config::{Args, BotConfig};
use log::{debug, error};
use structopt::StructOpt;
mod bot;
mod config;
mod context;
#[tokio::main]
async fn main() {
env_logger::init();
let args = Args::from_args();
debug!("Loading config at '{:?}'", args.config_path);
let config = match BotConfig::new(&args.config_path) {
Ok(c) => c,
Err(err) => {
error!("Error loading config: {}", err);
return;
}
};
start_bot(config).await;
}