Skip to content

Latest commit

 

History

46 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Acknowledgements

This project is based in part on the Microsoft Graph Communications EchoBot sample.

The original sample is licensed under the MIT License. See the LICENSE file for details.

Note:
Public Samples are provided by developers from the Microsoft Graph community.
Public Samples are not official Microsoft Communication samples, and not supported by the Microsoft Communication engineering team. It is recommended that you contact the sample owner before using code from Public Samples in production systems.


DeciScope Teams Voice Bot

Description: This application receives Teams meeting audio through the media socket. In Echo mode it sends the received audio back for compatibility with the upstream sample. In Speech Service mode it continuously transcribes the audio with Azure AI Speech and forwards transcript segments to the DeciScope Go API. It does not synthesize or send text-to-speech audio. The repository also contains the upstream VMSS deployment pipelines. Authors: @bcage29 and @brwilkinson


Table of Contents


Introduction

This project adapts the Teams Voice Echo Bot sample for DeciScope. It demonstrates how to receive the audio stream from a Teams call or meeting and includes scripts and pipelines for running the Bot on Azure VMSS.

After the Bot joins a meeting, Echo mode sends received audio back to the meeting. Speech Service mode instead performs continuous speech-to-text recognition and forwards the resulting transcript segments to the DeciScope Go API. Refer to the supported languages in the Speech Service documentation.

Echo Mode

This is the default mode when deployed (UseSpeechService == false). In this mode, the bot will listen to the inbound audio stream and will send the same stream of data back on the Audio Socket. This will create an echo and you will hear yourself repeated.

Speech Service Mode

In this mode, the Bot continuously sends meeting audio to Azure AI Speech for speech-to-text recognition. Recognized partial and final transcript segments are queued and forwarded to the configured DeciScope Go API endpoint. There is no keyword trigger, text-to-speech synthesis, or synthesized audio response.

To use Speech Service mode, set the following environment variables:

"UseSpeechService": true,
"SpeechConfigKey": "", // key for your speech service
"SpeechConfigRegion": "eastus2", // region where your speech service is deployed
"BotLanguage": "en-US", // legacy recognition language key; es-MX, fr-FR

Getting Started

  • Clone the Git repo for the Microsoft Graph Calling API Samples. Please see the instructions here to get started with VSTS Git.
  • Log in to your Azure subscription to host web sites and bot services.
  • Launch Visual Studio Code or open a terminal to the root folder of the sample.
  • Fork the repo or clone it and then push it to your own repo in GitHub.

Create a PFX Certificate

The Bot requires an SSL certificate signed by a Certificate Authority. If you don't have a certificate for your domain, you can create a free SSL certificate.

  1. Verify you have access to make DNS changes to your domain or buy a new domain.
  2. Install certbot
    • a. Follow the installation instructions
    • b. If 'certbot' command is not recognized in the terminal, add the path to the certbot.exe to the environment variables path ($env:Path)
  3. Open a terminal as an Adminstrator where certbot is loaded
  4. Execute
certbot certonly --manual --preferred-challenges=dns -d '*.example.com' --key-type rsa
  1. This will create a wildcard certificate for example.com.
  2. Follow the instructions and add the TXT record to your domain
  3. This will create PEM certificates and the default location is 'C:\Certbot\live\example.com'
  4. Install OpenSSL to convert the certifcate from PEM to PFX
  5. Execute
openssl pkcs12 -export -out C:\Certbot\live\example.com\star_example_com.pfx -inkey C:\Certbot\live\example.com\privkey.pem -in C:\Certbot\live\example.com\cert.pem
  1. Copy the path to the PFX certificate "C:\Certbot\live\example.com\star_example_com.pfx

Bot Registration

  1. Follow the instructions Register a Calling Bot. Take a note of the registered config values (Bot Id, MicrosoftAppId and MicrosoftAppPassword). You will need these values in the code sample config. NOTE: This step is creating an Azure Bot. If the bot is not created and configured correctly, the bot will not be able to join the meeting.

  2. Add the following Application Permissions to the bot:

    • Calls.AccessMedia.All
    • Calls.JoinGroupCall.All
  3. The permissions need to be consented by tenant admin. Go to "https://login.microsoftonline.com/common/adminconsent?client_id=<app_id>&state=<any_number>&redirect_uri=<any_callback_url>" using tenant admin to sign-in, then consent for the whole tenant.

