This commit is contained in:
nora 2022-06-19 13:36:06 +02:00
parent 5de20c475c
commit d6e6ab9699
3 changed files with 365 additions and 97 deletions

View file

@ -18,91 +18,136 @@ use crate::{
}; };
fn slash_commands(commands: &mut CreateApplicationCommands) -> &mut CreateApplicationCommands { fn slash_commands(commands: &mut CreateApplicationCommands) -> &mut CreateApplicationCommands {
commands.create_application_command(|command| { commands
command .create_application_command(|command| {
.name("lawsuit") command
.description("Einen Gerichtsprozess starten") .name("lawsuit")
.create_option(|option| { .description("Einen Gerichtsprozess starten")
option .create_option(|option| {
.name("create") option
.description("Einen neuen Gerichtsprozess anfangen") .name("create")
.kind(ApplicationCommandOptionType::SubCommand) .description("Einen neuen Gerichtsprozess anfangen")
.create_sub_option(|option| { .kind(ApplicationCommandOptionType::SubCommand)
option .create_sub_option(|option| {
.name("plaintiff") option
.description("Der Kläger") .name("plaintiff")
.kind(ApplicationCommandOptionType::User) .description("Der Kläger")
.required(true) .kind(ApplicationCommandOptionType::User)
}) .required(true)
.create_sub_option(|option| { })
option .create_sub_option(|option| {
.name("accused") option
.description("Der Angeklagte") .name("accused")
.kind(ApplicationCommandOptionType::User) .description("Der Angeklagte")
.required(true) .kind(ApplicationCommandOptionType::User)
}) .required(true)
.create_sub_option(|option| { })
option .create_sub_option(|option| {
.name("judge") option
.description("Der Richter") .name("judge")
.kind(ApplicationCommandOptionType::User) .description("Der Richter")
.required(true) .kind(ApplicationCommandOptionType::User)
}) .required(true)
.create_sub_option(|option| { })
option .create_sub_option(|option| {
.name("reason") option
.description("Der Grund für die Klage") .name("reason")
.kind(ApplicationCommandOptionType::String) .description("Der Grund für die Klage")
.required(true) .kind(ApplicationCommandOptionType::String)
}) .required(true)
.create_sub_option(|option| { })
option .create_sub_option(|option| {
.name("plaintiff_lawyer") option
.description("Der Anwalt des Klägers") .name("plaintiff_lawyer")
.kind(ApplicationCommandOptionType::User) .description("Der Anwalt des Klägers")
.required(false) .kind(ApplicationCommandOptionType::User)
}) .required(false)
.create_sub_option(|option| { })
option .create_sub_option(|option| {
.name("accused_lawyer") option
.description("Der Anwalt des Angeklagten") .name("accused_lawyer")
.kind(ApplicationCommandOptionType::User) .description("Der Anwalt des Angeklagten")
.required(false) .kind(ApplicationCommandOptionType::User)
}) .required(false)
}) })
.create_option(|option| { })
option .create_option(|option| {
.name("set_category") option
.description("Die Gerichtskategorie setzen") .name("set_category")
.kind(ApplicationCommandOptionType::SubCommand) .description("Die Gerichtskategorie setzen")
.create_sub_option(|option| { .kind(ApplicationCommandOptionType::SubCommand)
option .create_sub_option(|option| {
.name("category") option
.description("Die Kategorie") .name("category")
.kind(ApplicationCommandOptionType::Channel) .description("Die Kategorie")
.required(true) .kind(ApplicationCommandOptionType::Channel)
}) .required(true)
}) })
.create_option(|option| { })
option .create_option(|option| {
.name("close") option
.description("Den Prozess abschliessen") .name("close")
.kind(ApplicationCommandOptionType::SubCommand) .description("Den Prozess abschliessen")
.create_sub_option(|option| { .kind(ApplicationCommandOptionType::SubCommand)
option .create_sub_option(|option| {
.name("verdict") option
.description("Das Urteil") .name("verdict")
.kind(ApplicationCommandOptionType::String) .description("Das Urteil")
.required(true) .kind(ApplicationCommandOptionType::String)
}) .required(true)
}) })
.create_option(|option| { })
option .create_option(|option| {
.name("clear") option
.description("Alle Rechtsprozessdaten löschen") .name("clear")
.kind(ApplicationCommandOptionType::SubCommand) .description("Alle Rechtsprozessdaten löschen")
}) .kind(ApplicationCommandOptionType::SubCommand)
}) })
})
.create_application_command(|command| {
command
.name("prison")
.description("Leute im Gefängnis einsperren")
.create_option(|option| {
option
.name("arrest")
.description("Jemanden einsperren")
.kind(ApplicationCommandOptionType::SubCommand)
.create_sub_option(|option| {
option
.name("user")
.description("Die Person zum einsperren")
.kind(ApplicationCommandOptionType::User)
.required(true)
})
})
.create_option(|option| {
option
.name("release")
.description("Jemanden freilassen")
.kind(ApplicationCommandOptionType::SubCommand)
.create_sub_option(|option| {
option
.name("user")
.description("Die Person zum freilassen")
.kind(ApplicationCommandOptionType::User)
.required(true)
})
})
.create_option(|option| {
option
.name("set_role")
.description("Die Rolle für Gefangene setzen")
.kind(ApplicationCommandOptionType::SubCommand)
.create_sub_option(|option| {
option
.name("role")
.description("Die Rolle")
.kind(ApplicationCommandOptionType::Role)
.required(true)
})
})
})
} }
pub struct Handler { pub struct Handler {
@ -119,6 +164,12 @@ pub enum Response {
#[async_trait] #[async_trait]
impl EventHandler for Handler { impl EventHandler for Handler {
async fn guild_member_addition(&self, ctx: Context, new_member: Member) {
if let Err(err) = self.handle_guild_member_join(ctx, new_member).await {
error!(?err, "An error occurred in guild_member_addition handler");
}
}
async fn ready(&self, ctx: Context, ready: Ready) { async fn ready(&self, ctx: Context, ready: Ready) {
info!(name = %ready.user.name, "Bot is connected!"); info!(name = %ready.user.name, "Bot is connected!");
@ -146,7 +197,7 @@ impl EventHandler for Handler {
async fn interaction_create(&self, ctx: Context, interaction: Interaction) { async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
if let Interaction::ApplicationCommand(command) = interaction { if let Interaction::ApplicationCommand(command) = interaction {
if let Err(err) = self.handle_interaction(ctx, command).await { if let Err(err) = self.handle_interaction(ctx, command).await {
error!(?err, "An error occurred"); error!(?err, "An error occurred in interaction_create handler");
} }
} }
} }
@ -161,6 +212,7 @@ impl Handler {
let response = match command.data.name.as_str() { let response = match command.data.name.as_str() {
"lawsuit" => lawsuit_command_handler(&command, &ctx, &self.mongo).await, "lawsuit" => lawsuit_command_handler(&command, &ctx, &self.mongo).await,
"prison" => prison_command_handler(&command, &ctx, &self.mongo).await,
_ => Ok(Response::EphemeralStr("not implemented :(")), _ => Ok(Response::EphemeralStr("not implemented :(")),
}; };
@ -178,6 +230,36 @@ impl Handler {
} }
} }
async fn handle_guild_member_join(
&self,
ctx: Context,
mut member: Member,
) -> color_eyre::Result<()> {
let guild_id = member.guild_id;
let user_id = member.user.id;
let state = self.mongo.find_or_insert_state(guild_id.into()).await?;
debug!(member = ?member.user.id, "New member joined");
if let Some(role_id) = state.prison_role {
if self
.mongo
.find_prison_entry(guild_id.into(), user_id.into())
.await?
.is_some()
{
info!("New member was in prison, giving them the prison role");
member
.add_role(&ctx.http, role_id)
.await
.wrap_err("add role to member in prison")?;
}
}
Ok(())
}
async fn send_response( async fn send_response(
&self, &self,
ctx: Context, ctx: Context,
@ -369,6 +451,107 @@ async fn lawsuit_command_handler(
} }
} }
async fn prison_command_handler(
command: &ApplicationCommandInteraction,
ctx: &Context,
mongo_client: &Mongo,
) -> color_eyre::Result<Response> {
let options = &command.data.options;
let subcommand = options.get(0).wrap_err("needs subcommand")?;
let options = &subcommand.options;
let guild_id = command.guild_id.wrap_err("guild_id not found")?;
let member = command
.member
.as_ref()
.wrap_err("command must be used my member")?;
let permissions = member.permissions.wrap_err("must be in interaction")?;
match subcommand.name.as_str() {
"set_role" => {
if !permissions.contains(Permissions::MANAGE_GUILD) {
return Ok(Response::NoPermissions);
}
let role = RoleOption::get(options.get(0))?;
mongo_client
.set_prison_role(guild_id.into(), role.id.into())
.await?;
Ok(Response::EphemeralStr("isch gsetzt"))
}
"arrest" => {
if !permissions.contains(Permissions::MANAGE_GUILD) {
return Ok(Response::NoPermissions);
}
let (user, _) = UserOption::get(options.get(0))?;
let state = mongo_client.find_or_insert_state(guild_id.into()).await?;
let role = state.prison_role;
let role = match role {
Some(role) => role,
None => {
return Ok(Response::EphemeralStr(
"du mosch zerst e rolle setze mit /prison set_role",
))
}
};
mongo_client
.add_to_prison(guild_id.into(), user.id.into())
.await?;
guild_id
.member(&ctx.http, user.id)
.await
.wrap_err("fetching guild member")?
.add_role(&ctx.http, role)
.await
.wrap_err("add guild member role")?;
Ok(Response::EphemeralStr("hani igsperrt"))
}
"release" => {
if !permissions.contains(Permissions::MANAGE_GUILD) {
return Ok(Response::NoPermissions);
}
let (user, _) = UserOption::get(options.get(0))?;
let state = mongo_client.find_or_insert_state(guild_id.into()).await?;
let role = state.prison_role;
let role = match role {
Some(role) => role,
None => {
return Ok(Response::EphemeralStr(
"du mosch zerst e rolle setze mit /prison set_role",
))
}
};
mongo_client
.remove_from_prison(guild_id.into(), user.id.into())
.await?;
guild_id
.member(&ctx.http, user.id)
.await
.wrap_err("fetching guild member")?
.remove_role(&ctx.http, role)
.await
.wrap_err("remove guild member role")?;
Ok(Response::EphemeralStr("d'freiheit wartet"))
}
_ => Err(eyre!("Unknown subcommand")),
}
}
#[nougat::gat] #[nougat::gat]
trait GetOption { trait GetOption {
type Get<'a>; type Get<'a>;
@ -452,3 +635,20 @@ impl GetOption for ChannelOption {
} }
} }
} }
struct RoleOption;
#[nougat::gat]
impl GetOption for RoleOption {
type Get<'a> = &'a Role;
fn extract(
command: &ApplicationCommandInteractionDataOptionValue,
) -> crate::Result<Self::Get<'_>> {
if let ApplicationCommandInteractionDataOptionValue::Role(role) = command {
Ok(role)
} else {
Err(eyre!("Expected string!"))
}
}
}

