# Daily Brief Bot

> 매일 정해진 시간에 관심 주제 아티클을 수집·요약해 디스코드로 받는 봇을 만들고 튜닝한다. 톤·길이·발송 시각 조정 방법 포함.

- Skill: `bam-bam-2/daily-brief-bot` (Agent Skill)
- Install (CLI): `npx skillmds@latest add bam-bam-2/daily-brief-bot`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bam-bam-2/daily-brief-bot/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: bam-bam-2 (https://skillmd.com/u/bam-bam-2)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/bam-bam-2/daily-brief-bot

---


# 데일리 브리프봇

사용자는 매일 아침 10시에 디스코드 DM으로 **데일리 브리프봇** 아티클을 받는다.
지피터스(gpters.org) 신규 글을 전부 읽고, 사용자의 제2두뇌 볼트·에이사이드 스킬/메모리와 대조해
사용자가 지금 붙들고 있는 고민에 닿는 3건을 골라 편지 형식으로 보낸다.
각 항목에 번호(1~3)와 원문 링크가 붙어 있다.

"여백"은 이 봇이 후보를 고르는 내부 판정 기준을 부르는 말이다(볼트에 한 번도 안 나온 것).
사용자에게 말할 때는 봇 이름인 **데일리 브리프봇**으로 부른다.

## 트리거

- **"데일리 브리프봇 고치자"** — 톤·길이·건수·시간 등 편집. 아래 편집 표 참고
- **"데일리 브리프봇 2번 해보자"** / "8/13 2번 해보자" / "어제 그거 해보자" — 실행
- "오늘 데일리 브리프봇 뭐 왔어" — 읽어주기만
- "이거 볼트에 넣어" — 볼트 승격
- 옛 호칭 "여백 브리핑"으로 불러도 같은 것을 가리킨다

## 인프라 위치 (전부 원격 머신, `ssh remote-host`)

```
~/Projects/<프로젝트>/
  prompt-template.md  ★ 톤·형식·길이·몇 건을 결정하는 유일한 파일. 편집은 거의 여기만 고친다
  collect.mjs   지피터스 신규 글 수집 (Bettermode GraphQL, 게스트 토큰 · 로그인 불필요)
  brief.mjs     볼트·스킬·메모리 대조 → 후보 22건 추린 뒤 템플릿에 끼워 프롬프트 생성
  verify.mjs    브리핑 링크가 실제 수집분인지 대조·정정
  dm.py         디스코드 DM 발송 (stdin → 사용자 DM, 1900자씩 분할)
  capture.py    사용자의 DM 답장 회수 (10분마다)
  run.sh        매일 10:00 전체 실행 (launchd com.bambam.gpters-brief)
  .env          DISCORD_TOKEN / BAMBAM_USER_ID / CLAUDE_CODE_OAUTH_TOKEN (600)
  ctx/          맥북에서 동기화한 에이사이드 skills/ · memory/ ("이미 하고 있는 것" 판정용)
  state/
    posts-YYYY-MM-DD.json   그날 수집한 글 전문
    brief-YYYY-MM-DD.md     그날 보낸 아티클
    marks.jsonl             사용자가 DM에서 표시한 관심 항목
    done.jsonl              실제로 실현한 항목 (재제안 금지 목록)
```

사용자 볼트: `~/Library/Mobile Documents/com~apple~CloudDocs/제2두뇌` (원격 머신에서도 접근됨)

## 현재 확정된 형식 (2026-08-13 사용자 승인)

- **아티클이다. 목록이나 알림이 아니다.** 링크를 하나도 안 눌러도 내용을 다 알 수 있어야 한다.
- 문체는 **"사용자님" + 해요체의 편지**. 관찰 보고서 금지.
- 첫 문단은 반드시 **"요즘 ~에 걸려 계신 것 같아요"** 로 시작해 고민을 한 가지로 짚는다.
  노트 제목 나열 금지. 틀려도 좋으니 구체적으로 짚는다.
- 3건, 전체 1만자 안팛. 앞 2건은 짚은 고민에 바로 닿는 것, 마지막 1건은 안 해본 것.
- 각 항목: 그 사람 상황 → 무엇을 어떻게 했는지(도구·프롬프트·숫자 그대로) → 결과
  → 사용자님 쪽이랑 겹치는 데(볼트·스킬 근거) → "이거 한번 시도해보시면 어때요?" → 오늘 30분 → 원문 링크

## 편집 요청 처리

사용자가 "데일리 브리프봇 고치자" 계열로 말하면 아래에서 해당하는 파일만 고친다.

| 요청 | 고칠 곳 |
|---|---|
| 톤, 문체, 형식, 길이, 몇 건, 어떤 순서로, 금지 표현 | `prompt-template.md` ← 거의 전부 여기 |
| 어떤 글을 후보로 올릴지 (점수 기준, 후보 개수, 활동 판정 기간 7일) | `brief.mjs` |
| 어떤 카테고리를 수집할지, 몇 건 가져올지 | `collect.mjs` |
| 보내는 시각 | `~/Library/LaunchAgents/com.bambam.gpters-brief.plist` → 수정 후 unload/load |
| 모델 (클로드 → 코덱스 폴백) | `run.sh` |

고친 뒤에는 반드시 한 번 돌려보고 결과를 보여준 다음 발송한다.
```
ssh remote-host 'cd ~/Projects/<프로젝트> && node brief.mjs && set -a && . ./.env && set +a && claude -p < state/prompt-<날짜>.txt'
```

맥북 스킬/메모리가 바뀜으면 판정 근거도 갱신해야 한다.
```
rsync -az --delete 스킬 폴더 remote-host:~/Projects/<프로젝트>/ctx/skills/
rsync -az --exclude='.history.jsonl' --exclude='memory-index.json' --exclude='.moss-cache' --exclude='.git' 에이전트 메모 remote-host:~/Projects/<프로젝트>/ctx/memory/
```

## "데일리 브리프봇 N번 해보자" 처리 절차

1. **원문 전문부터 읽는다.** 브리핑 요약만 보고 시작하지 마라.
   ```
   ssh remote-host 'cat ~/Projects/<프로젝트>/state/brief-<날짜>.md'
   ```
   해당 항목의 링크에서 post ID를 뽑아 `posts-<날짜>.json`에서 `body`를 꺼낸다.

2. **사용자 볼트의 관련 노트를 읽는다.** 브리핑의 "사용자 쪽 연결" 줄에 적힌 노트를 실제로 열어본다.
   남이 쓴 방법을 그대로 옮기지 말고, 사용자가 이미 가진 것과 겹치는 부분을 먼저 확인한다.

3. **사용자 환경 기준으로 다시 짠 실행안을 제시하고 승인받는다.**
   - 원문에서 그대로 쓸 것 / 사용자 환경에 맞게 바꿀 것 / 안 쓸 것을 구분해서 보여준다
   - 되돌리기 어려운 작업(배포, 결제, 삭제)은 승인 전에 절대 실행하지 않는다

4. **실행한다.** 무거운 작업이면 원격 머신로 넘긴다(`macmini-offload` 스킬).

5. **끝나면 기록한다.**
   - 볼트: 새 노트를 만들지 말고 **기존 노트에 한 줄 덧붙인다.** `[[위키링크]]` 문법 지킬 것.
     예) `참고: 정본노트를 채널별 브리프로 분리하는 OSMU 방식 — https://...`
     새 개념 노트가 필요할 정도면 사용자에게 먼저 묻는다.
   - `done.jsonl`에 한 줄 추가:
     ```
     ssh remote-host 'echo "{\"date\":\"2026-08-13\",\"no\":2,\"post\":\"<postId>\",\"title\":\"...\",\"result\":\"...\"}" >> ~/Projects/<프로젝트>/state/done.jsonl'
     ```
     이게 있으면 같은 주제를 다시 제안하지 않는다.

