VIRGO

Microsoft Teams 추가하기

작성 기준
50a34893e3086a6012fd136d5e4a492a9066c5bc
제품 소스
adapters/teams/README.md, adapters/teams/adapter.json, cli/management-cli.ts, runtime/agent-management.ts, adapters/teams/adapter.ts, adapters/teams/file-port.ts
provider 문서
learn.microsoft.com/…/create-a-bot-for-teams, learn.microsoft.com/…/bot-framework-rest-connector-authentication

이 가이드는 고정된 문서를 그대로 옮긴 것이 아니라 이 사이트가 직접 쓴 글입니다. 위의 제품 커밋을 기준으로 작성했으며, 인용한 모든 제품 경로가 그 커밋에 실제로 있는지 확인했습니다.

provider 포털 단계는 각 provider의 공식 문서를 따릅니다. 문서 끝에 링크가 있으며, provider가 소유한 부분은 그 문서를 따르세요.

Microsoft Teams 추가하기

Teams adapter는 Bot Connector REST 프로토콜을 직접 사용합니다. 일반 activity, 인용 회신, 입력 중 activity, 편집, 리치 카드, 개인 채팅 파일 동의 카드를 보냅니다.

먼저 채팅 앱을 Virgo에 연결하기를 읽으세요. 이미 운영 중인 설치본에 adapter를 등록하는 것은 adapter add로 지원되므로, 아래 단계는 준비해 둘 서술자가 아니라 그 명령에서 끝납니다.

시작하기 전에

  • 이미 운영 중이고 Hub에 닿을 수 있는 Virgo 설치본.
  • 여러분이 관리하는 Microsoft 365 테넌트와 Teams 팀 또는 채팅.
  • Teams용으로 등록된 봇 애플리케이션. Microsoft 공식 문서를 따라 만드세요. Virgo가 애플리케이션이나 봇을 대신 만들지 않습니다.
  • 공개된 HTTPS 메시징 엔드포인트. Teams가 여러분의 봇을 호출합니다. Discord나 Telegram과 달리 이 adapter는 바깥으로 나가는 연결만 가능한 호스트에서는 동작할 수 없습니다.

엔드포인트와 인증 방식

adapter가 제공하는 재사용 가능한 HTTP ingress를 봇의 공개 HTTPS 메시징 경로에 붙이세요. 들어오는 모든 요청은 Microsoft의 Connector 인증 절차가 요구하는 그대로 검증됩니다. 공개된 OpenID/JWKS 문서에 대한 RS256 서명, 고정된 Connector issuer, 정확한 app-id audience, 5분 편차를 허용하는 유효 기간, Teams 키 보증, 서명된 serviceUrl, 그리고 설정된 테넌트와 대화입니다.

HTTP 응답은 adapter가 이벤트를 확인한 뒤에야 완료되므로, 지속적 ingress 커밋이 확인 경계로 남습니다.

HTTPS service URL만 받아들이며, 인라인 자격 증명이나 query, fragment는 허용되지 않습니다. 들어오는 activity는 설정된 service URL, 테넌트, 대화, 그리고 설정된 team과 channel ID와 일치해야 합니다.

클라이언트 시크릿을 비공개로 보관하기

credential은 봇 애플리케이션의 클라이언트 시크릿을 참조하고, appId는 비밀이 아닌 JWT audience이자 OAuth 클라이언트 id입니다. 실행 환경은 확인된 시크릿을 Microsoft의 테넌트별 Bot Framework OAuth 엔드포인트에서 https://api.botframework.com/.default scope로 교환하고, 수명이 짧은 액세스 토큰만 캐시합니다.

설치된 구성에는 액세스 토큰도 클라이언트 시크릿도 들어 있지 않습니다.

필요한 서술자

모두 자리표시자입니다.

{
  "id": "teams-main",
  "kind": "teams",
  "session": "teams-session",
  "vsp": "vsp:/your-account:your-space/teams/bot",
  "credential": "credential:teams/main",
  "appId": "00000000-0000-0000-0000-000000000000",
  "targetVsp": "vsp:/your-account:your-space/your-repo/lead",
  "inboundPort": "adapter-port:teams/inbound",
  "conversation": {
    "ref": { "value": "teams.conversation:main" },
    "conversationId": "your-provider-conversation-id",
    "serviceUrl": "https://smba.example.test/teams",
    "tenantId": "your-tenant-id",
    "teamId": "your-team-id",
    "channelId": "your-channel-id"
  }
}