Prerequisites

General

  • Visual Studio (only needed if running locally). You can download the community version here for free.
  • PowerShell 7.0+
  • Mirosoft Azure Subscription (If you do not already have a subscription, you can register for a free account)
  • An Office 365 tenant enabled for Microsoft Teams, with at least two user accounts enabled for the Calls Tab in Microsoft Teams (Check here for details on how to enable users for the Calls Tab)
  • Install .Net Framework 4.7.1. The solution will not build if you do not install this.
  • You will need Postman, Fiddler, or an equivalent installed to formulate HTTP requests and inspect the responses. The following tools are widely used in web development, but if you are familiar with another tool, the instructions in this sample should still apply.

Setup Script

  • PowerShell 7.0+
  • Azure Az PowerShell Module
    • Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force
  • GitHub CLI
    • This is not a hard requirement, but will automate the step to save the secret in your repo.
  • Must be an owner of the Azure subscription where you are deploying the infrastructure.
  • Must have permissions to create an Azure AD Application.
  • Note: The Azure Bot must be created in a tenant where you are an adminstrator because the bot permissions require admin consent. The bot infrastructure does not need to be in the same tenant where the Azure bot was created. This is useful if you are not an administrator in your tenant and you can use a separate tenant for the Azure Bot and Teams calling.
Secret Name Message
localadmin 'localadmin' is the username for the admin on the provisioned VMSS VMs. The password entered is the password to login and will be configured for all VMs.
AadAppId This is the Azure AD Application Client Id that was created when creating an Azure Bot. Refer to the registration instructions
AadAppSecret Client Secret created for the Azure AD Application during the Azure Bot registration.
ServiceDNSName Your public domain that will be used to join the bot to a call (ie bot.example.com)
UseSpeechService True or False setting to set the bot in Echo mode or Speech Service mode. If 'true', the following secrets need to be set.
SpeechConfigKey The Speech Service Key
SpeechConfigRegion The region where the Speech Service is deployed
BotLanguage Legacy recognition language setting (for example, en-US, es-MX, or fr-FR).

Deploy

PowerShell DSC

PowerShell Desired State Configuration (DSC) enables you to manage your IT development infrastructure with configuration as code. This sample uses DSC to configure the VMs to run the Teams Voice Echo Bot. Here are a few examples of where we are using DSC:

  • Set environment variables on the VM
  • Install software
  • Install the windows service

DSC Resources

Deploy the Prerequistes

  1. Navigate to the root directory of the sample in PowerShell.
  2. Run Get-AzContext to ensure you are deploying to the correct subscription.
    • You need to have the owner role on the subscription
    • You need permissions to create a Service Principal
  3. Run .\deploy.ps1 -OrgName <Your 2 - 7 Character Length Letter Abbreviation>
    • ie .\deploy.ps1 -OrgName TEB -Location eastus2
    # Option 1. Execute all pre-req steps i.e. run setup to deploy
    . .\deploy.ps1 -orgName <yourOrgName> -Location centralus
    # E.g.
    . .\deploy.ps1 -orgName DNA -Location centralus
    
    # Option 2. After you have run setup the first time, re-execute setup
    . .\deploy.ps1 -orgName <yourOrgName> -Location centralus -RunSetup
    # E.g.
    . .\deploy.ps1 -orgName DNA -Location centralus -RunSetup
    
    # Option 3a. After you have run setup you can deploy from the commandline
    . .\deploy.ps1 -orgName <yourOrgName> -Location centralus -RunDeployment
    # E.g.
    . .\deploy.ps1 -orgName DNA -Location centralus -RunDeployment

    # Option 3b. Alternatively skip 4a, check your code changes in and push to your repo
    # The deployment will exectute via GitHub workflow instead
    - You can manually run the 'BUILD' workflow to build the code
    - You can manually run the 'INFRA' workflow after the previous workflow to deploy the infrastructure

