[object Object]

← back to Norma Platform

IG captions: strip price + raw vendor (was leaking $4.25 memo price); quality + DW-service messaging, per-product variation (anti-spam)

37895ae7e640abb7a67664130ead3533adf89737 · 2026-08-11 08:28:50 -0700 · Steve Abrams

Files touched

Diff

commit 37895ae7e640abb7a67664130ead3533adf89737
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Aug 11 08:28:50 2026 -0700

    IG captions: strip price + raw vendor (was leaking $4.25 memo price); quality + DW-service messaging, per-product variation (anti-spam)
---
 agents/instagram-agent/content.js              |  29 +++++--
 agents/instagram-agent/gen-ig-activity.js      |  47 +++++++++++
 agents/instagram-agent/post-to.js              |  22 +++++
 agents/instagram-agent/public/ig-activity.json | 106 +++++++++++++++++++++++++
 4 files changed, 198 insertions(+), 6 deletions(-)

diff --git a/agents/instagram-agent/content.js b/agents/instagram-agent/content.js
index fb5456f..b5f1b0e 100644
--- a/agents/instagram-agent/content.js
+++ b/agents/instagram-agent/content.js
@@ -20,13 +20,30 @@ function handleFrom(input) {
   return { handle: s.replace(/^@/, ''), store: DEFAULT_STORE };
 }
 
+// QUALITY + DW SERVICE only. NEVER a price (the $-variant is the memo sample), and
+// NEVER the raw vendor (may be a private label that must not be named). Captions vary
+// per product so identical text across accounts doesn't trip Instagram's spam filter.
+const SERVICE_LINES = [
+  'Crafted to an exacting standard and specified for the trade — Designer Wallcoverings offers studio sampling, expert specification, and service designers rely on.',
+  'Quality you can feel, made for real interiors. Designer Wallcoverings supports the trade with samples to your studio, specification help, and white-glove service.',
+  'Considered materials, faithful color, lasting quality. Designer Wallcoverings — to-the-trade sampling and specification, handled with care.',
+  'Designer-grade quality, ready to specify. Samples to your studio and dedicated service from the Designer Wallcoverings trade team.',
+  'Beautifully made and built to last. Designer Wallcoverings brings the sampling, specification, and service serious projects demand.',
+];
 function autoCaption(p, url) {
-  const title = p.title || '';
-  const vendor = p.vendor ? ` by ${p.vendor}` : '';
-  const price = (p.variants && p.variants[0] && p.variants[0].price)
-    ? `\n$${Number(p.variants[0].price).toFixed(2)}` : '';
-  const tag = (p.product_type || 'wallcovering').toLowerCase().replace(/[^a-z0-9]/g, '');
-  return `${title}${vendor}.${price}\n\nShop: ${url}\n#wallpaper #interiordesign #wallcovering${tag ? ` #${tag}` : ''}`;
+  const title = String(p.title || 'Designer Wallcovering').split('|')[0].trim();
+  const type = (p.product_type || 'wallcovering').toLowerCase();
+  const tag = type.replace(/[^a-z0-9]/g, '');
+  const tagLine = tag && tag !== 'wallcovering' ? ` #${tag}` : '';
+  const idx = [...title].reduce((a, c) => a + c.charCodeAt(0), 0) % SERVICE_LINES.length;
+  return [
+    title,
+    '',
+    SERVICE_LINES[idx],
+    '',
+    `Explore: ${url}`,
+    `#wallpaper #interiordesign #wallcovering${tagLine} #tothetrade #designerwallcoverings`,
+  ].join('\n');
 }
 
 /** Resolve a product → { image_url, caption, title, url }. Throws if not found/imageless. */
