# Teams Planner Bot MS Teams 봇. 사용자의 자연어 작업 보고를 LLM으로 분류해서 Microsoft Planner에 자동으로 task를 생성하거나 업데이트한다. ## 동작 개요 ``` 사용자 (Teams) → 봇 → OAuthPrompt 로그인 (최초 1회) → Graph로 본인 Plans/Buckets/최근 Tasks 조회 → LLM(Claude 또는 Azure OpenAI)에게 발화 + 컨텍스트 전달 → tool/function call로 {action, planId, ...} JSON 강제 추출 → Planner Graph API로 create / patch → Adaptive Card로 결과 응답 ``` ## 구성 ``` src/ index.ts # restify 서버, CloudAdapter config/index.ts # 환경변수 로딩/검증 bot/PlannerBot.ts # 메시지 핸들러 + OAuth 다이얼로그 + 오케스트레이션 graph/ graphClientFactory.ts # 사용자 토큰을 받아 Graph Client 생성 plannerClient.ts # Plans/Buckets/Tasks 조회·생성·업데이트 (ETag 처리 포함) llm/ types.ts # ClassifiedAction 등 공용 타입 prompt.ts # 시스템 프롬프트 + tool/function 스키마 coerce.ts # LLM JSON → ClassifiedAction 검증 claudeClassifier.ts # Anthropic Claude 구현 (tool_use 강제) azureOpenAiClassifier.ts # Azure OpenAI 구현 (function calling 강제) factory.ts # LLM_PROVIDER 기반 분기 cards/confirmationCard.ts # 결과 Adaptive Card appPackage/manifest.json # Teams 앱 매니페스트 (개발자 포털에 업로드) ``` ## 사전 준비 (Azure / Microsoft 365) 1. **Azure AD 앱 등록** (Graph 위임 권한) - Azure Portal → App registrations → New - API permissions → Microsoft Graph → Delegated: - `Tasks.ReadWrite` - `Group.Read.All` - `User.Read` - `offline_access` - Grant admin consent - Certificates & secrets → Client secret 발급 - 노트: `tenantId`, `clientId`, `clientSecret` 2. **Azure Bot 리소스** - Azure Portal → Create resource → Azure Bot (Multi Tenant) - Microsoft App ID/Password 발급 → 노트 - **Configuration → OAuth Connection Settings → Add** - Name: `GraphConnection` (이 이름이 `.env`의 `OAUTH_CONNECTION_NAME`) - Service Provider: `Azure Active Directory v2` - Client id / secret: 1번에서 만든 값 - Tenant ID: 1번의 tenantId - Token Exchange URL: 비워두기 (SSO 안 쓸 경우) - Scopes: `Tasks.ReadWrite Group.Read.All User.Read offline_access` - "Test Connection" 으로 토큰이 떨어지는지 확인 3. **Teams 앱 매니페스트** - `appPackage/manifest.json` 의 `REPLACE_WITH_YOUR_BOT_APP_ID` 를 Azure Bot의 App ID로 치환 - `color.png` (192×192), `outline.png` (32×32) 아이콘을 `appPackage/`에 추가 - 폴더를 zip 으로 압축 → Teams 관리자 센터(또는 개발자 포털)에서 사이드로딩 ## 로컬 실행 ```bash npm install cp .env.example .env # .env 채우기 npm run dev ``` 다른 터미널에서 ngrok 등으로 외부 노출: ```bash ngrok http 3978 ``` ngrok URL을 Azure Bot의 **Messaging endpoint** 에 `https:///api/messages` 형태로 설정. ## LLM 교체 `.env` 의 `LLM_PROVIDER` 만 바꾸면 됨: - `claude` → `ANTHROPIC_API_KEY`, `CLAUDE_MODEL` 사용 - `azure-openai` → `AZURE_OPENAI_*` 사용 두 구현 모두 동일한 `LlmClassifier` 인터페이스를 구현하므로 호출부 변경 없음. ## 알아두면 좋을 점 / 함정 - **Planner write는 ETag 필수.** `plannerClient.ts` 가 PATCH 직전 항상 GET으로 ETag를 받아 `If-Match`에 넣는다. 만약 동시 편집이 잦은 환경이면 412 에러를 retry 로 감싸야 한다. - **`/me/planner/plans` 는 사용자가 속한 그룹의 Plan 만 반환한다.** 봇이 "보이지 않는다" 면 Teams 채널에 그 사용자가 멤버인지부터 확인. - **OAuthPrompt 의 토큰은 Bot Framework 의 토큰 서비스(token.botframework.com)에 캐시된다.** `logout` 명령으로 강제 갱신 가능. - **MemoryStorage** 는 개발용. 프로덕션은 CosmosDB/Blob 백엔드로 교체. - **확장 아이디어**: - 다중 후보일 때 카드의 `Action.Submit` 으로 사용자가 직접 plan 선택 - Plans/Buckets 캐시 (현재는 매 발화마다 다시 조회) - 일/주간 요약 발송 (proactive messaging) ## 다음 단계 1. `npm install` → 의존성 받기 2. Azure AD 앱 등록 + Azure Bot 리소스 + OAuth Connection 설정 3. `.env` 채우기 4. `npm run dev` 로 띄우고 ngrok 으로 노출 5. Teams 매니페스트 사이드로딩 → 개인 채팅으로 봇과 대화 6. `"오늘 견적서 초안 작성 시작했어"` 같은 첫 발화로 테스트