PHP Classes

File: docs/proposals/AdvertisingLibrary_Proposal.md

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   docs/proposals/AdvertisingLibrary_Proposal.md   Download  
File: docs/proposals/AdvertisingLibrary_Proposal.md
Role: Auxiliary data
Content type: text/markdown
Description: Auxiliary data
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 23,042 bytes
 

Contents

Class file image Download

Advertising Library and Campaigns Proposal

> Draft Notice: This proposal is a draft generated by AI and may not have been reviewed for accuracy. It is intended as a starting point for discussion and implementation planning.

Table of Contents

  1. Overview
  2. Current State
  3. Implementation Progress
  4. Goals
  5. Non-Goals
  6. Proposed Model - Ad Library - Dashboard Selection - Auto-Posting Tie-In - Future Sources
  7. Data Model
  8. Admin Experience
  9. Dashboard Experience
  10. Community Positioning
  11. Terminal Server Advertising
  12. Auto-Posting Workflow
  13. Migration Plan
  14. Next Steps
  15. Open Questions

Overview

BinktermPHP now includes a structured ANSI advertising system. The legacy flat-file bbs_ads/ workflow has been replaced in practice by a database-backed ad library, dashboard rotation, and schedule-based echomail campaigns.

This document now serves two purposes:

  • describe the implemented architecture
  • record the remaining gaps and future phases

The implemented system provides:

  • a sysop-managed ANSI ad library
  • a curated dashboard advertising pool
  • future expansion room for other sources such as echomail
  • campaign-oriented auto-posting controls

Current State

Current implementation:

  • ads now live in the database rather than the legacy flat `bbs_ads/` store
  • migration `v1.11.0.38_advertising_library.php` imports existing `bbs_ads/*.ans` as enabled ads
  • `src/Advertising.php` manages the ad library, dashboard selection, campaign execution, post history, duplicate hash warnings, and SAUCE stripping for outbound posts
  • `templates/admin/ads.twig` provides library upload, edit, delete, ANSI preview, and browser-based ANSI editing helpers
  • `templates/dashboard.twig` shows a multi-ad dashboard viewer with per-session anti-repeat selection, manual previous/next controls, and keyboard navigation
  • `scripts/post_ad.php` posts from the database-backed library
  • `templates/admin/ad_campaigns.twig` provides campaign CRUD, schedule editing, target management, assigned-ad management, and post history
  • `scripts/run_ad_campaigns.php` supports manual campaign execution
  • `scripts/binkp_scheduler.php` and `src/Binkp/Connection/Scheduler.php` process due ad campaigns automatically

Remaining limitations:

  • no terminal server advertising yet
  • no tag-based campaign filtering in the campaign UI
  • no saved-from-echomail import flow yet
  • no dedicated eligibility preview explaining why a given ad will or will not be selected by a campaign
  • no richer history detail for matched schedule slot or posted body snapshot

Implementation Progress

Completed work:

  • Database-backed ad library implemented
  • Legacy `bbs_ads/` import migration implemented and imported ads enabled by default
  • Admin ad library page implemented
  • ANSI preview modal and editor preview implemented
  • Browser-side ANSI editing helpers implemented
  • Duplicate-hash warning support implemented
  • Dashboard carousel implemented with per-session rotation
  • Dashboard arrow-button and keyboard navigation implemented
  • Duplicate ANSI payload de-duplication in dashboard selection implemented
  • Manual posting via `scripts/post_ad.php` updated to use the library
  • SAUCE stripping for preview and posting implemented
  • Campaign tables and campaign admin UI implemented
  • Multiple targets with per-target subject templates implemented
  • Day/time/timezone schedule rows implemented
  • Timezone selector implemented
  • Scheduler integration implemented through `binkp_scheduler`
  • Campaign post history table implemented
  • Admin navigation updated with a top-level `Ads` menu
  • Documentation added in `docs/Advertising.md`

Goals

  • Give the sysop a real library of ANSI ads they can upload and manage.
  • Allow the sysop to choose exactly which ads are eligible for the dashboard ad window.
  • Support multiple dashboard rotation strategies later without redesigning the storage model.
  • Add a clean tie-in for auto-posting ads to echomail areas.
  • Preserve compatibility with the existing `bbs_ads/` workflow during transition.
  • Leave room for future sources such as saved ANSI art from echomail messages.

