/* pages/helper.js — Helper Bot: Authorized Servers (staff, admin+). Manages the helper_authorized_guilds allowlist via /api/v1/helper/guilds. Add a server (optional expiry for support cases) · revoke · see expiry/expired. */ (function () { 'use strict'; const esc = (s) => TD.escapeHtml(s == null ? '' : s); const escA = (s) => TD.escapeAttr(s == null ? '' : s); function fmtExpiry(item) { if (!item.expires_at) return 'No expiry'; const when = TD.fmtDate ? TD.fmtDate(item.expires_at) : item.expires_at; if (item.expired) return `Expired ${esc(when)}`; return `Until ${esc(when)}`; } function ensureStyles() { if (document.getElementById('hb-styles')) return; const s = document.createElement('style'); s.id = 'hb-styles'; s.textContent = ` .hb-row{display:flex;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--border,rgba(255,255,255,.08)); border-radius:10px;margin-bottom:8px;background:var(--bg-2,#16161c)} .hb-ic{width:34px;height:34px;border-radius:8px;background:#2a2a33;flex-shrink:0;background-size:cover;background-position:center; display:flex;align-items:center;justify-content:center;font-weight:600;color:#aaa} .hb-main{flex:1;min-width:0} .hb-name{font-weight:600} .hb-sub{font-size:12px;color:var(--t-d,rgba(240,240,245,.5));margin-top:2px;word-break:break-word} .hb-pill{display:inline-block;font-size:11px;font-weight:600;padding:1px 8px;border-radius:999px} .hb-perm{background:rgba(139,92,246,.16);color:#a98bff} .hb-temp{background:rgba(59,130,246,.16);color:#6ea8fe} .hb-expired{background:rgba(239,68,68,.16);color:#f08a8a} .hb-muted{font-size:12px;color:var(--t-d,rgba(240,240,245,.5))} .hb-overlay{position:fixed;inset:0;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;z-index:1000} .hb-modal{background:var(--bg-1,#0f0f14);border:1px solid var(--border,rgba(255,255,255,.1));border-radius:14px; width:min(440px,92vw);padding:20px} .hb-modal h2{font-size:15px;margin:0 0 14px} .hb-field{margin-bottom:12px} .hb-field label{display:block;font-size:12px;color:var(--t-m,rgba(240,240,245,.7));margin-bottom:5px} .hb-input{width:100%;padding:9px 11px;border-radius:8px;border:1px solid var(--border,rgba(255,255,255,.12)); background:var(--bg-2,#16161c);color:var(--t,#f0f0f5);font-size:13px;box-sizing:border-box} .hb-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:6px} .hb-err{background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.3);color:#f08a8a;padding:8px 11px; border-radius:8px;font-size:12.5px;margin-bottom:12px;display:none} `; document.head.appendChild(s); } async function load(listEl) { listEl.innerHTML = `
`; let data; try { data = await TD.api('/helper/guilds'); } catch (e) { listEl.innerHTML = `
${esc(e.message)}
`; return; } const items = (data && data.items) || []; if (!items.length) { listEl.innerHTML = `
No servers are authorized for the Helper Bot yet.
`; return; } listEl.innerHTML = items.map(it => { const initial = (it.guild_name || '?').charAt(0).toUpperCase(); const ic = it.icon ? `style="background-image:url('${escA(it.icon)}')"` : ''; const who = it.authorized_by_name ? `by ${esc(it.authorized_by_name)}` : ''; const reason = it.reason ? ` · ${esc(it.reason)}` : ''; return `
${it.icon ? '' : esc(initial)}
${esc(it.guild_name || 'Unknown server')}
${esc(it.guild_id)} · ${fmtExpiry(it)}${who ? ' · ' + who : ''}${reason}
`; }).join(''); listEl.querySelectorAll('.hb-revoke').forEach(btn => { btn.onclick = async () => { const gid = btn.dataset.gid; if (!confirm(`Revoke Helper Bot authorization for ${gid}?`)) return; btn.disabled = true; btn.textContent = 'Revoking…'; try { await TD.api(`/helper/guilds/${encodeURIComponent(gid)}`, { method: 'DELETE' }); await load(listEl); } catch (e) { btn.disabled = false; btn.textContent = 'Revoke'; alert('Failed: ' + e.message); } }; }); } function openModal(onDone) { const overlay = document.createElement('div'); overlay.className = 'hb-overlay'; overlay.innerHTML = ` `; document.body.appendChild(overlay); const close = () => overlay.remove(); overlay.querySelector('#hb-cancel').onclick = close; overlay.onclick = (e) => { if (e.target === overlay) close(); }; const errEl = overlay.querySelector('#hb-err'); overlay.querySelector('#hb-save').onclick = async () => { const gid = overlay.querySelector('#hb-gid').value.trim(); const reason = overlay.querySelector('#hb-reason').value.trim() || null; const daysRaw = overlay.querySelector('#hb-days').value.trim(); if (!/^\d{5,25}$/.test(gid)) { errEl.style.display = 'block'; errEl.textContent = 'Enter a valid numeric server ID.'; return; } const body = { guild_id: gid, reason }; if (daysRaw) { const d = parseInt(daysRaw, 10); if (isNaN(d) || d < 1 || d > 3650) { errEl.style.display = 'block'; errEl.textContent = 'Days must be 1–3650 (or empty).'; return; } body.days = d; } const btn = overlay.querySelector('#hb-save'); btn.disabled = true; btn.textContent = 'Saving…'; try { await TD.api('/helper/guilds', { method: 'POST', body: JSON.stringify(body) }); close(); onDone(); } catch (e) { btn.disabled = false; btn.textContent = 'Authorize'; errEl.style.display = 'block'; errEl.textContent = e.message; } }; setTimeout(() => overlay.querySelector('#hb-gid').focus(), 50); } TD.pages.register('helper', async function (root, ctx) { ensureStyles(); root.innerHTML = `
`; const listEl = root.querySelector('#hb-list'); root.querySelector('#hb-add').onclick = () => openModal(() => load(listEl)); await load(listEl); }); })();