diff --git a/agents/instagram-agent/gen-ig-activity.js b/agents/instagram-agent/gen-ig-activity.js
new file mode 100644
index 0000000..f1936a5
--- /dev/null
+++ b/agents/instagram-agent/gen-ig-activity.js
@@ -0,0 +1,47 @@
+#!/usr/bin/env node
+/**
+ * gen-ig-activity.js — compile the post ledger into a static feed for the
+ * marketing.designerwallcoverings.com "IG Network Activity" panel.
+ * Reads data/post-ledger.jsonl → writes ig-activity.json (newest first, deduped)
+ * to (a) this agent's data/ and public/, and (b) the MCC public dir if present.
+ * No server coupling — the panel is a static page that fetches this JSON.
+ */
+const fs = require('fs');
+const path = require('path');
+
+const HERE = __dirname;
+const LEDGER = path.join(HERE, 'data', 'post-ledger.jsonl');
+const reg = (() => { try { return JSON.parse(fs.readFileSync(path.join(HERE, 'accounts.json'), 'utf8')).accounts || {}; } catch { return {}; } })();
+
+const rows = fs.existsSync(LEDGER)
+  ? fs.readFileSync(LEDGER, 'utf8').split('\n').filter(Boolean).map((l) => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean)
+  : [];
+
+// dedupe by permalink (keep first-seen), newest first
+const seen = new Set();
+const posts = rows.filter((r) => { const k = r.permalink || r.media_id || JSON.stringify(r); if (seen.has(k)) return false; seen.add(k); return true; })
+  .map((r) => ({ ...r, page_name: r.page_name || reg[r.handle]?.page_name || null }))
+  .sort((a, b) => new Date(b.ts) - new Date(a.ts));
+
+const byAccount = {};
+for (const p of posts) byAccount[p.handle] = (byAccount[p.handle] || 0) + 1;
+
+const out = {
+  generated_at: new Date().toISOString(),
+  total_posts: posts.length,
+  accounts_touched: Object.keys(byAccount).length,
+  last_post_at: posts[0]?.ts || null,
+  by_account: byAccount,
+  posts,
+};
+
+const targets = [
+  path.join(HERE, 'data', 'ig-activity.json'),
+  path.join(HERE, 'public', 'ig-activity.json'),
+  path.join(process.env.HOME, 'Projects', 'marketing-command-center', 'public', 'ig-activity.json'),
+];
+for (const t of targets) {
+  try { fs.mkdirSync(path.dirname(t), { recursive: true }); fs.writeFileSync(t, JSON.stringify(out, null, 2)); console.log('wrote', t); }
+  catch (e) { /* MCC may not exist on every host */ }
+}
+console.log(`ig-activity: ${out.total_posts} posts across ${out.accounts_touched} accounts`);
diff --git a/agents/instagram-agent/post-to.js b/agents/instagram-agent/post-to.js
index f36def7..9d3554d 100644
--- a/agents/instagram-agent/post-to.js
+++ b/agents/instagram-agent/post-to.js
@@ -202,6 +202,9 @@ async function main() {
       args.image = args.image || prod.image_url;
       if (!args.caption) args.caption = prod.caption;
       console.log(`Product: ${prod.title}\n  image: ${prod.image_url}\n`);
+      args._productTitle = prod.title;
+      args._productHandle = String(args.product).replace(/^@/, '');
+      args._imageUrl = prod.image_url;
     } catch (e) { console.error(`--product failed: ${e.message}`); process.exit(1); }
   }
 
