← back to Norma
TK-10758: stop+erase sassy reposts of others' content - disabled reshare launchd jobs, browser-free erase reconciler (114 live across 4 accts)
404bd1515de5ff5a1daa04a8abc3969f73fa1990 · 2026-08-20 16:44:42 -0700 · Steve Abrams
Files touched
A agents/instagram-agent/reshare-erase.js
Diff
commit 404bd1515de5ff5a1daa04a8abc3969f73fa1990
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Aug 20 16:44:42 2026 -0700
TK-10758: stop+erase sassy reposts of others' content - disabled reshare launchd jobs, browser-free erase reconciler (114 live across 4 accts)
---
agents/instagram-agent/reshare-erase.js | 109 ++++++++++++++++++++++++++++++++
1 file changed, 109 insertions(+)
diff --git a/agents/instagram-agent/reshare-erase.js b/agents/instagram-agent/reshare-erase.js
new file mode 100644
index 0000000..5492393
--- /dev/null
+++ b/agents/instagram-agent/reshare-erase.js
@@ -0,0 +1,109 @@
+#!/usr/bin/env node
+/**
+ * reshare-erase.js — TK-10758. Steve: "stop reposting other people's content + erase those
+ * we reshared with the sassy comments across all IG accounts."
+ *
+ * The resharer (hashtag-reshare.js / reshare-fleet.js — now DISABLED) republished OTHER people's
+ * images on DW accounts with a rotating SASS[...] caption. Every such post is logged in
+ * data/reshares-<account>.jsonl as { ts, permalink, our_media_id }. This tool reads all those
+ * ledgers and, per our_media_id:
+ * - GET /{id}?fields=id,caption,permalink → is it still live? capture the (sassy) caption.
+ * Then classifies live vs already-gone and writes the erase list.
+ *
+ * node reshare-erase.js # PROBE (read-only): list live reshares + captions, delete nothing
+ * node reshare-erase.js --go # DELETE each live reshare, verified + tombstoned (destructive)
+ *
+ * Deletion is browser-free Graph-API (no ban vector), paced, verified. Default probe-only.
+ */
+require('dotenv').config();
+const fs = require('fs');
+const path = require('path');
+const https = require('https');
+
+const DATA = path.join(__dirname, 'data');
+const TOMBS = path.join(DATA, 'deleted-posts.jsonl');
+const TOKEN = process.env.IG_ACCESS_TOKEN;
+const VER = process.env.IG_GRAPH_VERSION || 'v21.0';
+const HOST = 'graph.facebook.com';
+const PACE_MS = 15000;
+const TICKET = 'TK-10758';
+const args = process.argv.slice(2);
+const GO = args.includes('--go');
+const only = (() => { const i = args.indexOf('--only'); return i >= 0 ? args[i + 1] : null; })();
+const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+
+function req(method, p) {
+ return new Promise((resolve) => {
+ const r = https.request(`https://${HOST}/${VER}/${p}`, { method }, (res) => {
+ let s = ''; res.on('data', (d) => (s += d));
+ res.on('end', () => { let j = null; try { j = JSON.parse(s); } catch {} resolve({ status: res.statusCode, body: j }); });
+ });
+ r.on('error', () => resolve({ status: 0, body: null }));
+ r.end();
+ });
+}
+const get = (p) => req('GET', p);
+const del = (p) => req('DELETE', p);
+
+function loadLedgers() {
+ const rows = [];
+ for (const f of fs.readdirSync(DATA)) {
+ const m = f.match(/^reshares-(.+)\.jsonl$/);
+ if (!m) continue;
+ const handle = m[1];
+ if (only && handle !== only) continue;
+ fs.readFileSync(path.join(DATA, f), 'utf8').trim().split('\n').filter(Boolean).forEach((l) => {
+ try { const j = JSON.parse(l); if (j.our_media_id) rows.push({ handle, ...j }); } catch {}
+ });
+ }
+ // dedup by media id
+ const seen = new Set();
+ return rows.filter((r) => (seen.has(r.our_media_id) ? false : seen.add(r.our_media_id)));
+}
+function tombstoned() {
+ if (!fs.existsSync(TOMBS)) return new Set();
+ return new Set(fs.readFileSync(TOMBS, 'utf8').trim().split('\n').filter(Boolean)
+ .map((l) => { try { return JSON.parse(l).media_id; } catch { return null; } }).filter(Boolean));
+}
+
+(async () => {
+ const rows = loadLedgers();
+ const done = tombstoned();
+ const report = [];
+ for (const r of rows) {
+ if (done.has(r.our_media_id)) { report.push({ ...r, state: 'already-deleted' }); continue; }
+ const g = await get(`${r.our_media_id}?fields=id,caption,permalink&access_token=${TOKEN}`);
+ const live = g.status === 200 && g.body && g.body.id;
+ report.push({ ...r, state: live ? 'LIVE' : 'gone', caption: live ? (g.body.caption || '').replace(/\n/g, ' ').slice(0, 90) : null });
+ await sleep(120);
+ }
+ fs.writeFileSync(path.join(DATA, 'reshare-erase-list.json'), JSON.stringify({ ts: new Date().toISOString(), report }, null, 2));
+
+ const live = report.filter((x) => x.state === 'LIVE');
+ const byH = {}; live.forEach((x) => (byH[x.handle] = (byH[x.handle] || 0) + 1));
+ console.log('\n=== RESHARE ERASE (TK-10758) ===');
+ console.log('ledger rows:', report.length,
+ '| LIVE:', live.length,
+ '| already-deleted:', report.filter((x) => x.state === 'already-deleted').length,
+ '| gone:', report.filter((x) => x.state === 'gone').length);
+ console.log('LIVE by account:', JSON.stringify(byH, null, 2));
+ console.log('\nsample sassy captions (proof these are the reposts):');
+ live.slice(0, 6).forEach((x) => console.log(` @${x.handle} ${x.permalink}\n "${x.caption}"`));
+
+ if (!GO) { console.log('\nPROBE only — nothing deleted. Run --go to erase all LIVE reshares (paced, verified).'); return; }
+
+ let ok = 0, fail = 0;
+ for (const t of live) {
+ process.stdout.write(`DELETE @${t.handle} ${t.permalink} ... `);
+ const d = await del(`${t.our_media_id}?access_token=${TOKEN}`);
+ await sleep(1500);
+ const chk = await get(`${t.our_media_id}?fields=id&access_token=${TOKEN}`);
+ const gone = chk.status !== 200 || (chk.body && chk.body.error);
+ if (gone) {
+ fs.appendFileSync(TOMBS, JSON.stringify({ ts: new Date().toISOString(), handle: t.handle, media_id: t.our_media_id, permalink: t.permalink, method: 'graph-api', ticket: TICKET, reason: 'sassy-reshare-of-others-content', verified: true }) + '\n');
+ ok++; console.log('✓ deleted+verified');
+ } else { fail++; console.log(`✗ FAILED (${d.status})`); }
+ await sleep(PACE_MS);
+ }
+ console.log(`\nDONE: erased+verified ${ok}, failed ${fail}, of ${live.length}.`);
+})();
← 75f9762 TK-10573 GATE2: browser-free Graph-API reconcile + delete to
·
back to Norma
·
TK-10758: reshare-erase pacing 15s->5s (browser-free, own ac 7507111 →