ANetBBS Changelog

Versions are internal build numbers. Public releases are tagged
separately. Current release: v1.0b2.33 (July 2026). Full release: August 1 2026.

v1.0a2.149 — Echomail area sysop-only flag + security levels (June 2026)

  • Echomail EchoAreaForm: added is_sysop_only BooleanField; min_access_level already existed in the form but wasn't exposed in the template.
  • Area edit form (echomail/admin/area_form.html): Active, Subscribed, Sysop Only checkboxes now shown together in a flags box; Min Access Level field alongside with hint text.
  • Areas list (echomail/admin/areas.html): Status column replaced with four separate columns — Active ✓/✗, Subscribed ✓/✗, Sysop Only 🔒/—, Level badge (color-coded by range).
  • new_area route: now saves is_sysop_only and min_access_level on area creation.
  • web/echomail.py: extracted _check_area_access(area) helper; applied to area view, thread, compose, and next-unread routes. Area index and compose dropdown filter by both is_sysop_only and min_access_level <= user.access_level.
  • New templates: auth/verify_sent.html, auth/verified.html, auth/resend_verification.html.
  • Admin nav: new "SMTP / Email" link above Settings divider.

6 new web games

  • Klondike Solitaire — full rules, undo, auto-foundation, win detection.
  • Video Poker (Jacks or Better) — 5-card draw, pay table, multi-bet, hold mechanic.
  • Texas Hold'em vs CPU — 4-player table, easy/medium/hard bluffing AI, full 7-card hand eval.
  • Blackjack — hit/stand/double/split, 4-deck shoe, chip-click bet UI, dealer to 17.
  • Slot Machines — 3 themed machines (Classic Bars, Lucky Fruits, Retro BBS) with animated reels.
  • Galaga — Canvas arcade: enemy formations, dive attacks, tractor-beam boss capture, particle FX, touch controls.

v1.0a2.146 — Fix dial-out directory DB lookup in terminal context (June 2026)

  • Root cause of dial-out directory not syncing: _load_directory() in
    features/dialout.py queried the DB without a Flask app_context. Terminal
    processes have no ambient Flask context so the query raised silently
    (except pass), always falling back to the hardcoded DEFAULT_DIRECTORY.
    Added with _app().app_context(): — same pattern as bbs_ui.py / menu_engine.py.
    Both PeerBbs (web directory) and DialoutDestination entries now appear on
    SSH/telnet.

v1.0a2.145 — BBS Directory: edit local entries + remove Who's Online (June 2026)

  • Local BBS entries in the web admin (/bbses/admin) can now be edited via a
    modal form — all fields: name, hostname, port, web URL, location, software,
    FTN address, description, active toggle. Previously delete/disable only.
  • Removed finger/who's-online polling entirely: _do_finger, _refresh_peer,
    background refresh, online count badges, "Who's on" button, admin columns.
    view.html is now a clean BBS detail/connect page.

v1.0a2.144 — Dial-out dir sync + User access flags (June 2026)

  • Terminal dial-out menu now reads from PeerBbs (web BBS Directory Local tab)
    as primary source, with DialoutDestination as secondary. Entries added via
    the web BBS Directory are now visible on SSH/telnet.
  • New user_access_flags table; sysop can suspend echomail, MRC, IRC, games,
    QWK downloads, or file downloads per user from /admin/users/<id>/manage.
    New table — upgrade-safe, no migration needed. Suspended users see a clear
    message and are returned to the menu.

v1.0a2.143 — Fix ANSI menu action: @PAUSE@ pagination + end-of-screen hold (June 2026)

  • @PAUSE@ in ANSI body now splits into pages; user presses any key to advance.
    Previously the code passed through as literal text on screen.
  • ansi menu action now forces a final key-wait after the screen displays so
    the menu doesn't immediately redraw and erase the content.
  • _show_ansi_screen gains force_pause=False param; _act_ansi passes True.

v1.0a2.142 — BBS Directory: correct EtherTerm XML parser (June 2026)

  • Both TelnetBBSGuide and IPTIA use the same EtherTerm dialdirectory.xml
    format: <BBS name="..." ip="..." port="..." protocol="TELNET" />.
    Switched from ElementTree (fails on unescaped & in BBS names) and
    failed CSV/pipe parsers to a single regex-based _parse_etherterm_xml()
    shared by both sources. Parses 1,075 TelnetBBSGuide entries and 1,810
    IPTIA entries correctly. TelnetBBSGuide is fetched as a monthly ZIP;
    dialdirectory.xml is extracted from inside it.

v1.0a2.141 — BBS Directory: correct TelnetBBSGuide + IPTIA formats (June 2026)

  • TelnetBBSGuide: rewritten from CSV to ZIP parser. Monthly file URL
    constructed dynamically (ibbs{MM}{YYYY}.zip); falls back to previous
    month if current month returns 404. Extracts the text file from inside
    the ZIP and parses pipe-delimited rows with flexible column detection.
  • IPTIA: rewritten from CSV to XML parser. Fetches
    dialdirectory.xml and parses with xml.etree.ElementTree, trying
    common tag names for name/address/port/sysop/location/software/web/description.
  • Both parsers handle unknown field names gracefully; exceptions are caught
    and return 0 instead of crashing the background thread.