@@ -236,6 +239,19 @@ async function main() {
     try {
       const r = await publishOne(t, args);
       results.push(r);
+      if (!dry) {
+        try {
+          const fs = require('fs'), path = require('path');
+          const dir = path.join(__dirname, 'data');
+          if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
+          fs.appendFileSync(path.join(dir, 'post-ledger.jsonl'), JSON.stringify({
+            ts: new Date().toISOString(), handle: t.handle, page_name: t.page_name || null,
+            product_handle: args._productHandle || null, product_title: args._productTitle || null,
+            image_url: args._imageUrl || args.image || null,
+            permalink: r.permalink || null, media_id: r.media_id || null, kind: r.kind || null,
+          }) + '\n');
+        } catch (_) { /* ledger is best-effort; never block a post */ }
+      }
       console.log(dry
         ? `  ✓ @${t.handle} — dry-run OK (${r.kind})`
         : `  ✓ @${t.handle} — posted ${r.kind} ${r.permalink || r.media_id}`);
@@ -250,6 +266,12 @@ async function main() {
   const ok = results.filter((r) => !r.error).length;
   console.log(`\n${ok}/${results.length} ${dry ? 'validated' : 'posted'}.`);
   if (dry) console.log('Add --confirm to publish for real.');
+  // Refresh the marketing.dw "IG Network Activity" feed after any live post.
+  if (!dry && ok) {
+    try {
+      require('child_process').execFileSync(process.execPath, [require('path').join(__dirname, 'gen-ig-activity.js')], { stdio: 'ignore' });
+    } catch (_) { /* feed refresh is best-effort */ }
+  }
 }
 
 main().catch((e) => { console.error('FAILED:', e.message); process.exit(1); });
diff --git a/agents/instagram-agent/public/ig-activity.json b/agents/instagram-agent/public/ig-activity.json
new file mode 100644
index 0000000..92f8dbd
--- /dev/null
+++ b/agents/instagram-agent/public/ig-activity.json
@@ -0,0 +1,106 @@
+{
+  "generated_at": "2026-08-11T15:18:04.900Z",
+  "total_posts": 8,
+  "accounts_touched": 8,
+  "last_post_at": "2026-08-11T08:13:00-07:00",
+  "by_account": {
+    "screenprintedwallpaper": 1,
+    "metallicwallpaper": 1,
+    "goldleafwallpaper": 1,
+    "linenwallpaper": 1,
+    "grassclothwallpaper": 1,
+    "velvetwallpaper": 1,
+    "suedewallpaper": 1,
+    "textilewallpaper": 1
+  },
+  "posts": [
+    {
+      "ts": "2026-08-11T08:13:00-07:00",
+      "handle": "screenprintedwallpaper",
+      "page_name": "Screen Printed Wallpaper",
+      "product_handle": "regal-lattice-screen-printed-wallpaper-tre-12907",
+      "product_title": "Regal Lattice - Screen Printed Wallcovering",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/3547fe79068d43fd86725b501b7c7f01.jpg",
+      "permalink": "https://www.instagram.com/p/Db54ExfmxI5/",
+      "media_id": null,
+      "kind": "IMAGE"
+    },
+    {
+      "ts": "2026-08-11T08:12:00-07:00",
+      "handle": "metallicwallpaper",
+      "page_name": "Metallic Wallpaper",
+      "product_handle": "showgirls-wallpaper",
+      "product_title": "Showgirls Metallic Gold & Black Wallcovering | Graduate Collection",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/ScreenShot2023-09-11at1.53.19PM.png",
+      "permalink": "https://www.instagram.com/p/Db54BKHG8Sv/",
+      "media_id": null,
+      "kind": "IMAGE"
+    },
+    {
+      "ts": "2026-08-11T08:12:00-07:00",
+      "handle": "goldleafwallpaper",
+      "page_name": "Gold Leaf Wallpaper",
+      "product_handle": "dwtt-80525-designer-wallcoverings-los-angeles",
+      "product_title": "Metal Leaf Metallic Gold - Gold Wallcovering | Thibaut",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/T41011_e0cd5049-136c-456e-b07d-a26bbb77e65b.jpg",
+      "permalink": "https://www.instagram.com/p/Db54C7Cm-G_/",
+      "media_id": null,
+      "kind": "IMAGE"
+    },
+    {
+      "ts": "2026-08-11T07:56:00-07:00",
+      "handle": "linenwallpaper",
+      "page_name": "Linen Wallpaper",
+      "product_handle": "pure-elements-sisal-linen",
+      "product_title": "Pure Elements Sisal Linen Wallcoverings",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/0075624_sisal_650.jpg",
+      "permalink": "https://www.instagram.com/p/Db50HyfG93k/",
+      "media_id": null,
+      "kind": "IMAGE"
+    },
+    {
+      "ts": "2026-08-11T07:56:00-07:00",
+      "handle": "grassclothwallpaper",
+      "page_name": "Wallpaper Weekly",
+      "product_handle": "papalo-paper-grasscloth-prg-71096",
+      "product_title": "Papalo Paper Grasscloth | Phillipe Romano",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/cf484d7a1d1bd1eeba8b00c589a2453d.jpg",
+      "permalink": "https://www.instagram.com/p/Db50Jmnm-7m/",
+      "media_id": null,
+      "kind": "IMAGE"
+    },
+    {
+      "ts": "2026-08-11T07:55:00-07:00",
+      "handle": "velvetwallpaper",
+      "page_name": "Velvet Wallpaper",
+      "product_handle": "veronicas-madison-flocked-velvet-wallpaper-4",
+      "product_title": "Veronica's Madison Flocked Velvet Wallcovering",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/VV218_madison_MAROON_1056x_8d36f6e3-f1df-410f-a0a6-f8c80c8ac535.webp",
+      "permalink": "https://www.instagram.com/p/Db50Endm9JZ/",
+      "media_id": null,
+      "kind": "IMAGE"
+    },
+    {
+      "ts": "2026-08-11T07:55:00-07:00",
+      "handle": "suedewallpaper",
+      "page_name": "Suede Wallpaper",
+      "product_handle": "prw-920205-cts-07",
+      "product_title": "Ciudad Suede Natural | Phillipe Romano",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/prw-920205-cts-07.jpg",
+      "permalink": "https://www.instagram.com/p/Db50GKmG_k8/",
+      "media_id": null,
+      "kind": "IMAGE"
+    },
+    {
+      "ts": "2026-08-11T07:52:00-07:00",
+      "handle": "textilewallpaper",
+      "page_name": "Textile Wallpaper",
+      "product_handle": "wick-submerge-maharam",
+      "product_title": "Wick Submerge | Maharam",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/MH-WICK-021_0.jpg",
+      "permalink": "https://www.instagram.com/p/Db5zlrhG1xF/",
+      "media_id": null,
+      "kind": "IMAGE"
+    }
+  ]
+}
\ No newline at end of file

← 85c7619 auto-data-snapshot: 2026-08-10T13:03:09 (1 data files) — age  ·  back to Norma Platform  ·  ig-activity: best-effort scp to live marketing.dw after each 2d8bec8 →