使用 Python 和 Google Meet REST API 观察会议事件

本教程介绍了如何结合使用 Google Meet REST API、Google Workspace Events API 和 Google Cloud Pub/Sub 来观察会议事件并做出响应。该示例应用会记录会议何时开始和结束、参与者何时加入或退出,以及生成的任何会议工件何时可用。

如需详细了解如何使用活动,请参阅 Google Workspace Events API 文档中的订阅 Google Meet 活动

前提条件

如果您需要为组织启用以下前提条件,请让您的 Google Workspace 管理员为您启用:

准备环境

本部分介绍了如何为本教程创建和配置本地环境以及 Google Cloud 项目。

创建工作目录和 Python 虚拟环境

如需创建并激活新的虚拟环境,请在终端中运行以下命令。

Linux/macOS

mkdir meet-tutorial
cd meet-tutorial
python3 -mvenv env
source env/bin/activate

Windows(命令提示符)

mkdir meet-tutorial
cd meet-tutorial
python3 -mvenv env
env/bin/activate.bat

Windows (PowerShell)

mkdir meet-tutorial
cd meet-tutorial
python3 -mvenv env
env/bin/activate.ps1

创建 Google Cloud 项目

Google Cloud 控制台

  1. 在 Google Cloud 控制台中,依次点击“菜单”图标 > IAM 和管理 > 创建项目

    前往“创建项目”

  2. 项目名称字段中,为项目输入一个描述性名称。

    可选:如需修改项目 ID,请点击修改。项目创建后,项目 ID 便无法更改,因此请选择符合项目生命周期需求的 ID。

  3. 位置字段中,点击浏览以显示项目的可能位置。然后,点击选择
  4. 点击创建。Google Cloud 控制台会转到“信息中心”页面,您的项目会在几分钟内创建完毕。

gcloud CLI

在以下某个开发环境中,访问 Google Cloud CLI(“gcloud”):

  • Cloud Shell:如需使用已设置 gcloud CLI 的在线终端,请激活 Cloud Shell。
    激活 Cloud Shell
  • 本地 Shell:如需使用本地开发环境,请安装initialize gcloud CLI。
    如需创建 Cloud 项目,请使用“gcloud projects create”命令:
    gcloud projects create PROJECT_ID
    为要创建的项目设置 ID,以替换 PROJECT_ID

为 Google Cloud 项目启用结算功能

Google Cloud 控制台

  1. 在 Google Cloud 控制台中,前往结算。点击 Menu 图标 > Billing > My Projects

    前往“我的项目”的“结算”页面

  2. 选择组织中,选择与您的 Google Cloud 项目关联的组织。
  3. 在项目行中,打开操作菜单 (),点击更改结算信息,然后选择相应的 Cloud Billing 帐号。
  4. 点击设置账号

gcloud CLI

  1. 如需列出可用的结算帐号,请运行以下命令:
    gcloud billing accounts list
  2. 将结算帐号与 Google Cloud 项目相关联:
    gcloud billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID

    替换以下内容:

    • PROJECT_ID 是要为其启用结算功能的 Cloud 项目的项目 ID
    • BILLING_ACCOUNT_ID 是要与 Google Cloud 项目关联的结算帐号 ID

设置身份验证和授权

身份验证和授权允许应用访问 Meet REST API 资源。调用 Meet REST API 需要用户授权。 本部分介绍如何配置用户凭据和请求授权。

配置 OAuth 权限请求页面并选择范围

以下步骤提供了建议的占位符信息,供您为您的应用配置 OAuth 同意屏幕。请先更新此信息,然后再对外发布应用。

  1. 在 Google Cloud 控制台中,依次点击“菜单”图标 > API 和服务 > OAuth 同意屏幕

    转到 OAuth 同意屏幕

  2. 用户类型下,选择内部,然后点击创建
  3. 应用名称中,输入 Meet REST API Tutorial
  4. 填写应用注册表单,然后点击保存并继续
  5. 点击添加或移除范围。此时会显示一个面板,其中包含您在 Google Cloud 项目中启用的每个 API 的范围列表。
  6. 手动添加范围下,粘贴以下范围:
    • https://www.googleapis.com/auth/meetings.space.created
  7. 点击添加到表
  8. 点击更新
  9. 选择您的应用所需的范围后,点击保存并继续
  10. 如果您选择了外部作为用户类型,请添加测试用户:
    1. 测试用户下,点击添加用户
    2. 输入您的电子邮件地址和任何其他获得授权的测试用户,然后点击保存并继续
  11. 查看您的应用注册摘要。如要进行更改,请点击修改。如果应用注册看起来正常,请点击 Back to Dashboard