This script will do the following:

  1. Create a resource group with the naming convention ACU1-TEB-BOT-RG-D1 (Region Abbreviation - Your Org Name - BOT - Resource Group - Environment)
  2. Create a storage account
    • Grant current user the 'Storage Blob Data Contributor' role
    • Grant the service principal the 'Storage Blob Data Contributor' role
  3. Create a Key Vault
    • And grant current user the 'Key Vault Administrator' role
  4. Create an Azure AD Application
    • The Application will be granted the 'Owner' role to the subscription.
  5. Crete a GitHub Secret wiht name AZURE_CREDENTIALS__BOT
    {
        "clientId": "<GitHub Service Principal Client Id>",
        "clientSecret": "<GitHub Service Principal Secret>",
        "tenantId": "<Tenant ID>",
        "subscriptionId": "<Subscription ID>",
        "activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
        "resourceManagerEndpointUrl": "https://management.azure.com/",
        "activeDirectoryGraphResourceId": "https://graph.windows.net/",
        "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
        "galleryEndpointUrl": "https://gallery.azure.com/",
        "managementEndpointUrl": "https://management.core.windows.net/"
    }
  6. Generate the deployment parameters file, build workflow and infrastructure workflow
  7. Upload the PFX certificate to Key Vault
  8. Add the secrets and environment variables to Key Vault

After the script runs successfully, you should see the following:

  1. New resource group with the following resources:
    • Storage Account
    • Key Vault
  2. Azure AD Application in Azure AD
  3. In your GitHub Repo, Navigate to Settings > Secrets. You should see a new secret named 'AZURE_CREDENTIALS__BOT'
  4. Three new files have been created. Check these files in and push them to your repo.
    • app-build-.yml
    • app-infra-release-.yml
    • azuredeploy.parameters.json
  5. Once these files have been pushed to your repo, they will kick of the infrastructure and code deployment workflows.

Deploy the Infrastructure

The GitHub Action app-infra-release-.yml deploys the infrastructure.

You can also run the infrastructure deployment locally using the -RunDeployment flag.

.\deploy.ps1 -OrgName TEB -RunDeployment

Update DNS

Your DNS Name for your bot needs to point to the public load balacer in order to call your bot and have it join a meeting.

  1. Find the public IP resource for the load balancer and copy the DNS name.
  2. Navigate to your DNS settings for your domain and create a new CNAME record. ie CNAME bot acu1-teb-bot-d1-lbplb01-1.eastus2.cloudapp.azure.com

Deploy the Solution

The GitHub Action app-build-.yml builds the solution and uploads the output to the storage account. Once the infrastructure is deployed, DSC will pull the code from the storage account.

Running the Bot

会議への参加は、次節の認証付き DeciScope Bot 制御 API を使用します。

DeciScope Bot 制御 API

フロントエンドは VM Bot を直接呼びません。会議 URL 登録後の流れは常に Frontend -> Go API -> VM Bot -> Teams meeting です。VM Bot は WebSocket に 接続せず、WebSocket 配信は Go API からフロントエンドへの経路で扱います。

現在のプロダクト構成では、VM Bot はWindows VM上のプロセスまたはWindows Serviceとして 配置し、Docker Composeには含めません。Docker側ではGo API/PostgreSQL/Webを起動し、 Go APIからVM Botの制御APIへTailscale経由で接続します。

VM Bot は Go API から次の制御 API を受け付けます。

POST /internal/bot/join
POST /internal/bot/meeting-sessions/{sessionId}/end
GET  /healthz

POST /internal/bot/join のリクエスト:

{
  "sessionId": "session_...",
  "joinUrl": "https://teams.microsoft.com/l/meetup-join/..."
}

ヘッダー:

X-DeciScope-Bot-Control-Token: <DECISCOPE_BOT_CONTROL_TOKEN>

POST /internal/bot/meeting-sessions/{sessionId}/end も同じヘッダーで認証します。 リクエスト本文は省略可能です。指定する場合は次の形式で、本文の sessionId は ルートの値と一致する必要があります。

{
  "sessionId": "session_...",
  "botCallId": "call_...",
  "reason": "manual_end_requested"
}

終了命令は 202 Accepted を返し、レスポンスの activeCallFound で対象callが 見つかったかを示します。Go APIは DECISCOPE_BOT_CONTROL_URL の末尾 /internal/bot/join から終了用URLを組み立てるため、VMではjoinとendの両方の パスを同じ待受ポートで到達可能にしてください。

