GhEx.Notifications wraps the GitHub Notifications REST API for consuming the
authenticated user's inbox: list threads, mark them read or done, and read
GitHub's suggested poll interval. It is a set of thin GhEx.REST wrappers, so it
returns the usual {:ok, body, meta} / {:error, reason} shape and passes
opts through to Req.
{:ok, threads, _meta} = GhEx.Notifications.list(client, params: [participating: true])
for t <- threads do
IO.puts("#{t["reason"]}: #{t["subject"]["title"]}")
endA thread is a pointer, not a payload. It tells you that something happened
(reason, subject.type, subject.url) but not the details. To act on it,
fetch the subject:
{:ok, pr, _} = GhEx.REST.get(client, thread["subject"]["url"])Polling politely
The inbox has no webhook, so consuming it means polling. GitHub asks you to do three things, and gh_ex gives you each piece:
- Poll conditionally. Send the previous response's validator so an unchanged
inbox is cheap and does not count against your rate limit. A
304comes back as{:ok, :not_modified, meta}(distinct from a normal{:ok, body, meta}). - Respect
X-Poll-Interval.poll_interval/2reads the header, the seconds GitHub wants you to wait before the next poll. - Back off on the rate limit.
GhEx.RateLimit.delay_until_reset/2is the backstop when the bucket runs low.
gh_ex supplies these pieces; the loop is yours. A minimal poller:
defmodule Inbox do
def poll(client, last_modified \\ nil) do
headers = if last_modified, do: [{"if-modified-since", last_modified}], else: []
case GhEx.Notifications.list(client, headers: headers, params: [participating: true]) do
{:ok, :not_modified, meta} ->
wait_and_repeat(client, last_modified, meta)
{:ok, threads, meta} ->
Enum.each(threads, &handle/1)
wait_and_repeat(client, meta.last_modified, meta)
end
end
defp wait_and_repeat(client, last_modified, meta) do
Process.sleep(GhEx.Notifications.poll_interval(meta) * 1000)
poll(client, last_modified)
end
defp handle(thread), do: IO.inspect(thread["subject"]["title"])
endIn a job system the same shape is non-blocking: return {:snooze, poll_interval(meta)} instead of sleeping, and store meta.last_modified (or
meta.etag) on the job for the next run.
What is not here
GhEx.Notifications is a client, not a consumer engine: no poll loop, dedup, or
state ship with it.
Thread subscriptions are supported: get_thread_subscription/3,
set_thread_subscription/4 (%{ignored: true} to mute, %{ignored: false} to
subscribe), and delete_thread_subscription/3. Repository watch subscriptions,
which govern what enters the inbox at the repo level, belong to the Watching API
and are tracked separately.