posts: 08c
authorLucian Mogosanu <lucian.mogosanu@gmail.com>
Thu, 25 Apr 2019 19:11:58 +0000 (22:11 +0300)
committerLucian Mogosanu <lucian.mogosanu@gmail.com>
Thu, 25 Apr 2019 19:11:58 +0000 (22:11 +0300)
drafts/000-feedbot-iii.markdown [deleted file]
posts/y05/08c-feedbot-iii.markdown [new file with mode: 0644]

diff --git a/drafts/000-feedbot-iii.markdown b/drafts/000-feedbot-iii.markdown
deleted file mode 100644 (file)
index e75ab2f..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
----
-postid: 000
-title: Feedbot [iii]: the IRC bot
-date: April 25, 2019
-author: Lucian MogoČ™anu
-tags: tech, tmsr
----
-
-All the basic building blocks pertaining to RSS functionality --
-i.e. the [checker][feedbot-i] and the [announcer][feedbot-ii] -- being
-in place, we conclude the [Feedbot][feedbot] series with:
-
-* The [V patch][feedbot-irc.vpatch]; and
-* my [seal][feedbot-irc.vpatch.spyked.sig].
-
-The patch in this episode introduces the following changes:
-
-* Some methods are removed, due to their retrospective uselessness;
-  some methods are added, due to them being found useful for both IRC
-  implementation and maintenance operations.
-* Auxiliary functions are provided in order to handle IRC operation
-  and Feedbot command argument parsing.
-* Most importantly, functionality is added to: manage the bot state;
-  glue to Ircbot and adapt the announcer to IRC; and implement
-  [Trilemabot][trilemabot] commands.
-
-The remainder of this post will detail the last bullet above. The
-reader is however encouraged to read the [V patch][feedbot-irc.vpatch]
-in its entirety, to get a broader image of the changes.
-
-**VII. State management**
-
-Feedbot state is a list of the form:
-
-~~~~ {.commonlisp}
-(feed-db msg-queue)
-~~~~
-
-where `feed-db` is a feed db and `msg-queue` is a message queue.
-
-Feedbot state is kept persistent using principally the feedbot
-`save-state` and `load-state` methods:
-
-~~~~ {.commonlisp}
-(defmethod feedbot-save-state ((bot feedbot) &optional (path "state.sexp"))
-  "Save bot state to disk location given by `path'."
-  (let ((feed-db (with-feed-db (feed-db) bot feed-db))
-        (msg-queue (with-msg-queue (msg-queue) bot msg-queue)))
-    (with-open-file (out path
-                         :direction :output
-                         :if-does-not-exist :create
-                         :if-exists :supersede)
-      (write (list feed-db msg-queue) :stream out)
-      nil)))
-
-(defmethod feedbot-load-state ((bot feedbot) &optional (path "state.sexp"))
-  "Load bot state from disk location given by `path'.
-
-This method throws an error condition if the bot contains pre-existing
-state, i.e. `feed-db' and `msg-queue' are non-NIL."
-  (with-slots (feed-db msg-queue) bot
-    (assert (not feed-db))
-    (assert (not msg-queue)))
-  (feedbot-reload-state bot path))
-~~~~
-
-**VIII. Ircbot glue**
-
-IRC glue:
-
-**a\.** implements [entry announcer][announcer] functionality:
-
-~~~~ {.commonlisp}
-(defun announce-irc! (bot msg)
-  (announce-stdout! msg)
-  (let ((rcpt (get-msg-to! msg))
-        (entry (get-msg-entry! msg)))
-    (ircbot-send-message bot rcpt
-                         (format nil "~a << ~a -- ~a~%"
-                                 (get-entry-link entry)
-                                 (get-msg-feed-title! msg)
-                                 (get-entry-title entry)))))
-~~~~
-
-**b\.** starts the checker and announcer threads on rpl_welcome:
-
-~~~~ {.commonlisp}
-(defun feedbot-rpl_welcome (bot message)
-  (declare (ignore message))
-  (feedbot-start-checker-thread bot)
-  (feedbot-start-announcer-thread bot))
-~~~~
-
-**c\.** sends messages to online nicks on rpl_ison
-
-~~~~ {.commonlisp}
-(defun feedbot-rpl_ison (bot message)
-  ;; Only when our reply contains some nicks...
-  (when (cdr (arguments message))
-    (let ((nicks (parse-ison (cadr (arguments message)))))
-      ;; Process messages...
-      (feedbot-process-msg-queue
-       bot #'(lambda (msg)
-               ;; Only when msg not send and :to is online...
-               (when (and (not (get-msg-sent! msg))
-                          (member (get-msg-to! msg) nicks
-                                  :test #'string=))
-                ;; Wait a bit
-                (sleep *announce-delay*)
-                 ;; Announce and mark as sent.
-                 (announce-irc! bot msg)
-                 (set-msg-sent! msg)))))))
-~~~~
-
-**d\.** implements ircbot-{connect,disconnect} routines
-
-~~~~ {.commonlisp}
-(defmethod ircbot-connect :after ((bot feedbot))
-  (feedbot-load-state bot)
-  (let ((conn (ircbot-connection bot)))
-    (add-hook conn 'irc-rpl_welcome-message
-              #'(lambda (message)
-                  (feedbot-rpl_welcome bot message)))
-    (add-hook conn 'irc-rpl_ison-message
-              #'(lambda (message)
-                  (feedbot-rpl_ison bot message)))))
-
-(defmethod ircbot-disconnect :after ((bot feedbot)
-                                     &optional (quit-msg "feedbot out"))
-  (declare (ignore quit-msg))
-  (with-slots (db-mutex queue-mutex checker-thread announcer-thread) bot
-    (ignore-errors
-      (release-mutex db-mutex :if-not-owner :force)
-      (release-mutex queue-mutex :if-not-owner :force)
-      (terminate-thread checker-thread)
-      (terminate-thread announcer-thread))
-    (setf checker-thread nil
-          announcer-thread nil)
-    (feedbot-flush-state bot)))
-~~~~
-
-**IX. Trilemabot commands**
-
-Consult [the feedbot manual][feedbot-manual] for more details.
-
-~~~~ {.commonlisp}
-(trilemabot-define-cmd (:help bot message target arguments)
-  (declare (ignore arguments))
-  (ircbot-send-message bot (response-rcpt bot message target)
-          "http://thetarpit.org/posts/y05/081-feedbot-manual.html"))
-~~~~
-
-**Post codex:** In total, Feedbot weighs circa nine hundred lines of
-code, of which almost a hundred comprise auxiliary functionality
-(parsing, sanitization and low-level IRC code), the rest being mostly
-Feedbot mechanism. Of these, about 23% are comments and another 7% are
-inline function documentation strings.
-
-The mechanism is, as far as I'm concerned, done. There are some
-operator-side bits missing (e.g. bulk adding/removal of feeds), but
-then again, some of them can be easily scripted, while others are more
-indicative of brain-fixing rather than any imagined code-fixing.
-
-Bug fixes and other comments are, as usual, more than welcome.
-
-[feedbot-i]: /posts/y05/08a-feedbot-i.html
-[feedbot-ii]: /posts/y05/08b-feedbot-ii.html
-[feedbot]: http://btcbase.org/log-search?q=feedbot
-[feedbot-irc.vpatch]: TODO
-[feedbot-irc.vpatch.spyked.sig]
-[trilemabot]: /posts/y05/078-trilemabot-ii.html
-[announcer]: /posts/y05/08b-feedbot-ii.html#selection-249.0-252.0
-[feedbot-manual]: /posts/y05/081-feedbot-manual.html
diff --git a/posts/y05/08c-feedbot-iii.markdown b/posts/y05/08c-feedbot-iii.markdown
new file mode 100644 (file)
index 0000000..0e68c73
--- /dev/null
@@ -0,0 +1,203 @@
+---
+postid: 08c
+title: Feedbot [iii]: the IRC bot
+date: April 25, 2019
+author: Lucian MogoČ™anu
+tags: tech, tmsr
+---
+
+All the basic building blocks pertaining to RSS functionality --
+i.e. the [checker][feedbot-i] and the [announcer][feedbot-ii] -- being
+in place, we conclude the [Feedbot][feedbot] series with:
+
+* The [V patch][feedbot-irc.vpatch] adding IRC functionality; and
+* my [seal][feedbot-irc.vpatch.spyked.sig] for it.
+
+The patch in this episode introduces the following changes:
+
+* Some methods are removed, due to their retrospective uselessness; some
+  methods are added, due to their being found useful for both IRC
+  implementation and maintenance operations.
+* Auxiliary functions are provided in order to handle IRC-specific
+  functionality and Feedbot command argument parsing.
+* Most importantly, functionality is added to: manage the bot state;
+  glue to Ircbot and adapt the announcer to IRC; and implement
+  [Trilemabot commands][trilemabot].
+
+The remainder of this post will detail the last bullet above. The
+reader is however encouraged to read the [V patch][feedbot-irc.vpatch]
+in its entirety, to get a broader image of the changes.
+
+**VII. State management**
+
+Feedbot state is a list of the form:
+
+~~~~ {.commonlisp}
+(feed-db msg-queue)
+~~~~
+
+where `feed-db` is a [feed db][feed-db] and `msg-queue` is a
+[message queue][msg-queue].
+
+Feedbot state is kept persistent using principally the feedbot
+`save-state` and `reload-state` methods:
+
+~~~~ {.commonlisp}
+(defmethod feedbot-save-state ((bot feedbot) &optional (path "state.sexp"))
+  "Save bot state to disk location given by `path'."
+  (let ((feed-db (with-feed-db (feed-db) bot feed-db))
+        (msg-queue (with-msg-queue (msg-queue) bot msg-queue)))
+    (with-open-file (out path
+                         :direction :output
+                         :if-does-not-exist :create
+                         :if-exists :supersede)
+      (write (list feed-db msg-queue) :stream out)
+      nil)))
+
+(defmethod feedbot-reload-state ((bot feedbot) &optional (path "state.sexp"))
+  "Reload bot state from disk location given by `path'."
+  (let ((state (with-open-file (in path :direction :input)
+                 (read in))))
+    (with-feed-db (feed-db) bot
+      (setf feed-db (car state)))
+    (with-msg-queue (msg-queue) bot
+      (setf msg-queue (cadr state)))
+    nil))
+~~~~
+
+**VIII. Ircbot glue**
+
+IRC glue:
+
+**a\.** implements [entry announcer][announcer] functionality:
+
+~~~~ {.commonlisp}
+(defun announce-irc! (bot msg)
+  (announce-stdout! msg)
+  (let ((rcpt (get-msg-to! msg))
+        (entry (get-msg-entry! msg)))
+    (ircbot-send-message bot rcpt
+                         (format nil "~a << ~a -- ~a~%"
+                                 (get-entry-link entry)
+                                 (get-msg-feed-title! msg)
+                                 (get-entry-title entry)))))
+~~~~
+
+**b\.** starts the checker and announcer threads on
+  [rpl_welcome][rpl-welcome]:
+
+~~~~ {.commonlisp}
+(defun feedbot-rpl_welcome (bot message)
+  (declare (ignore message))
+  (feedbot-start-checker-thread bot)
+  (feedbot-start-announcer-thread bot))
+~~~~
+
+**c\.** sends messages to online nicks on [rpl_ison][rpl-ison]:
+
+~~~~ {.commonlisp}
+(defun feedbot-rpl_ison (bot message)
+  ;; Only when our reply contains some nicks...
+  (when (cdr (arguments message))
+    (let ((nicks (parse-ison (cadr (arguments message)))))
+      ;; Process messages...
+      (feedbot-process-msg-queue
+       bot #'(lambda (msg)
+               ;; Only when msg not send and :to is online...
+               (when (and (not (get-msg-sent! msg))
+                          (member (get-msg-to! msg) nicks
+                                  :test #'string=))
+                ;; Wait a bit
+                (sleep *announce-delay*)
+                 ;; Announce and mark as sent.
+                 (announce-irc! bot msg)
+                 (set-msg-sent! msg)))))))
+~~~~
+
+**d\.** implements [ircbot-{connect,disconnect}][ircbot-connect]
+routines:
+
+~~~~ {.commonlisp}
+(defmethod ircbot-connect :after ((bot feedbot))
+  (feedbot-load-state bot)
+  (let ((conn (ircbot-connection bot)))
+    (add-hook conn 'irc-rpl_welcome-message
+              #'(lambda (message)
+                  (feedbot-rpl_welcome bot message)))
+    (add-hook conn 'irc-rpl_ison-message
+              #'(lambda (message)
+                  (feedbot-rpl_ison bot message)))))
+
+(defmethod ircbot-disconnect :after ((bot feedbot)
+                                     &optional (quit-msg "feedbot out"))
+  (declare (ignore quit-msg))
+  (with-slots (db-mutex queue-mutex checker-thread announcer-thread) bot
+    (ignore-errors
+      (release-mutex db-mutex :if-not-owner :force)
+      (release-mutex queue-mutex :if-not-owner :force)
+      (terminate-thread checker-thread)
+      (terminate-thread announcer-thread))
+    (setf checker-thread nil
+          announcer-thread nil)
+    (feedbot-flush-state bot)))
+~~~~
+
+**IX. Trilemabot commands**
+
+Consult [the feedbot manual][feedbot-manual] for more details. For
+example, the `list` command:
+
+~~~~ {.commonlisp}
+(trilemabot-define-cmd (:list bot message target arguments)
+  (declare (ignore arguments))
+  ;; Execute everything inside a named block, to handle control-flow
+  ;; smoothly
+  (block cmd-body
+    (let ((rcpt (response-rcpt bot message target)))
+      ;; Never respond to list in channel
+      (when (channel-rcpt-p rcpt)
+       (return-from cmd-body))
+
+      ;; Get list of feed ids for rcpt and send them one by one in a
+      ;; separate reply each. Delay the response to avoid flooding.
+      (let ((ids-titles
+             (feedbot-select-feeds bot
+                                                  :fields '(:id :title)
+                                                  :where #'(lambda (feed)
+                                                                 (find-rcpt-in-feed!
+                                                                  feed rcpt)))))
+            (loop for val in ids-titles do
+                (destructuring-bind (feed-id feed-title) val
+                  (sleep *announce-delay*)
+                  (ircbot-send-message bot rcpt
+                                   (format nil "~a << ~a"
+                                           feed-id feed-title))))))))
+~~~~
+
+**Post codex:** In total, Feedbot weighs circa nine hundred lines of
+code, of which almost a hundred comprise auxiliary functionality
+(parsing, sanitization and low-level IRC code), the rest containing
+mostly the Feedbot mechanism. Of all these, about 23% are comments and
+another 7% are inline function documentation strings.
+
+The mechanism is, as far as I'm concerned, done. There are some
+operator-side bits missing (e.g. bulk adding/removal of feeds), but then
+again, some of them can be easily scripted, while others, I expect,
+aren't so much "missing features" as they are bits of the user's brain
+that require fixing.
+
+Bug fixes and other comments are, as usual, more than welcome.
+
+[feedbot-i]: /posts/y05/08a-feedbot-i.html
+[feedbot-ii]: /posts/y05/08b-feedbot-ii.html
+[feedbot]: http://btcbase.org/log-search?q=feedbot
+[feedbot-irc.vpatch]: http://lucian.mogosanu.ro/src/botworks/v/patches/feedbot-irc.vpatch
+[feedbot-irc.vpatch.spyked.sig]: http://lucian.mogosanu.ro/src/botworks/v/seals/feedbot-irc.vpatch.spyked.sig
+[feed-db]: /posts/y05/08a-feedbot-i.html#selection-74.0-74.1
+[msg-queue]: /posts/y05/08b-feedbot-ii.html#selection-58.0-58.1
+[trilemabot]: /posts/y05/078-trilemabot-ii.html
+[announcer]: /posts/y05/08b-feedbot-ii.html#selection-249.0-252.0
+[rpl-welcome]: https://archive.is/oKbeB#selection-2399.225-2399.340
+[rpl-ison]: https://archive.is/oKbeB#selection-2419.765-2419.913
+[ircbot-connect]: http://btcbase.org/patches/ircbot-genesis#L100
+[feedbot-manual]: /posts/y05/081-feedbot-manual.html