graham-campbell/github-notifications
A PHP client for GitHub's notifications API, letting you list, filter, mark as read, and manage repository notifications from your applications. Part of Graham Campbell's GitHub package ecosystem.
The package is a Laravel wrapper around the GitHub Notifications API whose purpose is to programmatically shed notification noise — bulk-marking threads as read/done, pruning stale threads, and ignoring the repositories that generate the most chatter. Day-to-day it is a scheduled maintenance tool, not a request-time dependency.
Minimum path from zero to value:
Install and discover.
composer require graham-campbell/github-notifications
Laravel's package discovery registers GrahamCampbell\GitHubNotifications\GitHubNotificationsServiceProvider. On older skeletons add it to config/app.php manually.
Publish the config.
php artisan vendor:publish --provider="GrahamCampbell\GitHubNotifications\GitHubNotificationsServiceProvider"
This drops config/github-notifications.php. Read it end-to-end before touching anything else — this file is the real API of the package, and the rules you care about live there.
Provide credentials. Create a classic PAT with at least notifications scope (repo too if you intend to resolve subject URLs or act on private repo threads), then set it in .env:
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
GITHUB_USERNAME=your-handle
The package typically consumes the shared graham-campbell/github connection config, so if you already have GITHUB_TOKEN wired for other Graham Campbell GitHub packages, you are usually done.
Confirm what commands exist. Don't guess command signatures — enumerate them:
php artisan list github
That prints the registered commands and their options. Run the primary command with --help before the first real execution.
First use case: a one-shot inbox purge. The highest-value first run is marking everything older than N days as read, and optionally unsubscribing. Do it once interactively so you can see the output, then move it to the scheduler. Run it in a dry-run/verbose mode if the command exposes one, and cross-check the GitHub notifications page in a browser immediately after — the API's notion of "read" and the UI's unread badge should agree.
Scheduled pruning (the canonical pattern). The package earns its keep as a nightly job. In routes/console.php:
Schedule::command('github-notifications:...')->dailyAt('06:00');
or in the legacy app/Console/Kernel.php $schedule array. Guard it with ->withoutOverlapping() — a slow paginated run against a large inbox will otherwise stack if the API is throttling.
Layered rules instead of one blunt rule. Treat the config as a policy document with progressively stricter tiers:
'rules' => [
// 1. Never touch review requests or direct @mentions.
['keep' => ['reason' => ['review_requested', 'mention']]],
// 2. Aggressively clear high-volume bot repos.
['repo' => 'org/renovate-bot', 'older_than' => '1 day', 'action' => 'done'],
// 3. Softer treatment for human repos.
['repo' => 'org/*', 'older_than' => '30 days', 'action' => 'read'],
],
Order matters: first match wins in most implementations, so put the protective "keep" rules at the top. Verify the precedence by running with verbose output on a small time window.
Dry-run → audit → enforce. Always prototype with the narrowest possible window (--since/older_than of a few hours or one repo) and compare the command's summary line against the GitHub UI. Once the counts match what you expect, widen the window. Keep the pre-change counts in a log so you can prove the tool isn't silently dropping threads you needed.
Separate "read" from "unsubscribe." These are different decisions with different blast radius. Marking read is reversible in the UI; unsubscribing is not recoverable without re-finding the thread. Run the read pass on a short schedule (daily) and any unsubscribe pass on a long one (monthly), or gate unsubscribe behind an explicit flag in the command invocation.
Multi-account handling. If you maintain personal and work identities, don't share one process. Register two GitHub connections (different token, different config) and run two scheduled commands, or parameterize the command with a connection name if supported. Mixing tokens in one run is the fastest way to mark the wrong account's threads as read.
Log the delta, not the chatter. Wrap the scheduled invocation and emit a metric:
Schedule::command('github-notifications:...')->daily()->then(function () {
// read the command's summary output / log line and push to your metrics sink
});
Trending "threads pruned per night" is the signal that your rules have drifted too aggressive or too lax.
Pair with an outbound notifier. If the package clears threads you'd otherwise see, you lose information. Feed the surviving notifications (review requests, mentions) into Slack/Mail via a small supplementary command so pruning never removes the last channel by which you'd learn about a blocker.
notifications yields empty result sets, not errors. If a run reports "0 threads processed" against an obviously busy inbox, check the token scope before you debug anything else.config:cache will bite you. After editing rules, run php artisan config:cache (or clear it) — a cached config is the usual explanation for "my rule changes did nothing."APP_TIMEZONE and reason in UTC when writing windows.last_read_at semantics, the all/participating split, deprecations around since). Pin the package version in composer.json and read the changelog before a major bump — a green test suite won't catch a changed default in an upstream API.php artisan list, (2) token scope, (3) config cached, (4) scheduler actually running (schedule:run in cron, not just schedule:list), (5) rules ordered such that a broad "keep" isn't swallowing everything.How can I help you explore Laravel packages today?