# Srt

> Searches, reserves, and manages Korean SRT train tickets via CLI, including continuous monitoring for canceled seats.

- Skill: `johnalbertini14-glitch/srt` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add johnalbertini14-glitch/srt`
- Raw SKILL.md: https://api.skillmd.com/api/skills/johnalbertini14-glitch/srt/raw
- Safety review: CAUTION (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity, Coding & Dev Tools, Personal Automation
- Tags: Cli, Cron, Discord, Korean Train, Srt, Train Reservation
- Author: johnalbertini14-glitch (https://skillmd.com/u/johnalbertini14-glitch)
- Updated: 2026-08-22
- Page: https://skillmd.com/skills/johnalbertini14-glitch/srt

---


# SRT Korean Train Service Skill

## Prerequisites

- Environment variables `SRT_PHONE` (format: `010-XXXX-XXXX`) and `SRT_PASSWORD` must 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
```bash
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)
```bash
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
```bash
cd <project_dir> && uv run --with SRTrain python3 scripts/srt_cli.py reserve list --format json
```

### Cancel Reservation
```bash
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)
```bash
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
```bash
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_DIR` and `--log-file` are 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 number
- `SUCCESS` — reservation succeeded (contains 예약번호, 좌석)
- `TIMEOUT` — timed out without success

### Step 3: Create periodic reporting cron job
Create an **isolated agentTurn** cron job (every 15 min) with **`--no-deliver`** (delivery mode: none).
The agent must use the `message` tool to post directly to the Discord channel — do **not** use `--announce` (the announce queue can fail with a gateway pairing error in isolated sessions).

CLI:
```bash
openclaw cron add \
  --agent srt \
  --name "SRT 모니터링 보고 (15분마다)" \
  --every 15m \
  --session isolated \
  --no-deliver \
  --message "..."
```

Agent message must instruct:
1. Check process status:
   ```bash
   cd <project_dir> && uv run --with SRTrain python3 scripts/srt_cli.py reserve status --pid-file <pid_file>
   ```
2. Read log tail: `tail -50 <log_file>`
3. Summarise attempt count, last attempt time, success/failure
4. **Send report via `message` tool** (`channel=discord`, `target=<channel_id>`)
5. On `SUCCESS` in log → include 예약번호/좌석 in message, then remove this cron job and termination job
6. On `NOT_RUNNING` without `SUCCESS` → report crash, remove this cron job

The message payload must include this job's own ID and the termination job ID so it can self-remove.

### Step 4: Create termination job
Create an **isolated agentTurn** `at`-schedule cron job (`--no-deliver`, `--delete-after-run`) at the deadline.

CLI:
```bash
openclaw cron add \
  --agent srt \
  --name "SRT 모니터링 종료" \
  --at "<ISO UTC time>" \
  --session isolated \
  --no-deliver \
  --delete-after-run \
  --message "..."
```

Agent message must instruct:
1. Stop the process:
   ```bash
   cd <project_dir> && uv run --with SRTrain python3 scripts/srt_cli.py reserve stop --pid-file <pid_file>
   ```
2. Remove the reporting cron job by ID
3. Read final log and **send outcome via `message` tool** to Discord channel

---

## JSON Output

**Search result item:**
```json
{
  "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:**
```json
{
  "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 search` then `reserve one-shot`
- "취소표 나오면 잡아줘 / 될 때까지 돌려줘" → Continuous Monitoring flow above
- "내 예약 확인해줘" → `reserve list`
- "취소해줘" → `list` then `cancel`

## Payment Note
Reservations must be paid via SRT app or <https://etk.srail.kr> within ~20 minutes of reservation.

