EN /HU | Login

Scriptorium BBS

The library of the BBS network: thousands of public-domain books from Project Gutenberg, cut into pageable chapters for a modern terminal or a Commodore 64.

Live at teletypegames.org:2330, in the browser on bbsweb, and through TTG Gate BBS

Overview

A Ruby app on the rubbs gem, like the Teletype BBS, Alexandria BBS and Commodex BBS.

It has two faces on one port:

  • Modern: ANSI colours, windows, menus and the mouse (SyncTERM, NetRunner, PuTTY, bbsweb). Shelves, books and chapters in two-pane windows, and a reader window that wraps the chapter to its width.
  • Commodore 64: PETSCII on a 40×25 screen, for a C64 terminal program (CCGMS, Novaterm, UltimateTerm) behind a WiFi modem. A one-key menu, numbered lists, and 38-column pages that wait for RETURN.

A call starts with rubbs' BBS::Terminal question: RETURN takes the guess, 1 picks the modern face, 2 the C64.

The books

  • The catalog is Project Gutenberg's own, pg_catalog.csv.gz (about 78,000 texts and 400 bookshelves). The board imports it into SQLite with a full-text index on titles and authors and reads it again every week; the old catalog serves until the new one is ready. Every search word matches as a word prefix, accents do not matter (bronte finds Brontë), and lang:fr keeps one language.
  • The Classics shelf is the board's own pick of 36 well-known books (config/classics.yml); it works even before the first catalog import.
  • A book's text is fetched from gutenberg.org when someone first opens it, cleaned (the Project Gutenberg header and footer off, prose lines joined into paragraphs) and cut into chapters by its headings (CHAPTER, BOOK, PART, ACT, STAVE, LETTER, CANTO, roman numerals…), skipping the table of contents. A very long chapter is split into parts; a book with no headings into sections of about 16 KB. The cleaned text and its chapter index stay in a disk cache (300 MB, least recently read out first), and only the chapter on the screen is read into memory.
  • Most read counts the books opened on this board, once per call.

Every book page credits it: eBook #N from Project Gutenberg, gutenberg.org/ebooks/N — public domain in the USA.

Reading

On a modern terminal:

Key Action
F2 The Classics shelf
F3 Search by title or author
F4 Project Gutenberg's bookshelves
F7 Most read on this board
Enter or Alt-R Open a book / read a chapter
PgUp, PgDn, Home, End Page in the reader
← → or P, N Previous / next chapter
F5 or Alt-Enter Full screen and back
Alt-X Log off

On a Commodore 64 the menu has 1 the Classics, 2 the bookshelves, 3 search, 4 most read, A about, Q log off. A book has an info page and a numbered chapter list; in the reader RETURN gives the next page (and at the end the next chapter), B goes back a page, N/P change chapter, Q goes back to the list. Curly quotes, dashes and accented letters are turned into what a C64 can show.

Configuration and data

Variable Default
SCRIPTORIUM_BBS_MIRROR https://www.gutenberg.org where the texts come from; a mirror works too
SCRIPTORIUM_BBS_CATALOG_URL https://www.gutenberg.org/cache/epub/feeds/pg_catalog.csv.gz the catalog
SCRIPTORIUM_BBS_CATALOG_TTL 7 days before the catalog is read again
SCRIPTORIUM_BBS_CLASSICS config/classics.yml the Classics shelf
SCRIPTORIUM_BBS_DB data/catalog.sqlite3 the imported catalog
SCRIPTORIUM_BBS_CATALOG_FILE data/pg_catalog.csv.gz the downloaded catalog
SCRIPTORIUM_BBS_TEXTS data/texts the text cache
SCRIPTORIUM_BBS_CACHE_MB 300 the text cache's size
SCRIPTORIUM_BBS_BOOK_MAX 8388608 the largest book fetched, in bytes
SCRIPTORIUM_BBS_READS data/reads.jsonl the reads, one JSON line each

The board has no secrets. After a change to the chapter rules, clear data/texts so the cached books are cut again.

Under the hood

A push to master deploys it from Woodpecker (RuboCop, compose check, ci-deploy scriptorium-bbs); the container is scriptorium-bbs, in /srv/stacks/scriptorium-bbs, and data/ stays between deploys. The entrypoint updates rubbs on start.

The code follows the house layering: lib/domain (the book, shelf, chapter and text models; the Gutenberg, catalog, classics, read and text-cache repositories; ChapterService, LibraryService, ReadingService; the composition root ScriptoriumBBS) knows nothing of telnet, and lib/ui holds UI::Modern with its ReaderWindow and UI::Petscii. A book is fetched and cut one at a time, so a burst of new books cannot blow the memory limit.