创建客户端 ID

在 OAuth 2.0 流程中,客户端 ID 充当您的应用的凭据。由于应用在本地运行,因此请创建桌面客户端 ID。

  1. 在 Google Cloud 控制台中,依次点击“菜单”图标 > API 和服务 > 凭据

    进入“凭据”页面

  2. 依次点击创建凭据 > OAuth 客户端 ID
  3. 依次点击应用类型 > 桌面应用
  4. 名称字段中,输入凭据的名称。此名称只会显示在 Google Cloud 控制台中。
  5. 点击创建。系统会显示 OAuth 客户端已创建屏幕,其中显示了您的新客户端 ID 和客户端密钥。
  6. 点击 OK。新创建的凭据会显示在 OAuth 2.0 客户端 ID 下方。

安装 Google 身份验证库

安装 Google 身份验证库:

pip install google-auth google-auth-oauthlib

执行授权

Meet REST API 要求用户凭据采用 OAuth 2.0 访问令牌的形式。在本部分中,您将实现 OAuth 2.0 流程,以请求用户的访问令牌和刷新令牌。

  1. 在工作目录中,创建文件 main.py 并添加以下内容:

    import os
    import json
    
    from google.auth.transport import requests
    from google.oauth2.credentials import Credentials
    from google_auth_oauthlib.flow import InstalledAppFlow
    
    def authorize() -> Credentials:
        """Ensure valid credentials for calling the Meet REST API."""
        CLIENT_SECRET_FILE = "./client_secret.json"
        credentials = None
    
        if os.path.exists('token.json'):
            credentials = Credentials.from_authorized_user_file('token.json')
    
        if credentials is None:
            flow = InstalledAppFlow.from_client_secrets_file(
                CLIENT_SECRET_FILE,
                scopes=[
                    'https://www.googleapis.com/auth/meetings.space.created',
                ])
            flow.run_local_server(port=0)
            credentials = flow.credentials
    
        if credentials and credentials.expired:
            credentials.refresh(requests.Request())
    
        if credentials is not None:
            with open("token.json", "w") as f:
                f.write(credentials.to_json())
    
        return credentials
    
    USER_CREDENTIALS = authorize()
    
  2. 如需运行代码,必须提供客户端 ID 和之前创建的密钥。将下载的客户端密钥文件复制到项目工作目录,然后将其重命名为 client_secret.json

  3. 如果您想测试授权的运作方式,请运行以下命令。 请求获得批准后,应用会提示授权,并在项目工作目录中创建 token.json 文件。

    python3 main.py
    

添加 Meet REST API

现在授权代码已完成,是时候启用并调用 Meet REST API 了。

启用 API

虽然本部分重点介绍 Meet REST API,但本教程还使用 Google Cloud Pub/Sub 和 Google Workspace Events API。

Google Cloud 控制台

  1. 在 Google Cloud 控制台中,启用 Google Meet REST API、Google Workspace Events API 和 Google Cloud Pub/Sub。

    启用 API

  2. 确认您要在正确的 Cloud 项目中启用 API,然后点击下一步

  3. 确认您启用了正确的 API,然后点击启用

gcloud CLI

  1. 如有必要,请将当前 Cloud 项目设置为您使用 gcloud config set project 命令创建的项目:

    gcloud config set project PROJECT_ID
    

    PROJECT_ID 替换为您创建的 Cloud 项目的项目 ID

  2. 使用 gcloud services enable 命令启用 Google Meet REST API、Google Workspace Events API 和 Google Cloud Pub/Sub:

    gcloud services enable meet.googleapis.com workspaceevents.googleapis.com pubsub.googleapis.com
    

安装 Meet REST API 客户端库

请按照以下步骤安装 Meet REST API 客户端库:

  1. 运行以下命令:

    pip install google-apps-meet
    
  2. 修改 main.py 文件以导入客户端:

    from google.apps import meet_v2 as meet
    

创建聊天室

现在 Meet REST API 可以使用了,请定义一个函数以创建可订阅的会议空间。

修改 main.py 并添加:

def create_space() -> meet.Space:
    """Create a meeting space."""
    client = meet.SpacesServiceClient(credentials=USER_CREDENTIALS)
    request = meet.CreateSpaceRequest()
    return client.create_space(request=request)

订阅事件

如需接收关于会议空间的活动,您可以使用 Google Workspace Events API 创建订阅。您还必须创建并订阅一个 Google Cloud Pub/Sub 主题,该主题将用作应用在其中接收事件的通知端点。

