Skip to content

Commit

Permalink
0.6.0 - Whitelist
Browse files Browse the repository at this point in the history
  • Loading branch information
ShayBox committed Nov 7, 2024
1 parent 29b2954 commit ba6df3b
Show file tree
Hide file tree
Showing 17 changed files with 644 additions and 148 deletions.
355 changes: 324 additions & 31 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "shaysbot"
version = "0.5.1"
version = "0.6.0"
authors = ["Shayne Hartford <shaybox@shaybox.com>"]
edition = "2021"
description = "My personal Minecraft bot using Azalea"
Expand Down Expand Up @@ -28,6 +28,7 @@ serde = "1"
serde_with = "3"
serenity = "0.12"
smart-default = "0.7"
structstruck = "0.4"
terminal-link = "0.1"
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
Expand All @@ -53,3 +54,4 @@ pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
# cargo = { level = "warn", priority = -1 }
multiple_crate_versions = "allow"
needless_pass_by_value = "allow"
8 changes: 5 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ extern crate lazy_regex;
#[macro_use]
extern crate serde_with;
#[macro_use]
extern crate structstruck;
#[macro_use]
extern crate tracing;

pub mod ncr;
Expand Down Expand Up @@ -68,10 +70,10 @@ pub async fn start() -> anyhow::Result<()> {
let trapdoors = Trapdoors::load().unwrap_or_default();
let address = settings.server_address.clone();
let token = settings.discord_token.clone();
let account = if settings.online {
Account::microsoft(&settings.username).await?
let account = if settings.online_mode {
Account::microsoft(&settings.account_username).await?
} else {
Account::offline(&settings.username)
Account::offline(&settings.account_username)
};

settings.save()?;
Expand Down
19 changes: 9 additions & 10 deletions src/ncr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl Encryption for EncryptionType {
}

#[must_use]
pub fn find_encryption(ciphertext: &str, key: &AesKey) -> (Option<EncryptionType>, String) {
pub fn find_encryption(content: &str, key: &AesKey) -> (Option<EncryptionType>, String) {
for &encoder in ENCODERS.iter() {
let encryptors = [
EncryptionType::CFB(encoder),
Expand All @@ -99,40 +99,39 @@ pub fn find_encryption(ciphertext: &str, key: &AesKey) -> (Option<EncryptionType
];

for encryptor in encryptors {
if let Ok(plaintext) = encryptor.decrypt(ciphertext, key) {
if let Ok(plaintext) = encryptor.decrypt(content, key) {
if let Ok(trimmed) = trim_header(&plaintext) {
return (Some(encryptor), String::from(trimmed));
}
}
}
}

(None, String::from(ciphertext))
(None, String::from(content))
}

#[must_use]
pub fn try_encrypt(
chat_encryption: &ChatEncryption,
type_encryption: Option<EncryptionType>,
plaintext: String,
content: String,
) -> String {
if chat_encryption.mode == EncryptionMode::Never {
return plaintext;
return content;
}

let key = AesKey::decode_base64(&chat_encryption.key).unwrap_or_else(|_| KEY.clone());
let plaintext = prepend_header(&content);

if let Some(encryption) = type_encryption {
if let Ok(ciphertext) = encryption.encrypt(&prepend_header(&plaintext), &key) {
if let Ok(ciphertext) = encryption.encrypt(&plaintext, &key) {
return ciphertext;
}
} else if chat_encryption.mode == EncryptionMode::Always {
if let Ok(ciphertext) =
Cfb8Encryption(NewBase64rEncoding).encrypt(&prepend_header(&plaintext), &key)
{
if let Ok(ciphertext) = Cfb8Encryption(NewBase64rEncoding).encrypt(&plaintext, &key) {
return ciphertext;
}
}

plaintext
content
}
1 change: 0 additions & 1 deletion src/plugins/auto_eat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ impl Plugin for AutoEatPlugin {
type QueryData<'a> = (Entity, &'a Hunger, &'a Inventory, &'a LookDirection);
type QueryFilter = (With<Player>, With<LocalEntity>);

#[allow(clippy::needless_pass_by_value)]
pub fn handle_auto_eat(
mut query: Query<QueryData, QueryFilter>,
mut packet_events: EventWriter<SendPacketEvent>,
Expand Down
1 change: 0 additions & 1 deletion src/plugins/auto_look.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ impl Plugin for AutoLookPlugin {
}
}

#[allow(clippy::needless_pass_by_value)]
pub fn handle_auto_look(
mut query: Query<Entity, (With<LocalEntity>, With<Player>)>,
entities: EntityFinder<With<Player>>,
Expand Down
1 change: 0 additions & 1 deletion src/plugins/auto_totem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ impl Plugin for AutoTotemPlugin {
type QueryData<'a> = (Entity, &'a Inventory);
type QueryFilter = (With<Player>, With<LocalEntity>);

#[allow(clippy::needless_pass_by_value)]
pub fn handle_auto_totem(
mut query: Query<QueryData, QueryFilter>,
mut container_click_events: EventWriter<ContainerClickEvent>,
Expand Down
40 changes: 27 additions & 13 deletions src/plugins/commands/discord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use azalea::{
use bevy_discord::{bot::events::BMessage, http::DiscordHttpResource, runtime::tokio_runtime};
use serenity::json::json;

use super::{CommandEvent, CommandSource, Registry, WhisperEvent};
use super::{CommandEvent, CommandSender, CommandSource, Registry, WhisperEvent};
use crate::settings::Settings;

pub struct DiscordCommandsPlugin;
Expand All @@ -17,7 +17,6 @@ impl Plugin for DiscordCommandsPlugin {
}
}

#[allow(clippy::needless_pass_by_value)]
pub fn handle_message_event(
mut command_events: EventWriter<CommandEvent>,
mut message_events: EventReader<BMessage>,
Expand All @@ -30,38 +29,53 @@ pub fn handle_message_event(
continue;
};

let http = event.ctx.http.clone();
let message = event.new_message.clone();
let Some((args, command)) = registry.find_command(&message.content, &settings.chat_prefix)
let Some((args, command)) =
registry.find_command(&message.content, &settings.command_prefix)
else {
continue;
};

let Some(sender) = args.front().map(String::to_owned) else {
// Check if whitelist is enabled and if the user is whitelisted.
if !settings.whitelist.is_empty()
&& !settings
.whitelist
.iter()
.filter_map(|(uuid, user_id)| user_id.as_ref().map(|user_id| (*uuid, user_id)))
.any(|(_, user_id)| user_id == &message.author.id.to_string())
{
let http = event.ctx.http.clone();
let prefix = settings.command_prefix.clone();
let user_id = message.author.id.to_string();
tokio_runtime().spawn(async move {
let map = &json!({
"content": "[404] Missing Player Name!"
let content = [
String::from("[404] Your Discord account isn't linked to a Minecraft account."),
format!("Message the bot in-game '{prefix}whitelist link {user_id}' to link."),
]
.join("\n");

let map = json!({
"content": content,
});

if let Err(error) = http.send_message(message.channel_id, Vec::new(), map).await {
if let Err(error) = http.send_message(message.channel_id, vec![], &map).await {
error!("{error}");
};
});

continue;
};
}

command_events.send(CommandEvent {
source: CommandSource::Discord(message.channel_id),
entity,
sender,
command: *command,
args,
command: *command,
source: CommandSource::Discord(message.channel_id),
sender: CommandSender::Discord(message.author.id),
});
}
}

#[allow(clippy::needless_pass_by_value)]
pub fn handle_discord_whisper_event(
mut whisper_events: EventReader<WhisperEvent>,
discord: Res<DiscordHttpResource>,
Expand Down
82 changes: 57 additions & 25 deletions src/plugins/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod discord;
mod pearl;
mod playtime;
mod seen;
mod whitelist;

use std::collections::{HashMap, VecDeque};

Expand All @@ -12,9 +13,10 @@ use azalea::{
chat::{handle_send_chat_event, ChatPacketKind, ChatReceivedEvent, SendChatKindEvent},
ecs::prelude::*,
prelude::*,
TabList,
};
use ncr::AesKey;
use serenity::all::ChannelId;
use serenity::all::{ChannelId, UserId};

use crate::{
ncr::{find_encryption, try_encrypt, EncryptionType, KEY},
Expand All @@ -26,6 +28,13 @@ pub enum Command {
Pearl,
Playtime,
Seen,
Whitelist,
}

#[derive(Clone, Debug)]
pub enum CommandSender {
Discord(UserId),
Minecraft(String),
}

#[derive(Clone, Debug)]
Expand All @@ -34,21 +43,21 @@ pub enum CommandSource {
Minecraft(Option<EncryptionType>),
}

#[derive(Debug, Event)]
#[derive(Clone, Debug, Event)]
pub struct CommandEvent {
pub source: CommandSource,
pub entity: Entity,
pub sender: String,
pub command: Command,
pub args: VecDeque<String>,
pub command: Command,
pub sender: CommandSender,
pub source: CommandSource,
}

#[derive(Debug, Event)]
#[derive(Clone, Debug, Event)]
pub struct WhisperEvent {
pub entity: Entity,
pub source: CommandSource,
pub sender: String,
pub content: String,
pub sender: CommandSender,
pub source: CommandSource,
}

#[derive(Default, Resource)]
Expand Down Expand Up @@ -97,10 +106,10 @@ impl Plugin for CommandsPlugin {
}
}

#[allow(clippy::needless_pass_by_value)]
pub fn handle_chat_received_event(
mut events: EventReader<ChatReceivedEvent>,
mut command_events: EventWriter<CommandEvent>,
query: Query<&TabList>,
registry: Res<Registry>,
settings: Res<Settings>,
) {
Expand All @@ -119,39 +128,62 @@ pub fn handle_chat_received_event(

let key = AesKey::decode_base64(&settings.encryption.key).unwrap_or_else(|_| KEY.clone());
let (encryption, content) = find_encryption(&content, &key);
let Some((args, command)) = registry.find_command(&content, &settings.chat_prefix) else {
let Some((args, command)) = registry.find_command(&content, &settings.command_prefix)
else {
continue;
};

if !settings.whitelist.is_empty() {
let Ok(tab_list) = query.get_single() else {
continue;
};

let Some((uuid, _info)) = tab_list
.iter()
.find(|(_, info)| info.profile.name == sender)
else {
continue; /* Not Online */
};

if !settings.whitelist.contains_key(uuid) {
continue; /* Not Whitelisted */
}
}

command_events.send(CommandEvent {
source: CommandSource::Minecraft(encryption),
entity: event.entity,
sender,
command: *command,
args,
command: *command,
sender: CommandSender::Minecraft(sender),
source: CommandSource::Minecraft(encryption),
});
}
}

#[allow(clippy::needless_pass_by_value)]
pub fn handle_minecraft_whisper_event(
mut chat_kind_events: EventWriter<SendChatKindEvent>,
mut whisper_events: EventReader<WhisperEvent>,
settings: Res<Settings>,
) {
for event in whisper_events.read() {
if let CommandSource::Minecraft(encryption) = event.source {
if settings.quiet {
continue;
}
for event in whisper_events.read().cloned() {
let CommandSender::Minecraft(sender) = event.sender else {
continue;
};

let content = try_encrypt(&settings.encryption, encryption, event.content.clone());
let CommandSource::Minecraft(type_encryption) = event.source else {
continue;
};

chat_kind_events.send(SendChatKindEvent {
entity: event.entity,
kind: ChatPacketKind::Command,
content: format!("w {} {content}", event.sender),
});
if settings.disable_responses {
continue;
}

let content = try_encrypt(&settings.encryption, type_encryption, event.content);

chat_kind_events.send(SendChatKindEvent {
entity: event.entity,
kind: ChatPacketKind::Command,
content: format!("w {sender} {content}"),
});
}
}
Loading

0 comments on commit ba6df3b

Please sign in to comment.