提交 0b745b62 编写于 作者: R root

Wed Jul 2 19:37:00 CST 2025 inscode

上级 9b4c3294
......@@ -107,10 +107,8 @@ function getTimestamp() {
return `${YYYY}${MM}${DD}_${hh}${mm}${ss}`;
}
async function detectChromePath() {
function detectChromePath() {
const candidates = ['google-chrome', 'chromium-browser', 'chromium'];
// 1. 先尝试检测已安装的浏览器
for (const cmd of candidates) {
try {
const path = execSync(`which ${cmd}`).toString().trim();
......@@ -122,38 +120,7 @@ async function detectChromePath() {
continue;
}
}
// 2. 尝试自动安装Chromium
console.log('🔄 未检测到Chromium,尝试自动安装...');
try {
execSync('apt-get update && apt-get install -y chromium', {stdio: 'inherit'});
const chromiumPath = execSync('which chromium').toString().trim();
if (chromiumPath) {
console.log(`✅ Chromium 安装成功,路径: ${chromiumPath}`);
return chromiumPath;
}
} catch (installErr) {
console.error('❌ Chromium自动安装失败:', installErr.message);
}
// 3. 提供详细的安装指引
const errorMsg = `
❌ 未检测到可用的 Chrome/Chromium
解决方案:
1. 自动安装(推荐):
- 运行: apt-get update && apt-get install -y chromium
2. 手动安装:
- Debian/Ubuntu: apt-get install chromium
- CentOS: yum install chromium
- MacOS: brew install chromium
- Windows: 下载安装 Chrome 或 Chromium
3. 指定已安装的浏览器路径:
- 修改 fetchWithPuppeteer.js 中的 executablePath 参数
`;
throw new Error(errorMsg);
throw new Error("❌ 未检测到可用的 Chrome/Chromium,请先安装!");
}
/**
* 等待出现 + 自动轮询点击指定验证选项
......@@ -205,7 +172,16 @@ async function checkTimerAndVerifyAutoRetry(page, verifyCode = '888888', timeout
let isKeyinput = false;
let isClickbtt = false;
pageGlobal = page; // 保存给后端接口用
try {
while (Date.now() - start < timeout) {
if (page.isClosed()) {
console.log('🛑 检测到页面已关闭,结束轮询!');
break;
}
const [timerElement] = await page.$x(xpathForTimer);
if (timerElement) {
......@@ -287,11 +263,16 @@ async function checkTimerAndVerifyAutoRetry(page, verifyCode = '888888', timeout
console.log(`[完成了] 倒计时元素[ isClickbtt:${getTimestamp()} isKeyinput:${getTimestamp()} ]`);
// return;
}
}//where
const beforeClickFilename = `img/before_[超时]_${getTimestamp()}.png`;
await page.screenshot({ path: beforeClickFilename });
}catch (err) {
console.log('🚫 访问 mainFrame 报错,说明页面已关闭,结束轮询!');
}
console.log(`❌ [超时] (${timeout / 1000}s) 未检测到倒计时 <= 30,未执行验证`);
}
......@@ -316,6 +297,7 @@ async function fetchQrFromPage(entryUrl, accountName = 'userTest') {
}
});
// try {
browserInstance = browser;
......@@ -366,6 +348,12 @@ async function fetchQrFromPage(entryUrl, accountName = 'userTest') {
}
// 监听二维码状态查询接口
if (url.includes('check_qrconnect') || url.includes("check_qrcode")) {
if (!response.ok) {
console.error(`❌ 上游接口返回错误: ${response.status} ${response.statusText}`);
console.error(`❌ 响应内容: ${text}`);
return;
}
const text = await response.text();
console.log('📡 原始响应:', text);
const json = JSON.parse(text);
......@@ -388,7 +376,9 @@ async function fetchQrFromPage(entryUrl, accountName = 'userTest') {
}
// 已过期可选:刷新二维码
if (json?.data?.status === 'expired' && json?.data?.qrcode) {
//json?.data?.status === 'expired' && refused
if (json?.data?.qrcode) {
qrCodeContent = json.data.qrcode;
console.log('♻️ 二维码已过期,已更新 base64');
await page.reload({ waitUntil: 'networkidle2' });
......@@ -463,8 +453,12 @@ page.on('framenavigated', async (frame) => {
console.log("📸 错误截图已保存为 error_screenshot.png");
}
// } finally {
// console.log('🛑 自动关闭 Puppeteer');
// await browser.close();
// }
//await browser.close();
return hookResult;
// return hookResult;
}
// module.exports = fetchQrFromPage;
......
......@@ -2,103 +2,142 @@
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>抖音扫码登录状态页</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
#qrcode-section, #scanned-section, #verify-section, #logged-section,#account1,#start-btn1 { display: none; margin-bottom: 20px; }
img { max-width: 200px; }
#avatar { border-radius: 50%; }
input { padding: 5px; }
button { padding: 5px 10px; }
.status { font-weight: bold; color: #0077cc; }
</style>
</head>
<body>
<h1>扫码登录</h1>
body {
font-family: -apple-system, BlinkMacSystemFont, Arial, sans-serif;
margin: 0; padding: 20px;
background: #f9f9f9;
color: #333;
text-align: center;
}
h1 { margin-bottom: 20px; }
<!-- 启动按钮 -->
<input id="account" placeholder="输入账户名">
<button id="start-btn">启动扫码</button>
.container {
max-width: 400px;
margin: 0 auto;
background: #fff;
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0,0,0,0.05);
}
<!-- 停止按钮(可选) -->
<button id="stop-btn">停止运行</button>
input {
width: 100%;
padding: 10px;
margin: 5px 0 15px;
border: 1px solid #ddd;
border-radius: 5px;
box-sizing: border-box;
font-size: 16px;
}
button {
width: 100%;
background: #0077cc;
border: none;
color: #fff;
font-size: 16px;
padding: 12px;
border-radius: 5px;
cursor: pointer;
margin-bottom: 10px;
}
<!-- 新状态提示 -->
<div id="qrcode-status">当前状态: <span id="scan-status" class="status">加载中...</span></div>
<!-- 一、二维码状态 -->
<div id="qrcode-section">
<h2>请使用抖音 APP 扫码</h2>
<img id="qrcode" alt="等待生成二维码..." />
</div>
button:hover {
background: #005fa3;
}
<img id="avatar" alt="头像" />
<!-- 二、已扫码状态 -->
<div id="scanned-section">
<h2>已扫码,请在手机确认</h2>
<p> <span id="scanned-nickname"></span></p>
</div>
img {
max-width: 200px;
border-radius: 10px;
display: block;
margin: 10px auto;
}
<!-- 三、需要验证码状态 -->
<div id="verify-section">
<h2>请输入短信验证码</h2>
<div id="verify-error" style="color:red;"></div>
#avatar { border-radius: 50%; width: 100px; height: 100px; object-fit: cover; }
<input id="code" placeholder="输入验证码" />
<button id="submit">提交验证码</button>
</div>
.status { font-weight: bold; color: #0077cc; }
<!-- 四、登录成功状态 -->
<div id="logged-section">
<h2>🎉 登录成功</h2>
<p>昵称: <span id="nickname"></span></p>
<img id="logged-avatar" alt="头像" />
</div>
#verify-error { color: red; margin: 10px 0; }
<script>
document.getElementById('start-btn').onclick = async () => {
const account = document.getElementById('account').value.trim();
if (!account) return alert('请先输入账户名');
@media (prefers-color-scheme: dark) {
body { background: #121212; color: #eee; }
.container { background: #1e1e1e; box-shadow: none; }
input { background: #333; border: 1px solid #555; color: #fff; }
button { background: #3399ff; }
button:hover { background: #0077cc; }
}
</style>
</head>
<body>
const res = await fetch('/start', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ account })
}).then(r => r.json());
<div class="container">
<h1>抖音扫码登录</h1>
alert(res.success ? res.msg : res.msg);
};
<input id="account" placeholder="输入账户名">
<button id="start-btn">🚀 启动扫码</button>
<button id="stop-btn">🛑 停止运行</button>
document.getElementById('stop-btn').onclick = async () => {
const res = await fetch('/stop', {
method: 'POST'
}).then(r => r.json());
alert(res.success ? res.msg : res.msg);
};
</script>
<p id="qrcode-status">当前状态: <span id="scan-status" class="status">加载中...</span></p>
<div id="qrcode-section">
<h2>请使用抖音 APP 扫码</h2>
<img id="qrcode" alt="等待生成二维码..." />
</div>
<img id="avatar" alt="头像" />
<div id="scanned-section">
<h2>已扫码,请在手机确认</h2>
<p>昵称: <span id="scanned-nickname"></span></p>
</div>
<div id="verify-section">
<h2>请输入短信验证码</h2>
<div id="verify-error"></div>
<input id="code" placeholder="输入验证码" />
<button id="submit">提交验证码</button>
</div>
<div id="logged-section">
<h2>🎉 登录成功</h2>
<p>昵称: <span id="nickname"></span></p>
<img id="logged-avatar" alt="头像" />
</div>
</div>
<script>
document.getElementById('start-btn').onclick = async () => {
const account = document.getElementById('account').value.trim();
if (!account) return alert('请先输入账户名');
const res = await fetch('/start', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ account })
}).then(r => r.json());
alert(res.success ? res.msg : res.msg);
};
document.getElementById('stop-btn').onclick = async () => {
const res = await fetch('/stop', {
method: 'POST'
}).then(r => r.json());
alert(res.success ? res.msg : res.msg);
};
async function pollStatus() {
const res = await fetch('/status').then(r => r.json());
// 更新状态文本
document.getElementById('scan-status').innerText = res.scanStatus || '等待启动';
document.getElementById('verify-error').innerText = res.verifyError || '';
// 重置所有区域显示
document.getElementById('qrcode-section').style.display = 'none';
document.getElementById('scanned-section').style.display = 'none';
document.getElementById('verify-section').style.display = 'none';
document.getElementById('logged-section').style.display = 'none';
document.getElementById('avatar').style.display = 'none';
document.getElementById('qrcode-status').style.display = 'none';
// 如果已登录
// 隐藏所有状态块
['qrcode-section', 'scanned-section', 'verify-section', 'logged-section', 'avatar'].forEach(id => {
document.getElementById(id).style.display = 'none';
});
if (res.user?.basic?.nickname) {
document.getElementById('logged-section').style.display = 'block';
document.getElementById('nickname').innerText = res.user.basic.nickname;
......@@ -106,53 +145,42 @@ document.getElementById('stop-btn').onclick = async () => {
if (avatarUrl) {
document.getElementById('logged-avatar').src = avatarUrl;
}
return; // 已登录直接返回
return;
}
document.getElementById('qrcode-status').style.display = 'block';
//显示头像
if (res.scanUser?.avatar_url) {
document.getElementById('avatar').style.display = 'block';
document.getElementById('avatar').src = res.scanUser.avatar_url;
}
// 否则根据状态显示
if (res.scanUser?.avatar_url) {
document.getElementById('avatar').style.display = 'block';
document.getElementById('avatar').src = res.scanUser.avatar_url;
}
if (res.scanStatus === 'new') {
document.getElementById('avatar').style.display = 'none';
document.getElementById('qrcode-section').style.display = 'block';
if (res.qrcode) {
document.getElementById('qrcode').src = 'data:image/png;base64,' + res.qrcode;
}
} else if (res.scanStatus === 'scanned') {
document.getElementById('scanned-section').style.display = 'block';
if (res.scanUser?.avatar_url) {
document.getElementById('avatar').src = res.scanUser.avatar_url;
}
document.getElementById('scanned-nickname').innerText = res.scanUser?.screen_name || '';
} else if (res.scanStatus === 'verify' && res.pendingVerify) {
document.getElementById('verify-section').style.display = 'block';
}
}
// 提交验证码
document.getElementById('submit').onclick = async () => {
const code = document.getElementById('code').value.trim();
if (!code) {
alert('请输入验证码');
return;
}
if (!code) return alert('请输入验证码');
const res = await fetch('/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ code })
}).then(r => r.json());
alert(res.success ? '✅ 验证码已提交!' : '❌ 提交失败');
};
// 定时轮询状态
setInterval(pollStatus, 2000);
pollStatus();
</script>
</body>
</html>
......@@ -26,8 +26,8 @@ app.post('/start', async (req, res) => {
account
).then(() => {
console.log(`✅ [结束] 账户:${account} 扫码流程结束`);
currentBot = null; // 自动释放锁
currentAccount = null;
// currentBot = null; // 自动释放锁
// currentAccount = null;
});
res.json({ success: true, msg: `已启动账户:${account}` });
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册