[object Object]

← back to Norma Platform

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

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 Platform  ·  TK-10758: reshare-erase pacing 15s->5s (browser-free, own ac 7507111 →