Site Builder
Editing:
tools-writing-blogs111.php
writable 0666
<?php /***************************************************************** * Promptinator – Article Generator (v2.1 / Toolbox skin) *****************************************************************/ declare(strict_types=1); /* ---------- 1. Constants & helpers ---------- */ const COOKIE_NAME = 'openai_key'; const COOKIE_TTL = 30 * 24 * 3600; // 30 days const SECRET = __FILE__; $host = $_SERVER['HTTP_HOST'] ?? ''; $cookieDomain = (!filter_var($host, FILTER_VALIDATE_IP) && preg_match('/([a-z0-9-]+\.[a-z]{2,})$/i', $host, $m)) ? '.'.$m[1] : ''; function enc(string $s): string { $m='aes-128-ctr'; $k=substr(hash('sha256',SECRET,true),0,16); $iv=random_bytes(openssl_cipher_iv_length($m)); return base64_encode(openssl_encrypt($s,$m,$k,0,$iv).'::'.$iv); } function dec(string $c): string { [$ct,$iv]=explode('::',base64_decode($c),2)+[null,null]; if(!$ct||!$iv) return ''; $m='aes-128-ctr'; $k=substr(hash('sha256',SECRET,true),0,16); return openssl_decrypt($ct,$m,$k,0,$iv)?:''; } /* ---------- 2. Key save / delete (regular request) ---------- */ if ($_SERVER['REQUEST_METHOD']==='POST' && isset($_POST['save_key'])) { $raw=trim($_POST['api_key']); $ctx=stream_context_create(['http'=>['method'=>'GET','header'=>"Authorization: Bearer $raw\r\n",'timeout'=>8]]); if (@file_get_contents('https://api.openai.com/v1/models',false,$ctx)) { setcookie(COOKIE_NAME,enc($raw),time()+COOKIE_TTL,'/',$cookieDomain,isset($_SERVER['HTTPS']),true); header('Location: '.$_SERVER['REQUEST_URI']); exit; } http_response_code(400); echo 'Invalid key'; exit; } if (isset($_GET['logout'])) { setcookie(COOKIE_NAME,'',time()-3600,'/',$cookieDomain,isset($_SERVER['HTTPS']),true); header('Location: '.$_SERVER['PHP_SELF']); exit; } /* ---------- 3. AJAX endpoint -------------------------------- */ if ($_SERVER['REQUEST_METHOD']==='POST' && ($_GET['ajax']??'')==='1') { header('Content-Type: application/json; charset=utf-8'); $F = fn($k,$d='') => trim($_POST[$k] ?? $d); $topic = $F('topic'); if($topic===''){echo json_encode(['error'=>'Topic required']);exit;} $model = $F('model','gpt-3.5-turbo'); $keywords = $F('keywords'); $linkurl = $F('linkurl'); $sections = max(1,(int)$F('sections',3)); $paras = max(1,(int)$F('paras',3)); $lang = $F('lang','English'); $style = $F('style','Creative'); $tone = $F('tone','Neutral'); $keyCookie = $_COOKIE[COOKIE_NAME] ?? ''; $key = $keyCookie ? dec($keyCookie) : ''; if(!$key){echo json_encode(['error'=>'Missing API key']);exit;} $prompt = "Write a $lang $style article in a $tone tone about «$topic». ". "Use $sections sections (each <h2>) and $paras paragraph".($paras>1?'s':'')." per section. ". ($keywords ? "Include keywords: $keywords. " : ''). ($keywords && $linkurl ? "Hyperlink each keyword to $linkurl. " : ''). 'Append <p class="excerpt">Excerpt: …</p>. Return ONLY HTML.'; $payload=[ 'model'=>$model, 'messages'=>[['role'=>'user','content'=>$prompt]], 'max_tokens'=>max(800,$sections*$paras*200), 'temperature'=>0.7 ]; $ch=curl_init('https://api.openai.com/v1/chat/completions'); curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>1, CURLOPT_HTTPHEADER=>['Authorization: Bearer '.$key,'Content-Type: application/json'], CURLOPT_POST=>1,CURLOPT_POSTFIELDS=>json_encode($payload),CURLOPT_TIMEOUT=>60]); $resp=curl_exec($ch); $err=curl_error($ch); curl_close($ch); if($err||!$resp){echo json_encode(['error'=>'Network error: '.$err]);exit;} $html = json_decode($resp,true)['choices'][0]['message']['content'] ?? ''; if(!$html){echo json_encode(['error'=>'OpenAI returned empty content']);exit;} echo json_encode(['html'=>$html,'prompt'=>$prompt]); exit; } /* ---------- 4. Render page ---------------------------------- */ $loggedIn = isset($_COOKIE[COOKIE_NAME]) && dec($_COOKIE[COOKIE_NAME]); $models = ['gpt-3.5-turbo','gpt-4o-mini','gpt-4']; ?><!doctype html><html lang="en"><head> <meta charset="utf-8"><title>Article Generator • Promptinator</title> <meta name=viewport content="width=device-width,initial-scale=1"> <link rel=stylesheet href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"> <style> :root{--blue:#0066ff;--blue-d:#0250e0;--brand:#0f3760;--bg:#f4f7fd;--radius:.7rem} body{margin:0;background:var(--bg);font-family:system-ui,Roboto,Arial,sans-serif} header{background:#eef1f8;padding:.8rem 1.2rem;font-weight:700;display:flex;justify-content:space-between;align-items:center} header nav a{color:var(--brand);text-decoration:none;margin-right:.4rem;font-size:.92rem} header nav a:last-child{margin:0} .container{max-width:960px;margin:2rem auto;padding:0 1rem} #topbar{display:flex;gap:1rem;flex-wrap:wrap;align-items:center;margin-bottom:1.6rem} #topbar input{padding:.5rem .75rem;border:1px solid #ccd3e5;border-radius:6px;width:260px;max-width:100%} #topbar select{padding:.45rem .6rem;border-radius:6px} .badge{display:flex;align-items:center;gap:.4ch;font-size:.9rem} .badge.green{color:#15a151}.badge button{border:none;background:none;font-size:1rem;cursor:pointer;color:inherit} button.primary{background:var(--blue);color:#fff;border:none;border-radius:6px;padding:.6rem 1.3rem;font-weight:600;cursor:pointer} button.primary:hover{background:var(--blue-d)} form.grid{display:grid;gap:1.2rem} @media(min-width:720px){form.grid{grid-template-columns:repeat(2,1fr)}} @media(min-width:1000px){form.grid{grid-template-columns:repeat(3,1fr)}} form.grid label{display:flex;flex-direction:column;font-size:.92rem} form.grid input,form.grid select,form.grid textarea{border-radius:6px} #prompt{min-height:90px;resize:vertical;font-family:"Fira Code",monospace} #spinner{display:none;gap:.6ch;align-items:center} #spinner svg{width:18px;height:18px;animation:spin 1s linear infinite} @keyframes spin{to{transform:rotate(360deg)}} .copyBtn{margin-top:.4rem} .preview{background:#fff;border:1px solid #e2e6f0;border-radius:8px;padding:1rem;max-height:60vh;overflow:auto} @media(max-width:540px){header nav{font-size:.82rem}} </style> </head><body> <header> <nav> <a href="/">BestDealOn</a> » <a href="/members/dashboard.php">Dashboard</a> » <a href="/ai-tools/tools.php">AI Toolbox</a> » Article Generator </nav> <a href="/<?=htmlspecialchars($_SESSION['slug']??'')?>" style="text-decoration:none">View Site</a> </header> <div class="container"> <!-- ───── top bar ───── --> <div id="topbar"> <?php if(!$loggedIn): ?> <form method="post" style="display:flex;gap:.6rem;flex-wrap:wrap"> <input type="password" name="api_key" placeholder="OpenAI key …" required> <button class="primary" name="save_key">Save</button> <a href="https://platform.openai.com/api-keys" target="_blank" style="font-size:.8rem">get key</a> </form> <?php else: ?> <span class="badge green">● key saved <button title="Remove" onclick="location='?logout=1'">✖</button></span> <select id="modelSel"><?php foreach($models as $m):?><option><?=$m?></option><?php endforeach;?></select> <?php endif;?> </div> <?php if(!$loggedIn): ?> <article class="preview"><h2>Get started</h2><p>Save your API key above to unlock Promptinator.</p></article> <?php else: ?> <!-- ───── generator form ───── --> <form class="grid" id="genForm"> <label>Topic <div style="position:relative"> <input name="topic" id="topic" placeholder="e.g. Electric bicycles" required> <button type="button" id="mic" style="position:absolute;right:.5rem;top:50%;transform:translateY(-50%);border:none;background:none;font-size:1.2rem">🎤</button> </div> </label> <label>Keywords (comma‑separated) <input name="keywords" id="keywords"> </label> <label>Link URL (optional) <input name="linkurl" id="linkurl" type="url"> </label> <label># Sections <select name="sections" id="sections"><?php for($i=1;$i<=8;$i++):?><option><?=$i?></option><?php endfor;?></select> </label> <label># Paragraphs / section <select name="paras" id="paras"><?php for($i=1;$i<=6;$i++):?><option><?=$i?></option><?php endfor;?></select> </label> <label>Language <select name="lang" id="lang"><?php foreach(['English','Spanish','German','French','Italian','Portuguese','Dutch'] as $l):?><option><?=$l?></option><?php endforeach;?></select> </label> <label>Style <select name="style" id="style"><?php foreach(['Creative','Informative','Narrative','Persuasive','Analytical','Journalistic'] as $s):?><option><?=$s?></option><?php endforeach;?></select> </label> <label>Tone <select name="tone" id="tone"><?php foreach(['Neutral','Cheerful','Humorous','Assertive','Inspirational','Professional','Emotional'] as $t):?><option><?=$t?></option><?php endforeach;?></select> </label> <!-- Prompt box always spans full width --> <label style="grid-column:1/-1">Prompt preview <textarea id="prompt" readonly></textarea> </label> <button type="button" class="primary copyBtn" id="copyPrompt" style="display:none">Copy prompt</button> <!-- Generate button --> <button type="submit" class="primary" id="generate" style="grid-column:1/-1;max-width:200px">Generate article</button> <div id="spinner" style="grid-column:1/-1"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle></svg> <span id="sec">0</span>s</div> </form> <!-- Results --> <section id="results" style="display:none"> <h3>HTML source</h3> <textarea id="raw" readonly style="min-height:170px;font-family:'Fira Code',monospace"></textarea> <button type="button" class="primary copyBtn" id="copyRaw">Copy HTML</button> <h3>Rendered preview</h3> <div class="preview" id="render"></div> <button type="button" class="primary copyBtn" id="copyRender">Copy rendered</button> </section> <?php endif; ?> </div><!-- /.container --> <script> (()=>{ /* ===== speech mic ===== */ const mic=document.getElementById('mic'); if(mic){ const SR=window.SpeechRecognition||window.webkitSpeechRecognition; if(SR){ const rec=new SR(); rec.lang='en-US'; rec.maxAlternatives=1; mic.onclick=()=>{try{rec.start();}catch{}}; rec.onresult=e=>{document.getElementById('topic').value=e.results[0][0].transcript;build();}; }else{mic.style.display='none';} } /* ===== live prompt build ===== */ const f=document.getElementById('genForm'), promptBox=document.getElementById('prompt'), copyPrompt=document.getElementById('copyPrompt'), modelSel=document.getElementById('modelSel'); function build(){ const v=n=>f.elements[n].value.trim(); if(!v('topic')){promptBox.value='';copyPrompt.style.display='none';return;} promptBox.value=`Write a ${v('lang')} ${v('style')} article in a ${v('tone')} tone about the topic for ${v('topic')}. ` +`Use ${v('sections')} sections (each <h2>) and ${v('paras')} paragraph${v('paras')>1?'s':''} per section. ` +(v('keywords')?`Include keywords: ${v('keywords')}. `:'') +(v('keywords')&&v('linkurl')?`Hyperlink each keyword to ${v('linkurl')}. `:'') +'Append <p class="excerpt">Excerpt: …</p>. Return ONLY HTML.'; copyPrompt.style.display=''; } if(f) f.querySelectorAll('input,select').forEach(el=>el.addEventListener('input',build)); build(); if(copyPrompt) copyPrompt.onclick=()=>navigator.clipboard.writeText(promptBox.value).then(()=>flash(copyPrompt)); /* ===== AJAX generate ===== */ if(f){ const spin=document.getElementById('spinner'), sec=document.getElementById('sec'), raw=document.getElementById('raw'), rend=document.getElementById('render'), res=document.getElementById('results'), copyRaw=document.getElementById('copyRaw'), copyRender=document.getElementById('copyRender'); let timer; f.onsubmit=e=>{ e.preventDefault(); const fd=new FormData(f); fd.append('model',modelSel.value); spin.style.display='flex'; let s=0; sec.textContent='0'; timer=setInterval(()=>{sec.textContent=++s},1000); fetch('?ajax=1',{method:'POST',body:fd}) .then(r=>r.json()).then(j=>{ clearInterval(timer); spin.style.display='none'; if(j.error){alert(j.error);return;} raw.value=j.html; rend.innerHTML=j.html; res.style.display=''; [copyRaw,copyRender].forEach(b=>b.onclick=()=>navigator.clipboard.writeText( b===copyRaw?raw.value:rend.innerHTML).then(()=>flash(b))); rend.scrollIntoView({behavior:'smooth'}); }).catch(err=>{clearInterval(timer);spin.style.display='none';alert(err);}); }; } /* ===== small helpers ===== */ function flash(btn){const t=btn.textContent;btn.textContent='Copied';setTimeout(()=>btn.textContent=t,1200);} })(); </script> </body></html>
Save changes
Create folder
writable 0777
Create
Cancel