blackmoon 0.2.1

Command-line astrology data-format converter built on astrogram
blackmoon-0.2.1 is not a library.

Blackmoon

Crates.io License

A command-line astrology data manager — reads any supported target, writes any supported target, deduplicates everything in between.

What it does

One CLI verb (run with no subcommand). Inputs are file paths or a web account; the output is another file or another web account. Target type is detected from the file extension (.SFcht, .zdb, .xml) or specified explicitly with --from / --to / --target.

blackmoon input.zdb --output out.SFcht
blackmoon a.SFcht b.zdb export.xml --output merged.SFcht
blackmoon --from luna --luna-session $COOKIE --output charts.SFcht
blackmoon --from astro --astro-user me@x.com --astro-pass ... --output charts.SFcht
blackmoon --from astrotheoros --astrotheoros-user me@x.com --astrotheoros-pass ... --output charts.SFcht
blackmoon charts.SFcht --normalize

Targets currently wired up:

  • File: Solar Fire .SFcht binary (cp1252), Zeus .zdb semicolon-text, Astrodatabank .xml.
  • Web (authenticated): lunaastrology.com (--from/--to luna), astro.com (--from/--to astro) — full CRUD including --delete <ids> for astro.com.

--normalize strips non-cp1252 characters and collapses whitespace; with no --output, it edits each input file in place. --output now.SFcht substitutes a UTC timestamp.

Why it exists

Astrologers can have their data spread out across many tools. Consolidating records is time consuming and error prone. Blackmoon automates the task entirely.

How it works

blackmoon is a thin CLI over astrogram. The pipeline for every conversion run is the same five steps:

  1. Read the sink first (if it already exists or is a web account) so the resulting set never gains duplicates. For LUNA® this is a cheap listing-only fetch keyed by (name, full datetime); for astro.com it's a full chart fetch with nhor IDs; for files it's a normal parse.
  2. Read each input in batch order, parsing through the per-format reader in astrogram. Inputs already present in the sink listing are filtered out before merge so duplicates never even reach the deduper.
  3. Merge and dedup with consolidate::merge_reporting: same name + same date + time within ±2h + lat/lon within 0.1°. First-seen wins; the dropped names are reportable with --verbose.
  4. Optionally normalize the merged set (cp1252 cleanup for any sink, mandatory before writing .SFcht).
  5. Write the merged set to the chosen sink. Web sinks (--to luna, --to astro) carry an interactive y/N confirmation before any mutation, plus per-chart progress reporting.

Credentials come from flags or env vars (LUNAASTROLOGY_COOKIE, ASTROCOM_COOKIE, ASTROCOM_USER, ASTROCOM_PASS); the help output hides their values. --delay rate-limits HTTP requests. --resume-from <prefix> resumes an interrupted LUNA® fetch.

Credential sources are not mutually exclusive. When several are available for a target (a browser cookie via --grant-cookie-access (powered by wristband), a token, and/or login creds), blackmoon tries them in order — cookie → token → login — and falls through to the next when one is rejected as stale (e.g. an expired cookie falls back to your saved password). It discloses which source authenticated, naming a fall-through when one occurred.

For astro.com specifically, the session cookie authenticates reads only; deleting a chart re-submits your account password. So --delete/--consolidate against astro.com requires --astrocom-user/--astrocom-pass (or ASTROCOM_USER/ASTROCOM_PASS) even when a working cookie is present — the cookie reads, the password deletes.

In-place LUNA® consolidation

blackmoon can dedupe a LUNA® account in place by surfacing candidate groups for human decision — no record is ever auto-dropped.

One-off delete by UUID

blackmoon --luna-delete <uuid1>,<uuid2> --luna-session "$LUNAASTROLOGY_COOKIE"

Each UUID is deleted via POST /phenomena/delete/<uuid> (CakePHP DELETE method tunnel). Per-record progress is printed; failures are tallied and the run exits non-zero if any delete failed. --delay is honoured between deletes.

Interactive consolidation

blackmoon --target luna --consolidate --luna-session "$LUNAASTROLOGY_COOKIE"

The flow:

  1. Fetch every chart in the LUNA® account.
  2. Cluster duplicate candidates by spacetime tolerance: date equal, time within ±2h, latitude and longitude within 0.1° of each other. Names are not part of the trigger — they appear in the screen as a labelling cue, since the bug we are fixing is exactly the case where two charts for the same person have different names.
  3. Walk one group per screen. Press a/b/c/… to mark the keeper — every other chart in that group becomes a Drop. Press s to skip the group (it will be re-prompted on the next run); press q to quit immediately (decisions already logged are still applied).
  4. Each keystroke is fsync'd to the decision log before the loop advances, so a crash never loses a prior choice.
  5. After the loop, a single confirmation gate (y/N) precedes the apply phase, which calls delete_phenom for every Drop record. --delay is honoured between deletes.

Singleton groups (no other candidate within tolerance) are silently filtered out — there is nothing to consolidate.

Decision log

Default path: $XDG_CACHE_HOME/blackmoon/luna-decisions.jsonl (or ~/.cache/blackmoon/luna-decisions.jsonl when XDG_CACHE_HOME is unset). Override with --decision-log <PATH>.

Each line is one JSON DecisionRecord with these fields:

  • group_id — opaque cluster identifier (first non-empty phenom UUID in the group).
  • phenom_id — LUNA® phenomenon UUID this decision applies to.
  • choice"keep", "drop", or "skip".
  • chart_name — display name at decision time, informational.

Re-running --consolidate reads the log first and silently skips any group whose group_id is already decided, so resuming is a no-op if everything was finished. A partially-written trailing line (the "crash mid-write" case) is silently skipped on read — the user is re-prompted for that one record on the next run.