Skip to content

Instantly share code, notes, and snippets.

@lifthrasiir
Last active July 23, 2017 13:21
Show Gist options
  • Save lifthrasiir/ffa5fa82c4400cfb7da25ef29a9b6057 to your computer and use it in GitHub Desktop.
Save lifthrasiir/ffa5fa82c4400cfb7da25ef29a9b6057 to your computer and use it in GitHub Desktop.
Cog
target
Cargo.lock
[package]
name = "cog"
version = "0.1.0"
authors = ["Kang Seonghoon <public+git@mearie.org>"]
[[bin]]
name = "cog"
path = "main.rs"
[dependencies]
irc = "0.11"
hyper = "0.9"
serde = "1.0"
serde_json = "1.0"
regex = "0.1"
extern crate irc;
extern crate hyper;
extern crate serde;
extern crate serde_json;
extern crate regex;
use std::io;
use std::ascii::AsciiExt;
use std::default::Default;
use std::collections::BTreeMap;
use std::thread;
use irc::client::prelude::*;
use regex::Regex;
use serde_json::Value;
fn playpen(code: &str, opts: &str) -> Result<String, String> {
let mut channel = None;
let mut mode = None;
for opt in opts.split(',') {
let opt = opt.trim().to_owned();
match &opt[..] {
"stable" | "beta" | "nightly" => { channel = Some(opt); }
"debug" | "release" => { mode = Some(opt); }
_ => {}
}
}
let channel = channel.unwrap_or_else(|| "stable".to_owned());
let mode = mode.unwrap_or_else(|| "debug".to_owned());
let mut map = BTreeMap::new();
map.insert("channel", Value::String(channel));
map.insert("mode", Value::String(mode));
map.insert("code", Value::String(code.to_string()));
map.insert("crateType", Value::String("bin".to_string()));
map.insert("tests", Value::Bool(false));
let json = serde_json::to_string(&map).unwrap();
let client = hyper::client::Client::new();
let req = client.post("https://play.rust-lang.org/execute");
let req = req.header(hyper::header::ContentType::json());
let req = req.body(&json);
match req.send() {
Ok(resp) => {
let map: BTreeMap<String, Value> = match serde_json::from_reader(resp) {
Ok(map) => map,
Err(e) => return Err(format!("playpen 응답 파싱 오류: {}", e)),
};
if let Some(&Value::String(ref e)) = map.get("error") {
Ok(format!("playpen 오류: {}", e))
} else if let Some(&Value::Bool(true)) = map.get("success") {
Ok(map.get("stdout").and_then(|v| v.as_str()).unwrap_or("").to_string())
} else {
Err(map.get("stderr").and_then(|v| v.as_str()).unwrap_or("").to_string())
}
}
Err(e) => {
Err(format!("playpen 접속 오류: {}", e))
}
}
}
fn truncate(s: &str, maxlines: usize, maxbytesperline: usize) -> Vec<&str> {
let mut lines = Vec::new();
for mut line in s.lines() {
while line.len() > maxbytesperline {
let mut i = maxbytesperline;
while !line.is_char_boundary(i) { i -= 1; }
lines.push(&line[..i]);
if lines.len() >= maxlines { break; }
line = &line[i..];
}
lines.push(line);
if lines.len() >= maxlines { break; }
}
lines
}
fn main_() -> io::Result<()> {
let config = Config {
nickname: Some(format!("cog")),
server: Some(format!("bassoon.irc.ozinger.org")),
channels: Some(vec![format!("#rust")]),
.. Default::default()
};
let server = try!(IrcServer::from_config(config));
try!(server.identify());
let code_pattern = Regex::new(r"(?x)
^ (?:
(?: \( \s*
(?P<opts> [a-z0-9]+\s* (?: , \s*[a-z0-9]+\s* )* )
\s* \) )?
[:,]
\s* (?P<code>.*) \s*
)? $").unwrap();
for message in server.iter() {
let message = try!(message);
print!("{}", message);
match message.command {
Command::PRIVMSG(ref target, ref msg) => {
let current_nick = server.current_nickname();
if !msg.starts_with(current_nick) { continue; }
let target = if target.starts_with("#") {
target.to_owned() // reply to the channel
} else if let Some(nick) = message.source_nickname() {
nick.to_owned() // reply to the private message
} else {
continue; // otherwise do not reply
};
let m = if let Some(m) = code_pattern.captures(&msg[current_nick.len()..]) {
m
} else {
continue;
};
let opts = m.name("opts").unwrap_or("").to_ascii_lowercase();
let code = m.name("code").unwrap_or("");
if code.is_empty() {
let _ = server.send_notice(&target, &format!(
"cog란 무엇입니까? 그것은 \"{nick}:\"나 \"{nick},\"로 부르면 \
Rust 코드를 실행해 주는 훌륭한 봇인 것입니다.", nick = current_nick));
let _ = server.send_notice(&target, &format!(
"\"{nick}(nightly):\"나 \"{nick}(o3),\"같은 것도 됩니다. 코드를 봅시다: \
https://gist.github.com/lifthrasiir/ffa5fa82c4400cfb7da25ef29a9b6057",
nick = current_nick));
continue;
}
let code = if !code.contains("fn main") {
// the code should be at its own line, otherwise comments won't work
format!("fn main() {{\n\
let result = {{\n\
{}\n\
}};\n\
println!(\"{{:?}}\", result);\n\
}}", code)
} else {
code.to_owned()
};
let server = server.clone();
thread::spawn(move || {
match playpen(&code, &opts) {
Ok(result) => {
for line in truncate(&result, 3, 400) {
let _ = server.send_notice(&target, line);
}
}
Err(error) => {
for line in truncate(&error, 3, 400 - 4) {
let _ = server.send_notice(&target, &format!("*** {}", line));
}
}
}
});
},
_ => (),
}
}
Ok(())
}
fn main() {
main_().unwrap();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment