-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
778 additions
and
546 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
[toolchain] | ||
channel = "nightly" | ||
[toolchain] # Failing to build | ||
channel = "nightly-2024-11-11" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
use std::collections::VecDeque; | ||
|
||
use azalea::{ | ||
app::{App, Plugin, Update}, | ||
chat::{handle_send_chat_event, ChatPacketKind, ChatReceivedEvent, SendChatKindEvent}, | ||
ecs::prelude::*, | ||
TabList, | ||
}; | ||
use ncr::AesKey; | ||
|
||
use crate::{ | ||
commands::{ | ||
handlers::Cooldown, | ||
CommandEvent, | ||
CommandSender, | ||
CommandSource, | ||
Commands, | ||
WhisperEvent, | ||
}, | ||
encryption::{find_encryption, try_encrypt, KEY}, | ||
settings::Settings, | ||
}; | ||
|
||
pub struct MinecraftCommandsPlugin; | ||
|
||
impl Plugin for MinecraftCommandsPlugin { | ||
fn build(&self, app: &mut App) { | ||
app.insert_resource(Cooldown::default()) | ||
.add_event::<CommandEvent>() | ||
.add_event::<WhisperEvent>() | ||
.add_systems( | ||
Update, | ||
( | ||
handle_chat_received_event, | ||
handle_minecraft_whisper_event.before(handle_send_chat_event), | ||
) | ||
.chain(), | ||
); | ||
} | ||
} | ||
|
||
pub fn handle_chat_received_event( | ||
mut events: EventReader<ChatReceivedEvent>, | ||
mut command_events: EventWriter<CommandEvent>, | ||
mut cooldown: ResMut<Cooldown>, | ||
query: Query<&TabList>, | ||
settings: Res<Settings>, | ||
) { | ||
let Ok(tab_list) = query.get_single() else { | ||
return; | ||
}; | ||
|
||
for event in events.read() { | ||
let (username, content) = event.packet.split_sender_and_content(); | ||
let (username, content) = if let Some(username) = username { | ||
(username, content) /* Vanilla Server Format */ | ||
} else if let Some((_whole, username, content)) = regex_captures!( | ||
r"^(?:\[.+\] )?([a-zA-Z_0-9]{1,16}) (?:> )?(?:whispers: |-> )?(.+)$", | ||
&content | ||
) { | ||
(username.to_string(), content.to_string()) | ||
} else { | ||
continue; | ||
}; | ||
|
||
let Some((uuid, _)) = tab_list.iter().find(|(_, i)| i.profile.name == username) else { | ||
continue; | ||
}; | ||
|
||
if !settings.whitelist.is_empty() && !settings.whitelist.contains_key(uuid) { | ||
continue; | ||
} | ||
|
||
let key = AesKey::decode_base64(&settings.encryption.key).unwrap_or_else(|_| KEY.clone()); | ||
let (encryption, content) = find_encryption(&content, &key); | ||
|
||
let mut args = content.split(' ').collect::<VecDeque<_>>(); | ||
let Some(alias) = args.pop_front() else { | ||
continue; /* Command Missing */ | ||
}; | ||
|
||
let Some(command) = Commands::find(&alias.replace(&settings.command_prefix, "")) else { | ||
continue; /* Command Invalid */ | ||
}; | ||
|
||
if cooldown.check(&username, settings.command_cooldown) { | ||
continue; /* Command Cooldown */ | ||
} | ||
|
||
command_events.send(CommandEvent { | ||
entity: event.entity, | ||
args: args.into_iter().map(String::from).collect(), | ||
command, | ||
sender: CommandSender::Minecraft(*uuid), | ||
source: CommandSource::Minecraft(encryption), | ||
}); | ||
} | ||
} | ||
|
||
pub fn handle_minecraft_whisper_event( | ||
mut chat_kind_events: EventWriter<SendChatKindEvent>, | ||
mut whisper_events: EventReader<WhisperEvent>, | ||
query: Query<&TabList>, | ||
settings: Res<Settings>, | ||
) { | ||
let Ok(tab_list) = query.get_single() else { | ||
return; | ||
}; | ||
|
||
if settings.disable_responses { | ||
return; | ||
} | ||
|
||
for mut event in whisper_events.read().cloned() { | ||
#[rustfmt::skip] | ||
let ( | ||
CommandSource::Minecraft(type_encryption), | ||
CommandSender::Minecraft(uuid) | ||
) = (event.source, event.sender) else { | ||
continue; | ||
}; | ||
|
||
let Some(username) = tab_list | ||
.iter() | ||
.find(|(_, info)| info.profile.uuid == uuid) | ||
.map(|(_, info)| info.profile.name.clone()) | ||
else { | ||
continue; /* Player Offline */ | ||
}; | ||
|
||
try_encrypt(&mut event.content, &settings.encryption, type_encryption); | ||
|
||
chat_kind_events.send(SendChatKindEvent { | ||
entity: event.entity, | ||
kind: ChatPacketKind::Command, | ||
content: format!("w {username} {}", event.content), | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use std::{ | ||
collections::HashMap, | ||
time::{Duration, Instant}, | ||
}; | ||
|
||
use azalea::{ecs::prelude::*, prelude::*}; | ||
|
||
pub mod prelude; | ||
|
||
mod discord; | ||
mod minecraft; | ||
|
||
#[derive(Default, Resource)] | ||
pub struct Cooldown(HashMap<String, Instant>); | ||
|
||
impl Cooldown { | ||
fn check(&mut self, sender: &str, duration: Duration) -> bool { | ||
if let Some(instant) = self.0.get(sender) { | ||
if instant.elapsed() < duration { | ||
return true; | ||
} | ||
} else { | ||
self.0.insert(sender.to_owned(), Instant::now()); | ||
} | ||
|
||
false | ||
} | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
pub mod handlers; | ||
pub mod prelude; | ||
|
||
mod pearl; | ||
mod playtime; | ||
mod seen; | ||
mod whitelist; | ||
|
||
use std::collections::VecDeque; | ||
|
||
use azalea::{ecs::prelude::*, prelude::*}; | ||
use serenity::all::{ChannelId, UserId}; | ||
use strum::IntoEnumIterator; | ||
use uuid::Uuid; | ||
|
||
use crate::{commands::prelude::*, encryption::EncryptionType}; | ||
|
||
pub trait Command { | ||
fn aliases(&self) -> Vec<&'static str>; | ||
} | ||
|
||
/// Compile time checked list of commands | ||
#[derive(Clone, Copy, Debug, Eq, PartialEq, EnumIter)] | ||
pub enum Commands { | ||
Pearl(PearlCommandPlugin), | ||
Playtime(PlaytimeCommandPlugin), | ||
Seen(SeenCommandPlugin), | ||
Whitelist(WhitelistCommandPlugin), | ||
} | ||
|
||
impl Commands { | ||
fn find(alias: &str) -> Option<Self> { | ||
Self::iter().find(|cmds| match cmds { | ||
Self::Pearl(cmd) => cmd.aliases().contains(&alias), | ||
Self::Playtime(cmd) => cmd.aliases().contains(&alias), | ||
Self::Seen(cmd) => cmd.aliases().contains(&alias), | ||
Self::Whitelist(cmd) => cmd.aliases().contains(&alias), | ||
}) | ||
} | ||
} | ||
|
||
#[derive(Clone, Copy, Debug)] | ||
pub enum CommandSender { | ||
Discord(UserId), | ||
Minecraft(Uuid), | ||
} | ||
|
||
#[derive(Clone, Copy, Debug)] | ||
pub enum CommandSource { | ||
Discord(ChannelId), | ||
Minecraft(Option<EncryptionType>), | ||
} | ||
|
||
#[derive(Clone, Debug, Event)] | ||
pub struct CommandEvent { | ||
pub entity: Entity, | ||
pub args: VecDeque<String>, | ||
pub command: Commands, | ||
pub sender: CommandSender, | ||
pub source: CommandSource, | ||
} | ||
|
||
#[derive(Clone, Debug, Event)] | ||
pub struct WhisperEvent { | ||
pub entity: Entity, | ||
pub content: String, | ||
pub sender: CommandSender, | ||
pub source: CommandSource, | ||
} |
Oops, something went wrong.