kbbi

    Unofficial KBBI (Kamus Besar Bahasa Indonesia) lookup for MoonBit: a library, a CLI, and an agent skill.

    kbbi
    indonesian
    dictionary
    cli
    skill
    agent
    Download zip
    Author
    Version
    0.2.0
    License
    Apache-2.0
    Last updated
    8 hours ago
    Downloads
    103

    #gaato/kbbi

    An unofficial lookup library, command-line tool, and agent skill for KBBI Daring, the online edition of the Indonesian dictionary Kamus Besar Bahasa Indonesia. It reads an entry page and returns typed data: headwords with homograph numbers, pronunciations, root words and nonstandard spellings, and each sense's labels, definition, examples, and example meanings.

    Status: unofficial. Results are scraped from KBBI Daring; this project is not affiliated with KBBI or its operator.

    #Install

    As a library:

    moon add gaato/kbbi

    As a command, run the prebuilt executable with MoonBit's moonx, which downloads the latest release:

    moonx gaato/kbbi/cmd/kbbi makan

    #Usage

    lookup takes a gaato/mttp/client.Client, so the library itself imports no async runtime and builds for wasm, native, and js. In an executable, construct the client with @backend.AsyncBackend::new() from gaato/http-async/backend and @runtime.AsyncClock::new() from gaato/http-async/runtime. The backend negotiates and decodes compressed responses. The caller owns and closes the client; its timeout, retry, and body-limit settings apply to lookups.

    ///|
    /// Prints the headword and first definition of each entry for `word`.
    async fn print_definitions(client : @client.Client, word : String) -> Unit {
    let result = @kbbi.lookup(client, word)
    if !result.found {
    println("\{word}: not in KBBI")
    }
    for entry in result.entries {
    println("\{entry.headword}: \{entry.senses[0].gloss}")
    }
    }

    ///|
    test "print_definitions compiles" {
    ignore(print_definitions)
    }

    parse reads a saved entry page without any IO, and entry_url gives the page to save:

    ///|
    test "parse a saved page" {
    let html =
    #|<h2>ma.kan<sup>1</sup></h2>
    #|<ol><li><font color="red"><i><span title="Verba: kata kerja">v</span></i></font>
    #|mengisap:<font color="grey"><i>-- candu</i></font></li></ol>
    let url = @kbbi.entry_url("makan")
    let result = @kbbi.parse(html, query="makan", url~)
    inspect(url, content="https://kbbi.kemendikdasmen.go.id/entri/makan")
    inspect(result.entries[0].headword, content="makan")
    inspect(result.entries[0].senses[0].gloss, content="mengisap")
    }

    A result converts to JSON with to_json(); the command prints the same document.

    #Packages

    PackageWhat it isTargets
    gaato/kbbiThe lookup library: lookup, parse, entry_url, and the result typeswasm, native, js
    gaato/kbbi/cmd/kbbiThe kbbi command and its agent skillwasm, native

    #Errors

    lookup raises KbbiError:

    • EmptyQuery when the query is blank after trimming; nothing is sent.
    • Http when sending or reading fails, carrying a gaato/http.TransportError.
    • Status when KBBI answers with a non-2xx status.
    • Decode when the page is not valid UTF-8.

    Other errors raised by the client's custom policies propagate unchanged. Non-2xx bodies are closed without reading or decoding them.

    A word KBBI does not have is not an error: the result has found: false and no entries.

    #Run

    moonx gaato/kbbi/cmd/kbbi makan moonx gaato/kbbi/cmd/kbbi rumah sakit moonx gaato/kbbi/cmd/kbbi --help

    Arguments form one query, joined with spaces. A lookup prints one pretty JSON document, including found: false for no match. --help (-h) and --version (-V) print plain text wherever they appear. The exit code is 0 on success, 1 for a network, status, or decoding failure, and 2 for invalid arguments; errors go to stderr as error: .... Requests time out after 10 seconds and response bodies are limited to 64 MiB, counting wire and decoded bytes separately.

    An optional moonrun policy limits the command's network access to KBBI. Save this as kbbi-policy.json:

    {"net":{"connect":["kbbi.kemendikdasmen.go.id:443"]}}

    moonx --experimental-policy kbbi-policy.json gaato/kbbi/cmd/kbbi gemas

    Please avoid rapid bulk lookups.

    #Agent skill

    The command's package includes an agent skill, cmd/kbbi/SKILL.md, that tells a coding agent how to run lookups and read the JSON.

    #License

    Apache-2.0

    KbbiError

    pub suberror KbbiError {
    EmptyQuery
    Http(
    TransportError
    )
    Status(Int)
    Decode(String)
    } derive(
    Debug
    )

    Why lookup failed: the query was empty after trimming, the transport failed, the site answered with a non-2xx status, or the body was not valid UTF-8.

    KbbiError::to_repr

    Renders a KbbiError for debugging with the derived Debug.

    Entry

    pub struct Entry {
    headword : String
    homograph : Int?
    pronunciation : String?
    root_words : Array[String]
    nonstandard_forms : Array[String]
    senses : Array[Sense]
    } derive(Eq,
    Debug
    ,
    ToJson
    )

    One headword on an entry page, with its senses in page order.

    headword has KBBI's syllable dots removed; homograph is the superscript number that tells homographs apart, and pronunciation the syllable spelling KBBI gives for some words. root_words lists the words a derived form is built from.

    Entry::equal

    fn Entry::equal(Entry, Entry) -> Bool

    Compares two Entry values field by field with the derived Eq.

    Entry::not_equal

    fn Entry::not_equal(x : Entry, y : Entry) -> Bool

    Compares two Entry values field by field with the derived Eq.

    Entry::to_json

    Converts a Entry to JSON with the derived ToJson.

    Entry::to_repr

    Renders a Entry for debugging with the derived Debug.

    Example

    pub struct Example {
    text : String
    meaning : String?
    } derive(Eq,
    Debug
    ,
    ToJson
    )

    An example phrase for a sense. In text, -- stands for the headword; meaning holds the explanation KBBI gives for an idiomatic example.

    Example::equal

    fn Example::equal(Example, Example) -> Bool

    Compares two Example values field by field with the derived Eq.

    Example::not_equal

    fn Example::not_equal(x : Example, y : Example) -> Bool

    Compares two Example values field by field with the derived Eq.

    Example::to_json

    Converts a Example to JSON with the derived ToJson.

    Example::to_repr

    Renders a Example for debugging with the derived Debug.

    Label

    pub struct Label {
    code : String
    name : String
    description : String
    } derive(Eq,
    Debug
    ,
    ToJson
    )

    A word-class or usage label on a sense, such as v for a verb.

    code is the abbreviation shown on the page, name the label's full name (the code when KBBI gives none), and description the explanation after the name, or empty when there is none.

    Label::equal

    fn Label::equal(Label, Label) -> Bool

    Compares two Label values field by field with the derived Eq.

    Label::not_equal

    fn Label::not_equal(x : Label, y : Label) -> Bool

    Compares two Label values field by field with the derived Eq.

    Label::to_json

    Converts a Label to JSON with the derived ToJson.

    Label::to_repr

    Renders a Label for debugging with the derived Debug.

    LookupResult

    pub struct LookupResult {
    found : Bool
    query : String
    url : String
    entries : Array[Entry]
    } derive(Eq,
    Debug
    ,
    ToJson
    )

    The outcome of one lookup. found is true when entries is not empty; query is the trimmed query and url the entry page that was read.

    LookupResult::equal

    Compares two LookupResult values field by field with the derived Eq.

    LookupResult::not_equal

    fn LookupResult::not_equal(x : LookupResult, y : LookupResult) -> Bool

    Compares two LookupResult values field by field with the derived Eq.

    LookupResult::to_json

    Converts a LookupResult to JSON with the derived ToJson.

    LookupResult::to_repr

    Renders a LookupResult for debugging with the derived Debug.

    Sense

    pub struct Sense {
    labels : Array[Label]
    gloss : String
    examples : Array[Example]
    } derive(Eq,
    Debug
    ,
    ToJson
    )

    One numbered definition of an entry: its word-class and usage labels, the definition text in gloss, and the example phrases that follow it.

    Sense::equal

    fn Sense::equal(Sense, Sense) -> Bool

    Compares two Sense values field by field with the derived Eq.

    Sense::not_equal

    fn Sense::not_equal(x : Sense, y : Sense) -> Bool

    Compares two Sense values field by field with the derived Eq.

    Sense::to_json

    Converts a Sense to JSON with the derived ToJson.

    Sense::to_repr

    Renders a Sense for debugging with the derived Debug.

    DEFAULT_BASE_URL

    let DEFAULT_BASE_URL : String

    The KBBI Daring site that lookup and entry_url use unless a base_url is given.

    entry_url

    fn entry_url(query : String, base_url? : String) -> String

    The entry page URL for query: the query is trimmed, lowercased and percent-encoded as one path segment under <base_url>/entri/. A trailing / on base_url is ignored.

    lookup

    async fn lookup(client :
    Client
    , query : String, base_url? : String) -> LookupResult

    Looks up query on KBBI Daring: trims it, sends a GET request for its entry_url through client, and parses the page with parse. A word KBBI does not have gives found: false, not an error.

    Raises EmptyQuery for a blank query (before any request), Http when the transport fails, Status for a non-2xx response, and Decode when the body is not valid UTF-8. Other client policy errors propagate unchanged. The caller owns the client; its policies and body limits apply to the call.

    parse

    fn parse(html : StringView, query~ : String, url~ : String) -> LookupResult

    Parses an entry page's HTML without any IO. query is trimmed and, with url, copied into the result. Each <h2> headword followed by a list of senses becomes an Entry; a "not found" page or HTML that cannot be parsed gives found: false. Never raises.