URI:
       Directory listing for: qdb
   DIR Parent directory (..)
       
  TEXT README.txt (13.1KB, 2026-05-21):
       
        qdb -- a Gopher <-> IRC quote database
       =============================================================
       
       qdb bridges IRC and gopherspace. In #main on irc.someodd.zip you
       type
       
           .qdb 10
       
       and the last 10 lines of channel buffer are saved forever as a
       plain-text file; oddbot replies in-channel with the gopher
       permalink. In gopherspace those snapshots are browsable as text or
       rendered as a Microsoft Comic Chat strip.
       
       The directory of .txt files IS the database -- no schema, no SQLite.
       Each file is self-contained: a setext header block then the raw
       transcript.
       
       ------------------------------------------------------------
        Directory contents
       ------------------------------------------------------------
       
         qdbviewer.lhs        the gopher applet (read side). Literate
                              Haskell, run by Venusia's .lhs script-
                              extension.
         qdb-watch.sh         capture side: tails ii's channel `out` file,
                              writes quotes/<id>.txt on `.qdb`, announces
                              the permalink back
         qdb-logprune.sh      weekly trim of ii's append-only `out` logs
         qdb-comic            render shim: a snapshot -> a Comic Chat strip
                              PNG via Spittoon. Called by qdbviewer.lhs.
         comic-config.yaml    Spittoon config used by qdb-comic
         spittoon-compat.rb   Ruby compatibility shim that lets Spittoon
                              (2005-era) run on modern RMagick + Ruby 3.x
         qdb.env              deployment config for the systemd units
         systemd/             qdb-stunnel / qdb-ii / qdb-watch / qdb-logprune
                              .service and .timer files
         stunnel/qdb.conf     TLS tunnel config (only used if the IRC server
                              is remote --- see setup section)
         setup.sh             installs the systemd user units
         README.txt           this file
         quotes/              data dir: <id>.txt snapshots, cached
                              <id>.comic.png renders, and a .counter file
       
       One level up, ../qdb.bcard is the directory sidecar shown when you
       browse the /applets menu. (Subdirectory applets get exactly one
       .bcard, at the parent level --- not another one inside.)
       
       ------------------------------------------------------------
        The .qdb command (IRC side)
       ------------------------------------------------------------
       
         .qdb        save the last 10 lines of channel buffer
         .qdb N      save the last N lines (1..50)
       
       oddbot replies with the gopher permalink. The buffer holds the last
       50 chat lines the watcher has seen since it started -- joins, parts,
       and other `-!-` server notices are excluded, so a snapshot is
       conversation, not channel noise.
       
       ------------------------------------------------------------
        Endpoints (gopher side)
       ------------------------------------------------------------
       
       `$SCRIPT` is wherever routes.toml mounts qdbviewer.lhs, e.g.
       /applets/qdb/qdbviewer.lhs
       
         $SCRIPT
               landing -- latest quotes, browse, random
       
         $SCRIPT/browse
               full index, newest first, plus a link to the raw
               quotes/ directory
       
         $SCRIPT/random
               a random quote's menu
       
         $SCRIPT/q/<id>
               per-quote menu: read-as-text / Comic Chat strip /
               raw .txt, plus a bookmarkable permalink
       
         $SCRIPT/q/<id>/txt
               the quote as plain gopher text
       
         $SCRIPT/q/<id>/comic
               the quote as a Microsoft Comic Chat strip (PNG). Rendered
               lazily on first request and cached beside the .txt as
               <id>.comic.png; later requests are served straight from
               the cached file by Venusia's [[files]] block.
       
       `<id>` is always all-digits; anything else is rejected.
       
       ------------------------------------------------------------
        From the command line
       ------------------------------------------------------------
       
           curl gopher://gopher.someodd.zip/1/applets/qdb/qdbviewer.lhs
           curl gopher://gopher.someodd.zip/1/applets/qdb/qdbviewer.lhs/q/0001
           curl gopher://gopher.someodd.zip/0/applets/qdb/qdbviewer.lhs/q/0001/txt
           curl gopher://gopher.someodd.zip/I/applets/qdb/qdbviewer.lhs/q/0001/comic > 0001.png
       
       ------------------------------------------------------------
        Snapshot file format (quotes/<id>.txt)
       ------------------------------------------------------------
       
           qdb #0042
           =========
           saved-by: alice
           channel:  #main
           network:  irc.someodd.zip
           date:     2026-05-14T17:30:00Z
           lines:    10
       
           2026-05-14 17:29 <alice> it compiled on my machine i swear
           2026-05-14 17:29 <bob>   your machine is a liar
       
       Title line + setext underline, a block of `key: value` headers, a
       blank line, then the verbatim transcript. The parser is tolerant:
       missing headers render as "?", a malformed file still shows its
       body.
       
       ------------------------------------------------------------
        Setting it up
       ------------------------------------------------------------
       
       qdb is two halves: a gopher applet (no setup beyond dropping the
       files in place) and an always-on IRC capture daemon (three small
       systemd user services).
       
       1. FILES
          Put this directory at applets/qdb/ under Venusia's gopher root,
          with ../qdb.bcard beside it. The applet is live immediately --
          Venusia's existing /applets [[files]] block already runs .lhs.
       
       2. routes.toml  (optional)
          The applet links cached comics as image items itself, so nothing
          is strictly required. To also have raw quotes/*.png show as
          images when the directory is browsed, add to the /applets
          [[files]] block:
       
              [[files.file_type]]
              extension = "png"
              item_type = "I"
       
       3. PERMISSIONS
          quotes/ must be writable by BOTH the watcher (snapshots) and the
          Venusia daemon (cached PNGs). Make it group-venusia, group-
          writable, setgid, and run the watcher as a user in group venusia:
       
              chgrp venusia applets/qdb applets/qdb/quotes
              chmod 2775    applets/qdb applets/qdb/quotes
       
       4. IRC CAPTURE DAEMON
          Edit qdb.env (nick, channel, network display name), then run --
          as the user the bot should run as (a member of group venusia):
       
              ./setup.sh
              systemctl --user enable --now qdb-ii qdb-watch
       
          setup.sh installs the user units; the two enabled here are:
       
              qdb-ii     ii: connects to the IRC server, joins the channel
              qdb-watch  tails ii's `out`, writes snapshots, announces
                         (BindsTo qdb-ii)
       
          Follow them:
       
              journalctl --user -u qdb-ii -u qdb-watch -f
       
          For the bot to survive logout:  loginctl enable-linger "$USER"
       
          ON SIMULACRA the bot runs as `venusia` -- the same user Venusia
          runs applets as. That makes `venusia` the OWNER of ii's `out` log,
          so any applet can read it directly with no cross-user group hop;
          the group-venusia/setgid quotes/ setup in step 3 still applies and
          is satisfied trivially. (Running as venusia also means applet code
          could write ii's `in` FIFO -- acceptable here, where only trusted
          venusia-owned applets run.)
       
          SHARED LOG: ii's `out` is a single append-only transcript with two
          independent readers -- qdb-watch (this: curated `.qdb` quotes) and
          girc.lhs's `/log` view (full scrollback). One logger, one log, two
          gopher front-ends; see ../girc.lhs.
       
          ii connects to 127.0.0.1:6667. On simulacra the IRC server
          (ngircd) runs on the same host, so that is a loopback connection
          -- no TLS needed, the traffic never leaves the machine. Because
          ii dials 127.0.0.1 its on-disk dir is ~/irc/127.0.0.1/ --
          qdb.env's QDB_IRC_NETDIR reflects that, while QDB_IRC_NET is the
          pretty name written into each snapshot.
       
          IF YOUR IRC SERVER IS REMOTE:
          ii is plaintext-only, so put TLS in front of it. setup.sh also
          installs qdb-stunnel.service (left disabled). Point the `connect`
          line in stunnel/qdb.conf at your server, then also:
       
              systemctl --user enable --now qdb-stunnel
       
          stunnel listens on 127.0.0.1:6667 and tunnels to the remote
          server; ii's connection is unchanged.
       
          Caveats:
          - ii does not auto-rejoin after an IRC-side reconnect. Run
            `systemctl --user restart qdb-ii` to rejoin.
          - NickServ identification, if the nick needs it, can be added as
            another ExecStartPost in qdb-ii.service.
       
       5. LOG PRUNING  (optional)
          ii has no built-in rotation -- it appends every channel line to
          one `out` file per channel, forever. qdb-logprune.sh trims those
          to a recent window (QDB_LOG_KEEP_DAYS in qdb.env, default 14);
          setup.sh installs it as a weekly timer, left disabled. Turn it on
          with:
       
              systemctl --user enable --now qdb-logprune.timer
       
          Because ii holds `out` open, the prune briefly stops qdb-ii (and
          thus qdb-watch) while it rewrites the files, ~10s once a week.
          qdb snapshots in quotes/ are NEVER pruned -- they are permanent,
          which is the whole point.
       
       ------------------------------------------------------------
        Comic Chat (the /comic render mode)
       ------------------------------------------------------------
       
       /comic shells out to qdb-comic, which drives Spittoon (statico/
       spittoon) -- an implementation of the Microsoft Comic Chat
       SIGGRAPH '96 layout algorithm.
       
       Dependencies (root-level, one-time):
       
         - Ruby + ImageMagick + RMagick
               sudo apt install imagemagick libmagickwand-dev ruby-dev
               sudo gem install rmagick           # must be a SYSTEM gem ---
                                                  # /comic runs as `venusia`
         - Spittoon
               sudo git clone https://github.com/statico/spittoon /opt/spittoon
       
       qdb works without these; /comic just returns a graceful "is Spittoon
       installed?" error until they are in place.
       
       Spittoon is 2005-era Ruby and predates several modern API changes
       (RMagick configuration blocks no longer instance_eval; File.exists?
       removed in Ruby 3.2). qdb-comic loads spittoon-compat.rb via `ruby
       -r` ahead of make_comic.rb to bridge those --- without touching the
       root-owned /opt/spittoon tree. If you upgrade RMagick / Ruby and
       /comic starts failing, the shim is the first place to look.
       
       qdb-comic is zero-config in the standard layout:
       
         - Spittoon lib/bin   /opt/spittoon/lib, /opt/spittoon/bin/make_comic.rb
         - Spittoon config    comic-config.yaml (beside qdb-comic)
         - assets dir         the dir cd'd into so the config's relative
                              artwork/background paths resolve: ./comic-assets/
                              if it has an artwork/ subdir, else
                              /opt/spittoon/examples/
       
       All four are overridable: QDB_SPITTOON_LIB, QDB_SPITTOON_BIN,
       QDB_SPITTOON_CONFIG, QDB_COMIC_ASSETS. Also QDB_COMIC_MAXPANELS
       (default 24) and QDB_COMIC_RETRIES (default 5).
       
       AVATARS -- the authentic look:
       
         Out of the box qdb-comic uses Spittoon's bundled example artwork
         (bucket/easy/melon/nibbles/taff). The real Microsoft Comic Chat
         cast (TIKI, MAYNARD, ANNA, ...) shipped as .avb files and is
         NOT a drop-in retrofit; here is the state of the world as of
         the build of this kit:
       
           - AVBuster (the only known .avb extractor) does NOT work on
             the official MS-shipped characters, only on user-made custom
             ones. <https://archive.org/details/AVBuster>
           - files.defcon.no/mscc/ has all 22 official characters as PNGs,
             but only ONE pose each (single portraits, ~50x128 px).
           - Spittoon expects 17 sprites per character (10 face names
             composited as the head + 7 pose names composited as the body),
             so the defcon portraits do not slot in directly.
           - The .avb v2.1/v2.5 format is documented at
             <http://justsolve.archiveteam.org/wiki/Microsoft_Comic_Chat>,
             but the official files appear to have additional encoding
             beyond what is published. Writing a working extractor is real
             preservation work, not a config swap.
       
         Drop-in path if you DO end up with a full per-expression sprite
         set: put it under a comic-assets/ dir beside qdb-comic and it is
         picked up automatically.
       
             comic-assets/artwork/faces/<character>-<face>.png
             comic-assets/artwork/poses/<character>-<pose>.png
             comic-assets/backgrounds/*.jpg
       
         Every character needs a PNG for each face name in comic-config.yaml
         (angry, annoyed, bored, grinning, grossed, happy, listening,
         shocked, speaking, surprised) and each pose name (exclaiming,
         explaining, pointing, question, standing1, standing2, surprised).
         List the character names under `characters:` in comic-config.yaml.
       
       Spittoon picks layout variations at random and occasionally bails
       when one doesn't fit; qdb-comic retries to absorb that. The first
       /comic hit for a quote renders and caches <id>.comic.png beside the
       .txt; later hits are served straight from the cache.
       
       ------------------------------------------------------------
        Running the doctests
       ------------------------------------------------------------
       
       qdbviewer.lhs's pure helpers carry doctest examples:
       
           stack exec --resolver lts-22.6 \
             --package doctest \
             --package text --package bytestring --package directory \
             --package filepath --package time --package process \
             -- doctest -XOverloadedStrings qdbviewer.lhs
       
       ------------------------------------------------------------
        Limits
       ------------------------------------------------------------
       
       `.qdb N` is clamped to 1..50. The ring buffer is 50 lines; right
       after a watcher restart it is short until the channel talks again.
       The comic render is capped at 24 panels (QDB_COMIC_MAXPANELS) --
       longer quotes show their first 24 lines as a comic; /txt always
       shows the whole thing. Unrecognised path-info, bad ids, and missing
       quotes come back as a type-3 row plus a "back to qdb" link.
       
   DIR quotes/                                         0B  2026-06-10
   DIR stunnel/                                        0B  2026-05-15
   DIR systemd/                                        0B  2026-05-15
   BIN comic-config.yaml                            2.2KB  2026-05-15
   BIN qdb-comic                                    8.4KB  2026-06-09
   BIN qdb-comic.bak.20260609                       7.2KB  2026-05-15
  TEXT qdb-logprune.sh                              1.8KB  2026-05-15
  TEXT qdb-watch.sh                                 5.2KB  2026-05-21
   BIN qdb.env                                       979B  2026-05-15
  TEXT qdbviewer.lhs                               25.6KB  2026-05-15
  TEXT setup.sh                                     1.7KB  2026-05-15
   BIN spittoon-compat.rb                           3.9KB  2026-06-09
   BIN spittoon-compat.rb.bak.20260609              2.7KB  2026-05-15