chat widget cleanup

This commit is contained in:
2025-10-09 22:06:29 -04:00
parent 7a165d2cf4
commit 36f1a0d86d
5 changed files with 1762 additions and 181 deletions

View File

@@ -77,11 +77,11 @@
</div>
<script src="http://localhost:9000/nostr-chat-widget.js"
<script src="https://btcforplebs.com/nostr-chat-widget-mini.js"
data-nostr-pubkey="75462f4dece4fbde54a535cfa09eb0d329bda090a9c2f9ed6b5f9d1d2fb6c15b"
data-brand-name="Chat with BTCforPlebs"
data-color="#8e30eb"
data-color-secondary="#fdad01">
data-color="#fdad01"
data-color-secondary="#222222">
</script>
<script>

View File

@@ -1,15 +1,13 @@
/**
* Nostr Chat Widget - Embeddable Version
* Nostr Chat Widget - Embeddable Version (Glassmorphism Design)
*
* HOST THIS FILE on your server (e.g., https://btcforplebs.com/chat-widget.js)
*
* USERS EMBED IT WITH:
* <script src="https://btcforplebs.com/nostr-chat-widget.js"
data-nostr-pubkey="YOUR_PUBKEY"
data-brand-name="My Company"
data-color="#8e30eb">
data-color-secondary="#ff8c00">
</script>
* EMBED IT WITH:
* <script src="https://btcforplebs.com/nostr-chat-widget.js"
* data-nostr-pubkey="YOUR_PUBKEY_HEX_FORMAT"
* data-brand-name="My Company"
* data-color="#8e30eb"
* data-color-secondary="#ff8c00">
* </script>
*/
(function() {
@@ -18,35 +16,79 @@
// Get configuration from script tag
const scriptTag = document.currentScript;
const csPubkey = scriptTag.getAttribute('data-nostr-pubkey') || 'PUBKEY_TO_RECEICE_MESSAGES';
const brandName = scriptTag.getAttribute('data-brand-name') || 'Support Team';
const primaryColor = scriptTag.getAttribute('data-color-primary') || '#fdad01';
const secondaryColor = scriptTag.getAttribute('data-color-secondary') || '#ff8c00';
const brandName = scriptTag.getAttribute('data-brand-name') || 'Support Team Messaging';
const primaryColor = scriptTag.getAttribute('data-color') || '#fdad01';
const secondaryColor = scriptTag.getAttribute('data-color-secondary') || '#000000';
// Default relay configuration
const DEFAULT_RELAYS = [
'wss://relay.damus.io',
'wss://relay.primal.net',
'wss://nos.lol',
'wss://relay.btcforplebs.com',
'wss://relay.logemedia.com'
'wss://relay.btcforplebs.com'
];
// Inject Tailwind CSS
const tailwindLink = document.createElement('link');
tailwindLink.href = 'https://cdn.tailwindcss.com';
tailwindLink.rel = 'stylesheet';
document.head.appendChild(tailwindLink);
// Add viewport meta tag for mobile optimization
let viewportMeta = document.querySelector('meta[name="viewport"]');
if (!viewportMeta) {
viewportMeta = document.createElement('meta');
viewportMeta.name = 'viewport';
viewportMeta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';
document.head.appendChild(viewportMeta);
}
// Inject custom styles
// Inject Tailwind CSS
const tailwindScript = document.createElement('script');
tailwindScript.src = 'https://cdn.tailwindcss.com';
document.head.appendChild(tailwindScript);
// Inject custom styles with glassmorphism
const style = document.createElement('style');
style.textContent = `
.safe-area-bottom {
padding-bottom: max(1rem, env(safe-area-inset-bottom)) !important;
padding-bottom: env(safe-area-inset-bottom);
}
#nostr-chat-widget-root > div {
pointer-events: auto !important;
z-index: 99999 !important;
}
.glass-morphism {
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.15);
}
.glass-morphism-light {
background: rgba(255, 255, 255, 0.03);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.glass-input {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.3);
}
.glass-input:focus {
background: rgba(255, 255, 255, 0.25);
border: 1px solid rgba(255, 255, 255, 0.4);
}
.glass-input::placeholder {
color: rgba(255, 255, 255, 0.6);
}
@media (max-width: 640px) {
#nostr-chat-widget-root .chat-window-mobile {
position: fixed !important;
top: 0 !important;
left: 0 !important;
right: 0 !important;
bottom: 0 !important;
width: 100% !important;
height: 100% !important;
max-height: 100vh !important;
border-radius: 0 !important;
}
}
`;
document.head.appendChild(style);
@@ -83,7 +125,8 @@
relays: ${JSON.stringify(DEFAULT_RELAYS)},
csPubkey: '${csPubkey}',
brandName: '${brandName}',
primaryColor: '${primaryColor}'
primaryColor: '${primaryColor}',
secondaryColor: '${secondaryColor}'
};
let state = {
@@ -243,6 +286,18 @@
if (!state.inputMessage.trim()) return;
const messageText = state.inputMessage;
state.inputMessage = '';
// Show message immediately (optimistic UI)
const tempMessage = {
id: 'temp_' + Date.now(),
text: messageText,
sender: 'user',
timestamp: new Date().toISOString()
};
state.messages.push(tempMessage);
render();
scrollToBottom();
try {
console.log('🔐 Encrypting and sending...');
@@ -282,16 +337,12 @@
console.log(\`✓ Published to \${published}/\${state.relays.length} relays\`);
const message = {
id: event.id,
text: messageText,
sender: 'user',
timestamp: new Date().toISOString()
};
addMessage('user', messageText, message);
state.inputMessage = '';
render();
// Update temp message with real ID
const msgIndex = state.messages.findIndex(m => m.id === tempMessage.id);
if (msgIndex !== -1) {
state.messages[msgIndex].id = event.id;
}
saveMessages();
} catch (error) {
console.error('Error sending:', error);
@@ -346,7 +397,8 @@
container.innerHTML = \`
<div class="fixed bottom-4 right-4 sm:bottom-6 sm:right-6 z-[99999]">
<button onclick="window.NostrChat.open()"
class="bg-gradient-to-br from-[\${CONFIG.primaryColor}] to-[#ff8c00] hover:from-[#ff8c00] hover:to-[\${CONFIG.primaryColor}] text-white rounded-full p-4 sm:p-5 shadow-2xl transition-all transform hover:scale-110 active:scale-95"
style="background: linear-gradient(to bottom right, \${CONFIG.primaryColor}, \${CONFIG.secondaryColor});"
class="hover:opacity-90 text-white rounded-full p-4 sm:p-5 shadow-2xl transition-all transform hover:scale-110 active:scale-95"
aria-label="Open chat"
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="sm:w-7 sm:h-7">
@@ -359,15 +411,15 @@
}
container.innerHTML = \`
<div class="fixed inset-4 sm:inset-auto sm:bottom-6 sm:right-6 z-[99999]">
<div class="bg-white rounded-2xl shadow-2xl w-full h-full sm:w-96 sm:h-[600px] max-w-full flex flex-col overflow-hidden">
<div class="bg-gradient-to-br from-[\${CONFIG.primaryColor}] to-[#ff8c00] text-white p-4 sm:p-5">
<div class="fixed inset-0 sm:inset-auto sm:bottom-6 sm:right-6 z-[99999]">
<div class="glass-morphism chat-window-mobile rounded-none sm:rounded-2xl shadow-2xl w-full h-full sm:w-96 sm:h-[600px] max-w-full flex flex-col overflow-hidden">
<div style="background: linear-gradient(to bottom right, \${CONFIG.primaryColor}, \${CONFIG.secondaryColor});" class="text-white p-3.5 sm:p-4">
<div class="flex justify-between items-center">
<div class="flex-1 min-w-0">
<h3 class="font-bold text-base sm:text-lg">\${CONFIG.brandName}</h3>
<div class="flex items-center gap-2 mt-1">
<div class="flex items-center gap-2 mt-0.5">
<div class="w-2 h-2 rounded-full flex-shrink-0 \${state.connected ? 'bg-green-400 animate-pulse' : 'bg-red-400'}"></div>
<span class="text-xs text-orange-100 truncate">
<span class="text-xs text-white/80 truncate">
\${state.connected ? \`P2P E2EE • \${state.relays.length} relays\` : 'Connecting...'}
</span>
</div>
@@ -385,9 +437,9 @@
</div>
</div>
<div id="nostr-messages" class="flex-1 overflow-y-auto p-3 sm:p-4 space-y-3 bg-gradient-to-b from-gray-50 to-white">
<div id="nostr-messages" class="flex-1 overflow-y-auto p-3 sm:p-3.5 space-y-3 glass-morphism-light">
\${state.messages.length === 0 ? \`
<div class="text-center text-gray-400 mt-8">
<div class="text-center text-white/60 mt-8">
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" class="mx-auto mb-3 opacity-50">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
@@ -397,7 +449,7 @@
if (msg.sender === 'system') {
return \`
<div class="flex justify-center">
<div class="bg-orange-50 text-orange-700 text-xs px-3 py-2 rounded-full border border-orange-200">
<div class="bg-orange-100/40 backdrop-blur-sm text-orange-800 text-xs px-3 py-2 rounded-full border border-orange-300/50">
\${escapeHtml(msg.text)}
</div>
</div>
@@ -406,10 +458,10 @@
return \`
<div class="flex justify-end">
<div class="max-w-[85%] sm:max-w-xs">
<div class="bg-gradient-to-br from-[\${CONFIG.primaryColor}] to-[#ff8c00] text-white rounded-2xl rounded-tr-sm px-3 py-2 sm:px-4 sm:py-3 shadow-md text-sm sm:text-base">
<div style="background: linear-gradient(to bottom right, \${CONFIG.primaryColor}, \${CONFIG.secondaryColor});" class="text-white rounded-2xl rounded-tr-sm px-3 py-2 sm:px-4 sm:py-3 shadow-md text-sm sm:text-base">
\${escapeHtml(msg.text)}
</div>
<div class="text-xs text-gray-400 mt-1 text-right">\${formatTime(msg.timestamp)}</div>
<div class="text-xs text-white/60 mt-1 text-right">Sent \${formatTime(msg.timestamp)}</div>
</div>
</div>
\`;
@@ -417,11 +469,10 @@
return \`
<div class="flex justify-start">
<div class="max-w-[85%] sm:max-w-xs">
<div class="bg-white border border-gray-200 text-gray-800 rounded-2xl rounded-tl-sm px-3 py-2 sm:px-4 sm:py-3 shadow-md text-sm sm:text-base">
<div class="text-xs font-semibold" style="color: \${CONFIG.primaryColor}">\${CONFIG.brandName}</div>
<div style="background: linear-gradient(to bottom right, #9ca3af, #6b7280);" class="text-white rounded-2xl rounded-tl-sm px-3 py-2 sm:px-4 sm:py-3 shadow-md text-sm sm:text-base">
\${escapeHtml(msg.text)}
</div>
<div class="text-xs text-gray-400 mt-1">\${formatTime(msg.timestamp)}</div>
<div class="text-xs text-white/60 mt-1">\${formatTime(msg.timestamp)}</div>
</div>
</div>
\`;
@@ -430,21 +481,21 @@
}).join('')}
</div>
<div class="border-t bg-white p-3 sm:p-4 pb-safe">
<div class="flex gap-2">
<div class="p-3 sm:p-3.5 mb-2 flex-shrink-0 safe-area-bottom">
<div class="glass-input rounded-xl p-2 flex gap-2 items-center">
<input
id="nostr-message-input"
type="text"
value="\${escapeHtml(state.inputMessage)}"
placeholder="Type your message..."
class="flex-1 px-3 py-2 sm:px-4 sm:py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 text-sm sm:text-base"
style="focus:ring-color: \${CONFIG.primaryColor}"
class="flex-1 bg-transparent px-2 py-1.5 focus:outline-none text-sm sm:text-base text-white placeholder-white/60"
\${!state.connected ? 'disabled' : ''}
>
<button
onclick="window.NostrChat.send()"
\${!state.connected || !state.inputMessage.trim() ? 'disabled' : ''}
class="bg-gradient-to-br from-[\${CONFIG.primaryColor}] to-[#ff8c00] hover:from-[#ff8c00] hover:to-[\${CONFIG.primaryColor}] disabled:from-gray-400 disabled:to-gray-400 text-white px-4 py-2 sm:px-5 sm:py-3 rounded-xl transition-all disabled:cursor-not-allowed active:scale-95 flex-shrink-0"
style="background: linear-gradient(to bottom right, \${CONFIG.primaryColor}, \${CONFIG.secondaryColor});"
class="hover:opacity-90 disabled:opacity-40 text-white p-2 rounded-lg transition-all disabled:cursor-not-allowed active:scale-95 flex-shrink-0"
aria-label="Send message"
>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -508,7 +559,6 @@
document.body.appendChild(widgetScript);
})();
/*!
* fill-range <https://github.com/jonschlinkert/fill-range>
*
@@ -594,4 +644,4 @@ Check your Browserslist config to be sure that your targets are set up correctly
https://github.com/postcss/autoprefixer#readme
https://github.com/browserslist/browserslist#readme
`))}gv.exports=Dr;function Dr(...r){let e;if(r.length===1&&V5(r[0])?(e=r[0],r=void 0):r.length===0||r.length===1&&!r[0]?r=void 0:r.length<=2&&(Array.isArray(r[0])||!r[0])?(e=r[1],r=r[0]):typeof r[r.length-1]=="object"&&(e=r.pop()),e||(e={}),e.browser)throw new Error("Change `browser` option to `overrideBrowserslist` in Autoprefixer");if(e.browserslist)throw new Error("Change `browserslist` option to `overrideBrowserslist` in Autoprefixer");e.overrideBrowserslist?r=e.overrideBrowserslist:e.browsers&&(typeof console!="undefined"&&console.warn&&(of.red?console.warn(of.red(mv.replace(/`[^`]+`/g,n=>of.yellow(n.slice(1,-1))))):console.warn(mv)),r=e.browsers);let t={ignoreUnknownVersions:e.ignoreUnknownVersions,stats:e.stats,env:e.env};function i(n){let s=hv,a=new F5(s.browsers,r,n,t),o=a.selected.join(", ")+JSON.stringify(e);return lf.has(o)||lf.set(o,new j5(s.prefixes,a,e)),lf.get(o)}return{postcssPlugin:"autoprefixer",prepare(n){let s=i({from:n.opts.from,env:e.env});return{OnceExit(a){H5(n,s),e.remove!==!1&&s.processor.remove(a,n),e.add!==!1&&s.processor.add(a,n)}}},info(n){return n=n||{},n.from=n.from||m.cwd(),U5(i(n))},options:e,browsers:r}}Dr.postcss=!0;Dr.data=hv;Dr.defaults=N5.defaults;Dr.info=()=>Dr().info()});var bv={};Ge(bv,{default:()=>W5});var W5,wv=P(()=>{u();W5=[]});var xv={};Ge(xv,{default:()=>G5});var vv,G5,kv=P(()=>{u();Xi();vv=pe(rn()),G5=St(vv.default.theme)});var Av={};Ge(Av,{default:()=>Q5});var Sv,Q5,Cv=P(()=>{u();Xi();Sv=pe(rn()),Q5=St(Sv.default)});u();"use strict";var Y5=vt(_y()),K5=vt($e()),X5=vt(yv()),Z5=vt((wv(),bv)),J5=vt((kv(),xv)),eP=vt((Cv(),Av)),tP=vt((Vs(),_f)),rP=vt((al(),sl)),iP=vt((sa(),sc));function vt(r){return r&&r.__esModule?r:{default:r}}console.warn("cdn.tailwindcss.com should not be used in production. To use Tailwind CSS in production, install it as a PostCSS plugin or use the Tailwind CLI: https://tailwindcss.com/docs/installation");var Ns="tailwind",uf="text/tailwindcss",_v="/template.html",Yt,Ev=!0,Ov=0,ff=new Set,cf,Tv="",Rv=(r=!1)=>({get(e,t){return(!r||t==="config")&&typeof e[t]=="object"&&e[t]!==null?new Proxy(e[t],Rv()):e[t]},set(e,t,i){return e[t]=i,(!r||t==="config")&&pf(!0),!0}});window[Ns]=new Proxy({config:{},defaultTheme:J5.default,defaultConfig:eP.default,colors:tP.default,plugin:rP.default,resolveConfig:iP.default},Rv(!0));function Pv(r){cf.observe(r,{attributes:!0,attributeFilter:["type"],characterData:!0,subtree:!0,childList:!0})}new MutationObserver(async r=>{let e=!1;if(!cf){cf=new MutationObserver(async()=>await pf(!0));for(let t of document.querySelectorAll(`style[type="${uf}"]`))Pv(t)}for(let t of r)for(let i of t.addedNodes)i.nodeType===1&&i.tagName==="STYLE"&&i.getAttribute("type")===uf&&(Pv(i),e=!0);await pf(e)}).observe(document.documentElement,{attributes:!0,attributeFilter:["class"],childList:!0,subtree:!0});async function pf(r=!1){r&&(Ov++,ff.clear());let e="";for(let i of document.querySelectorAll(`style[type="${uf}"]`))e+=i.textContent;let t=new Set;for(let i of document.querySelectorAll("[class]"))for(let n of i.classList)ff.has(n)||t.add(n);if(document.body&&(Ev||t.size>0||e!==Tv||!Yt||!Yt.isConnected)){for(let n of t)ff.add(n);Ev=!1,Tv=e,self[_v]=Array.from(t).join(" ");let{css:i}=await(0,K5.default)([(0,Y5.default)({...window[Ns].config,_hash:Ov,content:{files:[_v],extract:{html:n=>n.split(" ")}},plugins:[...Z5.default,...Array.isArray(window[Ns].config.plugins)?window[Ns].config.plugins:[]]}),(0,X5.default)({remove:!1})]).process(`@tailwind base;@tailwind components;@tailwind utilities;${e}`);(!Yt||!Yt.isConnected)&&(Yt=document.createElement("style"),document.head.append(Yt)),Yt.textContent=i}}})();
`))}gv.exports=Dr;function Dr(...r){let e;if(r.length===1&&V5(r[0])?(e=r[0],r=void 0):r.length===0||r.length===1&&!r[0]?r=void 0:r.length<=2&&(Array.isArray(r[0])||!r[0])?(e=r[1],r=r[0]):typeof r[r.length-1]=="object"&&(e=r.pop()),e||(e={}),e.browser)throw new Error("Change `browser` option to `overrideBrowserslist` in Autoprefixer");if(e.browserslist)throw new Error("Change `browserslist` option to `overrideBrowserslist` in Autoprefixer");e.overrideBrowserslist?r=e.overrideBrowserslist:e.browsers&&(typeof console!="undefined"&&console.warn&&(of.red?console.warn(of.red(mv.replace(/`[^`]+`/g,n=>of.yellow(n.slice(1,-1))))):console.warn(mv)),r=e.browsers);let t={ignoreUnknownVersions:e.ignoreUnknownVersions,stats:e.stats,env:e.env};function i(n){let s=hv,a=new F5(s.browsers,r,n,t),o=a.selected.join(", ")+JSON.stringify(e);return lf.has(o)||lf.set(o,new j5(s.prefixes,a,e)),lf.get(o)}return{postcssPlugin:"autoprefixer",prepare(n){let s=i({from:n.opts.from,env:e.env});return{OnceExit(a){H5(n,s),e.remove!==!1&&s.processor.remove(a,n),e.add!==!1&&s.processor.add(a,n)}}},info(n){return n=n||{},n.from=n.from||m.cwd(),U5(i(n))},options:e,browsers:r}}Dr.postcss=!0;Dr.data=hv;Dr.defaults=N5.defaults;Dr.info=()=>Dr().info()});var bv={};Ge(bv,{default:()=>W5});var W5,wv=P(()=>{u();W5=[]});var xv={};Ge(xv,{default:()=>G5});var vv,G5,kv=P(()=>{u();Xi();vv=pe(rn()),G5=St(vv.default.theme)});var Av={};Ge(Av,{default:()=>Q5});var Sv,Q5,Cv=P(()=>{u();Xi();Sv=pe(rn()),Q5=St(Sv.default)});u();"use strict";var Y5=vt(_y()),K5=vt($e()),X5=vt(yv()),Z5=vt((wv(),bv)),J5=vt((kv(),xv)),eP=vt((Cv(),Av)),tP=vt((Vs(),_f)),rP=vt((al(),sl)),iP=vt((sa(),sc));function vt(r){return r&&r.__esModule?r:{default:r}}console.warn("cdn.tailwindcss.com should not be used in production. To use Tailwind CSS in production, install it as a PostCSS plugin or use the Tailwind CLI: https://tailwindcss.com/docs/installation");var Ns="tailwind",uf="text/tailwindcss",_v="/template.html",Yt,Ev=!0,Ov=0,ff=new Set,cf,Tv="",Rv=(r=!1)=>({get(e,t){return(!r||t==="config")&&typeof e[t]=="object"&&e[t]!==null?new Proxy(e[t],Rv()):e[t]},set(e,t,i){return e[t]=i,(!r||t==="config")&&pf(!0),!0}});window[Ns]=new Proxy({config:{},defaultTheme:J5.default,defaultConfig:eP.default,colors:tP.default,plugin:rP.default,resolveConfig:iP.default},Rv(!0));function Pv(r){cf.observe(r,{attributes:!0,attributeFilter:["type"],characterData:!0,subtree:!0,childList:!0})}new MutationObserver(async r=>{let e=!1;if(!cf){cf=new MutationObserver(async()=>await pf(!0));for(let t of document.querySelectorAll(`style[type="${uf}"]`))Pv(t)}for(let t of r)for(let i of t.addedNodes)i.nodeType===1&&i.tagName==="STYLE"&&i.getAttribute("type")===uf&&(Pv(i),e=!0);await pf(e)}).observe(document.documentElement,{attributes:!0,attributeFilter:["class"],childList:!0,subtree:!0});async function pf(r=!1){r&&(Ov++,ff.clear());let e="";for(let i of document.querySelectorAll(`style[type="${uf}"]`))e+=i.textContent;let t=new Set;for(let i of document.querySelectorAll("[class]"))for(let n of i.classList)ff.has(n)||t.add(n);if(document.body&&(Ev||t.size>0||e!==Tv||!Yt||!Yt.isConnected)){for(let n of t)ff.add(n);Ev=!1,Tv=e,self[_v]=Array.from(t).join(" ");let{css:i}=await(0,K5.default)([(0,Y5.default)({...window[Ns].config,_hash:Ov,content:{files:[_v],extract:{html:n=>n.split(" ")}},plugins:[...Z5.default,...Array.isArray(window[Ns].config.plugins)?window[Ns].config.plugins:[]]}),(0,X5.default)({remove:!1})]).process(`@tailwind base;@tailwind components;@tailwind utilities;${e}`);(!Yt||!Yt.isConnected)&&(Yt=document.createElement("style"),document.head.append(Yt)),Yt.textContent=i}}})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -45,7 +45,7 @@
<p>Follow BTCforPlebs on <a href="https://nostrudel.btcforplebs.com/u/npub1w4rz7n0vunaau499xh86p84s6v5mmgys48p0nmttt7w36takc9dsf4382j">Nostr</a> or <a href="https://youtube.com/@btcforplebs">YouTube</a> for the latest</p>
</div>
<script src="https://btcforplebs.com/nostr-chat-widget.js"
<script src="https://btcforplebs.com/nostr-chat-widget-mini.js"
data-nostr-pubkey="75462f4dece4fbde54a535cfa09eb0d329bda090a9c2f9ed6b5f9d1d2fb6c15b"
data-brand-name="Chat with BTCforPlebs"
data-color="#8e30eb"