#!/bin/sh
# Claude Code Skill 설치 스크립트
# 생성: 2026-08-13 · Skill 1개 · 파일 1개
#
#   ./install-skills.sh            ~/.claude/skills/ 에 설치 (모든 프로젝트에서 사용)
#   ./install-skills.sh ./myrepo   ./myrepo/.claude/skills/ 에 설치 (그 저장소 전용)
set -e

DEST="${1:+$1/.claude/skills}"
DEST="${DEST:-$HOME/.claude/skills}"
mkdir -p "$DEST"
echo "설치 위치: $DEST"


mkdir -p "$DEST/paper-pdf-chrome-rules"
cat > "$DEST/paper-pdf-chrome-rules/SKILL.md" <<'SKILL_PAYLOAD_EOF'
---
name: paper-pdf-chrome-rules
description: undetected-chromedriver + publisher 규칙 DB 기반 논문 PDF 다운로더 v2. API로 못 받는 paywall/Cloudflare 저널을 위한 자체 학습형 파이프라인. 5가지 URL 변형 패턴 (transforms/sequence/sequences/dynamic placeholder), cookie banner 자동 동의, Cloudflare human-like 통과, button data-config 추출, PDF 내용 검증, 좀비 Chrome 자동 정리, 사람 수정 보호.
---

# paper-pdf-chrome-rules — 규칙 학습형 PDF 다운로더

기존 `paper-pdf-downloader` 가 Unpaywall/Crossref API 체인만 다루는 반면, 이 스킬은 **undetected-chromedriver + 저널별 규칙 DB** 로 paywall·Cloudflare 저널까지 처리한다. 성공한 규칙이 DB 에 누적되어 운영할수록 더 똑똑해진다.

## 언제 사용하나

- Unpaywall/Crossref API 로는 못 받는 paywall 저널 (Wiley, Elsevier, AJR, Radiology, AAN …)
- Cloudflare 방어가 있는 OA 저널 (MDPI, Frontiers 등 일부)
- 기관 EZ-Proxy 세션을 활용해야 하는 학회지

## 5가지 URL 변형 패턴 (v2)

| Pattern | 적용 | 예시 |
|---------|------|------|
| `transforms` (독립 시도) | 단순 `{doi}` 치환 | `["https://onlinelibrary.wiley.com/doi/pdfdirect/{doi}"]` |
| `sequence` (방문 순서) | 첫 URL 방문 → 두 번째 URL nav (referer/세션) | `["/doi/epdf/{doi}", "/doi/pdf/{doi}?download=true"]` |
| `sequences` (multi) | 여러 sequence 동시 시도 | `[[seq1...], [seq2...]]` |
| `{current_url_html_to_pdf}` | 페이지 URL 의 `/html` → `/pdf` | AME 일부 |
| `{current_url_last_to_pdf}` | 마지막 segment → `/pdf` | AME (`/article/view/14623/12290` → `/article/view/14623/pdf`) |

## 6가지 method (priority 순)

| method | 적용 | 파라미터 |
|--------|------|----------|
| `unpaywall` | OA 저널 자동 | (없음) |
| `url_transform` | publisher 별 URL 패턴 (transforms / sequence / sequences) | 위 5가지 패턴 |
| `citation_pdf_url` | Google Scholar 표준 meta 태그 (범용) | (없음) |
| `selector` | CSS 셀렉터 + `<button data-config>` URL 추출 | `{"selectors": ["button.js-ejp-login-btn"]}` |
| `ez_proxy` | 기관 세션 경유 | `{"proxyUrl": "..."}` |
| `browser_capture` | 마지막 수단 (렌더 PDF) | — |

## v2 새 기능

### 1) Cookie 자동 동의
OneTrust, Quantcast, 일반 "Accept all cookies" 버튼 자동 클릭. iframe 포함, JS click fallback.

