AI Blog Series — 별님 자동화 프로그램 4종 통합 인덱스
https://youtu.be/4InZbKXjHlY?si=4z0clEMMXVHAWiU0
4개 플랫폼 (네이버 / 티스토리 / 구글블록 / 워드프레스) 동일 알고리즘 운영.
공통 정책 + 플랫폼별 특화 분리 구조.
최종 업데이트: 2026-05-07 22:06 (v7.1 — 보답 대상 4가지 모드 + 로테이션 자동 + 즉시 발행 + TstoryBlog 표준 폰트)

🚨 사용자 PC 필수 설정 (별님 절대 지침 v7.1 — 2026-05-07)
디스플레이 배율 = 100% 절대 유지
Windows 설정 → 시스템 → 디스플레이 → “텍스트, 앱 및 기타 항목의 크기” = 100%
| 배율 | 결과 |
|---|---|
| 100% (필수) | ✅ 모든 자동화 정상 — 발행/공감/댓글/서이추 100% 성공 |
| 125%/150% | ⚠ 코드 자동 보정 시도 — 일부 환경에서 실패 가능 |
| 175%+ | ❌ 권장하지 않음 |
별님 발견: “발행 클릭이 안 된 이유 알겠어요. 해상도 100% 가 아니면 못 찾는 것 같습니다.”
코드 측 안전망 (SetProcessDpiAwareness + devicePixelRatio 보정) 적용했으나 100% 가 가장 안전.
🌟 시리즈 구성
| 프로젝트 | 플랫폼 | 폴더 위치 | 상태 |
|---|---|---|---|
| AutoBlog | 네이버 블로그 | C:\AutoBlog\AutoBlog\ | ✅ 1차 정리 완료 (2026-05-07) |
| TstoryBlog | 티스토리 (카카오) | C:\TstoryBlog\TstoryBlog\ | ✅ 운영 중 |
| 구글블록 | Google Blogger | TBD | ⏳ 준비 중 |
| 워드프레스 | WordPress | TBD | ⏳ 준비 중 |

📚 절대지침 구조 (B안 — 공통 + 특화 분리)
1. 시리즈 공통 (이 폴더)
C:\AI_Blog_Series\├── README.md 👈 이 파일├── 절대지침_공통.md ⭐ 시리즈 마스터 정책└── 처리과정/ ├── 01_황금키워드.md ├── 02_통합보답.md ├── 03_공감댓글.md ├── 04_본인답글.md ├── 05_구독_서로이웃.md ├── 06_글쓰기_이미지.md ├── 07_자격증명_로그인.md └── 08_24시간자가점검.md
2. 각 프로젝트 (플랫폼 특화)
{ProjectRoot}/├── README.md 👈 프로젝트 인덱스├── 절대지침.md 👈 마스터 + 특화 안내├── 절대지침_특화.md 👈 플랫폼 특화 (셀렉터/URL/특이사항)└── PROGRESS.md 👈 진행 기록
⚙ 작업 시작 순서 (달님 — AI 어시스턴트)
- 공통 정책 읽기:
절대지침_공통.md - 공통 처리 과정 파악: 작업할 모듈에 해당하는
처리과정/{번호}_*.md - 플랫폼 특화 읽기: 해당 프로젝트의
절대지침_특화.md - 작업 진행
✏ 정책 변경 순서 (별님 + 달님)
| 변경 종류 | 수정할 파일 |
|---|---|
| 4개 시리즈 모두 영향 | C:\AI_Blog_Series\절대지침_공통.md |
| 공통 알고리즘 변경 | C:\AI_Blog_Series\처리과정\{번호}_*.md |
| 특정 플랫폼만 영향 | 해당 프로젝트의 절대지침_특화.md |
| 인덱스/네비게이션 | 각 폴더의 README.md / 절대지침.md |
🌟 별님 핵심 원칙 — “4개 시리즈는 한 가족”
| 항목 | 통일 |
|---|---|
| 알고리즘 | 시리즈 공통 (공통 마스터 + 처리과정 명시) |
| 디자인 | AutoBlog GUI 마스터 기준 |
| 정책 | 5대 감정 어조, 일일 한도, 보답 우선 등 모두 공통 |
| 사용자 경험 | 동일 (탭 순서, 시계, 보드, 색상) |
플랫폼별 차이는 셀렉터/URL 만 다름 — 각 프로젝트 절대지침_특화.md 에 명시.
📁 시리즈 공통 코드 (모든 프로젝트 동일)
다음 파일들은 4개 시리즈 모두 같은 코드 (driver 인스턴스만 외부에서 받음):
core/├── health_report.py 🌙 자정 자가 점검 4 Phase├── midnight_daemon.py 🌙 자정 트리거 daemon├── gemini_client.py 🤖 Gemini API 래퍼└── humanize.py ⏱ 사람처럼 행동 (대기 + 타이핑)modules/├── reply/ai_reply.py 💬 5대 감정 어조 답글├── comment/ai_comment.py 💬 5대 감정 어조 댓글└── comment/visitor_collector.py 👥 본인 댓글 작성자 수집 (플랫폼별 셀렉터만 다름)
🚀 새 시리즈 추가 가이드

