Traffic Desk: AI-written text ads alongside banners
Text placements out-perform banners on this network — 357 text ads average ~171 clicks each against ~150 for banners, on far less inventory — so members can now run them too. The format is measured, not guessed. Across 60 live text ads: Subject caps at 20 characters and Body is ALWAYS exactly three <br>-joined lines of at most 24 characters. Every ad in the sample obeyed it. Emoji: the NAS columns are latin1, but so is the connection, so UTF-8 bytes pass through and render — which is how the existing emoji ads got there. The bridge's clean() would have deleted every emoji via iconv //TRANSLIT//IGNORE, so text copy now uses clean_ad_text(), which preserves them and strips angle brackets instead. The bridge builds the <br> separators itself from a lines[] array, so a member can never inject markup. The engine writes plain text to a tight budget and we apply emoji ourselves: models cannot count characters, least of all with emoji, so decoration is arithmetic we control. It also lets the emoji rotate independently of the copy. The palette carries no money emoji — the network is full of them, but implying earnings outranks matching the neighbours. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ const suiteTools = require('./suite-tools');
|
||||
const suiteEmail = require('./suite-email');
|
||||
const suiteVideo = require('./suite-video'); suiteVideo.init({ dataDir: DATA_DIR, publicDir: PUBLIC_DIR });
|
||||
const suiteTraffic = require('./suite-traffic'); suiteTraffic.init({ dataDir: DATA_DIR });
|
||||
const suiteTextAds = require('./suite-textads');
|
||||
const tgbot = require('./tgbot');
|
||||
tgbot.init({ dataDir: DATA_DIR, chain, getConfig, messages, baseUrl: 'https://rmcircle.team' });
|
||||
const SESSION_TTL = 8 * 60 * 60 * 1000;
|
||||
@@ -784,7 +785,8 @@ async function handleApi(req,res,pathname){
|
||||
// offered as an ad destination before it's been built.
|
||||
const pg=suitePages.load(e.d.id);
|
||||
const hasPage=!!(pg&&pg.copy);
|
||||
return json(res,200,{level:e.d.level,id:e.d.id,configured:suiteTraffic.configured(),status:st,live:live,hasPage:hasPage});
|
||||
return json(res,200,{level:e.d.level,id:e.d.id,configured:suiteTraffic.configured(),status:st,live:live,hasPage:hasPage,
|
||||
textAngles:suiteTextAds.angles(),textMeter:suiteMeter.check(e.d.id,e.d.level,'textad'),writerReady:suiteAI.configured()});
|
||||
}
|
||||
if(req.method==='POST'&&pathname==='/api/public/suite-traffic'){
|
||||
const e=await suiteEntitlement(req).catch(()=>({error:'Chain read hiccup — try again.',code:500}));
|
||||
@@ -796,6 +798,8 @@ async function handleApi(req,res,pathname){
|
||||
const pgRec=suitePages.load(e.d.id);
|
||||
const entry=await suiteTraffic.launch({
|
||||
id:e.d.id, level:e.d.level, size:b.size, creative:b.creative,
|
||||
kind:b.kind==='text'?'text':'banner',
|
||||
subject:b.subject, lines:b.lines,
|
||||
impressions:b.impressions, target:b.target, angle:b.angle,
|
||||
hasPage:!!(pgRec&&pgRec.copy),
|
||||
name:String(b.name||'').slice(0,60)
|
||||
@@ -804,6 +808,23 @@ async function handleApi(req,res,pathname){
|
||||
}catch(err){ return json(res,400,{error:String(err.message||err)}); }
|
||||
}
|
||||
|
||||
if(req.method==='POST'&&pathname==='/api/public/suite-textads'){
|
||||
const e=await suiteEntitlement(req).catch(()=>({error:'Chain read hiccup — try again.',code:500}));
|
||||
if(e.error)return json(res,e.code||500,{error:e.error});
|
||||
if(!e.inOrg||!e.allowed)return json(res,403,{error:'The Circle Suite is not open for this position yet.'});
|
||||
if(!suiteAI.configured())return json(res,503,{error:'The writer is warming up — try again shortly.'});
|
||||
const b=await bodyJson(req)||{};
|
||||
const gate=suiteMeter.check(e.d.id,e.d.level,'textad');
|
||||
if(!gate.allowed){
|
||||
return json(res,429,{error:'You have used all '+gate.limit+' text-ad batches for this month. It resets on the 1st — or a level upgrade raises your allowance.',meter:gate});
|
||||
}
|
||||
try{
|
||||
const r=await suiteTextAds.generate({angle:String(b.angle||'general'),count:5});
|
||||
suiteMeter.record(e.d.id,'textad',1);
|
||||
return json(res,200,{angle:r.angle,variants:r.variants,meter:suiteMeter.check(e.d.id,e.d.level,'textad')});
|
||||
}catch(err){ return json(res,502,{error:String(err.message||err)}); }
|
||||
}
|
||||
|
||||
if(req.method==='POST'&&pathname==='/api/public/suite-traffic-stop'){
|
||||
const e=await suiteEntitlement(req).catch(()=>({error:'Chain read hiccup — try again.',code:500}));
|
||||
if(e.error)return json(res,e.code||500,{error:e.error});
|
||||
|
||||
Reference in New Issue
Block a user