Skip to main content

navig-contacts

navig contacts: the address book and the alias-to-route table dispatch resolves against, in one store. Imports vCard and Telegram exports, merges the same person out of many records, and keeps only contacts with a phone number that validates.

Works on its own — its own CLI, no navig required · v0.3.0 · PyPI

On its own (no navig needed)

pip install navig-contacts
navig-contacts --help   # no navig needed

With navig

pip install navig-contacts
navig contacts --help
Commands
Command What it does
navig contacts add Add a contact by hand.
navig contacts doctor Check that the book says the same thing in both of its halves.
navig contacts export Export contacts as CSV.
navig contacts flag Record whether a handle still resolves, so exports can skip dead ones.
navig contacts import Import an address book, merging duplicates into one contact per person.
navig contacts list List contacts.
navig contacts liveness What the book knows about which routes still resolve.
navig contacts merge Fold one contact into another, reversibly.
navig contacts merges Every merge and split applied, and whether it was undone.
navig contacts migrate Bring a pre-merge contact book onto the shared schema.
navig contacts remove Remove a contact.
navig contacts resolve Ask Telegram which of these handles still reach a person.
navig contacts review Record that a flagged merge was looked at and found correct.
navig contacts route Add or remove a route for a contact.
navig contacts search Find someone by anything you remember about them.
navig contacts show Everything known about one contact, and where it came from.
navig contacts split Move one identifier onto a contact of its own.
navig contacts stats How many people, at which tiers, and how much evidence backs them.
navig contacts undo Reverse a merge, restoring the absorbed contact.

navig contacts — your address book and the routing table messages go out on, in one store. Import the exports you have accumulated, merge the same person out of the many records they are scattered across, and keep straight who you can actually reach.

navig contacts import ~/Backups/AddressBook --dry-run
navig contacts import ~/Backups/AddressBook
navig contacts list --tier verified
navig contacts search Totara
navig contacts add --alias alice --phone +33612345678

One command, two halves

navig contacts used to be two things. Core had an alias→route table for message dispatch — who is @alice, and where does a message to her go. Nothing had the other half: the people themselves, merged out of whatever exports had piled up.

They are the same question at two depths, so they are now one command over one store:

is example
a route a transport sms:+33612345678, whatsapp:…, discord:…
an identifier the address itself phone:+33612345678 — and it belongs to exactly one person

That last property is what makes an identifier the key that decides two records are the same human. And because both live in one store, importing an address book makes those people messageable: an imported phone becomes an sms: route, so navig dispatch send can reach anyone the import found.

Two books exist. The default is the global one at ~/.navig/data/contacts.db — what this command has always meant. --space <name> reads a space's own book, so a client list and a private circle are not forced into one table.

The rule it turns on

A contact is legit only if it has a phone number — and "has a phone number" means libphonenumber agrees the number is valid, not that the string contained digits. Real archives are full of things that look like numbers and are not: service short codes (3949, 33700, 36102), placeholders (+7**********), caller-ID-block prefixes (#31#15), doubled country codes (+7+79603166750 — repaired, then validated), and troll numbers (+7696969696969696969969999).

Everything else is filed as a lead, at a tier that says exactly what is known:

Tier Meaning
verified a phone number validated — the real contacts
email an email address, no phone
handle only a Skype / Telegram / Facebook / VK account
archive no way to reach them at all — reported, never stored

A lead is promoted automatically: the day a card turns up carrying a phone for someone known only by a handle, they become verified.

One person, many cards

contacts holds the person. Four tables hold the evidence:

  • contact_identifiers — every phone, email and handle they are reachable by. A partial unique index on (kind, value_norm) is the merge key: a second card carrying a known number attaches to the existing person instead of creating a twin.
  • contact_aliases — every name variant ever seen.
  • contact_sources — which file, which card, and the original vCard text, so any import decision can be re-derived.
  • merge_log — a JSON snapshot per absorbed record, so every merge is reversible with navig contacts undo.

Names are never a merge key

Not a preference — a measurement. On the archive this was built against, the only names shared between an imported vCard set and an existing contact database were generic first names (anna, alex, diana). Merging on them fuses strangers. Name similarity is reported for a human to judge and never applied.

What it tells you about its own work

Every import writes its reasoning to <space>/exports/:

Report What it settles
-verified.txt the roster of people with a valid phone
-merges.md every merge, and the identifier that caused it
-review-suspicious.md merges that may have fused two people — read this
-rejected-phones.csv every number thrown away, with the reason
-assumed-regions.csv every number that only validated because a region was guessed
vcf-interests-*.md the non-people found in a follow list
-dropped.csv everything discarded, so nothing vanishes unrecorded

A bare national number needs a region. The vCard ADR country and X-SKYPE-COUNTRY are consulted first; only genuinely context-free numbers fall back to a default, and every one of those assumptions is listed rather than made silently.

Commands

navig contacts import <file|folder> [--dry-run] [--source LABEL] [--no-reports]
navig contacts list [--tier verified|email|handle|archive] [--limit N] [--plain]
navig contacts search <query>          # any alias, name, phone, email, handle or route
navig contacts show <alias>
navig contacts add --alias A [--name N] [--phone P] [--route network:address]
navig contacts route <alias> add|remove <network:address> [--priority N]
navig contacts remove <alias> [--yes]
navig contacts merge <alias> <into>    # merges  /  undo <id>
navig contacts split <alias> KIND:VALUE [--as NAME]
navig contacts export [--tier T] [--output FILE]
navig contacts stats
navig contacts migrate [--apply]       # a book from before the two halves merged

Every command takes --space/-s; without it the global book is used.

Install

pip install navig-contacts                     # on its own: `navig-contacts --help`
pip install "navig-contacts[photos]"           # with avatar extraction
pip install -e plugins/navig-contacts          # in the monorepo

On its own it is the same address book at the same path (<data dir>/contacts.db), so a book built with navig-contacts is the one navig contacts and message dispatch read once navig is installed. Two things need navig and say so: --space (spaces are navig's) and importing a Telegram export (vCards import without it).

phonenumbers is a hard dependency — the phone gate is the product. Pillow and ImageHash are optional: without them avatar extraction reports itself as unavailable instead of failing the import.

Migrating a book from before the merge

A space that ran the older space-local tool has a book keyed on uid, with handles in a social_profiles table. navig contacts migrate --space <name> reports what it would change; --apply does it, after a backup. The columns are renamed in place and the handles become identifiers and routes — nothing is rebuilt and nothing is discarded. On a real 4,077-contact book that produced 2,038 routes, which is 2,038 people who became messageable.

Status

The engine came from a working tool with 4,077 contacts in it, so the vCard dialects it handles (Google Contacts, Apple/iOS, Skype) are the ones it was actually tested against.

Metadata

Release files for navig-contacts 0.3.0

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

Source distribution (sdist)

Source distribution for navig-contacts 0.3.0
File Size Uploaded
navig_contacts-0.3.0.tar.gz 111.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for navig-contacts 0.3.0
File Interpreter ABI Platform
navig_contacts-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 208.6 kB

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

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