配置 Google Cloud Pub/Sub

如需创建并订阅 Pub/Sub 主题,请执行以下操作:

Google Cloud 控制台

  1. 在 Google Cloud 控制台中,依次点击“菜单”图标 > Pub/Sub

    转到“Pub/Sub”

    确保为您的应用选择了 Cloud 项目。

  2. 点击 创建主题,然后执行以下操作:
    1. 输入 workspace-events 作为主题名称。
    2. 添加默认订阅处于选中状态。
    3. 点击创建。您的完整主题名称的格式为 projects/{project}/topics/{topic}。请记下此名称,以便在后续步骤中使用。
  3. 授予向主题发布 Pub/Sub 消息的权限:
    1. 在侧边栏中,打开权限标签页。
    2. 点击添加主账号
    3. 新主账号中,输入 meet-api-event-push@system.gserviceaccount.com
    4. 分配角色中,选择 Pub/Sub Publisher
    5. 点击保存

    更新主题的权限可能需要几分钟时间。

gcloud CLI

  1. 在您的 Cloud 项目中,运行以下命令来创建主题:
    gcloud pubsub topics create workspace-events

    输出会显示完整的主题名称,格式为 projects/{project}/topics/{topic}。请记下此名称,以便在后续步骤中使用。

  2. 授予向主题发布消息的权限:
     gcloud pubsub topics add-iam-policy-binding workspace-events --member='serviceAccount:meet-api-event-push@system.gserviceaccount.com' --role='roles/pubsub.publisher'

    更新主题的权限可能需要几分钟时间。

  3. 为该主题创建 Pub/Sub 订阅:
    gcloud pubsub subscriptions create workspace-events-sub --topic=TOPIC_NAME

    替换以下内容:

    • TOPIC_NAME:您在上一步中创建的主题的名称。

记下主题名称,并确保 {project} 的值是应用的 Cloud 项目 ID。稍后您将使用该主题名称创建 Google Workspace 订阅。

创建服务账号

Google Cloud 控制台

  1. 在 Google Cloud 控制台中,依次点击“菜单”图标 > IAM 和管理 > 服务帐号

    进入“服务账号”

  2. 点击创建服务账号
  3. 填写服务帐号详细信息,然后点击创建并继续
  4. 可选:为您的服务账号分配角色,以授予对 Google Cloud 项目资源的访问权限。如需了解详情,请参阅授予、更改和撤消对资源的访问权限
  5. 点击继续
  6. 可选:输入可以使用此服务账号管理和执行操作的用户或群组。如需了解详情,请参阅管理服务帐号模拟
  7. 点击完成。记下服务帐号的电子邮件地址。

gcloud CLI

  1. 创建服务帐号:
    gcloud iam service-accounts create meet-event-listener \
      --display-name="meet-event-listener"
  2. 可选:为您的服务账号分配角色,以授予对 Google Cloud 项目资源的访问权限。如需了解详情,请参阅授予、更改和撤消对资源的访问权限

使用服务帐号

创建服务帐号后,请为自己授予模拟该服务帐号的权限。

Google Cloud 控制台

  1. 在新创建的服务帐号的操作列中,依次点击 > 管理权限
  2. 点击添加密钥 > 授予访问权限
  3. 添加主账号下输入您的电子邮件地址。
  4. 选择服务帐号 > Service Account Token Creator 作为角色。
  5. 点击保存

gcloud CLI

  1. 如需添加权限,请使用服务帐号和用户的电子邮件地址运行 gcloud iam service-accounts add-iam-policy-binding
    gcloud iam service-accounts add-iam-policy-binding \
      SERVICE_ACCOUNT_EMAIL \
      --member="user:YOUR_EMAIL \
      --role="roles/iam.serviceAccountTokenCreator"
  2. 使用 gcloud 登录,将应用默认凭据设置为服务帐号。当系统提示您授权时,请使用前面步骤中使用的帐号登录。
    gcloud auth application-default login --impersonate-service-account=SERVICE_ACCOUNT_EMAIL

安装 Pub/Sub 客户端库

  1. 使用 pip 安装 Pub/Sub 客户端库:

    pip install google-cloud-pubsub
    
  2. 然后修改 main.py 以导入客户端:

    from google.cloud import pubsub_v1
    

创建 Google Workspace 订阅

将以下代码添加到 main.py 以定义订阅 Meet 事件的方法。此代码可订阅会议空间的所有活动。订阅后,事件将发布到 Pub/Sub 主题。

