Skip to main content

A Discord toolkit for building metadata browsers with embeds and button navigation.

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

discord_metadata-0.1.2.tar.gz (14.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

discord_metadata-0.1.2-py3-none-any.whl (16.4 kB view details)

Uploaded Python 3

File details

Details for the file discord_metadata-0.1.2.tar.gz.

File metadata

  • Download URL: discord_metadata-0.1.2.tar.gz
  • Upload date:
  • Size: 14.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for discord_metadata-0.1.2.tar.gz
Algorithm Hash digest
SHA256 50815fd2ab25b7fad0a1aa5e4d8e32d7465c901b0484de9f0942a89234df7515
MD5 74dfe50eeae27f8e989028c0d688d394
BLAKE2b-256 0683788319b17d4ffa8c288273663dfdfab99a8655f05186acf7f7b841448f3d

See more details on using hashes here.

File details

Details for the file discord_metadata-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: discord_metadata-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 16.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for discord_metadata-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 579724dd5dc4db51b8a4840052d7a1f8130422e749d57a2e6947277897b13c2c
MD5 e393b7dbb5f12fdff94713a0b5994474
BLAKE2b-256 013b99a9438089c767198bd91adfa892099951a6314d8dc543051705e5ee3b6b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page