conversation.ref는 기본 아웃바운드 대화이기도 합니다. 의존성 해석과 HTTP 라우팅은 설치된 adapter id 단위로 유지되므로, 하나의 봇을 공유하는 두 대화에 같은 자격 증명 참조가 의도적으로 두 번 나타날 수 있습니다.

접근과 관리자

Teams는 그 인증된 binding 안에서 open이 기본입니다. 여기서 binding은 여러분이 설정한 테넌트와 대화이며, 메시지 안의 내용이 아니라 검증된 JWT가 그것을 증명합니다. 공용 게이트가 restricted나 approval로 덮어쓸 수 있습니다.

관리자는 /virgo-access approve <requestId> 또는 /virgo-access deny <requestId>로, 아니면 타입이 정해진 카드 데이터 { "kind": "virgo.channel_access.decision.v1", "decision": "approve" | "deny", "requestId": "..." } 를 제출해 대기 중인 요청을 결정합니다.

카드 제출 데이터는 클릭한 행위자와 원래 맥락과 함께 검증된 상호작용으로 보존됩니다. 이는 신뢰할 수 없는 입력이며 아무 권한도 주지 않습니다.

카드와 파일, 반응

지원되는 출력은 Adaptive Card 1.4, Hero Card, Thumbnail Card입니다. Adaptive 본문은 텍스트, fact, 이미지, 텍스트 입력을 지원하고, action은 Action.Submit과 HTTPS Action.OpenUrl을 지원합니다. Action.Execute의 invoke 응답 처리는 이 adapter의 범위 밖입니다.

한도는 정확합니다. 메시지 텍스트는 32,000 UTF-8 바이트, 카드는 JSON 64 KiB, 제출 데이터는 최대 16 KiB에 중첩 8단계, 노드 256개, 배열이나 객체마다 항목 100개이며 문자열은 최대 8 KiB입니다. 지원되지 않는 카드 속성과 안전하지 않은 이미지·action URL은 네트워크 호출 전에 거부됩니다.

파일을 보내는 것은 개인 채팅 전용입니다. Teams 파일 동의를 사용하며, Teams 앱 manifest에 supportsFiles: true가 필요하고, conversation.personalRecipient와 파일 port가 필요합니다. 그룹·채널 파일 전달은 Microsoft Graph와 사용자 인가가 필요하며 이 adapter의 범위 밖입니다. 그것을 전제로 계획하지 마세요.

파일을 받는 것도 개인 채팅이며, 관리형 port가 필요합니다. Teams의 직접 파일 API는 개인 채팅 기능입니다. 개인 채팅에서 첨부된 파일은 인증된 file.download.info 첨부로 도착하고, 이 adapter가 읽는 것이 바로 그것입니다. 그룹·채널 파일은 Microsoft Graph의 영역이며, 그룹·채널 파일 전송은 어느 방향으로도 여기에 구현되어 있지 않습니다.

개인 채팅의 경우를 성립시키는 조건은 두 가지입니다. Teams 앱 manifest의 supportsFiles: true, 그리고 storeInbound를 제공하는 관리형 파일 port입니다. 둘이 갖춰지면 adapter는 그 바이트를 직접 내려받고 — 내려받기 URL은 HTTPS여야 하고 자격 증명을 담을 수 없으며 리다이렉트는 거부됩니다 — 50 MiB를 넘는 것은 거부한 뒤 storeInbound로 저장합니다. 관리 port는 이를 30일 보관으로 artifact 저장소에 씁니다. 저장된 artifact는 인바운드 이벤트에 함께 실리므로 agent는 메시지와 함께 파일을 봅니다. 파일만 있는 메시지는 [attachment <name>]으로 도착합니다. 저장은 테넌트와 activity, 파일 id로 키가 잡히므로 같은 activity가 다시 전달돼도 한 번만 저장됩니다. 그 port가 없으면 activity는 파일 없이 전달되는 대신 CAPABILITY_UNSUPPORTED로 거부됩니다.

