Commits

firefly  committed ae21b34 Merge

Merge with b8d0331f28d1.

  • Participants
  • Parent commits 778b54d, b8d0331

Comments (0)

Files changed (8)

File data/data.json.base

 {
-	"config": {
-		"prefixes": [
-			"+"
-		]
-	},
-	"connections": {
-		"FreeNode": {
-			"protocol": "irc",
-			"options": {
-				"hostname": "irc.freenode.net",
-				"port": 6667,
-				"nickname": "Eldis4|",
-				"ident": "eldis4",
-				"realname": "Eldis4 IRC bot"
-			},
-			"autoperform": {
-				"$type": "function",
-				"args": "",
-				"body": "this.joinRoom(\"##FireFly\");"
-			},
-			"users": {
-				"unaffiliated/firefly": {
-					"rights": {
-						"admin": true,
-						"chanop": [
-							"##FireFly",
-						],
-						"botmanager": true,
-						"service": true,
-						"unsafe": true
-					}
-				}
-			}
-		}
-	},
-	"commandData": {},
-	"developer-keys": {}
+  "config": {
+    "prefixes": [
+      "+"
+    ]
+  },
+  "connections": {
+    "FreeNode": {
+      "protocol": "irc",
+      "options": {
+        "hostname": "irc.freenode.net",
+        "port": 6667,
+        "nickname": "Eldis4|",
+        "ident": "eldis4",
+        "realname": "Eldis4 IRC bot"
+      },
+      "autoperform": {
+        "$type": "function",
+        "args": "",
+        "body": "this.joinRoom(\"##FireFly\");"
+      },
+      "users": {
+        "unaffiliated/firefly": {
+          "rights": {
+            "admin": true,
+            "chanop": [
+              "##FireFly",
+            ],
+            "botmanager": true,
+            "service": true,
+            "unsafe": true
+          }
+        }
+      }
+    }
+  },
+  "commandData": {},
+  "developer-keys": {}
 }

File modules/01-command-trigger.coffee

 		(getPrefix line)?
 
 	call: (conn, msg) ->
+		console.log "Calling commandTrigger for", msg
+
 		prefix = getPrefix msg.message
 		# TODO: fetch rest of line here as well...
 		# ----  clean up this part.

File modules/05-commands.coffee

 				
 				supDigitsMap = ("\u2070\u00b9\u00b2\u00b3\u2074\u2075\u2076" +
 							    "\u2077\u2078\u2079").split ""
-				supDigitsMap["-"] = "\207b"
+				supDigitsMap["-"] = "\u207b"
 				
 				beautify = (str) ->
 					str = str.replace /<sup>([^<]+)<\/sup>/g, (_, value) ->

File modules/05-interpreters.js

   }
 })
 