制御 API 用の環境変数:

  • DECISCOPE_BOT_JOIN_MODE: join 許可モード。未設定時は既存互換の auto_user_trigger
  • DECISCOPE_BOT_CONTROL_TOKEN: Go API からの制御命令用共有トークン。 未設定時は制御 API の join を有効にしません。ログには出しません。
  • DECISCOPE_BOT_CONTROL_BIND_URL: 制御 API の追加待受 URL。例: http://0.0.0.0:7071。未設定時は既存の Bot ホスト URL だけで待ち受けます。
  • DECISCOPE_DEFAULT_TENANT_ID: https://teams.microsoft.com/meet/{meetingId}?p={passcode} 形式の短い Teams 会議 URL で使う既定 tenant ID。単一テナント運用ではこの値を 設定してください。値そのものはログに出しません。

DECISCOPE_BOT_JOIN_MODE の値:

  • command: Go API からの POST /internal/bot/join でのみ join します。
  • auto_user_trigger: 既存の特定ユーザー入室トリガーでのみ join します。
  • both: 移行期間用。制御 API と既存トリガーの両方を許可します。
  • disabled: join しません。/healthz は確認できます。

本番想定は command です。移行期間は both を使えます。command または both でも DECISCOPE_BOT_CONTROL_TOKEN が未設定なら POST /internal/bot/join は有効になりません。

Go API 側には VM Bot の制御 API URL を設定します。Tailscale を使う場合の例:

http://<VM_TAILSCALE_IP>:7071/internal/bot/join

VM Bot 側の待受例:

[Environment]::SetEnvironmentVariable("DECISCOPE_BOT_JOIN_MODE", "command", "Machine")
[Environment]::SetEnvironmentVariable("DECISCOPE_BOT_CONTROL_TOKEN", "<長いランダム値>", "Machine")
[Environment]::SetEnvironmentVariable("DECISCOPE_BOT_CONTROL_BIND_URL", "http://0.0.0.0:7071", "Machine")
[Environment]::SetEnvironmentVariable("DECISCOPE_DEFAULT_TENANT_ID", "<tenant-guid>", "Machine")

Machine スコープの環境変数を変更した後は、Bot プロセスまたは Windows サービス を再起動してください。

可能なら DECISCOPE_BOT_CONTROL_BIND_URL は VM の Tailscale IP に bind して ください。難しい場合は http://0.0.0.0:7071 で待ち受け、Windows Firewall で Tailscale 側からの接続だけを許可します。

New-NetFirewallRule `
  -DisplayName "DeciScope Bot Control API from Tailscale" `
  -Direction Inbound `
  -Action Allow `
  -Protocol TCP `
  -LocalPort 7071 `
  -RemoteAddress <GO_API_TAILSCALE_IP>

接続確認:

Invoke-WebRequest `
  -Uri "http://<VM_TAILSCALE_IP>:7071/healthz" `
  -UseBasicParsing

手動 join 命令テスト:

$body = @{
  sessionId = "manual-session-1"
  joinUrl = "https://teams.microsoft.com/l/meetup-join/..."
} | ConvertTo-Json

Invoke-WebRequest `
  -Uri "http://localhost:7071/internal/bot/join" `
  -Method POST `
  -Headers @{ "X-DeciScope-Bot-Control-Token" = $env:DECISCOPE_BOT_CONTROL_TOKEN } `
  -ContentType "application/json" `
  -Body $body `
  -UseBasicParsing

短い Teams 会議 URL 形式も利用できます。

$body = @{
  sessionId = "manual-session-meet-url"
  joinUrl = "https://teams.microsoft.com/meet/<meetingId>?p=<passcode>"
} | ConvertTo-Json

Invoke-WebRequest `
  -Uri "http://localhost:7071/internal/bot/join" `
  -Method POST `
  -Headers @{ "X-DeciScope-Bot-Control-Token" = $env:DECISCOPE_BOT_CONTROL_TOKEN } `
  -ContentType "application/json" `
  -Body $body `
  -UseBasicParsing

/meet/{meetingId}?p={passcode} 形式では URL に tenant ID が含まれないため、 tenant ID は次の順で解決します。

  1. 制御 API リクエスト本文の tenantId
  2. DECISCOPE_DEFAULT_TENANT_ID
  3. どちらも無い場合は join せず failed を Go API に報告

