Update of /project/cl-irc/cvsroot/cl-irc/example In directory common-lisp.net:/home/bmastenbrook/cl-irc/example
Modified Files: cliki.lisp specbot.lisp Log Message: big changes to cliki-bot: tell users about things, gets mad over abuse
Date: Tue Jul 20 12:08:46 2004 Author: bmastenbrook
Index: cl-irc/example/cliki.lisp diff -u cl-irc/example/cliki.lisp:1.12 cl-irc/example/cliki.lisp:1.13 --- cl-irc/example/cliki.lisp:1.12 Tue Jul 6 14:30:44 2004 +++ cl-irc/example/cliki.lisp Tue Jul 20 12:08:46 2004 @@ -1,4 +1,4 @@ -;;;; $Id: cliki.lisp,v 1.12 2004/07/06 21:30:44 bmastenbrook Exp $ +;;;; $Id: cliki.lisp,v 1.13 2004/07/20 19:08:46 bmastenbrook Exp $ ;;;; $Source: /project/cl-irc/cvsroot/cl-irc/example/cliki.lisp,v $
;;;; cliki.lisp - CLiki as an infobot; only works on SBCL. @@ -151,6 +151,19 @@ :contents contents) *pending-memos*))
+(defun remove-memos (to &key from) + (let ((count 0)) + (setf *pending-memos* + (remove-if #'(lambda (m) + (and (string-equal (without-non-alphanumeric to) + (memo-to m)) + (or (not from) + (string-equal (without-non-alphanumeric from) + (memo-from m))) + (incf count))) + *pending-memos*)) + count)) + (defun lookup-paste (number) (and (find-package :lisppaste) (let ((paste (funcall (intern "FIND-PASTE" :lisppaste) number))) @@ -257,6 +270,7 @@ (setf first-line (regex-replace-all "\r" first-line " ")) (setf first-line (regex-replace-all "\n" first-line " ")) (setf first-line (regex-replace-all "_\(([^)]*)\)" first-line "\1")) + (setf first-line (regex-replace-all "#H\(([^)]*)\)" first-line "\1")) (setf first-line (regex-replace-all "\*\(([^)]*)\)" first-line "\1")) (setf first-line (regex-replace-all "<[^>]+>" first-line "")) (setf first-line (regex-replace-all "^(([^.]|\.\S)+)\.\s+.*$" first-line "\1.")) @@ -301,6 +315,9 @@ ("memos" . ,(lambda (nick) (format nil "To send a memo, say something like ``~A: memo for nick: the memo''. I'll remember the memo for any nick which is the same as the given nick, +/- differences in punctuation, and any nick which is an alias for it, and give it to them when they next speak." nick))) + ("avoiding memos" . + ,(lambda (nick) + (format nil "To flush all your memos without delivery, say something like ``~A: discard my memos''. To flush only memos from a specific person, say ``~A: discard my memos from person''." nick nick))) ("nicknames" . ,(lambda (nick) (format nil "If you have multiple nicknames and want to get your memos at any of them, say something like ``~A: nick1 is another nick for nick2''. If you decide to give up a nick, say ``~:*~A: forget nick2'' and I'll forget it." nick))) @@ -326,94 +343,185 @@ (cliki-find-help (concatenate 'string string (string #\s))))))))
+(defun random-element (list) + (elt list (random (length list)))) + +(defparameter *last-eliza-times* (make-list 6 :initial-element 0)) + +(defparameter *last-warning-time* 0) + +(defun do-eliza (first-pass) + (if (> (- (get-universal-time) 30) + *last-warning-time*) + (let ((time-6 (first *last-eliza-times*)) + (time-4 (third *last-eliza-times*)) + (time-2 (fifth *last-eliza-times*)) + (current-time (get-universal-time)) + (count 0) + (overload 0)) + (if (or + (and + (< (- current-time 15) + time-2) + (setf count 3) + (setf overload (- current-time time-2))) + (and + (< (- current-time 45) + time-4) + (setf count 5) + (setf overload (- current-time time-4))) + (and + (< (- current-time 75) + time-6) + (setf count 7) + (setf overload (- current-time time-6)))) + (progn + (setf *last-warning-time* (get-universal-time)) + (format nil "Would you /please/ stop playing with me? ~A messages in ~A seconds is too many." count overload)) + (progn + (setf *last-eliza-times* (nconc (cdr *last-eliza-times*) + (list (get-universal-time)))) + (ignore-errors (eliza::eliza first-pass))) + + )))) + (defun cliki-lookup (term-with-question &key sender channel) (let ((first-pass (regex-replace-all "^(\s*)([^?]+)(\?*)$" term-with-question "\2")) (should-send-cant-find t)) (setf first-pass (regex-replace-all "\s\s+" first-pass "")) (setf first-pass (regex-replace-all "\s*$" first-pass "")) (let ((scanned (or (nth-value 1 (scan-to-strings "^add\s+"([^"]+)"\s+as:*\s+(.+)$" first-pass)) - (nth-value 1 (scan-to-strings "^add\s+(.+)\s+as:*\s+(.+)$" first-pass))))) + (nth-value 1 (scan-to-strings "^add\s+(.+)\s+as:*\s+(.+)$" first-pass))))) (if scanned (let ((term (elt scanned 0)) (defn (elt scanned 1))) (add-small-definition term defn) "OK, done.") - (let ((scanned (or - (nth-value 1 (scan-to-strings "^alias\s+"([^"]+)"\s+as:*\s+(.+)$" first-pass)) - (nth-value 1 (scan-to-strings "^alias\s+(.+)\s+as:*\s+(.+)$" first-pass)) - (nth-value 1 (scan-to-strings "^(.+)\s+is\s+another\s+(name|word)\s+for:*\s+([^.]+)\.*$" first-pass))))) - (if scanned - (let ((term (elt scanned 0)) - (defn (elt scanned (1- (length scanned))))) - (add-alias term defn) - "OK, done.") - (progn - (setf first-pass (regex-replace-all "(:|/|\\|\#)" first-pass "")) - (when (and (scan "^(?i)lisppaste(\s|!|\?|\.|$)*" first-pass) - (find-package :lisppaste) - channel - (> (length channel) 0) - (char= (elt channel 0) ##) - (funcall (intern "SAY-HELP" :lisppaste) - channel)) - (return-from cliki-lookup nil)) - - (or - (if (string-equal first-pass "help") - (cliki-bot-help *cliki-nickname*)) - (let ((strings (nth-value 1 (scan-to-strings "^(?i)help\s"*([^"]+)"*$" first-pass)))) - (when strings - (cliki-find-help (elt strings 0)))) - (let ((strings (nth-value 1 (scan-to-strings "^(?i)(memo|note)\s+(for|to)\s+(\S+)\s*[:,]+\s+(.+)$" term-with-question)))) - (when (and sender strings) - (if (string-equal (without-non-alphanumeric - (elt strings 2)) - (without-non-alphanumeric - *cliki-nickname*)) - "Buzz off." - (progn - (add-memo - sender - (if (member (elt strings 2) '("self" "myself" "me") :test #'string-equal) - sender - (elt strings 2)) - (elt strings 3)) - (format nil "Remembered. I'll tell ~A when he/she/it next speaks." (elt strings 2)))))) - (let ((to-forget (nth-value 1 (scan-to-strings "^forget\s+([^.]+)\.*$" first-pass)))) - (when to-forget - (forget (elt to-forget 0)) - (format nil "What's ~A? Never heard of it." (elt to-forget 0)))) - (let ((strs (nth-value 1 (scan-to-strings "^(?i)paste\s+(\d+)$" first-pass)))) - (and strs - (lookup-paste (parse-integer (elt strs 0))))) - (if (scan "^(?i)hello(\s|$)*" first-pass) "what's up?") - (if (scan "^(?i)hi(\s|$)*" first-pass) "what's up?") - (if (scan "^(?i)yo(\s|$)*" first-pass) "what's up?") - (if (scan "^(?i)thank(s| you)(\s|!|\?|\.|$)*" first-pass) - (if sender - (format nil "~A: you failed the inverse turing test!" sender) - "you failed the inverse turing test!")) - (if (scan "^(?i)version(\s|!|\?|\.|$)*" first-pass) - (format nil "This is the minion bot, running on a ~A (~A) and running under ~A ~A." (machine-type) (machine-version) (lisp-implementation-type) (lisp-implementation-version))) - (if (scan "^(?i)(?i)do my bidding!*$" first-pass) "Yes, my master.") - (if (should-do-lookup first-pass (or channel sender "")) - (aif (or (small-definition-lookup first-pass) - (cliki-first-sentence first-pass) - (alias-lookup first-pass)) - (prog1 - (concatenate 'string first-pass ": " it) - (did-lookup first-pass (or channel sender "")))) - (setf should-send-cant-find nil)) - (if (or - (scan "(!|\.|\s.+\?|\)|\()\s*$" term-with-question) - (scan "^\s*\S+\s+\S+.*$" term-with-question)) - ;;(generate-text (+ 20 (random 6))) - (ignore-errors (eliza::eliza first-pass)) - ) - (when should-send-cant-find - (format nil "Sorry, I couldn't find anything in the database for ``~A''.~A" first-pass (if (scan " " first-pass) " Maybe you meant to end with punctuation?" ""))) - )))))))) - + (let ((scanned (or + (nth-value 1 (scan-to-strings "^alias\s+"([^"]+)"\s+as:*\s+(.+)$" first-pass)) + (nth-value 1 (scan-to-strings "^alias\s+(.+)\s+as:*\s+(.+)$" first-pass)) + (nth-value 1 (scan-to-strings "^(.+)\s+is\s+another\s+(name|word)\s+for:*\s+([^.]+)\.*$" first-pass))))) + (if scanned + (let ((term (elt scanned 0)) + (defn (elt scanned (1- (length scanned))))) + (add-alias term defn) + "OK, done.") + (progn + (setf first-pass (regex-replace-all "(:|/|\\|\#)" first-pass "")) + (when (and (scan "^(?i)lisppaste(\s|!|\?|\.|$)*" first-pass) + (find-package :lisppaste) + channel + (> (length channel) 0) + (char= (elt channel 0) ##) + (funcall (intern "SAY-HELP" :lisppaste) + channel)) + (return-from cliki-lookup nil)) + (or + (let ((strings + (nth-value 1 (scan-to-strings "^(?i)(direct|tell|show|inform|teach)\s+(\S+)\s+(about|on|in|to|through|)\s*(.+)$" first-pass)))) + (if strings + (let ((about (cliki-lookup (elt strings 3) :sender sender + :channel channel))) + (if about + (format nil "~A: ~A~A" + (elt strings 1) + (if (scan "http:" about) + (concatenate 'string + (random-element + '("have a look at" + "please look at" + "please see" + "direct your attention towards" + "look at")) + " ") + "") + about) + (setf should-send-cant-find nil))))) + (if (string-equal first-pass "help") + (if (should-do-lookup first-pass (or channel sender "")) + (progn + (did-lookup first-pass (or channel sender "")) + (cliki-bot-help *cliki-nickname*)) + (setf should-send-cant-find nil))) + (let ((strings (nth-value 1 (scan-to-strings "^(?i)help\s+(on|about|to|describing|)\s*"*([^"]+)"*$" first-pass)))) + (if strings + (if + (should-do-lookup first-pass (or channel sender "")) + (progn + (did-lookup first-pass (or channel sender "")) + (cliki-find-help (elt strings 1))) + (setf should-send-cant-find nil)))) + (let ((strings (nth-value 1 (scan-to-strings "^(?i)(memo|note)\s+(for|to)\s+(\S+)\s*[:,]+\s+(.+)$" term-with-question)))) + (when (and sender strings) + (if (string-equal (without-non-alphanumeric + (elt strings 2)) + (without-non-alphanumeric + *cliki-nickname*)) + "Buzz off." + (progn + (add-memo + sender + (if (member (elt strings 2) '("self" "myself" "me") :test #'string-equal) + sender + (elt strings 2)) + (elt strings 3)) + (format nil "Remembered. I'll tell ~A when he/she/it next speaks." (elt strings 2)))))) + (when (and sender + (scan "^(?i)(discard|forget)\s+(my\s+|)memo(s|)$" first-pass)) + (let ((count (remove-memos sender))) + (case count + (0 "You didn't have any memos!") + (1 "OK, I threw it out.") + (t "OK, I threw them out.")))) + (let ((strings (nth-value 1 (scan-to-strings "^(?i)(discard|forget)\s+(my\s+|)memo(s|)\s+from\s+([^ .]+)\.*$" first-pass)))) + (when (and sender + strings) + (let ((count (remove-memos sender :from (elt strings 3)))) + (case count + (0 "You didn't have any memos!") + (1 "OK, I threw it out.") + (t "OK, I threw them out."))) + )) + (let ((to-forget (nth-value 1 (scan-to-strings "^forget\s+([^.]+)\.*$" first-pass)))) + (when to-forget + (forget (elt to-forget 0)) + (format nil "What's ~A? Never heard of it." (elt to-forget 0)))) + (let ((strs (nth-value 1 (scan-to-strings "^(?i)paste\s+(\d+)$" first-pass)))) + (and strs + (lookup-paste (parse-integer (elt strs 0))))) + + (if (scan "^(?i)hello(\s|$)*" first-pass) "what's up?") + (if (scan "^(?i)hi(\s|$)*" first-pass) "what's up?") + (if (scan "^(?i)yo(\s|$)*" first-pass) "what's up?") + (if (scan "^(?i)thank(s| you)(\s|!|\?|\.|$)*" first-pass) + (if sender + (format nil "~A: you failed the inverse turing test!" sender) + "you failed the inverse turing test!")) + (if (scan "^(?i)version(\s|!|\?|\.|$)*" first-pass) + (format nil "This is the minion bot, running on a ~A (~A) and running under ~A ~A." (machine-type) (machine-version) (lisp-implementation-type) (lisp-implementation-version))) + (if (scan "^(?i)(?i)do my bidding!*$" first-pass) "Yes, my master.") + (if (should-do-lookup first-pass (or channel sender "")) + (aif (or (small-definition-lookup first-pass) + (cliki-first-sentence first-pass) + (alias-lookup first-pass)) + (prog1 + (concatenate 'string first-pass ": " it) + (did-lookup first-pass (or channel sender "")))) + (setf should-send-cant-find nil)) + (if (and + should-send-cant-find + (or + (scan "(!|\.|\s.+\?|\)|\()\s*$" term-with-question) + (scan "^\s*\S+\s+\S+.*$" term-with-question))) + ;;(generate-text (+ 20 (random 6))) + (progn + (setf should-send-cant-find nil) + (do-eliza first-pass)) + ) + (when should-send-cant-find + (format nil "Sorry, I couldn't find anything in the database for ``~A''.~A" first-pass (if (scan " " first-pass) " Maybe you meant to end with punctuation?" ""))) + )))))))) + (defun valid-cliki-message (message) (scan *cliki-attention-prefix* (trailing-argument message)))
@@ -426,7 +534,6 @@
(defun msg-hook (message) (let ((respond-to (if (string-equal (first (arguments message)) *cliki-nickname*) (source message) (first (arguments message))))) - (take-care-of-memos respond-to (source message)) (if (valid-cliki-message message) (let ((response (cliki-lookup (regex-replace *cliki-attention-prefix* (trailing-argument message) "") :sender (source message) :channel (first (irc:arguments message))))) (and response (privmsg *cliki-connection* respond-to response))) @@ -434,7 +541,8 @@ (aif (cliki-lookup (trailing-argument message) :sender (source message)) (privmsg *cliki-connection* respond-to it)) (if (anybody-here (trailing-argument message)) - (privmsg *cliki-connection* (first (arguments message)) (format nil "~A: hello." (source message)))))))) + (privmsg *cliki-connection* (first (arguments message)) (format nil "~A: hello." (source message)))))) + (take-care-of-memos respond-to (source message))))
(defvar *cliki-nickserv-password* "")
Index: cl-irc/example/specbot.lisp diff -u cl-irc/example/specbot.lisp:1.4 cl-irc/example/specbot.lisp:1.5 --- cl-irc/example/specbot.lisp:1.4 Fri Jul 9 09:03:35 2004 +++ cl-irc/example/specbot.lisp Tue Jul 20 12:08:46 2004 @@ -1,4 +1,4 @@ -;;;; $Id: specbot.lisp,v 1.4 2004/07/09 16:03:35 bmastenbrook Exp $ +;;;; $Id: specbot.lisp,v 1.5 2004/07/20 19:08:46 bmastenbrook Exp $ ;;;; $Source: /project/cl-irc/cvsroot/cl-irc/example/specbot.lisp,v $
;;;; specbot.lisp - an example IRC bot for cl-irc @@ -66,7 +66,7 @@
(defun add-simple-alist-lookup (file designator prefix description) (let ((alist (with-open-file (s file :direction :input) (read s)))) - (push (cons designator alist) *alists*) + (pushnew (cons designator alist) *alists* :test #'equal) (setf *spec-providers* (nconc *spec-providers* (list `((simple-alist-lookup ,designator) ,prefix ,description)))))) @@ -114,7 +114,7 @@ do (aif (strip-address to-lookup :address (second type) :final t) (let ((looked-up (funcall actual-fun it))) - (if (and (< 0 (count #\space it) 3) + (if (and (<= 0 (count #\space it) 1) (not looked-up)) (setf looked-up (format nil "Sorry, I couldn't find anything for ~A." it))) (and looked-up