Skip to main content

meeto

CI PyPI Python License: MIT

Open-source Google Meet bot. Join meetings, capture audio, and transcribe in real time using Playwright and pluggable STT providers.

Installation

pip install meeto
playwright install chromium

Quick Start

The simplest way to use meeto is guest mode — no Google account needed. The bot joins the meeting as a guest and waits for the host to admit it.

import asyncio
from meeto import run_meeting_worker
from meeto.config import WorkerConfig, JoinConfig, SttConfig

config = WorkerConfig(
    meeting_id="my-meeting-001",
    meet_url="https://meet.google.com/abc-defg-hij",
    join=JoinConfig(
        bot_name="Meeto Bot",
    ),
    stt=SttConfig(
        provider="deepgram",
        api_key="YOUR_DEEPGRAM_API_KEY",
    ),
)

asyncio.run(run_meeting_worker(config))

Or via the CLI example:

PYTHONPATH=src python scripts/example.py https://meet.google.com/abc-defg-hij --bot-name "Meeto Bot" --no-headless

Audio dumps are saved to ./audio/ and transcripts to ./transcripts/ by default.

Note: Guest mode requires a display — see Deployment for running on servers and containers.

Authenticated Mode

To join meetings as a Google account (no waiting room), generate a browser session first:

meeto-auth --output storage_state.json

This opens a Chromium window. Log in to Google, then press Enter in the terminal.

config = WorkerConfig(
    meeting_id="my-meeting-001",
    meet_url="https://meet.google.com/abc-defg-hij",
    duration_seconds=3600,
    join=JoinConfig(
        headless=True,
        storage_state_path="storage_state.json",
    ),
    stt=SttConfig(
        provider="deepgram",
        api_key="YOUR_DEEPGRAM_API_KEY",
    ),
)

asyncio.run(run_meeting_worker(config))

Deployment

Guest Mode on Servers / Containers

Google blocks headless browsers from joining meetings via server-side bot detection. To run guest mode in a headless environment, use Xvfb to provide a virtual display. meeto automatically detects the DISPLAY environment variable and switches to headed mode.

Quick option — wrap your process with xvfb-run:

apt-get install -y xvfb
xvfb-run python your_bot_script.py

Docker — start Xvfb in your entrypoint:

RUN apt-get update && apt-get install -y xvfb
ENV DISPLAY=:99
#!/bin/bash
Xvfb :99 -screen 0 1920x1080x24 &
sleep 1
exec python your_bot_script.py

Authenticated Mode on Servers / Containers

Authenticated mode uses a saved Google session (storage_state.json), which Google trusts as a real user. This means it works with headless=True out of the box — no Xvfb, no virtual display, no extra system dependencies.

config = WorkerConfig(
    meeting_id="my-meeting-001",
    meet_url="https://meet.google.com/abc-defg-hij",
    join=JoinConfig(
        headless=True,
        storage_state_path="storage_state.json",
    ),
)

Generate storage_state.json once on a machine with a display (your laptop), then copy it to the server or bake it into your deployment secrets.

Environment Variable Config

For container/job deployments, build config from env vars:

from meeto.config import worker_config_from_env

config = worker_config_from_env()

Required: MEETING_ID, MEET_URL. See meeto/config/env_config.py for the full list.

Configuration

Field Type Default Description
meeting_id str required Unique identifier for the meeting
meet_url str required Google Meet URL
duration_seconds int 3600 Max recording duration
audio AudioConfig defaults Audio capture settings
stt SttConfig defaults Speech-to-text settings
join JoinConfig defaults Browser join settings

Extending

Custom Storage Adapter

By default, artifacts stay local. To upload to cloud storage (S3, GCS, etc.), implement ArtifactStorageAdapter:

from meeto.storage import ArtifactStorageAdapter

class S3StorageAdapter(ArtifactStorageAdapter):
    def upload(self, local_path, content_type="application/octet-stream"):
        return f"s3://my-bucket/{local_path}"

asyncio.run(run_meeting_worker(config, storage_adapter=S3StorageAdapter()))

Custom Meeting State Store

By default, meeting lifecycle state is kept in memory. To persist state (e.g. to a database), implement MeetingLifecycleStore:

from meeto.state_store import MeetingLifecycleStore

class PostgresMeetingStore(MeetingLifecycleStore):
    def update_status(self, meeting_id, *, status, ended_at=None, transcription_path=None):
        ...

    def heartbeat(self, meeting_id):
        ...

asyncio.run(run_meeting_worker(config, state_store=PostgresMeetingStore()))

Custom STT Provider

Meeto ships with Deepgram support. To add your own STT provider, implement STTStreamingAdapter:

from meeto.stt import STTStreamingAdapter, register_stt

class MySTTAdapter(STTStreamingAdapter):
    async def connect(self): ...
    async def send_audio(self, pcm_bytes): ...
    async def start(self, on_segment): ...
    async def close(self): ...

register_stt("my_provider", MySTTAdapter)

Then set SttConfig(provider="my_provider").

Logging

meeto uses Python's standard logging module and does not configure any handlers itself. To see logs, configure logging in your application:

import logging
logging.basicConfig(level=logging.INFO)

For finer control:

logging.getLogger("meeto").setLevel(logging.DEBUG)         # all meeto logs
logging.getLogger("meeto.stt").setLevel(logging.WARNING)   # quieter STT logs

Architecture

meeto/
├── runtime.py              # Main entrypoint: run_meeting_worker()
├── pipeline.py             # Wires audio capture, STT, and transcript writing
├── storage.py              # ArtifactStorageAdapter ABC + LocalStorageAdapter
├── audio_writer.py         # PCM audio dump writer
├── transcript_writer.py    # JSONL transcript writer
├── auth/                   # Google login session generator
├── config/                 # Typed config models + env var parser
├── meet/                   # Playwright-based meeting join, end detection, speaker tracking
├── state_store/            # Meeting lifecycle state management
└── stt/                    # STT adapter interface + Deepgram implementation

Contributing

See CONTRIBUTING.md for setup, code style, and PR guidelines.

License

MIT

Metadata

Release files for meeto 0.3.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for meeto 0.3.3
File Size Uploaded Provenance
meeto-0.3.3.tar.gz 33.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for meeto 0.3.3
File Interpreter ABI Platform
meeto-0.3.3-py3-none-any.whl Python 3 none any Details

Total release size: 73.5 kB

Release files / meeto-0.3.3.tar.gz

Download URL meeto-0.3.3.tar.gz
Size 33.3 kB
Tags Source
SHA-256 checksum
How to use checksums
b646ec14b46c01e07f50b1ff7a1b88a696ed3895b6ee4b6e1996b103096ee84d
BLAKE2b-256 checksum
How to use checksums
2698e8582d5c39530f0891419ecc0093bfe054cc1cb9725afd509dd13a7c31ec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 13, 2026.

Transparency log

Release files / meeto-0.3.3-py3-none-any.whl

Download URL meeto-0.3.3-py3-none-any.whl
Size 40.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9a643a3fb6e6e6063cf8796cfb81b0b57c5796ad510f973ead8f78abc9c21681
BLAKE2b-256 checksum
How to use checksums
fef4735d403a37bc6387fd49a93046bb425548ec741be4274f45f9f8603d6661
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 13, 2026.

Transparency log

Security for meeto 0.3.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for meeto-0.3.3.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 13, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page