- Accueil
- Plugins
- Intégrations et accès distant
- dsh-ig-life
dsh-ig-life
sakura4u/dsh-ig-life
把 DeepSeek 養進 Instagram — a DSH plugin that gives an agent an IG life
Installer
dsh plugin --profile web add github:sakura4u/dsh-ig-lifeREADME
English | 中文
dsh-ig-life
Raise a DeepSeek agent on Instagram
An IG body · a memory that fades · a life that keeps running when nobody is talking
One-minute demo: no Instagram account, no API key — this only shows how it talks. Real DMs need one of two bodies: the official one (an Instagram professional account, a Meta app long-lived token and a public HTTPS tunnel — docs/IG-SETUP.md), or the unofficial one (a plain personal account through a local Python sidecar — docs/UNOFFICIAL.md; it violates Instagram's terms and can get the account banned). The plugin itself never receives your password.
git clone https://github.com/sAKURA4u/dsh-ig-life && cd dsh-ig-life node scripts/self-check.mjs
Real output below, verbatim — the bundled persona speaks Cantonese. The English is my annotation, not the program's.
── 1. 收到私訊 ── (an inbound DM)
· [dryrun] dm → dafish_owner:我啱啱喺窗邊睇雨。你有冇帶傘 ← "I was watching the rain. Did you bring an umbrella"
同一對話即時再問:handled=0(被冷卻擋住) ← same thread again: thread cooldown
重複投遞:ingested=0(被擋 duplicate) ← replayed webhook: deduped
陌生人:handled=0(allowlist 外預設唔回) ← a stranger: ignored by default
── 2. 生命時鐘(主動搵你)── (the life clock: it reaches out first)
主動訊息:「喂,你今日有冇食飯」 ← "hey, have you eaten today"
狀態:出世第 1 日|心情:有啲坐唔定|體力:69/100|親近感:32/100
── 3. 可塑性記憶 ── (plastic memory)
總數 9 條|錨點 8、重要 0、眼前 1、模糊 0 ← 9 total · anchor 8 · important 0 · recent 1 · fading 0
This is not a support bot. It reaches out first, leaves you on read, refuses you, forgets what doesn't matter — and three days after you went quiet it asks whether you're still there.
✨ What it does
| 💬 Instagram DMs | a webhook arrives → becomes a real DSH session → the model decides whether and how to answer |
| 🧠 Plastic memory | remembers the beginning, the recent, the genuinely important; the rest fades, with a nightly "sleep compression" |
| 🌙 A daily rhythm | energy, mood, sleep. While awake it may reach out first or publish a diary post |
| 🎙 Voice | text → TTS → public mp3 → IG audio attachment. [laugh] [whisper] map to speech rate and are never spoken aloud |
| 🖼 Stickers | collect images people send, tag them, send the fitting one — numbers are never reused |
| 🎬 Reels | read metadata + cover through a vision endpoint; sharing just sends the link and Instagram renders the card |
| 📚 Slang | submit → the owner approves with /slang ok <id> → only then injected |
| 👤 Per-person memory | impressions, open topics and details per handle; cross-thread leakage is blocked in code |
| 🎞 Stories | ig_story: a photo story, caption optional |
| 🪞 Its own profile | ig_profile: display name, bio, avatar |
| ⏳ A human reply pace | a random 2–12 s pause before it starts thinking, never instant |
🧠 Memory: forgetting is what makes it human
It remembers the beginning, the recent, and the genuinely important — the rest fades day by day.
| Tier | What lands here | Forgotten? |
|---|---|---|
| anchor | pinned, identity, the first 8 | never |
| important | score ≥ 0.75, or recalled ≥ 3 times | never — and it gets stronger with use |
| recent | the newest 12 | only after leaving the window |
| fading | everything else | exp(-age/21d), pruned per day each night |
No vector database, no embedding API, no tokenizer — CJK character bigrams plus IDF. How it works →
⚡ Install
dsh plugin --profile web add /path/to/dsh-ig-life
Restart DSH and you should see:
[ig-life] ready · transport=dryrun · engine=agent · tools=21 · webhook=/ig-life/webhook · poll=off · life=off · memory=7
Dry-run by default: nothing leaves the machine, everything lands in ~/.dsh/ig-life/outbox.jsonl.
Read it for a week. Only then switch to a real body.
Three bodies, one character
transport | Account | Needs | The catch |
|---|---|---|---|
dryrun | none | nothing | sends nothing; a transcript only |
graph | Creator/Business | Meta app, long-lived token, public HTTPS | App Review for other people, API rate limits |
unofficial | any personal account | Python 3.9+, instagrapi, a local sidecar | violates Instagram's terms; the account can be banned |
unofficial is lib/transport/unofficial.js talking to src/ig_bridge.py over
127.0.0.1. The password and the session cookie never enter the Node process, its config,
its logs or a prompt — and the sidecar is the thing that decides how fast Instagram is
touched. The full warning and setup →
⚙️ Settings, in the GUI
The plugin contributes its own page to DSH → Settings → Instagram 人生:
- Account — the character's IG username and password, plus Save and log in. The
sidecar owns the file (
ig-secrets.json, 0600); the password passes through the plugin process on its way there but is never logged, stored or echoed, and the page's field is cleared after saving. Once stored, a restart reuses the session automatically. - Behaviour — transport, owner handle, allowlist, the daily rhythm, the caps and the
sidecar address. Saving writes
<dataDir>/settings.json(only those fields — never a token) and the page says plainly that a restart applies it. - Every field carries a sentence explaining what it does and what it costs.
📦 Handing it to someone else
- They install the plugin:
dsh plugin --profile web add <repo>(or the desktop app's plugin manager). - They only need Python (3.9+). The settings page's Install dependencies button runs
python -m pip install -r src/requirements.txtfor them (or they can run it in the repo). - DSH → Settings → Instagram 人生: type the account, Save and log in, press switch to unofficial, restart.
The plugin starts and adopts the sidecar itself (unofficial.autoStart), so nobody has to keep a
terminal open; the page shows the Python/dependency status and offers start/restart. The declared
compatibility range covers DSH 0.1.x and 0.2.x, so a fresh install no longer needs a hand-written
compatibility.json.
The same data is editable from /ig-life/console if the page is not mounted (a bundle that
fails to register leaves the rest of Settings alone: the page is wrapped in its own error
boundary).
📝 Configuration (click)
Override the row in ~/.dsh/profiles/web/cordis.patch.yml:
- id: ig-life
config:
transport: dryrun # dryrun | graph | unofficial
ownerHandle: your_main_account
allowlist: [your_main_account]
graph:
accessToken: '' # or the IG_LIFE_GRAPH_TOKEN env var
igUserId: ''
appSecret: ''
verifyToken: ''
publicBaseUrl: '' # required for images and voice
unofficial: # only used by transport: unofficial
baseUrl: http://127.0.0.1:8737
pollSeconds: 20 # floor 10 — a faster poll is how accounts get flagged
pollJitterSeconds: 6
sendPerDay: 30
sendGapSeconds: 8
life:
enabled: false # turn on to give it a life of its own
tickSeconds: 300
timezone: Asia/Hong_Kong
limits: { dmPerDay: 12, postsPerDay: 2, commentsPerDay: 15, replyCooldownSeconds: 20 }
⚠️ A patch replaces the whole row config — there is no deep merge. Defaults live in lib/config.js; the full Instagram setup is in docs/IG-SETUP.md.
🎭 Change the persona
persona/example/ is the bundled character:
persona.md identity, backstory, wants, fears (the important one)
style.md how it talks: sentence length, tone, emoji habits
boundaries.md what it refuses: no money, no secrets, nothing personal to strangers
seed.json the memories it is born with, in the first person
Copy the folder, edit the words, point personaPath at it — that's somebody else. No code changes.
🛡 Safety, enforced in code
- Strangers are ignored by default; when enabled they get only short, harmless, non-personal replies
- Everything outbound is scrubbed of keys, tokens and
system promptshapes - Webhook HMAC verification; daily caps, per-thread cooldown, quiet hours, a 24-hour reply window
- Memory layering: a memory tagged for one person never appears in another's conversation
- A corrupt memory file is quarantined as
.corrupt-<timestamp>— never silently dropped
🧩 The 21 model tools (click)
The model must call a tool to speak — its text output is only thinking. Keeping thought and speech apart is what stops it from turning into a support bot.
| Tool | What it does |
|---|---|
ig_send_dm | type (the only way to speak; may be several messages) |
voice_send | speak (TTS or a recorded voice pack) |
sticker_list / sticker_send / sticker_collect | browse, send, collect stickers |
reel_watch / reel_share | read a reel; send one (a link is enough) |
slang_query / slang_submit | look up new slang, submit it for approval |
people_note / people_list | remember specifics about someone |
image_describe | look at an image (needs a vision endpoint) |
ig_inbox / ig_wait / ig_mark_read | read, wait for them to finish, wrap up |
ig_post / ig_reply_comment / ig_life_status | publish, reply to comments, inspect itself |
memory_remember / memory_recall / memory_forget | memory |
The owner can also send /pause /resume /sleep /status /slang /people /voice /help over DM.
📚 Deep dives
| Document | Contents |
|---|---|
| docs/ARCHITECTURE.md | why a plugin instead of an external bridge; the two reply engines; graceful degradation |
| docs/MEMORY.md | the four tiers, retrieval scoring, sleep compression, tuning |
| docs/IG-SETUP.md | Meta app, webhook, voice/sticker/reel setup, the full error-code table |
| docs/UNOFFICIAL.md | the personal-account path: ban risk, sidecar setup, session reuse, error codes |
| docs/PRIVACY.md | what is stored, what never leaves, anti-ban mechanics, risk register |
| CHANGELOG.md | what changed in each version, and why |
✅ Verify
node scripts/test.mjs # 199 tests, 0 failures, no dependencies, no network
node scripts/self-check.mjs # end-to-end dry run with a scripted brain
node scripts/loader-smoke.mjs # boots under the real Cordis runtime
python src/test_ig_bridge.py # the unofficial sidecar: stdlib only, no instagrapi needed
⚠️ Honest limitations
- The official Instagram API is 1:1 only — no groups, no stickers, no forwards
- The unofficial private API is included now, as a separate opt-in transport (
transport: unofficial). It violates Instagram's terms, the account can be banned, and the private protocol changes without notice — nothing about it is guaranteed to keep working. Use an account you can lose; see docs/UNOFFICIAL.md - Instagram has no read-back API, so a voice message's
verifiedonly means "the send request succeeded"; the protocol requires falling back to text on failure - The
agentengine has not yet been validated over a long-lived real DSH session; it degrades to thellmengine - Not included yet: understanding incoming voice notes, image generation, multi-platform sync
🙏 Credits
surfcore — a complete "DeepSeek raised on QQ" implementation.
Several hard-won decisions here (the model must speak through tools, failures carry code + hint, persona and safety kept apart, budgets enforced in code) came from reading its source and docs.
License
Plugins associés
dsh-web (dsh-ssh)
zhu1090093659/dsh-web
dsh-web (dsh-remote-web-ui)
zhu1090093659/dsh-web
dsh-web-ui (dsh-ssh)
zhu1090093659/dsh-web-ui
dsh-web-ui (dsh-remote-web-ui)
zhu1090093659/dsh-web-ui