This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
디스코드 서버 멤버들을 자동으로 매칭하여 커피챗(1:1 대화)을 연결해주는 봇입니다. GitHub Actions로 2주마다 자동 실행되며, Discord Role 기반으로 참여자를 관리합니다. /coffee join과 /coffee leave 슬래시 명령어로 사용자가 직접 참여/탈퇴할 수 있으며, Astro + @astrojs/cloudflare 어댑터로 웹사이트와 서버리스 엔드포인트를 통합 배포합니다.
# 매칭 실행 (로컬 테스트)
bun run match
# 테스트 실행
bun test
# 타입 체크
bun run typecheck
# Lint 체크
bun run lint
# 코드 포맷팅
bun run format
# 사이트 로컬 개발
bun run dev
# 사이트 빌드
bun run build
# 배포 (빌드 + wrangler deploy)
bun run deploy
# 슬래시 명령어 등록
bun run register-commandscoffee/
├── src/ ← Astro 소스 (웹사이트 + Discord 엔드포인트)
│ ├── pages/ ← 웹사이트 페이지 + API 엔드포인트
│ │ ├── api/discord.ts ← Discord Interactions 핸들러
│ │ ├── index.astro ← 메인 페이지
│ │ ├── features.astro
│ │ └── docs/
│ ├── components/
│ ├── layouts/
│ ├── content/
│ ├── styles/
│ └── lib/discord/ ← Discord 핸들러 로직
│ ├── handlers.ts ← /coffee join, /coffee leave 처리
│ ├── verify.ts ← Ed25519 서명 검증
│ ├── commands.ts ← 명령어 정의
│ └── discord-api.ts ← Discord REST API 호출
├── match/ ← 매칭 로직 (GitHub Actions에서 Bun으로 실행)
│ ├── index.ts ← 엔트리포인트: bun run match
│ ├── matcher.ts ← 매칭 알고리즘
│ ├── schedule.ts ← 스케줄 체크
│ ├── discord.ts ← Discord API 멤버 조회
│ ├── webhook.ts ← 매칭 결과 발표
│ └── types.ts
├── data/ ← 공유 데이터 + 타입
│ ├── roles.json ← 역할 설정
│ ├── types.ts ← 공유 타입 (RoleConfig 등)
│ └── */history.json ← 매칭 이력
├── scripts/
│ └── register-commands.ts
├── astro.config.mjs ← Astro + @astrojs/cloudflare 설정
├── wrangler.jsonc ← Cloudflare Worker 설정 (루트)
├── package.json ← 단일 package.json
└── tsconfig.json ← Astro tsconfig
- 스케줄 체크 (
schedule.ts) - 역할별 스케줄에 따라 매칭 실행 여부 판단 (수동 트리거 시 건너뜀) - 참여자 조회 (
discord.ts) - Discord API로 특정 Role을 가진 멤버 목록 가져오기 - 매칭 이력 로드 (
matcher.ts) -data/history.json에서 과거 매칭 기록 로드 - 매칭 생성 (
matcher.ts) - Fisher-Yates 셔플 + 중복 방지 알고리즘 - 이력 저장 (
matcher.ts) - 새로운 매칭을 history.json에 추가 - Discord 발표 (
webhook.ts) - Bot API로 매칭 결과 채널에 공지
- 서명 검증 (
verify.ts) - Discord 요청의 Ed25519 서명 검증 - PING/PONG - Discord 연결 확인 응답
- 명령어 라우팅 (
handlers.ts) -/coffee join또는/coffee leave처리 - Role 관리 (
discord-api.ts) - Discord REST API로 Role 추가/제거
- 중복 방지: 최근 4회 매칭 이력과 비교하여 같은 조합 회피 (최대 100번 재시도)
- 홀수 처리: 참여자가 홀수일 경우 마지막 조를 3인 1조로 구성
- 데이터 구조:
data/history.json에 날짜별 매칭 기록 저장
매칭 (GitHub Actions):
DISCORD_BOT_TOKEN- Discord Bot 토큰 (Secret)DISCORD_SERVER_ID- 디스코드 서버 ID (Variable)
Worker (Cloudflare):
DISCORD_PUBLIC_KEY- 서명 검증용 공개키 (wrangler.jsonc var)DISCORD_APPLICATION_ID- Discord 앱 ID (wrangler.jsonc var)DISCORD_SERVER_ID- 서버 ID (wrangler.jsonc var)DISCORD_BOT_TOKEN- Bot 토큰 (wrangler secret)
- 웹사이트 + Worker: Cloudflare Workers Builds (GitHub 연동, main push 시 자동 배포)
- 매칭 실행: GitHub Actions (
match.yml, 매주 월요일 cron) - CI: GitHub Actions (
ci.yml, lint + test)
배포 URL: https://coffee.dalestudy.com
Discord Interactions Endpoint: https://coffee.dalestudy.com/api/discord
# Cloudflare 로그인 (계정 전환 시)
bunx wrangler login
# 빌드 + 배포
bun run deploy
# 슬래시 명령어 등록
bun run register-commands-
Cloudflare 로그인
bunx wrangler login
-
workers.dev 서브도메인 등록 (신규 계정인 경우)
Cloudflare 대시보드 → Compute → Workers에서 서브도메인을 설정하거나, API로 등록:
# 현재 서브도메인 확인 curl -s "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/workers/subdomain" \ -H "Authorization: Bearer <TOKEN>" # 서브도메인 변경 (기존 것 삭제 후 재등록) curl -s -X DELETE "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/workers/subdomain" \ -H "Authorization: Bearer <TOKEN>" curl -s -X PUT "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/workers/subdomain" \ -H "Authorization: Bearer <TOKEN>" \ -H "Content-Type: application/json" \ --data '{"subdomain": "원하는이름"}'
-
배포
bun run deploy
-
Bot 토큰 등록
bunx wrangler secret put DISCORD_BOT_TOKEN
-
슬래시 명령어 등록
bun run register-commands
-
Discord Interactions Endpoint URL 설정
Discord Developer Portal → 앱 선택 → General Information → Interactions Endpoint URL에
https://coffee.dalestudy.com/api/discord입력 후 저장
wrangler.jsonc의vars에는 공개 가능한 값만 포함 (DISCORD_PUBLIC_KEY,DISCORD_APPLICATION_ID,DISCORD_SERVER_ID)DISCORD_BOT_TOKEN은 반드시wrangler secret으로 관리- Cloudflare 계정의 이메일 인증이 완료되어야 Worker 배포 가능
- Runtime: Bun (TypeScript 네이티브 지원)
- Formatter: Biome (tab indent, recommended rules)
- Testing: Bun test (
*.test.ts파일,match/디렉토리) - Import: ESM (
type: "module")
테스트 파일은 *.test.ts 형식으로 작성하며, Bun의 test runner를 사용합니다.
// expect().toBeDefined() 후 non-null assertion 사용 패턴
expect(capturedBody).toBeDefined();
const parsed = JSON.parse(capturedBody!); // OK in tests이 패턴 때문에 biome.json에서 noNonNullAssertion 규칙이 비활성화되어 있습니다.