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
|
||||
/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"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[package.metadata.nix]
|
||||
build = true
|
||||
toolchain = "nightly"
|
||||
|
||||
[dependencies]
|
||||
dotenv = "0.15.0"
|
||||
lazy_static = "1.4.0"
|
||||
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": {
|
||||
"devshell": {
|
||||
"locked": {
|
||||
"lastModified": 1636119665,
|
||||
"narHash": "sha256-e11Z9PyH9hdgTm4Vyl8S5iTwrv0um6+srzb1Ba+YUHA=",
|
||||
"lastModified": 1637098489,
|
||||
"narHash": "sha256-IWBYLSNSENI/fTrXdYDhuCavxcgN9+RERrPM81f6DXY=",
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"rev": "ab14b1a3cb253f58e02f5f849d621292fbf81fad",
|
||||
"rev": "e8c2d4967b5c498b12551d1bb49352dcf9efa3e4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -22,11 +22,11 @@
|
|||
"rustOverlay": "rustOverlay"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1637042995,
|
||||
"narHash": "sha256-qogt7Qt/z859NrMRQHxPfaQj1BdK0+xNYa06ZU48dOo=",
|
||||
"lastModified": 1637129418,
|
||||
"narHash": "sha256-bO6rLgIiqK6pdeF2ewKyD6c+hNAcBEfXDqiTRaWzNmo=",
|
||||
"owner": "yusdacra",
|
||||
"repo": "nix-cargo-integration",
|
||||
"rev": "298ea9dc79c5420a49976da3d20ca05d99b429f3",
|
||||
"rev": "1faede2be6c28a68a00b3479b77c849720324511",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -37,11 +37,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1636800699,
|
||||
"narHash": "sha256-SwbyVxXffu3G2ulJIbTf0iQfqhbGbdml4Dyv5j9BiAI=",
|
||||
"lastModified": 1636976544,
|
||||
"narHash": "sha256-9ZmdyoRz4Qu8bP5BKR1T10YbzcB9nvCeQjOEw2cRKR0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2fa862644fc15ecb525eb8cd0a60276f1c340c7c",
|
||||
"rev": "931ab058daa7e4cd539533963f95e2bb0dbd41e6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -59,11 +59,11 @@
|
|||
"rustOverlay": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1637028901,
|
||||
"narHash": "sha256-MJWtQbSZC1kNzJb2pg6mBcHTmIQdNBeQMznwbAfoqUg=",
|
||||
"lastModified": 1637115307,
|
||||
"narHash": "sha256-G+RKZeE1yLrnq+ExHF+HnSJsT+QJWebGhssgZHz3B00=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "ac1c572e4060c943e4077987da4b07bb7c3a491c",
|
||||
"rev": "8a2e5fa870df3d34667d28fb3383d19516d182e4",
|
||||
"type": "github"
|
||||
},
|
||||
"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() {
|
||||
println!("Hello, world!");
|
||||
//! # Gim's Library and Discord Orchestration System
|
||||
//!
|
||||
//! 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