Subject: [PATCH v2 1/2] services: Add snuik-service-type.

From: Janneke Nieuwenhuizen <[email-masked]>
Date: Thu, 13 Mar 2025 20:11:03 +0100

* gnu/services/messaging.scm (snuik-configuration): New type.
(snuik-services): New procedure.
(snuik-activation, %snuik-accounts, snuik-service-type): New variables.
* doc/guix.texi (Messaging Services): Document it.

Change-Id: I0c52b4804948876dc1b4d3b5ac660af848a13f25
---
 doc/guix.texi              |  59 ++++++++++++++++++-
 gnu/services/messaging.scm | 114 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 171 insertions(+), 2 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index d109877a32c..083e561e48c 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -36,7 +36,7 @@
 Copyright @copyright{} 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Efraim Flashner@*
 Copyright @copyright{} 2016 John Darrington@*
 Copyright @copyright{} 2016, 2017 Nikita Gillmann@*
-Copyright @copyright{} 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024 Janneke Nieuwenhuizen@*
+Copyright @copyright{} 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025 Janneke Nieuwenhuizen@*
 Copyright @copyright{} 2016, 2017, 2018, 2019, 2020, 2021 Julien Lepiller@*
 Copyright @copyright{} 2016 Alex ter Weele@*
 Copyright @copyright{} 2016, 2017, 2018, 2019, 2020, 2021 Christopher Baines@*
@@ -30208,6 +30208,63 @@ Messaging Services
 @end table
 @end deftp
 