구글블록 또는 워드프레스 추가 시:
1단계: 폴더 구조 생성
{새플랫폼}/{새플랫폼}/├── README.md├── 절대지침.md (마스터 + 특화 안내)├── 절대지침_특화.md (플랫폼별 셀렉터/URL)├── core/└── modules/
2단계: 시리즈 공통 코드 복사
C:\AI_Blog_Series\ 의 처리과정/ 참조 → 각 모듈 구현core/health_report.py + midnight_daemon.py + gemini_client.py 복사modules/reply/ai_reply.py + comment/ai_comment.py 복사
3단계: 플랫폼 특화 작성
절대지침_특화.md 에:- 로그인 흐름 (OAuth/SSO)- 댓글 영역 셀렉터- 좋아요/구독 버튼 셀렉터- 글쓰기 에디터 셀렉터- 검색 URL/API
4단계: 빌드 + 검증
pyinstaller --clean --noconfirm 빌드[🔑 로그인 미리하기] → 자정 점검 daemon 시작 확인각 모듈 단독 테스트
📞 연락처 / 별님 정보
- 별님 = 사용자 (프로젝트 오너)
- 달님 = AI 어시스턴트 (Claude / GPT / Gemini 등)
모든 작업은 별님 절대지침 우선. 절대지침과 충돌 시 별님께 먼저 확인.
📈 시리즈 진행 현황 (2026-05-06 기준)
| 프로젝트 | 자정 자가 점검 | 5대 감정 어조 | 3단계 보답 | 황금 키워드 100 | 빌드 |
|---|---|---|---|---|---|
| TstoryBlog | ✅ | ✅ | ✅ | ✅ | ✅ 17:48 |
| AutoBlog | ✅ | ✅ | ✅ | ✅ | ✅ v7.1 16:20 |
| 구글블록 | ⏳ | ⏳ | ⏳ | ⏳ | ⏳ |
| 워드프레스 | ⏳ | ⏳ | ⏳ | ⏳ | ⏳ |
v7.1 추가 기능 (2026-05-06)
| 기능 | 위치 | 비고 |
|---|---|---|
| 이웃 목록 일괄 수집 (PC+모바일 / PC / 모바일 3 모드) | gui/buddy_tab.py | DB 일괄 저장 |
| 서로이웃 신청 받음 일괄 수락 | gui/buddy_tab.py | BuddyInviteReceivedManage.naver |
| PC admin iframe(papermain) 자동 switch | modules/buddy/buddy_list_collector.py | 셀렉터 동작 보장 |
별님 ↔ 달님 = 좋은 동반자 🌙✨
절대지침 공통 — 4개 시리즈 통합 마스터 (별님 v7.1 — 2026-05-06)
별님 ↔ 달님 동반 정책. 4개 프로그램 (네이버 / 티스토리 / 구글블록 / 워드프레스) 동일 알고리즘 운영.
각 프로젝트
절대지침.md(개별) 는 이 마스터를 참조 + 플랫폼 특화만 추가.정책 변경 시 반드시 이 파일을 먼저 수정 → 모든 시리즈로 동기화.
최종 업데이트: 2026-05-06 (v7.1)
📜 버전 히스토리
| 버전 | 일자 | 핵심 변경 |
|---|---|---|
| v6 | 2026-05-05 | 일일 한도 100 자동 추적 |
| v7 | 2026-05-06 | 24/7 시간 설계 + 수동값 우선 + visitor DB |
| v7.1 (5/6 오전) | 2026-05-06 | 서로이웃 신청 일괄 수락 + 3 모드 수집 + iframe 자동 처리 |
| v7.1 (5/6 야간) | 2026-05-06 23:00 | 4그래프 동기화·프리셋 충돌 해소·30자 제목·발행 다이얼로그 fix |
| v7.1 (5/7 오전) | 2026-05-07 08:00 | SEO 18~30자 랜덤 + 원본 제목 보존 + 엑셀 행 마지막 이동 + 동의어 dedup 제거 |
| v7.1 (5/7 1차정리) | 2026-05-07 08:30 | popup_watcher pause + Korean 동적 + 발행 버튼 정확 매칭 — AutoBlog 1차 마무리 |
| v7.2 (5/7 시리즈통일) | 2026-05-07 21:00 | TistoryBlog 가 AutoBlog 와 1:1 통일 — 황금 키워드 색상/파일명/폴더 + comment 5중 검사 + 시드 단독 그래프 |
🌐 4시리즈 1:1 통일 절대지침 (별님 v7.2 — 2026-05-07)
AutoBlog (마스터) ↔ TistoryBlog ↔ 구글블록 ↔ 워드프레스
알고리즘·디자인·파일·색상·로직 모두 동일. 차이는 오직 플랫폼별 셀렉터/URL 만.
🎨 황금 키워드 5색 티어 (모든 시리즈 동일 — 별님 절대지침)
| 티어 | 검색량 | 경쟁률 | 배경 hex | 글자 hex | 라벨 |
|---|---|---|---|---|---|
| 🟡 황금 | ≥ 500 | ≤ 1.0 | #FFFF00 | #000000 | 노랑 |
| 🟢 우수 | ≥ 400 | ≤ 2.0 | #00B050 | #FFFFFF | 녹색 |
| 🔵 양호 | ≥ 300 | ≤ 5.0 | #0070C0 | #FFFFFF | 파랑 |
| 🔴 도전 | ≥ 200 | ≤ 10.0 | #FF0000 | #FFFFFF | 빨강 |
| 🟤 위험 | ≥ 100 | ≤ 100.0 | #5C0000 | #FFFFFF | 검은빨강 |
| 🟣 관심 | (보조) | ≤ 200 | (별님 v5 정책) | (보라) |
적용 위치 (모든 시리즈):
- 엑셀 PatternFill (
fill_hex— # 없음) - GUI Treeview 행 배경 (
badge_hex— # 포함) - GUI 하단 티어 칩 5개
- 헤더 배경:
#FFFF00(황금 티어와 동일 — 사용자 요청)
📁 파일 정리 절대지침 (별님 v7.2 — 2026-05-09)
핵심 원칙: 한 폴더당 1000개 넘지 않기 (Windows 탐색 속도 저하 방지)
모든 시계열 파일은 년-월 폴더(YYYY-MM) 로 자동 분리.
폴더 구조 (모든 시리즈 동일):
data/├── posts/ ← 글쓰기 마크다운│ ├── 2026-05/ ← 월별 폴더 (자동 생성)│ │ ├── 20260509_161432_챗GPT.md│ │ └── ...│ └── 2026-04/├── images/ ← AI 생성 이미지│ ├── 2026-05/│ │ └── 20260509_161432_챗GPT/ ← 글당 1폴더 (그대로)│ │ ├── 01_장점.png│ │ └── ...│ └── 2026-04/├── excel/goldkeyword/│ ├── today_gold_keyword.xlsx ← 단일 파일 (그대로)│ └── archive/│ └── 2026-05/ ← 월별 백업 폴더│ └── archived_20260501.xlsx└── health_reports/ └── {YYYY-MM-DD}.txt ← 이미 날짜 분리
자동화 규칙:
drafts.new_session()→posts/{YYYY-MM}/자동 생성image_gen.generate_image()→images/{YYYY-MM}/{session}/자동 생성- 1000개 임계값 도달 시 로그 경고 출력
- 기존 파일 마이그레이션 스크립트 1회 실행 (별님 v7.2 — 2026-05-09)
적용 대상 (모든 시리즈):
- AutoBlog:
dist/data/posts/,dist/data/images/ - TstoryBlog:
dist/data/posts/,dist/data/images/ - 구글블록 / 워드프레스: 추후 적용
📂 황금 키워드 파일 경로 (모든 시리즈 동일)
data/└── excel/ └── goldkeyword/ ← 단일 폴더 (시리즈 통일) ├── today_gold_keyword.xlsx ← 자동 누적 (writer 매일 자동 로드) ├── {base_keyword}_{ts}.xlsx ← 수동 검색 결과 └── archive/ ← 자동 백업
필수 함수 (core/paths.py):
excel_dir()—data/excel/goldkeyword_excel_dir()—data/excel/goldkeyword/goldkeyword_archive_dir()—data/excel/goldkeyword/archive/TODAY_GOLD_FILENAME = "today_gold_keyword.xlsx"
📊 엑셀 컬럼 표준 (8 또는 10 컬럼)
AutoBlog 표준 (8컬럼): 키워드 | PC월수 | MO월수 | 총월수 | 문서수 | 경쟁률 | 발행일 | 상태
TistoryBlog v5 확장 (10컬럼): 제목 | 키워드 | PC | MO | 총 | 문서수 | 경쟁률 | 패턴 | 발행일 | 상태
엑셀 헤더 자동 감지 (3가지 layout 호환):
- 신형 v5 final: 제목/키워드/…
- 구형 v5 initial: 키워드/제목/…
- legacy 8컬럼: 키워드/PC/…
💾 엑셀 저장 정책 (별님 v7.2 — 모든 시리즈 동일)
별님 핵심 명시: “AutoBlog 알고리즘 그대로, 변수까지 같이 사용”
1) 경쟁률 필터 — 0 < 경쟁률 < 100 만 저장
if r.is_published: filtered.append(r) # 발행완료는 그대로 보존 (마지막 정렬용)elif 0 < r.competition < 100: filtered.append(r) # 정상 키워드# else: 0 또는 100 이상 → 폐기 (경쟁률 = 0 = 데이터 부재, 100 이상 = 의미 없음)
| 케이스 | 처리 |
|---|---|
| 경쟁률 = 0 (네이버 데이터 부재) | ❌ 폐기 |
| 0 < 경쟁률 < 100 | ✅ 저장 |
| 경쟁률 ≥ 100 | ❌ 폐기 (의미 없는 키워드) |
| 경쟁률 = ∞ (검색량 0) | ❌ 폐기 |
| 이미 발행 완료된 행 | ✅ 무조건 보존 (정렬상 마지막) |
2) 중복 키워드 dedup — 정확 일치 (대소문자/양끝공백만 정규화)
def _canon_kw(s): """별님 v7.1: 정확 일치 dedup용 정규화 — 대소문자/양끝공백만.""" return str(s or "").strip().lower()
별님 절대 명시: “챗GPT / ChatGPT / chat gpt / 챗지피티” 는 모두 별개 키워드. 동의어 매핑 X (각각 SEO 가치).
3) A2 부터 데이터 — 헤더 표준화
| 셀 | 내용 |
|---|---|
| A1 | 헤더 (배경 #FFFF00 노랑 + 굵게 + 가운데 정렬) |
| A2 ~ | 데이터 (티어 색상 자동 적용) |
freeze_panes | "A2" (A1 헤더 고정) |
4) 글쓰기 시 사용 — A2 첫 행을 제목으로
# writer/runner.py 흐름:pending = excel_store.get_pending_keywords(blog_id, limit=N)# → 발행일이 빈 행만 (미발행 큐) 반환for row in pending: title = row.title or row.keyword # 매력 제목 우선, 없으면 키워드 # ... AI 본문 생성 + 이미지 + 발행 excel_store.mark_published(blog_id, row.keyword) # → 발행일 = 오늘, 상태 = "발행완료" 기록 # → sort_rows() 가 자동으로 마지막으로 이동
5) 발행 완료 → 행 마지막 이동 (자동)
def _sort_key(row): if not row.is_published: return (0, -row.total_volume, row.keyword) # 미발행: 검색량 큰 순서로 위 return (1, -_to_ord(row.published_at), row.keyword) # 발행완료: 최신 발행순으로 아래
→ 매번 sort_rows() 호출 시 자동으로 발행 완료된 행은 마지막으로.
6) 30일 만료 자동 부활
PUBLISH_EXPIRE_DAYS = 30# 발행일이 30일 지난 행 → 발행일 비움 → 다음 큐로 부활if pub_dt < (now - timedelta(days=30)): rec["발행일"] = None rec["상태"] = "부활"
7) Atomic Save — 사용자가 엑셀 열어둔 상태에서도 안전
1) 임시 파일 (.tmp) 에 먼저 저장2) tmp → 본 파일 rename (atomic)3) 잠겨있으면 2초씩 3회 재시도4) 끝까지 실패 시 .bak.xlsx 로 백업 (다음 저장 시 합쳐서 dedup)
핵심 변수 (시리즈 공통 — 변경 X)
| 변수 | 값 | 위치 |
|---|---|---|
TODAY_GOLD_FILENAME | "today_gold_keyword.xlsx" | excel_writer.py |
DAILY_MAX_KEYWORDS | 50 | excel_writer.py |
PUBLISH_EXPIRE_DAYS | 30 | excel_writer.py |
GOLD_MIN_SEARCH | 500 | excel_writer.py |
GOLD_MAX_COMPETITION | 1.0 | excel_writer.py |
GOLD_MIN_DOC | 1 | excel_writer.py |
HEADER_BG | "FFFF00" | excel_writer.py |
CUMULATIVE_CAP | 2000 | excel_store.py |
EXPIRE_DAYS | 30 | excel_store.py |
💬 댓글 / 답글 정책 (모든 시리즈 동일 — 별님 v7.2)
comment 모듈 — has_my_comment_already() 검사:
- 글 진입 → 댓글 영역에서 본인 blog_id 의 댓글 검사
- 있으면
already_my_comment반환 → runner 가skipped처리 history.mark_processed로 오늘 다시 X- 이유: “답글 없는 내 댓글 있는 글 = 모르는 분 찾기”
플랫폼별 셀렉터 (시리즈 특화 파일에 별도 명시):
- 네이버:
a.tit_name[href]/span.nick - 티스토리:
a.tt-link-user[href]/.tt-box-meta a[href]
🤝 서로이웃 / 구독 — 5중 사전 DB 검사 (모든 시리즈 동일)
페이지 진입 전 visitor_db 5중 검사 (시간 절약 + 정확도):
| # | 검사 함수 | 의미 | 반환 코드 |
|---|---|---|---|
| 1 | is_already_buddy(blog_id) | 이미 서로이웃/구독 | DB_ALREADY_BUDDY |
| 2 | is_pending_request_sent(blog_id) | 신청 진행 중 | DB_PENDING_SENT |
| 3 | is_decline_blocked(blog_id) | 영구 차단 (decline) | DB_DECLINE_BLOCKED |
| 4 | is_pending_buddy(blog_id) | 큐 대기 중 (친해진 후) | DB_PENDING_BUDDY |
| 5 | 페이지 셀렉터 (ALREADY_*) | 페이지에서 직접 확인 | ALREADY_SUBSCRIBED 등 |
성공 시 자동 기록: visitor_db.upsert_visitor(blog_id, is_my_buddy=1, last_action="...") → 다음 채굴 시 자동 skip
플랫폼별 차이:
- 네이버: 양방향 (서로이웃) — pending → accept/decline 흐름
- 티스토리: 일방향 (구독) — pending 단계 없음, 클릭 즉시 구독
📈 자동 황금 키워드 채굴 — 시드 단독 그래프 (별님 v7.2 — 2026-05-07)
시드 진행 그래프: 메인 채굴 그래프 옆/아래에 단독 으로 표시
🌐 시드 진행(얇은 막대 + 라운드 캡 + 우측 % 사용됨)- 우측에
🪙 현재 시드 [N/M]: {시드명}라벨 - 실시간 갱신 (정규식
[N/M] 시드 'XXX'매칭)
자동 박스 (3그래프 묶음) 폐지 — 별님 v7.2 단순화
🔄 시리즈 동기화 시 절대 변경 금지
| 항목 | 모든 시리즈 동일 (변경 X) |
|---|---|
| 5색 티어 색상 (5종 hex) | ✓ 절대 변경 X |
황금 키워드 파일명 (today_gold_keyword.xlsx) | ✓ 절대 변경 X |
엑셀 폴더 (data/excel/goldkeyword/) | ✓ 절대 변경 X |
visitor_db 인터페이스 (is_already_buddy 등) | ✓ 절대 변경 X |
| 사람 패턴 대기 시간 (humanize.py) | ✓ 절대 변경 X |
새 시리즈 (구글블록/워드프레스) 추가 시:
- AutoBlog 폴더 통째 복사 → 폴더명 변경
- 플랫폼 특화 파일 (
core/{platform}_login.py,core/{platform}_api.py,tistory_editor같은 에디터) 만 작성 - 위 절대 변경 금지 항목 그대로 유지
💝 본인 블로그 공감자 목록 — 별님 라이브 셀렉터 (별님 v7.1 — 2026-05-06)
별님 라이브 검증: 본인 블로그 글의 공감자 목록 펼치기 후 추출.
펼치기 버튼
| 별님 # 주석 | 셀렉터 |
|---|---|
| 공감자 목록 펼치기 | a._symList / a.btn_arr._symList / a[id^='Sympathy'] |
| blind 텍스트 | “이 글에 공감한 블로거 열고 닫기” |
공감자 목록 항목 (ul.list_sympathy > li.item)
| 추출 값 | 셀렉터 | 비고 |
|---|---|---|
| 컨테이너 | ul.list_sympathy > li.item | 한 명 = 1 li |
| 블로그 link | span.area_profile a.link.pcol2 | href = https://blog.naver.com/{blog_id} |
| 블로그 제목 (title) | 위 link 의 title 속성 | 예: “오니기리와 뭉치의 50억 프로젝트” |
| 닉네임 | strong.nick span.ell2 a | 예: “오니기리와 뭉치” |
| 블로그 설명 | span.desc | 한 줄 소개 |
| 프로필 이미지 | span.profile img | src = 썸네일 URL |
| ⭐ 서로이웃 신청 버튼 | a.btn_buddy._addBuddyPop | class 에 _param({blog_id}) 포함 |
blog_id 추출 — _param() 패턴이 가장 정확
import readdbtn = item.find_element(By.CSS_SELECTOR, "a.btn_buddy._addBuddyPop")cls = addbtn.get_attribute("class") or ""m = re.search(r"_param\(([\w_-]+)\)", cls)if m: blog_id = m.group(1) # 정확히 추출
공감 카운트 / 댓글 카운트 / 새 댓글 표시
| 정보 | 셀렉터 |
|---|---|
| 공감 컨테이너 | div#area_sympathy{post_id}.area_sympathy |
| 공감 카운트 | span.u_likeit_text._count.num |
| 댓글 영역 | div.area_comment |
| 댓글 토글 | a#Comi{post_id}.btn_comment |
| 댓글 카운트 | em#commentCount._commentCount |
| 새 댓글 표시 | i.ico_new span.icon_new (“새 댓글”) |
→ 별님 답글 처리 시 “새 댓글” 표시 있는 글 우선 (정책 6 보강 가능)
🛡 서로이웃 일일 한도 — 3중 안전 메커니즘 (별님 v7.1 — 2026-05-06)
별님 결정: 시도 기반 카운트 + 90건 사전 중단 + 네이버 메시지 즉시 중단
네이버 정책: 1일 신청 시도 100건 (성공/실패 무관, skipped 제외)
3중 안전 메커니즘
| # | 우선순위 | 동작 | 효과 |
|---|---|---|---|
| 1 | 1차 (사전) | 시도 90건 도달 시 사전 중단 (마진 10건) | 봇 의심 회피 + 안전 |
| 2 | 2차 (정확) | 시도 기반 카운트 (성공+실패 모두 차감) | 네이버와 일치 |
| 3 | 3차 (강제) | “더 이상 이웃을 추가할 수 없습니다” 감지 시 즉시 중단 | 안전장치 |
카운트 규칙
| 결과 | 시도 카운트 | daily_quota 차감 |
|---|---|---|
| ✅ 신청 성공 (success) | +1 | +1 |
| ❌ 신청 실패 (failed) | +1 | +1 |
| ⚠ daily_limit 응답 | +1 | (즉시 중단) |
| ⏭ skipped (이미 이웃 등) | 0 | 0 |
| ⏰ wait_friendship | 0 | 0 |
코드 (runner.py)
# 1차 — 사전 중단ATTEMPTS_SAFE_MARGIN = 90attempts_today = success + failif attempts_today >= ATTEMPTS_SAFE_MARGIN: log("⚠ 안전 사전 중단 — 시도 90/100 도달") break# 신청 시도result = applier.request_buddy(...)# 3차 — 강제 중단if result == "daily_limit": log("🚨 일일 한도 도달") break# 2차 — 시도 기반 카운트if result == "success": daily_quota.consume(ACTIVITY_BUDDY, 1)elif result == "failed": daily_quota.consume(ACTIVITY_BUDDY, 1) # ⭐ 실패도 차감
🔄 서로이웃 통합 자동 동기화 — 빈도 분리 (별님 v7.1 — 2026-05-06 절대 명시)
별님 명시: “받은 신청 수락도 해주시고” — 받은 신청은 매번 즉시 처리
buddy 모듈 시작 시 자동 동기화. 빈도 분리로 효율 + 매너 동시 보장.
빈도 분리 정책
| 작업 | 빈도 | 이유 |
|---|---|---|
| 1. 받은 서로이웃 신청 일괄 수락 | 매번 실행 | 시간 짧음 (~5초) + 양방향 즉시 형성 + 매너 ↑ |
| 2. 이웃 목록 전체 수집 (PC+모바일) | 1일 1회 | 시간 ↑ (1~2분), 5000명 이웃 가정 + 캐시 활용 |
영속화
| flag | 위치 | 의미 |
|---|---|---|
data/buddy_collect_YYYYMMDD.flag | core.paths.get_data_dir() | 오늘 이웃 수집 완료 표시 |
| (받은 신청 수락은 flag 없음 — 매번 실행) | — | — |
흐름 (buddy/runner.py._maybe_sync_buddies_today)
[1단계 — 매번 실행] blc.accept_all_buddy_invites(driver) → BuddyInviteReceivedManage.naver → 전체선택 + 일괄 수락 → 자동 양방향 형성 → 로그: "받은 신청 N명 일괄 수락" ↓[2단계 — 1일 1회 (flag 캐시)] flag 있으면 skip flag 없으면: blc.collect_all_buddies_smart(driver) → 모바일 우선 → PC fallback → 모든 이웃 (both / i_added / they_added) 수집 blc.save_to_visitor_db(entries) → visitor_db 채움 flag 저장 (오늘 다시 안 함) ↓[이후 신청 흐름] 사전 DB 검증 5중 (이미 이웃, 진행 중, 거절, 대기, 안내 분류)
별님 의도 정합성
- ✅ “받은 신청 수락도 해주시고” — 매번 즉시 수락 → 양방향 매너
- ✅ 이웃 5000명 수집 — 1일 1회 충분 (어차피 큰 변화 없음)
- ✅ 사전 DB 검증 정확도 ↑ — 매일 한 번 동기화로 충분
- ✅ EXE 재시작 시 받은 신청 즉시 처리 (캐시 X)
📜 서로이웃 안내 메시지 분류 + 매너 처리 (별님 v7.1 — 2026-05-06)
별님 통찰: 상대방 신청 폼에 “조금 친해진 후에 부탁드려요” 같은 안내 메시지가 있음.
→ 무시하면 매너 ↓ + 봇 의심 ↑.해결: 라디오 클릭 → 폼 등장 → 안내 메시지 자동 추출 → 키워드 분류 → 분기 처리.
분류 5종 (키워드 매칭 — Gemini 호출 0회)
| 분류 | 키워드 예시 | 동작 |
|---|---|---|
| welcome | 환영 / 부담 없이 / 신청 받아요 / 기다리고 | 즉시 신청 |
| wait_friendship | 친해진 후 / 댓글 자주 / 방문 후 / 조금 후에 | 오늘 X, 내일 처리 큐 등록 |
| decline | 받지 않 / 거절 / 사절 / 비공개 | 영구 skip + DB 표시 |
| condition | 맞팔 / 동일 카테고리 / 비슷한 주제 | 현재 skip + DB 메모 |
| unknown | (위 미일치) | 기본 신청 (무난한 인사) |
흐름
[1] 라디오 클릭 (Lazy 패턴) ↓[2] policy_reader.fetch_announce_message() — 안내 메시지 추출 ↓[3] policy_reader.classify_policy() — 키워드 분류 (Gemini 0회) ↓[4] 분기: welcome / unknown → 신청 진행 wait_friendship → 내일 큐 등록 (visitor_db.pending_until) decline → 영구 skip (visitor_db.buddy_policy='decline') condition → skip + 메모 ↓[5] (welcome / unknown 만) AI 인사말 생성 — 안내 메시지 컨텍스트 반영 (있을 때만) ↓[6] 입력 → 등록
다음날 큐 우선 처리 (별님 v7.1)
buddy/runner.py 시작 시:
visitor_db.get_pending_buddies()— 어제 wait_friendship 등록된 큐- 큐를
blog_ids앞으로 우선 배치 - 자연스러운 친밀도 흐름 (어제는 안 했고 오늘 다시 시도)
영구 차단 사용자 사전 skip
visitor_db.is_decline_blocked(blog_id)— 페이지 진입 전 미리 체크- decline 정책 사용자는 페이지 진입조차 X (시간 + AI 비용 절감)
인사말 안내 컨텍스트 반영
# preview["announce_context"] 가 있으면 Gemini 프롬프트에 추가:prompt += ( "[중요 — 상대방 안내 메시지]\n" f"이 블로거는 신청 폼에 다음 안내를 적어두셨어요: \"{announce}\"\n" "이 안내를 자연스럽게 존중하는 톤으로 인사말 작성. " "매너 있게 / 부담 주지 않게 / 진심으로.\n")
코드 구조
| 파일 | 역할 |
|---|---|
modules/buddy/policy_reader.py | 안내 추출 + 분류 (사전 키워드) |
modules/buddy/applier.py | 라디오 클릭 후 정책 분기 |
modules/buddy/runner.py | 다음날 큐 우선 + decline 사전 skip |
modules/buddy/ai_greeting.py | announce_context 프롬프트 반영 |
core/visitor_db.py | set_buddy_policy() / get_pending_buddies() / is_decline_blocked() |
| (DB 컬럼) | buddy_policy, pending_until |
🤝 서로이웃 그룹 자동 분할 (별님 v7.1 — 2026-05-06)
별님 통찰: 네이버 이웃 한도 5,000명 / 한 그룹 1,000명 넘으면 속도 저하 + 가독성 ↓
→ 1,000명 단위로 자동 그룹 분할 + 따뜻한 그룹명
정책
| 항목 | 값 |
|---|---|
| 네이버 이웃 한도 | 5,000명 (서로이웃 + 일반 이웃 합산) |
| 그룹당 인원 한도 | 1,000명 (별님 명시) |
| 일일 신청 한도 | 100명 (네이버 정책) |
| 풀 채우는 데 | 약 50일 (5000 ÷ 100) |
그룹 분할 로직
신청 시: 1) group_manager.get_appropriate_group_name() - 마지막 사용 그룹 인원 < 1000 이면 그 그룹 - 1000 도달 시 새 그룹 자동 생성 ↓ 2) Selenium select 에서 일치 옵션 선택 - 일치 X 면 마지막 그룹 fallback (별님이 직접 그룹 만들어 두면 자동 사용) ↓ 3) 신청 성공 시 group_manager.increment_group_count() - 영속화: data/buddy_groups.json
그룹명 풀 (사전 정의 + AI 백업)
별님 직접 제안: 가족 같은 / 사랑하는 / 고마움을 / 좋아하는 / 동창 같은 / 마음 통함 / 이심전심
확장 (따뜻한 느낌, 20자 이내):
- 정다운 / 응원하는 / 따뜻한 / 그리운 / 든든한 / 소중한 / 행복한
- 햇살 같은 / 꽃 같은 / 별 같은 / 바람 같은 / 새벽 같은 / 푸른 하늘
- 고운 인연 / 반가운 / 함께하는 / 우리들의 / 지혜로운 / 다정한 / 진실한 / 친한 벗 / 아끼는 / 기쁨 주는
→ 30개+ 사전 정의 → 30개 모두 사용 시 Gemini 호출 (백업, 비용 거의 무료)
코드 구조
| 파일 | 역할 |
|---|---|
modules/buddy/group_manager.py | 그룹명 풀 + 인원 추적 + 영속화 |
modules/buddy/applier.py | select_appropriate_group() 가 자동 분할 |
data/buddy_groups.json | 그룹별 인원 / 마지막 사용 그룹 |
5000명 한도 도달 시
- runner 시작 시
group_manager.is_naver_limit_reached()체크 → 즉시 중단 - 로그: “🚨 네이버 이웃 한도 5000명 도달 — 더 신청 불가”
💰 AI 호출 순서 정책 — Lazy Provider (별님 v7.1 — 2026-05-06 절대 명시)
별님 통찰: “답글을 먼저 생성하였으나 신청 진행 중이거나 이미 서로이웃 팝업이 뜨면 → 작성 사라짐 → API 비용만 낭비”
해결책: 버튼 클릭 → 폼/입력창 등장 확인 → 그제야 AI 호출
잘못된 순서 (이전, v7 까지)
1) AI 텍스트 생성 (Gemini 호출 — 비용 발생) ← 비용 ↑ ↓2) 페이지 진입 → 버튼 클릭 ↓3) "이미 이웃" 팝업 → 폼 X → 작성 못 함 ↓ 결과: API 비용 낭비 + 봇 의심 ↑
올바른 순서 (별님 v7.1 — Lazy Provider)
1) 페이지 진입 → 신청/답글 버튼 클릭 ↓2) 폼 / 입력창 등장 확인 ↓ (등장 X 면) ↓ (등장 O 면) AI 호출 0회 (비용 ↓) AI 호출 → 텍스트 → 입력 → 등록 skipped 반환 success 반환
적용 모듈
| 모듈 | 적용 여부 | 비고 |
|---|---|---|
buddy/applier.py.request_buddy() | ✅ 필수 (별님 명시) | “이미 이웃” / “신청 중” 팝업 빈번 |
comment/poster.py.process_single() | ✅ 적용 | 입력창 없는 글 회피 |
reply/poster.py.post_reply() | ⚠ 검토 (별님: “상관없다”) | 본인 글이라 폼 99% 떠 |
integrated/runner.py | ✅ buddy/comment 통해 자동 적용 |
코드 패턴 — Callable 또는 String
# runner.py 측def _greeting_provider(): if use_ai: return ai_greeting.generate_for_blog(...) # 폼 떴을 때만 호출됨 return fallback_greetingresult = applier.request_buddy(driver, blog_id, _greeting_provider, log=log)# applier.py / poster.py 측def request_buddy(driver, blog_id, message_or_provider, ...): # 페이지 진입 + 버튼 클릭 if not click_buddy_radio(driver): return "skipped" # ← AI 호출 0회 (비용 절감) # 폼 떴음 — 이제 AI 호출 if callable(message_or_provider): message = message_or_provider() # lazy 호출 else: message = message_or_provider # str 호환 fill_message(driver, message) ...
효과
- API 비용 절감: 폼 안 뜨면 Gemini 호출 0회
- 봇 의심 회피: 빈 폼에 텍스트만 남기는 것 방지
- 호환성 유지:
str도 그대로 받음 (callable 만 lazy 처리)
🚫 모달 팝업 금지 정책 (별님 v7.1 — 2026-05-06 절대 명시)
24/7 자동화 — 모달 팝업 하나가 프로그램 정지를 유발.
별님 명시: “어떠한 곳에서도 팝업 창이 뜨지 않도록 모든 팝업 창 삭제”
유일한 예외: 사용기간 만료 알림 팝업.
금지 항목 (모든 GUI 파일)
messagebox.showinfo(),showwarning(),showerror()— 사용 금지messagebox.askyesno(),askokcancel()— 확인 다이얼로그 사용 금지 (별님 직접 클릭 = 의도 명확)
유일한 허용
- 라이선스 만료 / 사용 불가 알림 (
app.py의 license_check 분기) - 그 외 모든 알림은
self._on_status()또는 status 라벨로 출력
대체 패턴 (코드 작성 시)
| 기존 | 신규 |
|---|---|
messagebox.showerror("입력 오류", msg) | self._on_status(f"[모듈] ⚠ {msg}") |
messagebox.showinfo("완료", msg) | self._on_status(f"[모듈] ✓ {msg}") |
messagebox.showwarning("경고", msg) | self._on_status(f"[모듈] ⚠ {msg}") |
if not messagebox.askyesno("확인", msg): return | (별님 클릭 = 의도 명확, 즉시 진행) |
askyesnocancel("삭제 확인", ...) | 삭제 작업은 즉시 수행 (별님 명시: “삭제 해야함”) |
이유:
- 모달 팝업은 thread block — 자동화 모듈 진행 정지
- 사용자 자리 비울 때 팝업 뜨면 → 24시간 운영 멈춤
- 로그 출력은 비차단 + 시각적 확인 가능 + 자동화 호환
별님 v7.2 (2026-05-10 후 26) 추가 사례
별님 직접 명시: “이 프로그램은 24시간 365일 자동으로 운영 되어야 하기에
절대 팝업창이 뜨지 않아야 합니다. 단 하나 사용기간 만료, 라이선스때 팝업창 뜸”
확인된 위반 사례 (모두 제거)
goldkeyword_tab._clear_board()— askyesnocancel “엑셀도 삭제?” → 팝업 제거schedule_dialog._apply_preset()— showinfo “프리셋 적용됨” → 로그로schedule_dialog._apply_preset()— showerror “저장 실패” → 로그로schedule_dialog._reset_default()— askyesno “기본 복원?” → 즉시 진행
🔁 도배 방지 + Daum 직접 요약 + 등록 검증 (별님 절대지침 — 2026-05-11 4종)
별님 직접 명시 (4가지 통합):
- 답글 dedup: 같은 사람에게 두 번 답글 X (도배 방지)
- 댓글 dedup: 내가 단 댓글에 답글 안 달렸으면 같은 사람 두 번 X
- 구독 시간 절약: Daum 검색 결과에서 직접 요약 추출 (글 페이지 진입 X)
- 등록 검증: 글 작성 끝나고 등록/확인 버튼 클릭 + 등록 완료 검증 후 다음 진행
1) 답글 dedup — 같은 사람 도배 방지
별님 증거 (2026-05-10): 같은 사람 센텀큐부동산과컴퓨터 에게 22:21, 22:23, 05:26 — 3번 답글
# modules/reply/runner.py# 댓글 ID 단위 dedup (기존)hist_key = f"{blog_id}/{post_no}/{comment_id}"# ★ 같은 사람 단위 dedup (신규)person_key = f"{blog_id}/PERSON/{author_blog_id or nickname}"if history.is_processed_today(MODULE_NAME, person_key): skip # "같은 분에게 오늘 이미 답글함 — 도배 방지"
2) 댓글 dedup — 내 댓글 답글 없으면 같은 사람 2번 X (기존 자동 적용)
# modules/comment/runner.pyif already_commented_without_reply(driver, own_blog_id): skip # "내 댓글에 답글 없음 — 도배 방지"
3) 구독 시간 절약 — Daum 검색 결과에서 직접 요약
옛 (15~30초 낭비):
- 글 페이지 진입 → 3단계 스크롤 (각 4~8초 random sleep) → 본문 추출
신 (별님 v7.2 — 2026-05-11):
- Daum 검색 결과 페이지에서 직접 추출:
- 셀렉터:
c-doc-web > c-item-doc - 제목:
strong.tit-g a - 본문 5개 슬라이드:
c-contents-desc(data-swap=0~4) FoundPost.summary필드에 보관 →subscribe_blog(daum_title=, daum_summary=)- 페이지 진입 후 짧은 안정화만 (2~3초)
- 약 25초 절감 / 블로그
4) 등록 검증 — 글 짤림 방지
별님 명시: “글쓰기 마치고 등록, 확인 버튼 누르고 다음 진행”
# 댓글/답글 — 등록 클릭 후def _verify_comment_submitted(driver, textarea, max_wait_sec=4.0): """입력창이 비워질 때까지 폴링 — 등록 완료 검증.""" end = time.time() + max_wait_sec while time.time() < end: actual = driver.execute_script( "return (arguments[0].textContent || arguments[0].value || '').trim();", textarea, ) if not actual or len(actual) < 3: return True # ✓ 등록 OK time.sleep(0.3) # 검증 실패해도 한 템포 대기 sleep_random(1.0, 2.5)
적용 위치
| 모듈 | 적용 항목 |
|---|---|
reply/runner.py | ✅ person_key dedup + 등록 검증 |
comment/runner.py | ✅ _verify_comment_submitted + already_commented_without_reply |
subscribe/runner.py | ✅ daum_title/daum_summary 인자 + 15~30초 스크롤 제거 |
comment/post_finder.py | ✅ Daum 검색 결과에서 summary 추출 |
🔒 글쓰기 lock — 모든 보답 모듈 대기 (별님 절대지침 v7.2 후 40)
별님 직접 명시: “모든 글쓰기 통합, 공감, 서로/구독, 답글에서 글을 작성 할때에는
글쓰기가 끝나야 다음 진행 합니다. 절대지침 통합입니다.”별님 추가 명시 (후 57): “글 쓰는 동안 기다려야함, 창을 닫는다거나, 기타 등등 X.
글 다쓰고 등록이나 확인 신청 마무리 버튼을 클릭 하였을때 다른 동작 할 수 있다.”
정책 (4 시리즈 통일)
core/cross_module_lock.py— byte-identical (AutoBlog / TstoryBlog / Blogger / WordPress)writer/runner.py진입 시:set_writer_active(True)comment/buddy(subscribe)/reply/integrated runner진입 시:wait_for_writer_done()- writer 종료 시:
set_writer_active(False)(try/finally 안전 해제 — 후 42)
동작 흐름
[writer 진입] ↓ lock SET ↓ 글쓰기 진행 (Two-Phase, 이미지 paste, 발행 등) ↓ 등록/확인 마무리 버튼 클릭 + 검증 ↓ lock UNSET ← 이 시점부터 다른 모듈 진행 가능
[comment/buddy/reply/integrated 시작 시도]
↓ wait_for_writer_done() — writer lock 해제 대기 (2초 poll) ↓ stop_event 시 즉시 중단 ↓ writer 종료 → 작업 재개
금지 사항 (별님 명시)
- 글쓰기 진행 중 다른 모듈이 driver 사용 X (창 닫기/이동 X)
- 글쓰기 끝(등록/확인 마무리 버튼) 클릭 전 절대 다른 동작 X
- 별님 절대지침 (후 43): type_humanlike 4단계 재시도 — 글자 누락 OK, 함수 빠지지 X
💬 대화 흐름 정책 — 댓글/답글 (별님 절대지침 v7.2 후 56, 57)
별님 직접 명시:
“두번 댓글이나 답글 달지 않는다 상대가 댓글을 달았을때 가능하다,
내가 적은 댓글이나 답글에 또다시 두번 세번 반복해서 답,댓글을 달지 않는다”
1) 답글 (후 56) — 마지막 답글 작성자 검사
| 자식 답글 상태 | 동작 |
|---|---|
| 자식 답글 없음 | ✅ 답글 (첫 답글) |
| 마지막 답글이 본인 | ⏭ skip (상대 응답 대기) |
| 마지막 답글이 상대 | ✅ 답글 (대답글 — 대화 흐름) |
# modules/reply/runner.py — comment_collectorreply_items_all = li.find_elements( "ul.tt-list-reply li.rp_reply, " "ul.tt-list-reply li.tt-item-reply, " "li.rp_reply, li.tt-item-reply.rp_reply")if reply_items_all: last_reply = reply_items_all[-1] if 마지막 답글 작성자 == 본인: already_replied_by_me = True # skip else: already_replied_by_me = False # 답글 가능 (대화 흐름)
2) 댓글 (후 57) — already_commented_without_reply
| 내 댓글 + 답글 상태 | 동작 |
|---|---|
| 내 댓글 없음 | ✅ 댓글 |
| 내 댓글 있음 + 답글 없음 | ⏭ skip (도배 방지) |
| 내 댓글 있음 + 답글 있음 | ✅ 댓글 (대화 흐름) |
# modules/comment/runner.pyif already_commented_without_reply(driver, own_blog_id): skip # "내 댓글에 답글 없음 — 도배 방지"# else: 답글 있음 → 댓글 가능 (대화 흐름)
영구 dedup 제거 (후 57 — 후 48 정정)
- 옛 후 48:
commented_blogs키 영구 마킹 → 너무 강함 (대화 흐름 무시) - 신 후 57: 시점별 검사만 사용 (
already_commented_without_reply) - 답글도 동일 — 영구 dedup 제거 (후 56)
모듈별 적용
| 모듈 | 적용 함수 |
|---|---|
comment/runner.py | already_commented_without_reply 시점별 검사 (영구 dedup 제거) |
integrated/runner.py | 동일 — 같은 함수 호출 (영구 dedup 제거) |
reply/runner.py | has_owner_reply 마지막 답글 작성자 검사 (후 56) |
🛡 AutoBlog 보호 — 특별 지시 시만 수정 (별님 절대지침 — 2026-05-11)
별님 직접 명시: “AutoBlog 절대 특별한 지시 사항 있을때까지 프로그램은 손대지 않습니다.”
핵심 원칙
- AutoBlog 코드 수정 절대 금지 (별님 특별 지시 시까지)
- AutoBlog 파일 변경 절대 금지
- AutoBlog 빌드 절대 금지
- ✅ 읽기만 허용 — TstoryBlog/Blogger/WordPress 동기화 시 참조
4 시리즈 통일 정책 (참고용 읽기는 가능)
- TstoryBlog 가 AutoBlog 와 통일 시 → AutoBlog 파일 읽어서 복사 (TstoryBlog 만 수정)
- 새 시리즈 (Blogger/WordPress) 만들 때 → AutoBlog 파일 읽어서 복사
예외 (별님 명시적 요청 시만)
- “AutoBlog 도 수정해” / “양쪽 동기화” / “4 시리즈 humanize 통일” 등 명시
- 그 외에는 AutoBlog 절대 수정 X
별님 사례 (2026-05-10)
core/humanize.py4 시리즈 통일 → 별님 “전부 통일” 명시 → 양쪽 수정 OK- 그 외 모든 작업 → TstoryBlog 만 수정, AutoBlog 무변경
보호 대상
C:\AutoBlog\AutoBlog\├── core/ ✓ 읽기만├── modules/ ✓ 읽기만├── gui/ ✓ 읽기만├── dist/ ✓ 읽기만├── AutoBlog.spec ✓ 읽기만└── data/ ✓ 별님 자산 — 절대 변경 X
📊 엑셀 헤더 표준 — A1=”키워드” 통일 (별님 절대지침 — 2026-05-11)
별님 직접 명시: “엑셀 키워드 저장시 제목열이 추가 되어 글쓰기 작동 안됨. 황금 키워드 채굴하여 저장할때 autoblog 프로그램과 동일. A2:~채굴한 키워드 삽입 저장, 글쓰기엔 A2 제목으로 발행하면 엑셀 키워드 A2는 제일 마지막 행으로 이동한다.”
핵심 원칙 (4 시리즈 통일)
- A1 헤더 = “키워드” (AutoBlog 와 동일)
- A2 부터 채굴된 키워드 순차 삽입
- 글쓰기 시 A2 첫 행을 제목으로 사용
- 발행 후 A2 행을 시트 마지막으로 이동 (다음 키워드가 A2로 올라옴)
헤더 표준 (excel_writer.COLUMNS)
COLUMNS = ["키워드", "PC월수", "MO월수", "총월수", "문서수", "경쟁률", "발행일", "상태"]
호환 헤더 (excel_input._TITLE_HEADERS)
# A1 헤더가 다음 중 어느 것이든 글쓰기 호환 (단, 표준은 "키워드")_TITLE_HEADERS = ("제목", "title", "키워드", "keyword")
자동 정규화 (별님 v7.2 — 2026-05-11)
def ensure_default_excel_exists(path): # 1) 파일 없음 → A1="키워드" 로 생성 # 2) 파일 있고 A1="제목" + A2 비어있음 → "키워드" 로 자동 정규화 # 3) 데이터 있는 파일은 그대로 보존 (자산 보호 절대지침)
흐름
[채굴] A1: 키워드 | PC월수 | MO월수 | ... A2: 챗지피티 ← 가장 우선 발행 대상 A3: 맞춤법검사기 A4: 제미나이 ... ↓[글쓰기 — A2 사용] 글 #1: '챗지피티' → 발행 ↓[발행 후 sort_rows()] A1: 키워드 | ... A2: 맞춤법검사기 ← A2 자리 자동으로 다음 키워드 A3: 제미나이 ... Z9: 챗지피티 (발행일 기록, 마지막으로 이동, 30일 후 부활)
절대 금지 (사용자 친화)
- ❌ “제목” 헤더로 빈 엑셀 만들기 (글쓰기 호환 안 됨 가능성)
- ❌ A1 헤더 변경 (4 시리즈 통일 깨짐)
- ❌ A2 행 직접 변경 (move_row_to_last 가 자동 처리)
적용 위치
modules/goldkeyword/excel_writer.py— 채굴 시 저장modules/writer/excel_input.py— 글쓰기 시 로드 + 자동 생성 + 정규화modules/writer/excel_input.py.move_row_to_last— 발행 후 마지막 이동
📝 글쓰기 Lazy AI — 글쓰기 클릭 후 API 호출 (별님 절대지침 — 2026-05-10 후 33)
별님 직접 명시: “글 작성도 api 손실 — 글쓰기 클릭후 api 글쓰기 작성”
핵심 원칙
- AI 본문/이미지 생성 = GoogleGemini / OpenAI Imagen API 비용 발생
- 글쓰기 페이지 진입 실패 → AI 비용 그대로 손실 (옛 패턴)
check_page_ready()가 입력란까지 다 확인 후에만 AI 호출 (Lazy 절약)
검증 단계 (writer/tistory_editor.py check_page_ready)
| # | 검증 | 셀렉터 | 실패 시 |
|---|---|---|---|
| 1 | 글쓰기 URL 진입 | https://{blog_id}.tistory.com/manage/newpost | False (네트워크/권한 X) |
| 2 | 제목 입력란 발견 | textarea#post-title-inp | False (페이지 로드 X) |
| 3 | 본문 입력란 발견 | 마크다운 textarea#markdown-source 또는 TinyMCE iframe[id$='_ifr'] | False (에디터 미준비) |
| 4 | 모두 OK | – | AI 호출 진행 가능 |
코드 패턴 (writer/runner.py)
# ❌ 옛 — AI 먼저 호출 → 페이지 실패 시 비용 손실post = generate_post(gemini=gemini, ...) # API 호출write_post(driver, req) # 여기서 페이지 실패 가능# ✅ 신 — Lazy: 페이지 입력란 다 확인 후 AI 호출if not check_page_ready(driver, blog_id): log("페이지 진입 실패 — AI 호출 스킵 (API 비용 절약)") continue # 비용 0post = generate_post(gemini=gemini, ...) # 여기서만 API 호출write_post(driver, req) # 진입 보장된 상태
적용 범위 (4 시리즈 통일)
| 모듈 | API | Lazy 적용 |
|---|---|---|
writer/tistory_editor.check_page_ready | Gemini / Imagen / OpenAI | ✅ 페이지+제목+본문 입력란 검증 후 AI |
comment/runner.write_comment | Gemini | ✅ 입력창 발견 후 provider() 호출 |
reply/runner.write_reply | Gemini | ✅ 입력창 발견 후 provider() 호출 |
subscribe/runner.subscribe_blog | Gemini | ✅ 입력창 발견 후 provider() 호출 |
절대 금지
- ❌ 페이지 진입 검증 없이 AI 먼저 호출 — 비용 손실
- ❌ 본문 입력란 미확인 상태로 본문 생성 — 마크다운/TinyMCE 모드 모를 수 있음
✍ 댓글/답글/인사 — 사람이 쓰듯이 입력 + 마무리 한 템포 (별님 절대지침 — 2026-05-10 후 32)
별님 직접 명시 (2026-05-10):
- “댓글 쓰다가 짤린다. 복사해서 바로 붙여 넣기도 보이는 듯 — 사람이 쓰듯이 규칙”
- “답글, 구독, 댓글 글자수 통일” (30~110자 — 절대지침 line 1191)
- “글 쓰는 시간을 정하지 말고 등록, 신청, 확인 마무리 하고 시간을 한템포 가지는 것 2~5초”
글자수 통일 (3 모듈 공통)
| 모듈 | 상수 | MIN | MAX |
|---|---|---|---|
| 답글 | REPLY_MIN/MAX | 30 | 110 |
| 댓글 | COMMENT_MIN/MAX | 30 | 110 |
| 구독 인사 | GREETING_MIN/MAX | 30 | 110 |
→ 사용자가 글자수로 어느 모듈인지 구분 못하게 통일 (자연스러움 ↑)
핵심 원칙 — 입력
- JS
textContent/innerHTML직접 설정 금지 (paste 와 동일 → 봇 감지) - 반드시
type_humanlike한 글자씩 타이핑 우선 (field_type="comment") - 한글 IME 이슈로 입력 부족할 때만 (< 70%) JS paste fallback (마지막 수단)
핵심 원칙 — 시간 (별님 v7.2 후 32 — 정정)
- ❌ 등록 전 대기 X — 별님 명시: “글 쓰는 시간을 정하지 말고”
- 옛(잘못 적용): 등록 전 1.0~2.5초 대기 → 제거
- 사람이 타이핑 끝내자마자 등록 클릭 = 자연스러움
- ✅ 등록/신청/확인 클릭 후 한 템포 1~2.5초 — 별님 명시 (2026-05-10 후 33 정정)
sleep_random(1.0, 2.5, stop_event=stop_event)- 다음 작업으로 넘어가기 전 사람이 결과 확인하는 시간
발견된 위반 사례 (별님 v7.2 — 2026-05-10)
[22:20:41] 💬 댓글: "정쌤! GPT 이미지 프롬프트 쓸 때 늘 막막했는데..." (97자)[22:20:43] ✅ 댓글 등록 완료 ← 2초만에 등록 = paste 의심
원인 (modules/comment/runner.py 옛 코드):
# ❌ 옛 — paste 와 동일driver.execute_script(""" el.innerHTML = ''; el.textContent = text; // paste 효과 el.dispatchEvent(...input event...);""", textarea, comment_text)
→ 한 번에 댓글 전체 commit → 사람 모방 X → 봇 감지 위험.
표준 패턴 (전 모듈 통일 — 별님 v7.2 후 32)
# 1차 — 사람처럼 한 글자씩 (시간 정하지 말 것 — 자연스러운 길이)type_humanlike( driver, textarea, comment_text, field_type="comment", # 0.025~0.060초/글자 (참고용 — 정하지 X) clear_first=True, enable_pauses=True, # 10% 확률 0.5~1.2초 휴지 (생각하는 척) stop_event=stop_event,)# 입력 검증 — IME 이슈로 부족하면 (< 70%) paste fallbackactual_len = len(driver.execute_script( "return arguments[0].textContent || '';", textarea).strip())if actual_len < int(len(comment_text) * 0.7): # JS paste (마지막 수단) ...# 등록 전 = 미세 전환만 (사람이 타이핑 끝내자마자 등록)wait_transition(stop_event=stop_event)submit.click()# ★ 등록 마무리 후 한 템포 (별님 절대지침 1~2.5초)sleep_random(1.0, 2.5, stop_event=stop_event)
적용 위치 (전 모듈)
| 모듈 | 함수 | 상태 |
|---|---|---|
modules/comment/runner.py | write_comment | ✅ 적용 (별님 v7.2 후 31) |
modules/reply/runner.py | write_reply | ✅ 적용 (멘션 보존 + 사람 타이핑) |
modules/subscribe/runner.py | subscribe_blog | ✅ write_comment 재사용 |
modules/integrated/runner.py | _process_one_visitor | ✅ write_comment 재사용 |
field_type 선택 (humanize.TIMING)
| 컨텍스트 | field_type | 글자당 (초) | 추가 휴지 |
|---|---|---|---|
| 댓글 | "comment" | 0.025~0.060 | 단어 끝 + 종결부호(.!?) 뒤 |
| 답글 | "comment" | 0.025~0.060 | 동일 |
| 인사 | "comment" 또는 "greeting" | 0.025~0.060 | 동일 |
| 글쓰기 본문 | "body" | 0.010~0.027 | 단어 끝 |
| 글쓰기 제목 | "title" | 0.017~0.033 | – |
사람처럼 타이핑 시간 계산 (참고)
| 댓글 길이 | 평균 타이핑 시간 | + 등록 전 대기 |
|---|---|---|
| 50자 | 약 2~3초 | 3.5~5.5초 |
| 100자 | 약 4.5~6초 | 5.5~8.5초 |
| 150자 | 약 6.5~9초 | 7.5~11.5초 |
→ paste 보다 5~10배 느림 = 사람 모방 + 봇 감지 회피
📰 메인 → 첫 글 자동 이동 (별님 절대지침 — 2026-05-10 후 30)
별님 직접 명시 (HTML 셀렉터 첨부):
<strong class="title">2026년 1분기 국산차 판매 순위 TOP 6...</strong><p class="summary">안녕하세요!...</p><div class="tt-cmt" contenteditable="true">...</div><button class="tt_btn_subscribe" data-tiara-action-name="프로필영역 구독 버튼_클릭">핵심: 블로그 메인 페이지(
https://xxx.tistory.com/)엔 댓글 입력창과 좋아요 버튼이 없다.
→ 댓글/좋아요 모듈은 반드시 개별 글 페이지로 진입해야 동작.
발견된 문제 (별님 v7.2 사례 — 2026-05-10)
[22:00:46] [comment] ✗ @bbadam9977 댓글 실패: 댓글 입력창 없음[22:07:01] [comment] ✗ @infogarage69 댓글 실패: 댓글 입력창 없음
원인: 포럼/visitor 풀에서 가져온 URL 이 메인 페이지(https://xxx.tistory.com/)
→ 메인엔 div.tt-cmt[contenteditable="true"] 없음 → 모든 댓글 시도 실패.
해결 패턴 (전 시리즈 적용)
def navigate_to_first_post_if_main(driver, stop_event=None) -> bool: """메인 페이지면 첫 번째 글로 자동 이동. URL 패턴 `https://[blog].tistory.com/?$` → 메인. 별님 명시 셀렉터로 첫 글 링크 찾아서 driver.get(href). """ candidates = [ # 별님 명시 — strong.title 의 부모 a 태그 (By.XPATH, "(//a[.//strong[contains(@class,'title')]])[1]"), # p.summary 의 컨테이너 안 a (By.XPATH, "(//*[.//p[contains(@class,'summary')]]/a)[1]"), # 일반 스킨 fallback (By.CSS_SELECTOR, "article a, .article-list a, .post-item a"), (By.CSS_SELECTOR, "a[href*='/entry/']"), ]
적용 위치 (전 시리즈)
| 모듈 | driver.get(post.url) 직후 |
|---|---|
| comment/runner.py | ✅ 적용 (별님 v7.2 후 30) |
| subscribe/runner.py | post_url 사용 — 보통 글 URL 받음 (필요 시 추가) |
| integrated/runner.py | comment/subscribe 위임 → 자동 적용 |
| reply/runner.py | 본인 글 직접 URL — 적용 불필요 |
표준 셀렉터 (티스토리 — 별님 v7.2 명시)
| 요소 | 셀렉터 |
|---|---|
| 첫 글 제목 | strong.title (메인 페이지 글 목록) |
| 첫 글 본문 미리보기 | p.summary |
| 첫 글 링크 | a:has(strong.title) 또는 부모 a XPath |
| 댓글 입력창 | div.tt-cmt[contenteditable="true"] |
| 댓글 등록 버튼 | button.tt-btn_register:not([disabled]) |
| 구독 버튼 | button.tt_btn_subscribe |
| 구독 버튼 (data-tiara) | button[data-tiara-action-name="프로필영역 구독 버튼_클릭"] |
절대지침 적용
- 4 시리즈 (네이버/티스토리/구글/워드프레스) 모두 메인 → 첫 글 자동 이동 패턴 의무
- 댓글 모듈은 메인 URL 받아도 자동 보정 (실패율 ↓)
⏱ humanize.py — 4 시리즈 통합 단일 소스 (별님 절대지침 — 2026-05-10 후 29)
별님 직접 명시: “네이버, 티스토리, 구글, 워드프레스 4개 블로그 시리즈로 프로그램 개발 중. 사람이 입력하듯 기준 기간 표 — 4개 시리즈 모두 통일”
핵심 원칙
- 단일 소스:
core/humanize.py한 파일이 모든 모듈의 시간 정책 결정 - 4 시리즈 동기화: AutoBlog (네이버) / TstoryBlog (티스토리) / Blogger (구글) / WordPress
core/humanize.py바이트 단위 동일 (cp로 복사 가능)- time.sleep() 직접 호출 금지 — 항상 humanize.* 함수 사용
- 정책 변경 시 → 한 시리즈에서 수정 → 나머지 3개에 즉시 동기화
함수 시그니처 표준 (호환 보장)
# 모든 wait 함수: stop_event keyword-only (인터럽트 가능)wait_transition(stage="default", *, stop_event=None) -> boolwait_after_page_load(*, stop_event=None) -> boolwork_unit_gap(kind="default", *, stop_event=None) -> boolsmart_work_gap(kind="default", *, stop_event=None) -> boolinterruptible_sleep(seconds, stop_event=None, tick=0.2) -> boolsleep_random(low, high, stop_event=None) -> bool# 호환 별칭 (TstoryBlog 기존 코드 호환)wait_inter_person, wait_inter_reply,wait_inter_comment, wait_inter_subscribe # 모두 (*, stop_event=None)# 타이핑 (field_type 으로 컨텍스트별 속도)type_humanlike(driver, element, text, field_type="default", *, clear_first=True, enable_typos=False, enable_pauses=True, stop_event=None)press_keys(driver, *keys)
1) TIMING dict — 글자당 타이핑 속도 (초)
| field_type | 범위 | 용도 |
|---|---|---|
id | (0.03, 0.05) | 로그인 ID 입력 |
pw | (0.03, 0.07) | 로그인 PW 입력 |
title | (0.017, 0.033) | 글쓰기 제목 (1/3 단축) |
body | (0.010, 0.027) | 글쓰기 본문 (1/3 단축) |
comment | (0.025, 0.060) | 댓글 (~50ms 평균) |
greeting | (0.025, 0.060) | 인사말 (144자→9초) |
keyword | (0.04, 0.08) | 검색 키워드 |
default | (0.04, 0.08) | 기타 |
2) TRANSITION dict — 단계 전환 (초, 별님 v7.2 ×0.67 단축)
| stage | 범위 | 용도 |
|---|---|---|
id_to_pw | (0.34, 0.47) | ID → PW 필드 이동 |
pw_to_login | (0.27, 0.54) | PW → 로그인 버튼 |
after_login | (1.0, 1.7) | 로그인 후 페이지 안정화 |
after_iframe | (0.34, 0.67) | iframe 진입 후 |
title_to_body | (0.4, 0.67) | 제목 → 본문 |
body_to_save | (0.54, 1.0) | 본문 → 저장/발행 |
like_to_comment | (0.34, 0.6) | 공감 → 댓글 |
comment_to_submit | (0.4, 0.67) | 댓글 입력 → 등록 |
before_clipboard_copy | (0.13, 0.27) | 이미지 클립보드 복사 전 |
between_copy_and_paste | (0.20, 0.33) | 복사 → 붙여넣기 사이 |
after_image_paste | (1.0, 1.67) | 이미지 paste 후 (단원당 ~1초 단축) |
default | (0.2, 0.47) | 기타 단계 |
3) WORK_UNIT_GAP dict — 작업 단위 간 (별님 v7.2 모두 2~10초 통합)
| kind | 범위 | 용도 |
|---|---|---|
comment | (2.0, 10.0) | 댓글 → 다음 댓글 |
buddy | (2.0, 10.0) | 서이추 → 다음 |
subscribe | (2.0, 10.0) | 구독 → 다음 (티스토리) |
writing | (2.0, 10.0) | 글쓰기 → 다음 글 |
reply | (2.0, 10.0) | 답글 → 다음 답글 |
person | (2.0, 10.0) | 사람 → 다음 사람 |
default | (2.0, 10.0) | 기타 |
4) 페이지 로드 후 안정화
| 상수 | 값 |
|---|---|
PAGE_LOAD_AFTER_MIN | 1.0초 |
PAGE_LOAD_AFTER_MAX | 2.0초 |
5) 휴먼 패턴 (오타/휴지 시뮬레이션)
| 상수 | 값 | 효과 |
|---|---|---|
TYPO_PROBABILITY | 0.05 | 5% 확률 오타 → Backspace → 정타 |
PAUSE_PROBABILITY | 0.10 | 10% 확률 글자 중간 휴지 |
PAUSE_RANGE | (0.5, 1.2) | 휴지 길이 |
WORD_END_PAUSE | (0.10, 0.30) | 본문 단어 끝 (공백 직전) |
SENTENCE_END_PAUSE | (0.20, 0.50) | 댓글 종결부호(.!?) 뒤 |
SPEED_MULTIPLIER | 1.0 | 전체 배율 (<1.0 빠르게, >1.0 안전) |
6) 멍때림 패턴 (비활성화 — 호환만 유지)
| 상수 | 값 | 비고 |
|---|---|---|
WORK_UNIT_GAP_LONG_PROB | 0.0 | 비활성 (별님: “최대 2분 너무 길다”) |
동기화 절차 (4 시리즈 신규 시리즈 추가 시)
# 새 시리즈 (예: BloggerBlog) 생성 시 humanize.py 즉시 복사cp C:/AutoBlog/AutoBlog/core/humanize.py C:/BloggerBlog/BloggerBlog/core/humanize.py# 정책 변경 시 4 시리즈 동기화 (master = 임의 시리즈)cp C:/AutoBlog/AutoBlog/core/humanize.py C:/TstoryBlog/TstoryBlog/core/humanize.pycp C:/AutoBlog/AutoBlog/core/humanize.py C:/BloggerBlog/BloggerBlog/core/humanize.pycp C:/AutoBlog/AutoBlog/core/humanize.py C:/WordPressBlog/WordPressBlog/core/humanize.py
주의 (절대 금지)
- ❌ 시리즈별로 humanize.py 다르게 유지 — 정책 분리 = 봇 감지 위험
- ❌ 모듈 코드에서
time.sleep(N)직접 호출 — 인터럽트 안 됨 - ❌ stop_event 인자 누락 — 자동 모드 중지 버튼 무력화
🎯 황금 키워드 채굴 시드 정책 (별님 절대지침 — 2026-05-10 후 28)
별님 직접 명시: “시드 100개 × 시드당 100개는 깊이가 없다. 하루 100개 포스팅 하기도 힘들다 — 적게 깊게 채굴해야 한다”
핵심 원칙
- 얕고 넓게 ❌ → 시드 많이 × 시도 적게 = 표면만 훑음, 황금 발굴 X
- 적고 깊게 ✅ → 시드 적게 × 시도 많이 = BFS 깊이 확보, 황금 발굴 O
새 기본값 (전 시리즈 통일)
| 항목 | 새 기본값 | 범위 | 근거 |
|---|---|---|---|
| 시드 개수 | 5 | 1~200 | 적게 — 깊이 우선 |
| 시드당 시도 | 700 | 10~2000 | 깊게 — BFS 5단계까지 충분 |
| 시드당 시간 | 20분 | 1~60 | 5×20 = 100분 (00:30~03:00 안) |
검증
- 5시드 × 700시도 = 3500 visited / 사이클
- 5시드 × 20분 = 100분 (1시간 40분) — 채굴 윈도우 안전
- 분류 저장 ~50% 가정 → ~1750개 황금/우수/양호 키워드 / 사이클
- 하루 채굴 5~10 사이클 → 자산 누적 가속
코드 위치
- TstoryBlog:
gui/goldkeyword_tab.py라인 ~118-138 (IntVar(value=...)) - AutoBlog:
gui/keyword_tab.py동일 위치 - fallback 값 (
.get() or N) 도 새 기본값으로 통일 필요
💎 엑셀 자산 보호 (별님 절대지침 — 2026-05-10 후 27)
별님 직접 명시: “엑셀은 지금까지 채굴한 자산입니다. 절대 프로그램 내에서 삭제 하시면 안됨”
이 프로그램은 24/365 자동 운영 → 채굴된 황금 키워드 = 누적된 자산.
핵심 원칙
- 황금 키워드 엑셀 (
today_gold_keyword.xlsx, blog별 누적 파일) = 자산 - 프로그램 내부 어디서도 자산 엑셀 자동 삭제 금지
- 사용자가 직접 파일 탐색기에서 삭제할 수는 있음 (그건 사용자 의도)
보드 지우기 정책 (전 시리즈 통일)
- [🗑 지우기] 버튼 클릭 → 화면(트리뷰)만 비움
- 엑셀 파일은 절대 보존 (자산 무손실)
- 다음
_load_from_excel시 다시 표시됨 → 사용자는 언제든 자산 복원 가능 - 로그:
🗑 보드 비움 (엑셀 자산 보존 — 절대지침)
안전한 unlink (허용)
- 임시 백업 파일 (
*_BACKUP_*.xlsx,*.tmp) — 메인 자산 저장 성공 후 정리 - 주의: 메인 파일 (
today_gold_keyword.xlsx,gold_keyword_*.xlsx) 은 절대 unlink 금지
코드 점검 시 확인 항목
| 위치 | 안전성 |
|---|---|
excel_writer.py bak_path.unlink() | ✅ 백업파일만 (메인 저장 성공 시) |
excel_store.py tmp_path.unlink() | ✅ 임시파일만 (저장 실패 정리) |
goldkeyword_tab._clear_board() | ✅ 화면만, 엑셀 절대 보존 |
| 신규 코드 작성 시 | ⚠ 자산 엑셀 unlink 코드 절대 추가 금지 |
🌟 별님 핵심 원칙 — “4개 시리즈는 한 가족”
| 항목 | 통일 |
|---|---|
| 알고리즘 | 시리즈 공통 |
| 디자인 | AutoBlog GUI 마스터 기준 |
| 정책 | 5대 감정 어조, 일일 한도, 보답 우선 등 모두 공통 |
| 사용자 경험 | 동일 (탭 순서, 시계, 보드, 색상) |
플랫폼별 차이는 셀렉터/URL 만 — 각 프로젝트 절대지침_특화.md 참조.
🕐 24/7 운영 시간 설계 (별님 v7 — 2026-05-06)
별님 명시: 사람이 실제 잠자는 시간 흉내 + 봇 감지 회피 + 모든 프로그램 충돌 없는 자동 분산.
일일 시간 흐름 (24시간 기준)
00:00 ~ 00:30 ⚙ 시스템 정리 (자정 자가 점검) - 캐시/쿠키/임시파일/로그 회전 - driver 재부팅 / chromedriver 정리 - GC 강제 + 일일 한도 100 초기화 - DB 백업 (visitor / blogs / schedule) ↓00:30 ~ 03:00 🥇 황금 키워드 채굴 (잠자는 시간 = 봇 감지 안전) - 100개 시드 → 분석 → 5색 티어 분류 - 차단 의심 회피 (블로그/검색 엔진) ↓03:00 ~ 04:00 🌅 자동화 프로그램 시작 (★ 랜덤 시작 ★) - AI 패턴 회피 위해 매일 다른 시각 (3:00~4:00 사이 랜덤) - 채굴된 황금 키워드 엑셀 → 글쓰기 시작 - 운영자 유형 4종 중 선택된 시간대 활성 ↓~ 23:59 📝 모든 자동화 모듈 운영 시간대 - 운영자 유형 (직장인/사업가/전업주부/야간부업) 별 윈도우 - 모듈 간 자동 충돌 회피 - 일일 한도 도달 시 자동 OFF ↓00:00 (다시 자정 → 다음 사이클)
운영자 유형별 4개 프리셋 (별님 v7 표준)
| 유형 | AM 시작 | AM 종료 | PM 시작 | PM 종료 | 특징 |
|---|---|---|---|---|---|
| 👔 직장인 | 06:30 | 08:30 | 18:30 | 22:30 | 출퇴근 전후 |
| 🏢 사업가 | 07:00 | 11:00 | 14:00 | 23:00 | 업무 시간 + 야근 |
| 🏠 전업주부 | 09:00 | 12:00 | 13:00 | 17:00 | 평일 낮 |
| 🌙 야간부업 | (없음) | — | 21:00 | 25:00 | 저녁~새벽 |
각 유형은 모듈별 시간대 자동 분산:
- 황금 키워드 채굴: 00:30~03:00 (모든 유형 공통)
- 자동화 시작: 03:00~04:00 랜덤 (모든 유형 공통)
- 글쓰기/답글/공감/서로이웃: 유형별 윈도우 안에서 자동 분산
모듈 충돌 방지 (master_daemon)
- 한 모듈이 driver 사용 중일 때 다른 모듈 자동 대기
- 시간대 겹쳐도 한 번에 하나만 실행
- 다음 사이클에서 못 한 모듈 자동 재시도
🎯 별님 절대 1순위 — “수동값 우선” 정책
별님이 GUI / 설정 파일에 명시한 값은 무조건 우선. 코드가 임의로 자동 계산/덮어쓰기 시 절대지침 위반.
우선순위 (모든 모듈 공통)
1. 1회차: 별님이 명시한 값 (수동 입력) ← 무조건 우선 ↓ 1회차 끝, 2회차+2. 2회차+: 자동 랜덤 (예약 발행 모드) - 글쓰기 프리셋 (정보성/홈플레이트/...) - 말투 (친근형/기본/...) - 이미지 프리셋 (3D 랜더형/빈티지 필름/...) - 글쓰기 간격 (30~120분 랜덤) - 최근 3회 회피 (같은 프리셋 반복 X) ↓ 자동 랜덤 결과 없을 때3. 코드 fallback (최후 수단)
적용 대상
| 설정 | 1회차 (수동 우선) | 2회차+ (자동 랜덤) |
|---|---|---|
| 글쓰기 프리셋 | GUI writer_preset | 랜덤 (최근 3회 회피) |
| 말투 | GUI writer_tone | 랜덤 |
| 이미지 프리셋 | GUI image_preset | 랜덤 |
| 글쓰기 간격 | schedule.json inter_item_gap [low, high] | 자동 랜덤 |
| 발행 모드 | GUI 라디오 (즉시 발행) | (1회차 그대로) |
| 일일 한도 | GUI 입력값 (1~100) | (남은 한도 자동 cap) |
코드 패턴
# ✅ 올바른 패턴def get_value(): user_value = config.get_manual_value() # 1순위: 사용자 명시 if user_value: return user_value return _auto_calculate_fallback() # 2순위: 자동 계산# ❌ 위반 패턴 (절대지침 위반)def get_value(): return _auto_calculate() # 사용자 설정 무시
📊 일일 한도 100 자동 추적 (별님 v6 — 2026-05-06)
활동 3종 + 통합
| 활동 | 일일 한도 | 모듈 | 비고 |
|---|---|---|---|
buddy (서로이웃 / 구독) | 100 | subscribe/buddy + integrated 공유 | 네이버 정책 강제 |
comment (공감 + 댓글) | 100 | comment + integrated 공유 | 안전 한도 (별님 v7.1) |
reply (본인 답글) | ♾ 무제한 | reply + integrated 공유 | 별님 v7.1: 본인 글 댓글에는 네이버 한도 없음 |
별님 v7.1 (2026-05-06) 명시: 답글은 무제한 — 본인 블로그의 댓글에 답글 다는 것은 네이버 정책상 한도 없음.
코드:core/daily_quota.py의ACTIVITY_LIMITS[ACTIVITY_REPLY] = 10**9(사실상 무제한).
입력 GUI: Spinboxto=9999, increment 10 (별님 v7.1).
처리 시 표시:(한도: 무제한 — 별님 v7.1)
동작 흐름
00:00 자정 자가 점검 → 한도 100/100/100 으로 리셋 ↓오전: 서로이웃 10개 → buddy 90 남음오후: 통합 보답에서 50개 → buddy 40 남음 (모듈 공유)저녁: 직접 30개 → buddy 10 남음 ↓다음날 00:00 → 다시 100/100/100
코드
from core import daily_quota# 입력 captarget = daily_quota.cap_input(daily_quota.ACTIVITY_BUDDY, config.target_count)if target <= 0: return # "오늘 한도 도달"# 차감if result == "success": daily_quota.consume(daily_quota.ACTIVITY_BUDDY, 1)
🥇 황금 키워드 채굴 — 봇 감지 회피 시간대
채굴 시간 (별님 v7)
- 00:30 ~ 03:00 (잠자는 시간 = 검색 차단 회피)
- 시드 7개 → 1단계 → 2단계 → 3단계 (Gemini)
- 100개 풀 → 분석 → 5색 티어 분류 → 50개 저장
5색 티어 (별님 표준)
| 라벨 | 배경 | 글자 | 기준 |
|---|---|---|---|
| 🟡 황금 | #FFFF00 | 검정 | 검색≥500, 경쟁≤1.0 |
| 🟢 우수 | #00B050 | 흰색 | 검색≥400, 경쟁≤2.0 |
| 🔵 양호 | #0070C0 | 흰색 | 검색≥300, 경쟁≤5.0 |
| 🔴 도전 | #FF0000 | 흰색 | 검색≥200, 경쟁≤10 |
| 🟤 위험 | #5C0000 | 흰색 | 검색≥100, 경쟁≤100 |
| 🟣 관심 | #9333EA | 흰색 | 검색≥500 (PC OR Mob), 경쟁≤200 — 폐기 방지 |
매력 제목 7대 패턴
- 숫자 강조 (예: “10가지 비법”)
- 질문형 (예: “왜 ~ 일까?”)
- 부정 (“~ 절대 하지 마세요”)
- 시급성 (“지금 안 하면 늦어요”)
- 비교 (“A vs B”)
- 비밀/노하우 (“아무도 안 알려주는”)
- 본인 경험 (“내가 직접 해본”)
💬 답글 / 댓글 / 인사말 — 5대 감정 어조 (시리즈 공통)
본인 답글 = 다른 분 댓글 = 구독 인사말 = 통합 보답 — 모두 동일 정책
길이 정책
- 30~110자 (절대 준수)
5대 감정 (조합 가능)
감사 · 공감 · 소중함 · 즐거움 · 행복
필수 요소
- 닉네임/별명 한 번 자연스럽게 호명 (
OO님 ~) - 댓글/글 내용 자연스럽게 호응 (직접 인용 금지)
- 이모티콘 1~3개 (31종 풀)
- 위트 있게 작성 (별님 v7 추가)
- 광고/홍보/외부링크 절대 금지
- 한 단락 (줄바꿈 X)
- 한국어 친근한 존댓말
이모티콘 풀 (31종)
🌸 🌷 🌹 💐 🌻 🌿 ☘️ 🍀 🌳 🍃 ✨ ⭐ 💫 🌟 ☀️ 🌈 ☕ 💕 💖 💗 ❤️ 🥰 😊 🤗 😄 🙌 👍 🎉 💌 ✏️ 📌
네이버 6가지 공감 반응 → 5대 감정 매핑 (별님 v7.1 라이브 발견)
네이버 글 보기 페이지의 공감 버튼은 6가지 반응 (
like/impressive/thanks/haha/wow/sad).
별님 5대 감정 어조와 매핑하여 봇 패턴 회피 (한 종류만 누르지 X).
| 5대 감정 | 1차 반응 | 2차 반응 | data-type |
|---|---|---|---|
| 감사 | 감사 | 공감 | thanks / like |
| 공감 | 공감 | 칭찬 | like / impressive |
| 소중함 | 칭찬 | 감사 | impressive / thanks |
| 즐거움 | 웃김 | 공감 | haha / like |
| 행복 | 공감 | 칭찬 | like / impressive |
분산 정책 (별님 v7 — 봇 감지 회피):
- 공감(
like) 60% + 나머지 5종 골고루 40% - 한 종류 연속 클릭 X (최근 3회 회피)
- 슬픔(
sad)은 신중 사용 (글 내용 부정적일 때만)
셀렉터:
- 공감 토글:
a.u_likeit_button._face - 6 반응 layer:
ul.u_likeit_layer._faceLayer - 개별 반응:
li.u_likeit_list.{like|impressive|thanks|haha|wow|sad} a._button
전체 셀렉터 표는 처리과정/03_공감댓글.md 의 D 섹션 참조.
모바일 글 보기 — #_post_property 메타 div ⭐ (별님 v7.1 핵심 발견)
모바일 페이지 (
m.blog.naver.com/{blog_id}/{post_id}) 의 숨겨진 div 한 곳에 모든 메타 정보.
PC 페이지에서 여러 element 추출보다 ~5배 빠른 메타 획득.
prop = driver.find_element(By.CSS_SELECTOR, "div#_post_property")relation = prop.get_attribute("relationtypewithblog") # 0/1/2/3 — 이웃 관계comment_enabled = prop.get_attribute("commentenable") == "true"is_blacklist = prop.get_attribute("isblacklistuser") == "true"comment_count = int(prop.get_attribute("commentcount") or 0)add_date_ms = int(prop.get_attribute("adddate") or 0)
자동화 활용:
relationtypewithblog→ 이미 서로이웃이면 신청 skip (= "1"체크)commentenable="false"→ 댓글 모듈 즉시 skipisblacklistuser="true"→ 차단 사용자, 모든 처리 skipcommentcount→ 빈 글 (0) skip / 활발한 글 (10+) 우선
권장: 모바일에서 메타 빠른 추출 → PC 로 전환해 댓글 작성 (각 모드 강점 활용).
전체 모바일 셀렉터 표는 처리과정/03_공감댓글.md 의 M 섹션 참조.
↩ 본인 답글 모듈 (별님 v7.1 — 무제한 한도 확정)
일일 한도 — ♾ 무제한 (별님 v7.1 절대 명시)
- 본인 블로그 댓글에 답글 다는 것은 네이버 정책상 한도 없음
- 코드:
daily_quota.ACTIVITY_LIMITS[ACTIVITY_REPLY] = 10**9(사실상 무제한) - GUI: Spinbox
to=9999, increment=10 - runner:
TARGET_HARD_LIMIT = 10**9(cap 없음) - 로그 표시:
한도: 무제한 — 별님 v7.1
글 분석 범위 — ♾ 무제한 (별님 v7.1 — 2026-05-06)
- 첫 실행 100개 / 2회+ 20개 → 첫 실행 무제한 / 2회+ 무제한 (전부 분석)
- RSS / HTML이 주는 모든 본인 글 분석
- 코드:
max_posts = 9999(사실상 무제한) - 시간 단축은 정책 6 (News.naver) + 정책 4 (모바일 사전 필터) 로 자동 처리
- 공감/댓글 작성자의 ID/별명/내용/날짜 → DB 화 (
data/comment_visitors_{blog_id}.json)
처리 정책 — 끝까지 진행 (별님 v7.1)
- 답글 작성 안 한 새 댓글만 처리
- 입력값(1~9999) 만큼
종료 조건 — 다음 중 하나라도 발생 시 종료 (별님 v7.1 — 2026-05-06)
| # | 조건 | 동작 |
|---|---|---|
| A | 답글 성공 카운트 = 입력값 도달 | ✅ 목표 달성 → 즉시 종료 |
| B | 본인 글 풀 완전 소진 (더 이상 분석할 글 없음) | 📭 종료 |
| C | stop_event 발생 (사용자 정지) | ⏹ 즉시 중단 |
| D | 자동 스케줄 시간 윈도우 종료 | ⏰ 즉시 중단 |
| E | 일일 한도 메시지 감지 (현재는 무제한이지만 안전장치) | 🚨 즉시 중단 |
| F | 연속 실패 3회 (네이버 차단 의심 안전장치) | ⚠ 즉시 중단 |
별님 명시: “100을 입력 하였다면 100개가 작성될 때까지, 블로그 글이 더 이상 존재하지 않을 때까지, 프로그램 진행 시간이 끝날 때까지 계속 진행 함.”
즉 — 새 댓글 부족이라도 글 풀이 남아 있으면 더 옛날 글까지 거슬러 올라가서 분석 (페이지네이션).
보드 실시간 동기화 (별님 v7.1 — 2026-05-06)
runner.py가 처리 시core.visitor_board.add_event()호출- 답글 OK ✅ / 이미답글 ⏭ / 오늘처리 ⏭ / 실패 ❌ 모두 보드에 즉시 반영
- GUI 보드 위젯이 1.5초마다 polling → 화면 갱신
5대 감정 어조 + 위트 + 110자
빠르게 실행 — 6가지 정책 (별님 절대지침 / TistoryBlog 표준 + v7.1)
시간 80% 단축 — 50개 글 중 3~5개만 실제 진입.
별님 v7.1 (2026-05-06): 5번째 관리자 이웃관리 DB 매칭 + 6번째 내소식 페이지 직접 활용.
| # | 정책 | 플랫폼 |
|---|---|---|
| 1 | 댓글 ≥ 1 인 글만 필터링 | 시리즈 공통 |
| 2 | 사이드바 최근 댓글 우선 (div.recent-comment) | 티스토리 |
| 3 | 빠른 댓글 수 체크 (em#commentCount) | 네이버 |
| 4 | 모바일 글 목록 사전 필터 (span.comments__iipHc) | 네이버 |
| 5 ⭐ | 관리자 이웃관리 DB 매칭 + 신청 일괄 수락 | 네이버 |
| 6 ⭐⭐ | 내소식 페이지 직접 활용 (m.blog.naver.com/News.naver) ★최고속★ | 네이버 |
6번 정책 핵심 (별님 v7.1 — 가장 빠른 처리)
https://m.blog.naver.com/News.naver = 본인이 받은 모든 알림 한 페이지에 누적:
- 새 댓글 / 공감 / 이웃 신청 / 멘션 알림 모두
→ 본인 글 50개 순회 X → 단 한 페이지로 즉시 처리 → 글로 직접 점프해 답글 작성.
본인 정보 자동 추출 (별님 라이브 발견)
| 정보 | 셀렉터 |
|---|---|
| 본인 닉네임 | div.nickname__rkKeV |
| 본인 blog_id | div.blog_id__eKHcP span.text__jvRR4 |
| 오늘/전체 방문자 | em.stats_count |
→ credential_loader 없이도 즉시 확보.
처리 속도 비교
| 정책 | 답글 1개당 시간 |
|---|---|
| 정책 0 (단순 50글 순회) | ~30초 |
| 정책 1+3+4 (v7) | ~8초 |
| 정책 1+3+4+5 (v7.1 DB 매칭) | ~5초 |
| 정책 6 단독 (내소식 활용) ⭐⭐ | ~2~3초 |
| 정책 5+6 통합 최적 ⭐⭐⭐ | ~1~2초 |
전체 셀렉터 / 코드 위치는 처리과정/04_본인답글.md 의 “빠르게 실행 6가지” 섹션 참조.
🤝 서로 이웃 / 구독 모듈 (별님 v7 명시)
DB 우선 활용
- 본인 답글 모듈이 저장한 DB 사용
- DB 의 visitor → 서로이웃 / 구독 신청
- 메시지 위트 있게 작성
DB 부족 시 보충
- 새 키워드로 블로그 검색
- 입력값(설정) 충족까지 신규 추가
- 하루 최대 100개 (네이버 정책 / 티스토리 안전선)
종료 조건
- 입력값 충족 → 즉시 종료
- 또는 일일 한도 100 도달
💬 공감 / 댓글 모듈 (별님 v7 명시)
DB 우선 활용
- 답글 모듈 DB → 공감/댓글
- 위트 있게 작성
DB 부족 시 보충
- 새 키워드로 블로그 검색
- 입력값(설정) 충족까지 신규 추가
- 하루 최대 200개 ~ 무한 (별님 v7)
종료 조건
- 입력값 충족
- 또는 일일 한도 도달
🤝 통합 보답 모듈 (별님 v7 명시)
실행 순서 (별님 명시)
1. 본인 답글 (DB 생성/갱신) ↓2. 서로 이웃 (DB 활용 → 부족 시 키워드 검색) ↓3. 공감 / 댓글 (DB 활용 → 부족 시 키워드 검색)
충돌 방지
- 통합 보답 vs 개별 모듈 (답글/서이추/공감) — 동시 실행 X
- 한 사람당 답글+좋아요+댓글+서이추 모두 처리하는 통합 vs 단독 운영
- master_daemon 이 락 관리
- 서로 다른 3개 모듈 — 동시 실행 가능
- 예: 답글 (모듈 A) + 서이추 (모듈 B) + 공감/댓글 (모듈 C) 동시 OK
- 단, 각 모듈은 별도 driver 슬롯 사용
친밀도 우선 (수집)
| 순위 | 대상 | 효과 |
|---|---|---|
| 1차 | 본인 답글 DB visitor | 친밀도 ↑ |
| 2차 | 본인 블로그 구독자/이웃 | 기존 인연 |
| 3차 | 키워드 검색 | 새 발견 (구독 X) |
| 4차 | 본인이 구독한 블로그 | 양방향 |
✍ 글쓰기 모듈 — 3개 구성 (별님 v7 명시)
구성
1. 글쓰기 프로그램 (본문 생성 + 프리셋/말투/구조)2. 이미지 생성 프로그램 (본문 ### 마다 이미지)3. 네이버/티스토리/... 글쓰기 프로그램 (에디터 자동 입력 + 발행)
“수동값 우선” 정책 (별님 v7 절대 적용)
글쓰기 프로그램
- 1회차: 별님 GUI 입력값 우선 (정보성/친근형/…)
- 2회차+: 예약 발행 자동 랜덤
- 프리셋 랜덤 (최근 3회 회피)
- 말투 랜덤
- 본문 ###/구조 랜덤
이미지 프로그램
- 1회차: 별님 GUI 입력값 우선 (3D 랜더형/…)
- 2회차+: 자동 랜덤 (최근 3회 회피)
네이버 글쓰기 프로그램
- 1회차: 별님 GUI 입력값 우선
- 2회차+: 예약 발행 자동 랜덤
- 글쓰기 간격 30~120분 진짜 랜덤
- 발행 모드 (즉시 발행 / 임시저장 등)
글자 수
- 9 프리셋: 9문단, ≈ 1,500자
- 14 프리셋: 14문단, ≈ 2,500자 (기본)
- 22 프리셋: 22문단, ≈ 4,000자
네이버 SmartEditor 3 — PC 글쓰기 핵심 셀렉터 (별님 v7.1 라이브 검증)
전체 셀렉터 표는
처리과정/06_글쓰기_이미지.md의 SmartEditor 3 섹션 참조.
| 영역 | 핵심 셀렉터 | 비고 |
|---|---|---|
| 에디터 본체 | div.se-body.__se-body | data-theme="greenLight" / lang="ko-KR" |
| 제목 컴포넌트 | div.se-component.se-documentTitle | data-a11y-title="제목" |
| 본문 컴포넌트 | div.se-component.se-text | data-a11y-title="본문" |
| 입력 단락 | p.se-text-paragraph | line-height 1.6 |
| 텍스트 노드 | span.se-ff-nanumgothic.se-fs19.__se-node | 19pt 본문 |
| 본문 placeholder | “글감과 함께 나의 일상을 기록해보세요!” | 사라지면 입력 확정 |
| 사진 삽입 (툴바) | button[data-name='image'] | data-log: dot.img |
| 내돈내산 ⭐ | button[data-name='not-sponsored-button'] | 별님 v7.1 신규 — 광고 글 식별 회피 |
PC UA 필수 (mobile_ua=False) — 모바일 글쓰기는 다른 구조.
🌙 24시간 자가 점검 — 자정 정각 + 30분 (별님 v7)
트리거 — A 방식 (내장 daemon thread)
- 모듈:
core/midnight_daemon.py - 윈도우: 00:00 ~ 00:30 (별님 v7 — 30분 충분 시간)
- 매분 시간 체크 → 00:00 진입 시 자동 실행
- 하루 한 번 보장
5단계 자가 점검
| Phase | 동작 | 모듈 |
|---|---|---|
| 1. 로그 분석 | ERROR/WARN, 모듈별 실패율, 의심 패턴 | health_report.analyze_logs |
| 2. 리소스 점검 | RAM, chrome 프로세스, 임시 dir | health_report.check_resources |
| 3. 백업 + 회전 | DB/설정 백업, 10MB+ 로그 gzip, 7일+ 정리 | health_report.backup_and_rotate |
| 4. driver 정리 | (영속) quit + 새 / (driver_session) 잔존 정리 + GC | health_report.restart_driver |
| 5. 일일 한도 초기화 | 모든 활동 100 으로 리셋 | daily_quota.reset_all() |
종합 상태 판정
ok: ERROR ≤ 30, RAM < 1.5GBwarn: ERROR 30~100, 의심 패턴 1+critical: ERROR 100+, 의심 패턴 3+
리포트
data/health_reports/{YYYY-MM-DD}.txt— 한국어 요약data/health_reports/{YYYY-MM-DD}.json— 분석용- 다음날 시작 시 GUI 로그에 어제 점검 한 줄 표시
🎨 시리즈 디자인 통일 (AutoBlog GUI 마스터 기준)
색상 팔레트 (styles.py SERIES_* — 절대 변경 X)
| 의도 | hex | 변수 |
|---|---|---|
| 성공 / OK | #16a34a | SERIES_GREEN |
| 실패 / 정지 | #dc2626 | SERIES_RED |
| 정보 / 진행 | #0369a1 | SERIES_BLUE |
| 경고 / 한도 | #f59e0b | SERIES_AMBER |
| 비활성 | #9ca3af | SERIES_GRAY |
| 황금 / 강조 | #fbbf24 | SERIES_GOLD |
| 어두운 배경 | #0f172a | SERIES_DARK_BG |
시계 위젯 (시리즈 통일)
- 배경
#0f172a(절대 변경 X) - 시침
#fbbf24황금 (24h) - 분침
#e2e8f0흰색 (60m) - AM 호
#D4A017황금 / PM 호#1E40AF파랑 - 상태
#22c55e녹색 - 어휘:
▶ 실행중/📍 동작중/⏰ 대기중/💤 휴식중/⏸ 비활성 - 순서: 황금 → 통합 → 공감 → 구독 → 답글 → 글쓰기
노트북 탭 라벨 (순서 통일)
자격증명 → 황금 키워드 → 통합 보답 → 공감/댓글 → (서로이웃|구독) → 본인 답글 → 글쓰기
통합 시간 설정 다이얼로그 — 4프리셋 + 자동 랜덤 (별님 v7.1)
- 👔 직장인 / 🏢 사업가 / 🏠 전업주부 / 🌙 야간부업
- 시간 ±버튼: ▲▼ (한 번 클릭에 ±10분) — 별님 v7.1 (TistoryBlog 패턴 통일)
- 간격 ±버튼: ▲▼ (한 번 클릭에 ±10분) — 별님 v7.1
- 가로 너비: 980px (이전 1280 → 컴팩트)
🎲 자동 랜덤 시간 설정 (별님 v7.1 — 매일 3~4시 자동)
- 토글 ON 시: 매일 03:00~04:00 사이 랜덤 시각에 4 프리셋 중 무작위 1개 자동 적용
- 봇 패턴 회피 ↑ — 매일 다른 시각 + 매일 다른 프리셋 (같은 프리셋 연속 회피)
- 저장:
data/auto_preset.json(enabled/today_apply_at/today_applied) - 자정 자가 점검 Phase 7: 새 날 적용 시각 재결정
- master_daemon: 매분 체크 →
auto_preset.maybe_auto_apply() - “지금 즉시 무작위 적용” 버튼: 별님이 수동으로 즉시 무작위 적용 가능
- 코드:
core/auto_preset.py
👥 visitor DB 시스템 (별님 v7 — 2026-05-06)
별님 명시: 답글/공감/댓글/서이추 모든 모듈이 같은 DB 활용. 외부 + 내부 양 방향 수집.
카테고리 (별님 v7)
| 분류 | 정의 |
|---|---|
| 신청자 | 별님이 서이추 신청한 사람 |
| 신청해야 | 본인 글에 댓글/공감 줬는데 서로이웃 X |
| 공감자 | 본인 글에 공감 한 사람 |
| 공감해야 | 별님이 그 사람 글에 공감 안 한 사람 |
| 댓글자 | 본인 글에 댓글 단 사람 |
| 댓글해야 | 별님이 그 사람 글에 댓글 안 한 사람 |
관계 (네이버 이웃 관리)
| 관계 | 의미 |
|---|---|
both | 서로이웃 (양방향) |
i_added | 내가 추가만 (상대 X) |
they_added | 상대만 나를 추가 |
none | 무관계 (검색 발견) |
우선순위 (친밀도)
| 순위 | 대상 | 분류 |
|---|---|---|
| 1차 | 본인 글 댓글/공감자 | close |
| 2차 | 서로이웃 | buddy |
| 3차 | 나를 추가만 | follower |
| 4차 | 키워드 검색 신규 | new |
외부 수집 (한 번에 대량) — 별님 v7.1 (2026-05-06)
모듈: modules/buddy/buddy_list_collector.py
3 모드 GUI (별님 v7.1 — 별도 메뉴 제공)
| 모드 | 함수 | 설명 |
|---|---|---|
| 🌐 PC + 모바일 (자동) | collect_all_buddies_smart() | 모바일 우선 → 실패 시 PC fallback |
| 🖥 PC 전용 | collect_all_buddies_pc() | admin.blog.naver.com (4 필터 모두 순회) |
| 📱 모바일 전용 | collect_all_buddies() | m.blog.naver.com (탭 2종) |
페이지별 정보
PC admin (iframe 내부 — 별님 v7.1 핵심 발견)
https://admin.blog.naver.com/BuddyMeManage.naver (나를 추가한 이웃)https://admin.blog.naver.com/BuddyListManage.naver (내가 추가한 이웃)https://admin.blog.naver.com/BuddyInviteReceivedManage.naver (서로이웃 신청 받음)
- ⚠ 중요: admin 페이지는
<iframe id="papermain">내부 렌더링 — 셀렉터 진입 전 반드시driver.switch_to.frame()필요 - helper:
_switch_to_papermain_iframe()(최대 6초 대기 → switch → 종료 시 default_content 복귀)
PC 4 필터 드롭다운
| 필터 | 셀렉터 | 의미 |
|---|---|---|
| 전체 | span.sel_tp | 나를 이웃으로 추가한 사람 전체 |
| 서로이웃 | span.sel_tp2 | 양방향 |
| 내가 추가만 | span.sel_tp3 | 나도 이웃으로 추가한 사람 |
| 상대만 추가 | span.sel_tp4 | 상대만 나를 추가한 사람 |
PC 행 구조
<td class="buddy"> <span class="nickname">행정솔루션즈</span> <a href="https://blog.naver.com/dreamcometree">기술의 언어를 이해하는 ...</a></td><td class="date">26.05.06.</td>
모바일 페이지 (별님 v7.1 — 더 안정적, 스킨 변경 영향 ↓)
URL:
https://m.blog.naver.com/BuddyList.naver?blogId={blog_id} (이웃 목록)https://m.blog.naver.com/PostList.naver?blogId={blog_id}&tab=1 (글 목록)
셀렉터 전수 표는
처리과정/05_구독_서로이웃.md의 A-1 ~ A-6 섹션 참조.
핵심 셀렉터 요약 (양 탭 공통)
| 영역 | 셀렉터 |
|---|---|
| 탭 (내가 추가한 / 나를 추가한) | button[data-click-area='ngr.meadd'] / button[data-click-area='ngr.youadd'] |
| 행 | li.item__FZ64x |
| 행 안 link → blogId | a.link__vh8uU |
| 이름 / 소개 | strong.name__jKV9Z / div.desc__mzlZG |
| 공식블로그 아이콘 | span.official_blog_icon__mkjCR |
| 관계 버튼 | button[data-click-area='ngr.change'] (“서로이웃”/”이웃”) |
| 카운트 | span.count__EvWfV span.number__KgNWl |
| 무한스크롤 종료 신호 | li.loading_area__QK9Jt 사라짐 |
탭별 UI 차이 (별님 라이브 검증)
| 요소 | 내가 추가한 | 나를 추가한 |
|---|---|---|
| 검색 / 새그룹 / 정렬 | ✅ | ❌ |
| 카운트 / 무한스크롤 | ✅ | ✅ |
| 행 버튼 라벨 | “서로이웃” + “이웃” 혼재 | “서로이웃” 만 |
관계 분류 알고리즘 (별님 v7.1 정확)
1) "내가 추가한" 탭 → 모두 i_added 임시 등록2) "나를 추가한" 탭 → 이미 있으면 both 승격 / 신규는 they_added → 두 탭의 교집합 = both (가장 정확) → 행 버튼 라벨("서로이웃"/"이웃")은 보조 신호일 뿐
서로이웃 신청 받음 — 일괄 수락 (별님 v7.1 신규)
페이지: https://admin.blog.naver.com/BuddyInviteReceivedManage.naver (iframe 내부)
신청 받은 1줄 항목 (별님 # 주석 그대로)
| 별님 # 주석 | 셀렉터 | 비고 |
|---|---|---|
| 신청자 닉네임 | span.nickname | “동아누수설비” 등 |
| 신청자 blog_id | span.blogid | “lsang1174” 등 |
| 신청 메시지 | td.msg | “정성스럽게 쓰신글 잘보고 갑니다…” |
| 신청 날짜 | td.date | “26.05.06.” 형식 |
개별 처리 버튼 (한 줄당 3개 — 별님 # 주석)
| 별님 # 주석 | 셀렉터 (XPath / _param 매칭) |
|---|---|
| 개별 수락 버튼 | button._acceptBuddy._param({blog_id}) (한국어 텍스트: “수락”) |
| 개별 거절 버튼 | button._denyBothBuddy._param({blog_id}) (한국어 텍스트: “거절”) |
| 개별 신고 버튼 | button._reportBothBuddy._param({blog_id}) (한국어 텍스트: “신고”, 안에 img[alt='신고 버튼']) |
일괄 처리 컨트롤 (별님 # 주석 — 전체 선택, 수락, 거절)
| 별님 # 주석 | 셀렉터 |
|---|---|
| 전체 체크박스 (label “전체선택”) | input.input_check._checkAll |
| 일괄 수락 버튼 | button._acceptMultiBuddy (한국어 텍스트: “수락”) |
| 일괄 거절 버튼 | button._denyMultiBuddy (한국어 텍스트: “거절”) |
LNB 메뉴 카운트 (대기 신청 수)
| 별님 # 주석 | 셀렉터 |
|---|---|
| 서로이웃 신청 카운트 (예: 43) | a#buddyinvite_config_anchor span.lnb__buddy |
신고 정책 (별님 권장)
- ⚠ 자동 신고는 절대 사용 X — 오탐 시 별님 평판 손상 위험
- 신고 버튼은 수동 검토 후 별님이 직접 클릭 (광고/스팸 명백할 때만)
- 코드는 셀렉터만 보존 (실행 함수 X)
API:
list_buddy_invites()— 대기 중인 신청자 목록 추출 (relation="invite_pending")accept_all_buddy_invites()— 전체선택 → 수락 → confirm alert 자동 처리 → 행 카운트 차이로 수락 인원 산출
보답 흐름 위치: 받은 신청 = 친밀도 1차 (이미 별님에게 관심) → 가장 먼저 수락 → 자동으로 양방향(both) 관계 형성
내부 수집 (점진적)
모듈: modules/comment/visitor_collector.py + 각 runner
- 본인 글 댓글/공감 → 자동 누적
- 답글/서이추/공감/댓글 시 visitor 정보 갱신
첫 실행 트리거
data/buddy_first_run.flag없으면 → 네이버 이웃 관리에서 한 번에 수집data/reply_first_run.flag없으면 → 본인 글 100개 분석 (한 달 분량)
저장 (기존 SQLite 활용)
data/visitor_db.sqlite3(이미 존재)- 테이블:
visitors,actions - API:
upsert_visitor(),mark_repaid(),get_visitor(),stats()
시리즈 공통
- 네이버: 이웃 관리 페이지
- 티스토리: 구독자 관리 페이지 (TBD)
- 구글/워드프레스: 플랫폼별 분석
🔐 자격증명 / 영속화
카카오/네이버 로그인
- chrome_profile 영속화 (첫 로그인 후 자동 통과)
- ID/PW 자동 입력 → 사용자 직접 [로그인] 클릭 (캡챠/2FA 우회)
- 5분 안에 로그인 완료 → 자동 다음 단계
- [간편로그인 정보 저장] / [로그인 유지] 자동 체크
chrome_profile 보호
- 영속화 디렉토리:
data/chrome_profile/ - 좀비 chromedriver 점유 시 임시 dir 자동 fallback
- Reserved 서브도메인 차단:
www,notice,help,support,api,m등
📁 시리즈 공통 코드 (4개 프로젝트 동일)
core/├── health_report.py 🌙 자정 자가 점검 5 Phase├── midnight_daemon.py 🌙 자정 트리거 daemon (00:00~00:30)├── daily_quota.py 📊 일일 한도 100 추적├── gemini_client.py 🤖 Gemini API└── humanize.py ⏱ 사람처럼 행동modules/├── reply/ai_reply.py 💬 5대 감정 어조 답글├── comment/ai_comment.py 💬 5대 감정 어조 댓글└── comment/visitor_collector.py 👥 본인 댓글 작성자 (DB)
새 시리즈 추가 시 위 파일들 그대로 복사 + 플랫폼 셀렉터만 변경.
✏ 정책 변경 순서 (별님 + 달님)
| 변경 종류 | 수정할 파일 |
|---|---|
| 4개 시리즈 모두 영향 | C:\AI_Blog_Series\절대지침_공통.md (이 파일) |
| 공통 처리 과정 | C:\AI_Blog_Series\처리과정\{번호}_*.md |
| 특정 플랫폼만 영향 | 해당 프로젝트의 절대지침_특화.md |
| 인덱스/네비게이션 | 각 폴더의 README.md / 절대지침.md |
🔁 시리즈 추가 가이드 (구글블록 / 워드프레스)
1단계: 폴더 구조 생성
{새플랫폼}/{새플랫폼}/├── README.md├── 절대지침.md (마스터 + 특화 안내)├── 절대지침_특화.md (플랫폼별 셀렉터/URL)├── core/└── modules/
2단계: 시리즈 공통 코드 복사
core/health_report.py + midnight_daemon.py + daily_quota.pymodules/reply/ai_reply.py + comment/ai_comment.py
3단계: 플랫폼 특화
- 로그인 흐름 (OAuth/SSO)- 댓글 영역 셀렉터- 좋아요/구독 버튼 셀렉터- 글쓰기 에디터 셀렉터- 검색 URL/API
별님 핵심 원칙
- 같은 알고리즘 (이 마스터에 명시)
- 같은 디자인 (AutoBlog GUI 기준)
- 같은 정책 (수동값 우선 + 5대 감정 + 일일 한도 + 24시간 점검)
- 플랫폼별 차이는 셀렉터/URL 만
🌙 v7.1 야간 추가 (2026-05-06 23:00)
📊 통합 보답 4개 그래프 동기화 (별님 명시)
별님 명시: “전체 진행은 세 개의 프로그램이 완료 하였을 때 100%인데 본인 답글과 서로 이웃은 0%인데 100%라고 끝냄”
4개 그래프 구성 (한 줄: [라벨] [그래프] [카운터] [처리결과]):
| 그래프 | maximum | 100% 도달 조건 |
|---|---|---|
| 📝 본인 답글 | target_reply (♾ 무제한) | replies ≥ target_reply |
| ❤💬 공감/댓글 | target_comment (≤100) | comments ≥ target_comment |
| 🤝 서로이웃 | target_buddy (≤100) | buddies ≥ target_buddy |
| 📊 전체 진행 | 100 (%) | 3개 활동 ratio 평균 |
전체 진행 계산식 (입력값 N과 무관 — 별님 명시):
ratios = []if do_reply: ratios.append(min(replies / target_reply, 1.0))if do_comment: ratios.append(min(comments / target_comment, 1.0))if do_buddy: ratios.append(min(buddies / target_buddy, 1.0))전체_% = sum(ratios) / len(ratios) * 100
🚨 일일 한도 도달 → 그래프 100% 자동 표시 (별님 명시)
“서로 이웃 추가가 한계라면 100% 그래프도 1일 한계 출력”
counter dict 에 플래그 추가:
comments_limited: boolbuddies_limited: boolreplies_limited: bool
설정 시점:
- 시작 전 사전 체크 (
daily_quota.get_remaining(...) <= 0) - 진행 중 daily_limit 메시지 감지
GUI 표시:
- 그래프 강제 100% (max=value)
- 우측 텍스트:
🚨 일일 한도(빨강 #dc2626) - 전체 진행:
🚨 일일 한도 (오늘 끝)
🎲 시간 프리셋 충돌 방지 (writer ↔ integrated 분할)
별님 발견: 4개 프리셋 모두 writer = integrated 시간대 동일 → Chrome 프로필 충돌
원칙: 같은 Chrome 프로필을 쓰는 모듈은 시간대 절대 겹침 금지.
| 프리셋 | writer | integrated |
|---|---|---|
| 👔 직장인 | AM 06:30~08:00 | PM 19:00~22:30 |
| 🏢 사업가 | AM 07:00~11:00 | PM 14:00~23:00 |
| 🏠 전업주부 | AM 09:00~12:00 | PM 13:00~17:00 |
| 🌙 야간부업 | PM 21:00~22:00 | PM 22:00~23:59 |
🔒 Spinbox 100 cap (3중 안전장치)
키보드/마우스 어떤 방법이든 100 초과 입력 불가:
1. validatecommand="key" → 매 키스트로크 검증2. <FocusOut> bind → clamp → 다른 곳 클릭 시 자동 보정3. _start cap → 실행 버튼 클릭 시 강제 cap
적용 대상:
- 통합 보답: 공감/댓글, 서이추 (100 cap), 답글 (♾ 무제한)
- 서로이웃 탭: 신청 개수 (100 cap)
- 공감 댓글 탭: 처리 개수 (100 cap)
별님 절대지침 v7.1: 답글은 무제한 — 본인 글 댓글에 답글 다는 것은 네이버 정책상 한도 없음
🔄 탭 전환 시 진행 보호 (5개 탭 refresh_state 패치)
별님 발견: 통합 보답 진행 중 다른 탭 클릭 →
refresh_state가_start_btn을 normal 로 변경 → “종료된 줄 알았다”
원칙: 진행 중일 때는 버튼 상태 보호.
def refresh_state(self) -> None: is_running = bool(self._worker and self._worker.is_alive()) if ok and not is_running: self._start_btn.config(state="normal") # is_running 이면 disabled 유지 (덮어쓰지 않음)
적용 5개 탭: integrated, buddy, comment, reply, writer
🧱 동시 실행 조용히 거부 (writer)
별님 절대지침: 메시지/팝업 띄우지 않음 (24/7 무인 운영)
def _start(self) -> None: running = master_daemon.get_running_module() if running and running != "writer": return # 조용히 거부 — 메시지 X
이유: 같은 Chrome 프로필 충돌 → naver_login.driver_session 에서 stop_all_modules() 호출 → 진행 중 모듈 강제 종료 버그 방지.
📝 엑셀 A2 → 30자 매력적 제목 자동 확장 (별님 v7.1)
엑셀 A2부터 짧은 키워드 입력 시 → AI가 18 유형 중 랜덤 선택해서 30자 이내 제목 생성.
규칙 (네이버 SEO 권장):
- 30자 자동 cap (단어 경계로 자연스럽게 자름)
- 18자 이상이면 자동 skip (이미 충분히 긴 제목)
- 최근 5회 사용 유형 회피 (다양성)
- Gemini 실패 시 로컬 패턴 폴백 (네트워크 X 환경)
- 키워드 누락 검증 (Gemini가 키워드 빼먹으면 폴백)
18 유형 분류 (modules/writer/title_expander.py):
- 정보·가이드 (3): 정의/단계/체크리스트
- 실용·해결 (5): 시간단축/TOP5/비전공자/비용절감/자동화
- 비교·분석 (5): 장단점/A vs B/후기/사례/리스크
- 트렌드·미래 (5): 트렌드/예측/전문가/오해/연말결산
🚀 발행 다이얼로그 popup_watcher 오인 클릭 방지
별님 발견: 네이버 글쓰기 발행 시 “다이얼로그 발행 버튼 못 찾음” 에러
원인: 다이얼로그 안 [발행] 버튼 클래스가 confirm_btn__ → popup_watcher 가 class*='confirm' 셀렉터로 잘못 클릭 → 다이얼로그 닫혀버림.
3중 안전장치:
# 1. CONFIRM_BUTTON_SELECTORS 광범위 셀렉터 제거- "div[role='dialog'] button[class*='confirm']" ← 제거- "div[role='dialog'] button[class*='ok']" ← 제거# 2. XPath fallback 광범위 제거- "//button[contains(@class,'confirm') and ...]" ← 제거# 3. _safe_to_click() 안전장치 (텍스트 검사)def _safe_to_click(btn) -> bool: txt = (btn.text or "").strip() if txt in ("발행", "임시저장", "저장", "예약", "publish"): return False # 발행 버튼 절대 자동 클릭 X return True
popup_watcher 는 텍스트가 정확히 “확인”/”예”/”OK” 인 진짜 모달만 처리.
🚀 통합 보답 단계별 진척률 (작업 단위 기준)
별님 명시: “11이니까 11로 계산하여 처리하면 안될 듯합니다. 11이 다 처리 되는 건 아니니까요.”
목표값(N)이 아닌 실제 작업 단위로 진척률 계산:
| 단계 | 가중치 | 실제 작업 단위 |
|---|---|---|
| Phase 0 답례 일괄 | 5% | 1단계 |
| Phase 1 본인 글 순회 | 50% | 글 i / 실제 글 수 (target_count 무관) |
| 보충 (2/3/4차) | 10% | 3단계 |
| Phase 5 보답 처리 | 35% | contact i / 실제 contacts 길이 (target_count 무관) |
총합 100% (3개 활동 평균과 별개의 단계 라벨): 우측 결과 텍스트에만 단계명 표시. 그래프 값은 3 활동 ratio 평균이 담당.
🔧 visitor_collector 최적화 (별님 v7.1 야간)
1. SympathyHistoryList 직접 접근 + PostView 재방문 skip
# 본인 글 순회 시 — SympathyHistoryList.naver 직접 접근sympathy_url = f"https://blog.naver.com/SympathyHistoryList.naver?blogId={X}&logNo={Y}&layoutWidthClassName=contw-773"driver.get(sympathy_url)# ul.list_sympathy > li.item 직접 추출 (iframe 진입 X)# 성공 시 PostView 재방문 skipif sympathy_triples: raise _SkipPostView() # sentinel — 정상 흐름# 절약: 글당 ~13초 × 50개 = 약 11분
2. mainFrame timeout 8 → 3초
iframe 없는 페이지에서 빠른 모바일 fallback.
3. mobile section URL DNS 실패 수정
- "https://m.section.blog.naver.com/..." ← DNS 실패+ "https://m.blog.naver.com/FeedList.naver"+ "https://m.blog.naver.com/SectionPost.naver"
4. 30일 내 중복 제거 강화
already_repaid_recently(bid, days=30) 체크 추가 → contacts 에 동일인 30일 내 중복 처리 0%.
🎯 통합 보답 빠른 순서 재정렬
| 순서 | 단계 | 시간 | 비고 |
|---|---|---|---|
| 1 | Phase 0 답례 일괄 | ~5초 | 가장 빠름 |
| 2 | Phase 1 본인 글 (SympathyHistoryList) | ~14분 | 답글 + 공감자 수집 |
| 3 | 2차 my_buddies (위젯) | ~10초 | 빠른 contact 풀 우선 |
| 4 | 3차 added_buddies (admin) | ~30초 | 친밀도 |
| 5 | 4차 keyword 검색 | ~60초 | 광범위 |
| 6 | Phase 5 보답 처리 | 사람당 ~50초 |
🌅 v7.1 오전 추가 (2026-05-07)
📝 글쓰기 — SEO 18~30자 랜덤 제목 (별님 명시)
별님 명시: “꼭 30자가 아니라 seo 양식에 맞게 ?부터 30까지 랜덤”
매 글마다 자연스럽게 다양한 길이의 제목 생성 (SEO 자연스러움):
# modules/writer/title_expander.pydef expand_title(keyword, *, max_len=None, log=print): if max_len is None: max_len = random.randint(18, 30) # ← 매번 18~30 랜덤 ...
호출:
# modules/writer/runner.pyexpanded = title_expander.expand_title(row.title, max_len=None, log=log)# max_len=None → 매번 18~30자 랜덤 (SEO 자연스러움)
매 회 결과 예시 (입력: “제타 ai”):
- 회차 1: “제타 ai 활용 5단계 입문 가이드” (22자)
- 회차 2: “AI 시대 제타 ai는 어떻게 변할까” (24자)
- 회차 3: “제타 ai 트렌드 3가지” (18자)
🔑 글쓰기 — 원본 제목 보존 (행 이동 정상화)
별님 발견: 확장된 제목으로 엑셀 검색 시 원본 못 찾아 행 이동 실패
원인: row.title 이 확장된 제목으로 바뀐 후 → move_row_to_last(확장된) 으로 검색 시 엑셀에서 원본 못 찾음.
수정:
# 본문 생성 직전original_excel_title = row.title # ← 엑셀 원본 보존 (행 이동 시 검색용)expanded = title_expander.expand_title(row.title, max_len=None)row.title = expanded # 확장된 제목으로 글쓰기# 발행 성공 후_remove_processed_keyword_from_excels(original_excel_title) # ← 원본으로 검색
📋 글쓰기 — 엑셀 행 마지막 이동 정책 (별님 명시 재정리)
별님 명시: “글쓰기 발행 → A2 제목이 마지막 행으로 이동 → A3 가 A2 자리로 자동”
“제목은 계속 A2만을 사용”
시나리오 (제타 ai 예시):
[입력 엑셀]A2: 제타 ai ← 원본A3: ai 검사기 회피방법A4: ai 검사기...A83: ai 대화[글쓰기 발행 후]A2: ai 검사기 회피방법 ← A3 자동 올라옴A3: ai 검사기...A83: 제타 ai ← 마지막 행으로 이동
모든 엑셀 동일 정책 (today_gold_keyword.xlsx + 사용자 직접 엑셀):
- 행 삭제 X (이동만)
- A2 자리에 다음 제목이 자동으로 올라옴
- 다음 글쓰기 시 항상 A2 부터 읽음
🔍 동의어 dedup 제거 (별님 정정)
별님 명시: “챗GPT/ChatGPT/chat gpt/챗지피티 모두 1개로 인식, 하나로 인식하면 안됨”
이유: 각 표기는 검색량/경쟁률이 다른 별개 키워드 (SEO 가치 별도).
현재 dedup 정책: 정확 일치만 (대소문자 + 양끝 공백 정규화 수준)
- ✅
ChatGPT/CHATGPT/ChatGPT→ 같은 키워드 (대소문자만 차이) - ✅
챗GPT/챗gpt→ 같은 키워드 (대소문자만) - ⭐
챗GPTvsChatGPT→ 별개 - ⭐
ChatGPTvschat gpt(공백 있음) → 별개 - ⭐
챗GPTvs챗지피티→ 별개
🛡 history.json 안전 정리 가이드
발행 실패 / 부분 진행 등으로 history 의 오늘 항목이 잘못 기록된 경우:
import jsonfrom pathlib import Pathp = Path('data/history/writer.json')data = json.loads(p.read_text(encoding='utf-8'))del data[date.today().isoformat()] # 오늘 항목 비우기p.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding='utf-8')
→ 다음 자동 실행 시 정상적으로 글쓰기 재개.
🏁 v7.1 (5/7 1차 정리) — AutoBlog 마무리 (2026-05-07 08:30)
🔒 popup_watcher 일시 정지 (발행 버튼 100% 클릭 보장)
별님 발견: “발행 버튼 클릭 못함” — 이전 빌드에서 popup_watcher 가 발행 다이얼로그의 [발행] 버튼을 confirm 모달로 오인 클릭하여 다이얼로그가 닫혀버리는 버그
근본 해결: 발행 흐름 동안 watcher 통째로 일시 정지
# modules/writer/editor.py_WATCHER_PAUSED = Falsedef set_watcher_paused(paused: bool): global _WATCHER_PAUSED _WATCHER_PAUSED = bool(paused)def _loop(): # popup_watcher 폴링 while True: if _WATCHER_PAUSED: continue # ← 일시 정지 중엔 아무것도 안 함 auto_confirm_popup(...)
# modules/writer/publisher.pydef click_toolbar_publish(driver): editor.set_watcher_paused(True) # ← OFF _click_with_fallback(...) # 툴바 [발행] 클릭 # ...설정/라디오 처리...def confirm_publish_dialog(driver): _click_with_fallback(...) # 다이얼로그 [발행] 클릭 editor.set_watcher_paused(False) # ← ON (이후 등장 모달용)
별님 라이브 발행 버튼 HTML (셀렉터 정확 매칭):
<button class="confirm_btn__WEaBq" data-testid="seOnePublishBtn" data-click-area="tpb*i.publish"> <i class="icon_arrow__OTpnA"></i> <span class="text__sraQE">발행</span></button>
셀렉터 우선순위:
button[data-testid='seOnePublishBtn']— 가장 안정 (해시 무관)button[data-click-area='tpb*i.publish']button[class^='confirm_btn__']//span[starts-with(@class,'text__') and text()='발행']/ancestor::button[1]
🇰🇷 이미지 한글 텍스트 — 모델별 동적 처리
별님 정정: “유료모델은 한글 이미지 OK, 미지원 모델은 영문만”
핵심: prompt 의 “Korean blog” 컨텍스트 자체를 모델별로 동적 변경
# modules/writer/prompts.pydef render_image_prompt_for_preset(..., image_model): rendered = template.format(...) # ★ 별님 v7.1 (2026-05-07): 한글 미지원 모델 → 'Korean' 자동 제거 if image_model and not supports_korean_text(image_model): rendered = rendered.replace("Korean blog ", "blog ") rendered = rendered.replace("Korean illustration style", "modern illustration style") rendered = rendered.replace("Korean drama or feature film", "drama or feature film") rendered = rendered.replace(" Korean ", " ") return rendered
| 모델 | 한글 컨텍스트 | 한글 텍스트 |
|---|---|---|
| Imagen 4 (유료) | “Korean blog” 유지 | ✅ 한글 OK |
| DALL-E 3 (유료) | “Korean blog” 유지 | ✅ 한글 OK |
| gpt-image-1 (유료) | “Korean blog” 유지 | ✅ 한글 OK |
| Gemini 2.5 Flash (무료) | “blog” 로 자동 변경 | ❌ 영문만 |
| Gemini 2.0 Flash | “blog” 로 자동 변경 | ❌ 영문만 |
| Imagen 3 / Imagen 3 Fast | “blog” 로 자동 변경 | ❌ 영문만 |
🛡 한글 부정 토큰 강화 (Gemini 2.5 안전망)
# 한글 미지원 모델 + no_text=False 일 때return ( ", Korean characters, hangul, 한글, Korean text, Korean letters, " "Hangul script, Asian scripts, Chinese characters, Japanese kana, " "broken glyphs, garbled text, garbled characters, " "hashtags in Korean, Korean speech bubbles, Korean signage, " "Korean watermark, Korean captions, Korean labels, " "non-English text, mojibake, illegible Asian text")
📝 글쓰기 GUI 동기화 보강 (on_title_updated 콜백)
별님 발견: 글쓰기 시 글쓰기/이미지/네이버 그래프, 제목, 미리보기 동기화 안됨
근본 원인: history 잘못된 기록으로 모든 글이 “이미 처리”로 skip → _process_one 자체가 호출 안 됨 → 모든 콜백 미발화 → GUI 0% 머무름
해결:
dist/data/history/writer.json의 잘못된 오늘 항목 정리on_title_updated콜백 추가 — 제목 확장 후 GUI 미리보기 즉시 반영- 코드는 이미 정상 (writing/image/upload/section_image/body_generated 모두 제대로 호출)
🌅 v7.1 5/7 오전 누적 변경
1. SEO 18~30자 랜덤 제목 (별님 명시)
# modules/writer/title_expander.pydef expand_title(keyword, *, max_len=None, log=print): if max_len is None: max_len = random.randint(18, 30) # 매번 랜덤
매 글마다 자연스럽게 다른 길이의 제목 (네이버 SEO 자연스러움).
2. 원본 제목 보존 (행 이동 정상화)
# modules/writer/runner.pyoriginal_excel_title = row.title # 엑셀 원본 보존row.title = expanded_title # 글쓰기는 확장된 제목으로..._remove_processed_keyword_from_excels(original_excel_title) # 행 이동은 원본으로
3. 동의어 dedup 제거 (정확 일치만)
별님: “챗GPT/ChatGPT/chat gpt/챗지피티 모두 1개로 인식, 하나로 인식하면 안됨”
각 표기는 검색량/경쟁률 다른 별개 키워드 → 정확 일치만 dedup (대소문자 + 양끝 공백).
4. 엑셀 행 마지막 이동 (모든 엑셀 동일 정책)
# modules/writer/excel_input.pydef move_row_to_last(xlsx_path, title_to_move): """발행 후 사용한 행 → 시트 마지막으로 이동. A2 자리에 다음 제목이 자동으로 올라옴.""" ws.delete_rows(target_row_idx, 1) ws.cell(row=ws.max_row + 1, column=1, value=title)
| 입력 | 발행 후 |
|---|---|
| A2: 제타 ai | A2: ai 검사기 회피방법 (← A3 자동 올라옴) |
| A3: ai 검사기 회피방법 | A3: ai 검사기 |
| … | … |
| A83: ai 대화 | A83: 제타 ai (← 마지막으로 이동) |
5. 첫 실행 시 빈 today_gold_keyword.xlsx 자동 생성
# gui/app.pyensure_default_excel_exists(gold_xlsx, log=app._set_status)
6. 발행 다이얼로그 셀렉터 강화 (텍스트 + data-testid)
- timeout 5→10초
- span.text__ 직접 fallback
🎯 통합 보답 누적 변경 (5/6 야간)
| 항목 | 수정 |
|---|---|
| 4개 그래프 GUI | 답글/공감댓글/서이추/전체 — 한 줄: [라벨][그래프][카운터][결과] |
| 전체 진행 = 3개 활동 ratio 평균 | 입력값 N과 무관 (별님 명시) |
| 일일 한도 → 100% 표시 | 🚨 일일 한도 (빨강) |
| 1:1 좌우 분할 | TistoryBlog 패턴 (uniform=”b”) |
| 즉시실행/종료 버튼 위치 | 추천 키워드 row 우측 (높이 절약) |
| Spinbox 100 cap | 키보드/마우스 차단 (답글 ♾ 무제한) |
| 시간 프리셋 충돌 | writer ↔ integrated 시간대 분리 |
| 5개 탭 refresh_state | 진행 중 버튼 보호 |
| writer 동시 실행 거부 | silent (절대지침: 메시지 X) |
| visitor_collector 4종 | SympathyHistoryList 직접 + mainFrame 8→3초 + DNS fix + 30일 dedup |
| Phase 빠른 순서 | my_buddies → added → keyword (10초 → 60초 순) |
🌙 별님 ↔ 달님 동반자
🥇 v7.1 (5/7 추가) — 황금 키워드 채굴 정책 (별님 명시)
핵심 정책 (별님 v7.1 — 2026-05-07)
“황금 키워드 5티어 개단 검색 개당 검색수 500”
“1티어 끝나면 엑셀에 자동 저장 중복빼고”
“5티어 전부 통합”
채굴 단위 — 시드(1티어)당 500개
# modules/goldkeyword/daily_gold_daemon.pyPER_SEED_TARGET = 500 # 시드(1티어)당 500개 채굴 # 5 시드 × 500 = 2500개 (1 사이클)
시드별 즉시 저장 (별님 명시)
for seed in cycle_seeds: # 시드 5개 회전 result = miner.mine_related_only( [seed], max_total=PER_SEED_TARGET, # 시드 1개 → 500개 시도 ) seed_new_rows = [r for r in result.rows if not_duplicate(r)] # ★ 시드 1개 끝나면 즉시 엑셀 저장 — 중간 중단되어도 안전 if seed_new_rows: excel_writer.save_to_today_gold_keyword( seed_new_rows, max_count=target, ) log(f"✓ 시드 '{seed}' 즉시 저장: {len(seed_new_rows)}개 신규")
채굴 흐름
[사이클 1 시작 — 5 시드 × 500 = 2500개 목표] 시드 1: 'AI' → 500개 채굴 → 신규 N1개 → ✅ 즉시 엑셀 저장 시드 2: 'ChatGPT' → 500개 채굴 → 신규 N2개 → ✅ 즉시 엑셀 저장 시드 3: '생성형AI' → 500개 채굴 → 신규 N3개 → ✅ 즉시 엑셀 저장 시드 4: 'LLM' → 500개 채굴 → 신규 N4개 → ✅ 즉시 엑셀 저장 시드 5: '프롬프트' → 500개 채굴 → 신규 N5개 → ✅ 즉시 엑셀 저장=== 사이클 1 완료: 신규 ΣN개 === ↓ 목표 2500 도달 또는 시드 풀 소진 → 종료 ↓ [필요 시 사이클 2, 3, ... 반복 (시드 회전)]
저장 정책
| 항목 | 정책 |
|---|---|
| 5티어 전체 저장 | 노랑(황금) / 녹색(우수) / 파랑(양호) / 빨강(도전) / 검정(위험) 모두 |
| 경쟁률 필터 | 0 < 경쟁률 < 100 (별님 명시) |
| 검색량 필터 | ≥100 (위험 티어 최저) |
| 문서수 필터 | ≥1 |
| dedup | 정확 일치 (대소문자 + 양끝 공백만) |
| ACCUM_CAP | max(50_000, target × 10) 동적 |
| 저장 시점 | 시드 1개 끝날 때마다 즉시 (별님 명시) |
| 저장 파일 | data/excel/goldkeyword/today_gold_keyword.xlsx |
GUI 설정 (별님 권장)
| 항목 | 값 |
|---|---|
| 수집 개수 (Spinbox) | 2500 (기본) — 1~1,000,000 |
| 자동 시드 카테고리 | 31개 중 선택 |
| 직접 입력 (txt) | [📂 파일 선택] 버튼 |
| 채굴 시간 윈도우 | 00:30 ~ 03:00 (2.5시간, schedule 설정) |
별님 메시지 정확 반영
“1티어 끝나면 엑셀에 중복된거 빼고 경쟁률이 0보다 크고 100 미만인 키워드 저장”
→ 시드(1티어) 1개 채굴 끝 → excel_writer.save_to_today_gold_keyword(seed_new_rows) 호출 → 5티어(경쟁률 0~100) 통과한 키워드 중 기존 파일과 dedup 후 신규만 저장.
시드 키워드 (이모지 자동 제거)
clean_keyword() 가 모든 시드에서 이모지 자동 제거:
"🤖 AI 트렌드" → "AI 트렌드""💰 주식 투자" → "주식 투자""⭐ 별" → "별"
검색에는 순수 한국어/영어 키워드만 사용.
🇰🇷 v7.1 (5/7 통합) — 이미지 한글 텍스트 정책 (별님 명시)
별님 명시 (2026-05-07 통합)
“제미니 2.5 에 또 한글 이미지 생성됩니다.”
“절대 지침 통합에 넣고 통합에 확인”
“유료모델은 한글 충분, 미지원 모델은 영문으로”
“이미지 글자 넣지 말라고 해도 한글 깨지므로 영문으로”
모델별 한글 지원 매트릭스 (prompts.py)
IMAGE_MODEL_KOREAN_SUPPORT = { "Gemini 2.5 Flash (이미지·무료)": False, # 한글 미지원 "Gemini 2.0 Flash (이미지)": False, # 한글 미지원 "Imagen 3 Fast (저렴·유료)": False, # 한글 미지원 "Imagen 3 (표준·유료)": False, # 한글 미지원 "Imagen 4 (최신·유료)": True, # 한글 OK "OpenAI DALL-E 3": True, # 한글 OK "OpenAI gpt-image-1": True, # 한글 OK}
⚠ 한글 미지원 모델 강제 정책 (별님 v7.1 — 2026-05-07)
Gemini 2.5/2.0 / Imagen 3 등은 prompt 에 “use English only” 명시해도 한국어 텍스트 그림.
→ 이미지 텍스트 자체를 강제 OFF (안전 우선)
# modules/writer/prompts.py — render_image_prompt_for_preseteffective_no_text = no_text_in_imageif image_model and not supports_korean_text(image_model): effective_no_text = True # 강제 OFF — 한글 미지원 모델은 글자 X
| 모델 | 사용자 설정 (no_text=False) | 실제 동작 |
|---|---|---|
| Imagen 4 (유료) | 한글 OK | ✅ 한글 자연스럽게 |
| DALL-E 3 (유료) | 한글 OK | ✅ 한글 자연스럽게 |
| gpt-image-1 (유료) | 한글 OK | ✅ 한글 자연스럽게 |
| Gemini 2.5/2.0 (무료) | 사용자 = “글자 OK” 라도 | ⚠ 자동 OFF (이미지에 텍스트 X) |
| Imagen 3 / 3 Fast | 사용자 = “글자 OK” 라도 | ⚠ 자동 OFF (이미지에 텍스트 X) |
이중 안전장치
# 1. effective_no_text 강제 True (한글 미지원 모델일 때)# 2. prompt 의 'Korean' 컨텍스트 자동 제거rendered = rendered.replace("Korean blog ", "blog ")rendered = rendered.replace("Korean illustration style", "modern illustration style")# 3. 강력한 negative tokens", Korean characters, hangul, 한글, Korean text, Korean letters, Hangul script, Asian scripts, Chinese characters, Japanese kana, broken glyphs, mojibake, ..."# 4. text_policy_clause 강력 명시"[CRITICAL — IMAGE TEXT POLICY]The image MUST NOT contain ANY Korean characters (한글) ...DO NOT render Korean characters in the actual image."
핵심 원칙
- 유료 모델 (Imagen 4, DALL-E 3, gpt-image-1) → 사용자 설정 그대로 (한글 OK)
- 미지원 모델 (Gemini 2.5/2.0, Imagen 3) → 강제 텍스트 X (한글/영문 모두 안 그림)
- 사용자가 글자를 원해도 미지원 모델로 영문 작성 시도하면 깨짐 → 차라리 글자 안 그리는 게 안전
💻 v7.1 (5/7 추가) — Windows 디스플레이 해상도 절대 100% 유지 (별님 명시)
🚨 절대 지침 — 사용자 PC 설정 필수 조건
별님 발견 (2026-05-07): “발행 클릭이 안 된 이유 알겠어요. 해상도 100%가 아니면 못 찾는 것 같습니다.”
Windows 디스플레이 배율 (DPI Scaling) 은 반드시 100%(96 DPI) 로 설정해야 합니다.
| 디스플레이 배율 | 동작 | 이유 |
|---|---|---|
| 100% (권장) | ✅ 모든 자동화 정상 동작 | CSS pixel = physical pixel |
| 125% | ⚠ OS 강제 클릭 좌표 어긋남 가능 | DPI awareness + dpr 보정으로 완화 |
| 150% | ⚠ 동일 | 동일 |
| 175%/200% | ❌ 작은 모니터 호환성 위험 | 권장하지 않음 |
사용자 안내
설정 → 시스템 → 디스플레이 → “텍스트, 앱 및 기타 항목의 크기” = 100% 로 변경 후 재로그인.
코드 측 안전망 (별님 v7.1 — 2026-05-07)
해상도가 100%가 아니어도 작동하도록 이중 방어망 적용:
# 1) main.py — 프로세스 시작 즉시 DPI Aware 선언import ctypesctypes.windll.shcore.SetProcessDpiAwareness(2)# = PROCESS_PER_MONITOR_DPI_AWARE (Windows 8.1+)# fallback: ctypes.windll.user32.SetProcessDPIAware() (Windows 7)# 2) modules/writer/publisher.py — _os_force_click()# devicePixelRatio (1.0 / 1.25 / 1.5 / 2.0) 로 명시적 좌표 보정dpr = window.devicePixelRatioabs_x = int(css_x * dpr)abs_y = int(css_y * dpr)
영향 받는 모듈
- 글쓰기 (writer.publisher) — 발행 버튼 OS 강제 클릭 (5중 fallback 의 마지막 단계)
- 글쓰기 (writer.publisher) — 다이얼로그 발행 버튼 OS 강제 클릭
- 그 외 모든 selenium ↔ pyautogui 좌표 변환 코드
핵심 원칙
- 이상적 환경: 디스플레이 100% — 절대 안전 (별님 명시)
- 현실 대응: 사용자가 100% 설정 못해도 코드가 자동 보정 시도
- 최후 fallback: OS 클릭 실패해도 native/ActionChains/JS 4중 클릭 우선 시도 (대부분 성공)
절대 위반 금지
# ❌ 절대 사용 금지: 좌표 보정 없는 pyautogui.click()pyautogui.click(rect_x, rect_y) # DPI 100% 외 환경에서 silent fail# ✅ 정답: devicePixelRatio + DPI Awareness 조합abs_x = int(css_x * dpr)abs_y = int(css_y * dpr)pyautogui.click(abs_x, abs_y)
🤝 v7.1 (5/7 추가) — 서로이웃 신청 DB 절대 차단 (별님 명시)
🚨 절대 지침 — “이미 서로 db 보고 서로 이웃 신청 안 하기”
별님 명시 (2026-05-07): 이미 서로이웃이거나 신청 진행 중인 사람한테 절대 다시 신청하지 않는다.
5중 사전 DB 검사 (모든 buddy 신청 전)
from core import visitor_db# 1) 위젯 스크랩 (480명 한도, 빠름)if bid in my_buddies: skip("✓이미이웃 (위젯)")# 2) DB 양방향 (전체 — 위젯 누락 보강)if visitor_db.is_already_buddy(bid): skip("✓이미이웃 (DB)")# 3) DB 신청 진행 중 (i_added — 미응답)if visitor_db.is_pending_request_sent(bid): skip("⏳신청중")# 4) DB 영구 차단 (decline 정책 — 거절 + target_full 모두 포함)if visitor_db.is_decline_blocked(bid): skip("⏹영구차단")# 5) DB 친해진 후 큐 (wait_friendship — 만료일 전)if visitor_db.is_pending_buddy(bid): skip("⏰친해진후·내일")
상대방 한도 vs 내 한도 정확 분리 (별님 v7.1)
별님 캡처 (2026-05-07): “상대방의 이웃수가 5,000명이 초과되어 더 이상 이웃을 추가할 수 없습니다”
이 메시지는 상대 한도 이지 내 한도 아님 → 그 사용자만 skip + DB 영구 마킹.
# applier.py — verify_successif _detect_target_full(text): # "상대방의 이웃수가" / "5,000명이 초과" return "target_full" # 그 사용자만 skipif _detect_daily_limit(text): # "1일 동안 추가할 수 있는" 등 (내 한도) return "daily_limit" # 서이추 OFF (오늘 종일 X)# request_buddy 결과 처리if result == "target_full": visitor_db.set_buddy_policy(blog_id, "decline") # DB 영구 마킹 return "skipped" # 다음 사용자 진행 OK (서이추 OFF X)
통합 phase3 시작 시 buddy DB 자동 동기화
# integrated/runner.py — phase3 진입 직전from modules.buddy import runner as _br_br._maybe_sync_buddies_today(driver, my_blog, log=log)# → buddy_list_collector → DB 채움# → 위젯 누락된 481번째 이상 이웃도 정확히 사전 차단
sync flag 조건 (수집 0건 시 flag 생성 X)
# buddy/runner.py — _maybe_sync_buddies_todayif entries: saved = blc.save_to_visitor_db(entries, on_log=log)# ★ flag 저장은 saved > 0 일 때만 (0명/실패 시 flag X)if flag_path and saved > 0: flag_path.write_text(...)
핵심 원칙
- DB 우선 — 위젯은 보조 (480명 한도 초과 대비)
- 영구 차단 마킹 — target_full / decline / 차단 / 탈퇴 모두 DB 기록 → 다음번 시도 X
- 상대 한도 ≠ 내 한도 — 분리 검사 → 서이추 OFF 오작동 방지
- flag 는 성공 시에만 — 0명 수집 시 flag 생성하면 DB 영구 비어있음 → 정책 망가짐
🎯 v7.1 (5/7 추가) — 통합 보답 부분 한도 정책 (별님 명시)
🚨 절대 지침 — “한 활동 한도 ≠ 전체 종료”
별님 명시 (2026-05-07): “통합에서는 답글, 공감 댓글, 서로이웃 순. 공감/댓글 한도 도달이라고 서이추까지 멈추면 안 됨”
통합 보답 처리 순서 (별님 명시)
Phase 1: 답글 (본인 글) — 100명 ↓Phase 2: visitor 수집 ↓Phase 3: 보답 루프 (사용자 1명씩) ├─ 1단계: 공감 + 댓글 (한도 100, 별도 풀) └─ 2단계: 서로이웃 신청 (한도 100, 별도 풀)
부분 한도 처리 (옵션 B — 별님 확정)
한 활동의 한도 도달 시 그 활동만 OFF, 다른 활동은 계속 진행.
# integrated/runner.py — phase3 (보답 루프 안에서 cmt_poster 결과 처리)if result == "daily_limit": log("[🚨 공감/댓글 일일 한도] — 공감/댓글만 OFF (서이추 계속)") counter["comments_limited"] = True # 그래프 100% 표시 do_like = False do_comment = False # 서이추도 OFF 면 진짜 종료 (모든 활동 한도) if not do_buddy: return # do_buddy=True 면 다음 분기 (서이추) 로 자연 진입
동작 시나리오
| 상황 | 결과 |
|---|---|
| 답글 100/100 + 공감 50/100 + 서이추 30/100 | 답글 OFF, 공감/서이추 계속 |
| 답글 100 + 공감 100 + 서이추 30 | 공감 OFF, 서이추 계속 ⭐ (옵션 B) |
| 답글 100 + 공감 80 + 서이추 100 | 서이추 OFF, 공감 계속 |
| 답글 100 + 공감 100 + 서이추 100 | 모두 OFF → phase3 종료 |
전체 종료 조건 (절대 위반 금지)
# ✅ 정답: 모든 활동 OFF 일 때만 종료if not do_like and not do_comment and not do_buddy: log("[phase3] 🎯 모든 활동 한계 도달 — 즉시 종료") break# ❌ 절대 사용 금지: 한 활동 한도 도달 → 전체 returnif result == "daily_limit": return # 다른 활동 풀 남아있어도 못 씀
핵심 원칙
- 3개 활동의 일일 풀(100/100/100 = 총 300건) 최대 활용 (별님 명시)
- 답글/공감·댓글/서이추 각각 별도 한도 — 한쪽 한도가 다른 쪽 OFF 시키면 안 됨
- 한도 도달 그래프 —
comments_limited/buddies_limited/replies_limited플래그로 GUI 100% 🚨 표시 - 종료는 모두 OFF 일 때만 — 운영 자원(시간/세션) 낭비 방지
📝 v7.1 (5/7 추가) — 마크다운 종합 처리 (별님 명시)
🚨 절대 지침 — “마크다운 형식의 모든 것들이 잘 출력 될 수 있게”
별님 명시 (2026-05-07): 모든 마크다운 요소를 네이버 SE One 에서 깔끔하게 표시.
처리 대상 — 18가지 마크다운 요소
블록 레벨 (10가지)
| # | 마크다운 | 변환 결과 |
|---|---|---|
| 1 | ```python ... ``` | 코드 내용만 (개행 유지) |
| 2 | | 표 |---|값| | ■ 항목 + • 헤더: 값 |
| 3 | > 인용구 | 인용구 (텍스트만) |
| 4 | --- *** ___ | 빈 줄 |
| 5 | # H1 ~ ###### H6 | 텍스트만 |
| 6 | H1\n=== H2\n--- (Setext) | 텍스트만 |
| 7 | 1. 항목 2. 항목 | 1) 항목 2) 항목 ★ 핵심 |
| 8 | - 항목 * 항목 + 항목 | • 항목 |
| 9 | - [ ] 작업 | ☐ 작업 |
| 10 | - [x] 작업 | ☑ 작업 |
인라인 (8가지)
| # | 마크다운 | 변환 결과 |
|---|---|---|
| 1 | **굵게** __굵게__ | 굵게 |
| 2 | *기울임* _기울임_ | 기울임 |
| 3 | ***둘다*** ___둘다___ | 둘다 |
| 4 | ~~취소선~~ | 취소선 |
| 5 | `코드` | 코드 |
| 6 | [링크](url) | 링크 (URL 제거) |
| 7 |  | alt |
| 8 | \* \_ \# (이스케이프) | * _ # (그대로) |
HTML 태그 (혹시 AI 생성)
| 태그 | 변환 결과 |
|---|---|
<br> <br/> | 개행 |
<b> <strong> <i> <em> | 텍스트만 |
<script> <style> | 내용까지 제거 |
| 그 외 | 태그만 제거 |
핵심 정책 — SE One 자동 list 트리거 회피
별님 캡처 (2026-05-07): “2 2. 육수 붓기:” 같은 이중 번호 발생
원인: SE One 이 줄 시작 1. - * 패턴을 자동 numbered/bullet list 로 변환
→ 자체 번호 + 입력 텍스트 번호가 함께 보여 “2 2.” 형태
해결:
- 순서 리스트:
1. 텍스트→1) 텍스트(점→괄호 변환으로 트리거 회피) - 비순서 리스트:
- 텍스트→• 텍스트(mathematical bullet 사용)
처리 순서 (절대 위반 금지)
# modules/writer/markdown.py — strip_for_naver()def strip_for_naver(text: str) -> str: out = text # 1) 코드 블록 — 가장 먼저 (그 안의 패턴이 잡히면 안 됨) out = _strip_code_blocks(out) # 2) 표 — ■ + • 형식 out = _convert_markdown_tables(out) # 3) 헤더 — 텍스트만 out = _strip_headers(out) # 4) 가로 구분선 → 빈 줄 out = _strip_horizontal_rules(out) # 5) 인용구 → 텍스트 out = _strip_blockquotes(out) # 6) 체크박스 → ☐ ☑ (리스트 정규화 전) out = _convert_checkboxes(out) # 7) 순서 리스트 1. → 1) (★ SE One 자동 list 회피) out = _normalize_numbered_lists(out) # 8) 비순서 리스트 - * + → • (★ SE One 자동 list 회피) out = _normalize_bullet_lists(out) # 9) HTML 태그 — <br> 개행, 그 외 제거 out = _strip_html(out) # 10) 인라인 패턴 — 굵게/기울임/취소선/코드/링크/이미지 out = _apply_inline_patterns(out) # 11) 이스케이프 풀기 — \* → * out = _unescape_markdown(out) # 12) 다중 빈 줄 정리 out = re.sub(r"\n{3,}", "\n\n", out) return out.strip() + "\n"
본문 첫 입력 누락 방지 (별님 v7.1)
별님 보고 (2026-05-07): “글 작성이 안되어 클릭하니 글이 작성됨”
원인: body_el.click() 만으로는 contenteditable 포커스가 안 잡혀 첫 keypress 무시.
해결: 3중 보장
# modules/writer/editor.py — focus_body()1) body_el.click() # 일반 클릭2) JS 직접 focus + selection 끝으로 # contenteditable 포커스 보장3) sleep_random(0.30, 0.50) # 안정화 대기# type_body_with_markers() 시작 시 한 번 더move_caret_to_end(driver)sleep_random(0.20, 0.40)
핵심 원칙
- 모든 마크다운 → 평문 + 시각 통일 (별님 명시)
- SE One 자동 list 회피 —
1.→1),-→• - 표는 항목별 그룹핑 — 첫 컬럼 =
■, 나머지 =• 헤더: 값 - 체크박스는 유니코드 —
☐(U+2610),☑(U+2611) - 첫 입력 누락 방지 — click + JS focus + selection + 대기 4중
절대 위반 금지
# ❌ 절대 금지: 마크다운을 그대로 입력 (raw `|...|` 노출)type_body(driver, body_markdown) # SE One 이 표 렌더링 안 함# ❌ 절대 금지: '1. ' 그대로 입력"1. 돼지고기 볶기" # SE One 자동 list → "2 2." 이중 번호# ✅ 정답: strip_for_naver 거쳐 평문화 후 입력clean_text = strip_for_naver(body_markdown)# clean_text 에는 '1) 돼지고기 볶기' / '■ 김치' / '• 항목' 만 존재
🔄 v7.1 (5/7 추가) — 보답 대상 4가지 모드 (별님 명시)
🚨 별님 명시: “매일 새로 50% 정도는 발굴 — 매번 같은 사람 X”
별님 발견 (2026-05-07): “100명 보답하다 보니 그 사람이 그 사람”
원인: 본인 글 답글/공감자만 우선으로 가다 보니 동일 인원 반복.
해결: 3개 모듈 (통합/공감댓글/서로이웃) 모두 4가지 보답 대상 모드 + 자동 기본 = 로테이션
4가지 모드
| 모드 | 동작 | 설명 |
|---|---|---|
| 🔄 로테이션 (자동 기본) ⭐ | 신규 50% + 기존 last_date 오래된 순 50% | 매일 다른 사람 만남 |
| 🆕 새 이웃 발굴만 | 검색/이웃새글 100% | 모르는 분만 |
| ⚖ 50:50 혼합 | visitor 50명 + 신규 50명 | 명확한 절반 |
| 👥 댓글·공감자 우선 (기존) | visitor 우선 + 부족시 검색 | 종전 방식 |
로테이션 알고리즘 (핵심)
def _select_by_oldest_last_date(contacts, take): def _key(c): v = visitor_db.get_visitor(c[0]) return (v.get("last_date") or "") if v else "" # last_date 빈 문자열 = 한 번도 만난 적 없음 = 최우선 return sorted(contacts, key=_key)[:take]신규 = supplement_search(N=50)기존 = _select_by_oldest_last_date(visitor_pool, take=50)contacts = 신규 + 기존
본인 자동 제외 (모든 모듈)
# 모든 모듈 phase2 visitor 수집 후contacts = [c for c in contacts if c[0] != my_blog]
자동 모드 — 로테이션 강제
# gui/app.py — _integrated_triggerir.run_once(target_count=100, ..., visitor_mode="rotation")
핵심 원칙
- 자동 = 로테이션 강제 — GUI 설정 무관
- last_date 오래된 순 — 한 번도 못 만난 사람 최우선
- 3개 모듈 통일 — 같은 정책 + 같은 GUI 콤보박스
- 본인 자동 제외
🚀 v7.1 (5/7 추가) — 자동 모드 = 즉시 발행 (별님 명시)
🚨 “예약 발행 대량 발행은 즉시 발행입니다”
별님 발견: mode=draft (임시저장) 로 동작 → 발행 안 됨
해결
# gui/app.py — _writer_triggerpublish_mode="publish" # ★ 자동 = 무조건 즉시 발행schedule_at="" # publish 모드는 schedule_at 무시
발행 다이얼로그 보존
# modules/writer/publisher.py# ❌ 절대 금지: 다이얼로그 떠있는 상태에서 ESC 호출# ❌ 절대 금지: 다이얼로그 떠있는 상태에서 close_popups 호출# (button[aria-label='닫기'] 셀렉터가 다이얼로그 X 도 매치)
핵심 원칙
- 자동 = publish 강제 (draft 금지)
- 다이얼로그 보존 — 떠있는 동안 ESC/close_popups 호출 X
- 5중 클릭 + DPI 보정 — native/AC/JS/dispatch/OS강제
🎨 v7.1 (5/7 추가) — TistoryBlog 표준 폰트 통일 (별님 시리즈)
별님 명시: “두개 크기 차이 보세요 같은 크기로”
별님 발견: AutoBlog 폰트가 TistoryBlog 보다 작음 → 시리즈 일관성 부재.
표준 (TistoryBlog 와 동일)
| 영역 | 폰트 | 크기 | 두께 | Padding |
|---|---|---|---|---|
| GUI 전체 | Malgun Gothic | 12pt | normal | — |
| tk scaling | — | — | — | 1.5 |
| Notebook 탭 | Malgun Gothic | 12pt | bold | (18, 6) |
| Button | Malgun Gothic | 12pt | bold | (12, 6) |
| 시스템 기본 폰트 9개 | Malgun Gothic | 12pt | normal | — |
| ttk 위젯 클래스 6개 | Malgun Gothic | 12pt | normal | — |
시스템 폰트 적용 (9개)
for fn in ("TkDefaultFont", "TkTextFont", "TkMenuFont", "TkHeadingFont", "TkCaptionFont", "TkSmallCaptionFont", "TkIconFont", "TkTooltipFont", "TkFixedFont"): f = tkfont.nametofont(fn) f.configure(family="Malgun Gothic", size=12)
ttk 위젯 클래스 적용 (6개)
for w in ("TLabel", "TEntry", "TCombobox", "TSpinbox", "TCheckbutton", "TRadiobutton", "TLabelframe.Label"): style.configure(w, font=("Malgun Gothic", 12))
보드 컬럼 잘림 방지
별님 보고 (2026-05-07): “rich_heart119 잘림”, “블로그 제목 잘림”
# gui/visitor_board_widget.pyself.tree.column("id", width=150, minwidth=140)self.tree.column("nick", width=130, minwidth=120)self.tree.column("btitle", width=320, minwidth=260) # ⭐ stretch=True# 표시 글자수 + 자동 …def _trunc(s, n): return s if len(s) <= n else s[:n] + "…"# id: 22자, nick: 18자, btitle: 36자
핵심 원칙
- 시리즈 통일 — AutoBlog ↔ TistoryBlog ↔ 미래 시리즈 모두 동일 표준
- 단일 폰트 — Malgun Gothic (한글/영문/숫자 안정)
- 단일 크기 12pt — HiDPI scaling 1.5 환경 가독성
- 강조만 bold — Notebook 탭 / Button 만
🤖 24/7 365 자동 동작 — visitor 풀 + 키워드 로테이션 (별님 v7.2 — 2026-05-08 절대 명시)
별님 통찰:
“DB에 명단이 있다면 수집 하지 말아야 하고, 그 명단으로 최근 30일 동안 사용 했다면
지나쳐야 하고, 다른 사람 선택할 수 있게 잘 만들어 주세요.
키워드 입력이 없으면 구동이 안되는데 24시간 365일 자동으로 동작하여야 하기에
키워드도 스스로 로테이션 되는 db를 사용하여야 함.”
visitor 풀 선택 정책 (3단)
1. DB 우선 — visitor_db.get_unrepaid_pool(limit=N, not_repaid_days=30) → 30일 안에 보답 안 한 사람 중 가장 오래된 순으로 N명 추출 → 충분하면 발굴 skip (시간/비용 절감)2. 포럼 발굴 — modules.comment.forum_explorer.collect_forum_posts() → tistory.com/community/forum/introduce (블로그 소개) → 매일 신규 블로거 50~100명 자동 풀 (답례 의지 ↑)3. 키워드 검색 — find_posts_by_keyword(q) → 키워드 로테이션 풀 사용 (아래 정책) → 결과는 30일 차단 자동 필터
30일 차단: core.visitor_db.already_repaid_recently(blog_id, days=30)
- 같은 사람에게 30일 안에 두 번 보답 X
- 30일 후 자동 재방문 가능
키워드 자동 로테이션 (3단 fallback)
1. 사용자 입력 (1회성) ← GUI 키워드 입력란2. 황금 키워드 (today_gold_keyword) ← 새벽 채굴 결과 매일 5개 자동 할당3. 정적 풀 30+개 ← 시간 시드 셔플로 부족분 보충
구현: core.keyword_rotator.get_today_keywords(user_keyword, blog_id, target_count=8)
효과:
- 사용자 입력 없어도 구동 (24/7 365 자동)
- 매일 새벽 채굴된 황금 키워드 5개 자동 사용 (별님 명시)
- 황금 키워드도 부족하면 정적 풀로 fallback (안정성)
★ 별님 절대지침 (2026-05-10): 모든 프로그램 키워드 기본값 자동 적용
“모든 프로그램은 키워드 초기값을 황금 키워드 초기값 설정 방식과 동일하게 적용한다.
입력을 안 해도 자동으로 기본 키워드가 입력되어야 한다.”
— 별님 명시
의무 적용 대상 (모든 모듈)
- 🥇 황금 키워드 (gold daemon) — 이미 적용
- ✍ 글쓰기 (writer) —
keyword_rotator사용 의무 - 👥 서이추 / 구독 (buddy / subscribe) — 검색 키워드 자동 채움
- 💬 공감/댓글 (comment) — 키워드 기반 검색 시 동일
- 💌 답글 (reply) — 보통 키워드 무관, 단 컨텍스트 필요 시 동일
- 🎁 통합 보답 (integrated) — 동일 정책
구현 패턴 (의무)
# 모듈 진입 시 — 키워드 비어 있으면 자동 채움from core import keyword_rotatordef run_once(keyword: str = "", ...): if not (keyword or "").strip(): # 사용자 입력 없음 → 황금 키워드 + 정적 풀 fallback keywords = keyword_rotator.get_today_keywords( user_keyword="", blog_id=naver_id_or_blog_id, target_count=N, ) keyword = keywords[0] if keywords else "정보" # 최후 정적 fallback log(f"[module] 🥇 키워드 자동 설정: {keyword} (사용자 입력 없음)")
GUI 정책 (모든 모듈 — 별님 v7.2 2026-05-10)
검색 키워드 입력란 의무사항:
- placeholder 텍스트:
"비워두면 황금 키워드 자동 사용 (오늘 황금 키워드)"(또는 동일 의미) - 라벨 옆 헬퍼 텍스트:
"입력 없으면 황금 키워드 자동 동기화" - 사용자가 입력 안 한 채 실행 → 에러 X, 자동으로 fallback 진행
- 사용자 입력 있으면 그것 우선 (1회차), 2회차+ 는 자동 랜덤
- 로그 출력:
🥇 키워드 자동 설정: 'XXX' (사용자 입력 없음 — 황금 키워드)
적용 대상 GUI 입력란:
- 답글 (reply) — 키워드 무관해도 컨텍스트용
- 공감/댓글 (comment) — 검색 키워드
- 서이추 / 구독 (buddy / subscribe) — 검색 키워드
- 통합 보답 (integrated) — Phase 3 검색 키워드
- 글쓰기 (writer) — 본문 키워드 (수동 제목 우선)
시리즈 통일: 모든 시리즈 (네이버/티스토리/구글블록/워드프레스) 동일 패턴 의무.
효과
- ✅ 24/7 365 자동 운영 — 사용자가 키워드 입력 안 해도 동작
- ✅ 매일 새 황금 키워드 자동 사용 → 다양성 + 봇 의심 ↓
- ✅ “키워드 비었습니다” 에러 발생 0
- ✅ 별님 절대 원칙 — “사용자 1번 설정 후 365일 무인 운영”
시리즈 통일
이 정책은 AutoBlog / TistoryBlog / 구글블록 / 워드프레스 모두 동일.
플랫폼별 차이는 visitor_db 스키마(필드)와 포럼 URL만.
📡 모니터 보드 / 로그 / 그래프 실시간 동기화 — 별님 절대지침 (2026-05-10)
절대 원칙
“모니터 보드와 로그와 그래프는 실시간으로 동기화한다.
단계별 상세 진행 상황을 정밀하게 동기화한다.”
— 별님 명시
시리즈 공통 — 모든 프로그램 의무 적용
이 정책은 AutoBlog / TistoryBlog / 구글블록 / 워드프레스 모두 동일 적용.
모듈/데몬 작성 시 반드시 준수.
1. 3중 동기화 원칙
| 구성요소 | 의무사항 |
|---|---|
| 로그 (텍스트) | 모든 단계마다 1줄 출력 — 단계 시작 / 진행 / 완료 / 오류 |
| 모니터 보드 (Treeview/표) | 새 행 / 갱신 / 정렬 즉시 반영 (after(0, …) 보장) |
| 그래프 (progress / 카운터) | cur/total 비율 즉시 갱신, 라벨 (분석 중 키워드 등) 표시 |
3개 모두 같은 시점 에 갱신되어야 함 (사용자가 보는 화면이 일관됨).
2. 단계별 상세 로그 의무
데몬 / 백그라운드 작업은 모든 단계 에 명확한 로그 남김:
[module.daemon] 시작 — ...[module.daemon] ─── 데몬 루프 진입 ───[module.daemon] 1단계: 사전 검사[module.daemon] → 결과: ...[module.daemon] 2단계: 데이터 로드[module.daemon] → N개 로드됨[module.daemon] 3단계: 메인 작업 시작[module.daemon] ... 진행 로그 ...[module.daemon] → 메인 작업 완료[module.daemon] 4단계: 다음 사이클 대기
금지: “시작” 만 출력하고 그 이후 침묵 (별님이 어디서 멈췄는지 파악 불가)
권장: 단계 번호 + 결과 + 시간 정보
3. 데몬 → GUI 동기화 패턴
콜백 등록 방식 (권장)
# 데몬 모듈_listener_on_row: Optional[Callable[[dict], None]] = None_listener_on_progress: Optional[Callable[[int, int, str], None]] = Nonedef register_listeners(*, on_row=None, on_progress=None) -> None: """GUI 콜백 등록 — 백그라운드 진행 시 자동 호출.""" global _listener_on_row, _listener_on_progress _listener_on_row = on_row _listener_on_progress = on_progress
# GUI app.pyfrom modules.xxx.daemon import register_listeners as _reg_reg(on_row=tab._on_daemon_row, on_progress=tab._on_daemon_progress)
# GUI tabdef _on_daemon_row(self, row: dict) -> None: self.after(0, self._add_row_to_board, row)def _on_daemon_progress(self, cur: int, total: int, label: str) -> None: def _u(): self._progress.config(maximum=max(total, 1), value=cur) self._counter.config(text=f"{cur} / {total}") self.after(0, _u)
4. 로그 형식 표준
| 기호 | 의미 |
|---|---|
⏸ | 대기 / 멈춤 (이유 명시) |
📐 | 폰트 / 스타일 변경 |
▶ | 진행 시작 |
🖱 | 마우스 클릭 |
🎯 | 수동 / 1회차 / 명시적 입력값 |
🎲 | 랜덤 / 자동 (회피 로직 포함) |
🗂 | DB 사전 필터 / 캐시 |
♻ | 재사용 (이미지 / 데이터) |
🔍 | 진단 / 검사 |
✅ | 성공 |
❌ | 실패 |
⚠ | 경고 |
🚨 | 일일 한도 / 차단 감지 |
5. 모니터 보드 실시간 갱신 정책
| 작업 | 보드 동작 |
|---|---|
| 새 데이터 행 추가 | tree.insert(...) + tree.see() (자동 스크롤) |
| 정렬 상태 유지 | 행 추가 후 _sort_board() |
| 행 번호 자동 재계산 | 정렬 후 _renumber_rows() |
| 카운터 갱신 | “{현재} / {목표}” 라벨 즉시 갱신 |
| 색상 / 태그 | 별님 5색 티어 (위험/도전/양호/우수/황금) |
6. 그래프 정책 — 입력값 기준
별님 보고 (2026-05-10): “3입력 했는데 그래프느 4/4 처리”
→ 그래프 분모 = 입력값 (target), 분자 = 시도 cap (min(i, target))
if progress: _graph_cur = min(i, target_success) progress(_graph_cur, target_success, f"{i}/{total} {label}")
시도가 입력값 넘어도 그래프는 100% 유지 (꽉 안 넘김).
7. visitor_board 실시간 push
모든 모듈은 작업 결과를 visitor_board.add_event() 로 push:
visitor_board.add_event( module="reply", # 모듈 이름 blog_id=bid, # 대상 nickname=nick, action="답글", # 작업 result="✅", # 결과)
GUI 의 visitor_board_widget 가 새 이벤트 즉시 표시.
8. counter 동기화 (그래프 + 보드)
counter = {"replies": 0, "comments": 0, "buddies": 0, "fail": 0}# 작업 성공 시counter["replies"] += 1if on_counter_update: on_counter_update(dict(counter)) # GUI 그래프 즉시 동기화
9. 의무 체크리스트 (코드 작성 시)
- [ ] 모든 단계에 로그 1줄 (
⏸ ▶ 📐 ✅등 명확한 기호) - [ ] 데몬은
register_listeners패턴으로 GUI 콜백 받음 - [ ] 보드 갱신은
after(0, ...)로 스레드 안전 - [ ] 그래프는 입력값 기준 (
min(i, target)) - [ ] 결과는
visitor_board.add_event로 push - [ ] 카운터는
on_counter_update(dict(counter))로 즉시 동기화
10. 시리즈 통일
이 동기화 정책은 AutoBlog / TistoryBlog / 구글블록 / 워드프레스 모두 동일.
새 모듈 / 데몬 작성 시 반드시 위 9개 체크리스트 통과.
별님이 어떤 시점에 화면을 봐도 로그 ↔ 보드 ↔ 그래프 가 일관된 상태를 유지해야 함.
