당신이 할 일은 딱 3가지.
나머지는 Claude가 Notion과 카카오톡까지 직접 연결해 자동화를 완성합니다.
시작 전에 사용자에게 다음을 확인합니다:
Claude가 추출 스크립트를 생성하고, 사용자는 딱 2줄만 터미널에서 실행합니다.
scripts/extract_dialogues.py로 저장합니다. PDF 경로만 실제 파일명으로 바꾸면 됩니다.
import pdfplumber, re, json
from pathlib import Path
from collections import Counter
ROOT = Path(__file__).resolve().parent.parent
PDF = ROOT / "your-movie-script.pdf" # ← 실제 PDF 파일명으로 변경
OUT = ROOT / "data" / "script_clean.json"
OUT.parent.mkdir(exist_ok=True)
PAGE_HDR = re.compile(r"[A-Z][a-z].*,\s*\d+/\d+/\d+")
CONTINUED = re.compile(r"^\s*\d*\s*CONTINUED:?", re.I)
SCENE_RE = re.compile(r"^\d+\s+(INT\.|EXT\.|INT/EXT|FADE)", re.I)
PAREN_RE = re.compile(r"\([^)]*\)")
CONTD_RE = re.compile(r"\(CONT'?D\)|\(O\.?S\.?\)|\(V\.?O\.?\)|\(MORE\)", re.I)
CHAR_MIN, DLG_MIN, DLG_MAX = 30, 20, 35
ABBREVS = ["Mr.","Mrs.","Ms.","Dr.","Sr.","Jr.","St.","Mt.","Co.","Inc.",
"Ltd.","Prof.","Gen.","Lt.","Sgt.","U.S.","etc.","vs.","e.g.","i.e."]
def split_sents(text):
p = text
for a in ABBREVS: p = p.replace(a, a.replace(".",""))
p = p.replace("--"," ").replace("...","")
parts = re.split(r"(?<=[.!?])\s+(?=[A-Z\"'])", p)
out=[]
for s in parts:
s=s.strip().replace("","...").replace("",".")
s=re.sub(r"\s+"," ",s)
if s and len(s)>=2: out.append(s)
return out
def is_char(c):
b=CONTD_RE.sub("",c); b=PAREN_RE.sub("",b).strip()
if not b or len(b)>35 or not any(x.isalpha() for x in b): return False
return not any(x.islower() for x in b) and not b.endswith(":")
raw=[]; cur_char=cur_scene=None; buf=[]
def flush():
global buf
if cur_char and buf:
blk=re.sub(r"\s+"," "," ".join(buf).strip())
if blk: raw.append((cur_char,cur_scene,blk))
buf=[]
with pdfplumber.open(PDF) as pdf:
for pg in pdf.pages:
for line in (pg.extract_text(layout=True) or "").split("\n"):
rs=line.rstrip(); c=rs.lstrip()
if not c: flush(); cur_char=None; continue
if PAGE_HDR.search(c) or CONTINUED.match(c): flush(); cur_char=None; continue
ind=len(rs)-len(c)
if SCENE_RE.match(c):
flush(); cur_char=None
cur_scene=re.sub(r"\s{2,}"," ",c); cur_scene=re.sub(r"\s+\d+\s*$","",cur_scene)
continue
if ind>=CHAR_MIN and is_char(c):
flush(); n=CONTD_RE.sub("",c); cur_char=PAREN_RE.sub("",n).strip(); continue
if cur_char and DLG_MIN<=ind<=DLG_MAX+5:
cl=PAREN_RE.sub("",c).strip()
if cl: buf.append(cl)
continue
flush(); cur_char=None
flush()
sents=[]; sid=0
for ch,sc,blk in raw:
for s in split_sents(blk):
sid+=1
sents.append({"id":sid,"char":ch,"scene":sc or "","text":s,"word_count":len(s.split())})
OUT.write_text(json.dumps(sents,ensure_ascii=False,indent=2),encoding="utf-8")
chars=Counter(s["char"] for s in sents)
print(f"✅ {len(sents)}개 문장 추출 완료 ({len(chars)}명 캐릭터)")
print(f" 약 {len(sents)//5}일치 학습 분량 (하루 5문장)")
print(f" 상위 캐릭터: {', '.join(f'{c}({n})' for c,n in chars.most_common(5))}")
print(f" 저장 위치: {OUT}")
사용자에게 묻지 않고 Claude가 Notion MCP를 사용해 직접 DB를 생성합니다.
업로드받은 script_clean.json을 파싱해 Notion에 30개 큐 페이지를 일괄 생성합니다.
Claude.ai 클라우드 Routine을 생성해 매일 08:00 KST에 자동 실행되도록 설정합니다.
"큐가 비었습니다" 카카오톡 알림을 받으면, Claude.ai 채팅창에 아래 메시지를 보내세요.
| 배치 | Day 범위 | 보충 시점 |
|---|---|---|
| 1차 (최초 셋업) | Day 1 ~ 30 | 지금 |
| 2차 | Day 31 ~ 60 | 30일 후 |
| 3차 | Day 61 ~ 90 | 60일 후 |
| 4차 ~ 9차 | Day 91 ~ 270 | 90 ~ 270일 후 |
| 마지막 | Day 271 ~ 끝 | 270일 후 |
| 증상 | Claude에게 이렇게 말하세요 |
|---|---|
| 카카오톡이 안 와요 | "Routine [ID] 실행은 됐는데 카카오톡이 안 왔어. PlayMCP 연결 확인하고 재실행해줘" |
| Notion이 업데이트가 안 됨 | "Routine 실행 후 Notion 페이지가 안 바뀌었어. Notion MCP 상태 확인해줘" |
| 큐가 비었다는 알림 | 위 "큐 보충 방법" 메시지를 Claude에게 보내세요 |
| PDF 추출 결과가 이상함 | "추출된 JSON 파일 앞부분을 보니 char가 이상해. 스크립트 CHAR_INDENT_MIN 값 조정 방법 알려줘" |
| Routine 시간을 바꾸고 싶음 | "Routine [ID] 스케줄을 07:00 KST로 변경해줘" |
| 카드 내용 형식을 바꾸고 싶음 | "Routine [ID] 프롬프트에서 어휘 설명을 3개로 늘리고 예문도 추가해줘" |