From 90a5694d43c89c02c88a2b0a3de8f5f7e6b2f5f0 Mon Sep 17 00:00:00 2001 From: Lucian Mogosanu Date: Thu, 25 Apr 2019 22:11:58 +0300 Subject: [PATCH] posts: 08c --- drafts/000-feedbot-iii.markdown | 173 ------------------------------ posts/y05/08c-feedbot-iii.markdown | 203 ++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+), 173 deletions(-) delete mode 100644 drafts/000-feedbot-iii.markdown create mode 100644 posts/y05/08c-feedbot-iii.markdown diff --git a/drafts/000-feedbot-iii.markdown b/drafts/000-feedbot-iii.markdown deleted file mode 100644 index e75ab2f..0000000 --- a/drafts/000-feedbot-iii.markdown +++ /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 index 0000000..0e68c73 --- /dev/null +++ b/posts/y05/08c-feedbot-iii.markdown @@ -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 -- 1.7.10.4