既存の https://teams.microsoft.com/l/meetup-join/... 形式は引き続き対応します。 短い URL の passcodejoinUrl 全文、control token はログに出しません。

制御 API は命令受付後すぐ 202 Accepted を返し、join 処理はバックグラウンドで 実行します。同じ sessionId が処理中の場合も二重実行せず 202 Accepted を 返します。ログには sessionId を出しますが、トークンと joinUrl 全文は出し ません。

command join 経由でも、会議参加後は auto user trigger 経由と同じ CallHandler / BotMediaStream / Azure Speech / TranscriptForwarder の パイプラインを使います。command join では sessionIdcallId を紐づけ、 文字起こし POST に sessionId を含めます。

Go APIへraw audioを送るMedia Ingressは使いません。Teams音声のSTTはこのBot内の Azure Speech pipelineで行い、Go APIへはtranscript segmentだけをHTTP POSTします。

制御 API 経由 join の状態は、既存の DECISCOPE_TRANSCRIPT_API_URL から /api/v1 のベース URL を推定し、次の Go API へ PATCH します。

PATCH /api/v1/bot/meeting-sessions/{sessionId}/status

認証ヘッダーは文字起こし送信と同じ X-DeciScope-Api-Key、値は DECISCOPE_TRANSCRIPT_API_KEY です。送信する status は joiningjoinedrecordingendedfailed です。状態更新に失敗した場合はログに残しますが、 無限再試行はしません。

会議タイトルを DeciScope API へ反映する場合は、Bot から PATCH /api/v1/bot/meeting-sessions/{sessionId}/metadatatitletitleSourceproviderthreadIdjoinMeetingIdorganizerId などを送信します。Bot は join URL の query/context に subject 相当が含まれる場合にまずそれを使い、取得できない 場合は Microsoft Graph で次の順に解決を試します。

  • 短い https://teams.microsoft.com/meet/{meetingId}?p=... URL の canonical URL 解決
  • canonical URL の context に含まれる Tid / Oid の抽出
  • joinMeetingId による /users/{candidateUserId}/onlineMeetings 検索
  • joinWebUrl による /users/{candidateUserId}/onlineMeetings 検索
  • joinUrl による /users/{candidateUserId}/events 検索

candidateUserId は Entra object id です。URL context の Oid、join URL から解析できた organizer id、 AppSettings__DefaultMeetingOrganizerUserIdAppSettings__MeetingTitleLookupUserIds の順に重複を除いて試します。 AppSettings__MeetingTitleLookupUserIds はカンマ、セミコロン、空白区切りで複数指定できます。UPN/email が 渡された場合は、先に /users/{upn}?$select=id,userPrincipalName,mail で object id に解決してから onlineMeeting / calendar lookup に使います。Go API からの command join では、object id は candidateUserIds、UPN/email は candidateUserPrincipalNames として Bot join command に渡されます。

Graph 取得には、実行主体に OnlineMeetings.Read.All、必要に応じて Calendars.Read / Calendars.ReadBasic.All 相当のアプリケーション権限と管理者同意が 必要です。/users/{id}/onlineMeetings を application permission で使う場合は、 対象ユーザーに対する Teams application access policy が必要になることがあります。 権限・ポリシー・organizer 不明などで取得できない場合は、permission_missingadmin_consent_missingapplication_access_policy_missinguser_not_allowed_by_policymeeting_not_foundcandidate_user_not_foundcalendar_permission_missing などの reason を Bot ログと session metadata に残します。取得できない場合、PC 側は user input title、それもなければ Teams会議 を fallback title として表示します。

会議参加を伴わずタイトル解決だけを確認する場合は、Bot control token 付きで 次の debug endpoint を呼びます。

POST /api/v1/debug/resolve-meeting-title
X-DeciScope-Bot-Control-Token: <control-token>
{
  "joinUrl": "https://teams.microsoft.com/meet/...",
  "tenantId": "...",
  "joinMeetingId": "4426674024458",
  "candidateUserIds": ["00000000-0000-0000-0000-000000000000"],
  "candidateUserPrincipalNames": ["organizer@example.com"]
}

response には titletitleSourcecanonicalJoinWebUrlorganizerIdtitleResolutionErrorCodeattempts が含まれます。