View file

@ -57,6 +57,7 @@ async fn main() -> Result<()> {
set_global_commands, set_global_commands,
mongo, mongo,
}) })
.intents(GatewayIntents::GUILD_MEMBERS)
.await .await
.wrap_err("failed to create discord client")?; .wrap_err("failed to create discord client")?;

View file

@ -8,7 +8,7 @@ use color_eyre::Result;
use mongodb::{ use mongodb::{
bson, bson,
bson::{doc, Bson, Uuid}, bson::{doc, Bson, Uuid},
options::{ClientOptions, Credential}, options::{ClientOptions, Credential, UpdateOptions},
Client, Collection, Database, Client, Collection, Database,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -92,6 +92,7 @@ pub struct State {
pub lawsuits: Vec<Lawsuit>, pub lawsuits: Vec<Lawsuit>,
pub court_category: Option<SnowflakeId>, pub court_category: Option<SnowflakeId>,
pub court_rooms: Vec<CourtRoom>, pub court_rooms: Vec<CourtRoom>,
pub prison_role: Option<SnowflakeId>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -101,6 +102,12 @@ pub struct CourtRoom {
pub role_id: SnowflakeId, pub role_id: SnowflakeId,
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrisonEntry {
pub guild_id: SnowflakeId,
pub user_id: SnowflakeId,
}
#[derive(Clone)] #[derive(Clone)]
pub struct Mongo { pub struct Mongo {
db: Database, db: Database,
@ -154,6 +161,7 @@ impl Mongo {
lawsuits: vec![], lawsuits: vec![],
court_category: None, court_category: None,
court_rooms: vec![], court_rooms: vec![],
prison_role: None,
}; };
let coll = self.db.collection::<State>("state"); let coll = self.db.collection::<State>("state");
@ -171,8 +179,8 @@ impl Mongo {
let _ = self.find_or_insert_state(guild_id).await?; let _ = self.find_or_insert_state(guild_id).await?;
let coll = self.state_coll(); let coll = self.state_coll();
coll.update_one( coll.update_one(
doc! {"guild_id": &guild_id }, doc! { "guild_id": &guild_id },
doc! {"$set": { "court_category": category }}, doc! { "$set": { "court_category": category } },
None, None,
) )
.await .await
@ -180,6 +188,23 @@ impl Mongo {
Ok(()) Ok(())
} }
pub async fn set_prison_role(
&self,
guild_id: SnowflakeId,
prison_role: SnowflakeId,
) -> Result<()> {
let _ = self.find_or_insert_state(guild_id).await?;
let coll = self.state_coll();
coll.update_one(
doc! { "guild_id": &guild_id },
doc! { "$set": { "prison_role": prison_role } },
None,
)
.await
.wrap_err("update prison role")?;
Ok(())
}
pub async fn add_court_room(&self, guild_id: SnowflakeId, room: &CourtRoom) -> Result<()> { pub async fn add_court_room(&self, guild_id: SnowflakeId, room: &CourtRoom) -> Result<()> {
let _ = self.find_or_insert_state(guild_id).await?; let _ = self.find_or_insert_state(guild_id).await?;
let coll = self.state_coll(); let coll = self.state_coll();
@ -246,22 +271,64 @@ impl Mongo {
Ok(()) Ok(())
} }
pub async fn delete_guild( pub async fn delete_guild(&self, guild_id: SnowflakeId) -> Result<()> {
&self,
guild_id: SnowflakeId,
) -> Result<()> {
let coll = self.state_coll(); let coll = self.state_coll();
coll.delete_one( coll.delete_one(doc! { "guild_id": &guild_id }, None)
doc! { "guild_id": &guild_id }, .await
None, .wrap_err("delete guild")?;
Ok(())
}
pub async fn add_to_prison(&self, guild_id: SnowflakeId, user_id: SnowflakeId) -> Result<()> {
let coll = self.prison_coll();
coll.update_one(
doc! { "guild_id": guild_id, "user_id": user_id },
doc! {
"$setOnInsert": {
"guild_id": guild_id, "user_id": user_id,
}
},
UpdateOptions::builder().upsert(true).build(),
) )
.await .await
.wrap_err("delete guild")?; .wrap_err("add to prison collection")?;
Ok(()) Ok(())
} }
pub async fn remove_from_prison(
&self,
guild_id: SnowflakeId,
user_id: SnowflakeId,
) -> Result<()> {
let coll = self.prison_coll();
coll.delete_one(doc! { "guild_id": guild_id, "user_id": user_id }, None)
.await
.wrap_err("remove from prison")?;
Ok(())
}
pub async fn find_prison_entry(
&self,
guild_id: SnowflakeId,
user_id: SnowflakeId,
) -> Result<Option<PrisonEntry>> {
let coll = self.prison_coll();
coll.find_one(doc! { "guild_id": guild_id, "user_id": user_id }, None)
.await
.wrap_err("remove from prison")
}
fn state_coll(&self) -> Collection<State> { fn state_coll(&self) -> Collection<State> {
self.db.collection("state") self.db.collection("state")
} }
fn prison_coll(&self) -> Collection<PrisonEntry> {
self.db.collection("prison")
}
} }