Actors can spawn other actors, but before we start the hive we use
this special "hive-create-actor*" method.
It takes the hive as its first argument, the actor class as the second
-argument, a decoraive "cookie" as the third argument (this is
+argument, a decorative "cookie" as the third argument (this is
optional, but it helps with debugging... you can skip it by setting it
to #f if you prefer), and the rest are initialization arguments to the
actor. hive-create-actor* passes back not the actor itself (we don't
;; Default
(_
(<- irc-bot (actor-id irc-bot) 'send-line channel
- "*stupid puppy look*"))))
- ;; Otherwise... just spit the output to current-output-port or whatever
- (_
- (if emote?
- (format #t "~a emoted ~s in channel ~a\n"
- speaker line channel)
- (format #t "~a said ~s in channel ~a\n"
- speaker line channel)))))
+ "*stupid puppy look*"))))))
#+END_SRC
Parsing the pattern matcher syntax is left as an exercise for the
;; Default
(_
- (respond "*stupid puppy look*"))))
- ;; Otherwise... just spit the output to current-output-port or whatever
- (_
- (if emote?
- (format #t "~a emoted ~s in channel ~a\n"
- speaker line channel)
- (format #t "~a said ~s in channel ~a\n"
- speaker line channel)))))
+ (respond "*stupid puppy look*"))))))
#+END_SRC
Okay, that looks pretty good!
and actors will immediately update their behavior, so you can test
and tweak things as you go.
+Okay, enough talking. Let's add it!
+Redefine run-bot like so:
+#+BEGIN_SRC scheme
+ (define* (run-bot #:key (username "examplebot")
+ (server "irc.freenode.net")
+ (channels '("##botchat"))
+ (repl-path "/tmp/8sync-repl"))
+ (define hive (make-hive))
+ (define irc-bot
+ (hive-create-actor* hive <my-irc-bot> "irc-bot"
+ #:username username
+ #:server server
+ #:channels channels))
+ (define repl-manager
+ (hive-create-actor* hive <repl-manager> "repl"
+ #:path repl-path))
+
+ (run-hive hive (list (bootstrap-message hive irc-bot 'init)
+ (bootstrap-message hive repl-manager 'init))))
+#+END_SRC
+
+If we put a call to run-bot at the bottom of our file we can call it,
+and the repl-manager will start something we can connect to automatically.
+Horray!
+Now when we run this it'll start up a REPL with a unix domain socket at
+the repl-path.
+We can connect to it in emacs like so:
+
+: M-x geiser-connect-local <RET> guile <RET> /tmp/8sync-repl <RET>
+
+Okay, so what does this get us?
+Well, we can now live edit our program.
+Let's change how our bot behaves a bit.
+Let's change handle-line and tweak how the bot responds to a botsnack.
+Change this part:
+
+#+BEGIN_SRC scheme
+ ;; From this:
+ ("botsnack"
+ (respond "Yippie! *does a dance!*"))
+
+ ;; To this:
+ ("botsnack"
+ (respond "Yippie! *catches botsnack in midair!*"))
+#+END_SRC
+Okay, now let's evaluate the change of the definition.
+You can hit "C-M-x" anywhere in the definition to re-evaluate.
+(You can also position your cursor at the end of the definition and press
+"C-x C-e", but I've come to like "C-M-x" better because I can evaluate as soon
+as I'm done writing.)
+Now, on IRC, ask your bot for a botsnack.
+The bot should give the new message... with no need to stop and start the
+program!
+Let's fix a bug live.
+Our current program works great if you talk to your bot in the same
+IRC channel, but what if you try to talk to them over private message?
-# Finally, show off pk
+#+BEGIN_SRC text
+IRC> /query examplebot
+<foo-user> examplebot: hi!
+#+END_SRC
+
+Hm, we aren't seeing any response on IRC!
+Huh? What's going on?
+It's time to do some debugging.
+There are plenty of debugging tools in Guile, but sometimes the simplest
+is the nicest, and the simplest debugging route around is good old
+fashioned print debugging.
+
+It turns out Guile has an under-advertised feature which makes print
+debugging really easy called "pk", pronounced "peek".
+What pk accepts a list of arguments, prints out the whole thing,
+but returns the last argument.
+This makes wrapping bits of our code pretty easy to see what's
+going on.
+So let's peek into our program with pk.
+Edit the respond section to see what channel it's really sending
+things to:
+#+BEGIN_SRC scheme
+ (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
+ line emote?)
+ ;; [... snip ...]
+ (define (respond respond-line)
+ (<- irc-bot (actor-id irc-bot) 'send-line (pk 'channel channel)
+ respond-line))
+ ;; [... snip ...]
+ )
+#+END_SRC
+
+Re-evaluate.
+Now let's ping our bot in both the channel and over PM.
+
+#+BEGIN_SRC text
+;;; (channel "##botchat")
+
+;;; (channel "sinkbot")
+#+END_SRC
+
+Oh okay, this makes sense.
+When we're talking in a normal multi-user channel, the channel we see
+the message coming from is the same one we send to.
+But over PM, the channel is a username, and in this case the username
+we're sending our line of text to is ourselves.
+That isn't what we want.
+Let's edit our code so that if we see that the channel we're sending
+to looks like our own username that we respond back to the sender.
+(We can remove the pk now that we know what's going on.)
+
+#+BEGIN_SRC scheme
+ (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
+ line emote?)
+ ;; [... snip ...]
+ (define (respond respond-line)
+ (<- irc-bot (actor-id irc-bot) 'send-line
+ (if (looks-like-me? channel)
+ speaker ; PM session
+ channel) ; normal IRC channel
+ respond-line))
+ ;; [... snip ...]
+ )
+#+END_SRC
+
+Re-evaluate and test.
+
+#+BEGIN_SRC text
+IRC> /query examplebot
+<foo-user> examplebot: hi!
+<examplebot> Oh hi foo-user!
+#+END_SRC
+Horray!
** Battle bot!