Skip to content

Commit

Permalink
[add] server helping
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisvrose committed Aug 14, 2022
1 parent 69b29aa commit 52ce33d
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 46 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ A smol discord bot to help start a server, without needing to ssh in.

- [X] `start` - Start the server
- [X] `status` - Get the server status
- [ ] `help` - Get commands
- [ ] `stop` - Stop the server
- [X] `help` - Get commands
- [X] `stop` - Stop the server

## Env vars

```env
DISCORD_TOKEN=xyz
ENV_PATH=/home/atreyab/server
RUNSCRIPT=./start.sh
GLOBAL_RUST_LOG=warn
RUST_LOG=trace
```
123 changes: 87 additions & 36 deletions src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,54 @@
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::framework::standard::macros::{command, help};
use serenity::framework::standard::{
help_commands, Args, CommandGroup, CommandResult, HelpOptions,
};
use serenity::model::channel::Message;
use serenity::model::prelude::UserId;
use serenity::prelude::*;

use log::{debug, error, trace, warn, info};
use std::collections::HashSet;
use std::env::{self, set_current_dir};

use std::io::Write;
use std::path::Path;
use std::process::Command;
use std::process::{Command, Stdio};

use crate::data::Serv;
use crate::server_helper::poll_child;

#[help]
#[individual_command_tip = "Hello!\n\n\
If you want more information about a specific command, just pass the command as argument."]
#[strikethrough_commands_tip_in_guild = ""]
pub async fn my_help(
context: &Context,
msg: &Message,
args: Args,
help_options: &'static HelpOptions,
groups: &[&'static CommandGroup],
owners: HashSet<UserId>,
) -> CommandResult {
let _ = help_commands::with_embeds(context, msg, args, help_options, groups, owners).await;
Ok(())
}

#[command]
#[description("Get a pong message")]
pub async fn ping(ctx: &Context, msg: &Message) -> CommandResult {
msg.reply(ctx, "Pong!").await?;

debug!("Ping command reply for {}", msg.author);
Ok(())
}

#[command]
#[description("Query the status of the minecraft server")]
pub async fn status(ctx: &Context, msg: &Message) -> CommandResult {
let child_status = {
let mut x = ctx.data.write().await;
let mut command = x.get_mut::<Serv>().expect("Could not unwrap data");

match &mut command.child_process {
Some(process) => {
let res_return = process.try_wait();
match res_return {
Ok(Some(_exit_code)) => true,
Ok(None) => false,
Err(_) => true,
}
}
None => false,
}
let mut command_lock = ctx.data.write().await;
!poll_child(&mut command_lock).await
};
// x.

msg.reply(
ctx,
format!(
Expand All @@ -49,30 +62,68 @@ pub async fn status(ctx: &Context, msg: &Message) -> CommandResult {
}

#[command]
#[description("Start the minecraft server")]
pub async fn start(ctx: &Context, msg: &Message) -> CommandResult {
let path = env::var("ENV_PATH").expect("ENV_PATH env var missing");
let script = env::var("RUNSCRIPT").expect("RUNSCRIPT env var missing");

let mut command_lock = ctx.data.write().await;
let has_exit = poll_child(&mut command_lock).await;
set_current_dir(Path::new(path.as_str())).expect("Could not change directory");

if has_exit{
let child = {
Command::new(script.as_str())
// .args(["-jar", "server.jar"])
.stdin(Stdio::piped())
.spawn()
};
match child {
Ok(command_child) => {
// do it in a block so the lock releases outside the block

command_lock.insert::<Serv>(Serv::new_with_child(command_child));

// x.
msg.reply(ctx, format!("Started server!",)).await?;
trace!("Started minecraft server");
}
Err(err) => {
msg.reply(ctx, format!("Could not start! Refer logs",))
.await?;
error!("Could not start server: {}", err.to_string());
}
};

let child = {
Command::new(script.as_str())
// .args(["-jar", "server.jar"])
.spawn()
};
}else{
info!("Tried to start server when already running!");
msg.reply(ctx, "The server is already running").await?;
}

if let Ok(command_child) = child {
// do it in a block so the lock releases outside the block
{
let mut command_lock = ctx.data.write().await;
command_lock.insert::<Serv>(Serv::new_with_child(command_child));
command_lock.downgrade()
};
// x.
msg.reply(ctx, format!("Started server!",)).await?;
Ok(())
}

#[command]
#[description = "Stop the minecraft server"]
pub async fn stop(ctx: &Context, msg: &Message) -> CommandResult {
let mut command_lock = ctx.data.write().await;
let running = poll_child(&mut command_lock).await;
let x = command_lock
.get_mut::<Serv>()
.and_then(|x| x.child_process.as_mut());

if !running {
match x {
Some(child) => {
let stdin = child.stdin.as_mut().unwrap();
stdin.write_all(b"stop\n").unwrap();
// drop(stdin);
}
None => {}
}
msg.reply(ctx, "Stopping the server").await?;
} else {
msg.reply(ctx, format!("Could not start! Refer logs",))
.await?;
msg.reply(ctx, "The server is already stopped").await?;
}

Ok(())
}
4 changes: 4 additions & 0 deletions src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ use std::process::Child;

use serenity::prelude::TypeMapKey;

/// Contains the data maintained by the bot
pub struct Serv {
/// Server child process
pub child_process: Option<Child>,
}
impl Serv {
/// Creates a new instance, childless
pub fn new() -> Self {
Serv {
child_process: None,
}
}
/// create with a child
pub fn new_with_child(x: Child) -> Self {
Serv {
child_process: Some(x),
Expand Down
22 changes: 14 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use serenity::framework::standard::StandardFramework;
use serenity::model::channel::Message;
use serenity::model::prelude::{Activity, Ready};
use serenity::prelude::*;

use log::{warn ,error,info,trace};
use std::env;

mod commands;
Expand All @@ -13,8 +13,11 @@ use commands::*;
mod data;
use data::*;

mod server_helper;

#[group]
#[commands(ping, start, status)]
#[commands(ping, start, status,stop)]
#[only_in(guilds)]
struct General;

struct Handler;
Expand All @@ -27,28 +30,31 @@ impl EventHandler for Handler {
serenity::model::user::OnlineStatus::Online,
)
.await;
println!("Ready!")
info!("Ready to accept commands");
}
}

#[hook]
async fn unrecognised_command_hook(_ctx: &Context, msg: &Message, unrecognised_command_name: &str) {
println!(
"A user named {:?} tried to execute an unknown command: {}",
msg.author.name, unrecognised_command_name
);
warn!("A user named {:?} tried to execute an unknown command: {}",
msg.author.name, unrecognised_command_name);

}

#[tokio::main]
async fn main() {
dotenv::dotenv().ok();
sensible_env_logger::init!();

let framework = StandardFramework::new()
.configure(|c| c.prefix("~")) // set the bot's prefix to "~"
.help(&MY_HELP)
.unrecognised_command(unrecognised_command_hook)
.group(&GENERAL_GROUP);

// Login with a bot token from the environment
let token = env::var("DISCORD_TOKEN").expect("token");
trace!("Got a token from environment");
let intents = GatewayIntents::non_privileged() | GatewayIntents::MESSAGE_CONTENT;
let mut client = Client::builder(token, intents)
.event_handler(Handler)
Expand All @@ -59,7 +65,7 @@ async fn main() {

// start listening for events by starting a single shard
if let Err(why) = client.start().await {
println!("An error occurred while running the client: {:?}", why);
error!("An error occurred while running the client: {:?}", why);
}
}

38 changes: 38 additions & 0 deletions src/server_helper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use log::{debug, warn};
use serenity::prelude::TypeMap;
use tokio::sync::RwLockWriteGuard;

use crate::data::Serv;

/// get whether child has exit
pub async fn poll_child<'a>(x: &mut RwLockWriteGuard<'a, TypeMap>) -> bool {
let child_status = {
let command = x.get_mut::<Serv>().expect("Could not unwrap data");

match &mut command.child_process {
Some(process) => {
debug!("Waiting for child process");
let res_return = process.try_wait();
match res_return {
Ok(Some(_exit_code)) => {
debug!("Exit code {}", _exit_code);
true
}
Ok(None) => {
debug!("Server has not exit yet");
false
}
Err(e) => {
warn!("Could not wait for child process: {}", e.to_string());
true
}
}
}
None => {
debug!("No child process for status command to query");
true
}
}
};
child_status
}

0 comments on commit 52ce33d

Please sign in to comment.