use crate::{logging::log_error, State}; use azalea::{pathfinder::BlockPosGoal, prelude::*, BlockPos}; use azalea_protocol::packets::game::{ self, serverbound_interact_packet::InteractionHand, ServerboundGamePacket, }; use chrono::{Local, TimeZone}; use strum::IntoEnumIterator; use strum_macros::EnumIter; #[derive(Debug, Clone, PartialEq, PartialOrd, EnumIter)] pub enum Command { Help, BotStatus, Whitelist, WhitelistAdd, WhitelistRemove, LastLocation, LastOnline, FollowPlayer, StopFollowPlayer, Goto, StopGoto, Say, Slot, UseItem, Look, Sneak, Unsneak, ToggleBotStatusMessages, ToggleAlertMessages, Unknown, } pub async fn process_command( command: &String, executor: &String, client: &mut Client, state: &mut State, ) -> String { let mut segments: Vec = command .split(" ") .map(|segment| segment.to_string()) .collect(); if segments.len() <= 0 { return "Hmm... I was unable to parse your command!".to_string(); }; let mut command = Command::Unknown; match segments[0].to_lowercase().as_str() { "help" => command = Command::Help, "bot_status" => command = Command::BotStatus, "whitelist" => command = Command::Whitelist, "whitelist_add" => command = Command::WhitelistAdd, "whitelist_remove" => command = Command::WhitelistRemove, "last_location" => command = Command::LastLocation, "last_online" => command = Command::LastOnline, "follow_player" => command = Command::FollowPlayer, "stop_follow_player" => command = Command::StopFollowPlayer, "goto" => command = Command::Goto, "stop_goto" => command = Command::StopGoto, "say" => command = Command::Say, "slot" => command = Command::Slot, "use_item" => command = Command::UseItem, "look" => command = Command::Look, "sneak" => command = Command::Sneak, "unsneak" => command = Command::Unsneak, "toggle_alert_messages" => command = Command::ToggleAlertMessages, "toggle_bot_status_messages" => command = Command::ToggleBotStatusMessages, _ => (), }; segments.remove(0); let return_value = match command { Command::Help => { let mut commands = Vec::new(); for command in Command::iter() { if command != Command::Unknown { commands.push(format!("{:?}", command)); } } return "Commands: ".to_owned() + &commands.join(", "); } Command::BotStatus => { let bot_status = state.bot_status.lock().unwrap().to_owned(); let metadata = client.metadata(); return format!( "Health: {:.1}/20, Food: {}/20, Saturation: {:.1}/20, Score: {}, Air Supply: {}", bot_status.health, bot_status.food, bot_status.saturation, metadata.score, metadata.air_supply ); } Command::Whitelist => { let whitelist = state.whitelist.lock().unwrap().join(", "); if whitelist.is_empty() { return "There are no whitelisted players...".to_string(); } else { return format!("Whitelisted players: {}", whitelist); } } Command::WhitelistAdd => { if segments.len() < 1 { return "Please tell me the name of the player!".to_string(); } let mut whitelist = state.whitelist.lock().unwrap().to_vec(); if whitelist.contains(&segments[0]) { return format!("{} is already whitelisted!", segments[0]); } whitelist.push(segments[0].to_owned()); *state.whitelist.lock().unwrap() = whitelist; return format!( "{} has been successfully added to the whitelist!", segments[0] ); } Command::WhitelistRemove => { if segments.len() < 1 { return "Please tell me the name of the player!".to_string(); } let mut whitelist = state.whitelist.lock().unwrap().to_vec(); if !whitelist.contains(&segments[0]) { return format!("{} is not whitelisted!", segments[0]); } whitelist.remove( whitelist .iter() .position(|item| *item == segments[0]) .unwrap(), ); *state.whitelist.lock().unwrap() = whitelist; return format!( "{} has been successfully removed from the whitelist!", segments[0] ); } Command::LastLocation => { if segments.len() < 1 { return "Please tell me the name of the player!".to_string(); } for (player, position_time_data) in state.player_locations.lock().unwrap().iter() { if player.username == segments[0] || player.uuid.to_string() == segments[0] { return format!( "{} was last seen at {}, {}, {} ({})", segments[0], position_time_data.position[0], position_time_data.position[1], position_time_data.position[2], Local .timestamp_opt(position_time_data.time as i64, 0) .unwrap() .format("%Y/%m/%d %H:%M:%S") ); } } format!("I haven't seen {} move anywhere near me...", segments[0]) } Command::LastOnline => { if segments.len() < 1 { return "Please tell me the name of the player!".to_string(); } for (player, player_time_data) in state.player_timestamps.lock().unwrap().iter() { if player == &segments[0] { return format!( "{} - last join: {}, last chat message: {}, last leave: {}", segments[0], if player_time_data.join_time != 0 { Local .timestamp_opt(player_time_data.join_time as i64, 0) .unwrap() .format("%Y/%m/%d %H:%M:%S") .to_string() } else { "never".to_string() }, if player_time_data.chat_message_time != 0 { Local .timestamp_opt(player_time_data.chat_message_time as i64, 0) .unwrap() .format("%Y/%m/%d %H:%M:%S") .to_string() } else { "never".to_string() }, if player_time_data.leave_time != 0 { Local .timestamp_opt(player_time_data.leave_time as i64, 0) .unwrap() .format("%Y/%m/%d %H:%M:%S") .to_string() } else { "never".to_string() }, ); } } format!("I haven't seen {} online yet...", segments[0]) } Command::FollowPlayer => { if segments.len() < 1 { return "Please tell me the name of the player!".to_string(); }; let mut found = true; for (player, _position_time_data) in state.player_locations.lock().unwrap().iter() { if player.username == segments[0] || player.uuid.to_string() == segments[0] { found = true; *state.followed_player.lock().unwrap() = Some(player.to_owned()); } } if found { return format!("I am now following {}...", segments[0]); } else { return format!("I was unable to find {}...", segments[0]); } } Command::StopFollowPlayer => { *state.followed_player.lock().unwrap() = None; let current_position = client.entity().pos().to_owned(); client.goto(BlockPosGoal { pos: BlockPos { x: current_position.x.round() as i32, y: current_position.y.round() as i32, z: current_position.z.round() as i32, }, }); "I am no longer following anyone!".to_string() } Command::Goto => { if segments.len() < 3 { return "Please give me X, Y, and Z coordinates to go to!".to_string(); } let mut coordinates: Vec = Vec::new(); for segment in segments { coordinates.push(match segment.parse() { Ok(number) => number, Err(error) => return format!("Unable to parse coordinates: {}", error), }) } log_error( client .send_command_packet(&format!( "msg {} I am now finding a path to {} {} {}...", executor, coordinates[0], coordinates[1], coordinates[2] )) .await, ); client.goto(BlockPosGoal { pos: BlockPos { x: coordinates[0], y: coordinates[1], z: coordinates[2], }, }); format!( "I have found the path to {} {} {}!", coordinates[0], coordinates[1], coordinates[2] ) } Command::StopGoto => { let current_position = client.entity().pos().to_owned(); client.goto(BlockPosGoal { pos: BlockPos { x: current_position.x.round() as i32, y: current_position.y.round() as i32, z: current_position.z.round() as i32, }, }); "I am no longer going anywhere!".to_string() } Command::Say => { if segments.len() < 1 { return "Please give me something to say!".to_string(); } log_error(client.chat(segments.join(" ").as_str()).await); "Successfully sent message!".to_string() } Command::Slot => { if segments.len() < 1 { return "Please give me a slot to set!".to_string(); } log_error( client .write_packet(ServerboundGamePacket::SetCarriedItem( game::serverbound_set_carried_item_packet::ServerboundSetCarriedItemPacket { slot: match segments[0].parse() { Ok(number) => number, Err(error) => return format!("Unable to parse slot: {}", error), }, }, )) .await ); "I have successfully switched slots!".to_string() } Command::UseItem => { log_error( client .write_packet(ServerboundGamePacket::UseItem( game::serverbound_use_item_packet::ServerboundUseItemPacket { hand: InteractionHand::MainHand, sequence: 0, }, )) .await, ); "I have successfully used the item!".to_string() } Command::Look => { if segments.len() < 2 { return "Please give me rotation vectors to look at!".to_string(); } let mut rotation: Vec = Vec::new(); for segment in segments { rotation.push(match segment.parse() { Ok(number) => number, Err(error) => return format!("Unable to parse rotation: {}", error), }) } client.set_rotation(rotation[0], rotation[1]); format!("I am now looking at {} {}!", rotation[0], rotation[1]) } Command::Sneak => { let entity_id = client.entity_id.read().to_owned(); log_error( client .write_packet(ServerboundGamePacket::PlayerCommand( game::serverbound_player_command_packet::ServerboundPlayerCommandPacket { id: entity_id, action: game::serverbound_player_command_packet::Action::PressShiftKey, data: 0, }, )) .await, ); return "I am now sneaking!".to_string(); } Command::Unsneak => { let entity_id = client.entity_id.read().to_owned(); log_error( client .write_packet(ServerboundGamePacket::PlayerCommand( game::serverbound_player_command_packet::ServerboundPlayerCommandPacket { id: entity_id, action: game::serverbound_player_command_packet::Action::ReleaseShiftKey, data: 0, }, )) .await, ); return "I am no longer sneaking!".to_string(); } Command::ToggleAlertMessages => { if state.alert_players.lock().unwrap().contains(executor) { let mut players = state.alert_players.lock().unwrap().to_vec(); players.remove( players .iter() .position(|item| *item == executor.to_owned()) .unwrap(), ); *state.alert_players.lock().unwrap() = players; "You will no longer be receiving alert messages!".to_string() } else { let mut players = state.alert_players.lock().unwrap().to_vec(); players.push(executor.to_owned()); *state.alert_players.lock().unwrap() = players; "You will now be receiving alert messages!".to_string() } } Command::ToggleBotStatusMessages => { if state.bot_status_players.lock().unwrap().contains(executor) { let mut players = state.bot_status_players.lock().unwrap().to_vec(); players.remove( players .iter() .position(|item| *item == executor.to_owned()) .unwrap(), ); *state.bot_status_players.lock().unwrap() = players; "You will no longer be receiving bot status messages!".to_string() } else { let mut players = state.bot_status_players.lock().unwrap().to_vec(); players.push(executor.to_owned()); *state.bot_status_players.lock().unwrap() = players; "You will now be receiving bot status messages!".to_string() } } _ => "".to_string(), }; if !return_value.is_empty() { return return_value; } "Sorry, I don't know what you mean...".to_string() }