Metadata-Version: 2.4
Name: navig-social
Version: 0.2.1
Summary: NAVIG Social module — compose, schedule & publish across social networks (Studio + one-call fan-out). A first-party navig plugin (free, toggleable).
Author-email: NAVIG Development Team <opensource@navig.run>
License-Expression: Apache-2.0
Project-URL: Homepage, https://navig.run
Project-URL: Documentation, https://navig.run/docs
Keywords: navig,cli,agent,automation,plugin,social,publishing,tiktok,instagram
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: navig-sdk>=2.15.0
Requires-Dist: navig-vault>=0.7.0
Requires-Dist: aiohttp>=3.9.0
Requires-Dist: requests>=2.25.0
Requires-Dist: typer>=0.14.0
Requires-Dist: rich>=13.7.0
Provides-Extra: browser
Requires-Dist: navig-browser[drive]>=0.1.0; extra == "browser"
Dynamic: license-file

# navig-social

<!-- navig:plugin-card:start -->
<!-- Generated by scripts/gen_plugin_catalog.py — edit the plugin's pyproject.toml, not this block. -->

> Compose, schedule & publish across social networks (Studio + one-call fan-out).

**Works on its own** — its own CLI, no navig required · v0.2.1 · [PyPI](https://pypi.org/project/navig-social/)

**On its own** (no navig needed)

```bash
pip install navig-social
navig-social --help   # no navig needed
```

**With navig**

```bash
pip install "navig[social]"
navig social --help
```

<details><summary>Commands</summary>

| Command | What it does |
|---|---|
| `navig facebook backup` | Download EVERY uploaded photo at full resolution + a manifest. Do this before deleting. |
| `navig facebook caption` | Edit one photo's caption (best-effort; some photos reject edits). |
| `navig facebook check` | Verify the token works and show which Page it controls. |
| `navig facebook delete` | Delete a single photo (permanent). |
| `navig facebook delete-all` | Delete every uploaded photo that is already backed up (never un-backed ones). |
| `navig facebook info` | Show the Page's current about / description / category / contact fields. |
| `navig facebook photos` | Count and preview the Page's uploaded photos. |
| `navig facebook set-about` | Update the Page's about / description / category text. |
| `navig fb backup` | Download EVERY uploaded photo at full resolution + a manifest. Do this before deleting. |
| `navig fb caption` | Edit one photo's caption (best-effort; some photos reject edits). |
| `navig fb check` | Verify the token works and show which Page it controls. |
| `navig fb delete` | Delete a single photo (permanent). |
| `navig fb delete-all` | Delete every uploaded photo that is already backed up (never un-backed ones). |
| `navig fb info` | Show the Page's current about / description / category / contact fields. |
| `navig fb photos` | Count and preview the Page's uploaded photos. |
| `navig fb set-about` | Update the Page's about / description / category text. |
| `navig social connect` | Authorize an account and store its access token in the vault. |
| `navig social disconnect` | Remove a provider's stored access token (app credentials are kept). |
| `navig social engagement` | Record engagement for a campaign — the *measure* side of the loop. |
| `navig social fan-out` | Publish ONE brief to many networks (X · Facebook · Dev.to · Telegram) with UTM tags. |
| `navig social presence crawl` | Crawl every account in a space's registry and record what moved. |
| `navig social presence trend` | First → latest movement for every tracked account, across all snapshots. |
| `navig social receipts` | What we've published — a campaign-tagged ledger of every live fan-out. |
| `navig social redirect-uri` | Print the redirect URLs to register in each provider's app settings. |
| `navig social refresh` | Renew an expiring access token in place (uses stored refresh/app creds). |
| `navig social relay cancel` | Cancel a still-pending post. A sent post can't be un-sent from here. |
| `navig social relay drain` | Send everything that is due now, without waiting for the next cron tick. |
| `navig social relay post` | Queue one post. Running the same command again does not queue it twice. |
| `navig social relay queue` | What is scheduled — the relay's stand-in for Telegram's Scheduled tab. |
| `navig social relay status` | Is the relay up, which channels may it post to, and does anything need a human? |
| `navig social report` | Campaign scorecard — how did it do? Joins what you published (receipts) with |
| `navig social stats` | Public follower / subscriber / view counts for ANY handle — no space, no linked account. |
| `navig social status` | Show which networks are connected (and, with --check, whether tokens work). |
| `navig social sync` | Pull live view/like counts from each network → the report's CTR goes real. |
| `navig social upload` | Upload a video file to YouTube (fan-out publishes text; this publishes a file). |

</details>

<!-- navig:plugin-card:end -->

NAVIG **Social** — compose and publish across social networks, track what each post did, and
watch your own accounts over time. It runs on its own as `navig-social`, and inside navig as
`navig social` / `navig facebook`.

## On its own

```bash
pip install navig-social                          # adds the `navig-social` command
navig-social connect linkedin                     # OAuth in the browser; the token goes to the vault
navig-social fan-out --file brief.json --to x,linkedin,devto --dry-run
navig-social stats youtube @mkbhd --only-public   # public counts for any handle
navig-social facebook info                        # Facebook Page tools (`navig facebook` inside navig)
```

Tokens live in the shared encrypted vault (`navig-vault`), the same one navig reads. App
credentials for `connect` come from the vault or the environment (`LINKEDIN_CLIENT_ID`,
`LINKEDIN_CLIENT_SECRET`, …); non-secret settings such as a Facebook page id come from
`config.yaml` or `NAVIG_FACEBOOK_PAGE_ID`-style variables. Receipts and engagement are recorded
in the same databases navig uses, so nothing moves when you install navig later.

**Browser-backed stats** (consent or login-walled sites) drive your installed Chrome through
navig-browser: `pip install "navig-social[browser]"`; `--only-public` needs no browser. Part of
navig, and they say so when used without it: posting to **Telegram / Discord** (navig's own
bot) and the **Studio** deck and its scheduler (navig's daemon). `presence` takes a space's
folder path on its own (`--space ./my-space`); space names need navig.

## Inside navig

## What it does

- **Publish** to X/Twitter, Facebook Page, LinkedIn, Reddit, Instagram, Threads,
  Pinterest, Dev.to (and Telegram) through a common `BasePublisher` framework.
- **Fan-out** — publish one brief to many networks in a single call, with per-platform
  adaptation + UTM tagging: `navig social fan-out --file brief.json --to x,facebook,devto,telegram`.
- **Connect** — one-command OAuth: `navig social connect <network>` (vault-backed tokens).
- **Facebook Page admin** — `navig facebook` (`fb`): check/info/set-about/photos/backup/caption/delete.
- **Studio** — the deck composer + scheduler (`/api/deck/studio/*`), fed by the background
  `ScheduledPostService`.

## Commands

```
navig social status [--check]            # connected networks
navig social connect <network>           # OAuth connect (vault-backed)
navig social fan-out --file brief.json --to x,facebook,devto,telegram [--dry-run]
navig social stats <platform> <handle>   # PUBLIC follower/view counts for ANY handle (no account link)
navig social presence crawl --space <s>  # crawl YOUR whole roster → dated snapshot + deltas
navig social presence trend  --space <s> # first → latest movement, no re-crawl
navig social relay post <channel> --text|--file … --at <when>   # schedule a Telegram post
navig social relay queue | cancel <id> | drain | status          # the relay's Scheduled tab
navig facebook check | info | photos | backup | caption | delete   (alias: navig fb)
```

## Relay — scheduled Telegram posts

**Telegram bots cannot schedule messages.** The Bot API has no method for it, and a bot trying
`schedule_date` over MTProto is refused with `ScheduleBotNotAllowedError`. So the schedule lives in
**navig-relay** (`apps/relay` in the navig repo): a Cloudflare Worker whose D1 queue is drained by
a cron, posting through the bot. `navig social relay` is the console for it — nobody hand-writes
JSON into `curl`, and delivery never depends on this machine being on.

```bash
navig social relay post somaleto --file note.md --at "2026-10-01T09:00"   # local wall-clock time
navig social relay post miztizm -t "navig 3.26 is out" --at +2h          # now · +30m · +2h · +3d · +1w
navig social relay post somaleto --file note.md --at +1d --dry-run        # show it, send nothing
navig social relay queue [--channel somaleto] [--status sent|failed|stuck|cancelled] [--all]
navig social relay cancel 3f2a9c1e      # an 8-character prefix from `queue` is enough
navig social relay drain                # send what is due now, without waiting for the cron
navig social relay status               # up? which channels? anything stuck?
```

- **Running the same `post` twice queues it once.** With an exact `--at`, the same text at the same
  time is one post. With `now` / `+2h` the resolved second moves every run, so the key is the text,
  the channel and the *day the command ran* — a retry can't double-post. `--force` queues a
  deliberate repeat, and re-posting something you cancelled says so and points at `--force`.
- A naive `--at` like `2026-10-01T09:00` is read in **this machine's timezone**, which is the clock
  you are looking at. Add `Z` or `+02:00` to be explicit.
- The relay enforces its own channel allowlist; anything else is refused before it is queued.
- `stuck` means a send whose outcome is unknown. The relay never retries those — check the channel
  in Telegram first. `status` warns when any exist.

Configuration — the first match wins:

| Setting | Flag | Environment | Vault (`relay` provider) |
|---|---|---|---|
| where the relay is | `--url` | `NAVIG_RELAY_URL` | field `url` |
| bearer token | — | `NAVIG_RELAY_TOKEN` | field `token` |

The token is sent as a header and never printed, logged, or put in an error message.

## Presence — your own accounts, over time

`stats` answers *"what is this handle's count right now"*. `presence` answers the question a
portfolio owner actually asks: *"what are ALL my accounts, and what moved since last time?"*

It reads the roster from `<space>/.navig/memory/account-registry.json`, crawls each account
through the same engine, and writes `<space>/analytics/raw/_snapshots/presence-YYYY-MM-DD.json`,
diffing against the previous snapshot so no verdict is ever made on numbers of unknown age.

```
navig social presence crawl --space human-presence --only-public   # no browser, API/public only
navig social presence crawl --space human-presence --brand somaleto --dry-run
navig social presence trend  --space human-presence --platform telegram
```

- `--only-public` keeps it to plain HTTPS + official APIs — no CDP, no login, cron-safe.
- Successful counts are written back to the registry (`--no-write-registry` to skip). A failed or
  login-walled fetch **never** overwrites the last number actually observed.
- Accounts marked `"snapshot_status": "dead"` are skipped, so a retired handle stops being
  re-crawled and stops counting as a failure.
- Public metadata only. No tokens, cookies or session data are read from or written to the space.

Schedule it so the numbers are never stale:

```
navig cron add --name presence-weekly --schedule "0 9 * * 1" \
  --command "navig social presence crawl --space human-presence --only-public"
```

## Install

Bundled with a full `navig` install (the `social` extra); standalone:
`navig store install pip:navig-social` — or `pip install navig-social` once published.
Requires `navig-core` in the same environment.

Credentials live in the NAVIG vault (`navig vault`) + config `adapters.social.<network>.*`.
Tokens are never printed.
