Skip to main content

discord-metadata

A Discord toolkit for building interactive metadata browsers with embeds and select-menu navigation.

Define your entities once and get Discord embeds with paginated sections, navigable links, and browsable search results. The Discord counterpart of rich-metadata.

Install

Requires Python 3.12+.

pip install discord-metadata
# or
uv add discord-metadata

Quick start

from discord_metadata import (
    BaseNavigator,
    DisplayEngine,
    EntityDef,
    HeaderField,
    HeaderLink,
    MetadataBot,
    SectionDef,
    SummaryField,
    SyncAPI,
    TableColumn,
)

# 1. Define entities
movie_def = EntityDef(
    type_name="movie",
    summary=[
        SummaryField(key="title", bold=True),
        SummaryField(key="year", prefix="(", transform=lambda v: f"{v})"),
    ],
    header_fields=[
        HeaderField("Director", key="director"),
        HeaderField("Year", key="year"),
        HeaderField("Genre", key="genre"),
    ],
    sections=[
        SectionDef(
            "cast",
            navigable=True,
            columns=[
                TableColumn("Actor", "name"),
                TableColumn("Role", "role"),
            ],
        ),
        SectionDef("synopsis"),
    ],
    header_links=[
        HeaderLink("Director: {director}", "person", ref_key="director_id"),
    ],
    title_key="title",
    color=0xE5A00D,
    footer="Movie Database",
    url_key="url",
)

# 2. Create engine and register definitions
engine = DisplayEngine()
engine.register(movie_def)

# 3. Wire up APIs and create bot
navigator = BaseNavigator(
    engine,
    apis={"movie": SyncAPI(MovieAPI())},  # or pass async APIs directly
)

bot = MetadataBot(navigator)

# 4. Register slash commands
@bot.tree.command(name="movie", description="Search for a movie")
async def cmd_movie(interaction, query: str):
    await bot.navigator.search_and_navigate(interaction, query, ["movie"])

# 5. Run
bot.run_with_args("DISCORD_TOKEN")

See examples/movie_bot.py for a complete working example.

Concepts

EntityDef

Declares how an entity type is rendered as a Discord embed:

Field Purpose
type_name Matches the _type key in data dicts
summary One-line representation for search results and select menus
header_fields Key-value fields shown in the embed header
sections Named content blocks (text, tables, navigable lists)
header_links Buttons that navigate to related entities
image_url_key / thumbnail_url_key Embed image or thumbnail
color / footer / url_key Embed appearance
auto_full Show all sections at once (skip section menu; navigable rows become read-only)

SectionDef

Each section can be:

  • Text — a plain string value rendered in the embed description
  • Table — a list of dicts rendered with columns (list of TableColumn)
  • Navigable — table rows become selectable items in the select menu
  • Lazy — fetched on demand via a lazy_fetchers={(type, section_key): fn} mapping passed to BaseNavigator (sync or async; sync receives the unwrapped API)
  • Numbered — items prefixed with 1., 2., etc. (default: True)

DisplayEngine

Registry of entity definitions. Call engine.register(def1, def2, ...) to add definitions.

BaseNavigator

The interactive browser. Main entry points:

Method Description
search_and_navigate(interaction, query, types) Search APIs, show results, navigate on selection
browse(interaction, fetch_page, title=...) Paginated browsing with a fetch_page(start, count) callable
browse_sources(interaction, sources) Let the user pick between multiple browsable sources
navigate_entity(interaction, entity) Display an entity directly
navigate_results(interaction, results) Show a result list

SyncAPI

Wraps a synchronous API (with .get(ref) and .search(query) methods) for safe use in async Discord handlers via asyncio.to_thread.

apis = {"book": SyncAPI(BookAPI(client))}

Async APIs can be passed directly without wrapping.

MetadataBot

A ready-made discord.Client subclass that handles:

  • Command tree setup and guild sync
  • .env loading via python-dotenv (optional)
  • --guild GUILD_ID CLI argument for instant command sync during development
  • Cleanup callback via on_close
bot = MetadataBot(navigator, on_close=client.close)
bot.run_with_args("DISCORD_TOKEN")

Navigator options

BaseNavigator(
    engine,
    apis={...},
    entity_ref_key="url",       # key used to fetch entity details (default: "url")
    lazy_fetchers={             # optional: (entity_type, section_key) -> fn(api, entity)
        ("movie", "synopsis"): lambda api, entity: api.fetch_synopsis(entity["id"]),
    },
    ephemeral=False,            # whether responses are ephemeral
    placeholder="Browse...",    # select menu placeholder text
    search_kwargs={},           # extra kwargs passed to search methods
)

Paginated browsing

For APIs that return paginated results (discover, recent, upcoming, etc.), use browse() with a fetch_page callable:

@bot.tree.command(name="recent")
async def cmd_recent(interaction):
    await interaction.response.defer()
    await navigator.browse(
        interaction,
        fetch_page=lambda start, count: api.fetch_page(start, count),
        title="Recent releases",
    )

fetch_page(start, count) must return (results: list[dict], total: int). It can be sync or async.

Data format

Entity data is passed as plain dicts with a _type discriminator:

{
    "_type": "movie",
    "title": "Blade Runner",
    "year": "1982",
    "director": "Ridley Scott",
    "cast": [
        {"_type": "person", "name": "Harrison Ford", "role": "Deckard"},
    ],
}

Keys prefixed with _ (except _type) are internal and stripped from public output.

Development

git clone https://github.com/gabriel-jung/discord-metadata.git
cd discord-metadata
uv sync

License

MIT

Metadata

Release files for discord-metadata 0.1.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 discord-metadata 0.1.3
File Size Uploaded Provenance
discord_metadata-0.1.3.tar.gz 14.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for discord-metadata 0.1.3
File Interpreter ABI Platform
discord_metadata-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 30.9 kB

Release files / discord_metadata-0.1.3.tar.gz

Download URL discord_metadata-0.1.3.tar.gz
Size 14.5 kB
Tags Source
SHA-256 checksum
How to use checksums
5ff8aedb8099d7363794ae6ee66281abb5478eacfe78af77c72c67df3cfc828a
BLAKE2b-256 checksum
How to use checksums
afdc9a87714a596012c05d0c45e78d550a88efbea1cd52d75a1107a8543f7646
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

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 Jul 25, 2026.

Transparency log

Release files / discord_metadata-0.1.3-py3-none-any.whl

Download URL discord_metadata-0.1.3-py3-none-any.whl
Size 16.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6567bc85d26262861f271e471b6c5b8e62ab53b927d9714e0682601aa99cb102
BLAKE2b-256 checksum
How to use checksums
6aa8e6a4084c569c6063bf82f141fae2b049855eb292c1a12e5e484e8c27a93c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

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 Jul 25, 2026.

Transparency log

Security for discord-metadata 0.1.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 discord_metadata-0.1.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 Jul 25, 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