NEWS WIRE 1.0.7
ATDT BBS Native PHP Door
========================

News Wire is a self-contained U.S. and World news reader for ATDT BBS. It
merges headlines from several RSS/Atom publishers into an 80-column ANSI
interface. Feed summaries are used first; for sources whose SysOp enables
Article fetching, News Wire can request the linked publisher page and cache a
bounded text excerpt when the feed summary is short or empty.

FEATURES
--------
- Separate UNITED STATES NEWS and WORLD NEWS desks
- Multiple independent publishers per desk for redundancy
- Admin-editable feed list with enable/disable controls
- Per-feed Article switch for linked publisher-page excerpts
- Headlines merged newest-first and de-duplicated by normalized title
- Five one-line headlines per BBS page with numbered story selection
- Color-coded publisher labels
- Paged story screens using feed summaries or cached article text excerpts
- Publisher article URL screen
- Manual refresh command
- Eight-second caller-screen network budget
- Five-minute feed cache with six-hour last-good fallback
- Short failure cooldowns so a dead source is not retried on every keypress
- Non-blocking per-URL refresh locks
- Separate feed and article-text cache pools
- 1 MiB in-flight response cap
- HTTPS-only redirects with localhost/private/reserved destination blocking
- ATDT SysOp admin page with bounded live feed diagnostics
- No API keys
- No Composer packages
- No cron job or daemon
- No News Wire ATDT database tables
- cursor:false in door.json, supported by ATDT 1.7.7, keeps the caller cursor hidden

DEFAULT SOURCES
---------------
UNITED STATES NEWS
  NPR National
    https://feeds.npr.org/1003/rss.xml

  The New York Times - U.S.
    https://rss.nytimes.com/services/xml/rss/nyt/US.xml

  The Guardian - U.S.
    https://www.theguardian.com/us-news/rss

WORLD NEWS
  NPR World
    https://feeds.npr.org/1004/rss.xml

  The New York Times - World
    https://rss.nytimes.com/services/xml/rss/nyt/World.xml

  The Guardian - World
    https://www.theguardian.com/world/rss

The built-in defaults remain in config.php. The active source list is normally
managed from the News Wire admin panel and stored in:

  newswire/config/feeds.json

REUTERS NOTE
------------
News Wire 1.0.7 does not use the old feeds.reuters.com URLs. Those legacy
public Reuters RSS endpoints are not treated as dependable current feeds.
A suitable HTTPS Reuters feed can be added later from the News Wire admin
panel if one is available to your host.

INSTALLATION - ATDT 1.7.7
-------------------------
1. Copy the complete "newswire" folder into ATDT's native door folder, for
   example:

     doors/
       newswire/
         admin.php
         config.php
         door.json
         door.php
         README.txt
         CHANGELOG.txt
         lib/
           FeedConfig.php
           HttpCache.php
           NewsService.php
           Version.php
         config/
           .htaccess
           index.html
         cache/
           .htaccess
           index.html

2. In the ATDT SysOp console open Native Doors and click:

     Scan doors/ folder

3. Open the News Wire admin panel, review the active feed list, and click:

     Test all active feeds

4. In Menus, add a menu item using the DOOR action and the newswire slug.

ATDT also supports data/doors/, but News Wire 1.0.7 intentionally keeps its
runtime cache and feed configuration inside the self-contained newswire folder.

HOSTING REQUIREMENTS
--------------------
- ATDT 1.7.7
- PHP 8.1 or newer
- mbstring, JSON, OpenSSL, and session support as required by ATDT
- SimpleXML preferred; News Wire includes an internal RSS/Atom fallback parser
- cURL preferred
- If cURL is unavailable, allow_url_fopen must be enabled; the fallback reader
  still enforces the News Wire wall-clock and response-size limits
- Outbound HTTPS/TCP 443 access to configured publisher hosts
- Write access to newswire/cache/
- Write access to newswire/config/ to save custom feeds from admin.php

ADMIN FEED MANAGEMENT
---------------------
Each U.S. and World desk supports up to eight feed rows:

  On        Include or temporarily disable the source
  Article   Permit linked publisher-page excerpt fetching for that source
  Name      Publisher/feed display name
  Label     Up to 8 characters used in the ANSI headline list
  URL       HTTPS RSS/Atom URL

Labels are uppercased and reduced to A-Z, 0-9, underscore and hyphen. Blank
URL rows are ignored. Clear a URL and save to remove that row. At least one
feed per desk must remain enabled.

Feed URLs must use HTTPS. Literal localhost/private/reserved addresses are
rejected when saving, and every live request/redirect is checked again before
connection. News Wire does not follow a publisher redirect to plain HTTP or a
local/private network destination.