status の意味:

  • joining: join 命令を受け付け、Teams 会議への参加処理を開始しました。
  • joined: Graph の join 要求が成功し、Bot の call が作られました。
  • recording: Azure Speech へ音声を流せる状態になりました。
  • ended: call が終了しました。
  • failed: join または Speech pipeline の開始に失敗しました。

joined だけでは文字起こし開始を意味しません。recording まで進むと、 Speech recognizer、PushAudioInputStream、audio frame queue が ready になって います。

Speech audio frame dropped が出る場合は、ログの ReasonSpeechPipelineReady を確認してください。

  • Reason=SpeechPipelineNotStarted: call は作られていますが、Speech pipeline がまだ開始されていません。
  • Reason=SpeechPipelineNotReady: Speech 開始処理中、または開始失敗後です。
  • Reason=SpeechQueueFull: Speech queue が詰まっています。
  • PeakAmplitude=0; RmsAmplitude=0: 会議側から届いた audio frame が無音の可能性 があります。SpeechPipelineReady=True なら pipeline 自体は動いています。

command join の手動確認では、joined の後に Speech pipeline startedStatus=recording が出ることを確認してください。その後、会議内で発話すると Speech recognized.Transcript forwarding queued.Transcript forwarding succeeded. の順に進みます。

制御 API 経由で参加した会議の文字起こし POST には sessionId が追加されます。 既存の auto_user_trigger 経由では sessionId は省略されます。既存の JSON 項目は変更しません。

DeciScope 文字起こし送信

文字起こし送信は環境変数だけで設定します。共有 API キーは、ソースコード、 appsettings、Git 管理対象ファイル、ログ、エラーメッセージには記載しないで ください。

VM 側で必須の環境変数:

  • DECISCOPE_TRANSCRIPT_FORWARD_ENABLED: 送信を有効にする場合は true
  • DECISCOPE_TRANSCRIPT_API_URL: Go API の文字起こし受信エンドポイントの 絶対 URL。
  • DECISCOPE_TRANSCRIPT_API_KEY: 共有キー。Go API 側の DECISCOPE_INGEST_API_KEY と同じ値を設定します。

任意の環境変数:

  • DECISCOPE_TRANSCRIPT_API_TIMEOUT_SECONDS: HTTP 送信 1 回ごとのタイムアウト。 既定値は 5 秒。
  • DECISCOPE_TRANSCRIPT_API_MAX_RETRY_ATTEMPTS: 再試行対象の失敗に対する最大 試行回数。既定値は 3 回。
  • DECISCOPE_TRANSCRIPT_FORWARD_QUEUE_CAPACITY: メモリ上の有界送信キューの 容量。既定値は 1000 件。
  • DECISCOPE_BOT_HEARTBEAT_SECONDS: Bot 生存確認のハートビート送信間隔(秒)。 既定値は 20 秒。0 以下を指定するとハートビート送信は無効になります。 Go API 側 watchdog の DECISCOPE_SESSION_BOT_LOST_AFTER_SECONDS(既定 60 秒)より十分小さい値(1/3 以下を推奨)にしてください。この値が LOST_AFTER 以上だと喪失/復旧の誤検知が、DECISCOPE_SESSION_BOT_END_AFTER_SECONDS (既定 180 秒)以上だと正常な会議の自動終了が発生します。

既存の文字起こし受信パス:

/api/v1/transcript-segments

Go API を PC ホスト上の Docker コンテナで動かす場合、VM からは PC ホストの Tailscale IP と、Go API のホスト側公開ポートへ接続します。Docker Compose の サービス名、コンテナ IP、PostgreSQL のアドレス、VM から見た localhost は 使用しないでください。

$env:DECISCOPE_TRANSCRIPT_FORWARD_ENABLED = "true"
$env:DECISCOPE_TRANSCRIPT_API_URL = "http://<Tailscale IP>:<公開ポート>/api/v1/transcript-segments"
$env:DECISCOPE_TRANSCRIPT_API_KEY = "<Go側と同じ共有キー>"

Machine スコープへ永続設定する例:

[Environment]::SetEnvironmentVariable(
  "DECISCOPE_TRANSCRIPT_FORWARD_ENABLED",
  "true",
  "Machine"
)

