← back to Rentv Ad Engine
LIVE: mounted at rentv.agentabrams.com/advertisers — Kamatera pm2 rentv-ad-engine :9793 (9789 was taken by ventura-claw-leads), nginx /advertisers block added to rentv vhost with auto-restore guard + backup; app-auth only. Verified 200/301/401 + rentv+ventura unaffected
ae55801e4c4e9799f61c0d6de3f44ce3f0a40455 · 2026-08-07 10:45:45 -0700 · Steve Abrams
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M deploy/nginx-advertisers.confM scripts/draft-emails.pyM server.js
Diff
commit ae55801e4c4e9799f61c0d6de3f44ce3f0a40455
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Aug 7 10:45:45 2026 -0700
LIVE: mounted at rentv.agentabrams.com/advertisers — Kamatera pm2 rentv-ad-engine :9793 (9789 was taken by ventura-claw-leads), nginx /advertisers block added to rentv vhost with auto-restore guard + backup; app-auth only. Verified 200/301/401 + rentv+ventura unaffected
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
deploy/nginx-advertisers.conf | 2 +-
scripts/draft-emails.py | 17 +++++++++++++++--
server.js | 12 +++++++++++-
3 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/deploy/nginx-advertisers.conf b/deploy/nginx-advertisers.conf
index 6d8494a..940503f 100644
--- a/deploy/nginx-advertisers.conf
+++ b/deploy/nginx-advertisers.conf
@@ -9,7 +9,7 @@
location = /advertisers { return 301 /advertisers/; }
location /advertisers/ {
- proxy_pass http://127.0.0.1:9789/; # trailing slash strips the /advertisers/ prefix
+ proxy_pass http://127.0.0.1:9793/; # trailing slash strips the /advertisers/ prefix
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
diff --git a/scripts/draft-emails.py b/scripts/draft-emails.py
index 4fd3ed2..d0f8f71 100644
--- a/scripts/draft-emails.py
+++ b/scripts/draft-emails.py
@@ -17,7 +17,7 @@ MODEL = os.environ.get("OLLAMA_MODEL", "qwen2.5:latest")
MIN_RUNS = int(os.environ.get("MIN_RUNS", "1"))
FORCE = os.environ.get("FORCE", "") == "1"
-PROMPT = """Write a warm, professional business-development email from RENTV to {name}, a company that has advertised/sponsored RENTV's "Commercial Real Estate Talk" show, to thank them for their partnership and renew or expand their advertising. Reference their sponsorship naturally; do NOT invent facts not in the context. Keep it under 170 words. Sign as "Steve Bloom, RENTV".
+PROMPT = """Write a warm, professional business-development email from RENTV to {name}, a company that has advertised/sponsored {show}, to thank them for their partnership and renew or expand their advertising. Reference their sponsorship naturally; do NOT invent facts not in the context, and do NOT name a show they did not sponsor. Keep it under 170 words. Sign as "Steve Bloom, RENTV".
Return ONLY JSON: {"subject":"...","body":"...(use \\n for line breaks)..."}
@@ -37,6 +37,19 @@ def ollama(prompt):
return json.loads(m.group(0)) if m else {}
+def show_phrase(a):
+ """Name the RIGHT RENTV property per advertiser, from their episodes' source:
+ the SoundCloud podcast, the Vimeo interview show, or both (cross-source)."""
+ srcs = {e.get("source", "vimeo") for e in a.get("episodes", [])}
+ pod = "soundcloud" in srcs
+ vim = "vimeo" in srcs or not srcs
+ if pod and vim:
+ return 'RENTV\'s "Commercial Real Estate Talk" show and the "Weekly CRE News & Views" podcast'
+ if pod:
+ return 'RENTV\'s "Weekly CRE News & Views" podcast (hosted by Steve Bloom)'
+ return 'RENTV\'s "Commercial Real Estate Talk" show'
+
+
def context_for(a):
return (f"Advertiser: {a['name']}\nCategory: {a.get('category','')}\n"
f"Websites: {', '.join(a.get('websites',[]))}\nPhones: {', '.join(a.get('phones',[]))}\n"
@@ -56,7 +69,7 @@ def main():
if a["name"] in prev and not FORCE:
out.append(prev[a["name"]]); continue
try:
- r = ollama(PROMPT.replace("{name}", a["name"]).replace("{context}", context_for(a)))
+ r = ollama(PROMPT.replace("{name}", a["name"]).replace("{show}", show_phrase(a)).replace("{context}", context_for(a)))
except Exception as e:
r = {"subject": "", "body": "", "err": str(e)}
out.append({
diff --git a/server.js b/server.js
index ce00489..39a2a6a 100644
--- a/server.js
+++ b/server.js
@@ -102,9 +102,19 @@ function draftEmail(req, res) {
const ctx = (b.context || "").slice(0, 2500);
const sender = b.sender || "Steve Bloom, RENTV";
const tone = b.tone || "warm, professional, concise";
+ // Name the RIGHT RENTV property. b.show may be passed explicitly by the client;
+ // otherwise infer from the episode titles in the context (the SoundCloud podcast
+ // episodes are titled "…Weekly CRE News & Views…").
+ const pod = /weekly cre news/i.test(ctx);
+ const vim = /cre talk/i.test(ctx) || !pod;
+ const show = b.show || (pod && vim
+ ? `RENTV's "Commercial Real Estate Talk" show and the "Weekly CRE News & Views" podcast`
+ : pod
+ ? `RENTV's "Weekly CRE News & Views" podcast (hosted by Steve Bloom)`
+ : `RENTV's "Commercial Real Estate Talk" show`);
const goal = kind === "guest"
? `Write a follow-up email to a past RENTV "CRE Talk" guest, thanking them for appearing and proposing a next touchpoint (a follow-up episode, a panel invite, or sharing the episode). ${name} is the guest.`
- : `Write a business-development email from RENTV to ${name}, a company that has advertised/sponsored RENTV's "CRE Talk" show, to renew or expand their advertising and thank them for their partnership. Reference their past sponsorship naturally.`;
+ : `Write a business-development email from RENTV to ${name}, a company that has advertised/sponsored ${show}, to renew or expand their advertising and thank them for their partnership. Reference their past sponsorship naturally, and do NOT name a show they did not sponsor.`;
const prompt = `${goal}
Tone: ${tone}. Sign as ${sender}. Keep it under 180 words. Do not invent facts not in the context.
CONTEXT (their extracted profile + episodes):
← 290f01a regenerate engagement email drafts incl podcast sponsors (Ca
·
back to Rentv Ad Engine
·
engagement emails: name the RIGHT RENTV property per sponsor 74ce7ce →