def subscribe_to_space(space_name: str = None, topic_name: str = None):
    """Subscribe to events for a meeting space."""
    session = requests.AuthorizedSession(USER_CREDENTIALS)
    body = {
        'targetResource': f"//meet.googleapis.com/{space_name}",
        "eventTypes": [
            "google.workspace.meet.conference.v2.started",
            "google.workspace.meet.conference.v2.ended",
            "google.workspace.meet.participant.v2.joined",
            "google.workspace.meet.participant.v2.left",
            "google.workspace.meet.recording.v2.fileGenerated",
            "google.workspace.meet.transcript.v2.fileGenerated",
        ],
        "payloadOptions": {
            "includeResource": False,
        },
        "notificationEndpoint": {
            "pubsubTopic": topic_name
        },
        "ttl": "86400s",
    }
    response = session.post("https://workspaceevents.googleapis.com/v1/subscriptions", json=body)
    return response

接下来,添加相应的代码以拉取和处理事件。

监听和处理事件

继续修改 main.py 并添加以下示例代码。此代码会实现接收端,并在事件可用时使用 Google Cloud Pub/Sub API 拉取事件。各种处理程序方法会输出有关相应事件的信息。

def format_participant(participant: meet.Participant) -> str:
    """Formats a participant for display on the console."""
    if participant.anonymous_user:
        return f"{participant.anonymous_user.display_name} (Anonymous)"

    if participant.signedin_user:
        return f"{participant.signedin_user.display_name} (ID: {participant.signedin_user.user})"

    if participant.phone_user:
        return f"{participant.phone_user.display_name} (Phone)"

    return "Unknown participant"


def fetch_participant_from_session(session_name: str) -> meet.Participant:
    """Fetches the participant for a session."""
    client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)
    # Use the parent path of the session to fetch the participant details
    parsed_session_path = client.parse_participant_session_path(session_name)
    participant_resource_name = client.participant_path(
        parsed_session_path["conference_record"],
        parsed_session_path["participant"])
    return client.get_participant(name=participant_resource_name)


def on_conference_started(message: pubsub_v1.subscriber.message.Message):
    """Display information about a conference when started."""
    payload = json.loads(message.data)
    resource_name = payload.get("conferenceRecord").get("name")
    client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)
    conference = client.get_conference_record(name=resource_name)
    print(f"Conference (ID {conference.name}) started at {conference.start_time.rfc3339()}")


def on_conference_ended(message: pubsub_v1.subscriber.message.Message):
    """Display information about a conference when ended."""
    payload = json.loads(message.data)
    resource_name = payload.get("conferenceRecord").get("name")
    client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)
    conference = client.get_conference_record(name=resource_name)
    print(f"Conference (ID {conference.name}) ended at {conference.end_time.rfc3339()}")


def on_participant_joined(message: pubsub_v1.subscriber.message.Message):
    """Display information about a participant when they join a meeting."""
    payload = json.loads(message.data)
    resource_name = payload.get("participantSession").get("name")
    client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)
    session = client.get_participant_session(name=resource_name)
    participant = fetch_participant_from_session(resource_name)
    display_name = format_participant(participant)
    print(f"{display_name} joined at {session.start_time.rfc3339()}")


def on_participant_left(message: pubsub_v1.subscriber.message.Message):
    """Display information about a participant when they leave a meeting."""
    payload = json.loads(message.data)
    resource_name = payload.get("participantSession").get("name")
    client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)
    session = client.get_participant_session(name=resource_name)
    participant = fetch_participant_from_session(resource_name)
    display_name = format_participant(participant)
    print(f"{display_name} left at {session.end_time.rfc3339()}")


def on_recording_ready(message: pubsub_v1.subscriber.message.Message):
    """Display information about a recorded meeting when artifact is ready."""
    payload = json.loads(message.data)
    resource_name = payload.get("recording").get("name")
    client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)
    recording = client.get_recording(name=resource_name)
    print(f"Recording available at {recording.drive_destination.export_uri}")


def on_transcript_ready(message: pubsub_v1.subscriber.message.Message):
    """Display information about a meeting transcript when artifact is ready."""
    payload = json.loads(message.data)
    resource_name = payload.get("transcript").get("name")
    client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)
    transcript = client.get_transcript(name=resource_name)
    print(f"Transcript available at {transcript.docs_destination.export_uri}")