[Environment]::SetEnvironmentVariable(
  "DECISCOPE_TRANSCRIPT_API_URL",
  "http://<Tailscale IP>:<公開ポート>/api/v1/transcript-segments",
  "Machine"
)

[Environment]::SetEnvironmentVariable(
  "DECISCOPE_TRANSCRIPT_API_KEY",
  "<Go側と同じ共有キー>",
  "Machine"
)

Machine スコープの環境変数を変更した後は、Bot プロセスまたは Windows サービス を再起動してください。Bot を Windows Service Control Manager、タスク スケジューラ、IIS、その他のプロセス管理方式で起動している場合は、その起動 プロセスが Machine スコープの環境変数を読み取れることを確認してください。

Go API がヘルスチェックエンドポイントを公開している場合は、会議を開始する前に VM から Tailscale 経由で接続できることを確認します。

Invoke-WebRequest `
  -Uri "http://<Tailscale IP>:<公開ポート>/healthz" `
  -UseBasicParsing

Invoke-WebRequest `
  -Uri "http://<Tailscale IP>:<公開ポート>/readyz" `
  -UseBasicParsing

動作確認の順序:

  1. PC 側で PostgreSQL、マイグレーション、Go API を Docker Compose で起動する。
  2. PC 側で /healthz/readyz を確認する。
  3. VM から Tailscale 経由で /healthz/readyz を確認する。
  4. Bot を起動し、Teams 会議へ参加させる。
  5. Speech recognized. ログを確認する。
  6. C# 側で CallIdSequenceNo を含む文字起こし送信成功ログを確認する。
  7. Go コンテナ側の受信ログを確認する。
  8. PostgreSQL に文字起こし行が保存され、(call_id, sequence_no) の重複がない ことを確認する。

Build and test:

dotnet restore src\EchoBot.sln
dotnet build src\EchoBot.sln -c Release -p:Platform=x64
dotnet test src\EchoBot.sln -c Release -p:Platform=x64

External configuration required for a real Teams meeting test:

  • Azure Bot registration configured as a calling bot.
  • Entra ID app registration for the bot application.
  • Microsoft Graph application permissions such as Calls.AccessMedia.All and Calls.JoinGroupCall.All, with tenant admin consent.
  • Valid public TLS certificate and CertificateThumbprint.
  • Public DNS name and calling webhook URL that match the configured certificate and bot endpoints.
  • Reachable media ports, Windows Firewall rules, and Azure NSG rules for the media platform.
  • Teams application policy and Microsoft 365 licensing appropriate for calling and meeting access in the tenant.

Local Testing

Refer to the Microsft Graph Documentation on (Local Testing)[https://microsoftgraph.github.io/microsoft-graph-comms-samples/docs/articles/Testing.html]

Note: The certificate is used by the MediaPlatformInstanceSettings and needs to match the ServiceFqdn property of that class.

Example: Using a custom domain with ngrok

  • Domain: example.com
  • Certificate: *.contoso.com
  • ServiceDnsName: bot.contoso.com
  • MediaDnsName: tcp.contoso.com
  • MediaInstanceExternalPort: 12332

DNS Entries

Type Name Value
CNAME bot ra8sxx2z.cname.us.ngrok.io.
CNAME tcp 1.tcp.ngrok.io.

ngrok config

authtoken: <yourAuthToken>
tunnels:
  bot-signaling:
    proto: http
    addr: "9442"
    hostname: bot.contoso.com
  bot-media:
    proto: tcp
    addr: 8445
    remote_addr: 1.tcp.ngrok.io:12332

Example: Using an ngrok subdomain with multi-level subdomain certificate

  • Domain: contoso.com
  • Certificate: *.bot.contoso.com
  • ServiceDnsName: bot.contoso.com
  • MediaDnsName: 5.bot.contoso.com
  • MediaInstanceExternalPort: 12332

DNS Entries

Type Name Value
CNAME 5.bot 5.tcp.ngrok.io.

ngrok config

authtoken: <yourAuthToken>
tunnels:
  bot-signaling:
    proto: http
    addr: "9442"
    hostname: signal.ngrok.io
  bot-media:
    proto: tcp
    addr: 8445
    remote_addr: 5.tcp.ngrok.io:12332

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages