在 Teams 中啟用會議錄影時,系統同時也會產生會議的文字記錄 (Transcript)。
這些文字記錄不僅能協助參與者回顧內容,還能交給 GPT 或其他工具產生更完整的會議紀錄。
一般情況下,只有會議主持人才能手動下載這些文字記錄。
那麼,是否能讓應用程式自動存取並下載會議文字記錄呢?本文將逐步示範實作方式。
由於必須透過 AP 存取,因此需先註冊一個 Azure AD App。完成後建立 Client Secret,並保存其 Value,稍後將用於取得存取 Token。詳細可以參考Teams App 代替使用者建立線上會議,讓該使用者為會議主持人的說明來註冊 App。
需要 Microsoft Graph API, Type 為 Application 的以下權限,
User.ReadBasic.AllCalendars.ReadBasic.AllOnlineMeetings.Read.AllOnlineMeetingTranscript.Read.AllTeam.ReadBasic.AllTeamSettings.Read.All
設定完成後,請記得該 Azure 管理者 按一下 Grant admin consent for … 允許 App 可以用這些 API,如下圖所示:

以下為 C# Console 的程式碼,先設定需要的變數,例如 clientId, clientSecret …
using System.Dynamic;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
var clientId = "{Application (client) ID}";
var clientSecret = "{clientSecret}";
var tenantId = "{Directory (tenant) ID}";
var userPrincipalName = "{通常是email}";
Console.OutputEncoding = Encoding.UTF8;
var contentType = "application/json";
var client = new HttpClient();
先取得 App 的 access token,用於呼叫 Graph API
var getTokenUrl = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token";
var authBody = $"grant_type=client_credentials&client_id={clientId}&client_secret={clientSecret}&scope=https://graph.microsoft.com/.default";
var authResponse = await client.PostAsync(getTokenUrl, new StringContent(authBody, Encoding.UTF8, "application/x-www-form-urlencoded"));
//取得 AccessToken
var authResult = await authResponse.Content.ReadAsStringAsync();
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
dynamic tokenResponse = JsonSerializer.Deserialize
(authResult, options);
var authObj = JsonNode.Parse(authResult);
accessToken = (string)authObj["access_token"];
Console.WriteLine("======== access Token =========");
Console.WriteLine(accessToken);
呼叫 API 取得使用者的 Azure AD User Id (User.ReadBasic.All 權限)
var getUserUrl = $"https://graph.microsoft.com/v1.0/users/{userPrincipalName}";
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(contentType));
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");
var userResponse = await client.GetAsync(getUserUrl);
var userResult = await userResponse.Content.ReadAsStringAsync();
var userObj = JsonNode.Parse(userResult);
var userId = (string)userObj["id"];
Console.WriteLine("======== Azure AD User Id =========");
Console.WriteLine(userId);
依會議主旨查詢 Events,並取得 Join URL
var searchSubject = "TID";
var getEventsBySubjectFilterUrl = $"https://graph.microsoft.com/v1.0/users/{userId}/events?$filter=startswith(subject, '{searchSubject}')";
//也可以用 日期 Filter
//var getEventsByDateFilterUrl = $"https://graph.microsoft.com/v1.0/users/{userId}/events?$filter=start/dateTime ge '2025/03/19' and start/dateTime le '2025/03/20' ";
var eventsResponse = await client.GetAsync(getEventsBySubjectFilterUrl);
var eventsResult = await eventsResponse.Content.ReadAsStringAsync();
var eventsObj = JsonNode.Parse(eventsResult);
var eventList = eventsObj["value"].AsArray();
Console.WriteLine("======== Events =========");
foreach (var eventItem in eventList)
{
var onlineMeeting = eventItem["onlineMeeting"];
if (onlineMeeting != null)
{
var subject = eventItem["subject"].ToString();
Console.WriteLine($"{subject}:{onlineMeeting["joinUrl"].ToString()}");
}
}
如果一開始就有線上會議的 Link,就可以省略4.依會議主旨來取得行事曆事件
var joinWebUrl = @"https://teams.microsoft.com/l/meetup-join/.....";
var getOnlineMeetingsUrl = $"https://graph.microsoft.com/v1.0/users/{userId}/onlineMeetings?$filter=JoinWebUrl eq '{joinWebUrl}'";
var onlineMeetingsResponse = await client.GetAsync(getOnlineMeetingsUrl);
var onlineMeetingsResult = await onlineMeetingsResponse.Content.ReadAsStringAsync();
Console.WriteLine("======== Online Meeting Id =========");
var meetingsObj = JsonNode.Parse(onlineMeetingsResult);
var meetingId = "";
//onlineMeetingResult
if (meetingsObj["error"] != null)
{
Console.WriteLine(meetingsObj["error"]["message"]);
}
else
{
meetingId = (string)meetingsObj["value"][0]["id"];
Console.WriteLine(meetingId);
Console.WriteLine((string)meetingsObj["value"][0]["subject"]);
}
var getMeetingTranscriptsUrl = $"https://graph.microsoft.com/v1.0/users/{userId}/onlineMeetings/{meetingId}/transcripts";
var meetingTranscriptsResponse = await client.GetAsync(getMeetingTranscriptsUrl);
var meetingTranscriptsResult = await meetingTranscriptsResponse.Content.ReadAsStringAsync();
var meetingTranscriptsObj = JsonNode.Parse(meetingTranscriptsResult);
var transcriptsCount = meetingTranscriptsObj["@odata.count"];
meetingTranscriptsObj["value"].AsArray();
Console.WriteLine($"Total Transcripts Count:{transcriptsCount}");
//the latest transcript
var lastTranscript = meetingTranscriptsObj["value"].AsArray().LastOrDefault();
var lastTranscriptId = (string)lastTranscript["id"];
線上會議的文字記錄可能會有多筆,可以依createdDateTime過濾所需的記錄。本文示範取最後一筆作為測試。
有了 TranscriptId 就可以取得文字記錄,格式選擇text/vtt
var meetingTranscriptUrl = $"https://graph.microsoft.com/v1.0/users/{userId}/onlineMeetings/{meetingId}/transcripts('{lastTranscriptId}')/content?$format=text/vtt";
var meetingTranscriptResponse = await client.GetAsync(meetingTranscriptUrl);
var meetingTranscriptResult = await meetingTranscriptResponse.Content.ReadAsStringAsync();
Console.WriteLine($"=== Transcript ===============");
Console.WriteLine(meetingTranscriptResult.Substring(0, 500));
綜合以上步驟,我們可以透過 Microsoft Graph API 依主旨或日期找到會議事件,取得 Join URL,再進一步查詢 OnlineMeeting Id,最後存取該會議的 Transcript。
此流程能協助開發者自動化取得 Teams 線上會議的逐字稿,進一步應用於會議紀錄、智慧摘要或 NLP 分析。
Teams App 代替使用者建立線上會議,讓該使用者為會議主持人
Microsoft Graph API Get a user
Microsoft Graph API Get onlineMeeting
Allow applications to access online meetings on behalf of a user
When you subscribe to the blog, we will send you an e-mail when there are new updates on the site so you wouldn't miss them.
評論