mirror of
https://github.com/PR0M3TH3AN/VoxVera.git
synced 2026-10-27 09:40:40 +00:00
feat: Add voxvera manage command and convert html generator to static site rendering
This commit is contained in:
1 parent
4e6e935acb
commit
7c0d6b041c
43 files changed
+188
-1176
No files matched your search
@@ -8,7 +8,7 @@ Generate printable flyers with QR codes linking to Tor (.onion) hidden services,
|
||||
|
||||
* **Interactive setup**: `voxvera init` prompts for metadata or extracts from a PDF form. When editing body text, a small Tkinter GUI window opens with existing content pre-filled, falling back to the user's `$EDITOR` if the GUI isn't available.
|
||||
* **Template support**: `voxvera init --template <name>` copies built-in templates (`blank`, `voxvera`).
|
||||
* **Build assets**: `voxvera build [--pdf <path>] [--download <file.zip>]` generates HTML, minified JS/CSS, QR codes, and bundles PDFs.
|
||||
* **Build assets**: `voxvera build [--download <file.zip>]` generates HTML, minified JS/CSS, QR codes, and optionally bundles a download zip.
|
||||
* **Batch import**: `voxvera import` processes all JSON configs in `imports/`.
|
||||
* **Automatic Tor hosting**: `voxvera serve` publishes via Tor/OnionShare. The .onion address is automatically derived from Tor keys and written into the flyer's tear-off links. Tor ports are auto-detected -- no manual configuration needed.
|
||||
* **All-in-one**: `voxvera quickstart` runs init, build, and serve in sequence.
|
||||
@@ -73,7 +73,7 @@ voxvera quickstart
|
||||
voxvera init
|
||||
|
||||
# 2. Build flyer assets (HTML, QR codes, PDFs)
|
||||
voxvera build --pdf form.pdf --download file.zip # optional flags
|
||||
voxvera build --download file.zip # optional flags
|
||||
|
||||
# 3. Host via Tor (auto-detects ports, generates .onion address)
|
||||
voxvera serve
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ host_dir="voxvera/host/$subdomain"
|
||||
ls -R "$host_dir" >>"$LOG_DIR/tree.txt"
|
||||
|
||||
# Verify essential files
|
||||
for f in index.html config.json nostr.html; do
|
||||
for f in index.html config.json; do
|
||||
[ -f "$host_dir/$f" ] || { echo "Missing $f in $host_dir" >&2; exit 1; }
|
||||
done
|
||||
echo "Build output verified in $host_dir"
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"name": "Vox Vera Printable Flyers",
|
||||
"subdomain": "voxvera",
|
||||
"title": "TOP SECRET",
|
||||
"subtitle": "DO <span class=\\\"redacted\\\">NOT</span> DISTRIBUTE",
|
||||
"headline": "OPERATION VOX VERA",
|
||||
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.\\n\\nUse memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.\\n\\nPrivacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.\\n\\nJoin us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.\n",
|
||||
"url_message": "Follow this link to learn more. Use Tor Browser.",
|
||||
"url": "https://voxvera.org/",
|
||||
"tear_off_link": "https://voxvera.org/",
|
||||
"binary_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=1024,initial-scale=1,maximum-scale=1,user-scalable=no"><title id="site-title">Vox Vera Printable Flyers</title><style>body{font-family:'Courier New',Courier,monospace;margin:0;padding:0;display:flex;justify-content:center;align-items:center;flex-direction:column;height:100vh;box-sizing:border-box;overflow:hidden;background-color:#fff}a{color:#b80000;text-decoration:none}a:hover{text-decoration:underline}.wrapper{display:flex;justify-content:center;align-items:center;flex-direction:column;height:100%;width:100%}.container{display:flex;flex-direction:row;height:11in;width:8.5in;border:1px solid #000;box-sizing:border-box;overflow:hidden;page-break-inside:avoid}.print-button-container{margin-top:20px}.print-button{padding:10px 20px;font-size:14px;cursor:pointer;background-color:#000;color:#fff;border:none;border-radius:0}.print-button:hover{background-color:#444}.left-tear-offs{width:3.75in;padding:8px;border-right:1px dashed #000;display:flex;flex-direction:column;justify-content:space-between;gap:5px;box-sizing:border-box}.tear-off{display:flex;padding:5px;border:1px dashed #000;font-size:10px;box-sizing:border-box;page-break-inside:avoid;align-items:flex-start}.tear-off-text{flex:1;min-width:0;word-wrap:break-word;overflow-wrap:break-word;word-break:break-all}.tear-off a{color:#b80000;text-decoration:none}.tear-off a:hover{text-decoration:underline}.qr-code{width:80px;height:80px;border:0 solid #000;margin-left:10px;flex-shrink:0}.qr-code img{width:100%;height:100%;object-fit:contain}.content{width:calc(8.5in - 3.75in);padding:20px;text-align:center;box-sizing:border-box;display:flex;flex-direction:column;justify-content:space-between;page-break-inside:avoid;overflow-wrap:break-word}h1{margin:0;margin-bottom:10px;font-size:24px;letter-spacing:2px}.distribute{font-size:18px;letter-spacing:6px}.redacted{text-decoration:line-through}.message{flex:1;margin-top:10px;font-size:14px;line-height:1.4;text-align:left;overflow-wrap:break-word;display:flex;flex-direction:column;justify-content:flex-end;white-space:pre-wrap;box-sizing:border-box;position:relative}.text-container{flex:1;width:100%;height:100%;border:none;outline:0;font-family:'Courier New',Courier,monospace;font-size:14px;line-height:1.4;overflow:hidden;box-sizing:border-box}.qr-code-body{width:100%;height:auto;display:flex;justify-content:space-between;align-items:center;margin-top:10px;padding:0;box-sizing:border-box}.qr-code-body img{width:80px;height:80px;object-fit:contain;flex-shrink:0}.qr-code-url{font-size:12px;text-align:left;word-wrap:break-word;overflow-wrap:break-word;hyphens:auto;flex-grow:1;margin-right:10px;max-width:60%}hr{border:none;border-top:1px solid #000;width:100%;margin:10px 0}.footer{font-size:10px;overflow-wrap:break-word;text-align:center;align-self:center;width:100%;padding:10px 0;box-sizing:border-box;border-top:1px solid #000}.footer p{margin:0;padding:0}.footer .binary{margin-bottom:5px;margin-top:20px}@media print{body,html{width:8.5in;height:11in;margin:0;padding:0}.container{box-shadow:none;border:none;page-break-inside:avoid;margin:0;padding:0;width:8.5in;height:11in}.print-button{display:none}body{transform:none}}.print-button{margin-top:20px;padding:10px 20px;font-size:14px;cursor:pointer;background-color:#000;color:#fff;border:none;border-radius:0}.print-button:hover{background-color:#444}</style></head><body><div class="container"><div class="left-tear-offs"><div class="tear-off"><div class="tear-off-text">Use Tor Browser<br><br>to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br>click the button below to print</div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text">Use Tor Browser<br><br>to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br>click the button below to print</div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text">Use Tor Browser<br><br>to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br>click the button below to print</div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text">Use Tor Browser<br><br>to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br>click the button below to print</div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text">Use Tor Browser<br><br>to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br>click the button below to print</div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text">Use Tor Browser<br><br>to help spread this message go to<Line truncated
|
||||
@@ -1 +0,0 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Connect with Me on KeyChat</title><style>body{font-family:'Courier New',Courier,monospace;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;margin:0;background-color:#fff}.container{background-color:#fff;padding:20px;border:1px solid #000;text-align:center;max-width:400px;width:90%;box-shadow:0 0 10px rgba(0,0,0,.1)}h1{color:#333}p{font-size:16px;color:#666}.code-box{background-color:#fff;border:1px solid #000;padding:10px;margin:20px 0;font-size:18px;color:#333;word-wrap:break-word;word-break:break-word;overflow-wrap:break-word;cursor:pointer;user-select:none}.copy-btn{background-color:#000;color:#fff;padding:10px 20px;border:1px solid #000;cursor:pointer;font-size:16px;transition:background-color .3s ease}.copy-btn:hover{background-color:#3d3d3d}footer{margin-top:20px;font-size:14px;color:#999}a{color:#a30000;text-decoration:none}a:hover{text-decoration:underline}</style></head><body><div class="container"><h1>Nostr</h1><p>Use a secure file shareing tool like <a href="https://onionshare.org/" target="_blank">Onion Share</a> or <a href="https://wormhole.app/" target="_blank">WormHole</a> to send your completed <a href="submission_form.pdf" target="_blank">submission form PDF</a> to the following <a href="https://www.0xchat.com/#/" target="_blank">Nostr</a> npub account:</p><p></p><a href="index.html">Back to Home</a><p></p><div class="code-box" id="keyChatCode">npub1ln8efl52vsyh6lg59c9v3kut56wev489lzcma0sv2mf8nm6jhwjqeteygt</div><button class="copy-btn" onclick="copyCode()">Copy Code</button></div><footer><p>Not on Nostr yet? <a href="https://nostr.how/en/what-is-nostr" target="_blank">Learn more here</a>.</p></footer><script>function a0_0x55b4(){var _0xeeb36b=['Failed\x20to\x20copy\x20code:\x20','36973gPfEDk','3hjGEPG','keyChatCode','clipboard','2471RXzkOy','2005220SDqWBz','then','8464mvqASw','24965hDeypK','468jCVegU','3143720lhnwYU','innerText','Code\x20copied\x20to\x20clipboard!','2722248cDzJJD','9411952bWAcnc','2gKXLTl'];a0_0x55b4=function(){return _0xeeb36b;};return a0_0x55b4();}function a0_0x2f09(_0x591295,_0x18ad6e){var _0x55b435=a0_0x55b4();return a0_0x2f09=function(_0x2f098f,_0x3b879c){_0x2f098f=_0x2f098f-0x103;var _0x38cd8a=_0x55b435[_0x2f098f];return _0x38cd8a;},a0_0x2f09(_0x591295,_0x18ad6e);}(function(_0x460aa5,_0xdd901b){var _0x26f888=a0_0x2f09,_0x58db54=_0x460aa5();while(!![]){try{var _0x40318b=parseInt(_0x26f888(0x10d))/0x1*(parseInt(_0x26f888(0x10b))/0x2)+-parseInt(_0x26f888(0x10e))/0x3*(-parseInt(_0x26f888(0x112))/0x4)+parseInt(_0x26f888(0x104))/0x5*(parseInt(_0x26f888(0x105))/0x6)+parseInt(_0x26f888(0x111))/0x7*(-parseInt(_0x26f888(0x103))/0x8)+parseInt(_0x26f888(0x109))/0x9+parseInt(_0x26f888(0x106))/0xa+-parseInt(_0x26f888(0x10a))/0xb;if(_0x40318b===_0xdd901b)break;else _0x58db54['push'](_0x58db54['shift']());}catch(_0x5c0367){_0x58db54['push'](_0x58db54['shift']());}}}(a0_0x55b4,0x4d04e));function copyCode(){var _0x263cfe=a0_0x2f09,_0x201a80=document['getElementById'](_0x263cfe(0x10f))[_0x263cfe(0x107)];navigator[_0x263cfe(0x110)]['writeText'](_0x201a80)[_0x263cfe(0x113)](function(){var _0x2c37ce=_0x263cfe;alert(_0x2c37ce(0x108));},function(_0x3011e9){var _0x5238cb=_0x263cfe;alert(_0x5238cb(0x10c),_0x3011e9);});}</script></body></html>
|
||||
@@ -26,7 +26,6 @@ dependencies = [
|
||||
"Pillow",
|
||||
"jsmin",
|
||||
"htmlmin2",
|
||||
"pypdf",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
|
||||
Binary file not shown.
+8
-1
@@ -285,4 +285,11 @@
|
||||
padding: 20px 0 30px;
|
||||
}
|
||||
|
||||
</style></head><body><div class="container"><div class="left-tear-offs"><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div></div><div class="content"><h1 id="top-secret-title">Loading...</h1> <div class="distribute" id="do-not-distribute">Loading...</div> <h1 id="operation-title-2"><br>Loading...</h1> <hr><div class="message"><div class="text-container" id="main-content" contenteditable="false"> Loading content... </div><div class="qr-code-body"><div class="qr-code-url"><span class="url-message">Loading message...</span><br><br> <a id="qr-link-2" href="#">Loading...</a> </div><img src="qrcode-content.png" alt="QR Code"></div></div><hr><div class="footer"><p class="credit">built with <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p><p class="binary" id="binary-message">0110010 0101011 0110010 0111101 0110100</p></div></div></div><div class="button-bar"><button class="print-button" onclick="window.print()">Print this page</button><a class="download-button" href="https://github.com/PR0M3TH3AN/VoxVera/archive/refs/heads/main.zip">Download</a></div><script>fetch('config.json').then(response=>response.json()).then(config=>{document.getElementById('site-title').innerText=config.name;document.getElementById('top-secret-title').innerText=config.title;document.getElementById('do-not-distribute').innerHTML=config.subtitle;document.getElementById('operation-title-2').innerText=config.headline;document.getElementById('main-content').innerText=config.content.replace(/\\n/g,'\n');document.querySelectorAll('.url-message').forEach(msg=>{msg.innerText=config.url_message;});document.getElementById('qr-link-2').innerText=config.url;document.getElementById('qr-link-2').href=config.url;document.getElementById('qr-link-2').target="_blank";document.querySelectorAll('.tear-off-link').forEach(link=>{link.innerText=config.tear_off_link;link.href=config.tear_off_link;link.target="_blank";});document.getElementById('binary-message').innerText=config.binary_message;}).catch(error=>console.error('Error loading config:',error));</script></body></html>
|
||||
</style></head><body><div class="container"><div class="left-tear-offs"><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div></div><div class="content"><h1 id="top-secret-title">TOP SECRET</h1><div class="distribute" id="do-not-distribute">DO <span class="redacted">NOT</span> DISTRIBUTE</div><h1 id="operation-title-2"><br>OPERATION VOX VERA</h1><hr><div class="message"><div class="text-container" id="main-content" contenteditable="false">Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.
|
||||
|
||||
Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.
|
||||
|
||||
Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.
|
||||
|
||||
Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.
|
||||
</div><div class="qr-code-body"><div class="qr-code-url"><span class="url-message">Get VoxVera and start spreading your message.</span><br><br><a id="qr-link-2" href="https://github.com/PR0M3TH3AN/VoxVera" target="_blank">https://github.com/PR0M3TH3AN/VoxVera</a></div><img src="qrcode-content.png" alt="QR Code"></div></div><hr><div class="footer"><p class="credit">built with <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p><p class="binary" id="binary-message"><br><br>0110010 0101011 0110010 0111101 0110100<br></p></div></div></div><div class="button-bar"><button class="print-button" onclick="window.print()">Print this page</button><a class="download-button" href="https://github.com/PR0M3TH3AN/VoxVera/archive/refs/heads/main.zip" download>Download</a></div></body></html>
|
||||
@@ -1,70 +0,0 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Connect with Me on KeyChat</title><style>
|
||||
body {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #000000;
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
h1 {
|
||||
color: #333;
|
||||
}
|
||||
p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
.code-box {
|
||||
background-color: white;
|
||||
border: 1px solid #000000;
|
||||
padding: 10px;
|
||||
margin: 20px 0;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.copy-btn {
|
||||
background-color: #000000;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: 1px solid #000000;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
.copy-btn:hover {
|
||||
background-color: #3d3d3d;
|
||||
}
|
||||
footer {
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.credit {
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
a {
|
||||
color: #a30000;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style></head><body><div class="container"><h1>Nostr</h1><p>Use a secure file shareing tool like <a href="https://onionshare.org/" target="_blank">Onion Share</a> or <a href="https://wormhole.app/" target="_blank">WormHole</a> to send your completed <a href="submission_form.pdf" target="_blank">submission form PDF</a> to the following <a href="https://www.0xchat.com/#/" target="_blank">Nostr</a> npub account:</p><p></p><a href="index.html">Back to Home</a></p><div class="code-box" id="keyChatCode">npub1ln8efl52vsyh6lg59c9v3kut56wev489lzcma0sv2mf8nm6jhwjqeteygt</div><button class="copy-btn" onclick="copyCode()">Copy Code</button></div><footer><p>Not on Nostr yet? <a href="https://nostr.how/en/what-is-nostr" target="_blank">Learn more here</a>.</p><p class="credit">built with <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p></footer><script>function copyCode(){var copyText=document.getElementById("keyChatCode").innerText;navigator.clipboard.writeText(copyText).then(function(){alert("Code copied to clipboard!");},function(err){alert("Failed to copy code: ",err);});}</script></body></html>
|
||||
Binary file not shown.
@@ -285,4 +285,4 @@
|
||||
padding: 20px 0 30px;
|
||||
}
|
||||
|
||||
</style></head><body><div class="container"><div class="left-tear-offs"><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div></div><div class="content"><h1 id="top-secret-title">Loading...</h1> <div class="distribute" id="do-not-distribute">Loading...</div> <h1 id="operation-title-2"><br>Loading...</h1> <hr><div class="message"><div class="text-container" id="main-content" contenteditable="false"> Loading content... </div><div class="qr-code-body"><div class="qr-code-url"><span class="url-message">Loading message...</span><br><br> <a id="qr-link-2" href="#">Loading...</a> </div><img src="qrcode-content.png" alt="QR Code"></div></div><hr><div class="footer"><p class="credit">built with <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p><p class="binary" id="binary-message">0110010 0101011 0110010 0111101 0110100</p></div></div></div><div class="button-bar"><button class="print-button" onclick="window.print()">Print this page</button><a class="download-button" href="download/download.zip" download>Download</a></div><script>fetch('config.json').then(response=>response.json()).then(config=>{document.getElementById('site-title').innerText=config.name;document.getElementById('top-secret-title').innerText=config.title;document.getElementById('do-not-distribute').innerHTML=config.subtitle;document.getElementById('operation-title-2').innerText=config.headline;document.getElementById('main-content').innerText=config.content.replace(/\\n/g,'\n');document.querySelectorAll('.url-message').forEach(msg=>{msg.innerText=config.url_message;});document.getElementById('qr-link-2').innerText=config.url;document.getElementById('qr-link-2').href=config.url;document.getElementById('qr-link-2').target="_blank";document.querySelectorAll('.tear-off-link').forEach(link=>{link.innerText=config.tear_off_link;link.href=config.tear_off_link;link.target="_blank";});document.getElementById('binary-message').innerText=config.binary_message;}).catch(error=>console.error('Error loading config:',error));</script></body></html>
|
||||
</style></head><body><div class="container"><div class="left-tear-offs"><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div></div><div class="content"><h1 id="top-secret-title">Loading...</h1> <div class="distribute" id="do-not-distribute">Loading...</div> <h1 id="operation-title-2"><br>Loading...</h1> <hr><div class="message"><div class="text-container" id="main-content" contenteditable="false"> Loading content... </div><div class="qr-code-body"><div class="qr-code-url"><span class="url-message">Loading message...</span><br><br> <a id="qr-link-2" href="#">Loading...</a> </div><img src="qrcode-content.png" alt="QR Code"></div></div><hr><div class="footer"><p class="credit">built with <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p><p class="binary" id="binary-message"><br><br>Loading...<br> </p></div></div></div><div class="button-bar"><button class="print-button" onclick="window.print()">Print this page</button><a class="download-button" href="download/download.zip" download>Download</a></div><script>fetch('config.json').then(response=>response.json()).then(config=>{document.getElementById('site-title').innerText=config.name;document.getElementById('top-secret-title').innerText=config.title;document.getElementById('do-not-distribute').innerHTML=config.subtitle;document.getElementById('operation-title-2').innerText=config.headline;document.getElementById('main-content').innerText=config.content.replace(/\\n/g,'\n');document.querySelectorAll('.url-message').forEach(msg=>{msg.innerText=config.url_message;});document.getElementById('qr-link-2').innerText=config.url;document.getElementById('qr-link-2').href=config.url;document.getElementById('qr-link-2').target="_blank";document.querySelectorAll('.tear-off-link').forEach(link=>{link.innerText=config.tear_off_link;link.href=config.tear_off_link;link.target="_blank";});document.getElementById('binary-message').innerText=config.binary_message;}).catch(error=>console.error('Error loading config:',error));</script></body></html>
|
||||
+6
-6
@@ -151,11 +151,11 @@ def test_serve_updates_url(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(cli, "require_cmd", lambda c: True)
|
||||
orig_build = cli.build_assets
|
||||
|
||||
def safe_build_assets(cfg, pdf_path=None, download_path=None):
|
||||
def safe_build_assets(cfg, download_path=None):
|
||||
dest = cli.ROOT / "host" / json.load(open(cfg))["subdomain"] / "config.json"
|
||||
if Path(cfg) == dest:
|
||||
return
|
||||
return orig_build(cfg, pdf_path=pdf_path, download_path=download_path)
|
||||
return orig_build(cfg, download_path=download_path)
|
||||
|
||||
monkeypatch.setattr(cli, "build_assets", safe_build_assets)
|
||||
monkeypatch.setattr(time, "sleep", lambda x: None)
|
||||
@@ -199,11 +199,11 @@ def test_quickstart_noninteractive(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(cli, "require_cmd", lambda c: True)
|
||||
orig_build = cli.build_assets
|
||||
|
||||
def safe_build_assets(cfg, pdf_path=None, download_path=None):
|
||||
def safe_build_assets(cfg, download_path=None):
|
||||
dest = cli.ROOT / "host" / json.load(open(cfg))["subdomain"] / "config.json"
|
||||
if Path(cfg) == dest:
|
||||
return
|
||||
return orig_build(cfg, pdf_path=pdf_path, download_path=download_path)
|
||||
return orig_build(cfg, download_path=download_path)
|
||||
|
||||
monkeypatch.setattr(cli, "build_assets", safe_build_assets)
|
||||
monkeypatch.setattr(time, "sleep", lambda x: None)
|
||||
@@ -259,7 +259,7 @@ def test_get_tor_ports_auto_detect_defaults(monkeypatch):
|
||||
|
||||
def test_build_without_url_skips_qr(tmp_path, monkeypatch, capsys):
|
||||
"""Test that build works even without URLs configured initially."""
|
||||
repo_root = _setup_tmp(monkeypatch, tmp_path)
|
||||
_setup_tmp(monkeypatch, tmp_path)
|
||||
|
||||
# Remove URLs from config
|
||||
config = json.load(open(tmp_path / "src" / "config.json"))
|
||||
@@ -288,7 +288,7 @@ def test_build_without_url_skips_qr(tmp_path, monkeypatch, capsys):
|
||||
|
||||
def test_build_with_only_url(tmp_path, monkeypatch):
|
||||
"""Test that QR codes work when only url (content link) is set."""
|
||||
repo_root = _setup_tmp(monkeypatch, tmp_path)
|
||||
_setup_tmp(monkeypatch, tmp_path)
|
||||
|
||||
# Set only url (content link)
|
||||
config = json.load(open(tmp_path / "src" / "config.json"))
|
||||
|
||||
@@ -345,11 +345,8 @@ class TestSiteFiles:
|
||||
# Check all expected files exist
|
||||
expected_files = [
|
||||
"index.html",
|
||||
"nostr.html",
|
||||
"config.json",
|
||||
"qrcode-content.png",
|
||||
"example.pdf",
|
||||
"submission_form.pdf",
|
||||
]
|
||||
|
||||
for filename in expected_files:
|
||||
|
||||
+1
-98
@@ -52,8 +52,7 @@ def generate_qr(config_path: Path, output_dir: Path) -> None:
|
||||
def obfuscate_html(input_file: Path, output_file: Path) -> None:
|
||||
"""Minify JS inside <script> tags and minify the overall HTML.
|
||||
|
||||
Replaces obfuscate_index.sh / obfuscate_nostr.sh
|
||||
(javascript-obfuscator + html-minifier-terser + node).
|
||||
Replaces obfuscate_index.sh (javascript-obfuscator + html-minifier-terser + node).
|
||||
"""
|
||||
if not input_file.exists():
|
||||
raise FileNotFoundError(f"Input file {input_file} does not exist.")
|
||||
@@ -72,99 +71,3 @@ def obfuscate_html(input_file: Path, output_file: Path) -> None:
|
||||
remove_optional_attribute_quotes=False,
|
||||
)
|
||||
output_file.write_text(html, encoding="utf-8")
|
||||
|
||||
|
||||
def _normalize_content(raw: str) -> str:
|
||||
"""Join soft-wrapped lines while preserving paragraph breaks.
|
||||
|
||||
Replicates the awk logic from extract_form_fields.sh.
|
||||
"""
|
||||
lines = raw.splitlines()
|
||||
paragraphs: list[str] = []
|
||||
current = ""
|
||||
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
if not stripped:
|
||||
if current:
|
||||
paragraphs.append(current)
|
||||
current = ""
|
||||
continue
|
||||
|
||||
if not current:
|
||||
current = stripped
|
||||
elif re.search(r"[.?!]$", current) and stripped[0:1].isupper():
|
||||
current += " " + stripped
|
||||
elif not re.search(r"[.?!]$", current):
|
||||
current += " " + stripped
|
||||
else:
|
||||
paragraphs.append(current)
|
||||
current = stripped
|
||||
|
||||
if current:
|
||||
paragraphs.append(current)
|
||||
|
||||
return "\n\n".join(paragraphs)
|
||||
|
||||
|
||||
def extract_form_fields(pdf_path: Path, config_path: Path) -> None:
|
||||
"""Extract form data from a filled PDF and update config.json.
|
||||
|
||||
Replaces extract_form_fields.sh (pdftotext + jq + grep/awk/sed).
|
||||
"""
|
||||
from pypdf import PdfReader
|
||||
|
||||
reader = PdfReader(str(pdf_path))
|
||||
text = "\n".join(
|
||||
page.extract_text(extraction_mode="layout") or "" for page in reader.pages
|
||||
)
|
||||
|
||||
def _extract_field(label: str, lines_after: int = 1) -> str:
|
||||
pattern = re.compile(re.escape(label), re.IGNORECASE)
|
||||
text_lines = text.splitlines()
|
||||
for i, line in enumerate(text_lines):
|
||||
if pattern.search(line):
|
||||
target = (
|
||||
text_lines[i + lines_after]
|
||||
if i + lines_after < len(text_lines)
|
||||
else ""
|
||||
)
|
||||
return target.strip()
|
||||
return ""
|
||||
|
||||
fields = {
|
||||
"name": _extract_field("name (max 25 characters):", lines_after=3),
|
||||
"subdomain": _extract_field("subdomain (max 30 characters):"),
|
||||
"title": _extract_field("title (max 30 characters):"),
|
||||
"subtitle": _extract_field("subtitle (max 45 characters):"),
|
||||
"headline": _extract_field("headline (max 50 characters):"),
|
||||
"url_message": _extract_field("url_message (max 60 characters):"),
|
||||
"url": _extract_field("url (max 90 characters):"),
|
||||
}
|
||||
|
||||
# Content: multi-line block between "content" label and "url_message" label
|
||||
content_match = re.search(
|
||||
r"content \(max 1,400 characters\):\s*\n(.*?)url_message",
|
||||
text,
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
if content_match:
|
||||
fields["content"] = _normalize_content(content_match.group(1))
|
||||
|
||||
missing = [k for k, v in fields.items() if not v]
|
||||
if missing:
|
||||
raise ValueError(f"Empty fields extracted from PDF: {', '.join(missing)}")
|
||||
|
||||
onion_base = "6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion"
|
||||
|
||||
with open(config_path) as f:
|
||||
config = json.load(f)
|
||||
|
||||
existing_tear = config.get("tear_off_link", "")
|
||||
fields["tear_off_link"] = (
|
||||
existing_tear or f"http://{fields['subdomain']}.{onion_base}"
|
||||
)
|
||||
|
||||
config.update(fields)
|
||||
with open(config_path, "w") as f:
|
||||
json.dump(config, f, indent=2)
|
||||
+151
-24
@@ -46,7 +46,6 @@ def check_deps():
|
||||
"PIL (Pillow)": "PIL",
|
||||
"jsmin": "jsmin",
|
||||
"htmlmin": "htmlmin",
|
||||
"pypdf": "pypdf",
|
||||
}
|
||||
|
||||
console.rule("Dependency Check")
|
||||
@@ -265,12 +264,6 @@ def interactive_update(config_path: str):
|
||||
save_config(data, config_path)
|
||||
|
||||
|
||||
def update_from_pdf(config_path: str, pdf_path: str):
|
||||
from voxvera.build import extract_form_fields
|
||||
|
||||
extract_form_fields(Path(pdf_path), Path(config_path))
|
||||
|
||||
|
||||
def copy_template(name: str) -> str:
|
||||
"""Copy a template directory into dist/ with a datestamped folder."""
|
||||
date = datetime.date.today().strftime("%Y%m%d")
|
||||
@@ -286,7 +279,7 @@ def copy_template(name: str) -> str:
|
||||
|
||||
|
||||
def build_assets(
|
||||
config_path: str, pdf_path: str | None = None, download_path: str | None = None
|
||||
config_path: str, download_path: str | None = None
|
||||
):
|
||||
from voxvera.build import generate_qr, obfuscate_html
|
||||
|
||||
@@ -296,18 +289,19 @@ def build_assets(
|
||||
generate_qr(config_path, src_dir)
|
||||
# minify HTML + inline JS (pure Python)
|
||||
obfuscate_html(src_dir / "index-master.html", src_dir / "index.html")
|
||||
obfuscate_html(src_dir / "nostr-master.html", src_dir / "nostr.html")
|
||||
data = load_config(config_path)
|
||||
with open(src_dir / "index.html", "r") as fh:
|
||||
html = fh.read()
|
||||
pattern = r'<p class="binary" id="binary-message">.*?</p>'
|
||||
repl = f'<p class="binary" id="binary-message">{data.get("binary_message", "")}</p>'
|
||||
html = re.sub(pattern, repl, html, flags=re.S)
|
||||
|
||||
# Statically replace {{placeholders}} with config values for Tor JS-disabled compatibility
|
||||
for key, value in data.items():
|
||||
html = html.replace(f"{{{{{key}}}}}", str(value))
|
||||
|
||||
with open(src_dir / "index.html", "w") as fh:
|
||||
fh.write(html)
|
||||
subdomain = data["subdomain"]
|
||||
dest = ROOT / "host" / subdomain
|
||||
os.makedirs(dest / "from_client", exist_ok=True)
|
||||
os.makedirs(dest, exist_ok=True)
|
||||
if download_path:
|
||||
os.makedirs(dest / "download", exist_ok=True)
|
||||
shutil.copy(download_path, dest / "download" / "download.zip")
|
||||
@@ -316,9 +310,6 @@ def build_assets(
|
||||
shutil.copy(config_path, dest / "config.json")
|
||||
for fname in [
|
||||
"index.html",
|
||||
"nostr.html",
|
||||
"example.pdf",
|
||||
"submission_form.pdf",
|
||||
]:
|
||||
shutil.copy(src_dir / fname, dest)
|
||||
# Copy QR codes only if they exist (may not be generated if URLs not set)
|
||||
@@ -326,8 +317,6 @@ def build_assets(
|
||||
qr_src = Path(src_dir) / qr_file
|
||||
if qr_src.exists():
|
||||
shutil.copy(qr_src, dest / qr_file)
|
||||
if pdf_path:
|
||||
shutil.copy(pdf_path, dest / "from_client" / "submission_form.pdf")
|
||||
print(f"Flyer files created under {dest}")
|
||||
|
||||
|
||||
@@ -406,6 +395,11 @@ def serve(config_path: str):
|
||||
]
|
||||
log_fh = open(logfile, "w")
|
||||
proc = subprocess.Popen(cmd, stdout=log_fh, stderr=subprocess.STDOUT, env=env)
|
||||
|
||||
pid_file = dir_path / "server.pid"
|
||||
with open(pid_file, "w") as f:
|
||||
f.write(str(proc.pid))
|
||||
|
||||
try:
|
||||
import time
|
||||
import re as _re
|
||||
@@ -417,6 +411,7 @@ def serve(config_path: str):
|
||||
print("OnionShare exited unexpectedly", file=sys.stderr)
|
||||
with open(logfile) as fh:
|
||||
sys.stderr.write(fh.read())
|
||||
pid_file.unlink(missing_ok=True)
|
||||
sys.exit(1)
|
||||
if os.path.exists(logfile):
|
||||
with open(logfile) as fh:
|
||||
@@ -472,6 +467,139 @@ def import_configs():
|
||||
build_assets(dest_config)
|
||||
|
||||
|
||||
def get_servers() -> list[str]:
|
||||
host_dir = ROOT / "host"
|
||||
if not host_dir.exists():
|
||||
return []
|
||||
servers = []
|
||||
for d in host_dir.iterdir():
|
||||
if d.is_dir() and (d / "config.json").exists():
|
||||
servers.append(d.name)
|
||||
return sorted(servers)
|
||||
|
||||
|
||||
def is_server_running(subdomain: str) -> bool:
|
||||
pid_file = ROOT / "host" / subdomain / "server.pid"
|
||||
if not pid_file.exists():
|
||||
return False
|
||||
try:
|
||||
with open(pid_file, "r") as f:
|
||||
pid = int(f.read().strip())
|
||||
except ValueError:
|
||||
pid_file.unlink(missing_ok=True)
|
||||
return False
|
||||
|
||||
import platform
|
||||
import subprocess
|
||||
if platform.system() == "Windows":
|
||||
try:
|
||||
output = subprocess.check_output(f'tasklist /FI "PID eq {pid}" /NH', shell=True, stderr=subprocess.STDOUT).decode()
|
||||
if "No tasks are running" in output:
|
||||
pid_file.unlink(missing_ok=True)
|
||||
return False
|
||||
return True
|
||||
except subprocess.CalledProcessError:
|
||||
pid_file.unlink(missing_ok=True)
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
except OSError:
|
||||
pid_file.unlink(missing_ok=True)
|
||||
return False
|
||||
|
||||
try:
|
||||
output = subprocess.check_output(["ps", "-p", str(pid), "-o", "command="], stderr=subprocess.STDOUT).decode().lower()
|
||||
if "onionshare" in output or "python" in output or "tor" in output:
|
||||
return True
|
||||
return True
|
||||
except Exception:
|
||||
return True
|
||||
|
||||
|
||||
def stop_server(subdomain: str):
|
||||
pid_file = ROOT / "host" / subdomain / "server.pid"
|
||||
if not pid_file.exists():
|
||||
print(f"Server {subdomain} is not running.")
|
||||
return
|
||||
try:
|
||||
with open(pid_file, "r") as f:
|
||||
pid = int(f.read().strip())
|
||||
import platform, signal
|
||||
if platform.system() == "Windows":
|
||||
subprocess.run(["taskkill", "/F", "/PID", str(pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
else:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
print(f"Stopped {subdomain} (PID {pid})")
|
||||
except Exception as e:
|
||||
print(f"Error stopping {subdomain}: {e}")
|
||||
finally:
|
||||
pid_file.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def delete_server(subdomain: str):
|
||||
if is_server_running(subdomain):
|
||||
stop_server(subdomain)
|
||||
dir_path = ROOT / "host" / subdomain
|
||||
if dir_path.exists():
|
||||
shutil.rmtree(dir_path, ignore_errors=True)
|
||||
print(f"Deleted server {subdomain}")
|
||||
|
||||
|
||||
def manage_servers():
|
||||
from InquirerPy import inquirer
|
||||
from InquirerPy.base.control import Choice
|
||||
console = Console()
|
||||
|
||||
while True:
|
||||
servers = get_servers()
|
||||
if not servers:
|
||||
console.print("[red]No servers found. Use 'voxvera init' to create one.[/red]")
|
||||
return
|
||||
|
||||
choices = []
|
||||
for s in servers:
|
||||
running = is_server_running(s)
|
||||
status_text = "[ON] " if running else "[OFF]"
|
||||
choices.append(Choice(s, f"{status_text} {s}"))
|
||||
|
||||
choices.append(Choice(None, "Exit"))
|
||||
|
||||
selected = inquirer.select(
|
||||
message="Select a server to manage (or Exit):",
|
||||
choices=choices
|
||||
).execute()
|
||||
|
||||
if selected is None:
|
||||
break
|
||||
|
||||
running = is_server_running(selected)
|
||||
action_choices = [
|
||||
Choice("toggle", "Stop" if running else "Start"),
|
||||
Choice("delete", "Delete"),
|
||||
Choice(None, "Back")
|
||||
]
|
||||
|
||||
action = inquirer.select(
|
||||
message=f"Manage {selected}:",
|
||||
choices=action_choices
|
||||
).execute()
|
||||
|
||||
if action == "toggle":
|
||||
if running:
|
||||
stop_server(selected)
|
||||
else:
|
||||
config_path = ROOT / "host" / selected / "config.json"
|
||||
try:
|
||||
serve(str(config_path))
|
||||
except SystemExit:
|
||||
pass
|
||||
elif action == "delete":
|
||||
confirm = inquirer.confirm(message=f"Are you sure you want to delete {selected}?").execute()
|
||||
if confirm:
|
||||
delete_server(selected)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(prog="voxvera")
|
||||
parser.add_argument(
|
||||
@@ -482,14 +610,12 @@ def main(argv=None):
|
||||
sub = parser.add_subparsers(dest="command")
|
||||
|
||||
p_init = sub.add_parser(
|
||||
"init", help="Update configuration interactively or from PDF"
|
||||
"init", help="Update configuration interactively"
|
||||
)
|
||||
p_init.add_argument("--template", help="Copy a template into dist/")
|
||||
p_init.add_argument("--from-pdf")
|
||||
p_init.add_argument("--non-interactive", action="store_true")
|
||||
|
||||
p_build = sub.add_parser("build", help="Build flyer assets from config")
|
||||
p_build.add_argument("--pdf")
|
||||
p_build.add_argument("--download")
|
||||
|
||||
sub.add_parser("import", help="Batch import JSON files from imports/")
|
||||
@@ -503,6 +629,7 @@ def main(argv=None):
|
||||
help="Skip interactive prompts and use existing config",
|
||||
)
|
||||
sub.add_parser("check", help="Check for required external dependencies")
|
||||
sub.add_parser("manage", help="Manage VoxVera servers interactively")
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
config_path = Path(args.config).resolve()
|
||||
@@ -511,16 +638,16 @@ def main(argv=None):
|
||||
if args.template:
|
||||
copy_template(args.template)
|
||||
return
|
||||
elif args.from_pdf:
|
||||
update_from_pdf(config_path, args.from_pdf)
|
||||
elif not args.non_interactive:
|
||||
interactive_update(config_path)
|
||||
elif args.command == "build":
|
||||
build_assets(config_path, pdf_path=args.pdf, download_path=args.download)
|
||||
build_assets(config_path, download_path=args.download)
|
||||
elif args.command == "serve":
|
||||
serve(config_path)
|
||||
elif args.command == "import":
|
||||
import_configs()
|
||||
elif args.command == "manage":
|
||||
manage_servers()
|
||||
elif args.command == "quickstart":
|
||||
if not args.non_interactive:
|
||||
if not sys.stdin.isatty():
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"name": "Vox Vera Printable Flyers",
|
||||
"subdomain": "voxvera",
|
||||
"title": "TOP SECRET",
|
||||
"subtitle": "DO <span class=\\\"redacted\\\">NOT</span> DISTRIBUTE",
|
||||
"headline": "OPERATION VOX VERA",
|
||||
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message. \n\nUse memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting. \n\nPrivacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.\n\nJoin us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.\n",
|
||||
"url_message": "Follow this link to learn more. Use Tor Browser.",
|
||||
"url": "https://voxvera.org/",
|
||||
"tear_off_link": "https://voxvera.org/",
|
||||
"binary_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
Binary file not shown.
@@ -1,70 +0,0 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Connect with Me on KeyChat</title><style>
|
||||
body {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #000000;
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
h1 {
|
||||
color: #333;
|
||||
}
|
||||
p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
.code-box {
|
||||
background-color: white;
|
||||
border: 1px solid #000000;
|
||||
padding: 10px;
|
||||
margin: 20px 0;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.copy-btn {
|
||||
background-color: #000000;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: 1px solid #000000;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
.copy-btn:hover {
|
||||
background-color: #3d3d3d;
|
||||
}
|
||||
footer {
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.credit {
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
a {
|
||||
color: #a30000;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style></head><body><div class="container"><h1>Nostr</h1><p>Use a secure file shareing tool like <a href="https://onionshare.org/" target="_blank">Onion Share</a> or <a href="https://wormhole.app/" target="_blank">WormHole</a> to send your completed <a href="submission_form.pdf" target="_blank">submission form PDF</a> to the following <a href="https://www.0xchat.com/#/" target="_blank">Nostr</a> npub account:</p><p></p><a href="index.html">Back to Home</a></p><div class="code-box" id="keyChatCode">npub1ln8efl52vsyh6lg59c9v3kut56wev489lzcma0sv2mf8nm6jhwjqeteygt</div><button class="copy-btn" onclick="copyCode()">Copy Code</button></div><footer><p>Not on Nostr yet? <a href="https://nostr.how/en/what-is-nostr" target="_blank">Learn more here</a>.</p><p class="credit">built with <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p></footer><script>function copyCode(){var copyText=document.getElementById("keyChatCode").innerText;navigator.clipboard.writeText(copyText).then(function(){alert("Code copied to clipboard!");},function(err){alert("Failed to copy code: ",err);});}</script></body></html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 376 B |
Binary file not shown.
|
Before Width: | Height: | Size: 376 B |
Binary file not shown.
@@ -3,10 +3,7 @@
|
||||
This directory contains the source assets used to build flyers:
|
||||
|
||||
- `index-master.html` -- Main flyer template (HTML/CSS/JS)
|
||||
- `nostr-master.html` -- Nostr sharing page template
|
||||
- `config.json` -- Default configuration values
|
||||
- `example.pdf` -- Example PDF included with built flyers
|
||||
- `submission_form.pdf` -- Submission form included with built flyers
|
||||
- `qrcode-content.png` -- Generated QR code for the content link
|
||||
- `qrcode-tear-offs.png` -- Generated QR code for the tear-off link
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"name": "Vox Vera Printable Flyers",
|
||||
"subdomain": "voxvera",
|
||||
"title": "TOP SECRET",
|
||||
"subtitle": "DO <span class=\\\"redacted\\\">NOT</span> DISTRIBUTE",
|
||||
"headline": "OPERATION VOX VERA",
|
||||
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message. \n\nUse memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting. \n\nPrivacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.\n\nJoin us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.\n",
|
||||
"url_message": "Follow this link to learn more. Use Tor Browser.",
|
||||
"url": "https://voxvera.org/",
|
||||
"tear_off_link": "https://voxvera.org/",
|
||||
"binary_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Legacy wrapper for voxvera Python CLI
|
||||
|
||||
set -e
|
||||
|
||||
if command -v voxvera >/dev/null 2>&1; then
|
||||
voxvera "$@"
|
||||
else
|
||||
python3 -m voxvera "$@"
|
||||
fi
|
||||
Binary file not shown.
@@ -1,33 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
CONFIG="${1:-config.json}"
|
||||
|
||||
# Ensure dependencies
|
||||
command -v jq >/dev/null 2>&1 || { echo "jq is required" >&2; exit 1; }
|
||||
command -v qrencode >/dev/null 2>&1 || { echo "qrencode is required" >&2; exit 1; }
|
||||
command -v convert >/dev/null 2>&1 || { echo "ImageMagick convert is required" >&2; exit 1; }
|
||||
|
||||
url=$(jq -r '.url' "$CONFIG")
|
||||
tear=$(jq -r '.tear_off_link' "$CONFIG")
|
||||
|
||||
[ -n "$url" ] || { echo "URL missing in $CONFIG" >&2; exit 1; }
|
||||
[ -n "$tear" ] || { echo "tear_off_link missing in $CONFIG" >&2; exit 1; }
|
||||
|
||||
tmp_content=$(mktemp)
|
||||
tmp_tear=$(mktemp)
|
||||
|
||||
cleanup() {
|
||||
rm -f "$tmp_content" "$tmp_tear"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
qrencode -o "$tmp_content" -s 10 -m 0 "$url"
|
||||
qrencode -o "$tmp_tear" -s 10 -m 0 "$tear"
|
||||
|
||||
convert "$tmp_content" -resize 128x128 "qrcode-content.png"
|
||||
convert "$tmp_tear" -resize 128x128 "qrcode-tear-offs.png"
|
||||
|
||||
echo "QR codes generated"
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
IMPORT_DIR="imports"
|
||||
shopt -s nullglob
|
||||
files=("$IMPORT_DIR"/*.json)
|
||||
|
||||
if [[ ${#files[@]} -eq 0 ]]; then
|
||||
echo "No JSON files found in $IMPORT_DIR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for json in "${files[@]}"; do
|
||||
echo "Processing $json"
|
||||
cp "$json" src/config.json
|
||||
subdomain=$(jq -r '.subdomain' "$json")
|
||||
dest="host/$subdomain"
|
||||
rm -rf "$dest"
|
||||
voxvera build --config src/config.json
|
||||
done
|
||||
@@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1024, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title id="site-title">Vox Vera Printable Flyers</title> <!-- Title will be replaced -->
|
||||
<title id="site-title">{{name}}</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
@@ -302,7 +302,7 @@
|
||||
<div class="tear-off-text">
|
||||
Use Tor Browser<br><br>
|
||||
to help spread this message go to<br>
|
||||
<a class="tear-off-link" href="#">Loading...</a><br> <!-- The class is used instead of an id -->
|
||||
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
|
||||
click the button below to print
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -313,7 +313,7 @@
|
||||
<div class="tear-off-text">
|
||||
Use Tor Browser<br><br>
|
||||
to help spread this message go to<br>
|
||||
<a class="tear-off-link" href="#">Loading...</a><br> <!-- The class is used instead of an id -->
|
||||
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
|
||||
click the button below to print
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -324,7 +324,7 @@
|
||||
<div class="tear-off-text">
|
||||
Use Tor Browser<br><br>
|
||||
to help spread this message go to<br>
|
||||
<a class="tear-off-link" href="#">Loading...</a><br> <!-- The class is used instead of an id -->
|
||||
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
|
||||
click the button below to print
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -335,7 +335,7 @@
|
||||
<div class="tear-off-text">
|
||||
Use Tor Browser<br><br>
|
||||
to help spread this message go to<br>
|
||||
<a class="tear-off-link" href="#">Loading...</a><br> <!-- The class is used instead of an id -->
|
||||
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
|
||||
click the button below to print
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -346,7 +346,7 @@
|
||||
<div class="tear-off-text">
|
||||
Use Tor Browser<br><br>
|
||||
to help spread this message go to<br>
|
||||
<a class="tear-off-link" href="#">Loading...</a><br> <!-- The class is used instead of an id -->
|
||||
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
|
||||
click the button below to print
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -357,7 +357,7 @@
|
||||
<div class="tear-off-text">
|
||||
Use Tor Browser<br><br>
|
||||
to help spread this message go to<br>
|
||||
<a class="tear-off-link" href="#">Loading...</a><br> <!-- The class is used instead of an id -->
|
||||
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
|
||||
click the button below to print
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -368,7 +368,7 @@
|
||||
<div class="tear-off-text">
|
||||
Use Tor Browser<br><br>
|
||||
to help spread this message go to<br>
|
||||
<a class="tear-off-link" href="#">Loading...</a><br> <!-- The class is used instead of an id -->
|
||||
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
|
||||
click the button below to print
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -379,7 +379,7 @@
|
||||
<div class="tear-off-text">
|
||||
Use Tor Browser<br><br>
|
||||
to help spread this message go to<br>
|
||||
<a class="tear-off-link" href="#">Loading...</a><br> <!-- The class is used instead of an id -->
|
||||
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
|
||||
click the button below to print
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -390,7 +390,7 @@
|
||||
<div class="tear-off-text">
|
||||
Use Tor Browser<br><br>
|
||||
to help spread this message go to<br>
|
||||
<a class="tear-off-link" href="#">Loading...</a><br> <!-- The class is used instead of an id -->
|
||||
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
|
||||
click the button below to print
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -401,28 +401,25 @@
|
||||
<div class="tear-off-text">
|
||||
Use Tor Browser<br><br>
|
||||
to help spread this message go to<br>
|
||||
<a class="tear-off-link" href="#">Loading...</a><br> <!-- The class is used instead of an id -->
|
||||
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
|
||||
click the button below to print
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
<img src="qrcode-tear-offs.png" alt="QR Code">
|
||||
</div>
|
||||
</div>
|
||||
<!-- Repeat similar structure for other tear-offs -->
|
||||
</div>
|
||||
<div class="content">
|
||||
<h1 id="top-secret-title">Loading...</h1> <!-- "Top Secret" title will be replaced -->
|
||||
<div class="distribute" id="do-not-distribute">Loading...</div> <!-- "DO NOT DISTRIBUTE" message will be replaced -->
|
||||
<h1 id="operation-title-2"><br>Loading...</h1> <!-- "Operation Vox Vera" title will be replaced -->
|
||||
<h1 id="top-secret-title">{{title}}</h1>
|
||||
<div class="distribute" id="do-not-distribute">{{subtitle}}</div>
|
||||
<h1 id="operation-title-2"><br>{{headline}}</h1>
|
||||
<hr>
|
||||
<div class="message">
|
||||
<div class="text-container" id="main-content" contenteditable="false"> <!-- Content will be replaced -->
|
||||
Loading content...
|
||||
</div>
|
||||
<div class="text-container" id="main-content" contenteditable="false">{{content}}</div>
|
||||
<div class="qr-code-body">
|
||||
<div class="qr-code-url">
|
||||
<span class="url-message">Loading message...</span><br><br> <!-- The message will be replaced -->
|
||||
<a id="qr-link-2" href="#">Loading...</a> <!-- The href and text will be replaced -->
|
||||
<span class="url-message">{{url_message}}</span><br><br>
|
||||
<a id="qr-link-2" href="{{url}}" target="_blank">{{url}}</a>
|
||||
</div>
|
||||
<img src="qrcode-content.png" alt="QR Code">
|
||||
</div>
|
||||
@@ -430,9 +427,7 @@
|
||||
<hr>
|
||||
<div class="footer">
|
||||
<p class="credit">built with <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p>
|
||||
<p class="binary" id="binary-message"><br>
|
||||
<br>Loading...<br> <!-- Binary message will be replaced -->
|
||||
</p>
|
||||
<p class="binary" id="binary-message"><br><br>{{binary_message}}<br></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -440,52 +435,5 @@
|
||||
<button class="print-button" onclick="window.print()">Print this page</button>
|
||||
<a class="download-button" href="download/download.zip" download>Download</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Fetch the configuration JSON file
|
||||
fetch('config.json')
|
||||
.then(response => response.json())
|
||||
.then(config => {
|
||||
// Update the site title
|
||||
document.getElementById('site-title').innerText = config.name;
|
||||
|
||||
// Update the "Top Secret" title
|
||||
document.getElementById('top-secret-title').innerText = config.title;
|
||||
|
||||
// Update the "DO NOT DISTRIBUTE" message
|
||||
document.getElementById('do-not-distribute').innerHTML = config.subtitle;
|
||||
|
||||
// Update the operation titles
|
||||
document.getElementById('operation-title-2').innerText = config.headline;
|
||||
|
||||
// Update the main content
|
||||
document.getElementById('main-content').innerText =
|
||||
config.content.replace(/\\n/g, '\n');
|
||||
|
||||
// Update the URL message
|
||||
document.querySelectorAll('.url-message').forEach(msg => {
|
||||
msg.innerText = config.url_message;
|
||||
});
|
||||
|
||||
// Update the QR URL link in the main content
|
||||
document.getElementById('qr-link-2').innerText = config.url;
|
||||
document.getElementById('qr-link-2').href = config.url;
|
||||
document.getElementById('qr-link-2').target = "_blank"; // Opens in a new tab
|
||||
|
||||
// Update all tear-off links
|
||||
document.querySelectorAll('.tear-off-link').forEach(link => {
|
||||
link.innerText = config.tear_off_link;
|
||||
link.href = config.tear_off_link;
|
||||
link.target = "_blank"; // Opens in a new tab
|
||||
});
|
||||
|
||||
// Update the binary message
|
||||
document.getElementById('binary-message').innerText = config.binary_message;
|
||||
})
|
||||
.catch(error => console.error('Error loading config:', error));
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,288 +0,0 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=1024, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title id="site-title">Vox Vera Printable Flyers</title><style>
|
||||
body {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #b80000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 11in;
|
||||
width: 8.5in;
|
||||
border: 1px solid #000;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.print-button-container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.print-button {
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.print-button:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
.download-button {
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.download-button:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
.left-tear-offs {
|
||||
width: 3.75in;
|
||||
padding: 8px;
|
||||
border-right: 1px dashed #000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
gap: 5px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tear-off {
|
||||
display: flex;
|
||||
padding: 5px;
|
||||
border: 1px dashed #000;
|
||||
font-size: 10px;
|
||||
box-sizing: border-box;
|
||||
page-break-inside: avoid;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.tear-off-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.tear-off a {
|
||||
color: #b80000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tear-off a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.qr-code {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 0px solid #000;
|
||||
margin-left: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.qr-code img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: calc(8.5in - 3.75in);
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
page-break-inside: avoid;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
margin-bottom: 10px;
|
||||
font-size: 24px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.distribute {
|
||||
font-size: 18px;
|
||||
letter-spacing: 6px;
|
||||
}
|
||||
|
||||
.redacted {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.message {
|
||||
flex: 1;
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
text-align: left;
|
||||
overflow-wrap: break-word;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
white-space: pre-wrap;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text-container {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
white-space: pre-wrap;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.qr-code-body {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.qr-code-body img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
object-fit: contain;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.qr-code-url {
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
hyphens: auto;
|
||||
flex-grow: 1;
|
||||
margin-right: 10px;
|
||||
max-width: 60%;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid #000;
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
font-size: 10px;
|
||||
overflow-wrap: break-word;
|
||||
text-align: center;
|
||||
align-self: center;
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid #000;
|
||||
}
|
||||
|
||||
.footer p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.footer .binary {
|
||||
margin-bottom: 5px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.footer .credit {
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body, html {
|
||||
width: 8.5in;
|
||||
height: 11in;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.container {
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
page-break-inside: avoid;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 8.5in;
|
||||
height: 11in;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.print-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.download-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.button-bar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 20px 0 30px;
|
||||
}
|
||||
|
||||
</style></head><body><div class="container"><div class="left-tear-offs"><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div><div class="tear-off"><div class="tear-off-text"> Use Tor Browser<br><br> to help spread this message go to<br><a class="tear-off-link" href="#">Loading...</a><br> click the button below to print </div><div class="qr-code"><img src="qrcode-tear-offs.png" alt="QR Code"></div></div></div><div class="content"><h1 id="top-secret-title">Loading...</h1> <div class="distribute" id="do-not-distribute">Loading...</div> <h1 id="operation-title-2"><br>Loading...</h1> <hr><div class="message"><div class="text-container" id="main-content" contenteditable="false"> Loading content... </div><div class="qr-code-body"><div class="qr-code-url"><span class="url-message">Loading message...</span><br><br> <a id="qr-link-2" href="#">Loading...</a> </div><img src="qrcode-content.png" alt="QR Code"></div></div><hr><div class="footer"><p class="credit">built with <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p><p class="binary" id="binary-message">0110010 0101011 0110010 0111101 0110100</p></div></div></div><div class="button-bar"><button class="print-button" onclick="window.print()">Print this page</button><a class="download-button" href="download/download.zip" download>Download</a></div><script>fetch('config.json').then(response=>response.json()).then(config=>{document.getElementById('site-title').innerText=config.name;document.getElementById('top-secret-title').innerText=config.title;document.getElementById('do-not-distribute').innerHTML=config.subtitle;document.getElementById('operation-title-2').innerText=config.headline;document.getElementById('main-content').innerText=config.content.replace(/\\n/g,'\n');document.querySelectorAll('.url-message').forEach(msg=>{msg.innerText=config.url_message;});document.getElementById('qr-link-2').innerText=config.url;document.getElementById('qr-link-2').href=config.url;document.getElementById('qr-link-2').target="_blank";document.querySelectorAll('.tear-off-link').forEach(link=>{link.innerText=config.tear_off_link;link.href=config.tear_off_link;link.target="_blank";});document.getElementById('binary-message').innerText=config.binary_message;}).catch(error=>console.error('Error loading config:',error));</script></body></html>
|
||||
@@ -1,102 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Connect with Me on KeyChat</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #000000;
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
h1 {
|
||||
color: #333;
|
||||
}
|
||||
p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
.code-box {
|
||||
background-color: white;
|
||||
border: 1px solid #000000;
|
||||
padding: 10px;
|
||||
margin: 20px 0;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.copy-btn {
|
||||
background-color: #000000;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: 1px solid #000000;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
.copy-btn:hover {
|
||||
background-color: #3d3d3d;
|
||||
}
|
||||
footer {
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.credit {
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
a {
|
||||
color: #a30000;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Nostr</h1>
|
||||
<p>Use a secure file shareing tool like <a href="https://onionshare.org/" target="_blank">Onion Share</a> or <a href="https://wormhole.app/" target="_blank">WormHole</a> to send your completed <a href="submission_form.pdf" target="_blank">submission form PDF</a> to the following <a href="https://www.0xchat.com/#/" target="_blank">Nostr</a> npub account:</p>
|
||||
<p></p><a href="index.html">Back to Home</a></p>
|
||||
<div class="code-box" id="keyChatCode">npub1ln8efl52vsyh6lg59c9v3kut56wev489lzcma0sv2mf8nm6jhwjqeteygt</div>
|
||||
<button class="copy-btn" onclick="copyCode()">Copy Code</button>
|
||||
</div>
|
||||
<footer>
|
||||
<p>Not on Nostr yet? <a href="https://nostr.how/en/what-is-nostr" target="_blank">Learn more here</a>.</p>
|
||||
<p class="credit">built with <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
function copyCode() {
|
||||
var copyText = document.getElementById("keyChatCode").innerText;
|
||||
navigator.clipboard.writeText(copyText).then(function() {
|
||||
alert("Code copied to clipboard!");
|
||||
}, function(err) {
|
||||
alert("Failed to copy code: ", err);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,70 +0,0 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Connect with Me on KeyChat</title><style>
|
||||
body {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #000000;
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
h1 {
|
||||
color: #333;
|
||||
}
|
||||
p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
.code-box {
|
||||
background-color: white;
|
||||
border: 1px solid #000000;
|
||||
padding: 10px;
|
||||
margin: 20px 0;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.copy-btn {
|
||||
background-color: #000000;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: 1px solid #000000;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
.copy-btn:hover {
|
||||
background-color: #3d3d3d;
|
||||
}
|
||||
footer {
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.credit {
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
a {
|
||||
color: #a30000;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style></head><body><div class="container"><h1>Nostr</h1><p>Use a secure file shareing tool like <a href="https://onionshare.org/" target="_blank">Onion Share</a> or <a href="https://wormhole.app/" target="_blank">WormHole</a> to send your completed <a href="submission_form.pdf" target="_blank">submission form PDF</a> to the following <a href="https://www.0xchat.com/#/" target="_blank">Nostr</a> npub account:</p><p></p><a href="index.html">Back to Home</a></p><div class="code-box" id="keyChatCode">npub1ln8efl52vsyh6lg59c9v3kut56wev489lzcma0sv2mf8nm6jhwjqeteygt</div><button class="copy-btn" onclick="copyCode()">Copy Code</button></div><footer><p>Not on Nostr yet? <a href="https://nostr.how/en/what-is-nostr" target="_blank">Learn more here</a>.</p><p class="credit">built with <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p></footer><script>function copyCode(){var copyText=document.getElementById("keyChatCode").innerText;navigator.clipboard.writeText(copyText).then(function(){alert("Code copied to clipboard!");},function(err){alert("Failed to copy code: ",err);});}</script></body></html>
|
||||
@@ -1,72 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_PATH="${1:-config.json}"
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "Error: required command '$1' not found" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
for cmd in javascript-obfuscator html-minifier-terser node; do
|
||||
require_cmd "$cmd"
|
||||
done
|
||||
|
||||
# Detect whether mktemp supports the --suffix option. If not, fall back to a
|
||||
# portable template-based approach (e.g., on macOS).
|
||||
if tmp=$(mktemp --suffix=.test 2>/dev/null); then
|
||||
rm -f "$tmp"
|
||||
mktemp_with_suffix() { mktemp --suffix="$1"; }
|
||||
else
|
||||
mktemp_with_suffix() { mktemp "${TMPDIR:-/tmp}/tmp.XXXXXX$1"; }
|
||||
fi
|
||||
|
||||
# Input and output file names
|
||||
input_file="index-master.html"
|
||||
output_file="index.html"
|
||||
temp_js_file=$(mktemp_with_suffix .js) # Temporary .js file for extracting JavaScript
|
||||
temp_js_obfuscated_file=$(mktemp_with_suffix .js) # Temporary file for obfuscated JavaScript
|
||||
temp_html_file=$(mktemp) # Temporary file for processing HTML without JS
|
||||
|
||||
cleanup() {
|
||||
rm -f "$temp_js_file" "$temp_js_obfuscated_file" "$temp_html_file"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# Check if the input file exists
|
||||
if [ ! -f "$input_file" ]; then
|
||||
echo "Input file $input_file does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract embedded JavaScript into a temporary .js file
|
||||
awk '/<script>/,/<\/script>/' "$input_file" | sed '1d;$d' > "$temp_js_file"
|
||||
|
||||
# Obfuscate the extracted JavaScript
|
||||
javascript-obfuscator "$temp_js_file" --output "$temp_js_obfuscated_file"
|
||||
|
||||
# Read the obfuscated JavaScript into a variable
|
||||
obfuscated_js=$(cat "$temp_js_obfuscated_file")
|
||||
|
||||
# Escape slashes in the obfuscated JavaScript
|
||||
escaped_js=$(echo "$obfuscated_js" | sed 's/[\/&]/\\&/g')
|
||||
|
||||
# Replace the original JavaScript in the HTML with the obfuscated version
|
||||
sed -e "/<script>/,/<\/script>/c\<script>$escaped_js<\/script>" "$input_file" > "$temp_html_file"
|
||||
|
||||
# Minify HTML, including the obfuscated embedded JavaScript and CSS
|
||||
html-minifier-terser \
|
||||
--collapse-whitespace \
|
||||
--minify-css true \
|
||||
--remove-comments \
|
||||
--remove-empty-attributes \
|
||||
--output "$output_file" \
|
||||
"$temp_html_file"
|
||||
|
||||
# Clean up temporary files handled by trap
|
||||
|
||||
echo "Obfuscation and minification complete. Output saved as $output_file."
|
||||
@@ -1,72 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_PATH="${1:-config.json}"
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "Error: required command '$1' not found" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
for cmd in javascript-obfuscator html-minifier-terser node; do
|
||||
require_cmd "$cmd"
|
||||
done
|
||||
|
||||
# Detect whether mktemp supports the --suffix option. If not, fall back to a
|
||||
# portable template-based approach (e.g., on macOS).
|
||||
if tmp=$(mktemp --suffix=.test 2>/dev/null); then
|
||||
rm -f "$tmp"
|
||||
mktemp_with_suffix() { mktemp --suffix="$1"; }
|
||||
else
|
||||
mktemp_with_suffix() { mktemp "${TMPDIR:-/tmp}/tmp.XXXXXX$1"; }
|
||||
fi
|
||||
|
||||
# Input and output file names
|
||||
input_file="nostr-master.html"
|
||||
output_file="nostr.html"
|
||||
temp_js_file=$(mktemp_with_suffix .js) # Temporary .js file for extracting JavaScript
|
||||
temp_js_obfuscated_file=$(mktemp_with_suffix .js) # Temporary file for obfuscated JavaScript
|
||||
temp_html_file=$(mktemp) # Temporary file for processing HTML without JS
|
||||
|
||||
cleanup() {
|
||||
rm -f "$temp_js_file" "$temp_js_obfuscated_file" "$temp_html_file"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# Check if the input file exists
|
||||
if [ ! -f "$input_file" ]; then
|
||||
echo "Input file $input_file does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract embedded JavaScript into a temporary .js file
|
||||
awk '/<script>/,/<\/script>/' "$input_file" | sed '1d;$d' > "$temp_js_file"
|
||||
|
||||
# Obfuscate the extracted JavaScript
|
||||
javascript-obfuscator "$temp_js_file" --output "$temp_js_obfuscated_file"
|
||||
|
||||
# Read the obfuscated JavaScript into a variable
|
||||
obfuscated_js=$(cat "$temp_js_obfuscated_file")
|
||||
|
||||
# Escape slashes in the obfuscated JavaScript
|
||||
escaped_js=$(echo "$obfuscated_js" | sed 's/[\/&]/\\&/g')
|
||||
|
||||
# Replace the original JavaScript in the HTML with the obfuscated version
|
||||
sed -e "/<script>/,/<\/script>/c\<script>$escaped_js<\/script>" "$input_file" > "$temp_html_file"
|
||||
|
||||
# Minify HTML, including the obfuscated embedded JavaScript and CSS
|
||||
html-minifier-terser \
|
||||
--collapse-whitespace \
|
||||
--minify-css true \
|
||||
--remove-comments \
|
||||
--remove-empty-attributes \
|
||||
--output "$output_file" \
|
||||
"$temp_html_file"
|
||||
|
||||
# Clean up temporary files handled by trap
|
||||
|
||||
echo "Obfuscation and minification complete. Output saved as $output_file."
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 376 B |
Binary file not shown.
|
Before Width: | Height: | Size: 376 B |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
1. Make content field smaller max number.
|
||||
2. URL for content not getting pulled into the config.json correctly every time.
|
||||
@@ -1,117 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Define the input PDF, output text file, and output JSON
|
||||
pdf_file="./from_client/submission_form.pdf"
|
||||
text_file="submission_form.txt"
|
||||
json_file="config.json"
|
||||
|
||||
# Log the start of the script
|
||||
echo "Starting script..."
|
||||
echo "Looking for PDF at: $pdf_file"
|
||||
echo "Config JSON: $json_file"
|
||||
|
||||
# Convert PDF to text
|
||||
echo "Converting PDF to text..."
|
||||
pdftotext -layout "$pdf_file" "$text_file"
|
||||
|
||||
# Extract individual fields using grep, logging each step
|
||||
echo "Extracting fields from the text file..."
|
||||
|
||||
# Adjusting the extraction of the name field
|
||||
name=$(grep -A 3 'name (max 25 characters):' "$text_file" | tail -n 1 | awk '{$1=$1;print}')
|
||||
echo "Extracted name: '$name'"
|
||||
|
||||
subdomain=$(grep -A 1 'subdomain (max 30 characters):' "$text_file" | tail -n 1 | awk '{$1=$1;print}')
|
||||
echo "Extracted subdomain: '$subdomain'"
|
||||
|
||||
title=$(grep -A 1 'title (max 30 characters):' "$text_file" | tail -n 1 | awk '{$1=$1;print}')
|
||||
echo "Extracted title: '$title'"
|
||||
|
||||
subtitle=$(grep -A 1 'subtitle (max 45 characters):' "$text_file" | tail -n 1 | awk '{$1=$1;print}')
|
||||
echo "Extracted subtitle: '$subtitle'"
|
||||
|
||||
headline=$(grep -A 1 'headline (max 50 characters):' "$text_file" | tail -n 1 | awk '{$1=$1;print}')
|
||||
echo "Extracted headline: '$headline'"
|
||||
|
||||
# Process the content to remove soft wraps while keeping hard returns
|
||||
content=$(grep -A 100 'content (max 1,400 characters):' "$text_file" | sed -n '2,/url_message/p' | sed '$d' | awk '
|
||||
{
|
||||
if (NR == 1) {
|
||||
prev = $0;
|
||||
next;
|
||||
}
|
||||
|
||||
# Handle paragraph breaks explicitly
|
||||
if ($0 ~ /^[[:space:]]*$/) {
|
||||
if (prev) print prev "\n";
|
||||
prev = "";
|
||||
}
|
||||
# Handle sentences that should stay together
|
||||
else if (prev ~ /[.?!]$/ && $0 ~ /^[[:upper:]]/) {
|
||||
prev = prev " " $0;
|
||||
}
|
||||
# Join lines that are part of the same sentence (soft wrap)
|
||||
else if (prev !~ /[.?!]$/) {
|
||||
prev = prev " " $0;
|
||||
}
|
||||
# Otherwise, treat as a new sentence/paragraph
|
||||
else {
|
||||
if (prev) print prev;
|
||||
prev = $0;
|
||||
}
|
||||
}
|
||||
END { if (prev) print prev }' | sed -E 's/\n\n[[:space:]]+/\n\n/g' | sed -E 's/^[[:space:]]+//g')
|
||||
|
||||
echo "Extracted content: '$content'"
|
||||
|
||||
url_message=$(grep -A 1 'url_message (max 60 characters):' "$text_file" | tail -n 1 | awk '{$1=$1;print}')
|
||||
echo "Extracted url_message: '$url_message'"
|
||||
|
||||
url=$(grep -A 1 'url (max 90 characters):' "$text_file" | tail -n 1 | awk '{$1=$1;print}')
|
||||
echo "Extracted url: '$url'"
|
||||
|
||||
# Construct the URLs with the subdomain
|
||||
onion_base="6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion"
|
||||
constructed_url="http://$subdomain.$onion_base"
|
||||
|
||||
# Use an existing tear_off_link from the config if provided; otherwise default
|
||||
# to the constructed onion URL.
|
||||
existing_tear_off_link=$(jq -r '.tear_off_link // empty' "$json_file")
|
||||
tear_off_link="${existing_tear_off_link:-http://$subdomain.$onion_base}"
|
||||
|
||||
echo "Constructed URL: '$constructed_url'"
|
||||
echo "Using Tear-off Link: '$tear_off_link'"
|
||||
|
||||
# Check if the extracted fields are not empty
|
||||
if [ -z "$name" ] || [ -z "$subdomain" ] || [ -z "$title" ] || [ -z "$subtitle" ] || [ -z "$headline" ] || [ -z "$content" ] || [ -z "$url_message" ] || [ -z "$url" ]; then
|
||||
echo "Error: One or more extracted fields are empty. Please check the PDF form and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Update the JSON using jq and log the operation
|
||||
echo "Updating config.json..."
|
||||
jq --arg name "$name" \
|
||||
--arg subdomain "$subdomain" \
|
||||
--arg title "$title" \
|
||||
--arg subtitle "$subtitle" \
|
||||
--arg headline "$headline" \
|
||||
--arg content "$content" \
|
||||
--arg url_message "$url_message" \
|
||||
--arg url "$url" \
|
||||
--arg tear_off_link "$tear_off_link" \
|
||||
'.name = $name |
|
||||
.subdomain = $subdomain |
|
||||
.title = $title |
|
||||
.subtitle = $subtitle |
|
||||
.headline = $headline |
|
||||
.content = $content |
|
||||
.url_message = $url_message |
|
||||
.url = $url |
|
||||
.tear_off_link = $tear_off_link' "$json_file" > tmp.json && mv tmp.json "$json_file"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Config file updated successfully."
|
||||
else
|
||||
echo "Error: Failed to update config file."
|
||||
exit 1
|
||||
fi
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Connect with Me on KeyChat</title><style>body{font-family:'Courier New',Courier,monospace;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;margin:0;background-color:#fff}.container{background-color:#fff;padding:20px;border:1px solid #000;text-align:center;max-width:400px;width:90%;box-shadow:0 0 10px rgba(0,0,0,.1)}h1{color:#333}p{font-size:16px;color:#666}.code-box{background-color:#fff;border:1px solid #000;padding:10px;margin:20px 0;font-size:18px;color:#333;word-wrap:break-word;word-break:break-word;overflow-wrap:break-word;cursor:pointer;user-select:none}.copy-btn{background-color:#000;color:#fff;padding:10px 20px;border:1px solid #000;cursor:pointer;font-size:16px;transition:background-color .3s ease}.copy-btn:hover{background-color:#3d3d3d}footer{margin-top:20px;font-size:14px;color:#999}.credit{font-size:12px;margin-top:10px}a{color:#a30000;text-decoration:none}a:hover{text-decoration:underline}</style></head><body><div class="container"><h1>Nostr</h1><p>Use a secure file shareing tool like <a href="https://onionshare.org/" target="_blank">Onion Share</a> or <a href="https://wormhole.app/" target="_blank">WormHole</a> to send your completed <a href="submission_form.pdf" target="_blank">submission form PDF</a> to the following <a href="https://www.0xchat.com/#/" target="_blank">Nostr</a> npub account:</p><p></p><a href="index.html">Back to Home</a><p></p><div class="code-box" id="keyChatCode">npub1ln8efl52vsyh6lg59c9v3kut56wev489lzcma0sv2mf8nm6jhwjqeteygt</div><button class="copy-btn" onclick="copyCode()">Copy Code</button></div><footer><p>Not on Nostr yet? <a href="https://nostr.how/en/what-is-nostr" target="_blank">Learn more here</a>.</p><p class="credit">Flyer generated with <a href="https://github.com/PR0M3TH3AN/VoxVera">Vox Vera</a>.</p></footer><script>function a0_0x55b4(){var _0xeeb36b=['Failed\x20to\x20copy\x20code:\x20','36973gPfEDk','3hjGEPG','keyChatCode','clipboard','2471RXzkOy','2005220SDqWBz','then','8464mvqASw','24965hDeypK','468jCVegU','3143720lhnwYU','innerText','Code\x20copied\x20to\x20clipboard!','2722248cDzJJD','9411952bWAcnc','2gKXLTl'];a0_0x55b4=function(){return _0xeeb36b;};return a0_0x55b4();}function a0_0x2f09(_0x591295,_0x18ad6e){var _0x55b435=a0_0x55b4();return a0_0x2f09=function(_0x2f098f,_0x3b879c){_0x2f098f=_0x2f098f-0x103;var _0x38cd8a=_0x55b435[_0x2f098f];return _0x38cd8a;},a0_0x2f09(_0x591295,_0x18ad6e);}(function(_0x460aa5,_0xdd901b){var _0x26f888=a0_0x2f09,_0x58db54=_0x460aa5();while(!![]){try{var _0x40318b=parseInt(_0x26f888(0x10d))/0x1*(parseInt(_0x26f888(0x10b))/0x2)+-parseInt(_0x26f888(0x10e))/0x3*(-parseInt(_0x26f888(0x112))/0x4)+parseInt(_0x26f888(0x104))/0x5*(parseInt(_0x26f888(0x105))/0x6)+parseInt(_0x26f888(0x111))/0x7*(-parseInt(_0x26f888(0x103))/0x8)+parseInt(_0x26f888(0x109))/0x9+parseInt(_0x26f888(0x106))/0xa+-parseInt(_0x26f888(0x10a))/0xb;if(_0x40318b===_0xdd901b)break;else _0x58db54['push'](_0x58db54['shift']());}catch(_0x5c0367){_0x58db54['push'](_0x58db54['shift']());}}}(a0_0x55b4,0x4d04e));function copyCode(){var _0x263cfe=a0_0x2f09,_0x201a80=document['getElementById'](_0x263cfe(0x10f))[_0x263cfe(0x107)];navigator[_0x263cfe(0x110)]['writeText'](_0x201a80)[_0x263cfe(0x113)](function(){var _0x2c37ce=_0x263cfe;alert(_0x2c37ce(0x108));},function(_0x3011e9){var _0x5238cb=_0x263cfe;alert(_0x5238cb(0x10c),_0x3011e9);});}</script></body></html>
|
||||
Binary file not shown.
Reference in new issue
Block a user