Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions docs/automation-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Zoom Host Automation — Design Document

## Project Goal

This project provides a lightweight Tampermonkey userscript that runs inside a Zoom Web meeting under a **host or co-host** account. Its primary task is to automatically grant **Multi-Pin** permission to any participant who raises their hand, removing the need for the host to perform this action manually.

Secondary goals include a spam-detection scaffold for the chat panel and a camera-off reminder scaffold.

---

## Architecture

The entire solution is a single self-contained JavaScript file (`scripts/zoom-host-tools.user.js`) executed by the [Tampermonkey](https://www.tampermonkey.net/) browser extension. No backend services, build steps, or external dependencies are required.

```
browser/
├── scripts/
│ └── zoom-host-tools.user.js # Main TamperMonkey userscript
├── selectors/
│ └── zoom-dom-selectors.json # Configurable CSS selectors (source of truth)
└── docs/
├── automation-design.md # This file
└── testing-checklist.md # Manual QA checklist
```

### Key Design Decisions

| Decision | Rationale |
|---|---|
| Single-file userscript | No build pipeline; install and run directly in TamperMonkey |
| Selectors isolated in JSON | Easy to update when Zoom updates its DOM without touching logic |
| `setInterval` polling (2 s) | Zoom's SPA does not expose reliable hooks; polling is the simplest resilient approach |
| `async/await` throughout | Clicking menus requires waiting for DOM transitions; async keeps the code readable |
| No frameworks | Keeps the payload tiny and the code auditable |

---

## Selector Strategy

CSS selectors for Zoom Web DOM elements are defined in two places:

1. **`selectors/zoom-dom-selectors.json`** — The canonical, human-editable reference.
Update this file whenever Zoom changes its DOM.

2. **Embedded `SELECTORS` object in the userscript** — A copy of the JSON embedded directly in the script so TamperMonkey can use them without fetching a separate file.
Keep this in sync with the JSON file.

Every selector is represented as `{ primary, fallback }`. The `resolve()` helper tries `primary` first; if it returns no element, it tries `fallback`. This two-layer approach provides resilience against minor DOM changes without requiring an immediate selector update.

---

## Multi-Pin Automation Logic

```
setInterval (every 2 s)
└─ scanParticipants()
└─ for each participantRow
├─ read name
├─ skip if already in processedParticipants Set
├─ detect raisedHandIcon
│ └─ (skip if absent)
├─ checkCameraStatus() ← scaffold, no-op for now
├─ needsMultipin()
│ ├─ open participant menu
│ ├─ look for "Allow to Multi-Pin" option
│ └─ close menu; return boolean
└─ grantMultipin() (only if needsMultipin returned true)
├─ open participant menu
├─ click "Allow to Multi-Pin"
├─ add name to processedParticipants
└─ update debug panel stats
```

### Idempotency

The `processedParticipants` `Set` is maintained in memory for the lifetime of the page. Once a participant has been processed (either Multi-Pin granted, or Multi-Pin was already active), their name is added to the set and they are skipped on all future scans.

> **Note:** The set is cleared if the page is reloaded. This is acceptable because a page reload resets the meeting UI state as well.

### Retry Protection

`grantMultipin()` will attempt to open the menu and find the option up to **two times** before giving up and logging a warning. This guards against transient rendering delays.

---

## Extension Points

### Camera Check (`checkCameraStatus`)

A scaffold function is already in place. To complete it:

1. Implement `sendChatMessage(text)` using the `chatInput` selector.
2. Call `sendChatMessage("Please turn your camera on to use Multi-Pin.")` inside `checkCameraStatus` when `cameraStatusIcon` is detected.

### Chat Moderation (`monitorChat`)

The `MutationObserver` attached to the chat container already detects messages containing known spam patterns. To add moderation actions:

1. Implement `muteParticipant(name)` using the participant menu.
2. Call it from inside the `spamDetected` block in `monitorChat`.

---

## Limitations & Known Risks

- **DOM Changes:** Zoom updates its web client regularly. If the script stops working, inspect the participant list in DevTools and update the selectors.
- **Host Privileges Required:** The script will silently do nothing if the user does not have host or co-host status, because the "Allow to Multi-Pin" menu item will not appear.
- **In-Memory State:** Reloading the page resets `processedParticipants`. This is acceptable but means participants who raised their hand before the reload may be re-processed.
- **Single-Tab:** The script runs independently in each browser tab. Running it in multiple tabs for the same meeting is not recommended.
142 changes: 142 additions & 0 deletions docs/testing-checklist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Zoom Host Automation — Testing Checklist

Manual QA checklist for `scripts/zoom-host-tools.user.js`.

## Prerequisites

- [ ] Tampermonkey extension is installed in the test browser
- [ ] `scripts/zoom-host-tools.user.js` is installed as a Tampermonkey userscript
- [ ] The test Zoom account has **host** or **co-host** privileges
- [ ] A second Zoom account (participant) is available for testing

---

## Test Cases

### 1. Script Loads Correctly

**Steps:**
1. Open a Zoom Web meeting (`https://*.zoom.us/wc/*`) with the host account.
2. Open the browser DevTools console.

**Expected:**
- `[ZoomHostAuto] Zoom Host Automation initializing…` appears in the console.
- `[ZoomHostAuto] Zoom Host Automation active (interval: 2000ms)` appears in the console.
- A floating debug panel is visible in the bottom-right corner of the page showing "🤖 Zoom Host Automation".

**Pass / Fail:** ___

---

### 2. Raised Hand Detected

**Steps:**
1. Have the participant account raise their hand in the meeting.
2. Wait up to 4 seconds (two scan intervals).

**Expected:**
- Console shows: `[ZoomHostAuto] ✋ Raised hand detected: "<participant name>"`
- The "Raised hands" counter in the debug panel increments by 1.

**Pass / Fail:** ___

---

### 3. Multi-Pin Granted Automatically

**Steps:**
1. Continue from Test 2 (participant's hand is raised and Multi-Pin has not been granted yet).
2. Wait up to 4 seconds after the raised-hand log entry.

**Expected:**
- Console shows: `[ZoomHostAuto] ✅ Granted Multi-Pin to "<participant name>"`
- "Multi-Pin grants" counter in the debug panel increments by 1.
- "Last action" in the debug panel updates to `Granted Multi-Pin to <name>`.
- In Zoom's participant list the participant now has Multi-Pin enabled (verify via the participant menu — the "Allow to Multi-Pin" option should be absent or replaced by "Disable Multi-Pin").

**Pass / Fail:** ___

---

### 4. Same Participant Not Processed Twice

**Steps:**
1. After Test 3, have the participant lower and then raise their hand again.
2. Wait for two scan intervals.

**Expected:**
- Console shows: `[ZoomHostAuto] ℹ️ Multi-Pin already granted for "<participant name>"; skipping` **OR** the participant is simply skipped silently (because their name is in `processedParticipants`).
- "Multi-Pin grants" counter does **not** increment again.

**Pass / Fail:** ___

---

### 5. Script Survives Missing Selectors

**Steps:**
1. Temporarily change a selector in the embedded `SELECTORS` object in the script to an invalid value (e.g., `participantList.primary = '.does-not-exist'`).
2. Reload the meeting page.
3. Wait for several scan intervals.

**Expected:**
- No uncaught JavaScript errors or exceptions in the console.
- The script continues to run (polling loop does not crash).
- Selector failures are either silently skipped or logged as warnings.

**Restore:** Revert the selector change after this test.

**Pass / Fail:** ___

---

### 6. Debug Logs Visible in Console

**Steps:**
1. Ensure `DEBUG_MODE = true` in the script (default).
2. Open the meeting with the host account.
3. Observe the DevTools console.

**Expected:**
- All `[ZoomHostAuto]` log lines appear in the console throughout the meeting.
- No logs appear when `DEBUG_MODE` is set to `false`.

**Pass / Fail:** ___

---

### 7. Chat Monitor Detects Spam (Scaffold)

**Steps:**
1. Open the chat panel in the meeting.
2. From the participant account, send a message containing a URL (e.g., `http://example.com`).
3. Observe the DevTools console on the host account's browser.

**Expected:**
- Console shows: `[ZoomHostAuto] ⚠️ Possible spam detected | user: "<name>" | message: "<text>"`
- No automatic moderation action is taken (this is a scaffold — action hooks are not yet implemented).

**Pass / Fail:** ___

---

### 8. Camera-Off Detection Logged (Scaffold)

**Steps:**
1. Ensure the participant's camera is **off**.
2. Have the participant raise their hand.
3. Observe the console after the scan picks up the raised hand.

**Expected:**
- Console shows: `[ZoomHostAuto] 📷 Camera is OFF for "<participant name>"`
- No chat message is sent automatically (full implementation is a TODO).

**Pass / Fail:** ___

---

## Regression Notes

| Date | Tester | Zoom Web Version | Overall Result | Notes |
|------|--------|-----------------|---------------|-------|
| | | | | |
Loading