def on_message(message: pubsub_v1.subscriber.message.Message) -> None:
    """Handles an incoming event from the Google Cloud Pub/Sub API."""
    event_type = message.attributes.get("ce-type")
    handler = {
        "google.workspace.meet.conference.v2.started": on_conference_started,
        "google.workspace.meet.conference.v2.ended": on_conference_ended,
        "google.workspace.meet.participant.v2.joined": on_participant_joined,
        "google.workspace.meet.participant.v2.left": on_participant_left,
        "google.workspace.meet.recording.v2.fileGenerated": on_recording_ready,
        "google.workspace.meet.transcript.v2.fileGenerated": on_transcript_ready,
    }.get(event_type)

    try:
        if handler is not None:
            handler(message)
        message.ack()
    except Exception as error:
        print("Unable to process event")
        print(error)


def listen_for_events(subscription_name: str = None):
    """Subscribe to events on the subscription."""
    subscriber = pubsub_v1.SubscriberClient()
    with subscriber:
        future = subscriber.subscribe(subscription_name, callback=on_message)
        print("Listening for events")
        try:
            future.result()
        except KeyboardInterrupt:
            future.cancel()
    print("Done")

完成代码

将以下代码添加到 main.py,以调用用于创建空间、订阅事件和监听的方法。使用您之前创建的自定义主题和订阅名称更新 TOPIC_NAMESUBSCRIPTION_NAME 常量。

  1. 将代码添加到 main.py

    space = create_space()
    print(f"Join the meeting at {space.meeting_uri}")
    
    TOPIC_NAME = "projects/PROJECT_ID/topics/TOPIC_ID"
    SUBSCRIPTION_NAME = "projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID"
    
    subscription = subscribe_to_space(topic_name=TOPIC_NAME, space_name=space.name)
    listen_for_events(subscription_name=SUBSCRIPTION_NAME)
    

    替换以下内容:

    • PROJECT_ID:您的应用的唯一 Cloud 项目 ID,例如 my-sample-project-191923

    • TOPIC_ID:您在 Cloud 项目中创建的 Pub/Sub 主题的名称。

    • SUBSCRIPTION_ID:订阅的名称,例如 workspace-events-sub

  2. 运行程序:

    python3 main.py
    

如果您之前未运行过该程序,它会首次提示授权。向该应用授予调用 Meet REST API 的权限。 程序成功运行后,您应该会看到类似如下所示的输出:

Join the meeting at https://meet.google.com/abc-mnop-xyz

加入会议

如需为应用生成事件,请使用应用显示的网址加入会议。加入后,您可以尝试执行以下操作来触发事件:

  • 退出并重新加入会议。
  • 邀请他人或使用手机拨号加入会议。
  • 启用录音和转写功能。

上述每个活动都会生成一个事件,应用会收到该事件,并将其记录到 Google Cloud 控制台中。

完成后,使用 ctrl-c 中断程序。

可选:可尝试的其他步骤

应用会记录有关事件的基本详情。如需继续探索 Meet REST API,请尝试修改应用以执行这些额外的操作。

  • 使用 People API 检索有关已登录参与者的其他信息。
  • 使用 Google Drive API 下载录制内容和转录文本。
  • 您可以使用 Meet REST API 中的结构化转录方法检索转录内容,而不是从 Google 云端硬盘下载转录内容。

可选:清理

为避免系统因本教程中使用的资源向您的 Google Cloud 控制台帐号收取费用,我们建议您清理创建的所有资源和项目。

如需删除订阅,请执行以下操作:

控制台

  1. 在 Google Cloud 控制台中,依次点击“菜单”图标 > Pub/Sub > 订阅

    前往订阅页面

  2. 选择相应订阅,然后点击更多操作

  3. 点击删除。系统随即会显示删除订阅窗口。

  4. 点击删除

gcloud CLI

  1. 删除订阅:

    gcloud pubsub subscriptions delete SUBSCRIPTION_NAME
    

要删除主题,请执行以下操作:

控制台

  1. 在 Google Cloud 控制台中,依次点击“菜单”图标 > Pub/Sub > 主题

    打开“主题”

  2. 选择相应主题,然后点击更多操作

  3. 点击删除。系统随即会显示删除主题窗口。

  4. 输入 delete,然后点击删除

gcloud CLI

  1. 删除主题:

    gcloud pubsub topics delete TOPIC_NAME
    

如需删除项目,请执行以下操作:

控制台

  1. 在 Google Cloud 控制台中,前往管理资源页面。依次点击菜单图标 > IAM 和管理 > 管理资源

    前往 Resource Manager

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关停以删除项目。

gcloud CLI

  1. 要删除项目,请使用 gcloud projects delete 命令:

    gcloud projects delete PROJECT_ID