syncbot.scm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. #!/usr/bin/guile \
  2. -e main -s
  3. !#
  4. ;; Copyright (C) 2015 Christopher Allan Webber <cwebber@dustycloud.org>
  5. ;; This library is free software; you can redistribute it and/or
  6. ;; modify it under the terms of the GNU Lesser General Public
  7. ;; License as published by the Free Software Foundation; either
  8. ;; version 3 of the License, or (at your option) any later version.
  9. ;;
  10. ;; This library is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. ;; Lesser General Public License for more details.
  14. ;;
  15. ;; You should have received a copy of the GNU Lesser General Public
  16. ;; License along with this library; if not, write to the Free Software
  17. ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  18. ;; 02110-1301 USA
  19. (use-modules (8sync)
  20. (8sync systems irc)
  21. (ice-9 format)
  22. (ice-9 match)
  23. (srfi srfi-37)
  24. (oop goops))
  25. (set! *random-state* (random-state-from-platform))
  26. (define (random-choice lst)
  27. (list-ref lst (random (length lst))))
  28. (define (random-food-response)
  29. (random-choice
  30. `("Yippie! *does a dance!*"
  31. "oh boy! oh girl! oh arbitrary-gender-exclamation!"
  32. "Scrum-diddly-umptious!"
  33. "eeeee!"
  34. ":D :D :D"
  35. "*catches botsnack in midair*"
  36. "YESSSSSS *nom nom nom nom nom*"
  37. "thanks!"
  38. "*eyes treat suspiciously... then furiously devours it!*"
  39. "*gratefully affirms that botsnack is the most important command an irc bot can know*"
  40. ,(string-concatenate
  41. (list "Horray! A delicious "
  42. (random-choice
  43. '("pear" "banana"
  44. "caramel" "tapioca pudding"
  45. "fudge slurry"
  46. "banana walnut muffin"
  47. "iced soy vanilla mocha salted caramel latte"
  48. "cartoon canine snack"))
  49. "!")))))
  50. (define fate-ladder
  51. '((-2 . "terrible")
  52. (-1 . "poor")
  53. (0 . "mediocre")
  54. (1 . "average")
  55. (2 . "fair")
  56. (3 . "good")
  57. (4 . "great")
  58. (5 . "superb")
  59. (6 . "fantastic")
  60. (7 . "epic")
  61. (8 . "legendary")))
  62. (define (fate-ladder-value roll-number)
  63. (cond ((< roll-number -2)
  64. "it's... really not good...")
  65. ((> roll-number 8)
  66. "it's... off the charts!")
  67. (else
  68. (assoc-ref fate-ladder roll-number))))
  69. (define fate-dice-map
  70. '((-1 . "[-]") (0 . "[_]") (1 . "[+]")))
  71. (define* (roll-fate #:optional (base-roll 0))
  72. (let* ((rolls (map (lambda _ (- (random 3) 1)) (iota 4)))
  73. (dice-string (string-join
  74. (map (lambda (x) (assoc-ref fate-dice-map x))
  75. rolls)
  76. " "))
  77. (score (apply + base-roll rolls)))
  78. (format #f "Rolling at ~a: ~a -> ~a! (~a)"
  79. base-roll dice-string score
  80. (fate-ladder-value score))))
  81. (define-class <syncbot> (<irc-bot>))
  82. (define-method (handle-line (irc-bot <syncbot>) message
  83. speaker channel line emote?)
  84. (define my-name (irc-bot-username irc-bot))
  85. (define (looks-like-me? str)
  86. (or (equal? str my-name)
  87. (equal? str (string-concatenate (list my-name ":")))))
  88. (define (reply-line line)
  89. (<- (actor-id irc-bot) 'send-line channel
  90. line))
  91. (let ((channel (if (looks-like-me? channel)
  92. speaker
  93. channel)))
  94. (match (string-split line #\space)
  95. (((? looks-like-me? _) action action-args ...)
  96. (match action
  97. ("botsnack"
  98. (reply-line (random-food-response)))
  99. ((or "hello" "hello!" "hello." "greetings" "greetings." "greetings!"
  100. "hei" "hei." "hei!" "hi" "hi." "hi!")
  101. (reply-line (format #f "Oh hi ~a!" speaker)))
  102. ("failboat"
  103. (/ 1 0))
  104. ("help"
  105. (reply-line "I can't help you... I can't even help myself!"))
  106. ("echo"
  107. (reply-line (string-join action-args " ")))
  108. ("roll-fate"
  109. (let ((base-roll
  110. (match action-args
  111. (((? string->number base-roll) rest ...)
  112. (string->number base-roll))
  113. (_ 0))))
  114. (reply-line (format #f "~a: ~a"
  115. speaker (roll-fate base-roll)))))
  116. ("d6"
  117. (reply-line (format #f "~a: *rolls*... you get a ~a!"
  118. speaker (+ (random 6) 1))))
  119. ("d20"
  120. (let ((score (+ (random 20) 1)))
  121. (if (eqv? score 20)
  122. (reply-line
  123. (format #f "~a: *rolls*... you get a ~a! *critical!*"
  124. speaker score))
  125. (reply-line
  126. (format #f "~a: *rolls*... you get a ~a!"
  127. speaker score)))))
  128. ("root"
  129. (match action-args
  130. (((or "on" "for") whom ...)
  131. (reply-line
  132. (format #f "~a ~a! :D"
  133. (random-choice '("Gooooooo"
  134. "Woo woo"
  135. "Keep it up"
  136. "Yay yay"))
  137. (string-join whom " "))))
  138. (_ (reply-line "Root for whom?"))))
  139. ("sympathize"
  140. (match action-args
  141. (("with" whom ...)
  142. (reply-line (format #f "~a ~a! :("
  143. (random-choice
  144. '("I'm so sorry"
  145. "Sorry"
  146. "I hope things get better"
  147. "Sorry, and good luck"))
  148. (string-join whom " "))))
  149. (_ (reply-line "Sympathize with whom?"))))
  150. ((or "greet" "welcome")
  151. (reply-line
  152. (format #f "~a ~a!"
  153. (random-choice '("Great to see you"
  154. "Hey, welcome"
  155. "Thanks for joining us"
  156. "Good to see you"
  157. "Thanks for dropping in"
  158. "Welcome"))
  159. (string-join action-args " "))))
  160. ("hug"
  161. (reply-line
  162. (format #f "*~a ~a ~a*"
  163. my-name
  164. (random-choice
  165. '("gives a big bear hug to"
  166. "gives a friendly hug to"
  167. "glomps" "hugs" "embraces"
  168. "gives a big ol fuzzy hug"
  169. "gives a gentle pet on the back to"))
  170. (string-join action-args " "))))
  171. ("sorry"
  172. (reply-line (random-choice
  173. '("It's okay."
  174. "I accept your apology."
  175. "No worries."))))
  176. ("shut"
  177. (match action-args
  178. (("up" _ ...)
  179. (reply-line "No, YOU shut up!"))
  180. (_
  181. (reply-line "Shut huh???"))))
  182. ("give"
  183. (match action-args
  184. ((object "to" whom ...)
  185. (reply-line
  186. (format #f "*gives ~a to ~a*"
  187. object (string-join whom " "))))
  188. (_ (reply-line "*stares*"))))
  189. ((or "d12" "d10" "d4")
  190. (reply-line
  191. (format #f "~a: *checks bag*... I left that one at home :("
  192. speaker)))
  193. ("source"
  194. (for-each
  195. reply-line
  196. '("I'm a little irc bot for 8sync! Patches welcome / happy hacking!"
  197. "My source: https://notabug.org/cwebber/syncbot"
  198. "8sync's source: https://notabug.org/cwebber/8sync")))
  199. ;; Add yours here
  200. (whatever
  201. (display whatever)(newline)
  202. (reply-line "*stupid puppy look*"))))
  203. (((or ":)" ":p" ";)" ":D" ":P" ";D" ";P") rest ...)
  204. ;; only wink back once every 10 times
  205. (if (equal? (random 10) 1)
  206. (reply-line (random-choice '(";)" ":)" ":D" "^_^" ":3")))))
  207. (_
  208. (cond
  209. (emote?
  210. (format #t "~a emoted ~s in channel ~a\n"
  211. speaker line channel))
  212. (else
  213. (format #t "~a said ~s in channel ~a\n"
  214. speaker line channel)))))))
  215. (define (display-help scriptname)
  216. (format #t "Usage: ~a [OPTION] username" scriptname)
  217. (display "
  218. -h, --help display this text
  219. --server=SERVER-NAME connect to SERVER-NAME
  220. defaults to \"irc.freenode.net\"
  221. --channels=CHANNEL1,CHANNEL2
  222. join comma-separated list of channels on connect
  223. defaults to \"##botchat\"")
  224. (newline))
  225. (define (parse-args scriptname args)
  226. (args-fold (cdr args)
  227. (list (option '(#\h "help") #f #f
  228. (lambda _
  229. (display-help scriptname)
  230. (exit 0)))
  231. (option '("server") #t #f
  232. (lambda (opt name arg result)
  233. `(#:server ,arg ,@result)))
  234. (option '("channels") #t #f
  235. (lambda (opt name arg result)
  236. `(#:channels ,(string-split arg #\,)
  237. ,@result))))
  238. (lambda (opt name arg result)
  239. (format #t "Unrecognized option `~a'\n" name)
  240. (exit 1))
  241. (lambda (option result)
  242. `(#:username ,option ,@result))
  243. '()))
  244. (define* (run-bot #:key (username "sinkbot")
  245. (server "irc.freenode.net")
  246. (channels '("##botchat"))
  247. (repl #f))
  248. (define hive (make-hive))
  249. (define irc-bot
  250. (bootstrap-actor* hive <syncbot> "irc-bot"
  251. #:username username
  252. #:server server
  253. #:channels channels))
  254. ;; TODO: load REPL
  255. (run-hive hive '()))
  256. (define (main args)
  257. (define parsed-args (parse-args "syncbot.scm" args))
  258. (apply (lambda* (#:key username #:allow-other-keys)
  259. (when (not username)
  260. (display "Error: username not specified!")
  261. (newline) (newline)
  262. (display-help "syncbot.scm")
  263. (exit 1)))
  264. parsed-args)
  265. (apply run-bot parsed-args))