-function getExecResponder(self, data) {
+// Helper that collects the output
+function getExecResponder(options, self, data) {
+  if (!options) {
+    // no `options`--shift arguments to the left
+    data = arguments[1]
+    self = arguments[0]
+    options = {}
+  }
+
   return function(err, stdout, stderr) {
+    if (options.firstLineIsInput) {
+      if (stdout.indexOf(options.input + "\n") == 0) {
+        stdout = stdout.slice(options.input.length + 1)
+      }
+    }
+
     if (err) {
       if (err.signal == 'SIGKILL') {
         self.send(data.target, data.nick + ": Timed out.")
           trailing = " [stdout: " + escapeString(stdout) + "]"
         }
 
+        console.warn("-- interpreter failed --")
+        console.warn("stdout:", stdout)
+        console.warn("stderr:", stderr)
+        console.warn("  ---")
         self.send(data.target, util.format("%s: (returned %s): %s%s",
                   data.nick, err.code, escapeString(stderr), trailing))
       }
     } else {
+      console.log("stdout:", stdout)
+      console.log("stderr:", stderr)
       self.send(data.target, util.format("%s: %s", data.nick,
                 escapeString(stdout)))
     }
 }
 
 // Assumes stuff/migol.jar exists and is a proper executable Migol interpreter.
+/*
 bot.triggers.command.add('migol', {
   help: "Evaluates a Migol program.",
   exec: function(data) {
     })
   }
 })
+*/
 
 // Assumes stuff/z.jar exists and is a proper executable Z interpreter.
 bot.triggers.command.add('z', {
       , options = {timeout:30000, killSignal:'SIGKILL'}
       , suffix  = Math.random()
       , tmpfile = '/tmp/tmp-eldis4-z-' + suffix
-    //  , command = "java -jar stuff/Z3.jar -w '[' ']' '" + tmpfile + "'"
-      , command = "stuff/z/runner.sh '" + tmpfile + "' '" + suffix + "'"
+
+   // , command = "java -jar stuff/Z3.jar -w '[' ']' '" + tmpfile + "'"
+   // , command = "stuff/z/runner.sh '" + tmpfile + "' '" + suffix + "'"
+
+      , runner  = 'stuff/z/runner.sh'
+      , args    = [tmpfile, suffix]
+
 
     fs.writeFile(tmpfile, data.line, function(err) {
       if (err) {
         return
       }
 
-      child_process.exec(command, options, getExecResponder(self, data))
+      child_process.execFile(runner, args, options, getExecResponder({}, self, data))
+   // child_process.exec(command, options, getExecResponder(self, data))
     })
   }
 })
