1 | ;;;; $Id: clhs.lisp 2 2004-01-05 14:13:03Z eenge $ |
---|
2 | ;;;; $Source$ |
---|
3 | |
---|
4 | ;;;; clhs.lisp - an example IRC bot for net-nittin-irc |
---|
5 | |
---|
6 | ;;; clhs is an example IRC bot for net-nittin-irc. It runs on |
---|
7 | ;;; irc.freenode.net in the channels #lisp and #clhs (preferred for |
---|
8 | ;;; testing). It responds to queries of the form "clhs symbol" for |
---|
9 | ;;; symbols in the spec, "clhs 3.1.2.1.2.1" for sections, and "clhs |
---|
10 | ;;; format:A" for format control args. You will want to edit |
---|
11 | ;;; *hyperspec-pathname* to point to where you have the HyperSpec |
---|
12 | ;;; unpacked. You should also check out Mop_Sym.txt and put it in the |
---|
13 | ;;; directory where you will be running the bot from. |
---|
14 | |
---|
15 | ;;; To use it, load the net-nittin-irc system, load clhs.lisp, and |
---|
16 | ;;; invoke (clhs::start-clhs-bot "desirednickname" "desiredserver" |
---|
17 | ;;; "#channel1" "#channel2" "#channel3" ...) |
---|
18 | |
---|
19 | (defpackage :clhs (:use :common-lisp :irc)) |
---|
20 | (in-package :clhs) |
---|
21 | |
---|
22 | ;;; CLHS. This will be the default lookup. |
---|
23 | (defparameter *hyperspec-pathname* #p"/Users/chandler/Sites/HyperSpec/") |
---|
24 | |
---|
25 | (defparameter *hyperspec-map-file* (merge-pathnames "Data/Map_Sym.txt" *hyperspec-pathname*)) |
---|
26 | |
---|
27 | (defparameter *hyperspec-root* "http://www.lispworks.com/reference/HyperSpec/") |
---|
28 | |
---|
29 | ;;; AMOP. |
---|
30 | (defparameter *mop-map-file* #p"Mop_Sym.txt") |
---|
31 | |
---|
32 | (defparameter *mop-root* "http://www.alu.org/mop/") |
---|
33 | |
---|
34 | (defvar *table* (make-hash-table :test 'equalp)) |
---|
35 | |
---|
36 | (defun add-clhs-section-to-table (&rest numbers) |
---|
37 | (let ((key (format nil "~{~d~^.~}" numbers)) |
---|
38 | (target (concatenate 'string *hyperspec-root* (format nil "Body/~2,'0d_~(~{~36r~}~).htm" (car numbers) (mapcar #'(lambda (x) (+ x 9)) (cdr numbers)))))) |
---|
39 | (setf (gethash key *table*) target))) |
---|
40 | |
---|
41 | (defun valid-target (&rest numbers) |
---|
42 | (probe-file (format nil "Body/~2,'0d_~(~{~36r~}~).htm" (car numbers) (mapcar #'(lambda (x) (+ x 9)) (cdr numbers))))) |
---|
43 | |
---|
44 | (defun populate-table () |
---|
45 | ;; Hyperspec |
---|
46 | (with-open-file (s *hyperspec-map-file*) |
---|
47 | ;; populate the table with the symbols from the Map file |
---|
48 | ;; this bit is easy and portable. |
---|
49 | (do ((symbol-name (read-line s nil s) (read-line s nil s)) |
---|
50 | (url (read-line s nil s) (read-line s nil s))) |
---|
51 | ((eq url s) 'done) |
---|
52 | (setf (gethash symbol-name *table*) (concatenate 'string *hyperspec-root* (subseq url 3)))) |
---|
53 | ;; add in section references. |
---|
54 | (let ((*default-pathname-defaults* *hyperspec-pathname*)) |
---|
55 | ;; Yuk. I know. Fixes welcome. |
---|
56 | (loop for section from 0 to 27 |
---|
57 | do (add-clhs-section-to-table section) |
---|
58 | do (loop named s for s1 from 1 to 17 |
---|
59 | unless (valid-target section s1) |
---|
60 | do (return-from s nil) |
---|
61 | do (add-clhs-section-to-table section s1) |
---|
62 | do (loop named ss for s2 from 1 to 17 |
---|
63 | unless (valid-target section s1 s2) |
---|
64 | do (return-from ss nil) |
---|
65 | do (add-clhs-section-to-table section s1 s2) |
---|
66 | do (loop named sss for s3 from 1 to 17 |
---|
67 | unless (valid-target section s1 s2 s3) |
---|
68 | do (return-from sss nil) |
---|
69 | do (add-clhs-section-to-table section s1 s2 s3) |
---|
70 | do (loop named ssss for s4 from 1 to 17 |
---|
71 | unless (valid-target section s1 s2 s3 s4) |
---|
72 | do (return-from ssss nil) |
---|
73 | do (add-clhs-section-to-table section s1 s2 s3 s4) |
---|
74 | do (loop named sssss for s5 from 1 to 17 |
---|
75 | unless (valid-target section s1 s2 s3 s4 s5) |
---|
76 | do (return-from sssss nil) |
---|
77 | do (add-clhs-section-to-table section s1 s2 s3 s4 s5)))))))) |
---|
78 | ;; format directives |
---|
79 | (loop for code from 32 to 127 |
---|
80 | do (setf (gethash (format nil "format:~A" (code-char code)) *table*) |
---|
81 | (concatenate 'string |
---|
82 | *hyperspec-root* |
---|
83 | (case (code-char code) |
---|
84 | ((#\c #\C) "Body/22_caa.htm") |
---|
85 | ((#\%) "Body/22_cab.htm") |
---|
86 | ((#\&) "Body/22_cac.htm") |
---|
87 | ((#\|) "Body/22_cad.htm") |
---|
88 | ((#\~) "Body/22_cae.htm") |
---|
89 | ((#\r #\R) "Body/22_cba.htm") |
---|
90 | ((#\d #\D) "Body/22_cbb.htm") |
---|
91 | ((#\b #\B) "Body/22_cbc.htm") |
---|
92 | ((#\o #\O) "Body/22_cbd.htm") |
---|
93 | ((#\x #\X) "Body/22_cbe.htm") |
---|
94 | ((#\f #\F) "Body/22_cca.htm") |
---|
95 | ((#\e #\E) "Body/22_ccb.htm") |
---|
96 | ((#\g #\G) "Body/22_ccc.htm") |
---|
97 | ((#\$) "Body/22_ccd.htm") |
---|
98 | ((#\a #\A) "Body/22_cda.htm") |
---|
99 | ((#\s #\S) "Body/22_cdb.htm") |
---|
100 | ((#\w #\W) "Body/22_cdc.htm") |
---|
101 | ((#\_) "Body/22_cea.htm") |
---|
102 | ((#\<) "Body/22_ceb.htm") |
---|
103 | ((#\i #\I) "Body/22_cec.htm") |
---|
104 | ((#\/) "Body/22_ced.htm") |
---|
105 | ((#\t #\T) "Body/22_cfa.htm") |
---|
106 | ;; FIXME |
---|
107 | ((#\<) "Body/22_cfb.htm") |
---|
108 | ((#\>) "Body/22_cfc.htm") |
---|
109 | ((#\*) "Body/22_cga.htm") |
---|
110 | ((#\[) "Body/22_cgb.htm") |
---|
111 | ((#\]) "Body/22_cgc.htm") |
---|
112 | ((#\{) "Body/22_cgd.htm") |
---|
113 | ((#\}) "Body/22_cge.htm") |
---|
114 | ((#\?) "Body/22_cgf.htm") |
---|
115 | ((#\() "Body/22_cha.htm") |
---|
116 | ((#\)) "Body/22_chb.htm") |
---|
117 | ((#\p #\P) "Body/22_chc.htm") |
---|
118 | ((#\;) "Body/22_cia.htm") |
---|
119 | ((#\^) "Body/22_cib.htm") |
---|
120 | ((#\Newline) "Body/22_cic.htm") |
---|
121 | (t "Body/22_c.htm"))))) |
---|
122 | ;; glossary. |
---|
123 | ) |
---|
124 | ;; MOP |
---|
125 | (with-open-file (s *mop-map-file*) |
---|
126 | (do ((symbol-name (read-line s nil s) (read-line s nil s)) |
---|
127 | (url (read-line s nil s) (read-line s nil s))) |
---|
128 | ((eq url s) 'done) |
---|
129 | (setf (gethash (concatenate 'string "MOP:" symbol-name) *table*) (concatenate 'string *mop-root* url))))) |
---|
130 | |
---|
131 | (defvar *clhs-connection*) |
---|
132 | (defvar *clhs-nickname*) |
---|
133 | |
---|
134 | (defmacro aif (test conseq &optional (else nil)) |
---|
135 | `(let ((it ,test)) |
---|
136 | (if it ,conseq |
---|
137 | (symbol-macrolet ((it ,test)) |
---|
138 | ,else)))) |
---|
139 | |
---|
140 | (defun spec-lookup (str) |
---|
141 | (aif (gethash str *table*) |
---|
142 | it |
---|
143 | (format nil "Nothing was found for: ~A" str))) |
---|
144 | |
---|
145 | (defparameter *clhs-attention-prefixes* '("clhs " "clhs: ")) |
---|
146 | |
---|
147 | (defun valid-clhs-message-1 (message prefix) |
---|
148 | (if (eql (search prefix (trailing-argument message) :test #'char-equal) 0) |
---|
149 | (and (not (find #\space (trailing-argument message) :start (length prefix))) |
---|
150 | (length prefix)) |
---|
151 | nil)) |
---|
152 | |
---|
153 | (defun valid-clhs-message (message) |
---|
154 | (some #'(lambda (e) (valid-clhs-message-1 message e)) *clhs-attention-prefixes*)) |
---|
155 | |
---|
156 | (defun msg-hook (message) |
---|
157 | (if (string-equal (first (arguments message)) *clhs-nickname*) |
---|
158 | (aif (valid-clhs-message message) |
---|
159 | (privmsg *clhs-connection* (source message) (spec-lookup (subseq (trailing-argument message) it))) |
---|
160 | (privmsg *clhs-connection* (source message) (spec-lookup (trailing-argument message)))) |
---|
161 | (aif (valid-clhs-message message) |
---|
162 | (privmsg *clhs-connection* (first (arguments message)) (spec-lookup (subseq (trailing-argument message) it)))))) |
---|
163 | |
---|
164 | (defun start-clhs-bot (nick server &rest channels) |
---|
165 | (populate-table) |
---|
166 | (setf *clhs-nickname* nick) |
---|
167 | (setf *clhs-connection* (connect :nickname *clhs-nickname* :server server)) |
---|
168 | (mapcar #'(lambda (channel) (join *clhs-connection* channel)) channels) |
---|
169 | (add-hook *clhs-connection* 'irc::irc-privmsg-message 'msg-hook) |
---|
170 | #+sbcl (start-background-message-handler *clhs-connection*) |
---|
171 | #-sbcl (read-message-loop *clhs-connection*)) |
---|
172 | |
---|
173 | (defun shuffle-hooks () |
---|
174 | (irc::remove-hooks *clhs-connection* 'irc::irc-privmsg-message) |
---|
175 | (add-hook *clhs-connection* 'irc::irc-privmsg-message 'msg-hook)) |
---|