Non-Goals

  • Full ad sales, billing, or customer management
  • Analytics-heavy impression tracking in the first version
  • Rich WYSIWYG ANSI editing in-browser
  • Multi-format ad handling beyond ANSI in the first version

Proposed Model

Ad Library

Introduce a first-class ad library where each ad has:

  • stored ANSI content
  • a canonical title / slug
  • optional description / notes
  • source metadata
  • activation flags
  • posting metadata

The library should support at least these sources:

  • `upload`
  • `generator`
  • `echoarea_saved` for future use

Each ad should exist as a record in the database, with the ANSI payload stored either in the database or in a managed storage path referenced by the record.

Recommended approach:

  • store metadata in the database
  • store ANSI payload directly in the database as text
  • keep a content hash in the database for deduplication warnings and provenance

This makes the ad library easier to back up, preview, import from echomail, and manage without a second storage layer.

Dashboard Selection

The dashboard no longer pulls from ?all ads in storage.? Instead, it pulls from a sysop-managed dashboard pool.

Each ad in the library can independently be:

  • stored but hidden
  • eligible for dashboard rotation
  • eligible for echomail auto-posting
  • both dashboard-eligible and auto-post-eligible

Implemented dashboard selection controls per ad:

  • `enabled`
  • `show_on_dashboard`
  • `dashboard_weight`
  • `dashboard_priority`
  • `start_at`
  • `end_at`
  • tags

These date-window fields are shared eligibility controls for presentation surfaces such as the dashboard and terminal slots, not terminal-only metadata.

Future rotation modes the data model could support:

  • random
  • weighted random
  • round-robin
  • pinned-first then rotate remainder

The current implementation ships weighted random selection and does not expose a configurable dashboard rotation mode.

Dashboard rotation should be user-session-aware rather than purely global. That means the system should remember which dashboard ads have been shown during the current user session and avoid immediately repeating the same ad for that same user.

Current implementation:

  • weighted random selection from eligible dashboard ads
  • per-session memory to avoid showing the same ad twice in a row to the same user
  • session-backed memory so the same ad is not immediately repeated when multiple eligible ads exist

This gives a better user experience for active users refreshing or revisiting the dashboard without requiring a globally synchronized rotation state.

Auto-Posting Tie-In

Auto-posting should be modeled as campaigns or schedules, not a single global boolean.

Recommended concept:

  • an Ad Campaign targets one or more area+domain destinations
  • a campaign chooses ads from an eligible pool
  • a campaign defines posting schedule and subject policy

Implemented campaign fields:

  • campaign name
  • enabled flag
  • from-user
  • to-name
  • ad selection mode
  • eligible ads
  • one or more area+domain targets, each with its own subject template
  • one or more schedule entries, each with days of week, post time, and timezone

Notes on current behavior:

  • campaign selection currently operates as weighted random
  • the campaign UI exposes `weighted_random` only
  • optional tag filtering is not implemented yet

Recommended schedule model:

  • campaigns should use explicit schedule rows rather than a raw minute interval
  • a schedule row defines: - enabled flag - one or more days of week - time of day - timezone
  • a campaign may have multiple schedule rows

Example:

  • Monday, Wednesday, Friday at `10:00`
  • Saturday at `20:00`

This lets the same library feed both the local dashboard and outbound ad posts without coupling those two concerns too tightly.

Future Sources

The library should anticipate future import flows:

  • save ANSI art from echomail into the ad library
  • import generated ads directly from the ad generator UI
  • clone an existing ad into a new edited version

For future provenance, the ad record may later include optional source fields such as:

  • source type
  • source message id
  • source echoarea
  • source filename
  • imported by user id

This avoids redesign when ?save from echomail? is added later.

Data Model

Suggested new tables:

advertisements

Core ad records.

Implemented fields:

  • `id`
  • `slug`
  • `title`
  • `description`
  • `content`
  • `content_hash`
  • `source_type`
  • `legacy_filename` nullable
  • `created_by_user_id` nullable
  • `updated_by_user_id` nullable
  • `is_active`
  • `show_on_dashboard`
  • `allow_auto_post`
  • `dashboard_weight`
  • `dashboard_priority`
  • `start_at` nullable
  • `end_at` nullable
  • `created_at`
  • `updated_at`