+@cindex irc-bot
+@cindex snuik
+The @uref{https://gitlab.com/janneke/snuik, Snuik IRC bot} can be run as
+a daemon to aid talking to users that are currently offline.  With the
+@code{(gnu services messaging)} service, you can configure it by adding
+a @code{snuik-service-type} service to the @code{services} field of your
+operating system declaration.
+
+@defvar snuik-service-type
+This is the type of the Snuik service, whose value is a
+@code{snuik-configuration} object.
+@end defvar
+
+Here is an example of a service and its configuration:
+
+@lisp
+(service snuik-service-type
+         (snuik-configuration
+          (password-file "/var/run/snuik/.password.snuik")
+          (channels '("#bootstrappable"
+                      "#dezyne"
+                      "#guix-risc-v"
+                      "#lilypond"))))
+@end lisp
+
+@deftp {Data Type} snuik-configuration
+Available @code{snuik-configuration} fields are:
+
+@table @asis
+@item @code{snuik} (default: @code{snuik}) (type: package)
+The Snuik package to use.
+
+@item @code{server} (default: @code{"irc.libera.chat"})
+The IRC server to connect to.
+
+@item @code{port} (default: @code{6665}) (type: integer)
+Port number used by the IRC server.
+
+@item @code{nick} (default: @code{"snuik"}) (type: string)
+The nickname for snuik to use.
+
+@item @code{password} (default: @code{#f}) (type: string)
+The password to use when logging in.
+
+@item @code{password-file} (default: @code{".password.<nick>})
+The file to read the password from to use when logging in.
+
+@item @code{channels} (default: @code{'("##botchat")})
+The channels for snuik to join, a list of strings.
+
+@item @code{extra-options} (default: @code{'()})
+Extra options will be passed to @command{snuik}, please run
+@command{snuik --help } for more information.
+
+@end table
+@end deftp
+
 @node Telephony Services
 @subsection Telephony Services
 
diff --git a/gnu/services/messaging.scm b/gnu/services/messaging.scm
index a914d0f89ec..9bfeabacf45 100644
--- a/gnu/services/messaging.scm
+++ b/gnu/services/messaging.scm
@@ -59,7 +59,10 @@ (define-module (gnu services messaging)
             bitlbee-service-type
 
             quassel-configuration
-            quassel-service-type))
+            quassel-service-type
+
+            snuik-configuration
+            snuik-service-type))
 
 ;;; Commentary:
 ;;;
@@ -1002,3 +1005,112 @@ (define quassel-service-type
                  "Run @url{https://quassel-irc.org/,quasselcore}, the backend
 for the distributed IRC client quassel, which allows you to connect from
 multiple machines simultaneously.")))
+
+
+;;;
+;;; Snuik.
+;;;
+(define-maybe integer (no-serialization))
+(define-configuration/no-serialization snuik-configuration
+  (snuik         (package snuik)       "The snuik package to use.")
+  (server        maybe-string          "The IRC server to connect to.")
+  (port          maybe-integer         "The port used by the IRC server.")
+  (nick          maybe-string          "The nickname for snuik to use.")
+  (password      maybe-string          "The password to use when logging in.")
+  (password-file maybe-string          "The file to read the password from.")
+  (channels      (list-of-strings '()) "The channels for snuik to join.")
+  (extra-options (list-of-strings '()) "Extra options to be passed to snuik.")
+  (home-service? (boolean for-home?)   "Running as home service?"))
+
+(define (snuik-services config)
+  "Return a <shepherd-service> for snuik with CONFIG."
+  (match-record config
+      <snuik-configuration>
+      (snuik server port nick password password-file channels extra-options
+             home-service?)
+    (let* ((password-file (snuik-configuration-password-file config))
+           (mappings `(,@(if home-service?
+                             '()
+                             `(,(file-system-mapping
+                                 (source "/var/run/snuik")
+                                 (target source)
+                                 (writable? #t))
+                               ,@(if password-file
+                                     (list (file-system-mapping
+                                            (source password-file)
+                                            (target source)))
+                                     '())))))
+           (snuik (least-authority-wrapper
+                   (file-append snuik "/bin/snuik")
+                   #:name "snuik"
+                   #:mappings mappings
+                   #:namespaces (delq 'net %namespaces)))
+           (command
+            #~'(#$snuik
+                #$@(if (and server (not (eq? server %unset-value)))
+                       (list (string-append "--server=" server))
+                       #~())
+                #$@(if (and port (not (eq? port %unset-value)))
+                       (list (string-append "--port=" (number->string port)))
+                       #~())
+                #$@(if (and nick (not (eq? nick %unset-value)))
+                       (list (string-append "--nick=" nick))
+                       #~())
+                #$@(if (and password (not (eq? password %unset-value)))
+                       (list (string-append "--password=" password))
+                       #~())
+                #$@(if (and password-file (not (eq? password-file %unset-value)))
+                       (list (string-append "--password-file=" password-file))
+                       #~())
+                #$@(if (pair? channels)
+                       (list (string-append "--channels=" (string-join channels ",")))
+                       #~())
+                #$@extra-options))
+           (log-file #~(string-append
+                        #$(if home-service? #~%user-log-dir "/var/log")
+                        "/snuik.log")))
+      (list (shepherd-service
+             (documentation "Run the snuik IRC bot.")
+             (provision '(snuik))
+             (requirement (if home-service? '() '(user-processes networking)))
+             (modules (if home-service?
+                          '((shepherd support)) ;for '%user-log-dir'
+                          '()))
+             (start #~(make-forkexec-constructor
+                       #$command
+                       #:log-file #$log-file
+                       #:user #$(and (not home-service?) "snuik")
+                       #:group #$(and (not home-service?) "snuik")))
+             (stop #~(make-kill-destructor)))))))
+
+(define snuik-activation
+  (with-imported-modules '((guix build utils))
+    #~(begin
+        (use-modules (guix build utils))
+        (let* ((user (getpw "snuik"))
+               (directory "/var/run/snuik"))
+          (mkdir-p directory)
+          (chown directory (passwd:uid user) (passwd:gid user))))))
+
+(define %snuik-accounts
+  (list (user-group (name "snuik") (system? #t))
+        (user-account
+         (name "snuik")
+         (group "snuik")
+         (system? #t)
+         (comment "Snuik IRC bot user")
+         (home-directory "/var/run/snuik")
+         (shell (file-append shadow "/sbin/nologin")))))
+
+(define snuik-service-type
+  (service-type
+   (name 'home-snuik)
+   (description "Run the Snuik IRC bot.")
+   (default-value (snuik-configuration))
+   (extensions
+    (list (service-extension activation-service-type
+                             (const snuik-activation))
+          (service-extension account-service-type
+                             (const %snuik-accounts))
+          (service-extension shepherd-root-service-type
+                             snuik-services)))))

base-commit: 678b3dddfe442e643fe5cff7730d4f9690c3e2c2
-- 
2.47.1