TITLE=RuBBS ROUTE=/engines/rubbs LOCALE=hu MODIFIED=1792691329 URL=https://wiki.teletypegames.org/hu/engines/rubbs L=1 #66 q│ A rubbs házon belül fejlesztett Ruby gem Telnet BBS-szerverek építéséhez — q│ egy egyképernyős prompt-tól a teljes, Synchronet-stílusú többablakos q│ alkalmazásig, mindezt Ruby-ban. Ezen fut a 1BBS-szerverünk. 1Amit kapsz 1══════════ • bTelnet és ANSI0 — IAC negotiation (SGA, ECHO, NAWS terminálméret), full-screen ANSI double-buffered delta diff-fel, xterm 1000/1006 SGR egérkezelés kattintható menükhöz és scrollozható listákhoz. • bWidgetek0 — Label, Button, TextInput, TextArea, Checkbox, ListBox, ScrollView, ProgressBar, Spinner, valamint HBox / VBox / Panel konténerek és egy CP437-es ANSI-artnézegető. • bKész window-k0 — cInfo0, cForm0, cMasterDetail0, cStream0 (chat / log), imperatívan vagy egy window DSL-ből megnyitva; ablakonként bekapcsolhatóan teljes képernyőre tehetők (zoom gomb, F5, Alt-Enter, dupla kattintás a címsoron). • bTémázás0 — öt preset (csynchronet0, cnortoncommander0, cdosnavigator0, cneumanntronics0, cmono0), mindegyik egy fix szerepnév → ANSI-paletta mapping, kulcsonként felülírható. • bMarkdown és ASCII-art0 — Markdown → ANSI renderelő, box-drawing bannerek, FIGlet (Artii) nagy bannerek, ERB screen template-ek. • bSzimulált gépek0 — cBBS::FakeOS0: egy karakteres operációs rendszer konfigurációként — teletype-bootszkript, parancstábla, és egy con_unknown hook minden másra. • bDoorok0 — cBBS::Door0: egy program, amibe a hívó a menüből lép át; a BBS átadja neki a vonalat, és visszaveszi, amikor a program kilép. • bFájlletöltés0 — cBBS::Zmodem0: a ZMODEM küldő oldala, így a SyncTERM, a NetRunner, a Qodem, az crz0 vagy egy webes terminál zmodem.js-e fájlt menthet a BBS-ről; az cApplication#with_line0 adja át neki a nyers vonalat, és utána újrarajzol. • bAI-karakterek0 — cBBS::AI0: egy YAML-fájlban leírt persona, ami egy kis modellel beszél bármilyen OpenAI-kompatibilis végponton, szavanként streamelve a terminálra, és beégetett válaszokkal, ha a modell nem elérhető. • bPerzisztencia0 — cBBS::Store0: thread-safe, csession_id0 szerint upsert-elő CSV-fájl in-memory cache-sel. Olvasáskor kiszűri a fájlból az érvénytelen UTF-8-at, íráskor pedig minden értéket megtisztít (az érvénytelen bájtok és a vezérlőkarakterek kimaradnak), így a játékos inputjával mentett nyers telnet-bájtok sem tehetik tönkre a store-t. 1Áttekintés 1══════════ A gem neve cbbs0. Elindít egy TCP listener-t, lekezeli a Telnet negotiation-t, minden kapcsolatnak ad egy cSession0-t, és három réteget kínál az építkezéshez. A kódod csak screen-eket, window-kat és service-eket deklarál. p require 'bbs' p p BBS.configure do |c| p c.flow = BBS::Flow.define do p big_banner 'My BBS' p ask :name, prompt: 'Your name' p say 'Welcome!', style: :success p end p end p p BBS.start Csatlakozás: ctelnet localhost 23230. 1A három réteg 1═════════════ t┌──────────────────┬────────────────────────────┬────────────────────────────┐ t│h Réteg t│h Mi ez t│h Mikor használd t│ t├──────────────────┼────────────────────────────┼────────────────────────────┤ t│0 cBBS::Flow0 t│0 Sororientált, deklaratív t│0 Login, regisztráció, t│ t│0 t│0 párbeszéd (ask / say / t│0 egyszerű wizard-ok t│ t│0 t│0 menu / persist) t│0 t│ t├──────────────────┼────────────────────────────┼────────────────────────────┤ t│0 cBBS::TUI0 t│0 Egyképernyős page router t│0 Egycélú, full-screen t│ t│0 t│0 framebuffer-alapú delta t│0 felületek t│ t│0 t│0 renderrel t│0 t│ t├──────────────────┼────────────────────────────┼────────────────────────────┤ t│0 cBBS::Application0 t│0 Widget framework: Menubar, t│0 Többablakos, t│ t│0 t│0 Window, Button, TextInput, t│0 Synchronet-stílusú BBS-ek t│ t│0 t│0 ListBox, … t│0 t│ t└──────────────────┴────────────────────────────┴────────────────────────────┘ Mellettük három építőelem van: a cBBS::FakeOS0 (szimulált gép), a cBBS::Door (átadja a vonalat egy Applicationből egy programnak, majd vissza) és a cBBS::AI (karakter, akivel a hívó beszélgethet). Egy session-ön belül keverhetők — jellemzően cFlow0 a login-hoz, majd cApplication0 a fő felülethez. Egy flow belsejéből átadhatod a vezérlést, és visszakapod, amikor a felhasználó kilép. p TCPServer → Session → FlowRunner ─┬→ TUIRunner p ├→ Application#run ⇄ Door#run (FakeOS, …) p └→ FlowRunner (al-flow) 1Doorok 1══════ A doort az Application DSL-ben kell deklarálni. A program egy cBBS::FakeOS0-definíció, egy cBBS::Flow0, vagy bármi, ami válaszol a ccall(session, ccontext)0 hívásra: p APP = BBS::Application.define do p door :sermo, Sermo::OS, label: '&SERMO' p p menubar do p menu '&Doors' do p doors # minden deklarált doorhoz egy menüpont p end p end p end p p # a login-flow-ban: p call do |ctx, runner| p APP.run(session: runner.session, session_id: ctx[:session_id], context: ct» p x) p nil p end Az copen_door(:sermo)0 bármelyik handlerből is működik (status-bar hint, gomb). Az cAPP.run0 felépíti és futtatja az alkalmazást; ha a hívó egy dooron át hagyja el, letörli a képernyőt, kikapcsolja az egérjelentést, lefuttatja a programot, és amikor az visszatér, újra felépíti az alkalmazást. A context hash végig ugyanaz, így a door a hívás végéig megtartja az állapotát. A 0.7.0 óta: • A bflow-door0 a context egy másolatán fut, így az cask0 lépései nem írhatják felül a BBS kulcsait, és mivel egy flow-nak nincs kilépő parancsa, ENTER-t kér, mielőtt visszaadja a vonalat. A cBBS::Flow.define(screens_dir: …)0 a flow mellett tartja az ERB-képernyőit, így egy gemben szállított flow bármelyik BBS-ben ki tudja rajzolni a cscreen0 lépéseit. • A színes képernyőjű FakeOS ccolors base: "\e[94;44m"0-et állít: minden reset ehhez tér vissza, és a ccls0 ezzel tölti ki a képernyőt, így a háttér megmarad a prompt, a visszhang és az új sorok alatt is. • A cBBS::Widgets::AnsiArt.sauce(path)0 beolvassa egy art-fájl SAUCE-rekordját (cím, alkotó, csoport, dátum). A 0.8.0 óta, kis modellekhez: a cChat#reply(text, ctx, facts:, fallback:)0 egy kérésre tényeket tesz a system promptba, és a modell hibájakor a beégetett válasz helyett a megadottat adja; a persona cdeflect:0 szabályaira pedig a kód válaszol, mielőtt a modellt megkérdezné. A Teletype BBS Chat of Ra-ja és TTG Masterje ezekre épül. A 0.9.0 óta: • A cBBS::Zmodem.send_files(session, [[név, bájtok, mtime]])0 ZMODEM-mel küld fájlokat (c:ok0, c:cancelled0, c:timeout0, c:hangup0), egy capp.with_line { |session| c… }0 blokkon belül. A vezérlőkarakterek ZDLE-escape-pel, a 0xFF duplázott telnet-IAC-ként megy, így nem kell BINARY mód, és a zmodem.js is olvassa. A Teletype BBS FTP Browsere ezzel tölt le. • A cMasterDetail0 kap con_activate0-et (Enter egy elemen) és creplace_items0-et, így egy böngésző helyben válthat mappát. • Egy auditkör: a NAWS-méret korlátos (egyetlen 65535×65535-ös jelentés korábban felélte a memóriát), a sorok és a telnet-alegyeztetések hossza is, a socketek az idle-korlátot kapják időtúllépésnek, a kapcsolatok száma korlátos (cmax_sessions0); a creadline0 UTF-8-at ad, a creadkey0 UTF-8 karaktereket olvas, a CR LF egy Enter, a magányos Esc nem vár a következő billentyűre; a szövegmező átengedi az Esc-et az ablaknak, a gombok gyorsbillentyűi működnek; a keskeny terminál, a görgő az ablak szélén és egy handler kivétele már nem bontja a hívást. A 0.10.0 óta: a cmaximizable: true0 (a window DSL-ben cw.maximizable true0) Turbo Vision-stílusú zoom gombot ad az ablak jobb felső sarkába: a c[↑]0 kitölti az asztalt a menüsor és az állapotsor között, a c[↕]0 visszaállítja, és ugyanezt teszi az F5, az Alt-Enter és a dupla kattintás a címsoron. Alapból ki van kapcsolva. A cMasterDetail0, a cStream0, a cForm0 és a görgethető cInfo0 tudja; egy saját ablak a cresizable?0, a cnormal_rect0 és a cplace(x, y, w, h) megvalósításával csatlakozhat. A 0.11.0 óta: az cAnsiArt.view(session, lines, title:)0 teljes képernyőn mutat egy képet, ahogy a régi BBS-ek képnézegetői, és görget, ha magasabb a képernyőnél; egy capp.with_line0 blokkon belül fut. A 0.12.0 óta: a nem nagyított ablak mozgatható, az átméretezhető fajta méretezhető is: a címsort vagy a jobb alsó sarkot kell húzni, vagy Ctrl-F5 / F6 után a nyilakkal (Shift+nyilak méreteznek, a Tab vált, az Enter befejezi). A cmovable: false0 kikapcsolja; a telnet-olvasó visszaadja az xterm-módosítókat (c:shift_up0, c:ctrl_f50). A 0.13.0 óta: az con_tick { … }0 másodpercenként lefut az alkalmazásban, amíg a hívó nem nyom semmit (óra a cstatusbar.right_text0-en), az con_door { |door, ccontext| … }0 pedig közvetlenül azelőtt, hogy a vonal egy doorhoz kerül (ki melyik doorban van, egy node-listához). A cstatus_hint(key, label, hidden: ctrue)0 úgy köt be egy billentyűt, hogy nem foglal helyet az állapotsoron. Javítások: a cDialogs.input0 mezőjében az Enter megnyomja az OK-t; a kivételt dobó door visszaviszi a hívót a BBS-be, ahelyett hogy bontaná a hívást; a Markdown-listaelem alatti behúzott sor folytatja az elemet. A Teletype BBS node-jai, állapotsori órája és belépési szekvenciája ezekre épül. A 0.14.0 óta az ablakok között Turbo Vision-módra lehet váltani. Az aktív mögötti ablakra kattintva az előre jön (és a kattintás rögtön hat is rajta), az F6 és a Shift-F6 lépteti őket, az Alt-1 … Alt-9 a felső keretükön látható szám alapján választ. Az aktív ablak dupla, a többi szimpla kerettel rajzolódik. A modális ablak (a cDialogs0) elöl marad, amíg be nem zárják; a cBBS::Windows0 típusok már nem modálisak. Az F6 csak akkor mozgat, ha egyetlen ablak van nyitva, a Ctrl-F5 mindig. Kódból az capp.raise_window0, a ccycle_windows0 és a cswitchable_windows0 teszi ugyanezt. 1AI-karakterek 1═════════════ A persona-fájl írja le, ki a karakter: p system: | p You are the oracle of this board. … p examples: # körök a megfelelő hangon p - user: hi p assistant: I am here. p options: # további kérésmezők, pl. mintavétel p max_tokens: 96 p temperature: 0.4 p fallback: # beégetett válaszok, fentről lefelé próbálva p replies: p - match: '\b(hello|hi)\b' p reply: I am here. p default: 'You said: "{input}".' a többit négy osztály végzi: p ORACLE = BBS::AI::Chat.new( p persona: BBS::AI::Persona.load('lib/oracle.yml'), p client: BBS::AI::Client.new(url: ENV.fetch('ORACLE_LLM_URL', '')), p history: 2 p ) p p on_unknown do |input, io| # egy FakeOS-definícióban p out = BBS::AI::Typewriter.new(io, prefix: 'ORACLE: ', style: :dim) p ORACLE.reply(input, io.ctx) { |piece| out.write(piece) } p out.finish p end t┌────────────┬───────────────────────────────────────────────────────────────┐ t│h Osztály t│h t│ t├────────────┼───────────────────────────────────────────────────────────────┤ t│0 cPersona0 t│0 a YAML-fájl: system prompt, példakörök, kérésbeállítások, t│ t│0 t│0 beégetett válaszok t│ t├────────────┼───────────────────────────────────────────────────────────────┤ t│0 cClient0 t│0 streamel egy OpenAI-kompatibilis c/v1/chat/completions0 t│ t│0 t│0 végpontról (llama.cpp cllama-server0, Ollama, vLLM, hosztolt t│ t│0 t│0 API); üres URL = nincs modell t│ t├────────────┼───────────────────────────────────────────────────────────────┤ t│0 cChat0 t│0 elküldi a personát, az előzményt és a kérdést, az előzményt a t│ t│0 t│0 hívó contextjében tartja, kiszűri a markdownt és az emojikat, t│ t│0 t│0 és visszaesik a beégetett válaszra t│ t├────────────┼───────────────────────────────────────────────────────────────┤ t│0 cTypewriter0 t│0 a streamelt választ a hívó terminálszélességére tördeli egy t│ t│0 t│0 prefix alatt t│ t└────────────┴───────────────────────────────────────────────────────────────┘ Kis modellnél (a 1SERMO Gemma 3 270M-mel fut) a példakörök többet számítanak, mint a system prompt, a hosszú előzménytől pedig a modell a saját kiesését ismétli: a tényeket példakörként tanítsd, és a chistory0 legyen rövid. 1Telepítés 1═════════ p gem 'bbs', git: 'https://git.teletypegames.org/engines/rubbs.git' 1A Teletype Games BBS ezt használja 1══════════════════════════════════ A nyilvános 1BBS-szerverünk (cteletypegames.org:23230) rubbs-on fut. A gem adja az ANSI-felületet, a menubar-t, a stackelt modal window-kat, a Telnet-szervert, az egértámogatást, a doorokat és az AI-chatet; a cbbs-server repóban csak a Teletype-specifikus konfig van — service-bekötések, window-definíciók, formatter-ek. A SERMO doorja egy cBBS::FakeOS0, aminek az analóg oldala egy cBBS::AI0-karakter; a Commodore 64 és a Plus/4 door színes cbase0-ű FakeOS-gép, az Impostor door pedig egy gemből jövő cBBS::Flow0 a saját képernyőivel. 1Forrásszerkezet 1═══════════════ t┌─────────────────────────────────────┬──────────────────────────────────────┐ t│h Fájl t│h Feladat t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 clib/bbs.rb0 t│0 Publikus felület: cBBS.configure0, t│ t│0 t│0 cBBS.config0, cBBS.start0. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/server.rb0 t│0 TCP accept loop, kliensenként egy t│ t│0 t│0 thread. A kliens bontása (reset, t│ t│0 t│0 broken pipe, timeout) eldobott t│ t│0 t│0 sessionként kerül a logba, nem t│ t│0 t│0 hibaként. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/session.rb0 t│0 Telnet negotiation, egér t│ t│0 t│0 ki-/bekapcsolás, idle timer. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/telnet.rb0 t│0 Telnet-protokoll, key parsing, xterm t│ t│0 t│0 SGR egér. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/flow.rb0, cbbs/flow_runner.rb0 t│0 Flow DSL és interpreter. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/tui.rb0, cbbs/tui_runner.rb0 t│0 Page router framebuffer-alapú t│ t│0 t│0 rendereléssel. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/frame_buffer.rb0 t│0 Double buffer, delta diff, ANSI t│ t│0 t│0 string utility-k. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/application.rb0 t│0 Widget event loop, window stack, t│ t│0 t│0 builder DSL. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/widgets.rb0, cbbs/widget.rb0, t│0 Widget-fa és beépített widget-ek. t│ t│0 cbbs/container.rb0 t│0 t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/window.rb0, cbbs/windows/*0 t│0 Window és kész window-típusok. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/fake_os.rb0 t│0 Karakteres OS-szimulátor: t│ t│0 t│0 bootszkript és parancsértelmező. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/door.rb0 t│0 Doorok: a vonal átadása egy t│ t│0 t│0 programnak és visszavétele. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/zmodem.rb0 t│0 ZMODEM-küldés: fájlletöltés a hívó t│ t│0 t│0 termináljára. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/ai.rb0 t│0 AI-karakterek: persona-fájl, t│ t│0 t│0 streamelő kliens, chat előzménnyel t│ t│0 t│0 és visszaeséssel, t│ t│0 t│0 typewriter-kimenet. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/theme.rb0 t│0 Stíluspaletta preset-ek. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/markdown.rb0 t│0 Markdown → ANSI line renderer. t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cbbs/store.rb0 t│0 Thread-safe, cached CSV upsert; t│ t│0 t│0 olvasáskor és íráskor kiszűri az t│ t│0 t│0 érvénytelen UTF-8-at. t│ t└─────────────────────────────────────┴──────────────────────────────────────┘ 1Repó 1════ https://git.teletypegames.org/engines/rubbs — MIT-licenc (cLICENSE0).