## 설계 원칙 (사용자가 직접 정한 것)

- **여백 = 사용자가 지금 하는 일과 인접한데, 볼트 761개 노트에 한 번도 기록된 적 없는 것.**
  미결 목록은 고민이 아니다. 사용자가 선택을 안 한 건 그냥 넘긴 것일 수 있다.
- "지금 하는 일"은 사용자에게 묻지 말고 **볼트에서 최근 7일 수정된 노트**로 판단한다.
- **읽지 않은 것은 볼트에 넣지 않는다.** 브리핑 원본은 원격 머신에 30일만 두고 자동 삭제된다.
  볼트로 올라가는 건 사용자가 표시했거나 실제로 해봤을 때뿐이다.
- 억지로 4개를 채우지 않는다. 그날 걸리는 게 없으면 비운다.
- 볼트가 커질수록 "이미 아는 것"이 두꺼워져 여백이 좁아진다. 그게 정상이다.

## 톤

- 이모지 쓰지 않는다.
- 금지 표현: "~이 아니라" 대조 구문, "굴러가다", "여정", "진짜 기준", 안심시키려는 문장.
- 볼트에 없는 걸 있다고 말하지 않는다. 근거는 실제 노트 경로로 댄다.

## 아바타 교체

후보 4종이 원격 머신 `~/Projects/<프로젝트>/assets/` 에 있다 (**b_bot 적용 중** / a_owl / c_book / d_cat).
사용자가 로봇을 골랐다. 40px 원형으로 잘려도 눈 두 개와 안테나 라임 점이 살아남는다.
커뮤니티 브랜드 컬러 기준: 배경 `#0d0d0d`, 강조 `#d4f000`. 디스코드는 작은 원으로 잘리니 여백을 넉넉히 둔 단순한 형태만 쓴다.

```
ssh remote-host 'cd ~/Projects/<프로젝트> && set -a && . ./.env && set +a && python3 -c "
import os,json,base64,urllib.request
img=base64.b64encode(open(\"assets/b_bot.png\",\"rb\").read()).decode()
H={\"Authorization\":\"Bot \"+os.environ[\"DISCORD_TOKEN\"],\"User-Agent\":\"DiscordBot (https://github.com, 1.0)\",\"Content-Type\":\"application/json\"}
r=urllib.request.Request(\"https://discord.com/api/v10/users/@me\",data=json.dumps({\"avatar\":\"data:image/png;base64,\"+img}).encode(),headers=H,method=\"PATCH\")
print(json.load(urllib.request.urlopen(r))[\"username\"])"'
```

## 어휘 수준 (2026-08-13 교정)

사용자는 개발자가 아니다. **읽다가 모르는 단어가 하나라도 나오면 실패다.**
전문용어는 처음 나올 때 괄호로 풀고, 영어 약어는 우리말로 바꾸고, 명령어·코드는 역할 설명이 붙을 때만 쓴다.
단 숫자와 구체적 사실은 그대로 살린다. 쉽게 쓰라는 게 내용을 빼라는 뜻이 아니다.
자세한 규칙은 `prompt-template.md` 의 "말투와 어휘" 절에 있다.

