A console application that scans your Inoreader RSS subscriptions, detects defunct (dead) or otherwise problematic feed URLs, and lets you interactively unsubscribe from them.
- Fetches all subscriptions from your Inoreader account via the Inoreader API.
- Filters out unsupported entries — email subscriptions and keyword-monitoring (non-RSS YouTube) subscriptions are skipped automatically.
- Normalizes feed URLs — handles
webfeed://custom scheme URLs by unwrapping and decoding the embedded URL. - Checks each feed URL by sending an HTTP
HEADrequest (falling back toGETifHEADis not supported or returns an error). - Prompts you to delete any subscription whose URL returns
404 Not Foundor503 Service Unavailable. - Unsubscribes via the Inoreader API when you confirm deletion.
- .NET 8 SDK
- An Inoreader account with API access (App ID and App Key)
Create or edit appsettings.json in the project root:
{
"InoreaderAPI": {
"AppId": "YOUR_APP_ID",
"AppKey": "YOUR_APP_KEY",
"BaseUrl": "https://www.inoreader.com/reader/api/0"
}
}
⚠️ Do not commit real credentials. Addappsettings.jsonto.gitignoreor use user secrets.
The app uses the OAuth2 Authorization Code flow to obtain and manage access tokens automatically.
- The app opens your browser to the Inoreader authorization page.
- You authorize the app and get redirected to
https://localhost/?code=.... - You paste the
codevalue from the redirect URL into the console. - The app exchanges the code for an access token and a refresh token, and saves both to
.tokens.json.
- If the cached access token is still valid (within its 24-hour lifetime), it is reused — no network call needed.
- If the access token has expired, the app automatically refreshes it using the saved refresh token — no browser interaction needed.
- If the refresh token is revoked or invalid, the app falls back to the browser authorization flow.
The token_type from the OAuth response (e.g., Bearer) is stored and used for API calls — it is never hardcoded.
dotnet build
dotnet runWhen you run the app, it will:
- Authenticate (automatically if tokens are cached, or prompt for browser authorization on first run).
- Fetch and scan all subscriptions.
- Display each defunct subscription it finds.
- Ask you to confirm deletion (
y/n) for each one.
Using cached access token.
🚀 Obtaining the subscriptions...
🚀 Processing the subscriptions...
❌ Checked status 404 NotFound: [Some Dead Blog]
URL: https://example.com/feed.xml
Delete this subscription? (y/n): y
✅ Deletion status True of https://example.com/feed.xml
├── Api/
│ ├── InoreaderSubscriptionClient.cs # Inoreader API client (subscriptions, URL checks, unsubscribe)
│ └── InoreaderHttpClientFactory.cs # Creates HttpClients for API and external URL checks
├── Auth/
│ ├── AuthPrompt.cs # Console UI for the browser authorization prompt
│ ├── InoreaderOAuthClient.cs # OAuth2 flows (authorize, exchange code, refresh tokens)
│ ├── OAuthTokenResponse.cs # Deserialization model for /oauth2/token responses
│ └── TokenStore.cs # Persists tokens to .tokens.json with expiry tracking
├── Config/
│ ├── InoreaderAPIConfig.cs # Strongly-typed config with URI validation
│ └── InoreaderConfigFactory.cs # Loads and validates config from appsettings.json
├── Extensions/
│ └── ExceptionExtensions.cs # Exception message chain helper (supports AggregateException)
├── Manager/
│ └── InoreaderSubscriptionManager.cs # Orchestrates the scan-and-clean workflow
├── Models/
│ └── Subscription.cs # Subscription model with URL normalization and validation
├── Utilities/
│ └── BrowserLauncher.cs # Cross-platform browser launcher
├── Program.cs # Entry point (orchestration only)
├── appsettings.json # Configuration (excluded from source control)
├── .tokens.json # OAuth tokens (auto-generated, excluded from source control) - MUST be in .gitignore
└── .editorconfig # Code style and naming conventions
This project is provided as-is for personal use.