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

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

您可以订阅 Meet 用户,而不是订阅特定会议室,以接收该用户拥有或组织的任何会议室的事件。如需了解详情,请参阅 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. Project Name 字段中,为项目输入一个描述性名称。

    可选:如需修改项目 ID,请点击修改。项目创建后,项目 ID 便无法更改,因此请选择一个在项目生命周期内能满足您需求的 ID。

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

gcloud CLI

在以下任一开发环境中,访问 Google Cloud CLI (gcloud):

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

为 Google Cloud 项目启用结算功能

Google Cloud 控制台

  1. 在 Google Cloud 控制台中,前往结算。依次点击菜单 > 结算 > 我的项目

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

  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 控制台中,依次点击“菜单”图标 > > 品牌

    前往“品牌”页面

  2. 如果您已配置 ,则可以在品牌受众群体数据访问中配置以下 OAuth 同意屏幕设置。 如果您看到一条消息,其中显示 尚未配置,请点击开始
    1. 应用信息下的应用名称中,输入 Meet REST API Tutorial
    2. 用户支持电子邮件中,选择一个支持电子邮件地址,以便用户在对其同意问题有疑问时与您联系。
    3. 点击下一步
    4. 观众下,选择内部
    5. 点击下一步
    6. 联系信息下,输入一个电子邮件地址,以便您接收有关项目的任何更改的通知。
    7. 点击下一步
    8. 完成下方,查看 Google API 服务用户数据政策,如果您同意,请选择我同意 Google API 服务:用户数据政策
    9. 点击继续
    10. 点击创建
    11. 如果您选择了外部作为用户类型,请添加测试用户:
      1. 点击受众群体
      2. 测试用户下,点击添加用户
      3. 输入您的电子邮件地址和任何其他已获授权的测试用户,然后点击保存
  3. 依次点击数据访问 > 添加或移除范围。系统随即会显示一个面板,其中列出了您在 Google Cloud 项目中启用的每个 API 的范围。
  4. 手动添加范围下,粘贴以下范围:
    • https://www.googleapis.com/auth/meetings.space.created
  5. 点击添加到表格
  6. 点击更新
  7. 选择应用所需的镜重后,点击保存

如需详细了解如何配置 OAuth 权限征求,请参阅 使用入门

创建客户端 ID

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

  1. 在 Google Cloud 控制台中,依次点击“菜单”图标 > > 客户端

    前往“客户”页面

  2. 点击创建客户端
  3. 依次点击应用类型 > 桌面应用
  4. 名称字段中,输入凭据的名称。此名称仅在 Google Cloud 控制台中显示。
  5. 点击创建

    新创建的凭据会显示在“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. 点击保存
  6. 返回终端并使用 gcloud 登录,将应用默认凭据设置为服务账号。当系统提示您授权时,请使用在上一步中使用的账号登录。
    gcloud auth application-default login --impersonate-service-account=SERVICE_ACCOUNT_EMAIL

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 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 和管理 > 管理资源

    前往资源管理器

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

gcloud CLI

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

    gcloud projects delete PROJECT_ID