Use Restore default feeds to return to the built-in NPR, New York Times and
Guardian source lists.

ARTICLE EXCERPTS
----------------
The Article checkbox is intentionally a SysOp decision. When enabled and a
feed summary is short or empty, News Wire may request the linked publisher
page. It examines at most 256 KiB of the downloaded HTML and stores only the
extracted text excerpt in the article cache; full article HTML is not retained.
The display is an excerpt reader, not an article archive. Script, style, analytics,
template and other non-article blocks are discarded before publisher-page text is shown.

Publishers may impose their own feed, automated-access, copyright or usage
terms. Leave Article disabled for a source if its terms or access controls do
not permit linked-page fetching. The original publisher URL remains available
on the story Link screen.

CALLER COMMANDS
---------------
MAIN MENU
  U       United States news
  W       World news
  R       Refresh both desks within the screen network budget
  ?       Help
  Q       Quit
  ENTER   Open United States news

HEADLINE DESK
  1-5     Read a headline on the current page
  N       Next headline page
  P       Previous headline page
  R       Refresh this desk
  U       United States desk
  W       World desk
  M       Main Menu
  ENTER   U.S. desk -> World desk; World desk -> Main Menu

STORY VIEW
  N       Next story page
  P       Previous story page
  L       Show the publisher article URL
  R       Refresh the desk/story within the network budget
  B       Back to the headline page
  M       Main Menu
  ENTER   Advance story page; after the last page, return to headlines

ARTICLE LINK
  B       Back to the story view
  M       Main Menu
  ENTER   Back to the story view

CACHE / STORAGE
---------------
News Wire runtime cache files are stored under:

  newswire/cache/

The cache is split logically by filename prefix:

  feed-*.json       RSS/Atom response cache
  article-*.json    extracted article text only
  *.fail            short negative-cache markers after failed requests
  *.lock            persistent per-URL lock files
  .gc               runtime cleanup timestamp, created automatically

Feed cache policy:
- normal TTL: 5 minutes
- last-good fallback: up to 6 hours
- cap: approximately 3 MiB / 72 response files

Article-text cache policy:
- normal TTL: 30 minutes
- stale fallback: up to 12 hours
- stores extracted text only, never the complete HTML page
- cap: approximately 1 MiB / 96 response files

A failed source receives a short retry cooldown so callers paging through a
desk or story do not repeatedly hit the same failed publisher. Old temporary,
failure and orphaned lock files are cleaned during normal operation.

NETWORK BEHAVIOR
----------------
Caller screens share an approximately 8-second total network budget. Individual
requests are also bounded, so enabling many custom feeds cannot make a single
screen wait for every source's full timeout in sequence. The SysOp feed test
uses a separate bounded admin budget.

Responses are capped at 1 MiB while they are being received. The cURL path
checks decoded response bytes in its write callback; the PHP stream fallback
uses a bounded read loop with a wall-clock deadline.

All redirects are handled explicitly. Every destination must remain HTTPS and
must resolve only to public network addresses. Loopback, private, link-local
and reserved targets are refused.

TEXT / TERMINAL SAFETY
----------------------
Publisher-controlled headlines, summaries, article excerpts and URLs are
painted as literal ANSI text instead of being passed through ATDT's MCI pipe
renderer. A publisher string such as "%BN" or "%HN" therefore remains literal
and cannot expand to board/caller information.

Story wrapping uses ATDT's multibyte-aware visible-width wrapper. Legacy feed
text handled by the fallback parser is converted to UTF-8 when possible.

Detailed DNS/TLS/cURL errors are not shown to callers. The caller receives a
generic feed-error screen; detailed diagnostics remain available to the SysOp
through the admin feed test and News Wire event logging.

TIMESTAMPS
----------
News Wire 1.0.7 continues to display publisher timestamps in UTC (Z) by design.

TROUBLESHOOTING
---------------
If a desk displays FEED ERROR or a DEGRADED notice:

1. Open Native Doors -> News Wire admin.
2. Click Test all active feeds.
3. Check the exact failing source and its network/parser message there.
4. Confirm outbound HTTPS/TCP 443 is allowed by the hosting provider.
5. If cURL is unavailable, confirm allow_url_fopen and OpenSSL are available.
6. Disable or replace a failed source from the News Wire admin feed table.
7. If Article fetching is blocked by a publisher but its RSS feed works,
   uncheck Article for that source and save.

A single failed source should not blank a desk when another configured source
has usable data.

News Wire is not affiliated with or endorsed by NPR, The New York Times, The
Guardian, Reuters, or ATDT BBS. Headlines, summaries, article excerpts and
linked articles remain the property of their respective publishers.
