330 lines
11 KiB
JavaScript
330 lines
11 KiB
JavaScript
/**
|
|
* AKSRT Credits Frontend
|
|
*
|
|
* 功能:
|
|
* - 签到 panel:拉取 /signin/today,按钮触发 POST /signin
|
|
* - 邀请 panel:拉取 /invite/me,复制邀请链接
|
|
*
|
|
* 仅在 page-profile 页面 + 启用积分时加载。
|
|
*/
|
|
(function () {
|
|
'use strict';
|
|
|
|
var aksrt = window.aksrtData;
|
|
if (!aksrt || !aksrt.siteUrl) {
|
|
return;
|
|
}
|
|
|
|
// 协议修正:页面是 HTTPS 但 siteUrl 是 HTTP 时,强制使用 HTTPS(避免混合内容拦截)
|
|
var siteUrl = aksrt.siteUrl;
|
|
if (window.location.protocol === 'https:' && siteUrl.substring(0, 5) === 'http:') {
|
|
siteUrl = 'https:' + siteUrl.substring(5);
|
|
}
|
|
|
|
var REST = siteUrl.replace(/\/$/, '') + '/wp-json/aksrt/v1';
|
|
var NONCE_HEADER = aksrt.restNonce ? { 'X-WP-Nonce': aksrt.restNonce } : (aksrt.nonce ? { 'X-WP-Nonce': aksrt.nonce } : {});
|
|
|
|
function api(path, options) {
|
|
var merged = Object.assign({
|
|
credentials: 'same-origin',
|
|
headers: Object.assign({ 'Content-Type': 'application/json' }, NONCE_HEADER),
|
|
}, options || {});
|
|
return fetch(REST + path, merged).then(function (r) {
|
|
// 401 时去掉 nonce 重试(页面缓存可能导致 nonce 过期,GET 请求可用 cookie 认证)
|
|
if (r.status === 401 && (!options || !options.method || options.method === 'GET')) {
|
|
var retry = Object.assign({}, merged, {
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
return fetch(REST + path, retry).then(function (r2) {
|
|
return r2.json().catch(function () {
|
|
return { message: '服务器返回了非 JSON 响应 (HTTP ' + r2.status + ')' };
|
|
}).then(function (j) { return { ok: r2.ok, status: r2.status, json: j }; });
|
|
});
|
|
}
|
|
return r.json().catch(function () {
|
|
return { message: '服务器返回了非 JSON 响应 (HTTP ' + r.status + ')' };
|
|
}).then(function (j) { return { ok: r.ok, status: r.status, json: j }; });
|
|
});
|
|
}
|
|
|
|
// ===== 签到 =====
|
|
function initSignin() {
|
|
var root = document.querySelector('[data-aksrt-signin]');
|
|
if (!root) return;
|
|
var status = root.querySelector('[data-signin-status]');
|
|
var btn = root.querySelector('[data-signin-btn]');
|
|
var tip = root.querySelector('[data-signin-tip]');
|
|
if (!btn) return;
|
|
|
|
function setStatus(text) { if (status) status.textContent = text; }
|
|
function setStatusHtml(html) { if (status) status.innerHTML = html; }
|
|
function setTip(text) { if (tip) tip.textContent = text; }
|
|
|
|
function refresh() {
|
|
api('/signin/today').then(function (r) {
|
|
if (!r.ok) {
|
|
setStatus((r.json && r.json.message) || '加载失败 (HTTP ' + r.status + ')');
|
|
return;
|
|
}
|
|
var j = r.json;
|
|
if (!j.enabled) {
|
|
setStatus('签到功能未启用');
|
|
btn.disabled = true;
|
|
setTip('');
|
|
return;
|
|
}
|
|
if (j.signed) {
|
|
setStatusHtml('已签到 <strong>' + j.streak + '</strong> 天 +' + j.reward_today);
|
|
btn.disabled = true;
|
|
btn.innerHTML = '已签到';
|
|
setTip('');
|
|
} else {
|
|
setStatusHtml('连续 <strong>' + j.streak + '</strong> 天,+' + j.reward_if_signin);
|
|
btn.disabled = false;
|
|
setTip('');
|
|
}
|
|
}).catch(function (err) {
|
|
if (window.console) console.error('[AKSRT Credits] /signin/today 请求失败:', err);
|
|
setStatus('加载失败,请刷新重试');
|
|
});
|
|
}
|
|
|
|
function collectCaptcha() {
|
|
var el = document.querySelector('[data-signin-captcha]');
|
|
if (!el) return {};
|
|
var out = {};
|
|
var keys = [
|
|
'captcha_verified', 'cf_turnstile_response',
|
|
'tencent_ticket', 'tencent_randstr', 'tencent_sid',
|
|
'geetest_lot_number', 'geetest_captcha_output', 'geetest_pass_token', 'geetest_gen_time',
|
|
'aliyun_captcha_verify_param',
|
|
];
|
|
keys.forEach(function (k) {
|
|
var inp = el.querySelector('input[name="' + k + '"]');
|
|
if (inp && inp.value !== '') out[k] = inp.value;
|
|
});
|
|
return out;
|
|
}
|
|
|
|
function resetCaptcha() {
|
|
try {
|
|
var state = window.AKSRTCaptchaState;
|
|
var scene = state && state.scenes && state.scenes.signin;
|
|
if (scene && typeof scene.reset === 'function') scene.reset();
|
|
} catch (_) {}
|
|
}
|
|
|
|
function triggerCaptcha(callback) {
|
|
var state = window.AKSRTCaptchaState;
|
|
var scene = state && state.scenes && state.scenes.signin;
|
|
if (scene && typeof scene.verify === 'function') {
|
|
scene.verify(callback);
|
|
return;
|
|
}
|
|
callback();
|
|
}
|
|
|
|
btn.addEventListener('click', function () {
|
|
btn.disabled = true;
|
|
btn.innerHTML = '…';
|
|
setTip('');
|
|
|
|
var captchaDone = false;
|
|
var timeoutId = setTimeout(function () {
|
|
if (!captchaDone) {
|
|
captchaDone = true;
|
|
doSignin();
|
|
}
|
|
}, 30000);
|
|
|
|
triggerCaptcha(function () {
|
|
clearTimeout(timeoutId);
|
|
if (captchaDone) return;
|
|
captchaDone = true;
|
|
doSignin();
|
|
});
|
|
|
|
function doSignin() {
|
|
api('/signin', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ captcha: collectCaptcha() })
|
|
}).then(function (r) {
|
|
if (!r.ok) {
|
|
resetCaptcha();
|
|
setTip((r.json && r.json.message) || '签到失败');
|
|
btn.disabled = false;
|
|
btn.innerHTML = '签到';
|
|
return;
|
|
}
|
|
setTip('签到成功,获得 ' + r.json.reward + ' 积分(连续 ' + r.json.streak + ' 天)');
|
|
var balanceEls = document.querySelectorAll('[data-credits-value]');
|
|
balanceEls.forEach(function (el) { el.textContent = r.json.balance.toLocaleString(); });
|
|
refresh();
|
|
}).catch(function () {
|
|
resetCaptcha();
|
|
setTip('网络错误,请重试');
|
|
btn.disabled = false;
|
|
btn.innerHTML = '签到';
|
|
});
|
|
}
|
|
});
|
|
|
|
refresh();
|
|
}
|
|
|
|
// ===== 邀请 =====
|
|
function initInvite() {
|
|
var root = document.querySelector('[data-aksrt-invite]');
|
|
if (!root) return;
|
|
var linkInput = root.querySelector('[data-invite-link]');
|
|
var copyBtn = root.querySelector('[data-invite-copy]');
|
|
var stats = root.querySelector('[data-invite-stats]');
|
|
var codeEl = root.querySelector('[data-invite-code]');
|
|
var listEl = root.querySelector('[data-invite-list]');
|
|
var totalEl = root.querySelector('[data-invite-total]');
|
|
|
|
function escapeHtml(value) {
|
|
return String(value == null ? '' : value)
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
function formatDate(value) {
|
|
if (!value || value === '0000-00-00 00:00:00') return '';
|
|
var date = new Date(String(value).replace(' ', 'T') + 'Z');
|
|
if (isNaN(date.getTime())) return String(value).slice(0, 16);
|
|
function pad(n) { return n < 10 ? '0' + n : String(n); }
|
|
return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + pad(date.getDate()) + ' ' + pad(date.getHours()) + ':' + pad(date.getMinutes());
|
|
}
|
|
|
|
function statusMeta(status) {
|
|
if (status === 'rewarded') {
|
|
return { label: '已奖励', text: '奖励已发放', className: 'is-rewarded' };
|
|
}
|
|
if (status === 'verified') {
|
|
return { label: '已验证', text: '等待奖励发放', className: 'is-verified' };
|
|
}
|
|
return { label: '已注册', text: '等待完成验证', className: 'is-registered' };
|
|
}
|
|
|
|
function setCopyText(text) {
|
|
if (!copyBtn) return;
|
|
var label = copyBtn.querySelector('span');
|
|
if (label) label.textContent = text;
|
|
else copyBtn.textContent = text;
|
|
}
|
|
|
|
function renderInviteList(items, total) {
|
|
if (!listEl) return;
|
|
listEl.classList.remove('is-loading');
|
|
if (totalEl) {
|
|
if (total > items.length) totalEl.textContent = '最近 ' + items.length + ' 条 / 共 ' + total + ' 条';
|
|
else totalEl.textContent = total ? '共 ' + total + ' 条' : '暂无记录';
|
|
}
|
|
if (!items.length) {
|
|
listEl.innerHTML = '<div class="aksrt-invite-record-empty">分享链接后,好友注册记录会显示在这里。</div>';
|
|
return;
|
|
}
|
|
|
|
listEl.innerHTML = items.map(function (item) {
|
|
var name = item.invitee_name || (item.invitee_id ? '用户 #' + item.invitee_id : '受邀用户');
|
|
var first = name ? name.charAt(0).toUpperCase() : '?';
|
|
var meta = statusMeta(item.status);
|
|
var createdAt = formatDate(item.created_at);
|
|
var rewardedAt = formatDate(item.rewarded_at);
|
|
var statusText = meta.text;
|
|
if (item.status === 'rewarded' && rewardedAt) statusText = '奖励于 ' + rewardedAt;
|
|
var avatar = item.invitee_avatar
|
|
? '<img src="' + escapeHtml(item.invitee_avatar) + '" alt="">'
|
|
: '<span>' + escapeHtml(first) + '</span>';
|
|
|
|
return '<div class="aksrt-invite-record ' + meta.className + '">' +
|
|
'<div class="aksrt-invite-record__avatar">' + avatar + '</div>' +
|
|
'<div class="aksrt-invite-record__main">' +
|
|
'<strong>' + escapeHtml(name) + '</strong>' +
|
|
'<span>' + (createdAt ? '注册于 ' + escapeHtml(createdAt) : '注册时间未知') + '</span>' +
|
|
'</div>' +
|
|
'<div class="aksrt-invite-record__status">' +
|
|
'<span class="aksrt-invite-badge ' + meta.className + '">' + meta.label + '</span>' +
|
|
'<small>' + escapeHtml(statusText) + '</small>' +
|
|
'</div>' +
|
|
'</div>';
|
|
}).join('');
|
|
}
|
|
|
|
api('/invite/me').then(function (r) {
|
|
if (!r.ok) {
|
|
if (linkInput) linkInput.value = '';
|
|
if (copyBtn) copyBtn.disabled = true;
|
|
if (codeEl) codeEl.textContent = '--';
|
|
if (stats) {
|
|
stats.querySelectorAll('[data-stat]').forEach(function (el) {
|
|
el.textContent = '—';
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
var j = r.json;
|
|
if (linkInput) linkInput.value = j.invite_url || '';
|
|
if (codeEl) codeEl.textContent = j.code || '--';
|
|
if (stats && j.stats) {
|
|
stats.querySelectorAll('[data-stat]').forEach(function (el) {
|
|
var k = el.getAttribute('data-stat');
|
|
el.textContent = (j.stats[k] || 0).toString();
|
|
});
|
|
}
|
|
}).catch(function () {
|
|
if (copyBtn) copyBtn.disabled = true;
|
|
if (codeEl) codeEl.textContent = '--';
|
|
});
|
|
|
|
if (listEl) {
|
|
api('/invite/list?per_page=8').then(function (r) {
|
|
if (!r.ok) {
|
|
listEl.classList.remove('is-loading');
|
|
listEl.innerHTML = '<div class="aksrt-invite-record-empty">邀请记录加载失败,请稍后刷新。</div>';
|
|
if (totalEl) totalEl.textContent = '加载失败';
|
|
return;
|
|
}
|
|
var j = r.json || {};
|
|
renderInviteList(j.items || [], Number(j.total || 0));
|
|
}).catch(function () {
|
|
listEl.classList.remove('is-loading');
|
|
listEl.innerHTML = '<div class="aksrt-invite-record-empty">邀请记录加载失败,请稍后刷新。</div>';
|
|
if (totalEl) totalEl.textContent = '加载失败';
|
|
});
|
|
}
|
|
|
|
if (!copyBtn) return;
|
|
var copying = false;
|
|
copyBtn.addEventListener('click', function () {
|
|
if (copying || !linkInput || !linkInput.value) return;
|
|
copying = true;
|
|
if (navigator.clipboard) {
|
|
navigator.clipboard.writeText(linkInput.value).then(function () {
|
|
setCopyText('已复制');
|
|
setTimeout(function () { setCopyText('复制链接'); copying = false; }, 2000);
|
|
}).catch(function () { copying = false; });
|
|
} else {
|
|
linkInput.select();
|
|
document.execCommand('copy');
|
|
setCopyText('已复制');
|
|
setTimeout(function () { setCopyText('复制链接'); copying = false; }, 2000);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
initSignin();
|
|
initInvite();
|
|
});
|
|
} else {
|
|
initSignin();
|
|
initInvite();
|
|
}
|
|
})();
|