// ==UserScript==
// @name 百度贴吧一键签到
// @namespace https://blog.qitongtingyu.online/
// @version 1.3
// @description 一键签到所有关注的贴吧,支持大量贴吧、智能间隔防限流、失败自动重试
// @author 栖桐听雨
// @icon https://tb3.bdstatic.com/public/icon/favicon-v2.ico
// @match *://tieba.baidu.com/*
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @connect tieba.baidu.com
// @connect c.tieba.baidu.com
// @require https://unpkg.com/crypto-js@4.1.1/core.js
// @require https://unpkg.com/crypto-js@4.1.1/md5.js
// @license MIT
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
const colors = {
primary: '#a18276', primaryDark: '#8a6f64', primaryLight: '#b89a8f',
accent: '#f4b886', bgPrimary: '#fefdfb', bgHighlight: 'rgba(244, 184, 134, 0.3)',
textPrimary: '#5c4a42', textWhite: '#ffffff', border: 'rgba(92, 74, 66, 0.12)',
success: '#7a9e7e', error: '#c97b7b', info: '#a18276',
scrollbarTrack: 'rgba(161, 130, 118, 0.08)', scrollbarThumb: 'rgba(161, 130, 118, 0.4)',
scrollbarThumbHover: 'rgba(161, 130, 118, 0.6)',
shadowPrimary: 'rgba(161, 130, 118, 0.35)', shadowPrimaryHover: 'rgba(161, 130, 118, 0.45)',
shadowPrimaryActive: 'rgba(161, 130, 118, 0.3)', shadowModal: 'rgba(0, 0, 0, 0.15)'
};
const svgIcons = {
close: '',
check: '',
skip: '',
warn: ''
};
const styles = `
body > #tiebaAutoSignBtn,
body > .tieba-sign-mask {
all: initial;
}
body > #tiebaAutoSignBtn {
position: fixed;
bottom: 24px;
right: 24px;
z-index: 99999;
padding: 14px 24px;
background: linear-gradient(135deg, ${colors.primary} 0%, ${colors.primaryDark} 100%);
color: ${colors.textWhite};
border: none;
border-radius: 12px;
cursor: pointer;
font-size: 15px;
font-weight: 500;
letter-spacing: 0.5px;
box-shadow: 0 6px 20px ${colors.shadowPrimary};
transition: transform 0.15s cubic-bezier(0.2, 0.8, 0.2, 1),
box-shadow 0.15s cubic-bezier(0.2, 0.8, 0.2, 1),
background 0.2s ease;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
outline: none;
display: block;
}
body > #tiebaAutoSignBtn:hover {
background: linear-gradient(135deg, ${colors.primaryLight} 0%, ${colors.primary} 100%);
box-shadow: 0 8px 28px ${colors.shadowPrimaryHover};
transform: translateY(-2px);
}
body > #tiebaAutoSignBtn:active {
transform: translateY(0) scale(0.98);
box-shadow: 0 4px 12px ${colors.shadowPrimaryActive};
}
body > #tiebaAutoSignBtn:disabled {
background: linear-gradient(135deg, #a89a90 0%, #8a7268 100%);
cursor: not-allowed;
transform: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
body > .tieba-sign-mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.45);
backdrop-filter: blur(4px);
z-index: 100000;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
touch-action: none;
}
@keyframes noise {
0%, 100% { background-position: 0 0; }
10% { background-position: -5% -10%; }
20% { background-position: -15% 5%; }
30% { background-position: 7% -25%; }
40% { background-position: 20% 25%; }
50% { background-position: -25% 10%; }
60% { background-position: 15% 5%; }
70% { background-position: 0 15%; }
80% { background-position: 25% 35%; }
90% { background-position: -10% 10%; }
}
@keyframes progressFlow {
0% { background-position: 0% 0%; }
100% { background-position: 200% 0%; }
}
@keyframes dialogEnter {
0% { opacity: 0; transform: scale(0.95) translateY(10px); }
100% { opacity: 1; transform: scale(1) translateY(0); }
}
.noise-bg::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.03;
pointer-events: none;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
animation: noise 8s steps(10) infinite;
}
.tieba-sign-dialog {
width: 90%;
max-width: 700px;
max-height: 85vh;
background: ${colors.bgPrimary};
border-radius: 16px;
box-shadow: 0 12px 48px ${colors.shadowModal};
display: flex;
flex-direction: column;
overflow: hidden;
animation: dialogEnter 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.tieba-sign-dialog-header {
background: linear-gradient(135deg, ${colors.primary} 0%, ${colors.primaryDark} 100%);
color: ${colors.textWhite};
padding: 16px 20px;
font-size: 17px;
font-weight: 500;
letter-spacing: 0.5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.tieba-sign-close {
width: 36px;
height: 36px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.12);
color: ${colors.textWhite};
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s ease;
-webkit-tap-highlight-color: transparent;
}
.tieba-sign-close svg {
width: 20px;
height: 20px;
}
.tieba-sign-close:hover {
background: rgba(255, 255, 255, 0.25);
transform: scale(1.05);
}
.tieba-sign-dialog-body {
padding: 20px;
overflow-y: auto;
flex: 1;
font-size: 14px;
line-height: 1.7;
color: ${colors.textPrimary};
}
.tieba-sign-dialog-body::-webkit-scrollbar {
width: 6px;
}
.tieba-sign-dialog-body::-webkit-scrollbar-track {
background: ${colors.scrollbarTrack};
border-radius: 3px;
}
.tieba-sign-dialog-body::-webkit-scrollbar-thumb {
background: ${colors.scrollbarThumb};
border-radius: 3px;
transition: background 0.2s ease;
}
.tieba-sign-dialog-body::-webkit-scrollbar-thumb:hover {
background: ${colors.scrollbarThumbHover};
}
.tieba-sign-progress-container {
margin-bottom: 16px;
}
.tieba-sign-progress-info {
display: flex;
justify-content: space-between;
margin-bottom: 6px;
font-size: 13px;
}
.tieba-sign-progress-bar-bg {
width: 100%;
height: 8px;
background: ${colors.border};
border-radius: 4px;
overflow: hidden;
}
.tieba-sign-progress-bar {
width: 0%;
height: 100%;
background: linear-gradient(90deg, ${colors.primary}, ${colors.accent}, ${colors.primary});
background-size: 200% 100%;
border-radius: 4px;
transition: width 0.3s ease;
}
.tieba-sign-progress-bar.loading {
animation: progressFlow 1.5s linear infinite;
}
.tieba-sign-summary {
background: linear-gradient(135deg, ${colors.bgHighlight} 0%, rgba(244, 184, 134, 0.1) 100%);
padding: 16px;
border-radius: 12px;
margin-bottom: 12px;
border-left: 4px solid ${colors.accent};
}
.tieba-sign-results {
max-height: 40vh;
overflow-y: auto;
margin-top: 12px;
}
.tieba-sign-results::-webkit-scrollbar {
width: 6px;
}
.tieba-sign-results::-webkit-scrollbar-track {
background: ${colors.scrollbarTrack};
border-radius: 3px;
}
.tieba-sign-results::-webkit-scrollbar-thumb {
background: ${colors.scrollbarThumb};
border-radius: 3px;
}
.tieba-sign-result-item {
padding: 10px 0;
border-bottom: 1px solid ${colors.border};
transition: background 0.15s ease;
display: flex;
gap: 8px;
align-items: flex-start;
}
.tieba-sign-result-item:hover {
background: rgba(161, 130, 118, 0.1);
}
.tieba-sign-result-item:last-child {
border-bottom: none;
}
.tieba-sign-result-item svg {
flex-shrink: 0;
width: 16px;
height: 16px;
margin-top: 2px;
}
.tieba-sign-result-item.tieba-sign-success {
color: ${colors.success};
font-weight: 500;
}
.tieba-sign-result-item.tieba-sign-existed {
color: ${colors.info};
}
.tieba-sign-result-item.tieba-sign-fail {
color: ${colors.error};
font-weight: 500;
}
.tieba-sign-dialog-footer {
padding: 16px 20px;
text-align: right;
border-top: 1px solid ${colors.border};
}
.tieba-sign-confirm {
padding: 10px 24px;
background: linear-gradient(135deg, ${colors.primary} 0%, ${colors.primaryDark} 100%);
color: ${colors.textWhite};
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 15px;
font-weight: 500;
letter-spacing: 0.5px;
transition: transform 0.15s ease, box-shadow 0.15s ease;
min-height: 44px;
min-width: 88px;
}
.tieba-sign-confirm:hover {
box-shadow: 0 4px 16px ${colors.shadowPrimary};
transform: translateY(-1px);
}
@media (max-width: 480px) {
body > #tiebaAutoSignBtn {
padding: 12px 20px;
font-size: 14px;
bottom: 16px;
right: 16px;
border-radius: 24px;
}
.tieba-sign-dialog {
width: 95%;
max-height: 80vh;
border-radius: 12px;
}
.tieba-sign-dialog-header {
padding: 12px 16px;
font-size: 16px;
}
.tieba-sign-close {
width: 32px;
height: 32px;
border-radius: 8px;
}
.tieba-sign-dialog-body {
padding: 16px;
font-size: 13px;
}
.tieba-sign-summary {
padding: 12px;
}
.tieba-sign-confirm {
padding: 8px 20px;
font-size: 14px;
min-height: 40px;
}
}
@media (min-width: 481px) and (max-width: 768px) {
body > #tiebaAutoSignBtn {
padding: 14px 22px;
font-size: 15px;
bottom: 20px;
right: 20px;
}
.tieba-sign-dialog {
width: 85%;
max-height: 80vh;
}
}
@media (orientation: landscape) and (max-height: 500px) {
.tieba-sign-dialog {
max-height: 90vh;
width: 80%;
}
body > #tiebaAutoSignBtn {
bottom: 12px;
padding: 10px 18px;
font-size: 13px;
}
}
`;
const CACHE_KEY = 'tieba_sign_cache_v2';
const FAKE_VERSION = '9.7.8.0';
function makeFakeParams(obj) {
return Object.assign({
_client_type: 4,
_client_version: FAKE_VERSION,
_phone_imei: '0'.repeat(15),
model: 'HUAWEI P40',
net_type: 1,
stErrorNums: 1,
stMethod: 1,
stMode: 1,
stSize: 320,
stTime: 117,
stTimesNum: 1,
timestamp: Date.now()
}, obj);
}
function sign(payload) {
const sortKeys = Object.keys(payload).sort();
let str = sortKeys.reduce((acc, key) => acc += `${key}=${payload[key]}`, '');
str += 'tiebaclient!!!';
return CryptoJS.MD5(str).toString();
}
function getTodayCache() {
const today = new Date();
const dateStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
const cache = JSON.parse(GM_getValue(CACHE_KEY, '{}'));
if (cache.date !== dateStr) {
GM_setValue(CACHE_KEY, JSON.stringify({ date: dateStr, signed: {} }));
return new Map();
}
return new Map(Object.entries(cache.signed || {}));
}
function saveToCache(forumName) {
const today = new Date();
const dateStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
const cache = JSON.parse(GM_getValue(CACHE_KEY, '{}'));
if (cache.date !== dateStr) {
cache.date = dateStr;
cache.signed = {};
}
cache.signed[forumName] = true;
GM_setValue(CACHE_KEY, JSON.stringify(cache));
}
function createStyles() {
const style = document.createElement('style');
style.textContent = styles;
document.head.appendChild(style);
}
function isMobile() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth <= 768;
}
function createProgressDialog(title) {
document.querySelector('.tieba-sign-mask')?.remove();
const mask = document.createElement('div');
mask.className = 'tieba-sign-mask';
mask.innerHTML = `
`;
document.body.appendChild(mask);
document.body.style.overflow = 'hidden';
const el = {
progressText: mask.querySelector('.tieba-sign-progress-text'),
progressPercent: mask.querySelector('.tieba-sign-progress-percent'),
progressBar: mask.querySelector('.tieba-sign-progress-bar'),
summary: mask.querySelector('.tieba-sign-summary'),
results: mask.querySelector('.tieba-sign-results'),
closeBtn: mask.querySelector('.tieba-sign-close'),
confirmBtn: mask.querySelector('.tieba-sign-confirm')
};
let aborted = false;
const close = () => {
aborted = true;
mask.remove();
document.body.style.overflow = '';
};
el.closeBtn.addEventListener('click', close);
el.confirmBtn.addEventListener('click', close);
el.confirmBtn.addEventListener('touchend', e => { e.preventDefault(); close(); });
mask.addEventListener('click', e => e.target === mask && close());
return {
updateProgress: (current, total) => {
if (aborted) return;
const percent = total > 0 ? Math.round((current / total) * 100) : 0;
el.progressText.textContent = `处理中:${current}/${total}`;
el.progressPercent.textContent = `${percent}%`;
el.progressBar.style.width = `${percent}%`;
el.progressBar.classList.remove('loading');
},
appendResult: (status, text) => {
if (aborted) return;
const item = document.createElement('div');
item.className = `tieba-sign-result-item tieba-sign-${status}`;
let icon = svgIcons.warn;
if (status === 'success') icon = svgIcons.check;
if (status === 'existed') icon = svgIcons.skip;
item.innerHTML = `${icon}${text.replace(/\n/g, '
')}`;
el.results.appendChild(item);
el.results.scrollTop = el.results.scrollHeight;
},
updateSummary: (success, existed, fail, totalContSign, totalCount) => {
if (aborted) return;
el.summary.innerHTML = `签到汇总
总数:${totalCount} | 成功:${success} | 跳过:${existed} | 失败:${fail}
累计连续签到:${totalContSign}天`;
},
setLoading: (text) => {
if (aborted) return;
el.progressText.textContent = text;
el.progressBar.style.width = '30%';
el.progressBar.classList.add('loading');
},
setComplete: () => {
if (aborted) return;
el.progressText.textContent = '完成';
el.confirmBtn.textContent = '确定';
el.progressBar.classList.remove('loading');
},
getAborted: () => aborted
};
}
function createSignButton() {
if (document.getElementById('tiebaAutoSignBtn')) return;
const btn = document.createElement('button');
btn.id = 'tiebaAutoSignBtn';
btn.textContent = '贴吧一键签到';
btn.className = 'noise-bg';
btn.addEventListener('click', async () => {
if (btn.disabled) return;
btn.disabled = true;
btn.textContent = '签到中...';
await autoSign();
btn.disabled = false;
btn.textContent = '贴吧一键签到';
});
document.body.appendChild(btn);
}
function extractTiebaNamesFromHtml(html) {
const names = new Set();
const regex = /([^<]+)<\/a>/g;
let match;
while ((match = regex.exec(html)) !== null) {
const name = (match[2] || match[3]).trim();
if (name) names.add(name);
}
if (names.size === 0) {
const regex2 = /balvname="([^"]+)"/g;
while ((match = regex2.exec(html)) !== null) {
const name = match[1].trim();
if (name) names.add(name);
}
}
return [...names];
}
function showErrorPrompt(message) {
const defaultTieba = 'redstone_machinery_communication';
const input = prompt(`${message}\n\n已为你填充默认贴吧:${defaultTieba}\n如需添加其他贴吧,请用英文逗号分隔`, defaultTieba);
return input ? input.split(',').map(name => name.trim()).filter(name => name).map(name => ({ forum_name: name })) : [];
}
async function getFollowedTieba() {
const forumMap = new Map();
const addForums = (forums) => {
forums.forEach(f => {
const name = f.forum_name?.trim() || f.name?.trim();
const id = f.forum_id?.toString() || f.id?.toString();
if (name) {
if (id && forumMap.has(id)) {
forumMap.get(id).forum_name = name;
} else {
forumMap.set(id || name, { forum_name: name });
}
}
});
};
const fromPage = () => {
const html = document.querySelector('html')?.innerHTML || '';
const forumIndex = html.indexOf('{"forumArr":');
if (forumIndex !== -1) {
try {
const content = html.slice(forumIndex);
const forumEnd = content.indexOf(');');
const jsonStr = content.slice(0, forumEnd);
const data = JSON.parse(jsonStr);
if (data.forumArr && Array.isArray(data.forumArr)) {
addForums(data.forumArr);
}
} catch (e) { }
}
};
const fromNewmoindex = async () => {
return new Promise(resolve => {
GM_xmlhttpRequest({
method: 'POST',
url: 'https://tieba.baidu.com/mo/q/newmoindex',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': document.cookie,
'User-Agent': navigator.userAgent
},
timeout: 20000,
onload: r => {
try {
const data = JSON.parse(r.responseText || '{}');
if (data.data?.like_forum && Array.isArray(data.data.like_forum)) {
addForums(data.data.like_forum);
}
} catch { }
resolve();
},
onerror: () => resolve(),
ontimeout: () => resolve()
});
});
};
const fromAppApi = async () => {
const appCommonHeader = {
'User-agent': `bdtb for Android ${FAKE_VERSION}`,
Accept: '',
'Content-Type': 'application/x-www-form-urlencoded'
};
let page = 1;
let hasMore = true;
while (hasMore && page <= 50) {
const baseParams = makeFakeParams({
BDUSS: document.cookie.match(/BDUSS=([^;]+)/)?.[1] || '',
page_no: page,
page_size: 200
});
const params = { ...baseParams, sign: sign(baseParams) };
const dataStr = Object.keys(params).map(k => `${k}=${encodeURIComponent(params[k])}`).join('&');
const response = await new Promise(resolve => {
GM_xmlhttpRequest({
method: 'POST',
url: 'https://c.tieba.baidu.com/c/f/forum/like',
headers: { ...appCommonHeader, 'Cookie': document.cookie },
data: dataStr,
timeout: 20000,
onload: r => {
try {
resolve(JSON.parse(r.responseText || '{}'));
} catch {
resolve({});
}
},
onerror: () => resolve({}),
ontimeout: () => resolve({})
});
});
if (response.forum_list) {
const forums = [];
if (response.forum_list.non_gconforum) {
const list = Array.isArray(response.forum_list.non_gconforum) ? response.forum_list.non_gconforum : [];
forums.push(...list);
}
if (response.forum_list.gconforum) {
const list = Array.isArray(response.forum_list.gconforum) ? response.forum_list.gconforum : [];
forums.push(...list);
}
if (Array.isArray(response.forum_list)) {
forums.push(...response.forum_list);
}
addForums(forums);
}
hasMore = response.has_more === '1' || response.has_more === 1;
page++;
await new Promise(r => setTimeout(r, 200));
}
};
const fromMylike = async () => {
let page = 1;
while (page <= 50) {
const html = await new Promise(resolve => {
GM_xmlhttpRequest({
method: 'GET',
url: `https://tieba.baidu.com/f/like/mylike?pn=${page}`,
headers: { 'Cookie': document.cookie, 'User-Agent': navigator.userAgent },
timeout: 20000,
onload: r => resolve(r.responseText || ''),
onerror: () => resolve(''),
ontimeout: () => resolve('')
});
});
const names = extractTiebaNamesFromHtml(html);
names.forEach(name => {
if (name) forumMap.set(name, { forum_name: name });
});
if (names.length < 20) break;
await new Promise(r => setTimeout(r, 300));
page++;
}
};
fromPage();
await fromNewmoindex();
await fromAppApi();
await fromMylike();
const tiebaList = [...forumMap.values()];
return tiebaList.length > 0 ? tiebaList : showErrorPrompt('自动提取贴吧列表失败');
}
async function getTBS() {
return new Promise(resolve => {
GM_xmlhttpRequest({
method: 'GET',
url: 'https://tieba.baidu.com/dc/common/tbs',
headers: { 'Cookie': document.cookie },
timeout: 10000,
onload: r => {
try { resolve(JSON.parse(r.responseText).tbs || ''); }
catch { resolve(''); }
},
onerror: () => resolve('')
});
});
}
async function signSingleTieba(name) {
return new Promise(async resolve => {
const tbs = await getTBS();
const data = `ie=utf-8&kw=${encodeURIComponent(name)}${tbs ? `&tbs=${tbs}` : ''}`;
GM_xmlhttpRequest({
method: 'POST',
url: 'https://tieba.baidu.com/sign/add',
data: data,
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Referer': `https://tieba.baidu.com/f?kw=${encodeURIComponent(name)}`,
'Cookie': document.cookie
},
timeout: 15000,
onload: r => {
try {
const res = JSON.parse(r.responseText);
switch (res.no) {
case 0: resolve({ status: 'success', msg: `${name}:签到成功\n连续签到:${res.data?.uinfo?.cont_sign_num || 0}天 | 总签到:${res.data?.uinfo?.total_sign_num || 0}次\n本吧排名:${res.data?.finfo?.current_rank_info?.sign_count || 0}` }); break;
case 1101: resolve({ status: 'existed', msg: `${name}:今日已签到` }); break;
case 100002: resolve({ status: 'fail', msg: `${name}:未登录/登录过期` }); break;
case 34004: resolve({ status: 'fail', msg: `${name}:需要验证` }); break;
default: resolve({ status: 'fail', msg: `${name}:失败(${res.error || '未知错误'})` }); break;
}
} catch { resolve({ status: 'fail', msg: `${name}:解析失败` }); }
},
onerror: () => resolve({ status: 'fail', msg: `${name}:网络错误` }),
ontimeout: () => resolve({ status: 'fail', msg: `${name}:请求超时` })
});
});
}
async function autoSign() {
const dialog = createProgressDialog('贴吧一键签到');
try {
dialog.setLoading('获取关注贴吧列表...');
const tiebaList = await getFollowedTieba();
if (dialog.getAborted()) return;
if (tiebaList.length === 0) {
dialog.appendResult('fail', '未获取到任何需要签到的贴吧');
dialog.updateSummary(0, 0, 1, 0, 0);
dialog.setComplete();
return;
}
const cache = getTodayCache();
const toSign = [];
const skipped = [];
const failedList = [];
for (const t of tiebaList) {
if (cache.has(t.forum_name)) {
skipped.push({ status: 'existed', msg: `${t.forum_name}:今日已签到(缓存)` });
} else {
toSign.push(t);
}
}
let [success, existed, fail, contSign] = [0, skipped.length, 0, 0];
dialog.updateSummary(success, existed, fail, contSign, tiebaList.length);
skipped.forEach((res, index) => {
dialog.appendResult(res.status, res.msg);
dialog.updateProgress(skipped.length + index + 1, tiebaList.length);
});
if (toSign.length === 0) {
dialog.appendResult('existed', '所有贴吧今日均已签到');
dialog.updateProgress(tiebaList.length, tiebaList.length);
dialog.setComplete();
return;
}
const baseDelay = isMobile() ? 1500 : 1200;
const batchDelay = 5000;
const batchSize = 5;
for (let i = 0; i < toSign.length; i++) {
if (dialog.getAborted()) break;
dialog.updateProgress(skipped.length + i + 1, tiebaList.length);
const res = await signSingleTieba(toSign[i].forum_name);
dialog.appendResult(res.status, res.msg);
if (res.status === 'success') {
success++;
saveToCache(toSign[i].forum_name);
const match = res.msg.match(/连续签到:(\d+)天/);
if (match) contSign += parseInt(match[1]);
} else if (res.status === 'existed') {
existed++;
saveToCache(toSign[i].forum_name);
} else {
fail++;
failedList.push(toSign[i]);
}
dialog.updateSummary(success, existed, fail, contSign, tiebaList.length);
if (i !== toSign.length - 1) {
if (dialog.getAborted()) break;
const randomOffset = Math.floor(Math.random() * 500);
await new Promise(r => setTimeout(r, baseDelay + randomOffset));
}
if ((i + 1) % batchSize === 0 && i !== toSign.length - 1) {
if (dialog.getAborted()) break;
await new Promise(r => setTimeout(r, batchDelay));
}
}
if (failedList.length > 0 && !dialog.getAborted()) {
dialog.appendResult('info', `准备重试 ${failedList.length} 个失败的贴吧...`);
await new Promise(r => setTimeout(r, 10000));
for (let j = 0; j < failedList.length; j++) {
if (dialog.getAborted()) break;
const name = failedList[j].forum_name;
dialog.updateProgress(skipped.length + toSign.length + j + 1, tiebaList.length + failedList.length);
const res = await signSingleTieba(name);
dialog.appendResult(res.status, res.msg);
if (res.status === 'success') {
success++;
fail--;
saveToCache(name);
const match = res.msg.match(/连续签到:(\d+)天/);
if (match) contSign += parseInt(match[1]);
} else if (res.status === 'existed') {
existed++;
fail--;
saveToCache(name);
}
dialog.updateSummary(success, existed, fail, contSign, tiebaList.length);
if (j !== failedList.length - 1) {
if (dialog.getAborted()) break;
await new Promise(r => setTimeout(r, 3000 + Math.floor(Math.random() * 2000)));
}
}
}
dialog.updateProgress(tiebaList.length, tiebaList.length);
dialog.setComplete();
} catch {
if (!dialog.getAborted()) {
dialog.appendResult('fail', '签到过程出错');
dialog.updateSummary(0, 0, 1, 0, 0);
dialog.setComplete();
}
}
}
function init() {
createStyles();
setTimeout(createSignButton, isMobile() ? 3000 : 2000);
}
document.readyState === 'complete' ? init() : window.addEventListener('load', init);
})();