Implement event system
This commit is contained in:
parent
021f8154c8
commit
8a90dfb77b
110
Cargo.lock
generated
110
Cargo.lock
generated
@ -45,6 +45,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
@ -60,6 +66,55 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
version = "0.5.1"
|
||||
@ -317,6 +372,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
@ -537,6 +598,29 @@ dependencies = [
|
||||
"syn 2.0.66",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"humantime",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
@ -727,6 +811,9 @@ checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
name = "group-bot"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"hashbrown 0.14.5",
|
||||
"log",
|
||||
"serde",
|
||||
"tokio",
|
||||
"toml",
|
||||
@ -760,6 +847,11 @@ name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@ -884,6 +976,12 @@ version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.3.1"
|
||||
@ -1027,6 +1125,12 @@ version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
@ -2407,6 +2511,12 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.8.0"
|
||||
|
@ -10,6 +10,9 @@ veloren-client = { git = "https://gitlab.com/veloren/veloren", branch = "master"
|
||||
veloren-common-net = { git = "https://gitlab.com/veloren/veloren", branch = "master" }
|
||||
toml = "0.8.14"
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
env_logger = "0.11.3"
|
||||
log = "0.4.21"
|
||||
hashbrown = { version = "0.14.5", features = ["equivalent"] }
|
||||
|
||||
[patch.crates-io]
|
||||
specs = { git = "https://github.com/amethyst/specs.git", rev = "4e2da1df29ee840baa9b936593c45592b7c9ae27" }
|
||||
|
186
src/bot.rs
186
src/bot.rs
@ -1,7 +1,8 @@
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use std::{collections::VecDeque, sync::Arc, time::Duration};
|
||||
|
||||
use log::{debug, info};
|
||||
use tokio::runtime::Runtime;
|
||||
use veloren_client::{addr::ConnectionArgs, Client, Event};
|
||||
use veloren_client::{addr::ConnectionArgs, Client, Event as VelorenEvent};
|
||||
use veloren_common::{
|
||||
clock::Clock,
|
||||
comp::{invite::InviteKind, ChatType, ControllerInputs},
|
||||
@ -13,11 +14,21 @@ use veloren_common_net::msg::PlayerInfo;
|
||||
|
||||
use crate::Config;
|
||||
|
||||
enum Event {
|
||||
Admin(String),
|
||||
Ban(String),
|
||||
Cheese,
|
||||
Invite(Uid),
|
||||
Kick(Uid),
|
||||
Unban(String),
|
||||
}
|
||||
|
||||
pub struct Bot {
|
||||
client: Client,
|
||||
clock: Clock,
|
||||
admin_list: Vec<Uuid>,
|
||||
ban_list: Vec<Uuid>,
|
||||
events: VecDeque<Event>,
|
||||
}
|
||||
|
||||
impl Bot {
|
||||
@ -27,6 +38,8 @@ impl Bot {
|
||||
admin_list: Vec<Uuid>,
|
||||
ban_list: Vec<Uuid>,
|
||||
) -> Result<Self, String> {
|
||||
info!("Connecting to veloren");
|
||||
|
||||
let client = connect_to_veloren(username, password)?;
|
||||
let clock = Clock::new(Duration::from_secs_f64(0.1));
|
||||
|
||||
@ -35,10 +48,13 @@ impl Bot {
|
||||
clock,
|
||||
admin_list,
|
||||
ban_list,
|
||||
events: VecDeque::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn select_character(&mut self) -> Result<(), String> {
|
||||
info!("Selecting a character");
|
||||
|
||||
self.client.load_character_list();
|
||||
|
||||
while self.client.character_list().loading {
|
||||
@ -70,14 +86,24 @@ impl Bot {
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) -> Result<(), String> {
|
||||
let events = self
|
||||
let veloren_events = self
|
||||
.client
|
||||
.tick(ControllerInputs::default(), self.clock.dt())
|
||||
.map_err(|error| format!("{error:?}"))?;
|
||||
|
||||
for event in events {
|
||||
debug!("Tick! Handling {} bot events", self.events.len());
|
||||
|
||||
while !self.events.is_empty() {
|
||||
if let Some(event) = self.events.pop_front() {
|
||||
self.handle_event(event)?;
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Tick! Handling {} veloren events", veloren_events.len());
|
||||
|
||||
for veloren_event in veloren_events {
|
||||
self.handle_veloren_event(veloren_event)?;
|
||||
}
|
||||
|
||||
self.client.cleanup();
|
||||
self.clock.tick();
|
||||
@ -85,8 +111,8 @@ impl Bot {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_event(&mut self, event: Event) -> Result<(), String> {
|
||||
if let Event::Chat(message) = event {
|
||||
fn handle_veloren_event(&mut self, event: VelorenEvent) -> Result<(), String> {
|
||||
if let VelorenEvent::Chat(message) = event {
|
||||
match message.chat_type {
|
||||
ChatType::Tell(sender, _) | ChatType::Group(sender, _) => {
|
||||
let sender_info = self.client.player_list().get(&sender).unwrap().clone();
|
||||
@ -105,60 +131,107 @@ impl Bot {
|
||||
|
||||
fn handle_message(&mut self, content: &str, sender: &PlayerInfo) -> Result<(), String> {
|
||||
let mut words = content.split_whitespace();
|
||||
let command = if let Some(command) = words.next() {
|
||||
command
|
||||
} else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if let Some(command) = words.next() {
|
||||
match command {
|
||||
"admin" => {
|
||||
if self.admin_list.contains(&sender.uuid) || self.admin_list.is_empty() {
|
||||
self.adminify_players(words)?;
|
||||
for word in words {
|
||||
self.events.push_back(Event::Admin(word.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
"ban" => {
|
||||
if self.admin_list.contains(&sender.uuid) {
|
||||
self.kick_players(words.clone());
|
||||
self.ban_players(words)?;
|
||||
for word in words {
|
||||
let uid = self.find_uid(word)?;
|
||||
|
||||
self.events.push_back(Event::Kick(uid.clone()));
|
||||
self.events.push_back(Event::Ban(word.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
"cheese" => {
|
||||
let uid = self.find_uid(&sender.player_alias)?;
|
||||
|
||||
if self.client.group_members().contains_key(&uid) {
|
||||
self.events.push_back(Event::Cheese);
|
||||
}
|
||||
}
|
||||
"cheese" => self
|
||||
.client
|
||||
.send_command("group".to_string(), vec!["I love cheese!".to_string()]),
|
||||
"inv" => {
|
||||
if !self.ban_list.contains(&sender.uuid) {
|
||||
if content == "inv" {
|
||||
self.invite_players([sender.player_alias.as_str()].into_iter());
|
||||
let uid = self.find_uid(&sender.player_alias)?;
|
||||
|
||||
self.events.push_back(Event::Invite(uid));
|
||||
} else {
|
||||
self.invite_players(words);
|
||||
for word in words {
|
||||
let uid = self.find_uid(word)?;
|
||||
|
||||
self.events.push_back(Event::Invite(uid));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"kick" => {
|
||||
if self.admin_list.contains(&sender.uuid) {
|
||||
self.kick_players(words);
|
||||
for word in words {
|
||||
let uid = self.find_uid(word)?;
|
||||
|
||||
self.events.push_back(Event::Kick(uid));
|
||||
}
|
||||
}
|
||||
}
|
||||
"unban" => {
|
||||
if self.admin_list.contains(&sender.uuid) {
|
||||
self.unban_players(words)?;
|
||||
for word in words {
|
||||
self.events.push_back(Event::Unban(word.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_event(&mut self, event: Event) -> Result<(), String> {
|
||||
match event {
|
||||
Event::Admin(name) => {
|
||||
self.adminify_player(&name)?;
|
||||
}
|
||||
Event::Ban(name) => {
|
||||
self.ban_player(&name)?;
|
||||
}
|
||||
Event::Cheese => self
|
||||
.client
|
||||
.send_command("group".to_string(), vec!["I love cheese!".to_string()]),
|
||||
Event::Invite(uid) => {
|
||||
self.client.send_invite(uid, InviteKind::Group);
|
||||
}
|
||||
Event::Kick(uid) => {
|
||||
self.client.kick_from_group(uid);
|
||||
}
|
||||
Event::Unban(name) => {
|
||||
self.unban_player(&name)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn adminify_players<'a, T: Iterator<Item = &'a str>>(
|
||||
&mut self,
|
||||
names: T,
|
||||
) -> Result<(), String> {
|
||||
for name in names {
|
||||
if let Some(uuid) = self.find_uuid(&name) {
|
||||
fn adminify_player(&mut self, name: &str) -> Result<(), String> {
|
||||
info!("Adminifying {name}");
|
||||
|
||||
let uuid = self.find_uuid(name)?;
|
||||
|
||||
if !self.admin_list.contains(&uuid) && !self.ban_list.contains(&uuid) {
|
||||
self.admin_list.push(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let old_config = Config::read()?;
|
||||
let new_config = Config {
|
||||
@ -173,14 +246,14 @@ impl Bot {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ban_players<'a, T: Iterator<Item = &'a str>>(&mut self, names: T) -> Result<(), String> {
|
||||
for name in names {
|
||||
if let Some(uuid) = self.find_uuid(&name) {
|
||||
fn ban_player(&mut self, name: &str) -> Result<(), String> {
|
||||
info!("Banning {name}");
|
||||
|
||||
let uuid = self.find_uuid(name)?;
|
||||
|
||||
if !self.admin_list.contains(&uuid) && !self.ban_list.contains(&uuid) {
|
||||
self.ban_list.push(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let old_config = Config::read()?;
|
||||
let new_config = Config {
|
||||
@ -195,43 +268,18 @@ impl Bot {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn invite_players<'a, T: Iterator<Item = &'a str>>(&mut self, names: T) {
|
||||
for name in names {
|
||||
if let Some(player_id) = self.find_uid(name) {
|
||||
self.client
|
||||
.send_invite(player_id.clone(), InviteKind::Group);
|
||||
} else {
|
||||
eprintln!("{name} could not be invited")
|
||||
}
|
||||
}
|
||||
}
|
||||
fn unban_player(&mut self, name: &str) -> Result<(), String> {
|
||||
info!("Unbanning {name}");
|
||||
|
||||
fn kick_players<'a, T: Iterator<Item = &'a str>>(&mut self, names: T) {
|
||||
for name in names {
|
||||
if let Some(uid) = self.find_uid(&name) {
|
||||
if let Some(uuid) = self.find_uuid(&name) {
|
||||
if !self.admin_list.contains(&uuid) {
|
||||
self.client.kick_from_group(uid.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let uuid = self.find_uuid(name)?;
|
||||
|
||||
fn unban_players<'a, T: Iterator<Item = &'a str>>(&mut self, names: T) -> Result<(), String> {
|
||||
let uuids = names
|
||||
.filter_map(|name| self.find_uuid(name))
|
||||
.collect::<Vec<Uuid>>();
|
||||
|
||||
for uuid in uuids {
|
||||
if let Some(index) = self
|
||||
if let Some(uuid) = self
|
||||
.ban_list
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(index, banned)| if &uuid == banned { Some(index) } else { None })
|
||||
{
|
||||
self.ban_list.remove(index);
|
||||
}
|
||||
self.ban_list.remove(uuid);
|
||||
}
|
||||
|
||||
let old_config = Config::read()?;
|
||||
@ -247,24 +295,32 @@ impl Bot {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_uid<'a>(&'a self, name: &str) -> Option<&'a Uid> {
|
||||
self.client.player_list().iter().find_map(|(id, info)| {
|
||||
fn find_uid(&self, name: &str) -> Result<Uid, String> {
|
||||
self.client
|
||||
.player_list()
|
||||
.iter()
|
||||
.find_map(|(id, info)| {
|
||||
if info.player_alias == name {
|
||||
Some(id)
|
||||
Some(id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok_or(format!("Failed to find uid for player {}", name))
|
||||
}
|
||||
|
||||
fn find_uuid(&self, name: &str) -> Option<Uuid> {
|
||||
self.client.player_list().iter().find_map(|(_, info)| {
|
||||
fn find_uuid(&self, name: &str) -> Result<Uuid, String> {
|
||||
self.client
|
||||
.player_list()
|
||||
.iter()
|
||||
.find_map(|(_, info)| {
|
||||
if info.player_alias == name {
|
||||
Some(info.uuid)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok_or(format!("Failed to find uid for player {}", name))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,8 @@ impl Config {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let config = Config::read().unwrap();
|
||||
let mut bot = Bot::new(
|
||||
&config.username,
|
||||
|
Loading…
Reference in New Issue
Block a user