Created
January 30, 2012 21:17
-
-
Save lopex/1706763 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package pl.irc | |
import org.jibble.pircbot.PircBot | |
import dispatch._ | |
import Http._ | |
import net.liftweb.json._ | |
import net.liftweb.json.JsonDSL._ | |
import net.liftweb.json.Printer.{compact} | |
object Multibottest extends PircBot { | |
val PRODUCTION = Option(System getProperty "multibot.production") map (_ toBoolean) getOrElse false | |
val BOTNAME = if (PRODUCTION) "multibot_" else "multibot__" | |
val BOTMSG = BOTNAME + ":" | |
val NUMLINES = 5 | |
val INNUMLINES = 8 | |
val LAMBDABOT = "lambdabot" | |
val ADMINS = List("***") | |
def main(args: Array[String]) { | |
setName(BOTNAME) | |
setVerbose(true) | |
setEncoding("UTF-8") | |
connect() | |
} | |
def connect() { | |
connect("irc.freenode.net") | |
val channels = if (PRODUCTION) List("#clojure.pl", "#scala.pl", "#ruby.pl", "#rubyonrails.pl", "#scala", "#scalaz", "#lift") else List("#multibottest") | |
channels foreach joinChannel | |
} | |
override def onDisconnect: Unit = while (true) | |
try { | |
connect() | |
return | |
} catch { case e: Exception => | |
e.printStackTrace | |
Thread sleep 10000 | |
} | |
override def onPrivateMessage(sender: String, login: String, hostname: String, message: String) = sender match { | |
case LAMBDABOT => lastChannel foreach (sendMessage(_, message)) | |
case _ => onMessage(sender, sender, login, hostname, message) | |
} | |
override def onNotice(sender: String, login: String, hostname: String, target: String, notice: String) = sender match { | |
case LAMBDABOT => lastChannel foreach (sendNotice(_, notice)) | |
case _ => | |
} | |
override def onAction(sender: String, login: String, hostname: String, target: String, action: String) = sender match { | |
case LAMBDABOT => lastChannel foreach (sendAction(_, action)) | |
case _ => | |
} | |
override def onMessage(channel: String, sender: String, login: String, hostname: String, message: String) = | |
serve(Msg(channel, sender, login, hostname, message)) | |
object Cmd { | |
def unapply(s: String) = if (s.contains(' ')) Some(s.split(" ", 2).toList) else None | |
} | |
case class Msg(channel: String, sender: String, login: String, hostname: String, message: String) | |
import scala.tools.nsc.interpreter.{IMain} | |
import java.io.{PrintWriter, StringWriter, PrintStream, ByteArrayOutputStream} | |
val scalaInt = scala.collection.mutable.Map[String, (IMain, ByteArrayOutputStream)]() | |
var lastChannel: Option[String] = None | |
val stdOut = System.out | |
val stdErr = System.err | |
val conOut = new ByteArrayOutputStream | |
val conOutStream = new PrintStream(conOut) | |
var pythonSession = "" | |
def scalaInterpreter(channel: String)(f: (IMain, ByteArrayOutputStream, ByteArrayOutputStream) => Unit) = this.synchronized { | |
val (si, intOut) = scalaInt.getOrElseUpdate(channel, { | |
val settings = new scala.tools.nsc.Settings(null) | |
settings.usejavacp.value = true | |
settings.deprecation.value = true | |
val intOut = new ByteArrayOutputStream | |
val si = new IMain(settings) { override def parentClassLoader = Thread.currentThread.getContextClassLoader } | |
si.quietImport("scalaz._") | |
si.quietImport("Scalaz._") | |
si.quietImport("org.scalacheck.Prop._") | |
(si, intOut) | |
}) | |
try { | |
System setOut conOutStream | |
System setErr conOutStream | |
f(si, intOut, conOut) | |
} finally { | |
System setOut stdOut | |
System setErr stdErr | |
intOut.flush | |
intOut.reset | |
conOut.flush | |
conOut.reset | |
} | |
} | |
def errorx(s: String) = { | |
val wr = new java.io.FileWriter(new java.io.File("log.txt")) | |
wr.append(s) | |
wr.flush | |
wr.close | |
} | |
val LAMBDABOTIGNORE = Set("#scala", "#scalaz") | |
def serve(implicit msg: Msg): Unit = msg.message match { | |
case Cmd(BOTMSG :: m :: Nil) if ADMINS contains msg.sender => m match { | |
case Cmd("join" :: ch :: Nil) => joinChannel(ch) | |
case Cmd("leave" :: ch :: Nil) => partChannel(ch) | |
case Cmd("reply" :: ch :: Nil) => sendMessage(msg.channel, ch) | |
case Cmd("cookies":: "" :: Nil) => sendMessage(msg.channel, cookies.map{case (k, v) => k + " -> " + v}.mkString(" - ")) //cookies foreach {case(k, v) => sendMessage(msg.channel, k + " -> " + v)} | |
case _ => sendMessage(msg.channel, "unknown command") | |
} | |
case "@listchans" => sendMessage(msg.channel, getChannels mkString " ") | |
case "@bot" | "@bots" => sendMessage(msg.channel, ":)") | |
case "@help" => sendMessage(msg.channel, "(!) scala, (%) ruby, (,) clojure, (>>) haskell, (^) python, (&) javascript, (#) groovy, lambdabot relay (" + !LAMBDABOTIGNORE.contains(msg.channel) + ")") | |
case Cmd("!" :: m :: Nil) => scalaInterpreter(msg.channel){(si, iout, cout) => | |
import scala.tools.nsc.interpreter.Results._ | |
(si interpret m match { | |
case Success => cout.toString.replaceAll("(?m:^res[0-9]+: )", "") // + "\n" + iout.toString.replaceAll("(?m:^res[0-9]+: )", "") | |
case Error => cout.toString.replaceAll("^<console>:[0-9]+: ", "") | |
case Incomplete => "error: unexpected EOF found, incomplete expression" | |
}).split("\n") take NUMLINES foreach (m => sendMessage(msg.channel, " " + (if (m.charAt(0) == 13) m.substring(1) else m))) | |
} | |
case Cmd("!paste" :: m :: Nil) => // Http(url(m) >- {source => serve(msg.copy(message = "! " + source))}) | |
val conOut = new ByteArrayOutputStream | |
(new Http with NoLogging)(url(m) >>> new PrintStream(conOut)) | |
serve(msg.copy(message = "! " + conOut)) | |
case Cmd("!type" :: m :: Nil) => scalaInterpreter(msg.channel)((si, iout, cout) => sendMessage(msg.channel, si.typeOfExpression(m) map (_.toString) getOrElse "Failed to determine type.")) | |
case "!reset" => scalaInt -= msg.channel | |
case "!reset-all" => scalaInt.clear | |
case Cmd("!scalex" :: m :: Nil) => respondJSON(:/("api.scalex.org") <<? Map("q" -> m)) { | |
case JObject(_ :: _ :: JField("results", JArray(results)) :: _) => Some(results.collect { | |
case JObject(JField("resultType", JString(rtype)) :: | |
JField("parent", JObject(JField("name", JString(pname)) :: _ :: JField("typeParams", JString(ptparams)) :: Nil)) :: | |
_ :: | |
JField("name", JString(name)) :: | |
_ :: | |
JField("typeParams", JString(tparams)) :: | |
_ :: | |
JField("comment", | |
JObject( | |
_ :: | |
_ :: | |
JField("short", | |
JObject(_ :: | |
JField("txt", JString(txt)) :: | |
Nil | |
) | |
) :: | |
_ :: | |
_) | |
) :: | |
JField("valueParams", JString(vparams)) :: | |
Nil | |
) => pname + ptparams + " " + name + tparams + ": " + vparams + ": " + rtype + " '" + txt + "'" | |
}.mkString("\n")) | |
case e => Some("unexpected: " + e) | |
} | |
case Cmd("!!" :: m :: Nil) => respond(:/("www.simplyscala.com") / "interp" <<? Map("bot" -> "irc", "code" -> m)) { | |
case "warning: there were deprecation warnings; re-run with -deprecation for details" | | |
"warning: there were unchecked warnings; re-run with -unchecked for details" | | |
"New interpreter instance being created for you, this may take a few seconds." |"Please be patient." => None | |
case line => Some(line.replaceAll("^res[0-9]+: ", "")) | |
} | |
case Cmd("," :: m :: Nil) => respondJSON(:/("try-clojure.org") / "eval.json" <<? Map("expr" -> m)) { | |
case JObject(JField("expr", JString(_)) :: JField("result", JString(result)) :: Nil) => Some(result) | |
case JObject(JField("error", JBool(true)) :: JField("message", JString(message)) :: Nil) => Some(message) | |
case e => Some("unexpected: " + e) | |
} | |
case Cmd(">>" :: m :: Nil) => respondJSON(:/("tryhaskell.org") / "haskell.json" <<? Map("method" -> "eval", "expr" -> m)) { | |
case JObject(JField("result", JString(result)) :: JField("type", JString(xtype)) :: JField("expr", JString(_)) :: Nil) => Some(result + " :: " + xtype) | |
case JObject(JField("error", JString(error)) :: Nil) => Some(error) | |
case e => Some("unexpected: " + e) | |
} | |
case Cmd("%" :: m :: Nil) => respondJSON(:/("tryruby.org") / "/levels/1/challenges/0" <:< | |
Map("Accept" -> "application/json, text/javascript, */*; q=0.01", | |
"Content-Type" -> "application/x-www-form-urlencoded; charset=UTF-8", | |
"X-Requested-With" -> "XMLHttpRequest", | |
"Connection" -> "keep-alive") <<< "cmd=" + java.net.URLEncoder.encode(m, "UTF-8")) { | |
case JObject(JField("success", JBool(true)) :: JField("output", JString(output)) :: _) => Some(output) | |
case JObject(JField("success", JBool(false)) :: _ :: JField("result", JString(output)) :: _) => Some(output) | |
case e => Some("unexpected: " + e) | |
} | |
case Cmd("&" :: m :: Nil) => | |
val src = """ | |
var http = require('http'); | |
http.createServer(function (req, res) { | |
res.writeHead(200, {'Content-Type': 'text/plain'}); | |
var a = (""" + m + """) + ""; | |
res.end(a); | |
}).listen(); | |
""" | |
respondJSON((:/("jsapp.us") / "ajax" << compact(render( ("actions", List(("action", "test") ~ ("code", src) ~ ("randToken", "3901") ~ ("fileName", ""))) ~ ("user", "null") ~ ("token", "null"))))) { | |
case JObject(JField("user", JNull) :: JField("data", JArray(JString(data) :: Nil)) :: Nil) => var s: String = ""; (new Http with NoLogging)(url(data) >- {source => s = source}); Some(s) | |
case e => Some("unexpected: " + e) | |
} | |
case Cmd("^" :: m :: Nil) => respondJSON2(:/("try-python.appspot.com") / "json" << compact(render( ("method", "exec") ~ ("params", List(pythonSession, m)) ~ ("id" -> "null") )), | |
:/("try-python.appspot.com") / "json" << compact(render( ("method", "start_session") ~ ("params", List[String]()) ~ ("id" -> "null") ))) { | |
case JObject(JField("error", JNull) :: JField("id" , JString("null")) :: JField("result", JObject(JField("text", JString(result)) :: _)) :: Nil) => Some(result) | |
case e => Some("unexpected: " + e) | |
} { | |
case JObject(_ :: _ :: JField("result" , JString(session)) :: Nil) => pythonSession = session; None | |
case e => None | |
} | |
case Cmd("#" :: m :: Nil) => respondJSON(:/("groovyconsole.appspot.com") / "executor.groovy" <<? Map("script" -> m), true) { | |
case JObject(JField("executionResult", JString(result)) :: JField("outputText", JString(output)) :: JField("stacktraceText", JString("")) :: Nil) => Some(result.trim + "\n" + output.trim) | |
case JObject(JField("executionResult", JString("")) :: JField("outputText", JString("")) :: JField("stacktraceText", JString(err)) :: Nil) => Some(err) | |
case e => Some("unexpected" + e) | |
} | |
case m if (m.startsWith("@") || m.startsWith(">") || m.startsWith("?")) && m.trim.length > 1 && !LAMBDABOTIGNORE.contains(msg.channel) => | |
lastChannel = Some(msg.channel) | |
sendMessage(LAMBDABOT, m) | |
case _ => | |
} | |
val cookies = scala.collection.mutable.Map[String, String]() | |
def respondJSON(req: Request, join: Boolean = false)(response: JValue => Option[String])(implicit msg: Msg) = respond(req, join){line => response(JsonParser.parse(line))} | |
def respondJSON2(req: Request, init: Request)(response: JValue => Option[String])(initResponse: JValue => Option[String])(implicit msg: Msg) = try { | |
respond(req){line => response(JsonParser.parse(line))} | |
} catch { | |
case _ => | |
respond(init){line => initResponse(JsonParser.parse(line))} | |
respond(req){line => response(JsonParser.parse(line))} | |
} | |
def respond(req: Request, join: Boolean = false)(response: String => Option[String])(implicit msg: Msg) = { | |
val Msg(channel, sender, login, hostname, message) = msg | |
val host = req.host | |
val request = cookies.get(channel + host) map (c => req <:< Map("Cookie" -> c)) getOrElse req | |
// val request = cookies.get(channel + host) map (c => req <:< Map("Cookie" -> c)) getOrElse { | |
// val req2 = req >:> { headers => println(headers.get("Set-Cookie"))} | |
// (new Http)(req2) | |
// req | |
// } | |
val handler = request >+> { r => | |
r >:> { headers => headers.get("Set-Cookie").foreach(h => h.foreach(c => cookies(channel + host) = c.split(";").head)) | |
r >~ { source => | |
val lines = source.getLines.take(NUMLINES) | |
(if (join) List(lines.mkString) else lines).foreach(line => response(line).foreach(l => l.split("\n").take(INNUMLINES).foreach(ml => sendMessage(channel, ml)))) | |
}}} // non empty lines | |
(new Http with NoLogging)(handler) // with thread.Safety | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment