Implement bot
This commit is contained in:
parent
c5b440311c
commit
ac4f180dd8
|
@ -1,3 +1,3 @@
|
||||||
/target
|
/target
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
secret
|
prod_config.toml
|
||||||
|
|
|
@ -5,8 +5,8 @@ authors = ["kageru <kageru@encode.moe>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serenity = "0.7"
|
serenity = "0.7.4"
|
||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
toml = "0.5.5"
|
toml = "0.5.5"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
itertools = "0.8.2"
|
cmd_lib = "0.7.8"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# RCEAADB
|
# RCEAADB
|
||||||
|
|
||||||
## I’m sorry?
|
## I’m sorry?
|
||||||
RCEAABD. Remote code execution as a discord bot.
|
RCEAADB. Remote code execution as a discord bot.
|
||||||
Almost as good as ncmpcpp.
|
Almost as good as ncmpcpp.
|
||||||
|
|
||||||
## But why? Do you hate security that much?
|
## But why? Do you hate security that much?
|
||||||
|
|
24
config.toml
24
config.toml
|
@ -1,4 +1,22 @@
|
||||||
|
secret = "your login secret"
|
||||||
|
|
||||||
[[command]]
|
[[command]]
|
||||||
trigger = ">restart_example"
|
# The prefix (a constant in the source code)
|
||||||
command = "/usr/bin/restart_something"
|
# does not need to be specified here.
|
||||||
users = [12345, 54321]
|
# It is added automatically.
|
||||||
|
trigger = "create_file"
|
||||||
|
# Spaces are not escaped here
|
||||||
|
command = "touch /tmp/test-rce"
|
||||||
|
users = [137780880344088576]
|
||||||
|
|
||||||
|
[[command]]
|
||||||
|
trigger = "append_to_file"
|
||||||
|
# Pipes and redirection are allowed
|
||||||
|
command = "echo asd >> /tmp/test-rce"
|
||||||
|
users = [137780880344088576]
|
||||||
|
|
||||||
|
[[command]]
|
||||||
|
trigger = "delete_file"
|
||||||
|
command = "rm /tmp/test-rce"
|
||||||
|
# Multiple user IDs can be added here
|
||||||
|
users = [137780880344088576, 123456789098765]
|
||||||
|
|
|
@ -1,41 +1,36 @@
|
||||||
use itertools::Itertools;
|
use super::config::CONFIG;
|
||||||
use lazy_static;
|
use cmd_lib::{CmdResult, Process};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serenity::model::channel::Message;
|
use serenity::model::channel::Message;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct Command<'a> {
|
pub struct Command {
|
||||||
trigger: &'a str,
|
trigger: String,
|
||||||
command: &'a str,
|
command: String,
|
||||||
users: Vec<u64>,
|
users: Vec<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct Config {
|
|
||||||
#[serde(rename = "command")]
|
|
||||||
commands: Vec<Command<'static>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref RAW_CONFIG: String = super::file::read_file("config.toml").join("\n");
|
|
||||||
static ref COMMANDS: Vec<Command<'static>> = {
|
|
||||||
toml::from_str::<Config>(&RAW_CONFIG).expect("Error in config").commands
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_commands() {
|
pub fn print_commands() {
|
||||||
COMMANDS.iter().for_each(|c| println!("{:?}", c));
|
CONFIG.commands.iter().for_each(|c| println!("{:?}", c));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_matching(message: &str) -> Option<&Command> {
|
pub fn find_matching(message: &str) -> Option<&Command> {
|
||||||
COMMANDS.iter().find(|&c| message.starts_with(&c.trigger))
|
CONFIG.commands.iter().find(|&c| message[1..] == c.trigger)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Command<'a> {
|
impl Command {
|
||||||
pub fn execute(&self, msg: &Message) -> Result<(), &'static str> {
|
/// Check permissions for a command and execute it.
|
||||||
|
/// Returns an error for insufficient permissions or non-zero return codes.
|
||||||
|
pub fn execute(&self, msg: &Message) -> Result<(), String> {
|
||||||
|
println!(
|
||||||
|
"User {} tried to execute command {}",
|
||||||
|
&msg.author, &msg.content
|
||||||
|
);
|
||||||
if !self.users.contains(&msg.author.id.0) {
|
if !self.users.contains(&msg.author.id.0) {
|
||||||
return Err("You don’t have the permissions to execute this command.");
|
return Err("You don’t have the permissions to execute this command.".to_owned());
|
||||||
}
|
}
|
||||||
unimplemented!()
|
Process::new(self.command.clone())
|
||||||
|
.wait::<CmdResult>()
|
||||||
|
.map_err(|e| format!("{:?}", e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
use super::commands::*;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
pub fn read_config() -> String {
|
||||||
|
fs::read_to_string("config.toml").expect("Could not read config file. Make sure a file named config.toml exists in the current working directory.")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Config {
|
||||||
|
#[serde(rename = "command")]
|
||||||
|
pub commands: Vec<Command>,
|
||||||
|
pub secret: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref CONFIG: Config = toml::from_str(&read_config()).unwrap();
|
||||||
|
}
|
|
@ -1,9 +0,0 @@
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{prelude::*, BufReader};
|
|
||||||
|
|
||||||
pub fn read_file(path: &'static str) -> impl Iterator<Item = String> {
|
|
||||||
let reader = BufReader::new(File::open(path).expect("File not found"));
|
|
||||||
reader
|
|
||||||
.lines()
|
|
||||||
.map(|l| l.expect(&format!("Could not read from file")))
|
|
||||||
}
|
|
24
src/main.rs
24
src/main.rs
|
@ -5,24 +5,17 @@ use serenity::model::channel::Message;
|
||||||
use serenity::model::id::ChannelId;
|
use serenity::model::id::ChannelId;
|
||||||
use serenity::prelude::*;
|
use serenity::prelude::*;
|
||||||
mod commands;
|
mod commands;
|
||||||
mod file;
|
mod config;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
/*
|
|
||||||
Client::new(
|
|
||||||
file::read_file("secret")
|
|
||||||
.next()
|
|
||||||
.expect("The file containing the login secret is present but empty"),
|
|
||||||
Handler,
|
|
||||||
)
|
|
||||||
.expect("Error creating client")
|
|
||||||
.start()
|
|
||||||
.expect("Could not connect to discord");
|
|
||||||
*/
|
|
||||||
print_commands();
|
print_commands();
|
||||||
|
Client::new(&config::CONFIG.secret, Handler)
|
||||||
|
.expect("Error creating client")
|
||||||
|
.start()
|
||||||
|
.expect("Could not connect to discord");
|
||||||
}
|
}
|
||||||
|
|
||||||
const PREFIX: char = '$';
|
const PREFIX: char = '>';
|
||||||
|
|
||||||
pub struct Handler;
|
pub struct Handler;
|
||||||
impl EventHandler for Handler {
|
impl EventHandler for Handler {
|
||||||
|
@ -33,10 +26,9 @@ impl EventHandler for Handler {
|
||||||
if let Some(command) = find_matching(&msg.content) {
|
if let Some(command) = find_matching(&msg.content) {
|
||||||
let response = match command.execute(&msg) {
|
let response = match command.execute(&msg) {
|
||||||
Err(e) => e,
|
Err(e) => e,
|
||||||
// TODO: maybe check the return code here?
|
Ok(()) => "Done".to_owned(),
|
||||||
Ok(()) => "Done",
|
|
||||||
};
|
};
|
||||||
send(msg.channel_id, response, &ctx);
|
send(msg.channel_id, &response, &ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user