RWParcer is a .NET application that parses rw.by data and provides train subscriptions with notifications on changing seats.
RWParcer/— application containing the bot, command routing, configuration, and session store integration.RWParcerCore/— domain logic, entities, repositories, services, interfaces, and database infrastructure.RWParcerCore.Tests/— unit tests.docker-compose.yml— environment definition for running app and required proxies.Dockerfile/Dockerfile.psiphon— application image build instructions.env/— example configuration (appsettings, data, logging).
- .NET (C#)
- Entity Framework Core (migrations and database access)
- PostgreSQL
- Docker + docker-compose
-
Make sure you have installed:
- Docker & Docker Compose
-
Review / update configuration in
env/appsettings.jsonas needed.
git clone https://github.com/danilaltd/RWParcer
cd RWParcer
docker compose up --buildThis will start:
- the application service (built from
Dockerfile) - required proxies
To stop, run
docker compose down.
Program.csinitializes DI, loads configuration, and registers services.BotService(orCommandRouter) handles incoming commands and calls intoRWParcerCore.RWParcerCorecontains business logic, domain entities, and repository implementations.- Sessions/state are persisted to the database (PostgreSQL/SQLite) via
SessionDbContext.
The project uses GitHub Actions with a three-stage workflow defined in .github/workflows/main.yml:
Push to main branch
↓
[ci-checks] (github-hosted ubuntu-latest)
- Secret detection (Gitleaks)
- .NET build check
- Optional: Unit tests
↓
[build] (github-hosted ubuntu-latest)
- Build Docker images
- Push to GitHub Container Registry (ghcr.io)
↓
[deploy] (self-hosted runner)
- Pull and deploy using docker-compose
- Apply runtime configuration from secrets
Runs code quality and build validation:
- Secret Detection — Gitleaks scans for accidentally committed secrets.
- Setup .NET — Uses .NET 8.0.x SDK.
- Restore & Build — Restores NuGet dependencies and builds in Release mode.
Builds and publishes Docker images:
- Builds two images:
- ghcr.io/<owner>/rwparcer:latest (main application)
- ghcr.io/<owner>/rwparcer-psiphon:latest (proxy service)
- Waits for ci-checks job to pass
- Pushes both images to GitHub Container Registry
- Requires GITHUB_TOKEN permissionsDeploys the application to production:
- Checks out code
- Creates
./env/directory - Injects
appsettings.jsonfrom GitHub Secrets - Pulls latest images from ghcr.io
- Runs
docker compose up -dto deploy/update services
The self-hosted runner must be configured on a production/staging server with:
- Docker & Docker Compose installed
- GitHub Runner installed and registered
Ensure the runner can:
- Execute
dockeranddocker composecommands - Read/write to
/var/lib/rwparcer/data - Access GitHub Container Registry (via
docker login)
The CI/CD pipeline requires these secrets configured in GitHub Settings → Secrets and variables → Actions:
- Default token, used for:
- Container registry authentication
- Wait-on-check workflow step
- Complete
appsettings.jsoncontent as a multiline secret - Example structure:
{ "BotSettings": { "Token": "your-bot-token" }, "DatabaseSettings": { "ConnectionString": "Server=parcer-db;..." }, "ProxySettings": { "ProxyRotationEnabled": true }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning" } } }
- Go to GitHub Repository → Settings → Secrets and variables → Actions
- Click New repository secret
- Name:
APP_SETTINGS_JSON - Value: Paste full
appsettings.jsoncontent - Save
Psiphon is a proxy/VPN service used to:
- Rotate IP addresses for web scraping (rw.by requires proxy rotation)
- Distribute requests across multiple proxy instances
- Bypass IP-based rate limiting
In docker-compose.yml, the Psiphon service is configured with:
services:
psiphon:
image: ghcr.io/danilaltd/rwparcer-psiphon:latest
volumes:
- /var/lib/rwparcer/data:/app/data
environment:
NODE_ID_FROM_HOSTNAME: "1"
replicas: 10 # 10 parallel proxy instances
restart: always- 10 Replicas — 10 independent proxy nodes running in parallel
- Shared Data Volume — All replicas share
/var/lib/rwparcer/datafor state/logging - NODE_ID_FROM_HOSTNAME — Each replica gets a unique ID from its hostname
- Always Restart — Automatically restarts if a replica fails
The main parcer service communicates with Psiphon replicas:
services:
parcer:
environment:
PSIPHON_REPLICAS: 10 # Number of proxy instances to connect toThe application code (in RWParcerCore/Infrastructure) uses HttpClientFactoryWithProxyRotation to:
- Select a random Psiphon replica
- Route HTTP requests through it
- Rotate to next replica on next request (load balancing)
docker compose up --buildStarts both psiphon (10 replicas) and parcer (main app) locally.
- Developer pushes to
mainbranch - GitHub Actions triggered automatically:
- Runs
ci-checks(linting, build) - Builds Docker images (
rwparcer+rwparcer-psiphon) - Pushes to ghcr.io
- Runs
- Self-hosted runner pulls latest images
- Production deployment via
docker compose pull && docker compose up -d
If deployment fails:
# On self-hosted machine
docker compose down
docker compose up -d --remove-orphans