|
| 1 | +--- |
| 2 | +name: add-event-handler |
| 3 | +description: Adds a new GitHub event type case to the switch block in `web/github.php`. Handles extracting fields from `$Message`, building a `$ChatMsg` string with markdown links, setting `$Msg['text']`, and calling `SendToChat('notifications', $Msg, $useRC, $useTeams)`. Use when user says 'add support for X event', 'handle X webhook', 'new event type', or adds a case to `web/github.php`. Do NOT use for modifying existing event handlers. |
| 4 | +--- |
| 5 | +# add-event-handler |
| 6 | + |
| 7 | +## Critical |
| 8 | + |
| 9 | +- **Never** remove the `break` at the end of a case — fall-through will corrupt routing. |
| 10 | +- `$Msg['text']` MUST be set before calling `SendToChat`. Teams POSTs only `$Payload['text']`; missing it sends `null`. |
| 11 | +- Add new cases **before** `default:` — the default catch-all must remain last. |
| 12 | +- `$useRC` and `$useTeams` are already `true` at switch entry. Only override them if the event or repo should skip a target (see `issues` case for the per-repo pattern). |
| 13 | +- Do NOT re-declare `$RepositoryName`, `$EventType`, `$Message`, `$User`, or `$Msg` — they are set before the switch. |
| 14 | + |
| 15 | +## Instructions |
| 16 | + |
| 17 | +1. **Look up the GitHub payload schema** for the target event at `https://docs.github.com/en/webhooks/webhook-events-and-payloads#<event_name>`. Note which top-level keys carry the entity (e.g. `pull_request`, `issue`, `release`) and which carry `action`, `html_url`, `title`/`name`. |
| 18 | + Verify: you know the exact key names before writing any `$Message['...']` access. |
| 19 | + |
| 20 | +2. **Add the case block** inside the `switch ($EventType)` block in `web/github.php`, immediately before `default:` (line ~265): |
| 21 | + |
| 22 | + ```php |
| 23 | + case 'your_event': |
| 24 | + $Entity = $Message['your_event']; // top-level entity key |
| 25 | + $Action = $Message['action'] ?? ''; |
| 26 | + $Url = $Entity['html_url'] ?? ''; |
| 27 | + $Title = $Entity['title'] ?? $Entity['name'] ?? ''; |
| 28 | + |
| 29 | + $ChatMsg = "🔔 [{$User}](https://github.com/{$User}) **{$Action}** " |
| 30 | + . "[{$Title}]({$Url}) " |
| 31 | + . "in [{$RepositoryName}](https://github.com/{$RepositoryName})."; |
| 32 | + |
| 33 | + // Optional: append body preview (max 500 chars) |
| 34 | + if (!empty($Entity['body'])) { |
| 35 | + $ChatMsg .= "\n\n> " . substr($Entity['body'], 0, 500) |
| 36 | + . (strlen($Entity['body']) > 500 ? "…" : ""); |
| 37 | + } |
| 38 | + |
| 39 | + $Msg['text'] = $ChatMsg; |
| 40 | + SendToChat('notifications', $Msg, $useRC, $useTeams); |
| 41 | + break; |
| 42 | + ``` |
| 43 | + |
| 44 | + This step uses `$User`, `$RepositoryName`, `$Msg` set before the switch. |
| 45 | + |
| 46 | +3. **Choose an emoji** that fits the event. Reference from existing cases: |
| 47 | + - Push / release: `📦` |
| 48 | + - Issue / bug: `🐛` |
| 49 | + - Pull request: `🔀` |
| 50 | + - Wiki (gollum): `📝` |
| 51 | + - CI check pass/fail: `✅` / `❌` / `⏳` |
| 52 | + - Generic/info: `ℹ️` |
| 53 | + |
| 54 | +4. **Suppress noisy CI-style events** by commenting out `SendToChat` and leaving `$Msg['text']` set (matches `check_suite`/`workflow_run` pattern): |
| 55 | + ```php |
| 56 | + $Msg['text'] = $ChatMsg; |
| 57 | + //SendToChat('notifications', $Msg, $useRC, $useTeams); |
| 58 | + break; |
| 59 | + ``` |
| 60 | + |
| 61 | +5. **Suppress for specific repos** using the `issues` case pattern: |
| 62 | + ```php |
| 63 | + case 'your_event': |
| 64 | + if (in_array($RepositoryName, ['owner/repo1', 'owner/repo2'])) { |
| 65 | + $useTeams = false; |
| 66 | + break; |
| 67 | + } |
| 68 | + // ... rest of handler |
| 69 | + ``` |
| 70 | + |
| 71 | +6. **Add a test fixture** under `tests/events/<event_name>/`: |
| 72 | + - `payload.json` — raw GitHub webhook payload (copy from GitHub delivery logs or docs) |
| 73 | + - `type.txt` — event name only, e.g. `your_event` (no trailing newline needed; `trim()` is applied) |
| 74 | + - Note: `expected.bin` / `discord.json` are consumed by `IrcConverter`/`DiscordConverter` tests, not by `web/github.php` — add them only if those converters also need updating. |
| 75 | + |
| 76 | +7. **Run quality checks** to verify no syntax errors or style violations: |
| 77 | + ```bash |
| 78 | + vendor/bin/phpstan analyse |
| 79 | + vendor/bin/php-cs-fixer fix --dry-run |
| 80 | + ``` |
| 81 | + Fix any reported issues before committing. |
| 82 | + |
| 83 | +## Examples |
| 84 | + |
| 85 | +**User says:** "Add support for the `milestone` webhook event" |
| 86 | + |
| 87 | +**Actions taken:** |
| 88 | +1. GitHub docs show payload has `$Message['milestone']` with keys `html_url`, `title`, `description`, and `$Message['action']`. |
| 89 | +2. Insert before `default:`: |
| 90 | + ```php |
| 91 | + case 'milestone': |
| 92 | + $Milestone = $Message['milestone']; |
| 93 | + $Action = $Message['action'] ?? ''; |
| 94 | + $Url = $Milestone['html_url'] ?? ''; |
| 95 | + $Title = $Milestone['title'] ?? ''; |
| 96 | + |
| 97 | + $ChatMsg = "🏁 [{$User}](https://github.com/{$User}) **{$Action}** " |
| 98 | + . "milestone [{$Title}]({$Url}) " |
| 99 | + . "in [{$RepositoryName}](https://github.com/{$RepositoryName})."; |
| 100 | + |
| 101 | + if (!empty($Milestone['description'])) { |
| 102 | + $ChatMsg .= "\n\n> " . substr($Milestone['description'], 0, 500) |
| 103 | + . (strlen($Milestone['description']) > 500 ? "…" : ""); |
| 104 | + } |
| 105 | + |
| 106 | + $Msg['text'] = $ChatMsg; |
| 107 | + SendToChat('notifications', $Msg, $useRC, $useTeams); |
| 108 | + break; |
| 109 | + ``` |
| 110 | +3. Created `tests/events/milestone/payload.json` and `tests/events/milestone/type.txt` containing `milestone`. |
| 111 | +4. Ran `vendor/bin/phpstan analyse` — no errors. |
| 112 | + |
| 113 | +**Result:** `milestone` events POST to Rocket Chat and Teams with action, linked title, and optional description preview. |
| 114 | + |
| 115 | +## Common Issues |
| 116 | + |
| 117 | +- **`Undefined array key 'your_event'`** at `$Entity = $Message['your_event']`: The payload root key name differs from the event name (e.g. `deployment_status` event uses `$Message['deployment_status']` but `check_run` also appears nested). Dump `$Message` via the log file in `log/` and check the actual keys. |
| 118 | + |
| 119 | +- **Teams receives `null` body**: `$Msg['text']` was not set before `SendToChat`. `SendToChat` sends `$Payload['text']` to Teams — ensure `$Msg['text'] = $ChatMsg` is present. |
| 120 | + |
| 121 | +- **`php-cs-fixer` reports indentation errors**: The switch body uses a **tab** for the `case` line and **8 spaces** for the body (mixed in the file). Match the surrounding case's indentation exactly — do not auto-convert. |
| 122 | + |
| 123 | +- **Event still hits `default:`**: The `$EventType` string comes from the `X-GitHub-Event` header. Verify your `case` string matches exactly (lowercase, underscores) — e.g. `'pull_request'` not `'pullRequest'`. |
| 124 | + |
| 125 | +- **`SendToChat` returns `false` silently**: The channel key `'notifications'` must exist in both `$chatChannels['rocketchat']` and `$chatChannels['teams']` in `src/config.php`. If the URL is missing, curl will fail with HTTP 0. |
0 commit comments