repomatic.github.unsubscribe module

Unsubscribe from closed, inactive GitHub notification threads.

Processes notification threads in two phases:

  1. REST notification threads — Fetches all Issue/PullRequest notification threads via /notifications, inspects each for closed + stale status, and unsubscribes via DELETE + PATCH.

  2. GraphQL threadless subscriptions — Searches for closed issues/PRs the user is involved in but that lack notification threads, and unsubscribes via the updateSubscription mutation.

Requires the gh CLI to be installed and authenticated with a token that has the notifications scope (classic PAT) or equivalent fine-grained permissions.

repomatic.github.unsubscribe.GRAPHQL_PAGE_SIZE = 25

Per-page count for GraphQL search results.

repomatic.github.unsubscribe.NOTIFICATION_PAGE_SIZE = 50

Per-page count for REST /notifications results.

repomatic.github.unsubscribe.NOTIFICATION_SUBJECT_TYPES = frozenset({'Issue', 'PullRequest'})

Notification subject types to process.

class repomatic.github.unsubscribe.DetailRow(action, html_url, number, repo, title, updated_at)[source]

Bases: object

Per-item detail for the markdown report table.

action: ReportAction
html_url: str
number: int | None
repo: str
title: str
updated_at: datetime | None
class repomatic.github.unsubscribe.Phase1Result(batch_size=0, cutoff=None, newest_updated=None, oldest_updated=None, rows=<factory>, threads_failed=0, threads_inspected=0, threads_skipped_open=0, threads_skipped_recent=0, threads_skipped_unknown=0, threads_total=0, threads_unsubscribed=0)[source]

Bases: object

Accumulated counts and details from REST notification phase.

batch_size: int = 0
cutoff: datetime | None = None
newest_updated: datetime | None = None
oldest_updated: datetime | None = None
rows: list[DetailRow]
threads_failed: int = 0
threads_inspected: int = 0
threads_skipped_open: int = 0
threads_skipped_recent: int = 0
threads_skipped_unknown: int = 0
threads_total: int = 0
threads_unsubscribed: int = 0
class repomatic.github.unsubscribe.Phase2Result(batch_size=0, cutoff=None, graphql_failed=0, graphql_not_subscribed=0, graphql_skipped_recent=0, graphql_total=0, graphql_unsubscribed=0, rows=<factory>, search_query='', skipped=False, skip_reason='')[source]

Bases: object

Accumulated counts and details from GraphQL threadless phase.

batch_size: int = 0
cutoff: datetime | None = None
graphql_failed: int = 0
graphql_not_subscribed: int = 0
graphql_skipped_recent: int = 0
graphql_total: int = 0
graphql_unsubscribed: int = 0
rows: list[DetailRow]
search_query: str = ''
skipped: bool = False
skip_reason: str = ''
class repomatic.github.unsubscribe.UnsubscribeResult(dry_run=False, months=3, phase1=<factory>, phase2=<factory>)[source]

Bases: object

Accumulated results from both unsubscribe phases.

dry_run: bool = False
months: int = 3
phase1: Phase1Result
phase2: Phase2Result
repomatic.github.unsubscribe.render_report(result)[source]

Render a markdown report from unsubscribe results.

Pure function that produces the same markdown structure as the downstream unsubscribe.yaml workflow’s $GITHUB_STEP_SUMMARY.

Parameters:

result (UnsubscribeResult) – Structured results from both phases.

Return type:

str

Returns:

Markdown report string.

repomatic.github.unsubscribe.unsubscribe_threads(months, batch_size, dry_run)[source]

Unsubscribe from closed, inactive notification threads.

Runs two phases, each behind its own runner:

  1. REST notification threads (_run_rest_phase()) — Fetches notification threads, inspects each subject for closed + stale status, and unsubscribes.

  2. GraphQL threadless subscriptions (_run_graphql_phase()) — Searches for closed issues/PRs the user is involved in and unsubscribes via mutation.

Parameters:
  • months (int) – Inactivity threshold in months.

  • batch_size (int) – Maximum threads/items to process per phase.

  • dry_run (bool) – If True, report what would be done without acting.

Return type:

UnsubscribeResult

Returns:

Structured results from both phases.