207 lines
5.6 KiB
JavaScript
207 lines
5.6 KiB
JavaScript
|
|
import WebSocket from 'ws';
|
||
|
|
import { appendFileSync } from 'fs';
|
||
|
|
|
||
|
|
const CDP_PORT = 18800;
|
||
|
|
const COOKIE_VALUE = '3cfeb30b562daec31ba63bf64fdb3838';
|
||
|
|
const TARGET_URL = 'https://www.nodeseek.com';
|
||
|
|
const LOG_FILE = '/Users/jianzhang/.openclaw/workspace/scripts/checkin.log';
|
||
|
|
|
||
|
|
let msgId = 1;
|
||
|
|
let pendingCallbacks = new Map();
|
||
|
|
let loadEventResolve = null;
|
||
|
|
|
||
|
|
function log(message) {
|
||
|
|
const timestamp = new Date().toISOString();
|
||
|
|
const logLine = `[${timestamp}] [VP404] ${message}\n`;
|
||
|
|
console.log(logLine.trim());
|
||
|
|
try {
|
||
|
|
appendFileSync(LOG_FILE, logLine);
|
||
|
|
} catch (e) {
|
||
|
|
console.error('Failed to write log:', e.message);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function sleep(ms) {
|
||
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||
|
|
}
|
||
|
|
|
||
|
|
async function send(ws, method, params = {}) {
|
||
|
|
return new Promise((resolve, reject) => {
|
||
|
|
const id = msgId++;
|
||
|
|
const msg = JSON.stringify({ id, method, params });
|
||
|
|
pendingCallbacks.set(id, { resolve, reject });
|
||
|
|
ws.send(msg);
|
||
|
|
|
||
|
|
setTimeout(() => {
|
||
|
|
if (pendingCallbacks.has(id)) {
|
||
|
|
pendingCallbacks.delete(id);
|
||
|
|
reject(new Error(`Timeout for ${method}`));
|
||
|
|
}
|
||
|
|
}, 30000);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
async function main() {
|
||
|
|
log('开始 NodeSeek VP404 签到任务');
|
||
|
|
|
||
|
|
let ws;
|
||
|
|
let targetId = null;
|
||
|
|
|
||
|
|
try {
|
||
|
|
// Step 1: Get browser WebSocket URL
|
||
|
|
log('获取 CDP 连接信息...');
|
||
|
|
const resp = await fetch(`http://127.0.0.1:${CDP_PORT}/json/version`);
|
||
|
|
const version = await resp.json();
|
||
|
|
const browserWsUrl = version.webSocketDebuggerUrl;
|
||
|
|
log(`Browser WS: ${browserWsUrl}`);
|
||
|
|
|
||
|
|
// Step 2: Connect to browser
|
||
|
|
ws = new WebSocket(browserWsUrl);
|
||
|
|
|
||
|
|
ws.on('message', (data) => {
|
||
|
|
try {
|
||
|
|
const msg = JSON.parse(data.toString());
|
||
|
|
|
||
|
|
// Handle command responses
|
||
|
|
if (msg.id && pendingCallbacks.has(msg.id)) {
|
||
|
|
const { resolve, reject } = pendingCallbacks.get(msg.id);
|
||
|
|
pendingCallbacks.delete(msg.id);
|
||
|
|
if (msg.error) {
|
||
|
|
reject(new Error(msg.error.message || JSON.stringify(msg.error)));
|
||
|
|
} else {
|
||
|
|
resolve(msg.result);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handle load event
|
||
|
|
if (msg.method === 'Page.loadEventFired' && loadEventResolve) {
|
||
|
|
loadEventResolve();
|
||
|
|
loadEventResolve = null;
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
// Ignore
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
await new Promise((resolve, reject) => {
|
||
|
|
ws.on('open', resolve);
|
||
|
|
ws.on('error', reject);
|
||
|
|
});
|
||
|
|
log('CDP 连接成功');
|
||
|
|
|
||
|
|
// Step 3: Create new target
|
||
|
|
log('创建新标签页...');
|
||
|
|
const targetResult = await send(ws, 'Target.createTarget', {
|
||
|
|
url: 'about:blank'
|
||
|
|
});
|
||
|
|
targetId = targetResult.targetId;
|
||
|
|
log(`Target ID: ${targetId}`);
|
||
|
|
|
||
|
|
// Step 4: Get target's WebSocket URL via HTTP
|
||
|
|
const targetsResp = await fetch(`http://127.0.0.1:${CDP_PORT}/json`);
|
||
|
|
const targets = await targetsResp.json();
|
||
|
|
const pageTarget = targets.find(t => t.id === targetId);
|
||
|
|
|
||
|
|
if (!pageTarget) {
|
||
|
|
throw new Error('找不到创建的标签页');
|
||
|
|
}
|
||
|
|
|
||
|
|
const pageWsUrl = pageTarget.webSocketDebuggerUrl;
|
||
|
|
log(`Page WS: ${pageWsUrl}`);
|
||
|
|
|
||
|
|
// Step 5: Close browser connection and connect to page
|
||
|
|
ws.close();
|
||
|
|
await sleep(500);
|
||
|
|
|
||
|
|
ws = new WebSocket(pageWsUrl);
|
||
|
|
ws.on('message', (data) => {
|
||
|
|
try {
|
||
|
|
const msg = JSON.parse(data.toString());
|
||
|
|
|
||
|
|
if (msg.id && pendingCallbacks.has(msg.id)) {
|
||
|
|
const { resolve, reject } = pendingCallbacks.get(msg.id);
|
||
|
|
pendingCallbacks.delete(msg.id);
|
||
|
|
if (msg.error) {
|
||
|
|
reject(new Error(msg.error.message || JSON.stringify(msg.error)));
|
||
|
|
} else {
|
||
|
|
resolve(msg.result);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (msg.method === 'Page.loadEventFired' && loadEventResolve) {
|
||
|
|
loadEventResolve();
|
||
|
|
loadEventResolve = null;
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
// Ignore
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
await new Promise((resolve, reject) => {
|
||
|
|
ws.on('open', resolve);
|
||
|
|
ws.on('error', reject);
|
||
|
|
});
|
||
|
|
log('已连接到页面');
|
||
|
|
|
||
|
|
// Step 6: Enable domains
|
||
|
|
await send(ws, 'Page.enable');
|
||
|
|
await send(ws, 'Network.enable');
|
||
|
|
|
||
|
|
// Step 7: Set cookie
|
||
|
|
log('设置 Cookie...');
|
||
|
|
await send(ws, 'Network.setCookie', {
|
||
|
|
name: '_nk',
|
||
|
|
value: COOKIE_VALUE,
|
||
|
|
domain: '.nodeseek.com',
|
||
|
|
path: '/'
|
||
|
|
});
|
||
|
|
log('Cookie 设置成功');
|
||
|
|
|
||
|
|
// Step 8: Navigate
|
||
|
|
log('导航到 NodeSeek...');
|
||
|
|
const loadPromise = new Promise(resolve => { loadEventResolve = resolve; });
|
||
|
|
await send(ws, 'Page.navigate', { url: TARGET_URL });
|
||
|
|
|
||
|
|
log('等待页面加载...');
|
||
|
|
await loadPromise;
|
||
|
|
log('页面加载完成');
|
||
|
|
|
||
|
|
// Step 9: Wait 8 seconds
|
||
|
|
log('等待 8 秒...');
|
||
|
|
await sleep(8000);
|
||
|
|
|
||
|
|
// Step 10: Call attendance API
|
||
|
|
log('调用签到 API...');
|
||
|
|
const evalResult = await send(ws, 'Runtime.evaluate', {
|
||
|
|
expression: `fetch('/api/attendance',{
|
||
|
|
method:'POST',
|
||
|
|
headers:{'Content-Type':'application/json'},
|
||
|
|
body:JSON.stringify({random:true})
|
||
|
|
}).then(r => r.json()).catch(e => ({error: e.message}))`,
|
||
|
|
returnByValue: true,
|
||
|
|
awaitPromise: true
|
||
|
|
});
|
||
|
|
|
||
|
|
const result = evalResult.result.value;
|
||
|
|
log(`签到结果: ${JSON.stringify(result)}`);
|
||
|
|
|
||
|
|
// Step 11: Close page
|
||
|
|
log('关闭标签页...');
|
||
|
|
await send(ws, 'Page.close');
|
||
|
|
|
||
|
|
ws.close();
|
||
|
|
log('签到任务完成 ✓');
|
||
|
|
|
||
|
|
} catch (error) {
|
||
|
|
log(`错误: ${error.message}`);
|
||
|
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
||
|
|
try {
|
||
|
|
ws.close();
|
||
|
|
} catch (e) {}
|
||
|
|
}
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
main();
|