Base for RCON and Discord ends
This commit is contained in:
parent
4c00dc6930
commit
06ac88f6ff
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
/result
|
/result
|
||||||
|
/.env
|
||||||
|
|
1140
Cargo.lock
generated
1140
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -2,11 +2,20 @@
|
||||||
name = "glados"
|
name = "glados"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
[package.metadata.nix]
|
[package.metadata.nix]
|
||||||
build = true
|
build = true
|
||||||
toolchain = "nightly"
|
toolchain = "nightly"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
dotenv = "0.15.0"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
rcon = "0.5.1"
|
rcon = "0.5.1"
|
||||||
|
serenity = "0.10.9"
|
||||||
|
structopt = "0.3.25"
|
||||||
|
thiserror = "1.0.30"
|
||||||
|
|
||||||
|
[dependencies.tokio]
|
||||||
|
version = "1.14.0"
|
||||||
|
features = [ "macros", "rt" ]
|
||||||
|
|
5
README.md
Normal file
5
README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# GLaDOS
|
||||||
|
|
||||||
|
Helper tool for my minecraft instance.
|
||||||
|
|
||||||
|
License: MIT
|
3
README.tpl
Normal file
3
README.tpl
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{{readme}}
|
||||||
|
|
||||||
|
License: {{license}}
|
24
flake.lock
24
flake.lock
|
@ -2,11 +2,11 @@
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"devshell": {
|
"devshell": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1636119665,
|
"lastModified": 1637098489,
|
||||||
"narHash": "sha256-e11Z9PyH9hdgTm4Vyl8S5iTwrv0um6+srzb1Ba+YUHA=",
|
"narHash": "sha256-IWBYLSNSENI/fTrXdYDhuCavxcgN9+RERrPM81f6DXY=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "devshell",
|
"repo": "devshell",
|
||||||
"rev": "ab14b1a3cb253f58e02f5f849d621292fbf81fad",
|
"rev": "e8c2d4967b5c498b12551d1bb49352dcf9efa3e4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -22,11 +22,11 @@
|
||||||
"rustOverlay": "rustOverlay"
|
"rustOverlay": "rustOverlay"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1637042995,
|
"lastModified": 1637129418,
|
||||||
"narHash": "sha256-qogt7Qt/z859NrMRQHxPfaQj1BdK0+xNYa06ZU48dOo=",
|
"narHash": "sha256-bO6rLgIiqK6pdeF2ewKyD6c+hNAcBEfXDqiTRaWzNmo=",
|
||||||
"owner": "yusdacra",
|
"owner": "yusdacra",
|
||||||
"repo": "nix-cargo-integration",
|
"repo": "nix-cargo-integration",
|
||||||
"rev": "298ea9dc79c5420a49976da3d20ca05d99b429f3",
|
"rev": "1faede2be6c28a68a00b3479b77c849720324511",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -37,11 +37,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1636800699,
|
"lastModified": 1636976544,
|
||||||
"narHash": "sha256-SwbyVxXffu3G2ulJIbTf0iQfqhbGbdml4Dyv5j9BiAI=",
|
"narHash": "sha256-9ZmdyoRz4Qu8bP5BKR1T10YbzcB9nvCeQjOEw2cRKR0=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "2fa862644fc15ecb525eb8cd0a60276f1c340c7c",
|
"rev": "931ab058daa7e4cd539533963f95e2bb0dbd41e6",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -59,11 +59,11 @@
|
||||||
"rustOverlay": {
|
"rustOverlay": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1637028901,
|
"lastModified": 1637115307,
|
||||||
"narHash": "sha256-MJWtQbSZC1kNzJb2pg6mBcHTmIQdNBeQMznwbAfoqUg=",
|
"narHash": "sha256-G+RKZeE1yLrnq+ExHF+HnSJsT+QJWebGhssgZHz3B00=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "ac1c572e4060c943e4077987da4b07bb7c3a491c",
|
"rev": "8a2e5fa870df3d34667d28fb3383d19516d182e4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
50
src/conf.rs
Normal file
50
src/conf.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use serenity::model::id::UserId;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref ARGS: Args = Args::from_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct Args {
|
||||||
|
#[structopt(long,
|
||||||
|
short = "t",
|
||||||
|
value_name = "TOKEN",
|
||||||
|
validator = validate_token,
|
||||||
|
env = "DISCORD_TOKEN")]
|
||||||
|
pub discord_token: String,
|
||||||
|
|
||||||
|
#[structopt(long, value_name = "URL", env = "RCON_ADDRESS")]
|
||||||
|
pub rcon_address: String,
|
||||||
|
|
||||||
|
#[structopt(long, value_name = "PASSWORD", env = "RCON_PASSWORD")]
|
||||||
|
pub rcon_password: String,
|
||||||
|
|
||||||
|
#[structopt(long, short, value_name = "ID", env = "DISCORD_OWNERS")]
|
||||||
|
pub owners: Vec<UserId>,
|
||||||
|
|
||||||
|
#[structopt(
|
||||||
|
long,
|
||||||
|
short,
|
||||||
|
value_name = "PREFIX",
|
||||||
|
env = "DISCORD_PREFIX",
|
||||||
|
default_value = "~"
|
||||||
|
)]
|
||||||
|
pub prefix: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_token(raw: String) -> std::result::Result<(), String> {
|
||||||
|
serenity::client::validate_token(raw).map_err(|_| "Invalid discord token. Maybe a typo?".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Args {
|
||||||
|
pub fn owners(&self) -> HashSet<UserId> {
|
||||||
|
if self.owners.is_empty() {
|
||||||
|
eprintln!("You should probably specify at least one `--owner`");
|
||||||
|
}
|
||||||
|
self.owners.iter().cloned().collect()
|
||||||
|
}
|
||||||
|
}
|
40
src/discord.rs
Normal file
40
src/discord.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use serenity::{
|
||||||
|
framework::{standard::macros::group, StandardFramework},
|
||||||
|
Client,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
conf::ARGS,
|
||||||
|
error::{Error, Result},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Api {
|
||||||
|
client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[group]
|
||||||
|
#[owners_only]
|
||||||
|
struct Owner;
|
||||||
|
|
||||||
|
impl Api {
|
||||||
|
pub async fn init() -> Result<Self> {
|
||||||
|
let framework = StandardFramework::new()
|
||||||
|
.configure(|c| {
|
||||||
|
c.with_whitespace(true)
|
||||||
|
.owners(ARGS.owners())
|
||||||
|
.prefix(&ARGS.prefix)
|
||||||
|
.delimiters(vec![", ", ","])
|
||||||
|
})
|
||||||
|
.group(&OWNER_GROUP);
|
||||||
|
let client = Client::builder(&ARGS.discord_token)
|
||||||
|
.framework(framework)
|
||||||
|
.await
|
||||||
|
.map_err(Error::CreatingDiscordClient)?;
|
||||||
|
Ok(Self { client })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start(mut self) -> Result {
|
||||||
|
self.client.start().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
17
src/error.rs
Normal file
17
src/error.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub type Result<T = ()> = ::std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Configuration key {_0:?} is missing. Try setting it through args or the environment")]
|
||||||
|
MissingConfiguration(String),
|
||||||
|
#[error("Failed to initialize RCON: {_0}")]
|
||||||
|
InitializingRcon(#[source] rcon::Error),
|
||||||
|
#[error("Failed to execute RCON command {_0:?}: {_1}")]
|
||||||
|
RconCommand(String, #[source] rcon::Error),
|
||||||
|
#[error("Failed to create the Discord client: {_0}")]
|
||||||
|
CreatingDiscordClient(#[source] serenity::Error),
|
||||||
|
#[error("Error while listening for Discord events: {_0}")]
|
||||||
|
Serenity(#[from] serenity::Error),
|
||||||
|
}
|
28
src/main.rs
28
src/main.rs
|
@ -1,3 +1,27 @@
|
||||||
fn main() {
|
//! # Gim's Library and Discord Orchestration System
|
||||||
println!("Hello, world!");
|
//!
|
||||||
|
//! Helper tool for my minecraft instance.
|
||||||
|
|
||||||
|
mod conf;
|
||||||
|
mod discord;
|
||||||
|
mod error;
|
||||||
|
mod rcon;
|
||||||
|
mod state;
|
||||||
|
|
||||||
|
use error::Result;
|
||||||
|
use state::State;
|
||||||
|
|
||||||
|
#[tokio::main(flavor = "current_thread")]
|
||||||
|
async fn main() {
|
||||||
|
match real_main().await {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(why) => eprintln!("Error: {}", why),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn real_main() -> Result {
|
||||||
|
dotenv::dotenv().ok();
|
||||||
|
|
||||||
|
let state = State::init().await?;
|
||||||
|
state.run_bot().await
|
||||||
}
|
}
|
||||||
|
|
33
src/rcon.rs
Normal file
33
src/rcon.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use crate::{
|
||||||
|
conf::ARGS,
|
||||||
|
error::{Error, Result},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Rcon {
|
||||||
|
inner: ::rcon::Connection,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rcon {
|
||||||
|
pub async fn init() -> Result<Self> {
|
||||||
|
let addr = &ARGS.rcon_address;
|
||||||
|
let password = &ARGS.rcon_password;
|
||||||
|
let inner = rcon::Connection::builder()
|
||||||
|
.enable_minecraft_quirks(true)
|
||||||
|
.connect(addr, &password)
|
||||||
|
.await
|
||||||
|
.map_err(Error::InitializingRcon)?;
|
||||||
|
Ok(Self { inner })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||||
|
self.inner
|
||||||
|
.cmd(cmd)
|
||||||
|
.await
|
||||||
|
.map_err(|why| Error::RconCommand(cmd.into(), why))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn greet(&mut self) -> Result {
|
||||||
|
let _ = self.cmd("say GLaDOS is now online").await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
18
src/state.rs
Normal file
18
src/state.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use crate::{discord::Api, error::Result, rcon::Rcon};
|
||||||
|
|
||||||
|
pub struct State {
|
||||||
|
pub rcon: Rcon,
|
||||||
|
pub api: Api,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub async fn init() -> Result<Self> {
|
||||||
|
let rcon = Rcon::init().await?;
|
||||||
|
let api = Api::init().await?;
|
||||||
|
Ok(State { rcon, api })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_bot(self) -> Result {
|
||||||
|
self.api.start().await
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue