feat: working example!!!

This commit is contained in:
Jef Roosens 2023-05-12 13:38:55 +02:00
parent 8ce74e3486
commit bfd438cf71
Signed by: Jef Roosens
GPG key ID: B75D4F293C7052DB
5 changed files with 741 additions and 55 deletions

View file

@ -1,5 +1,5 @@
[package]
name = "bot"
name = "affy"
version = "0.1.0"
edition = "2021"
@ -10,3 +10,4 @@ affluences-api = { path = "../affluences-api" }
tokio = { version = "1.28.1", features = ["full"] }
chrono = "*"
uuid = "*"
poise = "0.5.5"

77
bot/src/commands.rs Normal file
View file

@ -0,0 +1,77 @@
use crate::{Context, Error};
/// Show this help menu
#[poise::command(prefix_command, track_edits, slash_command)]
pub async fn help(
ctx: Context<'_>,
#[description = "Specific command to show help about"]
#[autocomplete = "poise::builtins::autocomplete_command"]
command: Option<String>,
) -> Result<(), Error> {
poise::builtins::help(
ctx,
command.as_deref(),
poise::builtins::HelpConfiguration {
extra_text_at_bottom: "This is an example bot made to showcase features of my custom Discord bot framework",
..Default::default()
},
)
.await?;
Ok(())
}
/// Vote for something
///
/// Enter `~vote pumpkin` to vote for pumpkins
#[poise::command(prefix_command, slash_command)]
pub async fn vote(
ctx: Context<'_>,
#[description = "What to vote for"] choice: String,
) -> Result<(), Error> {
// Lock the Mutex in a block {} so the Mutex isn't locked across an await point
let num_votes = {
let mut hash_map = ctx.data().votes.lock().unwrap();
let num_votes = hash_map.entry(choice.clone()).or_default();
*num_votes += 1;
*num_votes
};
let response = format!("Successfully voted for {choice}. {choice} now has {num_votes} votes!");
ctx.say(response).await?;
Ok(())
}
/// Retrieve number of votes
///
/// Retrieve the number of votes either in general, or for a specific choice:
/// ```
/// ~getvotes
/// ~getvotes pumpkin
/// ```
#[poise::command(prefix_command, track_edits, aliases("votes"), slash_command)]
pub async fn getvotes(
ctx: Context<'_>,
#[description = "Choice to retrieve votes for"] choice: Option<String>,
) -> Result<(), Error> {
if let Some(choice) = choice {
let num_votes = *ctx.data().votes.lock().unwrap().get(&choice).unwrap_or(&0);
let response = match num_votes {
0 => format!("Nobody has voted for {} yet", choice),
_ => format!("{} people have voted for {}", num_votes, choice),
};
ctx.say(response).await?;
} else {
let mut response = String::new();
for (choice, num_votes) in ctx.data().votes.lock().unwrap().iter() {
response += &format!("{}: {} votes", choice, num_votes);
}
if response.is_empty() {
response += "Nobody has voted for anything yet :(";
}
ctx.say(response).await?;
};
Ok(())
}

View file

@ -1,38 +1,103 @@
use affluences_api::{AffluencesClient, Reservation};
use chrono::NaiveDate;
use uuid::{uuid, Uuid};
mod commands;
use poise::serenity_prelude as serenity;
use std::{collections::HashMap, env::var, sync::Mutex, time::Duration};
// Types used by all command functions
type Error = Box<dyn std::error::Error + Send + Sync>;
type Context<'a> = poise::Context<'a, Data, Error>;
// Custom user data passed to all command functions
pub struct Data {
votes: Mutex<HashMap<String, u32>>,
}
async fn on_error(error: poise::FrameworkError<'_, Data, Error>) {
// This is our custom error handler
// They are many errors that can occur, so we only handle the ones we want to customize
// and forward the rest to the default handler
match error {
poise::FrameworkError::Setup { error, .. } => panic!("Failed to start bot: {:?}", error),
poise::FrameworkError::Command { error, ctx } => {
println!("Error in command `{}`: {:?}", ctx.command().name, error,);
}
error => {
if let Err(e) = poise::builtins::on_error(error).await {
println!("Error while handling error: {}", e)
}
}
}
}
#[tokio::main]
async fn main() {
let site = "faculty-library-sciences";
let id: Uuid = uuid!("4737e57a-ee05-4f7b-901a-7bb541eeb297");
let mut client = AffluencesClient::new();
// FrameworkOptions contains all of poise's configuration option in one struct
// Every option can be omitted to use its default value
let options = poise::FrameworkOptions {
commands: vec![commands::help(), commands::vote(), commands::getvotes()],
prefix_options: poise::PrefixFrameworkOptions {
prefix: Some("~".into()),
edit_tracker: Some(poise::EditTracker::for_timespan(Duration::from_secs(3600))),
additional_prefixes: vec![
poise::Prefix::Literal("hey bot"),
poise::Prefix::Literal("hey bot,"),
],
..Default::default()
},
/// The global error handler for all error cases that may occur
on_error: |error| Box::pin(on_error(error)),
/// This code is run before every command
pre_command: |ctx| {
Box::pin(async move {
println!("Executing command {}...", ctx.command().qualified_name);
})
},
/// This code is run after a command if it was successful (returned Ok)
post_command: |ctx| {
Box::pin(async move {
println!("Executed command {}!", ctx.command().qualified_name);
})
},
/// Every command invocation must pass this check to continue execution
command_check: Some(|ctx| {
Box::pin(async move {
if ctx.author().id == 123456789 {
return Ok(false);
}
Ok(true)
})
}),
/// Enforce command checks even for owners (enforced by default)
/// Set to true to bypass checks, which is useful for testing
skip_checks_for_owners: false,
event_handler: |_ctx, event, _framework, _data| {
Box::pin(async move {
println!("Got an event in event handler: {:?}", event.name());
Ok(())
})
},
..Default::default()
};
// let res = client.site_data(site).await.unwrap();
let date = NaiveDate::from_ymd_opt(2023, 5, 12).unwrap();
let res = client
.available(id, date, 1)
poise::Framework::builder()
.token(
var("DISCORD_TOKEN")
.expect("Missing `DISCORD_TOKEN` env var, see README for more information."),
)
.setup(move |ctx, _ready, framework| {
Box::pin(async move {
println!("Logged in as {}", _ready.user.name);
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
Ok(Data {
votes: Mutex::new(HashMap::new()),
})
})
})
.options(options)
.intents(
serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::MESSAGE_CONTENT,
)
.run()
.await
.unwrap();
let first = res.first().unwrap();
let hour = first.hours[0];
let reservation = Reservation{
auth_type: None,
email: String::from("mark.verbeken@ugent.be"),
date: date,
start_time: hour.hour,
end_time: hour.hour + chrono::Duration::minutes(30),
note: String::from("hello"),
user_firstname: String::from("Mark"),
user_lastname: String::from("Verbeken"),
user_phone: None,
person_count: 1
};
println!("{:?}", first);
println!("{:?}", hour);
println!("{:?}", reservation);
// let res2 = client.make_reservation(first.resource_id, &reservation).await;
// println!("{:?}", res2);
}