### 2) Cloudflare 자동 통과 (human-like)
- "Just a moment..." challenge 페이지 자동 감지
- ActionChains 마우스 천천히 다단계 이동 + 스크롤 + 자연 대기
- 실패 시 logs/challenge-screenshots/ 에 스크린샷
- **35초 내 자동통과 실패 시 → `PdfHumanClickQueue` 자동 적재 후 즉시 다음 논문으로** (Conv_home 통합).
  사용자가 한가할 때 `scripts/human-click-session.py` 로 일괄 처리 (사람 클릭 1번 → publisher 별 cf_clearance 쿠키 영구 발급 → 같은 publisher 후속 논문 자동 통과). 상세는 Conv_home `docs/pdf-human-click-queue.md`.

### 3) Button data-config 추출 (LWW)
`<button data-config='{"eventDetail":{"url":"..."}}'>` 형태에서 PDF URL 자동 추출.

### 4) PDF 내용 검증 (가짜양성 방지)
다운로드 후 제목 키워드 매칭. 매치 < 40% 이면 자동 롤백 + 파일 삭제.
- 단 'Letter to the editor', 'Correspondence', 'Corrigendum' 등은 통과

### 5) 좀비 Chrome 자동 정리
실행 시 `pkill undetected_chromedriver` + Singleton 파일 삭제.

### 6) 사람 수정 저자정보 보호
extract-authors 스크립트가 다음 중 하나라도 해당하면 무조건 skip:
- ExtractedAuthorInfo.source = 'manual'
- Activity 'paper.authorEdited' 로그 존재

## 검증된 Publisher 규칙 (2026-04-15 시드 기준)

```yaml
# URL 변형이 통하는 저널들
wiley:
  doiPrefix: 10.1002
  method: url_transform
  params: {"transforms": ["https://onlinelibrary.wiley.com/doi/pdfdirect/{doi}"]}

ajr_arrs:
  doiPrefix: 10.2214
  method: url_transform
  params: {"transforms": ["https://www.ajronline.org/doi/pdf/{doi}"]}

mdpi:
  doiPrefix: 10.3390
  method: url_transform
  params: {"transforms": ["{current_url}/pdf"]}   # article_url → article_url/pdf

liebert_sage:
  doiPrefix: 10.1089
  method: url_transform
  # 2023년 Liebert → SAGE 인수로 URL 변경됨
  params: {"transforms": ["https://journals.sagepub.com/doi/pdf/{doi}"]}

# citation_pdf_url 이 통하는 OA 저널
plos: {doiPrefix: 10.1371, method: citation_pdf_url}
frontiers: {doiPrefix: 10.3389, method: citation_pdf_url}
```

## 사용법 (프로젝트 통합)

### 전제: 전용 Chrome 프로파일 셋업 (1회)
```bash
python3 scripts/chrome-driver-pdf.py --setup
# Chrome 창이 뜨면 기관 EZ-Proxy 로그인 → 창 닫지 말고 터미널 엔터
```

### 단일 DOI 다운로드 (DB 규칙 사용)
```bash
python3 scripts/chrome-driver-pdf.py \
    --doi 10.1148/radiol.2018181197 \
    --output /tmp/test.pdf \
    --db-path prisma/prod.db
```

### Publisher discovery (대표 1건 테스트)
```bash
# 미해결 publisher 목록
python3 scripts/discover-publisher.py --list

# 특정 prefix 테스트 (headful Chrome 창 뜸)
python3 scripts/discover-publisher.py --prefix 10.2214
```

### Publisher 일괄 재다운로드 (페이싱 + DOI dedupe 자동)
```bash
python3 scripts/batch-redownload-publisher.py --prefix 10.2214
# --dedupe-by-doi 기본 ON: 같은 DOI 는 1번만 다운, N 레코드에 공유
# 페이싱: 건 사이 15~35s 랜덤, 5건마다 60~120s 휴식
```

### 저자 재추출 (DOI 캐시)
```bash
DATABASE_URL="file:./prisma/prod.db" \
  npx tsx scripts/extract-authors-for-updated-papers.ts --skip-existing
```

## Publisher 규칙 추가하는 법

### 자동 (discovery 가 성공한 경우)
```bash
python3 scripts/discover-publisher.py --prefix 10.XXXX
# 성공 시 chrome-driver-pdf.py 가 successCount++ 해서 자동 학습
```

