Skip to content

Add WebSocket notification system for real-time BBS and mail mentions#905

Merged
boomzero merged 14 commits intodevfrom
boomzero/wss
Feb 11, 2026
Merged

Add WebSocket notification system for real-time BBS and mail mentions#905
boomzero merged 14 commits intodevfrom
boomzero/wss

Conversation

@boomzero
Copy link
Member

@boomzero boomzero commented Feb 11, 2026

Replaces focus-based polling with persistent WebSocket connection to the backend notification service. Notifications now arrive within 1-2 seconds with automatic reconnection and exponential backoff. Maintains polling as fallback for reliability when WebSocket is unavailable.

What does this PR aim to accomplish?:

This PR introduces a WebSocket-based real-time notification system to replace the existing focus-based polling mechanism for BBS mentions and mail notifications.
This closes #335.

Related PRs:

Key Goals:

  • Provide instant notifications (1-2 second latency) for BBS mentions and mail notifications
  • Reduce server load by replacing frequent polling with persistent WebSocket connections
  • Improve user experience by delivering notifications without requiring page focus
  • Maintain reliability with automatic reconnection and fallback mechanisms

Benefits:

  • Users receive notifications immediately when mentioned in BBS posts or receive mail
  • No need to manually refresh or switch tabs to check for new notifications
  • More efficient resource usage compared to polling
  • Better user experience with real-time updates

How does this PR accomplish the above?:

Implementation Details:

  1. WebSocket Client (ws://127.0.0.1:8787 for debug, wss://api.xmoj-bbs.me/ws/notifications for production)

    • Persistent connection with 30-second ping/keepalive to maintain connection
    • Exponential backoff for reconnection attempts (1s, 2s, 4s, 8s, up to 30s max)
    • Automatic reconnection on visibilitychange event when tab becomes visible
    • PHPSESSID cookie authentication
    • Only connects when user is logged in and has notifications enabled
  2. Real-time Notification Handling

    • Enriched bbs_mention and mail_mention payloads from backend
    • Instant toast notifications without additional API fetches
    • Sanitized PostTitle via escapeHTML() to prevent XSS attacks
    • Proper username handling using appendChild() in mail toasts
  3. Fallback Mechanisms

    • Maintains existing polling system as fallback when WebSocket is unavailable
    • Falls back to polling on focus events and every 60 seconds when socket is down
    • Graceful degradation ensures notifications continue working even if WebSocket fails
  4. Bug Fixes

    • Store reconnect timer reference to prevent duplicate connections
    • XSS protection for notification content
    • Improved DOM manipulation to preserve usernames

Changelog:

  • ✨ NEW: WebSocket client with persistent connection for real-time notifications
  • ✨ NEW: 30-second ping keepalive mechanism
  • ✨ NEW: Exponential backoff reconnection strategy
  • ✨ NEW: Automatic reconnection on tab visibility change
  • ✨ NEW: Enriched notification payloads (bbs_mention, mail_mention)
  • 🐛 FIX: Prevent duplicate WebSocket connections
  • 🐛 FIX: XSS vulnerability in PostTitle via escapeHTML sanitization
  • 🐛 FIX: Username preservation in mail toast notifications
  • 🔄 IMPROVE: Reduced notification latency from ~60s (polling) to 1-2s (WebSocket)
  • 🔄 IMPROVE: Reduced server load by replacing polling with push notifications

Files Modified:

  • XMOJ.user.js: +306/-128 lines (WebSocket implementation, notification handlers)
  • Update.json: Version bump and release notes
  • package.json: Version update to 2.7.3

By submitting this pull request, I confirm the following:

  1. ✅ I have read and understood the contributor's guide, as well as this entire template. I understand which branch to base my commits and Pull Requests against.
  2. ✅ I have commented on my proposed changes within the code and I have tested my changes.
  3. ✅ I am willing to help maintain this change if there are issues with it later.
  4. ✅ It is compatible with the GNU General Public License v3.0
  5. ✅ I have squashed any insignificant commits. (git rebase)
  6. ✅ I have checked that another pull request for this purpose does not exist.
  7. ✅ I have considered and confirmed that this submission will be valuable to others.
  8. ✅ I accept that this submission may not be used, and the pull request can be closed at the will of the maintainer.
  9. ✅ I give this submission freely and claim no ownership to its content.

  • I have read the above and my PR is ready for review. Check this box to confirm

Summary by cubic

Adds a persistent WebSocket for real-time BBS and mail mention notifications with enriched payloads for instant toasts. Ships in v2.7.3 (prerelease) with auto-reconnect, 30s keepalive pings, and guardrails to prevent XSS, duplicate connections, and duplicate toasts.

  • New Features

    • WebSocket client with 30s ping, exponential backoff, and reconnect on visibilitychange.
    • Enriched bbs_mention and mail_mention payloads for instant toasts; fallback polling on focus and every 60s when the socket is down.
    • PHPSESSID auth; connects only when logged in with popups enabled. ws://127.0.0.1:8787 (debug), wss://api.xmoj-bbs.me/ws/notifications (prod).
  • Bug Fixes

    • Prevent duplicates: store reconnect timer; clear toast container before polling.
    • Sanitize PostTitle with escapeHTML to block XSS.
    • Use appendChild in mail toasts to preserve usernames.
    • Skip UpdateVersion when the last commit is by github-actions[bot] to prevent infinite workflow loops.

Written for commit 25ab8d7. Summary will update on new commits.

Replaces focus-based polling with persistent WebSocket connection to the backend notification service. Notifications now arrive within 1-2 seconds with automatic reconnection and exponential backoff. Maintains polling as fallback for reliability when WebSocket is unavailable.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@boomzero boomzero marked this pull request as draft February 11, 2026 00:21
@hendragon-bot hendragon-bot bot added the user-script This issue or pull request is related to the main user script label Feb 11, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3fa3423595

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 1 file

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="XMOJ.user.js">

<violation number="1" location="XMOJ.user.js:628">
P1: Race condition: `ReconnectNotificationSocket` schedules a `setTimeout` but never stores the timer ID. When the `visibilitychange` handler also calls `ConnectNotificationSocket()`, both can fire, creating duplicate WebSocket connections. Store the timer ID in a module-level variable and clear it before creating a new connection.</violation>

<violation number="2" location="XMOJ.user.js:667">
P2: Matching mail mentions only by `FromUserID` is insufficient when the same sender has multiple unread mentions. This loop will match the first (potentially older) mention in the list rather than the newly pushed one, causing the wrong `MentionID` to be marked as read on click while the actual new mention remains unread. Consider matching by a unique identifier like `MentionID` from the notification payload instead.</violation>

<violation number="3" location="XMOJ.user.js:709">
P1: XSS vulnerability: `mention.PostTitle` is inserted directly via `innerHTML` without sanitization. If a malicious user crafts a post title containing `<script>` or event handler attributes, it will execute in the context of the victim's browser session. Use `escapeHTML()` or `textContent` to safely render user-supplied data.</violation>

<violation number="4" location="XMOJ.user.js:770">
P2: Bug: `innerHTML +=` after `appendChild(ToastUser)` destroys the DOM node created by the async `GetUsernameHTML()`. When you use `innerHTML +=`, the browser serializes existing children to HTML, appends the string, and re-parses everything—destroying event listeners and breaking async updates in progress. Use `appendChild(document.createTextNode(...))` instead.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

boomzero and others added 2 commits February 11, 2026 19:06
The backend now includes all required fields (PostTitle, PageNumber, MentionID) in WebSocket notifications, eliminating the need for additional API calls to fetch mention details. This reduces latency and server load for real-time notifications.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…issues

1. Race condition (P1): Store reconnect timer ID to prevent duplicate WebSocket connections when visibilitychange handler and delayed reconnect fire simultaneously

2. XSS vulnerability (P1): Sanitize user-supplied PostTitle with escapeHTML() before rendering to prevent script injection attacks

3. DOM destruction (P2): Replace innerHTML += with appendChild to preserve async GetUsernameHTML() results in mail mention toasts

Note: Mail mention matching issue (violation #2) was already resolved by previous commit that passes notification.data directly

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@boomzero boomzero marked this pull request as ready for review February 11, 2026 11:25
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc2099ddcc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 1 file

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="XMOJ.user.js">

<violation number="1" location="XMOJ.user.js:793">
P2: Race condition in `PollNotifications`: when both BBSPopup and MessagePopup are enabled, the BBS callback unconditionally clears `ToastContainer.innerHTML`, but if the mail response arrives first, its toasts will be destroyed when the BBS response arrives later. Consider clearing the container once before both requests, or chaining the requests sequentially.</violation>

<violation number="2" location="XMOJ.user.js:1540">
P2: The `visibilitychange` handler doesn't account for `WebSocket.CONNECTING` state. If the socket is already connecting, this will trigger another `ConnectNotificationSocket()` call, creating a duplicate connection. Add a check for `CONNECTING` state.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="Update.json">

<violation number="1" location="Update.json:3293">
P2: Avoid manually editing Update.json version metadata; the UpdateVersion workflow owns these timestamps and will overwrite or conflict with hand-edits. Revert this change and let the automation update the entry.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@XMOJ-Script-dev XMOJ-Script-dev deleted a comment from cubic-dev-ai bot Feb 11, 2026
github-actions bot and others added 2 commits February 11, 2026 20:53
…ns[bot]

This prevents infinite loops where the bot commits version updates,
which triggers the workflow again, causing another commit.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@hendragon-bot hendragon-bot bot added the update-script Related to our update infrastructure! label Feb 11, 2026
@boomzero boomzero enabled auto-merge February 11, 2026 14:10
@boomzero boomzero merged commit 8c30579 into dev Feb 11, 2026
5 checks passed
@boomzero boomzero deleted the boomzero/wss branch February 11, 2026 14:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L update-script Related to our update infrastructure! user-script This issue or pull request is related to the main user script

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants