SRT Korean Train Service Skill
Prerequisites
- Environment variables
SRT_PHONE(format:010-XXXX-XXXX) andSRT_PASSWORDmust be set before running scripts.
Reference
Environment variables:
| Variable | Required | Description |
|---|---|---|
SRT_PHONE |
β | SRT account phone number (hyphens required: 010-XXXX-XXXX) |
SRT_PASSWORD |
β | SRT account password |
SRT_DATA_DIR |
optional | Directory for logs, cache, and state files. Defaults to system temp dir (/tmp/srt). |
Station names (Korean only): μμ, λΆμ°, λλꡬ, λμ , μ²μμμ°, μ€μ‘, κ΄μ£Όμ‘μ , μΈμ°, ν¬ν, κ²½μ£Ό, κΉμ²κ΅¬λ―Έ, μ΅μ°, μ μ£Ό, λͺ©ν¬, μ κ²½μ£Ό
Date: YYYYMMDD Β· Time: HHMMSS (e.g. 200000 = 20:00)
Commands
Search Trains
cd <project_dir> && uv run --with SRTrain python3 scripts/srt_cli.py train search \
--departure "μμ" --arrival "λλꡬ" --date "20260227" --time "200000"
Search params and results are cached (in SRT_DATA_DIR) and required by reserve.
Reserve (one-shot)
cd <project_dir> && uv run --with SRTrain python3 scripts/srt_cli.py reserve one-shot --train-id "1"
--train-id is the 1-based index from search results. Must run train search first.
View Reservations
cd <project_dir> && uv run --with SRTrain python3 scripts/srt_cli.py reserve list --format json
Cancel Reservation
cd <project_dir> && uv run --with SRTrain python3 scripts/srt_cli.py reserve cancel \
--reservation-id "RES123456" --confirm
Continuous Monitoring (μ·¨μν λͺ¨λν°λ§)
For "keep trying until a seat opens" requests, do not loop inside a cron job.
Instead: run srt_cli.py reserve retry as a persistent background process, then create a separate cron job to read the log and report.
Step 1: Search (populate cache)
cd <project_dir> && uv run --with SRTrain python3 scripts/srt_cli.py train search \
--departure "μμ" --arrival "λλꡬ" --date "20260227" --time "200000"
Note the train_id of the target train from the results.
Step 2: Start background retry process
LOG_FILE=<choose_any_path>.log
PID_FILE=<choose_any_path>.pid
cd <project_dir> && nohup uv run --with SRTrain python3 scripts/srt_cli.py reserve retry \
--train-id <id> --timeout-minutes 1440 --wait-seconds 10 \
--log-file "$LOG_FILE" > /dev/null 2>&1 &
echo $! > "$PID_FILE"
The script prints LOG_FILE: <path> on startup β capture this to know exactly where logs are written.
You may also set SRT_DATA_DIR to control where auto-generated logs and cache files are placed.
Path safety:
SRT_DATA_DIRand--log-fileare validated at runtime to resolve within the user's home directory or system temp dir only. Paths that escape these boundaries (e.g. via../) are rejected.
reserve retry options:
| Option | Default | Description |
|---|---|---|
--train-id |
(all) | 1-based index from search; comma-separated for multiple |
--timeout-minutes |
60 | Total duration. Use 1440 for 24h |
--wait-seconds |
10 | Delay between attempts |
--log-file |
auto | Explicit log file path (overrides SRT_DATA_DIR default) |
Log markers to watch for:
=== μλ #Nβ attempt numberSUCCESSβ reservation succeeded (contains μμ½λ²νΈ, μ’μ)TIMEOUTβ timed out without success
Step 3: Create periodic reporting cron job
Create an isolated agentTurn cron job (every 15 min) that:
- Checks process status:
Outputscd <project_dir> && uv run --with SRTrain python3 scripts/srt_cli.py reserve status --pid-file <pid_file>RUNNING (<pid>)orNOT_RUNNING (...)β no shell command substitution involved. - Reads log tail:
tail -50 <log_file> - Parses attempt count and last attempt time from log
- Reports to channel
- On
SUCCESSin log β extract μμ½λ²νΈ/μ’μ info, report, remove this cron job - On
TIMEOUTor processNOT_RUNNINGβ report, remove this cron job
The cron job's task message must include its own job ID (update after creation) so it can self-remove.
Step 4: Create termination job
Create an isolated agentTurn at-schedule cron job at the end time that:
- Stops the process:
cd <project_dir> && uv run --with SRTrain python3 scripts/srt_cli.py reserve stop --pid-file <pid_file> - Removes the reporting cron job by ID
- Reads final log and reports outcome
JSON Output
Search result item:
{
"train_number": "369",
"departure_time": "200000",
"arrival_time": "213600",
"departure_station": "μμ",
"arrival_station": "λλꡬ",
"seat_available": false,
"general_seat": "λ§€μ§",
"special_seat": "λ§€μ§",
"train_id": "1"
}
Reservation result:
{
"success": true,
"data": {
"reservation_id": "RES123456",
"train_number": "369",
"seat_number": "3A",
"payment_required": true
}
}
Exit codes: 0 = success Β· 1 = retryable (no seats) Β· 2 = fatal
Error Handling
| Error | Cause | Resolution |
|---|---|---|
AuthenticationFailed |
Wrong credentials | Check SRT_PHONE / SRT_PASSWORD |
NoSeatsAvailable |
Sold out | Use --retry or try different train |
StationNotFound |
Invalid name | Use Korean station names above |
NoTrainsFound |
No trains found | Try different date/time |
RateLimitExceeded |
Too many attempts | Wait a few minutes |
Natural Language Handling
Extract from Korean input:
- Stations β Korean names (μμ, λλꡬ, etc.)
- Date β relative ("λ΄μΌ", "λ€μμ£Ό κΈμμΌ") to YYYYMMDD
- Time β ("20μ μ΄ν", "μ€ν 2μ") to HHMMSS
- Passenger count β default 1 if not specified
Patterns:
- "κ²μν΄μ€" β
train search - "μμ½ν΄μ€" (one-shot) β
train searchthenreserve one-shot - "μ·¨μν λμ€λ©΄ μ‘μμ€ / λ λκΉμ§ λλ €μ€" β Continuous Monitoring flow above
- "λ΄ μμ½ νμΈν΄μ€" β
reserve list - "μ·¨μν΄μ€" β
listthencancel
Payment Note
Reservations must be paid via SRT app or https://etk.srail.kr within ~20 minutes of reservation.