v1.0a2.140 — BBS Directory CSV parser fixes (June 2026)

  • IPTIA parser crashcsv.DictReader produces a None key when a CSV
    header row has a trailing comma. k.strip() on None raised AttributeError.
    Both parsers now filter if k is not None. Fixed in web/peers.py.
  • Parser exceptions no longer crash background threadparser(text) and
    the DB insert block are now each wrapped in try/except so a malformed CSV
    silently returns 0 instead of killing the refresh thread.
  • TelnetBBSGuide URL updated to /bbs/list/full/csv/; both external URLs
    are now overridable via TELNETBBSGUIDE_CSV_URL and IPTIA_CSV_URL in .env.

v1.0a2.139 — Fix 500 on BBS Directory (missing PeerBbs columns) (June 2026)

  • Hotfix: PeerBbs model was missing the new columns (is_approved,
    telnet_port, web_url, location, software, submitted_by_user_id)
    added in v1.0a2.138. SQLAlchemy raised InvalidRequestError on
    filter_by(is_approved=True) because the attribute wasn't defined in
    the model class. _ensure_column adds them to the DB but the ORM also
    needs them in the model. Fixed in models.py.

v1.0a2.138 — Enhanced BBS Directory: TelnetBBSGuide + IPTIA tabs (June 2026)

  • Three-tab BBS Directory — Local (existing peers + user self-submission
    with sysop approval), TelnetBBSGuide (CSV fetched + cached every 6h),
    IPTIA (same pattern). Each entry shows name, sysop, location, software,
    description, and Telnet/Web connect buttons. Admin panel handles pending
    approvals and manual external refresh. New external_bbs_cache table;
    new columns on peer_bbses via _ensure_column.

v1.0a2.137 — Session crash fix; nginx MRC auto-repair (June 2026)

  • Session crash on early disconnectUnboundLocalError in session.start()
    finally block when a connection exited before completing login (bot gate, failed
    login, all nodes full). _hb_task = None was inside the outer try; moved it
    before the try alongside presence = None. core/session.py.

  • nginx MRC auto-repairupdate.sh now detects and inserts the missing
    location /mrcws + location = /mrc-auth-check nginx blocks if absent.
    Without them web MRC WebSocket connections returned 404 from gunicorn.

v1.0a2.136 — Security questions at terminal registration; sysop profile field control (June 2026)

  • Sysop profile field control — new BuiltinFieldConfig model lets sysops
    enable/disable each built-in profile field (Display Name, Bio, Location, Website,
    Forum Signature, FTN Tagline, Date of Birth, Show Email Option) from
    /admin/custom-fields. Disabled fields are hidden from both the profile edit
    form and the public profile view. Defaults to all enabled. Custom fields
    (add/edit/delete) remain on the same page.

  • Security questions on terminal registration — new users registering via
    telnet/SSH now complete the same 3 password-recovery security Q&A as web
    registration. Questions are presented numbered; user picks 3 different ones
    and enters answers. Answers saved via UserSecurityAnswer (same table/hashing
    as web). Implemented in _collect_security_questions() in core/session.py;
    save_security_answers() added to UserManager.

v1.0a2.135 — Duplicate email DoS fix; case-insensitive usernames (June 2026)

  • DoS crash fix — registering with a duplicate email caused an unhandled
    IntegrityError that terminated the BBS session. Added email_exists() and
    username_exists() pre-checks in the registration loop; user is re-prompted
    with a clear message instead of crashing. create_user() now returns a result
    code string and catches IntegrityError as defense-in-depth.
  • Case-insensitive usernames — all username lookups (registration, login,
    get_user, web form validation) now use func.lower() so StingRay and
    stingray are treated as the same account. Existing usernames unchanged.

v1.0a2.134 — MRC terminal fixes; Custom User Fields; User ID # (June 2026)

  • MRC PAUSED indicator\x1b[1;37;43m bold white on orange (was invisible reverse-video in SyncTERM)
  • MRC char count — top-right of status bar shows remaining chars (140 - typed); cyan > 15, yellow ≤ 15, red if over
  • MRC /t DM local echo — after sending DM, client echoes [DM -> target] message in pink
  • MRC tab autocomplete case fix — old code doubled wrong-case prefix; now deletes from start and re-inserts full canonical-case name
  • MRC status bar live update_draw_status_line() called after every keypress, backspace, and Ctrl+U
  • Custom User Fields — new UserField + UserFieldValue models; admin CRUD at /admin/custom-fields; field types: text/textarea/url/number/select; shown on public profile and admin user-manage page
  • User ID #user.id (auto-increment PK) displayed in admin users list and public profile page

v1.0a2.132 — ANEdit: CP437 char fix (■/·); slash /commands replace F-keys (June 2026)

  • CP437 fix — title bar used / (not in CP437), displayed as ? on
    SSH/telnet terminals. Replaced with (CP437 0xFE, modified) and · (clean).
    Also fixed em-dash in flash messages and box titles.
  • Slash /commands — F-keys are unreliable over SSH. ANEdit now intercepts /
    typed at column 0 and reads a short command: /? or /help = help, /t = theme,
    /m = mark, /cc = color picker, /find, /replace, /undo, /redo, /save,
    /send, /q = abort. Unknown commands fall back to inserting the text. F-keys
    retained as secondary bindings. Hint bar and help overlay updated.