Deferred provenance fields for future echomail import work:

  • `source_message_id` nullable
  • `source_echoarea` nullable

advertisement_campaigns

Auto-post definitions.

Implemented fields:

  • `id`
  • `name`
  • `description`
  • `is_active`
  • `from_user_id`
  • `to_name`
  • `selection_mode`
  • `last_posted_at` nullable
  • `last_posted_ad_id` nullable
  • `created_at`
  • `updated_at`

Legacy compatibility fields still present in the current schema/service layer but no longer used by the scheduling model:

  • `post_interval_minutes`
  • `min_repeat_gap_minutes`

advertisement_campaign_schedules

Per-campaign schedule rows.

Implemented fields:

  • `id`
  • `campaign_id`
  • `days_mask`
  • `time_of_day`
  • `timezone`
  • `is_active`

days_mask can be a simple bitmask for Sunday through Saturday. A normalized one-row-per-day model would also work, but a bitmask keeps the admin UI simpler when a sysop wants one time to apply to multiple days.

advertisement_campaign_targets

Per-campaign posting targets.

Implemented fields:

  • `id`
  • `campaign_id`
  • `echoarea_tag`
  • `domain`
  • `subject_template`
  • `is_active`

advertisement_campaign_ads

Join table between campaigns and eligible ads.

Implemented fields:

  • `campaign_id`
  • `advertisement_id`
  • `weight`

advertisement_tags

Implemented fields:

  • `id`
  • `name`
  • `slug`

Examples:

  • `general`
  • `door`
  • `network`
  • `event`

Tags should be free-form rather than pre-seeded or hardcoded. The example tags above are suggestions, not required built-ins.

advertisement_tag_map

Implemented fields:

  • `advertisement_id`
  • `tag_id`

advertisement_post_log

Audit and dedupe history for manual and automatic posting.

This is the shared post history table for:

  • manual "post now" actions from the admin UI
  • legacy CLI-triggered posts
  • scheduled campaign posts

Implemented fields:

  • `id`
  • `advertisement_id`
  • `campaign_id` nullable
  • `message_id` nullable
  • `echoarea_tag`
  • `domain`
  • `subject`
  • `posted_by_user_id` nullable
  • `post_mode`
  • `posted_at`
  • `status`
  • `error_text` nullable

Optional later table:

advertisement_impressions

Only if dashboard analytics become important later. This should not be required for the first implementation.

Admin Experience

The current Admin -> Advertisements page is now the ad library manager.

Current sections and capabilities:

Library List

Columns:

  • title
  • source
  • active/inactive
  • dashboard enabled
  • auto-post enabled
  • updated at
  • actions

Actions:

  • preview
  • edit metadata
  • enable/disable
  • include/exclude from dashboard
  • include/exclude from auto-posting
  • delete
  • duplicate
  • post now

Upload

Upload ANSI files into the library rather than directly into bbs_ads/.

Metadata on upload:

  • title
  • optional slug override
  • description
  • tags
  • mark for dashboard
  • mark for auto-posting

If the uploaded ANSI hash already matches an existing ad, the UI should warn but still allow the upload.

Dashboard Pool Management

This can either live inside the ad library or on a dedicated subpage.

Implemented controls:

  • which ads are shown on the dashboard
  • ad weight / priority
  • upload/edit preview from the ad library UI

Not yet implemented:

  • preview of the current eligible dashboard pool as a dedicated management view
  • rotation mode setting
  • automatic carousel timing or display mode controls

Campaigns

Implemented campaign page capabilities:

  • list campaigns
  • create/edit campaign
  • assign ads to campaign
  • manage multiple area+domain targets with separate subject templates
  • manage one or more schedule rows with day-of-week and time selection
  • test post / post now
  • enable/disable schedule
  • inspect post history

Not yet implemented:

  • tag-based campaign filtering
  • campaign eligibility preview / explanation

Dashboard Experience

The dashboard ad window currently keeps the basic card presentation:

  • ANSI preview rendered in the existing dashboard ad card