+
+
+//-- '~hs' for haskell eval -------------------------------
+bot.triggers.command.add('hs', {
+  help: "Evaluates a Haskell snippet.",
+  exec: function(data) {
+    var self    = this
+      , options = {timeout:8000, killSignal:'SIGKILL'}
+      , suffix  = Math.random()
+   // , escaped = shellEscape(shellEscape(data.line)) // note double-escape!
+   // , command = "stuff/hs/runner.sh '" + escaped + "'"
+
+      , input   = data.line
+
+      , runner  = 'stuff/hs/mueval'
+      , args    = [ '--inferred-type', '--time-limit=3', '--no-imports'
+                  , '--load-file=stuff/hs/Environment.hs', '-e', input ]
+
+//${mueval} --inferred-type --time-limit="$timeout" --no-imports \
+//          --load-file="$library" -e "${irc_input}"
+
+      , responderOpts = { input:            input
+                        , firstLineIsInput: true }
+
+ // child_process.exec(command, options, getExecResponder(responderOpts, self, data))
+    child_process.execFile(runner, args, options,
+                           getExecResponder(responderOpts, self, data))
+
+    function shellEscape(str) {
+      // foo"bar  => foo"\""bar, which is useful if embedded in a double-quoted
+      // string in shell.
+      return str.replace(/"/g,     '"\\""')
+             // .replace(/[{}$]/g, '\\$&')
+    }
+  }
+})
+

File modules/05-wikipedia/index.js

 bot.triggers.command.add('w', {
   help: "Fetches information from Wikipedia.",
   exec: function(data) {
-    var self = this
+    var self  = this
+      , ident = parseIdent(data.line)
 
-    var title = data.line
-      , subdomain
-
-    // Handle interwiki prefixes; either two- or three-char language code or
-    // one of a number of special prefixes.
-    if (match = title.match(/^([a-z]{2,3}|simple):(.*)/i)) {
-        subdomain= match[1].toLowerCase()
-        title     = match[2]
-    } else {
-        subdomain = 'en'
-    }
+    var title     = ident.title
+      , subdomain = ident.namespace
 
     var encodedTitle = encodeURIComponent(title).replace(/%20/g, '_')
 
     console.log("Fetching %s from %s...", encodedTitle, subdomain)
 
+    // Responds with the given article.
+    function respondWith(tree) {
+      var content  = tree.headings[0].content
+        , result   = stringifier.stringifyArr(content, substitutor)
+        , match    = result.match(/^(?:[^.]|\.(?! ))+\./)
+        , response = match ? match[0] : result
+
+      // We don't want any newlines in the IRC output...
+      response = response.replace(/\n/g, " ")
+
+      // Make sure that the response isn't too long...
+      if (response.length > 200) {
+        response = response.slice(0, 199) + "\u2026" // Ellipsis
+      }
+
+      // Add the wikipedia URL to the response
+      var niceURL = "<http://en.wikipedia.org/wiki/" + encodedTitle + ">"
+      if (niceURL.length > 130) {
+        niceURL = "[URL too long]"
+      }
+      response += "  " + niceURL
+
+      // ...and reply!
+      self.reply("\x03" + response)
+    }
+
     backend.net.get(subdomain + ".wikipedia.org/w/index.php",
       { "action" : "raw"
       , "title"  : encodedTitle
         if (!done) return
         if (err) throw err
 
+        // Check that we actually got data from the server--otherwise, the
+        // article wasn't found.
         if (data.length == 0) {
           self.reply("Article not found.")
           return
         }
 
+        // Parse the data, and make sure that we actually got correct data back
+        // from the parser.  Just a sanity check to catch & report parser bugs.
         var tree = wikiParser.parse(data, true)
         if (!tree.headings || !tree.headings[0]) {
           self.error(new Error("Couldn't parse article! Parser bug?"))
           return
         }
 
-        var content  = tree.headings[0].content
-          , result   = stringifier.stringifyArr(content, substitutor)
-          , match    = result.match(/^(?:[^.]|\.(?! ))+\./)
-          , response = match ? match[0] : result
+        // Check for redirection
+        var redirTarget = getRedirection(tree)
 
-        // Replace newlines
-        response = response.replace(/\n/g, " ")
+        if (redirTarget != null) {
+          var redirEncoded = encodeURIComponent(redirTarget).replace(/%20/g, '_')
 
-        // Make sure that the response isn't too long...
-        if (response.length > 200) {
-          response = response.slice(0, 199) + "\u2026" // Ellipsis
+          backend.net.get(subdomain + ".wikipedia.org/w/index.php",
+            { "action" : "raw"
+            , "title"  : redirEncoded
+            }, function(err, data, done) {
+              if (!done) return
+              if (err) throw err
+
+              // Check that we actually got data from the server--otherwise, the
+              // article wasn't found.
+              if (data.length == 0) {
+                self.reply("Article not found.")
+                return
+              }
+
+              // Parse the data, and make sure that we actually got correct data back
+              // from the parser.  Just a sanity check to catch & report parser bugs.
+              var tree = wikiParser.parse(data, true)
+              if (!tree.headings || !tree.headings[0]) {
+                self.error(new Error("Couldn't parse article! Parser bug?"))
+                return
+              }
+
+              // Check for redirection
+              var redirTarget = getRedirection(tree)
+
+              if (redirTarget != null) {
+                self.error(new Error("Double redirection!"))
+                return
+              }
+
+              // Respond
+              respondWith(tree)
+            })
+
+        } else {
+          // No redirection
+          respondWith(tree)
         }
-
-        // Add the wikipedia URL to the response
-        var niceURL = "<http://en.wikipedia.org/wiki/" + encodedTitle + ">"
-        if (niceURL.length > 130) {
-          niceURL = "[URL too long]"
-        }
-        response += "  " + niceURL
-
-        self.reply("\x03" + response)
       })
 
+    // Parses an ident (either just title, or namespace:title) into its separate
+    // components.  The default namespace is "en".
+    //   For Wikipedia this is useful for interwiki prefixes--two- or three-char
+    // langauge codes or a number of special prefixes.
+    function parseIdent(ident) {
+      var matches
+        , identPat = /^([a-z]{2,3}|simple):(.*)/i
+
+      if (matches = ident.match(identPat)) {
+        return { namespace : matches[1].toLowerCase()
+               , title     : matches[2] }
+
+      } else {
+        return { namespace : "en"
+               , title     : ident }
+      }
+    }
+
+    // Gets the target article of a redirection from a parse tree.
+    function getRedirection(tree) {
+      if ( tree.name == 'root'
+
+        // First child has to be a '#' list
+        && tree.content[0]
+        && tree.content[0].name == 'list'
+        && tree.content[0].type == '#'
+
+        // First child of *that* node has to be a list-node
+        && tree.content[0].content[0]
+        && tree.content[0].content[0].name == 'list-node'
+
+        // First child of *that* node has to be a string "REDIRECT",
+        // followed by a wikilink which points to the redirection target.
+        && tree.content[0].content[0].content[0].match(/^\s*REDIRECT\s*$/i)
+        && tree.content[0].content[0].content[1].name == 'wikilink'
+         ) {
+        var target = tree.content[0].content[0].content[1].target
+        return target
+
+      } else {
+        // No redirection found--signal this by returning null
+        return null
+      }
+    }
+
+    // Encodes a title to be part of a mediawiki URL
     function encodeTitle(str) {
       return encodeURIComponent(str).replace(/%20/g, '_')
-                               .replace(/[()]/g, urlEscape)
+                                    .replace(/[()]/g, urlEscape)
 
       function urlEscape(chr) {
         var hex = chr.charCodeAt().toString(16)

File src/eldis4.js

-
 var util   = require('util')
   , vm     = require('vm')
   , fs     = require('fs')
   , events = require('events')
-
-  , fomatto = require('fomatto')
-  , bot     = require('./bot')
-
-//{EventEmitter} = require 'events'
-//{Formatter}    = require 'fomatto'
-//{Bot, backend} = require '../lib/bot'
+  , bot    = require('./bot')
 
 // Shared string formatter
-var format = new fomatto.Formatter()
 
 function Eldis4() {
 	bot.Bot.call(this)
 			, bot        : eldis4
 			, module     : moduleObj
 			, backend    : bot.backend
-			, format     : format
 			}
 		
 		eldis4.modules.contexts[name] = ctx
 			if (matches = filename.match(/^(.*)\.js$/)) {
 				eldis4.modules.load(matches[1], false)
 
-			} else if (filename.match(/^[^.]+$/)) {
+			} else if (filename.match(/^[^.]*[^.~]$/)) {
 				eldis4.modules.load(filename, true)
 			}
 		})

File stuff/hs/Environment.hs

+module Environment where
+
+import           Prelude
+import           Control.Applicative
+import           Control.Arrow
+import qualified Control.Exception as E
+import           Control.Monad
+import           Control.Monad.Fix
+import           Control.Monad.Reader
+import           Control.Monad.State
+import           Control.Monad.Trans
+import           Control.Monad.Writer
+--import           Control.Parallel
+import           Data.Bits
+import qualified Data.ByteString.Lazy as B
+import           Data.Char
+import           Data.Complex
+import           Data.Function
+import           Data.Functor
+import           Data.List
+import           Data.Map (Map)
+import qualified Data.Map as Map
+import           Data.Maybe
+import           Data.Monoid
+import           Data.Ratio
+import           Data.Set (Set)
+import qualified Data.Set as Set
+import           Data.Typeable
+import           Data.Word
+import           Numeric
+--import           System
+--import           System.IO
+--import           System.Process
+--import           System.Random
+import           Text.Printf
+--import           Text.Regex
+import           Text.Show.Functions
+
+instance Typeable a => Show (IO a) where
+  show io = '<' : show (typeOf io) ++ ">"
+
+data PutStr = PutStr String
+
+instance Show PutStr where
+  show (PutStr s) = s

File stuff/hs/runner.sh

+#!/bin/sh
+
+# config vars
+#basedir="/tmp/eldis4/z-eval/"
+library="stuff/hs/Environment.hs"
+mueval="stuff/hs/mueval"
+#whitelist="import unpack cast random math stdout inspect"
+timeout=3
+
+
+# Begin program..
+irc_input="$1"
+
+# Run it!
+${mueval} --inferred-type --time-limit="$timeout" --no-imports \
+          --load-file="$library" -e "${irc_input}"