-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathsilly.lisp
318 lines (266 loc) · 12.9 KB
/
silly.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
(in-package #:org.shirakumo.maiden.agents.silly)
(defvar *silly-functions* (make-hash-table :test 'eql))
(defun silly-function (name)
(gethash name *silly-functions*))
(defun (setf silly-function) (func name)
(setf (gethash name *silly-functions*) func))
(defun remove-silly-function (name)
(remhash name *silly-functions*))
(defun dispatch-silly (sender message)
(loop for func being the hash-values of *silly-functions*
for result = (funcall func sender message)
when result collect result))
(defmacro define-silly (name (sender message) &body body)
`(setf (silly-function ',name)
(named-lambda ,name (,sender ,message)
(declare (ignorable ,sender))
,@body)))
(defmacro define-simple-silly (name (sender regex &rest args) &body body)
(let ((message (gensym "MESSAGE")))
`(define-silly ,name (,sender ,message)
(cl-ppcre:register-groups-bind ,args (,(format NIL "(?i)~a" regex) ,message)
(format NIL ,(first body) ,@(rest body))))))
(defun cut-to-first-vowel (word)
(flet ((p (c) (position c word :test #'char-equal)))
(subseq word (or (p #\a) (p #\e) (p #\i) (p #\o) (p #\u) (p #\y) (p #\w) 0))))
(define-simple-silly thants (sender "thanks,? ([^!?.。!¡?¿.,:;\\s]+)" thank)
"... Th~(~a~)." (cut-to-first-vowel thank))
(define-simple-silly blants (sender "bless you,? ([^!?.。!¡?¿.,:;\\s]+)" bless)
"... Bl~(~a~)." (cut-to-first-vowel bless))
(define-simple-silly now-we-know (sender "((i|you|he|she|it|we|they)( all)? know(s?) now|now (i|you|he|she|it|we|they)( all)? know(s?))")
(alexandria:random-elt '("...now we know." "... oh yeah we know now." "NOW WE KNOW!" "NOW WE KNOOOW!!" "...yeah that's good. Now we know.")))
(define-simple-silly the-plan (sender "that('s| was| is) the plan")
"... to give you a boner. AND YOU GOT ONE!")
(define-simple-silly nano-machines (sender "(how is (this|that) (even )?possible)|(how the hell)|(how in the world)|how('d| would) that even work")
"NANO MACHINES, SON!")
(define-simple-silly nespresso (sender "what else")
"Nespresso.")
(define-simple-silly clooney (sender "who else")
"George Clooney.")
(define-simple-silly tomorrow (sender "when('s| is)( the next| the new) (.*?)( going to| gonna)?( be| come)?")
"Tomorrow.")
(define-simple-silly the-answer (sender "I('ll| will) let (you|him|her|them|us) decide")
"... but the answer is yes.")
(define-simple-silly great (sender "it(('s| is) (gonna be|going to be)|('ll| will) be) great")
"It's gonna be great.")
(define-simple-silly galo-sengen (sender "go\\s*go\\s*go")
"GALO SENGEN")
(define-simple-silly take-it-easy (sender "yukkuri|take it easy|ゆっくり")
(if (< 1 (random 10))
"ゆっくりしていってね!"
"~
_,,....,,_ _人人人人人人人人人人人人人人人_
-''\":::::::::::::`''> ゆっくりしていってね!!! <
ヽ::::::::::::::::::::: ̄^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄
|::::::;ノ´ ̄\:::::::::::\_,. -‐ァ __ _____ ______
|::::ノ ヽ、ヽr-r'\"´ (.__ ,´ _,, '-´ ̄ ̄`-ゝ 、_ イ、
_,.!イ_ _,.ヘーァ'二ハ二ヽ、へ,_7 'r ´ ヽ、ン、
::::::rー''7コ-‐'\"´ ; ', `ヽ/`7 ,'==─- -─==', i
r-'ァ'\"´/ /! ハ ハ ! iヾ_ノ i イ iゝ、イ人レ/_ルヽイ i |
!イ´ ,' | /__,.!/ V 、!__ハ ,' ,ゝ レリイi (ヒ_] ヒ_ン ).| .|、i .||
`! !/レi' (ヒ_] ヒ_ン レ'i ノ !Y!\"\" ,___, \"\" 「 !ノ i |
,' ノ !'\" ,___, \"' i .レ' L.',. ヽ _ン L」 ノ| .|
( ,ハ ヽ _ン 人! | ||ヽ、 ,イ| ||イ| /
,.ヘ,)、 )>,、 _____, ,.イ ハ レ ル` ー--─ ´ルレ レ´"))
(define-silly numberwang (sender message)
(when (and (cl-ppcre:scan "^(([1-9]\\d*(('\\d+)*(\\.\\d*))?)|0)$" message)
(< (random 100) 30))
"That's Numberwang!"))
(define-consumer silly (agent)
((gun-cylinder :initform (vector NIL NIL NIL NIL NIL NIL) :accessor gun-cylinder)))
(define-handler (silly handle (and message-event passive-event)) (c ev user message)
:class activatable-handler
:module #.*package*
(unless (matches (username (client ev)) (user ev))
(dolist (response (dispatch-silly (name user) message))
(sleep (+ 0.5 (random 3)))
(reply ev "~a" response))))
(define-command (silly eight) (c ev)
:command "8"
(reply ev "Eight."))
(define-command (silly jerkcity) (c ev)
(multiple-value-bind (content status headers uri) (request-as :html "https://www.bonequest.com/random/")
(declare (ignore headers))
(when (= 200 status)
(reply ev "~a ~a"
(lquery:$ content "title" (text) (node))
(puri:render-uri uri NIL)))))
(define-command (silly roll) (c ev &optional (size "6") (times "1"))
(cond
((or (string-equal size "infinity") (string-equal times "infinity"))
(reply ev "~ad~a: infinity" times size))
((string-equal times "mom")
(if (string-equal size "your")
(reply ev "I would never hurt my mom!")
(reply ev "Down the hill rolls the fatty...")))
((or (string-equal times "joint") (string-equal size "joint"))
(reply ev "Drugs are bad, mkay."))
((string-equal size "over")
(reply ev "No."))
((or (string-equal size "dice")
(and (string-equal size "the")
(string-equal times "dice")))
(reply ev "1d6: ~d" (1+ (random 6))))
((or (string-equal size "cylinder")
(string-equal size "gun")
(string-equal size "russian"))
(if (= 0 (random 6))
(reply ev "BANG!")
(reply ev ".... click")))
(T
(let ((size (parse-integer size :junk-allowed T))
(times (parse-integer times :junk-allowed T)))
(if (and size times)
(reply ev "~dd~d: ~d" times size (loop for i from 0 below times summing (1+ (random size))))
(reply ev "I don't know how to roll that."))))))
(define-command (silly pick) (c ev &rest choices)
:command "randomly pick one of"
(if (null choices)
(reply ev "Uh, I'll need at least one thing to pick from, you know.")
(reply ev "Okey, I choose... ~a!" (alexandria:random-elt choices))))
(defun profane-p (thing)
(find thing '("shit" "ass" "fuck" "cunt" "retard" "idiot" "stupid" "cock" "dick" "autist" "scrap" "trash" "garbage" "junk" "sex")
:test (lambda (a b) (search b a))))
(define-command (silly welcome) (c ev &string place)
(cond ((string= place "back")
(reply ev "Thanks, it's good to be back."))
((starts-with "to " place :test #'char-equal)
(reply ev "Thanks, I'm glad to be in ~a."
(subseq place 3)))
(T
(reply ev "Thanks!"))))
(define-command (silly hello) (c ev &string other)
(cond ((profane-p other)
(reply ev "Well fuck you, too."))
(T
(reply ev "Hi!"))))
(define-command (silly present) (c ev &string thing)
:command "have a"
(cond ((profane-p thing)
(reply ev "... Hey!"))
(T
(reply ev "Thanks for the ~a!" thing))))
(define-command (silly love-you) (c ev)
:command "I love you"
(reply ev (alexandria:random-elt
'("Aw shucks."
"Haha, thanks!"
"You're making me blush. Stop it."
"Flirting with robots, eh?"))))
(define-command (silly you-are) (c ev &string thing)
:command "you are"
(cond ((profane-p thing)
(reply ev "You must be pretty miserable not to have anything better to do than to try and insult a bot."))
(T
(reply ev (alexandria:random-elt
'("If you want me to be."
"If that's what you want to believe, sure."
"Sure."
"Ok?"))))))
(define-command (silly make) (c ev &string thing)
:command "make me a"
(cond ((search "sandwich" thing)
(reply ev "Not even in your dreams, buddy."))
((profane-p thing)
(reply ev "... Hey!"))
(T
(reply ev "Enjoy your ~a! It will approximately be ready in ~a"
thing (format-relative-time (+ (get-universal-time) (random (* 60 60 24 365 1000))))))))
(define-command (silly tell-to) (c ev target to &string thing)
:command "tell to"
(reply ev "~a: Hey, ~a!" target (string-left-trim ".?!" thing)))
(define-command (silly say) (c ev &string thing)
(reply ev "~a" thing))
(defparameter *fortunes*
(with-open-file (s (asdf:system-relative-pathname :maiden-silly "fortunes.txt"))
(loop for line = (read-line s NIL NIL)
while line collect line)))
(defun fortune (name &optional (time (get-universal-time)))
(multiple-value-bind (s m h dd mm yy) (decode-universal-time time)
(declare (ignore s m h))
(let ((date-hash (+ (+ dd (* mm 31)) (* yy 365)))
(nick-hash (reduce #'+ name :key #'char-code)))
(elt *fortunes* (mod (+ date-hash nick-hash) (length *fortunes*))))))
(define-command (silly fortune) (c ev &optional name)
(if name
(reply ev "~@(~a~)'s fortune for today is: ~a" name (fortune name))
(reply ev "Your fortune for today is: ~a" (fortune (name (user ev))))))
(define-command (silly fortune-for) (c ev name)
:command "fortune for"
(reply ev "~@(~a~)'s fortune for today is: ~a" name (fortune name)))
(define-command (silly check-gun) (c ev)
:command "check the gun"
(reply ev "You check the gun... it's ~[empty~:;loaded with ~:*~d bullet~:p~]!"
(count T (gun-cylinder c))))
(define-command (silly load-gun) (c ev)
:command "load the gun"
(let ((count (count T (gun-cylinder c))))
(cond ((= count (length (gun-cylinder c)))
(reply ev "The cylinder is already fully loaded."))
(T
(dotimes (i (length (gun-cylinder c)))
(unless (aref (gun-cylinder c) i)
(setf (aref (gun-cylinder c) i) T)
(return)))
(incf count)
(if (= 1 count)
(reply ev "You load the gun. There is now one bullet in the cylinder.")
(reply ev "You load the gun. There are now ~d bullets in the cylinder." count))))))
(define-command (silly empty-gun) (c ev)
:command "empty the gun"
(fill (gun-cylinder c) NIL)
(reply ev "You empty the cylinder. The gun is now empty."))
(define-command (silly spin-gun) (c ev)
:command "spin the gun"
(alexandria:rotate (gun-cylinder c) (random 6))
(reply ev "You spin the cylinder."))
(define-command (silly fire-gun) (c ev)
:command "fire the gun"
(let ((bullet (aref (gun-cylinder c) 0)))
(setf (aref (gun-cylinder c) 0) NIL)
(alexandria:rotate (gun-cylinder c))
(reply ev "You aim the gun and ...")
(sleep (+ 0.5 (random 1.0)))
(reply ev "~:[click~;BANG!~]" bullet)))
(defparameter *songs*
(with-open-file (s (asdf:system-relative-pathname :maiden-silly "songs.txt"))
(loop for line = (read-line s NIL NIL)
while line collect line)))
(defun fuse (parts position)
(let ((cons (nthcdr position parts)))
(when (rest cons)
(setf (car cons) (format NIL "~a ~a" (first cons) (second cons)))
(setf (cdr cons) (cddr cons)))
parts))
(defun split-song (song)
(let ((count (count #\Space song))
(parts (cl-ppcre:split " " song)))
(loop while (< 0 count)
repeat (max 3 (ceiling count 5))
do (setf parts (fuse parts (random count)))
(decf count))
parts))
(define-command (silly sing) (c ev &string song)
:command "sing"
(reply ev "𝄞𝄙 ~{~a~a ~a ~}~a~a𝄙𝄂"
(loop for word in (split-song (if (string= song "")
(alexandria:random-elt *songs*)
song))
collect (alexandria:random-elt #("♭" "♮" "♯" "𝆑" "" "" "" "" "" "" "" "" ""))
collect (alexandria:random-elt "𝅝𝅗𝅥𝅘𝅥𝅘𝅥𝅮𝅘𝅥𝅯𝅘𝅥𝅰𝅘𝅥𝅱𝅘𝅥𝅲♬♫")
collect word)
(alexandria:random-elt #("♭" "♮" "♯" "𝆑" "" "" "" "" "" "" "" "" ""))
(alexandria:random-elt "𝅝𝅗𝅥𝅘𝅥𝅘𝅥𝅮𝅘𝅥𝅯𝅘𝅥𝅰𝅘𝅥𝅱𝅘𝅥𝅲♬♫")))
(define-event tell-message (message-event passive-event)
((original-event :initarg :original-event)
(target-user :initarg :target-user)))
(defmethod reply ((event tell-message) format &rest args)
(reply (slot-value event 'original-event)
"~a: ~?" (slot-value event 'target-user) format args))
(define-command (silly tell) (c ev user &string command)
(issue (make-instance 'tell-message :message (format NIL "~a: ~a" (username (client ev)) command)
:user (user ev)
:client (client ev)
:original-event ev
:target-user user)
(core ev)))