Its selection source now comes from eligible dashboard ads in the library.

Current selection order is effectively:

  1. active ads only
  2. `show_on_dashboard = true`
  3. within active time window
  4. de-duplicate duplicate ANSI payloads by content hash
  5. choose using weighted random with session-aware anti-repeat behavior

Implemented presentation:

  • single card
  • manual carousel

The carousel should be treated as a dashboard presentation mode over the same eligible ad pool, not as a separate ad type.

Current controls:

  • manual next/previous controls
  • keyboard advancement for accessibility and keyboard-centric use

Automatic advance is not implemented today.

Optional later improvements: - rotate after page refresh threshold - support dashboard categories or themed sets

Community Positioning

This proposal should be treated as a community-edition feature.

That means the full advertising workflow described here should be available to all installations, including:

  • the ANSI ad library
  • dashboard pool management
  • carousel display modes
  • schedule-based auto-posting
  • multi-target campaigns
  • posting history
  • future echomail-based ad import

This keeps the advertising system aligned with the project principle that core BBS/community-building features should remain broadly available.

Terminal Server Advertising

Terminal server advertising should reuse the same ad library as the web dashboard and campaigns, but it should have its own presentation and eligibility controls. Terminal display is more constrained than the web UI, so this should be modeled as a separate delivery surface rather than reusing show_on_dashboard.

Recommended Placement

First implementation should support two explicit terminal slots:

  • `login` - show once after successful login and before the main menu
  • `main_menu` - show in a dedicated framed area or panel on the main menu

These are the highest-value placements because they are visible, predictable, and do not interrupt message reading, file browsing, or command entry.

Not recommended for the first version:

  • inline ads inside message readers
  • inline ads inside file listings
  • prompt-time or command-entry ads
  • random interruption during navigation

Possible later slot:

  • `transition` - show when returning to the main menu from major sections

Selection Model

Terminal ads should be selected from a terminal-specific eligible pool.

Recommended selection rules for a terminal slot:

  1. ad is active
  2. ad is enabled for terminal display in the requested slot
  3. ad is within its shared active date window (`start_at` / `end_at`)
  4. duplicate ANSI payloads are de-duplicated by content hash
  5. choose by weighted random
  6. avoid immediate repeat within the same terminal session when multiple ads are eligible

Recommended session behavior:

  • `login` slot chooses one ad once per login session
  • `main_menu` slot keeps a stable ad for the session unless the user explicitly cycles
  • later enhancement: rotate after N returns to the main menu rather than every refresh

Selection state should be per terminal session, not global.

Recommended Data Model

Do not overload show_on_dashboard for terminal use. The cleanest model is a slot mapping table so terminal placements can grow without repeatedly changing the advertisements table.

Recommended table:

advertisement_terminal_slots

Suggested fields:

  • `advertisement_id`
  • `slot` - e.g. `login`, `main_menu`
  • `weight`
  • `priority`
  • `is_active`

This is preferred over a simple show_on_terminal boolean because it supports:

  • per-slot eligibility
  • per-slot weight/priority
  • future slots without schema churn
  • cleaner divergence between web and terminal presentation

Admin Experience

The ad editor should gain terminal-specific controls.

Recommended controls:

  • enable ad for terminal display
  • assign one or more terminal slots
  • set per-slot weight
  • set per-slot priority
  • preview ad in terminal mode

If the main menu slot has stricter size limits than the login slot, the admin UI should validate those constraints before save or clearly warn when an ad is too large for a slot.

Rendering Constraints

Terminal ads should follow stricter rules than dashboard ads:

  • ANSI-only rendering
  • SAUCE stripped before display
  • fixed-width terminal-safe layout
  • slot-specific height limits
  • no browser-only presentation assumptions

Recommended slot constraints:

  • `login` may allow a larger full-screen or near-full-screen ad
  • `main_menu` should use a smaller framed region with a strict height cap

If an ad is too tall for a slot, prefer validation or exclusion over runtime cropping.

Shell Integration

The terminal shell should ask the advertising service for a terminal ad by slot. The advertising system should decide eligibility and selection; the shell should own placement and framing.

This keeps responsibilities clear:

  • `Advertising` owns content selection
  • terminal shell code owns layout and screen composition

