Skip to content

Instantly share code, notes, and snippets.

@hiredman
Created November 22, 2008 03:18

Revisions

  1. Kevin Downey revised this gist Dec 22, 2008. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -321,6 +321,7 @@
    java.io.StringReader.
    java.io.PushbackReader.
    read)
    ; http://malde.org/~ketil/Hazard_lambda.svg
    thunk1 #(eval form)
    thunk2 #(binding [*ns* (find-or-create-ns 'foo)
    *out* (java.io.StringWriter.)]
  2. Kevin Downey revised this gist Dec 20, 2008. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -398,7 +398,7 @@
    (dosync (commute url
    assoc
    (re-find url-regex (:message pojo)) (java.util.Date.)))
    (prn (:sender pojo) "> " (:message pojo)))
    (prn (str (:sender pojo) ", " (:message pojo))))

    (defmethod responder :literal [pojo]
    (let [q (.replaceFirst (:message pojo) (str "^" nick ": literal ") "")]
  3. Kevin Downey revised this gist Dec 20, 2008. 1 changed file with 60 additions and 40 deletions.
    100 changes: 60 additions & 40 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -17,6 +17,7 @@
    (:import (org.jibble.pircbot PircBot)
    (java.util.concurrent FutureTask TimeUnit TimeoutException)))

    ;; set up the namespace for the sandbox
    (binding [*ns* (create-ns 'foo)]
    (clojure.core/refer 'clojure.core)
    (import '(java.util Date)))
    @@ -25,9 +26,9 @@
    (def channel "#clojure")
    (def net "chat.us.freenode.net")

    (def *bot*)
    (def *bot*) ;this will be the bot object

    (def *execution-timeout* 10)
    (def *execution-timeout* 10) ;time out for sandbox exec

    (def start-date (java.util.Date.))

    @@ -121,11 +122,15 @@
    (defmacro sendMsg-who [pojo msg]
    `(sendMsg (:this ~pojo) (who ~pojo) ~msg))

    (defn cache-svn-rev [rev]
    (defn cache-svn-rev
    "puts an svn rev into the cache"
    [rev]
    (dosync (commute svn-rev-cache conj rev)))


    (defn term-lists [msg]
    (defn term-lists
    "generates permutions of the words in string"
    [msg]
    (let [x (re-seq #"\w+" msg)
    ignore #(not (contains? #{"a" "where" "what" "is" "who" "are" (str nick ": ")} %))]
    (filter ignore
    @@ -136,16 +141,24 @@
    (map #(reverse (filter identity (inits (drop % x))))
    (take (count x) (iterate inc 0))))))))

    (defn rlookup [terms]
    (defn rlookup
    "look up terms from a seq until you find a defi"
    [terms]
    (loop [t terms]
    (if t
    (if (@dict-is (first t))
    (first t)
    (recur (rest t))))))

    (defn fuzzy-lookup [message]
    (defn fuzzy-lookup
    "look up based on permutation"
    [message]
    (rlookup (term-lists message)))

    (defn fuzzy-key-lookup
    "look up based on match part of a term"
    [term]
    (randth (filter #(when (> (.lastIndexOf % term) -1) true) (keys @dict-is))))

    (defn who
    "am I talking to someonein a privmsg, or in a channel?"
    @@ -254,12 +267,6 @@
    (try (f) (catch Exception e (str :EXCEPTION (.getMessage e)))))
    ;;;;;;;;;;;

    (defmacro thk-bind [binds & forms]
    `(fn []
    (binding ~binds
    ~@forms)))


    (defn sandbox [func]
    (let [perms (java.security.Permissions.)
    domain (java.security.ProtectionDomain.
    @@ -299,27 +306,31 @@

    (defmulti #^{:doc "currently all messages are routed though this function"} responder dispatch)

    (defn naughty-forms [string])
    (defn naughty-forms? [strang]
    (let [nf #{"catch" "finally" "clojure.asm" "hiredman.clojurebot"}]
    (some #(not= -1 %) (map #(.lastIndexOf strang %) nf))))

    (defn find-or-create-ns [n]
    (if-let [s (find-ns n)] s (create-ns n)))

    (defmethod responder :code-sandbox [pojo]
    (println (:message pojo))
    (let [form (-> (.replaceAll (:message pojo) "^," "")
    java.io.StringReader.
    java.io.PushbackReader.
    read)
    thunk1 #(eval form)
    thunk2 (thk-bind [*ns* (if-let [n (find-ns 'foo)]
    n
    (create-ns 'foo))
    *out* (java.io.StringWriter.)]
    [(thunk1) (str *out*)])
    thunk3 #(sandbox thunk2)]
    (let [o (thunk-timeout thunk3 *execution-timeout*)]
    (if (vector? o)
    (let [[result out] o]
    (sendMsg-who pojo out)
    (sendMsg-who pojo result))
    (sendMsg-who pojo o)))))
    (println (str (:sender pojo) " " (:message pojo)))
    (if (and (not (naughty-forms? (:message pojo))) (not= "karmazilla" (:sender pojo)))
    (let [_ (println "accepted")
    form (-> (.replaceAll (:message pojo) "^," "")
    java.io.StringReader.
    java.io.PushbackReader.
    read)
    thunk1 #(eval form)
    thunk2 #(binding [*ns* (find-or-create-ns 'foo)
    *out* (java.io.StringWriter.)]
    [(wrap-exceptions thunk1) (str *out*)])
    thunk3 #(sandbox thunk2)]
    (let [o (thunk-timeout thunk3 *execution-timeout*)]
    (if (vector? o)
    (doseq [i (reverse o)] (sendMsg-who pojo i))
    (sendMsg-who pojo o))))
    (sendMsg-who pojo (befuddled))))


    (defmethod responder :math [pojo]
    @@ -347,27 +358,36 @@
    (is! term defi))
    (sendMsg-who pojo (ok))))

    (defn prep-reply [sender term defi]
    (.replaceAll (if (re-find #"^<reply>" defi)
    (.trim (.replaceFirst (str defi) "^<reply>" ""))
    (str term " is " defi))
    "#who"
    sender))

    (defmethod responder :lookup [pojo]
    (let [msg (d?op (.trim (.replaceFirst (:message pojo) (str "^" nick ":") "")))
    result (what-is msg)]
    (cond
    result
    result,
    (sendMsg-who pojo
    (.replaceAll (if (re-find #"^<reply>" result)
    (.trim (.replaceFirst (str result) "^<reply>" ""))
    (str msg " is " result))
    "#who"
    (:sender pojo)))
    (fuzzy-lookup msg)

    (fuzzy-lookup msg),
    (let [x (fuzzy-lookup msg)
    r (what-is x)]
    (sendMsg-who pojo
    (.replaceAll (if (re-find #"^<reply>" r)
    (.trim (.replaceFirst (str r) "^<reply>" ""))
    (str x " is " r))
    "#who"
    (:sender pojo))))
    :else
    (sendMsg-who pojo (prep-reply (:sender pojo) term defi)))

    (fuzzy-key-lookup msg),
    (let [term (fuzzy-key-lookup msg)
    defi (what-is term)]
    (sendMsg-who pojo (prep-reply (:sender pojo) term defi)))

    :else,
    (sendMsg-who pojo (befuddled)))))


  4. Kevin Downey revised this gist Dec 18, 2008. 1 changed file with 45 additions and 16 deletions.
    61 changes: 45 additions & 16 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -11,20 +11,26 @@
    ;; factoids. Addressing is in optional mode.


    ;java -ms32m -mx200m
    ;java -server -ms16m -mx64m -Xss128m

    (ns hiredman.clojurebot
    (:import (org.jibble.pircbot PircBot)))
    (:import (org.jibble.pircbot PircBot)
    (java.util.concurrent FutureTask TimeUnit TimeoutException)))

    (binding [*ns* (create-ns 'foo)]
    (clojure.core/refer 'clojure.core))
    (clojure.core/refer 'clojure.core)
    (import '(java.util Date)))

    (def nick "clojurebot")
    (def channel "#clojure")
    (def net "chat.us.freenode.net")

    (def *bot*)

    (def *execution-timeout* 10)

    (def start-date (java.util.Date.))

    ;; dictionaries for storing relationships
    ;; 'are' dict is not used right now.
    (def dict-is (ref {}))
    @@ -232,7 +238,28 @@

    (defn enable-security-manager []
    (System/setSecurityManager (SecurityManager.)))

    ;;;;;;;; Chousuke
    (defn thunk-timeout [thunk seconds]
    (let [task (FutureTask. thunk)
    thr (Thread. task)]
    (try
    (.start thr)
    (.get task seconds TimeUnit/SECONDS)
    (catch TimeoutException e
    (.cancel task true)
    (.stop thr (Exception. "Thread stopped!")) "Execution timed out"))))

    (defn wrap-exceptions [f]
    (try (f) (catch Exception e (str :EXCEPTION (.getMessage e)))))
    ;;;;;;;;;;;

    (defmacro thk-bind [binds & forms]
    `(fn []
    (binding ~binds
    ~@forms)))


    (defn sandbox [func]
    (let [perms (java.security.Permissions.)
    domain (java.security.ProtectionDomain.
    @@ -274,24 +301,26 @@

    (defn naughty-forms [string])

    (defn sb-in-ns [form n]
    (binding [*ns* (if-let [n (find-ns n)]
    n
    (create-ns n))]
    (sandbox form)))

    (defmethod responder :code-sandbox [pojo]
    (println (:message pojo))
    (let [form (-> (.replaceAll (:message pojo) "^," "")
    java.io.StringReader.
    java.io.PushbackReader.
    read)]
    (binding [*out* (java.io.StringWriter.)]
    (let [o (sb-in-ns #(eval form) 'foo)
    osw (str *out*)]
    (when-not (= osw "")
    (sendMsg-who pojo osw))
    (sendMsg-who pojo (str o))))))
    read)
    thunk1 #(eval form)
    thunk2 (thk-bind [*ns* (if-let [n (find-ns 'foo)]
    n
    (create-ns 'foo))
    *out* (java.io.StringWriter.)]
    [(thunk1) (str *out*)])
    thunk3 #(sandbox thunk2)]
    (let [o (thunk-timeout thunk3 *execution-timeout*)]
    (if (vector? o)
    (let [[result out] o]
    (sendMsg-who pojo out)
    (sendMsg-who pojo result))
    (sendMsg-who pojo o)))))


    (defmethod responder :math [pojo]
    (let [[op & num-strings] (re-seq #"[\+\/\*\-0-9]+" (:message pojo))
  5. Kevin Downey revised this gist Dec 18, 2008. 1 changed file with 50 additions and 2 deletions.
    52 changes: 50 additions & 2 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -3,10 +3,22 @@
    ;; "Though a program be but three lines long, someday it will have to be
    ;; maintained."
    ;;
    ;;
    ;; [01:30] <uhelp> lexxan: Since Mon May 2 17:22:46 2005,
    ;; there have been 0 modifications and 0 questions.
    ;; I have been awake for 7 minutes and 36 seconds
    ;; this session, and currently reference 19
    ;; factoids. Addressing is in optional mode.


    ;java -ms32m -mx200m

    (ns hiredman.clojurebot
    (:import (org.jibble.pircbot PircBot)))

    (binding [*ns* (create-ns 'foo)]
    (clojure.core/refer 'clojure.core))

    (def nick "clojurebot")
    (def channel "#clojure")
    (def net "chat.us.freenode.net")
    @@ -143,7 +155,7 @@
    pojo))


    (def svn-command "svn -v --xml --limit 5 log https://clojure.svn.sourceforge.net/svnroot/clojure")
    (def svn-command "svn -v --xml --limit 5 log http://clojure.googlecode.com/svn/")

    (defn svn-summaries
    "takes output of clojure.xml/parse on svn's xml log, returns
    @@ -218,6 +230,20 @@
    (when-let [f (@dict-is term)]
    (if (vector? f) (randth f) f)))

    (defn enable-security-manager []
    (System/setSecurityManager (SecurityManager.)))

    (defn sandbox [func]
    (let [perms (java.security.Permissions.)
    domain (java.security.ProtectionDomain.
    (java.security.CodeSource. nil
    (cast java.security.cert.Certificate nil))
    perms)
    context (java.security.AccessControlContext. (into-array [domain]))
    pA (proxy [java.security.PrivilegedAction] [] (run [] (func)))]
    (java.security.AccessController/doPrivileged
    pA context)))


    (defn dispatch
    "this function does dispatch for responder"
    @@ -246,6 +272,27 @@

    (defmulti #^{:doc "currently all messages are routed though this function"} responder dispatch)

    (defn naughty-forms [string])

    (defn sb-in-ns [form n]
    (binding [*ns* (if-let [n (find-ns n)]
    n
    (create-ns n))]
    (sandbox form)))

    (defmethod responder :code-sandbox [pojo]
    (println (:message pojo))
    (let [form (-> (.replaceAll (:message pojo) "^," "")
    java.io.StringReader.
    java.io.PushbackReader.
    read)]
    (binding [*out* (java.io.StringWriter.)]
    (let [o (sb-in-ns #(eval form) 'foo)
    osw (str *out*)]
    (when-not (= osw "")
    (sendMsg-who pojo osw))
    (sendMsg-who pojo (str o))))))

    (defmethod responder :math [pojo]
    (let [[op & num-strings] (re-seq #"[\+\/\*\-0-9]+" (:message pojo))
    nums (map #(Integer/parseInt %) num-strings)]
    @@ -336,7 +383,6 @@
    (defn pircbot []
    (proxy [PircBot] []
    (onJoin [channel sender login hostname]
    (prn :foo)
    (user-watch))
    (onMessage [channel sender login hostname message]
    (handleMessage this channel sender login hostname message))
    @@ -387,8 +433,10 @@
    (send-off *agent* this))))

    (def *bot* (pircbot))
    (enable-security-manager)
    (.connect *bot* net)
    (.changeNick *bot* nick)
    (.joinChannel *bot* channel)
    (load-dicts)
    (svn-notifier-thread)
    (dump-thread)
  6. Kevin Downey revised this gist Dec 16, 2008. 1 changed file with 16 additions and 4 deletions.
    20 changes: 16 additions & 4 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -150,7 +150,7 @@
    a vector of [rev-number commit-message]"
    [tag-map]
    (map (fn [x]
    [(.parseInt Integer (:revision (:attrs x)))
    [(Integer/parseInt (:revision (:attrs x)))
    (first
    (:content
    (first
    @@ -159,7 +159,7 @@
    (:content tag-map)))

    (defn get-last-svn-rev []
    (.parseInt Integer (@dict-is "latest")))
    (Integer/parseInt (@dict-is "latest")))

    (defn filter-newer-svn-revs [revs]
    (filter #(> (first %) (get-last-svn-rev))
    @@ -225,6 +225,8 @@
    (cond
    (doc-lookup? (:message pojo))
    :doc-lookup
    (re-find #"^,\(" (:message pojo))
    :code-sandbox
    (and (addressed? pojo) (re-find #"how much do you know?" (:message pojo)))
    :know
    (and (addressed? pojo) (re-find #" is " (:message pojo)) (not= \? (last (:message pojo))))
    @@ -246,7 +248,7 @@

    (defmethod responder :math [pojo]
    (let [[op & num-strings] (re-seq #"[\+\/\*\-0-9]+" (:message pojo))
    nums (map #(.parseInt java.lang.Integer %) num-strings)]
    nums (map #(Integer/parseInt %) num-strings)]
    (sendMsg-who pojo
    (let [out (apply (find-var (symbol "clojure.core" op)) nums)]
    (if (> out 4)
    @@ -307,7 +309,7 @@
    (prn q)))

    (defmethod responder :svn-rev-lookup [pojo]
    (let [r (.parseInt Integer (re-find #"[0-9]+" (:message pojo)))
    (let [r (Integer/parseInt (re-find #"[0-9]+" (:message pojo)))
    t (filter #(= (first %) r) @svn-rev-cache)]
    (if (not= 0 (count t))
    (send-svn-revs t)
    @@ -317,6 +319,13 @@
    (send-svn-revs b)
    (dorun (map cache-svn-rev b)))))))

    (defn user-watch []
    (let [cur (count (.getUsers *bot* "#clojure"))
    pre (Integer/parseInt (what-is "max people"))]
    (when (> cur pre)
    (is! "max people" (str cur)))))


    (defn handleMessage [this channel sender login hostname message]
    (responder (struct junks this channel sender login
    hostname message)))
    @@ -326,6 +335,9 @@

    (defn pircbot []
    (proxy [PircBot] []
    (onJoin [channel sender login hostname]
    (prn :foo)
    (user-watch))
    (onMessage [channel sender login hostname message]
    (handleMessage this channel sender login hostname message))
    (onPrivateMessage [sender login hostname message]
  7. Kevin Downey revised this gist Dec 14, 2008. 1 changed file with 14 additions and 60 deletions.
    74 changes: 14 additions & 60 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -195,26 +195,9 @@
    [cmd]
    (.getInputStream (.. Runtime getRuntime (exec cmd))))

    (defmulti define (fn [pojo term defi]
    (if (and (@dict-is term) (re-find #" also " (:message pojo)))
    :add
    :new)))

    (defmethod define :new [pojo term defi]
    (dosync
    (commute dict-is assoc (.trim term) (.trim defi)))
    (sendMsg-who pojo (ok)))

    (defmethod define :add [pojo term defi]
    (let [old (@dict-is term)
    ne (if (vector? old)
    (conj old defi)
    [old defi])]
    (dosync
    (commute dict-is assoc term ne))
    (sendMsg-who pojo (ok))))

    (defn is [term defi]
    (defn is
    "add a new definition to a term in dict-is"
    [term defi]
    (if (@dict-is term)
    (let [old (@dict-is term)
    v (if (vector? old)
    @@ -223,15 +206,17 @@
    (dosync (commute dict-is assoc term v)))
    (dosync (commute dict-is assoc term defi))))

    (defn is! [term defi]
    (defn is!
    "define a term in dict-is, overwriting anything that was there"
    [term defi]
    (dosync (commute dict-is assoc term defi)))


    (defn what-is [term]
    (defn what-is
    "looks up a term in @dict-is"
    [term]
    (when-let [f (@dict-is term)]
    (if (vector? f)
    (randth f)
    f)))
    (if (vector? f) (randth f) f)))


    (defn dispatch
    @@ -274,48 +259,15 @@
    5
    (dec (count (:message pojo)))))))

    ;;(defmethod responder :define-is [pojo]
    ;; (let [a (.trim (.replaceFirst (:message pojo) "^clojurebot:" " "))
    ;; term (term a)
    ;; defi (.replaceFirst (strip-is a) "^also " "")]
    ;; (define pojo term defi)))

    (defmethod responder :define-is [pojo]
    (let [a (.trim (.replaceFirst (:message pojo) "^clojurebot:" " "))
    term (term a)
    x (strip-is a)
    defi (.replaceFirst x "^also " "")]
    (if (re-find #"^also " x)
    (is term defi)
    (is! term defi))))


    ;;(defmethod responder :lookup [pojo]
    ;; (let [msg (d?op (.trim (.replaceFirst (:message pojo) (str "^" nick ":") "")))
    ;; result ((deref dict-is) msg)
    ;; result (if (vector? result)
    ;; (randth result)
    ;; result)]
    ;; (cond
    ;; result
    ;; (sendMsg-who pojo
    ;; (.replaceAll (if (re-find #"^<reply>" result)
    ;; (.trim (.replaceFirst (str result) "^<reply>" ""))
    ;; (str msg " is " result))
    ;; "#who"
    ;; (:sender pojo)))
    ;; (fuzzy-lookup msg)
    ;; (let [x (fuzzy-lookup msg)
    ;; r (@dict-is x)
    ;; r (if (vector? r) (randth r) r)]
    ;; (sendMsg-who pojo
    ;; (.replaceAll (if (re-find #"^<reply>" r)
    ;; (.trim (.replaceFirst (str r) "^<reply>" ""))
    ;; (str x " is " r))
    ;; "#who"
    ;; (:sender pojo))))
    ;; :else
    ;; (sendMsg-who pojo (befuddled)))))
    (is! term defi))
    (sendMsg-who pojo (ok))))

    (defmethod responder :lookup [pojo]
    (let [msg (d?op (.trim (.replaceFirst (:message pojo) (str "^" nick ":") "")))
    @@ -426,3 +378,5 @@
    (.connect *bot* net)
    (.changeNick *bot* nick)
    (.joinChannel *bot* channel)
    (load-dicts)
    (svn-notifier-thread)
  8. Kevin Downey revised this gist Dec 10, 2008. 1 changed file with 79 additions and 14 deletions.
    93 changes: 79 additions & 14 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -21,6 +21,8 @@
    ;url is for storing urls, must figure out something to do with this
    (def url (ref {}))

    (def svn-rev-cache (ref []))

    (def url-regex #"[A-Za-z]+://[^ ^/]+\.[^ ^/]+[^ ]+")

    ;; this struct is used to pass around messages
    @@ -101,6 +103,9 @@
    (defmacro sendMsg-who [pojo msg]
    `(sendMsg (:this ~pojo) (who ~pojo) ~msg))

    (defn cache-svn-rev [rev]
    (dosync (commute svn-rev-cache conj rev)))


    (defn term-lists [msg]
    (let [x (re-seq #"\w+" msg)
    @@ -208,6 +213,26 @@
    (dosync
    (commute dict-is assoc term ne))
    (sendMsg-who pojo (ok))))

    (defn is [term defi]
    (if (@dict-is term)
    (let [old (@dict-is term)
    v (if (vector? old)
    (conj old defi)
    [old defi])]
    (dosync (commute dict-is assoc term v)))
    (dosync (commute dict-is assoc term defi))))

    (defn is! [term defi]
    (dosync (commute dict-is assoc term defi)))


    (defn what-is [term]
    (when-let [f (@dict-is term)]
    (if (vector? f)
    (randth f)
    f)))


    (defn dispatch
    "this function does dispatch for responder"
    @@ -249,18 +274,52 @@
    5
    (dec (count (:message pojo)))))))

    ;;(defmethod responder :define-is [pojo]
    ;; (let [a (.trim (.replaceFirst (:message pojo) "^clojurebot:" " "))
    ;; term (term a)
    ;; defi (.replaceFirst (strip-is a) "^also " "")]
    ;; (define pojo term defi)))

    (defmethod responder :define-is [pojo]
    (let [a (.trim (.replaceFirst (:message pojo) "^clojurebot:" " "))
    term (term a)
    defi (.replaceFirst (strip-is a) "^also " "")]
    (define pojo term defi)))
    x (strip-is a)
    defi (.replaceFirst x "^also " "")]
    (if (re-find #"^also " x)
    (is term defi)
    (is! term defi))))


    ;;(defmethod responder :lookup [pojo]
    ;; (let [msg (d?op (.trim (.replaceFirst (:message pojo) (str "^" nick ":") "")))
    ;; result ((deref dict-is) msg)
    ;; result (if (vector? result)
    ;; (randth result)
    ;; result)]
    ;; (cond
    ;; result
    ;; (sendMsg-who pojo
    ;; (.replaceAll (if (re-find #"^<reply>" result)
    ;; (.trim (.replaceFirst (str result) "^<reply>" ""))
    ;; (str msg " is " result))
    ;; "#who"
    ;; (:sender pojo)))
    ;; (fuzzy-lookup msg)
    ;; (let [x (fuzzy-lookup msg)
    ;; r (@dict-is x)
    ;; r (if (vector? r) (randth r) r)]
    ;; (sendMsg-who pojo
    ;; (.replaceAll (if (re-find #"^<reply>" r)
    ;; (.trim (.replaceFirst (str r) "^<reply>" ""))
    ;; (str x " is " r))
    ;; "#who"
    ;; (:sender pojo))))
    ;; :else
    ;; (sendMsg-who pojo (befuddled)))))

    (defmethod responder :lookup [pojo]
    (let [msg (d?op (.trim (.replaceFirst (:message pojo) (str "^" nick ":") "")))
    result ((deref dict-is) msg)
    result (if (vector? result)
    (randth result)
    result)]
    result (what-is msg)]
    (cond
    result
    (sendMsg-who pojo
    @@ -271,8 +330,7 @@
    (:sender pojo)))
    (fuzzy-lookup msg)
    (let [x (fuzzy-lookup msg)
    r (@dict-is x)
    r (if (vector? r) (randth r) r)]
    r (what-is x)]
    (sendMsg-who pojo
    (.replaceAll (if (re-find #"^<reply>" r)
    (.trim (.replaceFirst (str r) "^<reply>" ""))
    @@ -282,6 +340,7 @@
    :else
    (sendMsg-who pojo (befuddled)))))


    (defmethod responder :know [pojo]
    (sendMsg-who pojo (str "I know " (+ (count (deref dict-is)) (count (deref dict-are))) " things")))

    @@ -296,9 +355,15 @@
    (prn q)))

    (defmethod responder :svn-rev-lookup [pojo]
    (let [r (re-find #"[0-9]+" (:message pojo))
    cmd (.replace svn-command "--limit 5" (str "-r " r))]
    (send-svn-revs (svn-summaries (clojure.xml/parse (svn-xml-stream cmd))))))
    (let [r (.parseInt Integer (re-find #"[0-9]+" (:message pojo)))
    t (filter #(= (first %) r) @svn-rev-cache)]
    (if (not= 0 (count t))
    (send-svn-revs t)
    (let [cmd (.replace svn-command "--limit 5" (str "-r " r))
    b (svn-summaries (clojure.xml/parse (svn-xml-stream cmd)))]
    (do
    (send-svn-revs b)
    (dorun (map cache-svn-rev b)))))))

    (defn handleMessage [this channel sender login hostname message]
    (responder (struct junks this channel sender login
    @@ -326,9 +391,9 @@
    (defn svn-notifier-thread []
    (send-off (agent nil)
    (fn this [& _]
    (svn-message
    (svn-summaries
    (clojure.xml/parse (svn-xml-stream svn-command))))
    (let [m (svn-summaries (clojure.xml/parse (svn-xml-stream svn-command)))]
    (svn-message m)
    (map cache-svn-rev m))
    (Thread/sleep (* 5 60000))
    (send-off *agent* this))))

  9. Kevin Downey revised this gist Dec 8, 2008. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -60,7 +60,7 @@

    (defn term
    "returns the part of a string before the first occurence
    of \"is"""
    of \"is\""
    [string]
    (first (.split string " is ")))

    @@ -298,7 +298,7 @@
    (defmethod responder :svn-rev-lookup [pojo]
    (let [r (re-find #"[0-9]+" (:message pojo))
    cmd (.replace svn-command "--limit 5" (str "-r " r))]
    (prn (svn-summaries (clojure.xml/parse (svn-xml-stream cmd))))))
    (send-svn-revs (svn-summaries (clojure.xml/parse (svn-xml-stream cmd))))))

    (defn handleMessage [this channel sender login hostname message]
    (responder (struct junks this channel sender login
  10. Kevin Downey revised this gist Dec 8, 2008. 1 changed file with 57 additions and 55 deletions.
    112 changes: 57 additions & 55 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -137,6 +137,59 @@
    (when (or (re-find #"^clojurebot:" (:message pojo)) (nil? (:channel pojo)))
    pojo))


    (def svn-command "svn -v --xml --limit 5 log https://clojure.svn.sourceforge.net/svnroot/clojure")

    (defn svn-summaries
    "takes output of clojure.xml/parse on svn's xml log, returns
    a vector of [rev-number commit-message]"
    [tag-map]
    (map (fn [x]
    [(.parseInt Integer (:revision (:attrs x)))
    (first
    (:content
    (first
    (filter #(= (:tag %) :msg)
    (:content x)))))])
    (:content tag-map)))

    (defn get-last-svn-rev []
    (.parseInt Integer (@dict-is "latest")))

    (defn filter-newer-svn-revs [revs]
    (filter #(> (first %) (get-last-svn-rev))
    revs))

    (defn send-svn-revs [revs]
    (dorun
    (map #(sendMsg *bot*
    channel
    (str "svn rev " (first %) "; " (last %)))
    revs)))


    (defn svn-message
    "takes a seq of vectors containing [rev msg]
    sends out messages about new revs. updates \"latest\"
    to latest rev"
    [summaries]
    (let [newrevs (filter-newer-svn-revs (reverse summaries))]
    (when newrevs
    (do
    (send-svn-revs newrevs)
    (dosync
    (commute dict-is
    assoc
    "latest"
    (str (first (first summaries)))))
    ;don't want to see the whole hash in the repl
    nil))))

    (defn svn-xml-stream
    "get the xml stream from svn"
    [cmd]
    (.getInputStream (.. Runtime getRuntime (exec cmd))))

    (defmulti define (fn [pojo term defi]
    (if (and (@dict-is term) (re-find #" also " (:message pojo)))
    :add
    @@ -243,7 +296,9 @@
    (prn q)))

    (defmethod responder :svn-rev-lookup [pojo]
    (prn (re-find #"[0-9]+" (:message pojo))))
    (let [r (re-find #"[0-9]+" (:message pojo))
    cmd (.replace svn-command "--limit 5" (str "-r " r))]
    (prn (svn-summaries (clojure.xml/parse (svn-xml-stream cmd))))))

    (defn handleMessage [this channel sender login hostname message]
    (responder (struct junks this channel sender login
    @@ -268,65 +323,12 @@
    (.close *out*)))
    [["is" dict-is] ["are" dict-are]]))

    (def svn-command "svn -v --xml --limit 5 log https://clojure.svn.sourceforge.net/svnroot/clojure")

    (defn svn-summaries
    "takes output of clojure.xml/parse on svn's xml log, returns
    a vector of [rev-number commit-message]"
    [tag-map]
    (map (fn [x]
    [(.parseInt Integer (:revision (:attrs x)))
    (first
    (:content
    (first
    (filter #(= (:tag %) :msg)
    (:content x)))))])
    (:content tag-map)))

    (defn get-last-svn-rev []
    (.parseInt Integer (@dict-is "latest")))

    (defn filter-newer-svn-revs [revs]
    (filter #(> (first %) (get-last-svn-rev))
    revs))

    (defn send-svn-revs [revs]
    (dorun
    (map #(sendMsg *bot*
    channel
    (str "svn rev " (first %) "; " (last %)))
    revs)))


    (defn svn-message
    "takes a seq of vectors containing [rev msg]
    sends out messages about new revs. updates \"latest\"
    to latest rev"
    [summaries]
    (let [newrevs (filter-newer-svn-revs (reverse summaries))]
    (when newrevs
    (do
    (send-svn-revs newrevs)
    (dosync
    (commute dict-is
    assoc
    "latest"
    (str (first (first summaries)))))
    ;don't want to see the whole hash in the repl
    nil))))


    (defn svn-xml-stream
    "get the xml stream from svn"
    []
    (.getInputStream (.. Runtime getRuntime (exec svn-command))))

    (defn svn-notifier-thread []
    (send-off (agent nil)
    (fn this [& _]
    (svn-message
    (svn-summaries
    (clojure.xml/parse (svn-xml-stream))))
    (clojure.xml/parse (svn-xml-stream svn-command))))
    (Thread/sleep (* 5 60000))
    (send-off *agent* this))))

  11. Kevin Downey revised this gist Dec 8, 2008. 1 changed file with 5 additions and 0 deletions.
    5 changes: 5 additions & 0 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -170,6 +170,8 @@
    :literal
    (re-find #"^\([\+ / \- \*] [ 0-9]+\)" (:message pojo))
    :math
    (re-find #"^svn rev [0-9]+$" (:message pojo))
    :svn-rev-lookup
    (addressed? pojo)
    :lookup
    (re-find url-regex (:message pojo))
    @@ -240,6 +242,9 @@
    (let [q (.replaceFirst (:message pojo) (str "^" nick ": literal ") "")]
    (prn q)))

    (defmethod responder :svn-rev-lookup [pojo]
    (prn (re-find #"[0-9]+" (:message pojo))))

    (defn handleMessage [this channel sender login hostname message]
    (responder (struct junks this channel sender login
    hostname message)))
  12. Kevin Downey revised this gist Dec 6, 2008. 1 changed file with 12 additions and 3 deletions.
    15 changes: 12 additions & 3 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -23,9 +23,12 @@

    (def url-regex #"[A-Za-z]+://[^ ^/]+\.[^ ^/]+[^ ]+")

    ;; this struct is used to pass around messages
    (defstruct junks :this :channel :sender :login :hostname :message)

    (defn randth [se]
    (defn randth
    "random item from sequence"
    [se]
    (let [s (seq se)]
    (first (drop (rand-int (count se)) se))))

    @@ -49,10 +52,16 @@
    (repeat (lazy-cat s [nil]))))))


    (defn strip-is [string]
    (defn strip-is
    "return a string with everything up to the end of the
    first \"is\" removed"
    [string]
    (.trim (.substring string (+ 3 (.indexOf string " is ")))))

    (defn term [string]
    (defn term
    "returns the part of a string before the first occurence
    of \"is"""
    [string]
    (first (.split string " is ")))

    (defn doc-lookup?
  13. Kevin Downey revised this gist Dec 5, 2008. 1 changed file with 98 additions and 62 deletions.
    160 changes: 98 additions & 62 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -11,6 +11,7 @@
    (def channel "#clojure")
    (def net "chat.us.freenode.net")

    (def *bot*)

    ;; dictionaries for storing relationships
    ;; 'are' dict is not used right now.
    @@ -38,6 +39,16 @@
    (defn befuddled []
    (randth befuddl))

    (defn inits
    "this is Chouser's fault"
    [s]
    (map first
    (take-while second
    (map split-at
    (iterate inc 0)
    (repeat (lazy-cat s [nil]))))))


    (defn strip-is [string]
    (.trim (.substring string (+ 3 (.indexOf string " is ")))))

    @@ -71,16 +82,39 @@
    (defmacro async
    "just do this, I don't care"
    [& x]
    `(send-off (agent nil) (f))[& _#] ~@x )))
    `(send-off (agent nil) (fn [& _#] ~@x )))

    (defn sendMsg
    "send a message to a recv, a recv is a channel name or a nick"
    [this recv msg]
    (async (.sendMessage this recv (.replace (str msg) \newline \ ))))
    (.sendMessage this recv (.replace (str msg) \newline \ )))

    (defmacro sendMsg-who [pojo msg]
    `(sendMsg (:this ~pojo) (who ~pojo) ~msg))


    (defn term-lists [msg]
    (let [x (re-seq #"\w+" msg)
    ignore #(not (contains? #{"a" "where" "what" "is" "who" "are" (str nick ": ")} %))]
    (filter ignore
    (apply concat
    (map (fn [x]
    (map (fn [y]
    (reduce #(str % " " %2) y)) x))
    (map #(reverse (filter identity (inits (drop % x))))
    (take (count x) (iterate inc 0))))))))

    (defn rlookup [terms]
    (loop [t terms]
    (if t
    (if (@dict-is (first t))
    (first t)
    (recur (rest t))))))

    (defn fuzzy-lookup [message]
    (rlookup (term-lists message)))


    (defn who
    "am I talking to someonein a privmsg, or in a channel?"
    [pojo]
    @@ -95,26 +129,23 @@
    pojo))

    (defmulti define (fn [pojo term defi]
    (if (and (@dict-is term) (re-find #"also" (:message pojo)))
    (if (and (@dict-is term) (re-find #" also " (:message pojo)))
    :add
    :new)))

    (defmethod define :new [pojo term defi]
    (dosync
    (alter dict-is
    (fn [dict]
    (let [r (assoc dict (.trim term) (.trim defi))]
    (when r (sendMsg (:this pojo) (who pojo) (ok)))
    r)))))
    (commute dict-is assoc (.trim term) (.trim defi)))
    (sendMsg-who pojo (ok)))

    (defmethod define :add [pojo term defi]
    (let [old (@dict-is term)
    ne (if (vector? old)
    (conj old defi)
    [old defi])]
    (dosync (alter dict-is (fn [dict] (let [r (assoc dict term ne)]
    (when r (sendMsg (:this pojo) (who pojo) (ok)))
    r))))))
    (dosync
    (commute dict-is assoc term ne))
    (sendMsg-who pojo (ok))))

    (defn dispatch
    "this function does dispatch for responder"
    @@ -124,8 +155,6 @@
    :doc-lookup
    (and (addressed? pojo) (re-find #"how much do you know?" (:message pojo)))
    :know
    (and (addressed? pojo) (re-find #"svn" (:message pojo)))
    :svn
    (and (addressed? pojo) (re-find #" is " (:message pojo)) (not= \? (last (:message pojo))))
    :define-is
    (and (addressed? pojo) (re-find #" literal " (:message pojo)))
    @@ -144,21 +173,17 @@
    (defmethod responder :math [pojo]
    (let [[op & num-strings] (re-seq #"[\+\/\*\-0-9]+" (:message pojo))
    nums (map #(.parseInt java.lang.Integer %) num-strings)]
    (sendMsg (:this pojo) (who pojo)
    (let [out (apply (find-var (symbol "clojure.core" op)) nums)]
    (if (> out 4)
    "*suffusion of yellow*"
    out)))))
    (sendMsg-who pojo
    (let [out (apply (find-var (symbol "clojure.core" op)) nums)]
    (if (> out 4)
    "*suffusion of yellow*"
    out)))))

    (defmethod responder :doc-lookup [pojo]
    (sendMsg (:this pojo)
    (who pojo)
    (symbol-to-var-doc (subs (:message pojo) 5 (dec (count (:message pojo)))))))

    (defmethod responder :svn [pojo]
    (sendMsg (:this pojo)
    (who pojo)
    "svn co https://clojure.svn.sourceforge.net/svnroot/clojure clojure"))
    (sendMsg-who pojo
    (symbol-to-var-doc (subs (:message pojo)
    5
    (dec (count (:message pojo)))))))

    (defmethod responder :define-is [pojo]
    (let [a (.trim (.replaceFirst (:message pojo) "^clojurebot:" " "))
    @@ -174,31 +199,32 @@
    result)]
    (cond
    result
    (sendMsg (:this pojo) (who pojo)
    (.replaceAll (if (re-find #"^<reply>" result)
    (.trim (.replaceFirst (str result) "^<reply>" ""))
    (str msg " is " result))
    "#who"
    (:sender pojo)))
    (sendMsg-who pojo
    (.replaceAll (if (re-find #"^<reply>" result)
    (.trim (.replaceFirst (str result) "^<reply>" ""))
    (str msg " is " result))
    "#who"
    (:sender pojo)))
    (fuzzy-lookup msg)
    (let [x (fuzzy-lookup msg)
    r (@dict-is x)
    r (if (vector? r) (randth r) r)]
    (sendMsg-who pojo
    (.replaceAll (if (re-find #"^<reply>" r)
    (.trim (.replaceFirst (str r) "^<reply>" ""))
    (str x " is " r))
    "#who"
    (:sender pojo))))
    :else
    (sendMsg (:this pojo) (who pojo) (befuddled)))))

    (defn fuzzy-lookup [message]
    (let [msg (d?op (.trim (.replaceFirst message (str "^" nick ":") "")))
    words (re-seq #"\w+" msg)
    filtered-words (filter #(not (contains? #{"what" "is" "who" "are"} %)) words)]
    (loop [w filtered-words]
    (if w
    (if (@dict-is (first w))
    (first w)
    (recur (rest w)))))))
    (sendMsg-who pojo (befuddled)))))

    (defmethod responder :know [pojo]
    (sendMsg (:this pojo) (who pojo) (str "I know " (+ (count (deref dict-is)) (count (deref dict-are))) " things")))
    (sendMsg-who pojo (str "I know " (+ (count (deref dict-is)) (count (deref dict-are))) " things")))

    (defmethod responder :url [pojo]
    (dosync (alter url (fn [url]
    (assoc url (re-find url-regex (:message pojo)) (java.util.Date.)))))
    (dosync (commute url
    assoc
    (re-find url-regex (:message pojo)) (java.util.Date.)))
    (prn (:sender pojo) "> " (:message pojo)))

    (defmethod responder :literal [pojo]
    @@ -243,24 +269,37 @@
    (:content x)))))])
    (:content tag-map)))

    (defn get-last-svn-rev []
    (.parseInt Integer (@dict-is "latest")))

    (defn filter-newer-svn-revs [revs]
    (filter #(> (first %) (get-last-svn-rev))
    revs))

    (defn send-svn-revs [revs]
    (dorun
    (map #(sendMsg *bot*
    channel
    (str "svn rev " (first %) "; " (last %)))
    revs)))


    (defn svn-message
    "takes a seq of vectors containing [rev msg]
    sends out messages about new revs. updates \"latest\"
    to latest rev"
    [summaries]
    (dosync
    (let [newrevs (filter #(> (first %)
    (.parseInt Integer (@dict-is "latest")))
    (reverse summaries))]
    (when newrevs
    (do
    (dorun
    (map #(sendMsg *bot*
    "hiredman"
    (str "svn rev " (first %) "; " (last %)))
    newrevs))
    (alter dict-is
    assoc "latest" (str (first (first summaries)))))))))
    (let [newrevs (filter-newer-svn-revs (reverse summaries))]
    (when newrevs
    (do
    (send-svn-revs newrevs)
    (dosync
    (commute dict-is
    assoc
    "latest"
    (str (first (first summaries)))))
    ;don't want to see the whole hash in the repl
    nil))))


    (defn svn-xml-stream
    @@ -302,9 +341,6 @@
    (Thread/sleep (* 10 60000))
    (send-off *agent* this))))




    (def *bot* (pircbot))
    (.connect *bot* net)
    (.changeNick *bot* nick)
  14. Kevin Downey revised this gist Dec 4, 2008. 1 changed file with 72 additions and 7 deletions.
    79 changes: 72 additions & 7 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -11,10 +11,13 @@
    (def channel "#clojure")
    (def net "chat.us.freenode.net")


    ;; dictionaries for storing relationships
    ;; 'are' dict is not used right now.
    (def dict-is (ref {}))
    (def dict-are (ref {}))

    ;url is for storing urls, must figure out something to do with this
    (def url (ref {}))

    (def url-regex #"[A-Za-z]+://[^ ^/]+\.[^ ^/]+[^ ]+")
    @@ -68,13 +71,16 @@
    (defmacro async
    "just do this, I don't care"
    [& x]
    `(send-off (agent nil) (fn [& _#] ~@x )))
    `(send-off (agent nil) (f))[& _#] ~@x )))

    (defn sendMsg
    "send a message to a recv, a recv is a channel name or a nick"
    [this recv msg]
    (async (.sendMessage this recv (.replace (str msg) \newline \ ))))

    (defmacro sendMsg-who [pojo msg]
    `(sendMsg (:this ~pojo) (who ~pojo) ~msg))

    (defn who
    "am I talking to someonein a privmsg, or in a channel?"
    [pojo]
    @@ -120,11 +126,11 @@
    :know
    (and (addressed? pojo) (re-find #"svn" (:message pojo)))
    :svn
    (and (addressed? pojo) (re-find #" is " (:message pojo)))
    (and (addressed? pojo) (re-find #" is " (:message pojo)) (not= \? (last (:message pojo))))
    :define-is
    (and (addressed? pojo) (re-find #" literal " (:message pojo)))
    :literal
    (re-find #"^\([\+ / - \*] [ 0-9]+\)" (:message pojo))
    (re-find #"^\([\+ / \- \*] [ 0-9]+\)" (:message pojo))
    :math
    (addressed? pojo)
    :lookup
    @@ -133,7 +139,7 @@
    :else
    nil))

    (defmulti #^{:docs "currently all messages are routed though this function"} responder dispatch)
    (defmulti #^{:doc "currently all messages are routed though this function"} responder dispatch)

    (defmethod responder :math [pojo]
    (let [[op & num-strings] (re-seq #"[\+\/\*\-0-9]+" (:message pojo))
    @@ -177,6 +183,16 @@
    :else
    (sendMsg (:this pojo) (who pojo) (befuddled)))))

    (defn fuzzy-lookup [message]
    (let [msg (d?op (.trim (.replaceFirst message (str "^" nick ":") "")))
    words (re-seq #"\w+" msg)
    filtered-words (filter #(not (contains? #{"what" "is" "who" "are"} %)) words)]
    (loop [w filtered-words]
    (if w
    (if (@dict-is (first w))
    (first w)
    (recur (rest w)))))))

    (defmethod responder :know [pojo]
    (sendMsg (:this pojo) (who pojo) (str "I know " (+ (count (deref dict-is)) (count (deref dict-are))) " things")))

    @@ -212,7 +228,57 @@
    (.close *out*)))
    [["is" dict-is] ["are" dict-are]]))


    (def svn-command "svn -v --xml --limit 5 log https://clojure.svn.sourceforge.net/svnroot/clojure")

    (defn svn-summaries
    "takes output of clojure.xml/parse on svn's xml log, returns
    a vector of [rev-number commit-message]"
    [tag-map]
    (map (fn [x]
    [(.parseInt Integer (:revision (:attrs x)))
    (first
    (:content
    (first
    (filter #(= (:tag %) :msg)
    (:content x)))))])
    (:content tag-map)))

    (defn svn-message
    "takes a seq of vectors containing [rev msg]
    sends out messages about new revs. updates \"latest\"
    to latest rev"
    [summaries]
    (dosync
    (let [newrevs (filter #(> (first %)
    (.parseInt Integer (@dict-is "latest")))
    (reverse summaries))]
    (when newrevs
    (do
    (dorun
    (map #(sendMsg *bot*
    "hiredman"
    (str "svn rev " (first %) "; " (last %)))
    newrevs))
    (alter dict-is
    assoc "latest" (str (first (first summaries)))))))))


    (defn svn-xml-stream
    "get the xml stream from svn"
    []
    (.getInputStream (.. Runtime getRuntime (exec svn-command))))

    (defn svn-notifier-thread []
    (send-off (agent nil)
    (fn this [& _]
    (svn-message
    (svn-summaries
    (clojure.xml/parse (svn-xml-stream))))
    (Thread/sleep (* 5 60000))
    (send-off *agent* this))))

    ;(svn-message (svn-summaries (clojure.xml/parse (svn-xml-stream))))

    (defn load-dicts []
    (dosync
    (ref-set dict-is
    @@ -228,13 +294,12 @@
    (defn dump-thread []
    (send-off (agent nil)
    (fn this [& _]
    (prn (java.util.Date.))
    (binding [*out* (-> "clojurebot.is"
    java.io.File.
    java.io.FileWriter.)]
    (prn @dict-is)
    (.close *out*))
    (Thread/sleep 60000)
    (Thread/sleep (* 10 60000))
    (send-off *agent* this))))


  15. Kevin Downey revised this gist Dec 1, 2008. 1 changed file with 17 additions and 10 deletions.
    27 changes: 17 additions & 10 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -26,8 +26,8 @@
    (first (drop (rand-int (count se)) se))))

    ;; responses that can be randomly selected from
    (def input-accepted ["In Ordnung" "Ik begrijp" "Alles klar" "Ok." "Roger." "You don't have to tell me twice." "Ack. Ack." "c'est bon!"])
    (def befuddl ["Titim gan éirí ort." "Gabh mo leithscéal?" "No entiendo" "Извините?" "excusez-moi" "Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."])
    (def input-accepted ["'Sea, mhuise." "In Ordnung" "Ik begrijp" "Alles klar" "Ok." "Roger." "You don't have to tell me twice." "Ack. Ack." "c'est bon!"])
    (def befuddl ["Titim gan éirí ort." "Gabh mo leithscéal?" "No entiendo" "excusez-moi" "Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."])

    (defn ok []
    (randth input-accepted))
    @@ -212,14 +212,6 @@
    (.close *out*)))
    [["is" dict-is] ["are" dict-are]]))

    (defn dump-thread []
    (.start (Thread.
    (fn []
    (prn (str (java.util.Date.) " " :dump))
    (dumpdicts)
    (Thread/sleep 60000)
    (recur)))))


    (defn load-dicts []
    (dosync
    @@ -233,6 +225,21 @@
    (.close *in*)
    a))))))

    (defn dump-thread []
    (send-off (agent nil)
    (fn this [& _]
    (prn (java.util.Date.))
    (binding [*out* (-> "clojurebot.is"
    java.io.File.
    java.io.FileWriter.)]
    (prn @dict-is)
    (.close *out*))
    (Thread/sleep 60000)
    (send-off *agent* this))))




    (def *bot* (pircbot))
    (.connect *bot* net)
    (.changeNick *bot* nick)
  16. Kevin Downey revised this gist Nov 29, 2008. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -27,7 +27,7 @@

    ;; responses that can be randomly selected from
    (def input-accepted ["In Ordnung" "Ik begrijp" "Alles klar" "Ok." "Roger." "You don't have to tell me twice." "Ack. Ack." "c'est bon!"])
    (def befuddl ["Titim gan éirí ort." "No entiendo" "Извините?" "excusez-moi" "Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."])
    (def befuddl ["Titim gan éirí ort." "Gabh mo leithscéal?" "No entiendo" "Извините?" "excusez-moi" "Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."])

    (defn ok []
    (randth input-accepted))
  17. Kevin Downey revised this gist Nov 25, 2008. 1 changed file with 13 additions and 5 deletions.
    18 changes: 13 additions & 5 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -36,7 +36,7 @@
    (randth befuddl))

    (defn strip-is [string]
    (.trim (.substring string (inc (inc (.indexOf string "is"))))))
    (.trim (.substring string (+ 3 (.indexOf string " is ")))))

    (defn term [string]
    (first (.split string " is ")))
    @@ -94,9 +94,12 @@
    :new)))

    (defmethod define :new [pojo term defi]
    (dosync (alter dict-is (fn [dict] (let [r (assoc dict (.trim term) (.trim defi))]
    (when r (sendMsg (:this pojo) (who pojo) (ok)))
    r)))))
    (dosync
    (alter dict-is
    (fn [dict]
    (let [r (assoc dict (.trim term) (.trim defi))]
    (when r (sendMsg (:this pojo) (who pojo) (ok)))
    r)))))

    (defmethod define :add [pojo term defi]
    (let [old (@dict-is term)
    @@ -119,6 +122,8 @@
    :svn
    (and (addressed? pojo) (re-find #" is " (:message pojo)))
    :define-is
    (and (addressed? pojo) (re-find #" literal " (:message pojo)))
    :literal
    (re-find #"^\([\+ / - \*] [ 0-9]+\)" (:message pojo))
    :math
    (addressed? pojo)
    @@ -128,7 +133,7 @@
    :else
    nil))

    (defmulti responder dispatch)
    (defmulti #^{:docs "currently all messages are routed though this function"} responder dispatch)

    (defmethod responder :math [pojo]
    (let [[op & num-strings] (re-seq #"[\+\/\*\-0-9]+" (:message pojo))
    @@ -180,6 +185,9 @@
    (assoc url (re-find url-regex (:message pojo)) (java.util.Date.)))))
    (prn (:sender pojo) "> " (:message pojo)))

    (defmethod responder :literal [pojo]
    (let [q (.replaceFirst (:message pojo) (str "^" nick ": literal ") "")]
    (prn q)))

    (defn handleMessage [this channel sender login hostname message]
    (responder (struct junks this channel sender login
  18. Kevin Downey revised this gist Nov 24, 2008. 1 changed file with 56 additions and 33 deletions.
    89 changes: 56 additions & 33 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -9,12 +9,13 @@

    (def nick "clojurebot")
    (def channel "#clojure")
    (def net "chat.feenode.org")
    (def net "chat.us.freenode.net")

    ;; dictionaries for storing relationships
    ;; 'are' dict is not used right now.
    (def dict-is (ref {}))
    (def dict-are (ref {}))
    (def url (ref {}))

    (def url-regex #"[A-Za-z]+://[^ ^/]+\.[^ ^/]+[^ ]+")

    @@ -25,8 +26,8 @@
    (first (drop (rand-int (count se)) se))))

    ;; responses that can be randomly selected from
    (def input-accepted ["Ok." "Roger." "You don't have to tell me twice." "Ack. Ack." "c'est bon!"])
    (def befuddl ["Titim gan éirí ort." "excusez-moi" "Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."])
    (def input-accepted ["In Ordnung" "Ik begrijp" "Alles klar" "Ok." "Roger." "You don't have to tell me twice." "Ack. Ack." "c'est bon!"])
    (def befuddl ["Titim gan éirí ort." "No entiendo" "Извините?" "excusez-moi" "Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."])

    (defn ok []
    (randth input-accepted))
    @@ -57,9 +58,11 @@
    "this returns the doc metadata from a var in the
    clojure ns or a befuddled response"
    [symb]
    (let [x (:doc (meta (find-var (symbol "clojure.core" symb))))]
    (let [a (meta (find-var (symbol "clojure.core" symb)))
    x (:doc a)
    y (:arglists a)]
    (if x
    x
    (str x "; arglists " y)
    (befuddled))))

    (defmacro async
    @@ -84,6 +87,25 @@
    [pojo]
    (when (or (re-find #"^clojurebot:" (:message pojo)) (nil? (:channel pojo)))
    pojo))

    (defmulti define (fn [pojo term defi]
    (if (and (@dict-is term) (re-find #"also" (:message pojo)))
    :add
    :new)))

    (defmethod define :new [pojo term defi]
    (dosync (alter dict-is (fn [dict] (let [r (assoc dict (.trim term) (.trim defi))]
    (when r (sendMsg (:this pojo) (who pojo) (ok)))
    r)))))

    (defmethod define :add [pojo term defi]
    (let [old (@dict-is term)
    ne (if (vector? old)
    (conj old defi)
    [old defi])]
    (dosync (alter dict-is (fn [dict] (let [r (assoc dict term ne)]
    (when r (sendMsg (:this pojo) (who pojo) (ok)))
    r))))))

    (defn dispatch
    "this function does dispatch for responder"
    @@ -111,7 +133,11 @@
    (defmethod responder :math [pojo]
    (let [[op & num-strings] (re-seq #"[\+\/\*\-0-9]+" (:message pojo))
    nums (map #(.parseInt java.lang.Integer %) num-strings)]
    (sendMsg (:this pojo) (who pojo) (apply (find-var (symbol "clojure.core" op)) nums))))
    (sendMsg (:this pojo) (who pojo)
    (let [out (apply (find-var (symbol "clojure.core" op)) nums)]
    (if (> out 4)
    "*suffusion of yellow*"
    out)))))

    (defmethod responder :doc-lookup [pojo]
    (sendMsg (:this pojo)
    @@ -126,41 +152,38 @@
    (defmethod responder :define-is [pojo]
    (let [a (.trim (.replaceFirst (:message pojo) "^clojurebot:" " "))
    term (term a)
    defi (strip-is a)]
    (dosync
    (alter dict-is
    (fn [dict]
    (let [r (assoc dict (.trim term) (.trim defi))]
    (when r
    (sendMsg (:this pojo) (who pojo) (ok)))
    r))))))
    defi (.replaceFirst (strip-is a) "^also " "")]
    (define pojo term defi)))

    (defmethod responder :lookup [pojo]
    ; looks up message in dict
    (let [msg (d?op (.trim (.replaceFirst (:message pojo) "^clojurebot:" "")))]
    (let [msg (d?op (.trim (.replaceFirst (:message pojo) (str "^" nick ":") "")))
    result ((deref dict-is) msg)
    result (if (vector? result)
    (randth result)
    result)]
    (cond
    ((deref dict-is) msg)
    (sendMsg (:this pojo)
    (who pojo)
    (.replaceAll (if (re-find #"^<reply>" ((deref dict-is) msg))
    (.trim
    (.replaceFirst (str ((deref dict-is) msg)) "^<reply>" ""))
    (str msg " is " ((deref dict-is) msg)))
    result
    (sendMsg (:this pojo) (who pojo)
    (.replaceAll (if (re-find #"^<reply>" result)
    (.trim (.replaceFirst (str result) "^<reply>" ""))
    (str msg " is " result))
    "#who"
    (:sender pojo)))
    :else
    (sendMsg (:this pojo) (who pojo) (befuddled)))))
    (sendMsg (:this pojo) (who pojo) (befuddled)))))

    (defmethod responder :know [pojo]
    (sendMsg (:this pojo) (who pojo) (str "I know " (+ (count (deref dict-is)) (count (deref dict-are))) " things")))

    (defmethod responder :url [pojo]
    (dosync (alter url (fn [url]
    (assoc url (re-find url-regex (:message pojo)) (java.util.Date.)))))
    (prn (:sender pojo) "> " (:message pojo)))


    (defn handleMessage [this channel sender login hostname message]
    (responder (struct junks this channel sender login
    hostname message))
    hostname message)))

    (defn handlePrivateMessage [this sender login hostname message]
    (handleMessage this nil sender login hostname message))
    @@ -181,27 +204,27 @@
    (.close *out*)))
    [["is" dict-is] ["are" dict-are]]))

    (defn write-thread []
    (send-off (anget nil)
    (fn this [& _]
    (defn dump-thread []
    (.start (Thread.
    (fn []
    (prn (str (java.util.Date.) " " :dump))
    (dumpdicts)
    (.sleep Thread 600000)
    (send-off *agent* this))))
    (Thread/sleep 60000)
    (recur)))))


    (defn load-dicts []
    (dosync
    (ref-set dict-is
    (eval
    (binding [*in* (-> "clojurebot.is"
    java.io.File.
    java.io.FileWriter.
    java.io.FileReader.
    java.io.PushbackReader.)]
    (let [a (read)]
    (.close *in*)
    a))))))

    ;; (update-proxy bot {'onMessage handleMessage
    ;; 'onPrivateMessage handlePrivateMessage})
    (def *bot* (pircbot))
    (.connect *bot* net)
    (.changeNick *bot* nick)
  19. Kevin Downey revised this gist Nov 24, 2008. 1 changed file with 29 additions and 31 deletions.
    60 changes: 29 additions & 31 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -20,20 +20,19 @@

    (defstruct junks :this :channel :sender :login :hostname :message)

    ;; responses that can be randomly selected from
    (def response
    {:input-accepted ["Ok." "Roger." "You don't have to tell me twice." "Ack. Ack." "c'est bon!"]
    :befuddled ["Titim gan éirí ort." "excusez-moi" "Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."]})
    (defn randth [se]
    (let [s (seq se)]
    (first (drop (rand-int (count se)) se))))

    (defn random-response
    "select a random response of the correct type"
    [type]
    ((response type) (rand-int (count (response type)))))
    ;; responses that can be randomly selected from
    (def input-accepted ["Ok." "Roger." "You don't have to tell me twice." "Ack. Ack." "c'est bon!"])
    (def befuddl ["Titim gan éirí ort." "excusez-moi" "Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."])

    (defn ok []
    (random-response :input-accepted))
    (randth input-accepted))

    (defn befuddled []
    (random-response :befuddled))
    (randth befuddl))

    (defn strip-is [string]
    (.trim (.substring string (inc (inc (.indexOf string "is"))))))
    @@ -91,7 +90,7 @@
    [pojo]
    (cond
    (doc-lookup? (:message pojo))
    :doc-lookup
    :doc-lookup
    (and (addressed? pojo) (re-find #"how much do you know?" (:message pojo)))
    :know
    (and (addressed? pojo) (re-find #"svn" (:message pojo)))
    @@ -181,30 +180,29 @@
    (prn @rels)
    (.close *out*)))
    [["is" dict-is] ["are" dict-are]]))

    (defn write-thread []
    (send-off (anget nil)
    (fn this [& _]
    (dumpdicts)
    (.sleep Thread 600000)
    (send-off *agent* this))))

    ;; (.start (Thread. (fn []
    ;; (loop []
    ;; (dumpdicts)
    ;; (.sleep Thread 600000)
    ;; (recur)))))
    ;;
    ;; (dosync
    ;; (ref-set dict-is
    ;; (eval (binding [*in* (-> "clojurebot.is"
    ;; java.io.File.
    ;; java.io.FileReader.
    ;; java.io.PushbackReader.)]
    ;; (let [a (read)]
    ;; (.close *in*)
    ;; a))))
    ;; )
    (defn load-dicts []
    (dosync
    (ref-set dict-is
    (eval
    (binding [*in* (-> "clojurebot.is"
    java.io.File.
    java.io.FileWriter.
    java.io.PushbackReader.)]
    (let [a (read)]
    (.close *in*)
    a))))))

    ;; (update-proxy bot {'onMessage handleMessage
    ;; 'onPrivateMessage handlePrivateMessage})

    (def *bot* (pircbot))
    (.connect *bot* net)
    (.changeNick *bot* nick)
    (.joinChannel *bot* channel)

    (defn randth [se]
    (let [s (seq se)])
  20. Kevin Downey revised this gist Nov 24, 2008. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -205,3 +205,6 @@
    (.connect *bot* net)
    (.changeNick *bot* nick)
    (.joinChannel *bot* channel)

    (defn randth [se]
    (let [s (seq se)])
  21. Kevin Downey revised this gist Nov 24, 2008. 1 changed file with 11 additions and 5 deletions.
    16 changes: 11 additions & 5 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -35,6 +35,11 @@
    (defn befuddled []
    (random-response :befuddled))

    (defn strip-is [string]
    (.trim (.substring string (inc (inc (.indexOf string "is"))))))

    (defn term [string]
    (first (.split string " is ")))

    (defn doc-lookup?
    "is this a well formed doc-string lookup?"
    @@ -66,7 +71,7 @@
    (defn sendMsg
    "send a message to a recv, a recv is a channel name or a nick"
    [this recv msg]
    (async (.sendMessage this recv (.replace msg \newline \ ))))
    (async (.sendMessage this recv (.replace (str msg) \newline \ ))))

    (defn who
    "am I talking to someonein a privmsg, or in a channel?"
    @@ -105,9 +110,9 @@
    (defmulti responder dispatch)

    (defmethod responder :math [pojo]
    (let [[op & num-strings] (re-seq #"[\+\/\*\-0-9]+" "(- 1 2 3 4)")
    nums (map #(.parseInt java.lang.String %) num-strings)]
    (prn nums)))
    (let [[op & num-strings] (re-seq #"[\+\/\*\-0-9]+" (:message pojo))
    nums (map #(.parseInt java.lang.Integer %) num-strings)]
    (sendMsg (:this pojo) (who pojo) (apply (find-var (symbol "clojure.core" op)) nums))))

    (defmethod responder :doc-lookup [pojo]
    (sendMsg (:this pojo)
    @@ -121,7 +126,8 @@

    (defmethod responder :define-is [pojo]
    (let [a (.trim (.replaceFirst (:message pojo) "^clojurebot:" " "))
    [term defi] (.split a " is ")]
    term (term a)
    defi (strip-is a)]
    (dosync
    (alter dict-is
    (fn [dict]
  22. Kevin Downey revised this gist Nov 23, 2008. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -105,8 +105,9 @@
    (defmulti responder dispatch)

    (defmethod responder :math [pojo]
    ;(re-seq #"[\+\/\*\-0-9]+" "(- 1 2 3 4)")
    (prn :math))
    (let [[op & num-strings] (re-seq #"[\+\/\*\-0-9]+" "(- 1 2 3 4)")
    nums (map #(.parseInt java.lang.String %) num-strings)]
    (prn nums)))

    (defmethod responder :doc-lookup [pojo]
    (sendMsg (:this pojo)
  23. Kevin Downey revised this gist Nov 23, 2008. 1 changed file with 6 additions and 0 deletions.
    6 changes: 6 additions & 0 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,9 @@
    ;;
    ;; Thus spake the master programmer:
    ;; "Though a program be but three lines long, someday it will have to be
    ;; maintained."
    ;;

    (ns hiredman.clojurebot
    (:import (org.jibble.pircbot PircBot)))

  24. Kevin Downey revised this gist Nov 23, 2008. 1 changed file with 13 additions and 10 deletions.
    23 changes: 13 additions & 10 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@
    (def dict-is (ref {}))
    (def dict-are (ref {}))

    (def url-regex #"[A-Za-z]+://[^ ^/].[^ ^/][^ ]+")
    (def url-regex #"[A-Za-z]+://[^ ^/]+\.[^ ^/]+[^ ]+")

    (defstruct junks :this :channel :sender :login :hostname :message)

    @@ -126,15 +126,18 @@
    (defmethod responder :lookup [pojo]
    ; looks up message in dict
    (let [msg (d?op (.trim (.replaceFirst (:message pojo) "^clojurebot:" "")))]
    (cond
    ((deref dict-is) msg)
    (sendMsg (:this pojo)
    (who pojo)
    (if (re-find #"^<reply>" ((deref dict-is) msg))
    (.trim (.replaceFirst (str ((deref dict-is) msg)) "^<reply>" ""))
    (str msg " is " ((deref dict-is) msg))))
    :else
    (sendMsg (:this pojo) (who pojo) (befuddled)))))
    (cond
    ((deref dict-is) msg)
    (sendMsg (:this pojo)
    (who pojo)
    (.replaceAll (if (re-find #"^<reply>" ((deref dict-is) msg))
    (.trim
    (.replaceFirst (str ((deref dict-is) msg)) "^<reply>" ""))
    (str msg " is " ((deref dict-is) msg)))
    "#who"
    (:sender pojo)))
    :else
    (sendMsg (:this pojo) (who pojo) (befuddled)))))

    (defmethod responder :know [pojo]
    (sendMsg (:this pojo) (who pojo) (str "I know " (+ (count (deref dict-is)) (count (deref dict-are))) " things")))
  25. Kevin Downey revised this gist Nov 23, 2008. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@
    (def dict-is (ref {}))
    (def dict-are (ref {}))

    (def url-regex #"[A-Za-z]+://[^ ]+")
    (def url-regex #"[A-Za-z]+://[^ ^/].[^ ^/][^ ]+")

    (defstruct junks :this :channel :sender :login :hostname :message)

  26. Kevin Downey revised this gist Nov 23, 2008. 1 changed file with 27 additions and 25 deletions.
    52 changes: 27 additions & 25 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,10 @@
    (ns hiredman.clojurebot
    (:import (org.jibble.pircbot PircBot)))

    (def nick "clojurebot")
    (def channel "#clojure")
    (def net "chat.feenode.org")

    ;; dictionaries for storing relationships
    ;; 'are' dict is not used right now.
    (def dict-is (ref {}))
    @@ -153,9 +157,6 @@
    (onPrivateMessage [sender login hostname message]
    (handlePrivateMessage this sender login hostname message))))

    (update-proxy bot {'onMessage handleMessage
    'onPrivateMessage handlePrivateMessage})

    (defn dumpdicts []
    (map (fn [[rel rels]]
    (binding [*out* (-> (str "clojurebot." rel)
    @@ -165,25 +166,26 @@
    (.close *out*)))
    [["is" dict-is] ["are" dict-are]]))


    (.start (Thread. (fn []
    (loop []
    (dumpdicts)
    (.sleep Thread 600000)
    (recur)))))

    (dosync
    (ref-set dict-is
    (eval (binding [*in* (-> "clojurebot.is"
    java.io.File.
    java.io.FileReader.
    java.io.PushbackReader.)]
    (let [a (read)]
    (.close *in*)
    a))))
    )

    (def bot (pircbot))
    (.connect bot "chat.freenode.org")
    (.changeNick bot "clojurebot")
    (.joinChannel "#clojure")
    ;; (.start (Thread. (fn []
    ;; (loop []
    ;; (dumpdicts)
    ;; (.sleep Thread 600000)
    ;; (recur)))))
    ;;
    ;; (dosync
    ;; (ref-set dict-is
    ;; (eval (binding [*in* (-> "clojurebot.is"
    ;; java.io.File.
    ;; java.io.FileReader.
    ;; java.io.PushbackReader.)]
    ;; (let [a (read)]
    ;; (.close *in*)
    ;; a))))
    ;; )
    ;; (update-proxy bot {'onMessage handleMessage
    ;; 'onPrivateMessage handlePrivateMessage})

    (def *bot* (pircbot))
    (.connect *bot* net)
    (.changeNick *bot* nick)
    (.joinChannel *bot* channel)
  27. Kevin Downey revised this gist Nov 22, 2008. 1 changed file with 5 additions and 3 deletions.
    8 changes: 5 additions & 3 deletions clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,9 @@
    ;; 'are' dict is not used right now.
    (def dict-is (ref {}))
    (def dict-are (ref {}))
    (def url-regex #"(([a-zA-Z][0-9a-zA-Z+\\-\\.]*:)?/{0,2}[0-9a-zA-Z;/?:@&=+$\\.\\-_!~*'()%]+)?(#[0-9a-zA-Z;/?:@&=+$\\.\\-_!~*'()%]+)?")

    (def url-regex #"[A-Za-z]+://[^ ]+")

    (defstruct junks :this :channel :sender :login :hostname :message)

    ;; responses that can be randomly selected from
    @@ -83,10 +85,10 @@
    :define-is
    (re-find #"^\([\+ / - \*] [ 0-9]+\)" (:message pojo))
    :math
    (re-find url-regex (:message pojo))
    :url
    (addressed? pojo)
    :lookup
    (re-find url-regex (:message pojo))
    :url
    :else
    nil))

  28. Kevin Downey revised this gist Nov 22, 2008. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@

    ;; responses that can be randomly selected from
    (def response
    {:input-accepted ["Ok." "Roger." "You don't have to tell me twice." "Ack. Ack."]
    {:input-accepted ["Ok." "Roger." "You don't have to tell me twice." "Ack. Ack." "c'est bon!"]
    :befuddled ["Titim gan éirí ort." "excusez-moi" "Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."]})

    (defn random-response
  29. Kevin Downey revised this gist Nov 22, 2008. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@
    ;; responses that can be randomly selected from
    (def response
    {:input-accepted ["Ok." "Roger." "You don't have to tell me twice." "Ack. Ack."]
    :befuddled ["Titim gan éirí ort." "Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."]})
    :befuddled ["Titim gan éirí ort." "excusez-moi" "Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."]})

    (defn random-response
    "select a random response of the correct type"
  30. Kevin Downey revised this gist Nov 22, 2008. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion clojurebot.clj
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@
    ;; responses that can be randomly selected from
    (def response
    {:input-accepted ["Ok." "Roger." "You don't have to tell me twice." "Ack. Ack."]
    :befuddled ["Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."]})
    :befuddled ["Titim gan éirí ort." "Excuse me?" "Huh?" "I don't understand." "Pardon?" "It's greek to me."]})

    (defn random-response
    "select a random response of the correct type"