반응은 공식 Teams API 클라이언트를 사용하며, 이 대화의 정확한 service URL과 이미 설정된 봇 토큰으로 만들어집니다. ID는 정확한 반응 ID 3,112개의 고정 카탈로그와, Microsoft 자체 참고 문서가 기록한 이모지 별칭에서 옵니다. 유사 일치는 없으며, 알 수 없는 값은 거부됩니다.

adapter는 Teams에 무언가를 바꿔 달라고 요청하기 전에 자신이 남긴 반응을 기록합니다. 그래서 교체는 기록된 이전 반응을 제거하고, 제거는 기록된 반응을 제거하며, 이는 Host 재시작을 건너서도 유지됩니다. 자신이 남겼다고 기록하지 않은 반응은 절대 지우지 않습니다.

이미 있는 설치본에 등록하기

새 설치본을 만들 때는 install이 --adapters와 --host-settings로 서술자를 받습니다. 설치본이 이미 있다면 지원되는 경로는 adapter add입니다.

adapter add --file /absolute/path/to/teams.json
adapter list
adapter remove teams-main

경로는 절대 경로여야 합니다. 이미 목록에 있는 id는 교체되지 않고 충돌로 거부되며, 새 항목만 준비되고, 그다음 목록 전체를 부팅이 검증하는 방식 그대로 검증합니다. 그래서 추가가 시작되지 않을 구성을 남길 수 없습니다. 서술자의 credentialBinding은 별도의 단계가 아니라 같은 작업 안에서 등록됩니다. adapter list는 각 항목이 지칭하는 자격 증명 참조와 함께 목록을 되읽으며, 비밀 자체는 보여 주지 않습니다.

다음 Host 시작에서 활성화됩니다. 동작 중인 Host는 부팅할 때 가지고 있던 adapter를 유지하고, 네이티브 agent 세션은 계속 돌아갑니다.

Teams를 추가하려고 Hub를 다시 설치하지 마세요.

재전송 안전성

Bot Connector에는 effect-key 멱등성도, 알 수 없는 제출을 조회하는 수단도 없습니다. 모호한 전송이나 업로드 결과는 unknown으로 남고 그 adapter 인스턴스가 다시 제출하지 않습니다. 파일 port 커밋이 동의 완료의 지속적 인계 지점이며, 동일한 커밋이 반복될 때 멱등적으로 처리해야 합니다.

문제 해결과 제거

  • 아무것도 도착하지 않습니다. 엔드포인트는 HTTPS로 공개적으로 닿을 수 있어야 하고 Microsoft가 받아들이는 인증서를 제시해야 합니다. 검증 실패는 재시도가 아니라 거부입니다.
  • activity가 거부됩니다. 들어온 activity의 service URL과 테넌트, 대화를 설정된 값과 비교하세요. 불일치는 설계상 거부됩니다.
  • 파일 카드가 아무 동작도 하지 않습니다. 그 채팅이 개인 채팅인지, 앱 manifest에 supportsFiles: true가 있는지 확인하세요.
  • 인바운드 파일이 거부됩니다. 그 채팅이 개인 채팅인지, 그리고 storeInbound를 제공하는 관리형 파일 port가 설정되어 있는지 확인하세요. 없으면 파일이 실린 activity는 파일 없이 전달되는 대신 거부됩니다.
  • 제거. 테넌트에서 봇을 제거하고 자격 증명을 폐기하세요. 기존 설치본의 구성에서는 adapter remove <id>로 빼내며, Host는 다음 시작부터 그것을 제공하지 않습니다.

오늘 검증된 것

adapter 소스는 통합되어 있고 구성이 검증됩니다. 그것이 여러분의 테넌트에서의 실제 봇 검증은 아닙니다. 이미 있는 설치본에 등록하는 일은 정규 경로에서 Discord에 대해서만 수행되었고 거기서 Gateway READY까지 도달했습니다. agent가 Seat을 claim하는 것, 사람의 인바운드 메시지, 그에 상응하는 회신은 Teams를 포함한 모든 provider에서 아직 검증되지 않았습니다. 가용성과 증거를 보세요.

프로토콜 참고 자료: 메시지 송수신, Connector 인증, 리치 카드, 파일 동의.