Recommended First Implementation

Phase 1 terminal support should include:

  • `login` and `main_menu` slots only
  • slot-based eligibility using a mapping table
  • weighted random selection with session anti-repeat
  • terminal preview in the admin UI
  • no inline ads inside readers, prompts, or file listings

This gives terminal advertising real visibility without making the terminal UX feel noisy or intrusive.

Auto-Posting Workflow

Auto-posting should reuse the library rather than reading arbitrary filenames from disk.

Proposed flow:

  1. Scheduler daemon or cron job asks for active ad campaigns.
  2. For each campaign, determine whether one or more schedule rows are due.
  3. Choose an eligible ad using the campaign selection rules.
  4. Post it via the existing echomail posting pipeline.
  5. Log the result in `advertisement_post_log`.
  6. Update campaign `last_posted_at` and last-posted ad metadata.

This now complements scripts/post_ad.php rather than replacing it.

Recommended CLI evolution:

  • keep `post_ad.php` for compatibility
  • teach it to accept either `--ad=slug-or-id` or legacy filename
  • add a new scheduler-oriented script such as `scripts/run_ad_campaigns.php`
  • ensure manual CLI posts also write to `advertisement_post_log`

Schedule runner behavior:

  • the runner should evaluate each campaign schedule row in its configured timezone
  • the runner should allow a small grace window so a periodic cron job does not need to fire at the exact minute
  • the runner should record enough information to avoid duplicate posting for the same campaign schedule slot if it runs multiple times in the same window

Campaign selection modes worth supporting later:

  • random
  • weighted random
  • least recently posted
  • strict round-robin

The current implementation uses weighted random only.

Migration Plan

Phase 1: Library Foundation

Status: completed

  • Advertisement library tables created.
  • Migration/import script added for `bbs_ads/*.ans`.
  • One ad record is created per existing legacy file.
  • Imported ads from `bbs_ads/` are marked active/enabled by default.

Phase 2: Admin Library UI

Status: completed

  • Upload/list/delete page replaced with a library manager.
  • Preview and metadata editing added.
  • `show_on_dashboard` and `allow_auto_post` toggles added.
  • Tags and duplicate-hash warning UI added.

Phase 3: Dashboard Pool

Status: completed

  • Dashboard ad selection now uses the library.
  • Per-session anti-repeat behavior added.
  • Single-card display plus manual next/previous browsing implemented.
  • Existing dashboard rendering preserved and enhanced.

Phase 4: Post-Now and Auto-Post Integration

Status: implemented with follow-up items remaining

  • `post_ad.php` updated to read from the library.
  • Campaign tables and admin pages added.
  • Multi-target campaigns with per-area subject templates added.
  • Explicit day/time/timezone schedule rows added and used by the scheduler.
  • Scheduler script for campaign posting added.
  • Day-of-week and time editing in the campaign UI added.
  • Post history and error tracking added.
  • Due campaign targets are processed in a deterministic order.
  • Legacy interval fields remain in parts of the schema/service layer but are no longer used by scheduling behavior.
  • Remaining items: tag-based ad selection/filtering, richer history detail, and a campaign eligibility preview are not yet implemented.

Phase 5: Future Content Sources

Status: not started

  • Add "save ANSI from echomail to ad library"
  • Add provenance UI and duplicate detection workflow

Next Steps

Recommended next work:

  • Add advertising support to the Terminal Server
  • Add tag-based campaign filtering / eligibility controls
  • Improve campaign history with matched schedule-slot detail
  • Add clearer campaign validation warnings for no active schedules, targets, or ads
  • Add "save ANSI from echomail" import into the ad library
  • Add a campaign eligibility preview to explain which ads can currently be selected

Future Phase: Terminal Server Advertising

  • moved to the dedicated Terminal Server Advertising section above

Open Questions

  • Should manual "post now" support posting to a whole multi-target campaign or also allow targeting just one destination from that campaign?
  • Should dashboard carousel mode remember per-user position between refreshes, or restart from the first selected ad on each dashboard load?
  • Should campaign logs keep only success/failure metadata, or also store a copy of the rendered subject/body that was posted?
  • Should schedule rows use the board timezone by default, or always require an explicit timezone on each row?