### 수동 (새 URL 패턴 발견 시)
1. 브라우저에서 `https://doi.org/{sample DOI}` 열어 PDF 링크 확인
2. `/admin/pdf-rules` 에서 "규칙 추가":
   - `publisherKey`: 식별 키 (예: `new_journal`)
   - `doiPrefix`: 해당 저널 DOI prefix
   - `domainPattern`: URL substring (선택)
   - `method`: `url_transform`
   - `params`: `{"transforms": ["발견한 패턴"]}`
   - `priority`: 1 (최우선)
   - `confirmedByHuman`: ✅ (자동 재조정에서 제외)
3. 다시 `discover-publisher.py --prefix` 로 재검증
4. 성공하면 `batch-redownload-publisher.py --prefix` 로 일괄 처리

## 페이싱 (차단 방지 — 필수)

| 구분 | 기본값 |
|------|--------|
| 건 사이 | 15~35초 랜덤 |
| 5건 연속 후 | 60~120초 휴식 |
| Publisher 전환 | 60초+ |
| 차단 지표(429/captcha) 감지 | 즉시 중단 |

**절대 `--no-pace` 쓰지 말 것**. publisher 에 차단되면 해당 IP/세션이 최소 수시간 블록됨.

## DOI 단위 dedupe (중요)

DB 에 같은 DOI 가 여러 Content 레코드(교수 n명 등록)로 존재하는 경우가 있다. `--dedupe-by-doi` (기본 ON) 이 작동하면:

- **다운로드**: 1번만 수행 → `pdfFile` 경로를 모든 sibling 레코드에 공유
- **저자 추출**: WOS/PubMed 쿼리도 1번만 → `ExtractedAuthorInfo` 만 각자 저장
- **결과**: publisher 트래픽 ↓, 저장 공간 ↓, 차단 위험 ↓

## 실패 패턴과 대응

| 실패 원인 | 해결 |
|----------|------|
| headless 에서 Access Denied | headful 로 실행 (기본값) |
| 403 / Cloudflare | 기관 EZ-Proxy 세션 필요, `--setup` 다시 |
| 가짜 양성 (엉뚱한 PDF) | selector 규칙 비활성, url_transform 으로 교체 |
| Publisher URL 변경 | 브라우저 직접 확인 → 어드민 UI 수정 |
| 신규 publisher | discovery CLI 실행 → 성공/수동 rule |

## 성공률 자동 관리

- 성공 시 `successCount++`, 실패 시 `failureCount++`
- 주기적으로 `/admin/pdf-rules` 의 "성공률 집계 + priority 재정렬" 클릭
- `confirmedByHuman=true` 규칙은 건드리지 않음 (수동 확정 보호)

## 제약 & 주의

- **macOS**: chrome-driver headful 은 GUI 세션 필요. PM2 서비스(프로덕션 서버) 에서 직접 실행 불가.
- **자동화 한계**: cron 으로 돌리려면 사용자가 로그인된 GUI 세션 안에서 실행해야 함.
- **검증 권장**: 다운로드 성공 후 `pdftotext -l 1 file.pdf` 로 첫 페이지 텍스트 확인.
- **차단 지표**: 429/blocked/captcha 감지 시 즉시 `break`.

## 관련 스킬·문서

- `paper-pdf-downloader` (API-only 구 스킬, 폴백 1단계)
- `paper-author-extractor-wos` / `paper-author-extractor-pubmed` (저자 추출)
- 프로젝트 내: `docs/pdf-download-pipeline.md`, `docs/pdf-download-learning-workflow.md`

## 설계 철학

1. **저널 = 방법 1:1** — 같은 저널 내 여러 논문마다 발견할 필요 없음
2. **누적 학습** — 한 번 성공한 규칙이 DB 에 쌓여 다음에 자동 재사용
3. **DOI 단위 dedupe** — publisher 에 부담 최소, 저장·시간 절약
4. **페이싱 필수** — 속도보다 안정성. 차단되면 복구 어려움
5. **수동·자동 혼합** — 인간 확정(`confirmedByHuman`) 과 자동 재조정이 공존
SKILL_PAYLOAD_EOF

echo ""
echo "완료 — Skill 1개를 설치했습니다."
echo "Claude Code를 다시 시작하면 인식됩니다."
