autoreghug / worker.js
iyougame's picture
Update worker.js (#1)
2d986c5 verified
import http from 'http';
import puppeteer from 'puppeteer';
import crypto from 'crypto';
/*
Package Information (原 package.json 内容):
- Name: mindvideo-hf-deploy
- Version: 1.0.0
- Description: MindVideo 多账号机器人 - Hugging Face 部署版
- Main: worker.js
- Type: module
- Dependencies: puppeteer ^23.0.0, @puppeteer/browsers ^2.0.0
- Node Engine: >=18.0.0
System Dependencies (原 requirements.txt 内容):
- @supabase/supabase-js>=2.38.0 (Python package, 仅作说明)
- node-fetch>=3.3.2 (Python package, 仅作说明)
- System dependencies: chromium, libgtk-3-0, libx11-xcb1, libxcomposite1, etc. (通过 Dockerfile 安装)
*/
// 配置信息 - 基于 HF 部署版本优化
// Hugging Face Spaces 配置: 端口 7860,监听所有接口 (原 config.capnp 内容)
const CONFIG = {
PORT: process.env.PORT || 7860,
// Supabase 配置
SUPABASE_URL: process.env.SUPABASE_URL,
SUPABASE_KEY: process.env.SUPABASE_KEY,
// MindVideo 配置
MINDVIDEO_LOGIN_URL: "https://www.mindvideo.ai/zh/auth/signin/",
MINDVIDEO_CHECKIN_API: "https://api.mindvideo.ai/api/checkin",
SIGN_APP_KEY: "s#c_120*AB",
USER_AGENT: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
};
// 全局变量
let browser = null;
let debugInfo = { capturedRequests: [] };
// Supabase 存储服务
class SupabaseService {
constructor() {
this.url = CONFIG.SUPABASE_URL;
this.key = CONFIG.SUPABASE_KEY;
this.enabled = !!(this.url && this.key);
}
async request(method, endpoint, data = null) {
if (!this.enabled) return null;
try {
const options = {
method,
headers: {
'Authorization': `Bearer ${this.key}`,
'Content-Type': 'application/json',
'apikey': this.key
}
};
if (data) {
options.body = JSON.stringify(data);
}
const response = await fetch(`${this.url}/rest/v1/${endpoint}`, options);
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
return await response.json();
} catch (error) {
console.error('❌ Supabase 请求失败:', error.message);
return null;
}
}
async insert(table, data) {
return await this.request('POST', table, data);
}
async select(table, columns = '*', filters = {}) {
const queryString = Object.entries(filters)
.map(([key, value]) => `${key}=eq.${encodeURIComponent(value)}`)
.join('&');
const endpoint = `${table}?select=${columns}${queryString ? '&' + queryString : ''}`;
return await this.request('GET', endpoint);
}
async update(table, data, filters) {
const queryString = Object.entries(filters)
.map(([key, value]) => `${key}=eq.${encodeURIComponent(value)}`)
.join('&');
const endpoint = `${table}?${queryString}`;
return await this.request('PATCH', endpoint, data);
}
}
// 临时邮箱服务类 - 负责与linshiyou.com网站交互
class LinshiyouEmailService {
constructor() {
this.baseUrl = 'https://www.linshiyou.com';
this.userAgent = CONFIG.USER_AGENT;
this.browser = null;
this.page = null;
this.timeout = 30000;
}
// 初始化浏览器
async initBrowser() {
if (!this.browser) {
// Hugging Face 环境优化的浏览器配置
const isHfEnvironment = process.env.SPACE_ID !== undefined || process.env.HF_SPACE !== undefined;
this.browser = await puppeteer.launch({
headless: 'new',
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
'--no-first-run',
'--no-zygote',
...(isHfEnvironment ? [] : ['--single-process']), // HF环境避免单进程模式
'--disable-gpu',
'--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows',
'--disable-renderer-backgrounding',
'--disable-features=TranslateUI',
'--disable-extensions',
'--disable-plugins',
'--disable-default-apps',
'--disable-sync',
'--metrics-recording-only',
'--no-report-upload',
'--disable-logging',
'--silent',
'--log-level=3'
].concat(isHfEnvironment ? [
// HF 环境特定优化
'--memory-pressure-off',
'--max_old_space_size=256',
'--optimize-for-size'
] : [])
});
this.page = await this.browser.newPage();
await this.page.setUserAgent(this.userAgent);
await this.page.setViewport({ width: 1366, height: 768 });
}
return this.browser;
}
// 创建临时邮箱
async createTempEmail() {
try {
console.log('📧 开始创建临时邮箱...');
await this.initBrowser();
// 访问linshiyou.com主页
await this.page.goto(this.baseUrl, { waitUntil: 'networkidle2', timeout: this.timeout });
// 等待页面加载完成,尝试多种可能的邮箱输入框选择器
let emailSelector = null;
const possibleSelectors = ['#email', '#email-input', 'input[type="email"]', 'input[name="email"]'];
for (const selector of possibleSelectors) {
try {
await this.page.waitForSelector(selector, { timeout: 5000 });
emailSelector = selector;
break;
} catch (e) {
continue;
}
}
if (!emailSelector) {
// 如果找不到输入框,尝试直接使用生成的邮箱地址
const randomPrefix = this.generateRandomString(8);
const emailAddress = `${randomPrefix}@linshiyou.com`;
console.log(`✅ 生成临时邮箱地址: ${emailAddress}`);
return {
email: emailAddress,
created_at: new Date().toISOString(),
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
};
}
// 清空输入框并输入生成的邮箱地址
await this.page.evaluate((selector) => {
const element = document.querySelector(selector);
if (element) element.value = '';
}, emailSelector);
const randomPrefix = this.generateRandomString(8);
const emailAddress = `${randomPrefix}@linshiyou.com`;
await this.page.type(emailSelector, emailAddress);
// 查找并点击创建按钮
let createButton = null;
const possibleButtonSelectors = ['#create-email', '#submit', '#create', 'button[type="submit"]', 'button:contains("创建")', 'button:contains("Create")'];
for (const selector of possibleButtonSelectors) {
try {
if (selector.includes(':contains(')) {
// 处理包含文本的选择器
const buttonText = selector.match(/:contains\("([^"]+)"\)/)[1];
createButton = await this.page.evaluateHandle((text) => {
const buttons = Array.from(document.querySelectorAll('button'));
return buttons.find(btn => btn.textContent.includes(text));
}, buttonText);
} else {
await this.page.waitForSelector(selector, { timeout: 5000 });
createButton = await this.page.$(selector);
}
if (createButton) {
await createButton.click();
break;
}
} catch (e) {
continue;
}
}
// 等待邮箱创建完成
await new Promise(resolve => setTimeout(resolve, 2000));
console.log(`✅ 临时邮箱创建成功: ${emailAddress}`);
return {
email: emailAddress,
created_at: new Date().toISOString(),
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
};
} catch (error) {
console.error(`❌ 创建临时邮箱失败: ${error.message}`);
throw new Error(`创建临时邮箱失败: ${error.message}`);
}
}
// 获取收件箱
async getInbox(emailAddress) {
try {
console.log(`📬 检查邮箱收件箱: ${emailAddress}`);
await this.initBrowser();
// 尝试访问收件箱页面
let inboxUrl = `${this.baseUrl}/inbox/${emailAddress}`;
try {
await this.page.goto(inboxUrl, { waitUntil: 'networkidle2', timeout: this.timeout });
} catch (e) {
// 如果直接访问失败,尝试从主页导航
await this.page.goto(this.baseUrl, { waitUntil: 'networkidle2', timeout: this.timeout });
// 查找收件箱链接或按钮
const inboxButton = await this.page.evaluateHandle(() => {
const links = Array.from(document.querySelectorAll('a'));
return links.find(link =>
link.textContent.includes('收件箱') ||
link.textContent.includes('inbox') ||
link.href.includes('inbox')
);
});
if (inboxButton) {
await inboxButton.click();
await this.page.waitForNavigation({ waitUntil: 'networkidle2', timeout: this.timeout });
}
}
// 等待邮件列表加载
await new Promise(resolve => setTimeout(resolve, 2000));
// 尝试提取邮件列表
const emails = await this.page.evaluate(() => {
const emailElements = document.querySelectorAll('.email-item, .mail-item, .message-item, tr.email');
return Array.from(emailElements).map(el => {
const subjectElement = el.querySelector('.subject, .title, .message-subject');
const senderElement = el.querySelector('.sender, .from, .message-from');
const timeElement = el.querySelector('.time, .date, .message-time');
return {
subject: subjectElement ? subjectElement.textContent.trim() : '',
sender: senderElement ? senderElement.textContent.trim() : '',
time: timeElement ? timeElement.textContent.trim() : '',
id: el.getAttribute('data-email-id') || el.getAttribute('id') || ''
};
}).filter(email => email.subject || email.sender);
});
console.log(`📬 找到 ${emails.length} 封邮件`);
return emails;
} catch (error) {
console.error(`❌ 获取收件箱失败: ${error.message}`);
// 返回空数组而不是抛出错误,让调用方处理
return [];
}
}
// 获取最新邮件
async getLatestEmail(emailAddress) {
try {
console.log(`📨 获取最新邮件: ${emailAddress}`);
const emails = await this.getInbox(emailAddress);
if (emails.length === 0) {
console.log('📭 收件箱为空');
return null;
}
const latestEmail = emails[0];
console.log(`📨 最新邮件: ${latestEmail.subject}`);
// 尝试点击邮件查看内容
try {
if (latestEmail.id) {
const emailElement = await this.page.$(`[data-email-id="${latestEmail.id}"], #${latestEmail.id}`);
if (emailElement) {
await emailElement.click();
await this.page.waitForSelector('.email-content, .mail-content, .message-content', { timeout: 5000 });
}
}
} catch (e) {
// 如果点击失败,继续使用已有信息
console.log('⚠️ 无法点击邮件,使用基本信息');
}
// 等待邮件内容加载
await new Promise(resolve => setTimeout(resolve, 2000));
// 提取邮件内容
let content = '';
try {
content = await this.page.$eval('.email-content, .mail-content, .message-content, .content', el => el.textContent);
} catch (e) {
// 如果找不到内容区域,尝试获取整个页面文本
content = await this.page.evaluate(() => document.body.textContent);
}
const result = {
...latestEmail,
content: content
};
console.log(`📨 邮件内容提取成功,长度: ${content.length} 字符`);
return result;
} catch (error) {
console.error(`❌ 获取最新邮件失败: ${error.message}`);
return null;
}
}
// 提取验证码
async extractVerificationCode(emailContent) {
console.log('🔍 开始提取验证码...');
// 匹配验证码的正则表达式
const patterns = [
/\b(\d{6})\b/g,
/验证码[::\s]*(\d{6})/,
/code[::\s]*(\d{6})/i,
/verification[::\s]*(\d{6})/i,
/验证[::\s]*(\d{6})/,
/码[::\s]*(\d{6})/
];
for (const pattern of patterns) {
const matches = emailContent.match(pattern);
if (matches && matches.length > 0) {
// 返回最后一个匹配的6位数字
const code = matches[matches.length - 1].match(/\d{6}/)[0];
console.log(`✅ 验证码提取成功: ${code}`);
return code;
}
}
// 如果正则匹配失败,尝试查找所有6位数字
const allNumbers = emailContent.match(/\b\d{6}\b/g);
if (allNumbers && allNumbers.length > 0) {
const code = allNumbers[allNumbers.length - 1];
console.log(`✅ 验证码提取成功(备选方法): ${code}`);
return code;
}
console.error('❌ 无法从邮件内容中提取验证码');
throw new Error('无法从邮件内容中提取验证码');
}
// 等待验证邮件
async waitForVerificationEmail(emailAddress, maxWaitTime = 300000) {
console.log(`⏳ 等待验证邮件,最长等待: ${maxWaitTime/1000}秒`);
const startTime = Date.now();
const checkInterval = 5000; // 5秒检查一次
let attempts = 0;
while (Date.now() - startTime < maxWaitTime) {
attempts++;
console.log(`📧 检查邮件 (${attempts}次)...`);
try {
const latestEmail = await this.getLatestEmail(emailAddress);
if (latestEmail && latestEmail.content) {
try {
const verificationCode = await this.extractVerificationCode(latestEmail.content);
return {
code: verificationCode,
email: latestEmail,
received_at: new Date().toISOString(),
attempts: attempts
};
} catch (extractError) {
console.log(`⚠️ 邮件已收到但验证码提取失败: ${extractError.message}`);
}
}
} catch (error) {
console.log(`⚠️ 检查邮件时出错: ${error.message}`);
}
await new Promise(resolve => setTimeout(resolve, checkInterval));
}
console.error(`❌ 等待验证邮件超时,已尝试 ${attempts} 次`);
throw new Error(`等待验证邮件超时,已尝试 ${attempts} 次`);
}
// 生成随机字符串
generateRandomString(length) {
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
// 关闭浏览器
async closeBrowser() {
if (this.page) {
await this.page.close();
this.page = null;
}
if (this.browser) {
await this.browser.close();
this.browser = null;
}
}
}
// MindVideo账号注册服务类
class MindVideoRegistrationService {
constructor() {
this.registrationUrl = 'https://www.mindvideo.ai/zh/auth/signup/';
this.browser = null;
this.page = null;
this.emailService = new LinshiyouEmailService();
this.timeout = 30000;
}
// 初始化浏览器
async initBrowser() {
if (!this.browser) {
// Hugging Face 环境优化的浏览器配置
const isHfEnvironment = process.env.SPACE_ID !== undefined || process.env.HF_SPACE !== undefined;
this.browser = await puppeteer.launch({
headless: 'new',
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
'--no-first-run',
'--no-zygote',
...(isHfEnvironment ? [] : ['--single-process']), // HF环境避免单进程模式
'--disable-gpu',
'--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows',
'--disable-renderer-backgrounding',
'--disable-features=TranslateUI',
'--disable-extensions',
'--disable-plugins',
'--disable-default-apps',
'--disable-sync',
'--metrics-recording-only',
'--no-report-upload',
'--disable-logging',
'--silent',
'--log-level=3'
].concat(isHfEnvironment ? [
// HF 环境特定优化
'--memory-pressure-off',
'--max_old_space_size=256',
'--optimize-for-size'
] : [])
});
this.page = await this.browser.newPage();
await this.page.setUserAgent(CONFIG.USER_AGENT);
await this.page.setViewport({ width: 1366, height: 768 });
}
return this.browser;
}
// 注册单个账号
async registerAccount(email, password) {
try {
console.log(`🚀 开始注册账号: ${email}`);
await this.initBrowser();
// 访问注册页面
await this.page.goto(this.registrationUrl, { waitUntil: 'networkidle2', timeout: this.timeout });
console.log('📱 已访问注册页面');
// 等待页面加载
await new Promise(resolve => setTimeout(resolve, 3000));
// 查找并填写注册表单
const formFields = await this.findRegistrationFormFields();
if (!formFields.email || !formFields.password) {
throw new Error('未找到注册表单字段');
}
// 清空并填写邮箱
await this.page.evaluate((selector) => {
const element = document.querySelector(selector);
if (element) element.value = '';
}, formFields.email);
await this.page.type(formFields.email, email);
// 清空并填写密码
await this.page.evaluate((selector) => {
const element = document.querySelector(selector);
if (element) element.value = '';
}, formFields.password);
await this.page.type(formFields.password, password);
// 如果有确认密码字段
if (formFields.confirmPassword) {
await this.page.evaluate((selector) => {
const element = document.querySelector(selector);
if (element) element.value = '';
}, formFields.confirmPassword);
await this.page.type(formFields.confirmPassword, password);
}
// 处理条款同意(如果存在)
if (formFields.agreeCheckbox) {
const isChecked = await this.page.evaluate((selector) => {
const element = document.querySelector(selector);
return element ? element.checked : false;
}, formFields.agreeCheckbox);
if (!isChecked) {
await this.page.click(formFields.agreeCheckbox);
console.log('✅ 已同意服务条款');
}
}
// 提交注册表单
console.log('📤 提交注册表单...');
await this.submitRegistrationForm(formFields.submitButton);
// 等待页面响应
await new Promise(resolve => setTimeout(resolve, 3000));
// 检查是否需要邮箱验证
const needsVerification = await this.checkEmailVerificationRequired();
if (needsVerification) {
console.log('📧 需要进行邮箱验证');
// 处理邮箱验证
const verificationResult = await this.verifyEmail(email);
console.log('✅ 邮箱验证完成');
} else {
console.log('⚠️ 注册可能不需要邮箱验证,或已自动完成');
}
// 提取用户Token
const token = await this.extractToken();
if (token) {
console.log(`✅ 账号注册成功,Token已获取`);
return {
success: true,
email: email,
password: password,
token: token,
created_at: new Date().toISOString()
};
} else {
console.log('⚠️ 注册可能成功但未获取到Token');
return {
success: true,
email: email,
password: password,
token: null,
created_at: new Date().toISOString(),
warning: '注册成功但未获取到Token'
};
}
} catch (error) {
console.error(`❌ 注册账号失败: ${error.message}`);
return {
success: false,
email: email,
error: error.message,
created_at: new Date().toISOString()
};
}
}
// 查找注册表单字段
async findRegistrationFormFields() {
const fields = {
email: null,
password: null,
confirmPassword: null,
agreeCheckbox: null,
submitButton: null
};
// 查找邮箱字段
const emailSelectors = [
'#email', '#email-input', 'input[type="email"]', 'input[name="email"]',
'[placeholder*="邮箱"]', '[placeholder*="email"]'
];
for (const selector of emailSelectors) {
try {
await this.page.waitForSelector(selector, { timeout: 2000 });
fields.email = selector;
break;
} catch (e) {
continue;
}
}
// 查找密码字段
const passwordSelectors = [
'#password', '#password-input', 'input[type="password"]', 'input[name="password"]',
'[placeholder*="密码"]', '[placeholder*="password"]'
];
for (const selector of passwordSelectors) {
try {
await this.page.waitForSelector(selector, { timeout: 2000 });
fields.password = selector;
break;
} catch (e) {
continue;
}
}
// 查找确认密码字段
const confirmPasswordSelectors = [
'#confirm-password', '#confirmPassword', '#password-confirm',
'input[name="confirm_password"]', '[placeholder*="确认密码"]', '[placeholder*="confirm"]'
];
for (const selector of confirmPasswordSelectors) {
try {
await this.page.waitForSelector(selector, { timeout: 1000 });
fields.confirmPassword = selector;
break;
} catch (e) {
continue;
}
}
// 查找同意条款复选框
const checkboxSelectors = [
'#agree', '#terms', '#agree-terms', '#terms-checkbox',
'input[type="checkbox"][name*="agree"]', 'input[type="checkbox"][name*="terms"]'
];
for (const selector of checkboxSelectors) {
try {
await this.page.waitForSelector(selector, { timeout: 1000 });
fields.agreeCheckbox = selector;
break;
} catch (e) {
continue;
}
}
// 查找提交按钮
const submitSelectors = [
'#register', '#register-btn', '#signup', '#signup-btn',
'button[type="submit"]', 'input[type="submit"]',
'button:contains("注册")', 'button:contains("注册")', 'button:contains("Sign Up")'
];
for (const selector of submitSelectors) {
try {
if (selector.includes(':contains(')) {
// 处理包含文本的选择器
const buttonText = selector.match(/:contains\("([^"]+)"\)/)[1];
const button = await this.page.evaluateHandle((text) => {
const buttons = Array.from(document.querySelectorAll('button, input[type="submit"]'));
return buttons.find(btn => btn.textContent.includes(text) || btn.value.includes(text));
}, buttonText);
if (button) {
fields.submitButton = button;
break;
}
} else {
await this.page.waitForSelector(selector, { timeout: 2000 });
fields.submitButton = selector;
break;
}
} catch (e) {
continue;
}
}
console.log('📋 找到的表单字段:', fields);
return fields;
}
// 提交注册表单
async submitRegistrationForm(submitButton) {
try {
if (typeof submitButton === 'string') {
await this.page.click(submitButton);
} else {
// 如果是ElementHandle
await submitButton.click();
}
console.log('📤 注册表单已提交');
} catch (error) {
// 尝试使用JavaScript提交
await this.page.evaluate(() => {
const forms = document.querySelectorAll('form');
if (forms.length > 0) {
forms[forms.length - 1].submit();
}
});
console.log('📤 使用JavaScript提交注册表单');
}
}
// 检查是否需要邮箱验证
async checkEmailVerificationRequired() {
try {
// 检查页面是否包含验证相关的元素
const verificationSelectors = [
'#verification-code', '#code', '#verification',
'input[name="verification_code"]', 'input[name="code"]',
'[placeholder*="验证码"]', '[placeholder*="verification"]',
'[placeholder*="code"]'
];
for (const selector of verificationSelectors) {
try {
await this.page.waitForSelector(selector, { timeout: 2000 });
return true; // 找到验证码输入框,需要验证
} catch (e) {
continue;
}
}
// 检查页面文本内容
const pageText = await this.page.evaluate(() => document.body.textContent.toLowerCase());
const verificationKeywords = ['验证码', 'verification code', '邮箱验证', 'email verification', '请查收邮件'];
return verificationKeywords.some(keyword => pageText.includes(keyword));
} catch (error) {
console.log('⚠️ 检查验证要求时出错:', error.message);
return false;
}
}
// 处理邮箱验证
async verifyEmail(emailAddress) {
try {
console.log(`📧 开始邮箱验证流程: ${emailAddress}`);
// 获取验证码
const verificationData = await this.emailService.waitForVerificationEmail(
emailAddress,
120000 // 2分钟超时
);
console.log(`📨 收到验证码: ${verificationData.code}`);
// 查找验证码输入框
const codeSelectors = [
'#verification-code', '#code', '#verification', '#verify-code',
'input[name="verification_code"]', 'input[name="code"]',
'[placeholder*="验证码"]', '[placeholder*="verification"]', '[placeholder*="code"]'
];
let codeInputSelector = null;
for (const selector of codeSelectors) {
try {
await this.page.waitForSelector(selector, { timeout: 5000 });
codeInputSelector = selector;
break;
} catch (e) {
continue;
}
}
if (!codeInputSelector) {
throw new Error('未找到验证码输入框');
}
// 输入验证码
await this.page.type(codeInputSelector, verificationData.code);
console.log('🔢 验证码已输入');
// 查找并点击验证按钮
const verifyButtonSelectors = [
'#verify-btn', '#verify', '#submit-btn', '#submit',
'button[type="submit"]', 'button:contains("验证")', 'button:contains("Verify")',
'button:contains("确认")', 'button:contains("Submit")'
];
let verifyButton = null;
for (const selector of verifyButtonSelectors) {
try {
if (selector.includes(':contains(')) {
const buttonText = selector.match(/:contains\("([^"]+)"\)/)[1];
verifyButton = await this.page.evaluateHandle((text) => {
const buttons = Array.from(document.querySelectorAll('button, input[type="submit"]'));
return buttons.find(btn => btn.textContent.includes(text) || btn.value.includes(text));
}, buttonText);
} else {
await this.page.waitForSelector(selector, { timeout: 3000 });
verifyButton = await this.page.$(selector);
}
if (verifyButton) {
await verifyButton.click();
break;
}
} catch (e) {
continue;
}
}
if (!verifyButton) {
// 尝试按回车键
await this.page.keyboard.press('Enter');
console.log('⌨️ 使用回车键提交验证码');
} else {
console.log('🔘 已点击验证按钮');
}
// 等待验证结果
await new Promise(resolve => setTimeout(resolve, 3000));
// 检查验证是否成功
const success = await this.checkVerificationSuccess();
if (success) {
return {
success: true,
verification_code: verificationData.code,
verified_at: new Date().toISOString()
};
} else {
throw new Error('邮箱验证失败');
}
} catch (error) {
throw new Error(`邮箱验证失败: ${error.message}`);
}
}
// 检查验证是否成功
async checkVerificationSuccess() {
try {
// 检查是否有成功消息
const successSelectors = [
'#success-message', '#success', '.success', '.alert-success',
'[class*="success"]', '[id*="success"]'
];
for (const selector of successSelectors) {
try {
await this.page.waitForSelector(selector, { timeout: 2000 });
console.log('✅ 找到验证成功标识');
return true;
} catch (e) {
continue;
}
}
// 检查页面文本
const pageText = await this.page.evaluate(() => document.body.textContent.toLowerCase());
const successKeywords = ['验证成功', 'verification successful', '注册成功', 'registration successful', '验证完成'];
const hasSuccessKeyword = successKeywords.some(keyword => pageText.includes(keyword));
// 检查是否被重定向到其他页面(通常是主页)
const currentUrl = this.page.url();
const isRedirected = !currentUrl.includes('signup') && !currentUrl.includes('verify');
return hasSuccessKeyword || isRedirected;
} catch (error) {
console.log('⚠️ 检查验证结果时出错:', error.message);
return false;
}
}
// 提取用户Token
async extractToken() {
try {
console.log('🔑 开始提取用户Token...');
// 等待页面稳定
await new Promise(resolve => setTimeout(resolve, 2000));
// 尝试多种方式提取Token
// 1. 从localStorage提取
let token = await this.page.evaluate(() => {
const keys = ['token', 'auth_token', 'jwt_token', 'access_token', 'mindvideo_token'];
for (const key of keys) {
const value = localStorage.getItem(key);
if (value && value.startsWith('eyJ')) { // JWT通常以eyJ开头
return value;
}
}
return null;
});
if (token) {
console.log('🔑 从localStorage提取到Token');
return token;
}
// 2. 从cookie提取
const cookies = await this.page.cookies();
const cookieToken = cookies.find(c =>
(c.name.includes('token') || c.name.includes('auth')) &&
c.value.startsWith('eyJ')
);
if (cookieToken) {
console.log('🔑 从Cookie提取到Token');
return cookieToken.value;
}
// 3. 从页面脚本或全局变量提取
token = await this.page.evaluate(() => {
// 检查常见的全局变量
if (window.token) return window.token;
if (window.authToken) return window.authToken;
if (window.user && window.user.token) return window.user.token;
// 检查script标签中的token
const scripts = Array.from(document.querySelectorAll('script'));
for (const script of scripts) {
const text = script.textContent;
const tokenMatch = text.match(/(?:token|auth[_-]?token)\s*[:=]\s*['"`]([^'"`]+)['"`]/i);
if (tokenMatch && tokenMatch[1].startsWith('eyJ')) {
return tokenMatch[1];
}
}
return null;
});
if (token) {
console.log('🔑 从页面脚本提取到Token');
return token;
}
// 4. 从API响应中提取(如果有请求拦截)
try {
const responses = await this.page.evaluate(() => {
if (window.performance && window.performance.getEntriesByType) {
const entries = window.performance.getEntriesByType('navigation');
return entries.map(entry => entry.name);
}
return [];
});
console.log('🔍 检查API响应记录...');
} catch (e) {
console.log('⚠️ 无法检查API响应记录');
}
console.log('⚠️ 未能提取到用户Token');
return null;
} catch (error) {
console.error(`❌ 提取Token失败: ${error.message}`);
return null;
}
}
// 生成随机密码
generatePassword(prefix = 'Pwd', length = 12) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%';
let password = prefix;
for (let i = prefix.length; i < length; i++) {
password += chars.charAt(Math.floor(Math.random() * chars.length));
}
return password;
}
// 关闭浏览器
async closeBrowser() {
await this.emailService.closeBrowser();
if (this.page) {
await this.page.close();
this.page = null;
}
if (this.browser) {
await this.browser.close();
this.browser = null;
}
}
}
// 注册控制器类 - 负责协调整个注册流程
class RegistrationController {
constructor() {
this.registrationService = new MindVideoRegistrationService();
this.storageService = new StorageService();
this.maxConcurrent = 3; // 最大并发数
this.activeTasks = new Map(); // 活跃任务管理
}
// 批量注册账号
async batchRegisterAccounts(count, options = {}) {
try {
console.log(`🚀 启动批量注册任务,账号数量: ${count}`);
const taskId = this.generateTaskId();
const {
passwordPrefix = 'Pwd',
passwordLength = 12
} = options;
// 创建任务记录
await this.createTaskRecord(taskId, count, options);
// 启动批量注册任务(异步)
this.runBatchRegistration(taskId, count, passwordPrefix, passwordLength);
return {
success: true,
task_id: taskId,
message: `已启动${count}个账号的批量注册任务`,
estimated_time: Math.ceil(count / this.maxConcurrent) * 3 + '分钟'
};
} catch (error) {
console.error(`❌ 启动批量注册任务失败: ${error.message}`);
return {
success: false,
error: error.message
};
}
}
// 运行批量注册
async runBatchRegistration(taskId, count, passwordPrefix, passwordLength) {
console.log(`🔄 开始执行批量注册任务: ${taskId}`);
const results = [];
const concurrent = Math.min(this.maxConcurrent, count);
try {
// 初始化注册服务
await this.registrationService.initBrowser();
// 分批处理
for (let i = 0; i < count; i += concurrent) {
const batch = [];
const batchEnd = Math.min(i + concurrent, count);
console.log(`📦 处理批次 ${Math.floor(i/concurrent) + 1}: 账号 ${i+1}-${batchEnd}`);
// 创建并发任务
for (let j = i; j < batchEnd; j++) {
const accountIndex = j + 1;
const password = this.registrationService.generatePassword(passwordPrefix, passwordLength);
batch.push(this.registerSingleAccount(taskId, accountIndex, password));
}
// 等待当前批次完成
const batchResults = await Promise.allSettled(batch);
// 处理批次结果
for (let k = 0; k < batchResults.length; k++) {
const result = batchResults[k];
const accountIndex = i + k + 1;
if (result.status === 'fulfilled') {
results.push(result.value);
if (result.value.status === 'success') {
await this.updateTaskProgress(taskId, 'completed');
} else {
await this.updateTaskProgress(taskId, 'failed');
}
} else {
results.push({
task_id: taskId,
account_index: accountIndex,
status: 'failed',
error_message: result.reason.message,
created_at: new Date().toISOString()
});
await this.updateTaskProgress(taskId, 'failed');
}
}
// 更新任务状态
await this.updateTaskStatus(taskId, results);
// 批次间延迟
if (batchEnd < count) {
console.log('⏳ 批次间延迟 3 秒...');
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
// 标记任务完成
await this.completeTask(taskId, results);
console.log(`✅ 批量注册任务完成: ${taskId}`);
} catch (error) {
console.error(`❌ 批量注册任务失败: ${error.message}`);
await this.failTask(taskId, error.message);
} finally {
await this.registrationService.closeBrowser();
}
}
// 单个账号注册
async registerSingleAccount(taskId, accountIndex, password) {
try {
console.log(`🔐 注册单个账号 [${accountIndex}]`);
// 创建临时邮箱
const tempEmail = await this.registrationService.emailService.createTempEmail();
console.log(`📧 创建临时邮箱成功: ${tempEmail.email}`);
// 执行注册流程
const registrationResult = await this.registrationService.registerAccount(
tempEmail.email,
password
);
const result = {
task_id: taskId,
account_index: accountIndex,
email: tempEmail.email,
password: password, // 这里应该加密存储
status: registrationResult.success ? 'success' : 'failed',
token: registrationResult.token || null,
error_message: registrationResult.error || null,
created_at: new Date().toISOString()
};
// 保存到数据库
if (registrationResult.success) {
await this.storageService.saveAccount(result);
console.log(`💾 账号 ${accountIndex} 已保存到数据库`);
}
return result;
} catch (error) {
console.error(`❌ 注册账号 ${accountIndex} 失败: ${error.message}`);
return {
task_id: taskId,
account_index: accountIndex,
status: 'failed',
error_message: error.message,
created_at: new Date().toISOString()
};
}
}
// 获取注册进度
async getRegistrationProgress(taskId) {
try {
console.log(`📊 查询注册进度: ${taskId}`);
const task = await this.storageService.getTask(taskId);
if (!task) {
return {
success: false,
error: '任务不存在'
};
}
const results = await this.storageService.getTaskResults(taskId);
const progress = {
total: task.total_count,
completed: task.completed_count,
failed: task.failed_count,
cancelled: task.cancelled_count || 0,
percentage: Math.round((task.completed_count / task.total_count) * 100)
};
return {
success: true,
task_id: taskId,
status: task.status,
progress: progress,
results: results,
created_at: task.created_at,
updated_at: task.updated_at,
completed_at: task.completed_at
};
} catch (error) {
console.error(`❌ 查询注册进度失败: ${error.message}`);
return {
success: false,
error: error.message
};
}
}
// 取消注册任务
async cancelRegistrationTask(taskId) {
try {
console.log(`🛑 取消注册任务: ${taskId}`);
// 更新任务状态
const updated = await this.storageService.updateTaskStatus(taskId, 'cancelled');
if (!updated) {
return {
success: false,
error: '任务不存在或无法取消'
};
}
// 从活跃任务中移除
this.activeTasks.delete(taskId);
return {
success: true,
message: '任务已取消',
cancelled_at: new Date().toISOString()
};
} catch (error) {
console.error(`❌ 取消任务失败: ${error.message}`);
return {
success: false,
error: error.message
};
}
}
// 获取所有注册任务
async getAllRegistrationTasks() {
try {
console.log('📋 获取所有注册任务');
const tasks = await this.storageService.getAllTasks();
return {
success: true,
data: tasks,
total: tasks.length
};
} catch (error) {
console.error(`❌ 获取任务列表失败: ${error.message}`);
return {
success: false,
error: error.message
};
}
}
// 生成任务ID
generateTaskId() {
return `reg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
// 创建任务记录
async createTaskRecord(taskId, totalCount, options = {}) {
const taskData = {
task_id: taskId,
status: 'running',
total_count: totalCount,
completed_count: 0,
failed_count: 0,
cancelled_count: 0,
options: JSON.stringify(options),
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
};
await this.storageService.saveTask(taskData);
this.activeTasks.set(taskId, taskData);
console.log(`📝 创建任务记录: ${taskId}`);
}
// 更新任务进度
async updateTaskProgress(taskId, type) {
try {
if (type === 'completed') {
await this.storageService.incrementCompletedCount(taskId);
} else if (type === 'failed') {
await this.storageService.incrementFailedCount(taskId);
} else if (type === 'cancelled') {
await this.storageService.incrementCancelledCount(taskId);
}
} catch (error) {
console.error(`❌ 更新任务进度失败: ${error.message}`);
}
}
// 更新任务状态
async updateTaskStatus(taskId, results) {
try {
const successCount = results.filter(r => r.status === 'success').length;
const failedCount = results.filter(r => r.status === 'failed').length;
await this.storageService.updateTaskProgress(taskId, {
completed_count: successCount,
failed_count: failedCount,
updated_at: new Date().toISOString()
});
// 更新内存中的任务状态
if (this.activeTasks.has(taskId)) {
const task = this.activeTasks.get(taskId);
task.completed_count = successCount;
task.failed_count = failedCount;
task.updated_at = new Date().toISOString();
}
} catch (error) {
console.error(`❌ 更新任务状态失败: ${error.message}`);
}
}
// 完成任务
async completeTask(taskId, results) {
try {
await this.storageService.updateTaskStatus(taskId, 'completed');
// 保存所有结果
for (const result of results) {
await this.storageService.saveRegistrationResult(result);
}
this.activeTasks.delete(taskId);
console.log(`✅ 任务 ${taskId} 已完成`);
} catch (error) {
console.error(`❌ 完成任务失败: ${error.message}`);
}
}
// 任务失败
async failTask(taskId, errorMessage) {
try {
await this.storageService.updateTaskStatus(taskId, 'failed');
await this.storageService.updateTaskError(taskId, errorMessage);
this.activeTasks.delete(taskId);
console.log(`❌ 任务 ${taskId} 已失败: ${errorMessage}`);
} catch (error) {
console.error(`❌ 标记任务失败失败: ${error.message}`);
}
}
// 获取活跃任务列表
getActiveTasks() {
return Array.from(this.activeTasks.values());
}
// 清理过期任务
async cleanupExpiredTasks() {
try {
console.log('🧹 清理过期任务...');
const expiredTasks = await this.storageService.getExpiredTasks(24); // 24小时前的任务
for (const task of expiredTasks) {
await this.storageService.deleteTask(task.task_id);
this.activeTasks.delete(task.task_id);
}
console.log(`🧹 清理了 ${expiredTasks.length} 个过期任务`);
} catch (error) {
console.error(`❌ 清理过期任务失败: ${error.message}`);
}
}
}
const supabase = new SupabaseService();
// 通用存储服务(使用 Supabase)
class StorageService {
async saveAccount(account) {
return await supabase.insert('accounts', account);
}
async updateAccount(email, data) {
return await supabase.update('accounts', data, { email });
}
async getAccounts() {
return await supabase.select('accounts', '*');
}
async saveTask(task) {
return await supabase.insert('registration_tasks', task);
}
async updateTask(taskId, data) {
return await supabase.update('registration_tasks', data, { task_id: taskId });
}
// ========== 注册任务相关方法 ==========
// 获取单个任务
async getTask(taskId) {
const tasks = await supabase.select('registration_tasks', '*', { task_id: taskId });
return tasks.length > 0 ? tasks[0] : null;
}
// 获取所有任务
async getAllTasks() {
return await supabase.select('registration_tasks', '*', {}, 'created_at.desc');
}
// 更新任务状态
async updateTaskStatus(taskId, status) {
const updateData = {
status,
updated_at: new Date().toISOString()
};
if (status === 'completed') {
updateData.completed_at = new Date().toISOString();
}
return await supabase.update('registration_tasks', updateData, { task_id: taskId });
}
// 更新任务进度
async updateTaskProgress(taskId, progressData) {
const updateData = {
...progressData,
updated_at: new Date().toISOString()
};
return await supabase.update('registration_tasks', updateData, { task_id: taskId });
}
// 更新任务错误信息
async updateTaskError(taskId, errorMessage) {
const updateData = {
error_message: errorMessage,
status: 'failed',
updated_at: new Date().toISOString()
};
return await supabase.update('registration_tasks', updateData, { task_id: taskId });
}
// 增加完成计数
async incrementCompletedCount(taskId) {
return await supabase.rpc('increment_completed_count', { task_id: taskId });
}
// 增加失败计数
async incrementFailedCount(taskId) {
return await supabase.rpc('increment_failed_count', { task_id: taskId });
}
// 增加取消计数
async incrementCancelledCount(taskId) {
return await supabase.rpc('increment_cancelled_count', { task_id: taskId });
}
// 保存注册结果
async saveRegistrationResult(result) {
return await supabase.insert('registration_results', result);
}
// 获取任务结果
async getTaskResults(taskId) {
return await supabase.select('registration_results', '*', { task_id: taskId }, 'account_index.asc');
}
// 获取过期任务
async getExpiredTasks(hoursOld = 24) {
const cutoffTime = new Date(Date.now() - hoursOld * 60 * 60 * 1000).toISOString();
return await supabase.select('registration_tasks', '*', {
created_at: `lt.${cutoffTime}`,
status: 'in.completed' // 不是完成状态的任务
}, 'created_at.asc');
}
// 删除任务
async deleteTask(taskId) {
// 需要先删除相关的结果记录
await supabase.request('DELETE', `registration_results?task_id=eq.${taskId}`);
return await supabase.request('DELETE', `registration_tasks?task_id=eq.${taskId}`);
}
// 获取统计信息
async getRegistrationStats() {
const stats = await supabase.rpc('get_registration_stats');
return stats;
}
}
const storage = new StorageService();
const registrationController = new RegistrationController();
async function initBrowser() {
if (!browser) {
console.log('🚀 启动浏览器...');
// Hugging Face 环境优化的浏览器配置
const isHfEnvironment = process.env.SPACE_ID !== undefined || process.env.HF_SPACE !== undefined;
const launchArgs = [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu',
'--disable-accelerated-2d-canvas',
'--no-first-run',
'--no-zygote',
'--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows',
'--disable-renderer-backgrounding',
'--disable-features=TranslateUI',
'--disable-extensions',
'--disable-plugins',
'--disable-default-apps',
'--disable-sync',
'--metrics-recording-only',
'--no-report-upload',
'--disable-logging',
'--silent',
'--log-level=3'
];
// HF 环境特定优化
if (isHfEnvironment) {
launchArgs.push(
'--memory-pressure-off',
'--max_old_space_size=256',
'--optimize-for-size'
);
console.log('🤖 检测到 Hugging Face 环境,应用优化配置');
} else {
launchArgs.push('--single-process');
}
browser = await puppeteer.launch({
headless: 'new',
args: launchArgs
});
}
return browser;
}
function md5(message) {
return crypto.createHash('md5').update(message).digest('hex');
}
async function generateSign() {
const nonce = crypto.randomUUID().replace(/-/g, '').substring(0, 16);
const timestamp = Date.now();
const signStr = `nonce=${nonce}&timestamp=${timestamp}&app_key=${CONFIG.SIGN_APP_KEY}`;
const sign = md5(signStr);
return JSON.stringify({ nonce, timestamp, sign });
}
async function performCheckIn(token, index = 1, email = null) {
console.log(`📅 [账号 ${index}] ${email ? `(${email}) ` : ''}尝试签到...`);
try {
const signHeader = await generateSign();
const response = await fetch(CONFIG.MINDVIDEO_CHECKIN_API, {
method: 'POST',
headers: {
"accept": "application/json, text/plain, */*",
"authorization": `Bearer ${token}`,
"content-type": "application/json",
"i-lang": "zh-CN",
"i-version": "1.0.8",
"i-sign": signHeader,
"origin": "https://www.mindvideo.ai",
"referer": "https://www.mindvideo.ai/",
"user-agent": CONFIG.USER_AGENT
}
});
const text = await response.text();
let data;
try {
data = JSON.parse(text);
} catch (e) {
console.error(`❌ [账号 ${index}] 签到响应非 JSON:`, text.substring(0, 100));
return { success: false, message: "无效的 JSON 响应" };
}
if (data.code === 0) {
console.log(`✅ [账号 ${index}] 签到成功! 获得积分: ${data.data?.credits}, 连续天数: ${data.data?.current_day}`);
return {
success: true,
credits: data.data?.credits,
total: data.data?.total_credits,
day: data.data?.current_day
};
} else {
console.warn(`⚠️ [账号 ${index}] 签到失败: ${data.message}`);
return { success: false, message: data.message };
}
} catch (error) {
console.error(`❌ [账号 ${index}] 签到过程出错:`, error.message);
return { success: false, error: error.message };
}
}
async function getUserInfo(token, index = 1) {
console.log(`🔍 [账号 ${index}] 查询用户信息...`);
try {
const signHeader = await generateSign();
const response = await fetch('https://api.mindvideo.ai/api/user/info', {
method: 'GET',
headers: {
"accept": "application/json, text/plain, */*",
"authorization": `Bearer ${token}`,
"i-lang": "zh-CN",
"i-version": "1.0.8",
"i-sign": signHeader,
"origin": "https://www.mindvideo.ai",
"referer": "https://www.mindvideo.ai/",
"user-agent": CONFIG.USER_AGENT
}
});
const text = await response.text();
let data;
try {
data = JSON.parse(text);
} catch (e) {
console.warn(`⚠️ [账号 ${index}] 用户信息响应非 JSON`);
return null;
}
if (data.code === 0 && data.data) {
console.log(`✅ [账号 ${index}] 积分: ${data.data.credits || 0}`);
return {
credits: data.data.credits || 0,
email: data.data.email || null,
nickname: data.data.nickname || null,
avatar: data.data.avatar || null
};
} else {
console.warn(`⚠️ [账号 ${index}] 无法获取用户信息`);
return null;
}
} catch (error) {
console.warn(`⚠️ [账号 ${index}] 查询用户信息失败: ${error.message}`);
return null;
}
}
async function loginSingleAccount(browser, username, password, index) {
console.log(`🔄 [账号 ${index}] 开始登录流程: ${username}`);
const page = await browser.newPage();
try {
await page.setUserAgent(CONFIG.USER_AGENT);
// 在登录前清除所有 Cookie 和存储
console.log(`[账号 ${index}] 清除浏览器缓存和 Cookie...`);
const client = await page.target().createCDPSession();
await client.send('Network.clearBrowserCookies');
await client.send('Network.clearBrowserCache');
await page.evaluateOnNewDocument(() => {
localStorage.clear();
sessionStorage.clear();
});
await page.goto(CONFIG.MINDVIDEO_LOGIN_URL, { waitUntil: 'networkidle2', timeout: 60000 });
console.log(`[账号 ${index}] 输入账号密码...`);
await page.waitForSelector('#email', { timeout: 10000 });
await page.evaluate(() => {
if (document.querySelector('#email')) document.querySelector('#email').value = '';
if (document.querySelector('#password')) document.querySelector('#password').value = '';
});
await page.type('#email', username);
await page.type('#password', password);
console.log(`[账号 ${index}] 点击登录...`);
const loginBtn = await page.$('button[type="submit"]');
if (loginBtn) {
await loginBtn.click();
} else {
const buttons = await page.$$('button');
let clicked = false;
for (const btn of buttons) {
const text = await page.evaluate(el => el.innerText, btn);
if (text.replace(/\s/g, '').includes('登录')) {
await btn.click();
clicked = true;
break;
}
}
if (!clicked) throw new Error("未找到登录按钮");
}
await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 60000 });
console.log(`[账号 ${index}] 提取 Token...`);
const cookies = await page.cookies();
const cookieToken = cookies.find(c => c.name === 'token');
const lsToken = await page.evaluate(() => localStorage.getItem('token'));
const finalToken = (cookieToken ? cookieToken.value : null) || lsToken;
if (finalToken) {
console.log(`✅ [账号 ${index}] Token 获取成功!`);
console.log(` Token 预览: ${finalToken.substring(0, 30)}...`);
// 解析 JWT Token 获取用户信息
let expireTime = null;
let userId = null;
let tokenEmail = null;
let isExpired = false;
try {
const payload = JSON.parse(Buffer.from(finalToken.split('.')[1], 'base64').toString());
expireTime = payload.exp ? new Date(payload.exp * 1000).toISOString() : null;
userId = payload.uid || payload.sub || null;
tokenEmail = payload.email || null;
if (payload.exp) {
const expireDate = new Date(payload.exp * 1000);
const now = new Date();
isExpired = expireDate < now;
}
} catch (e) {
console.warn(`⚠️ 无法解析 Token 信息: ${e.message}`);
}
// 保存账号信息到数据库
const accountData = {
email: username,
password: password,
token: finalToken,
user_id: userId,
token_email: tokenEmail,
created_at: new Date().toISOString(),
last_login_at: new Date().toISOString(),
expire_at: expireTime,
is_expired: isExpired,
status: isExpired ? "expired" : "active"
};
await storage.saveAccount(accountData);
// 清除会话
try {
await page.evaluate(() => {
localStorage.clear();
sessionStorage.clear();
});
await client.send('Network.clearBrowserCookies');
console.log(`[账号 ${index}] 已清除登录会话`);
} catch (e) {
console.warn(`[账号 ${index}] 清除会话失败: ${e.message}`);
}
await client.detach();
return finalToken;
} else {
console.warn(`⚠️ [账号 ${index}] 未找到 Token Cookie`);
await client.detach();
return null;
}
} catch (error) {
console.error(`❌ [账号 ${index}] 登录失败:`, error.message);
return null;
} finally {
await page.close();
}
}
async function processAllAccounts() {
console.log("🚀 开始多账号处理流程...");
// 从环境变量获取账号信息
const accountsRaw = process.env.MINDVIDEO_ACCOUNTS;
if (!accountsRaw) {
console.error("❌ 未找到账号配置");
return { success: false, message: "未找到 MINDVIDEO_ACCOUNTS 配置" };
}
const accounts = accountsRaw.split(/[\n;]/)
.map(line => line.trim())
.filter(line => line && line.includes(':'))
.map(line => {
const parts = line.split(':');
const user = parts[0].trim();
const pass = parts.slice(1).join(':').trim();
return { user, pass };
});
if (accounts.length === 0) {
console.error("❌ 账号格式错误或为空");
return { success: false, message: "账号格式错误,请使用 '账号:密码' 格式,每行一个" };
}
console.log(`📋 发现 ${accounts.length} 个账号,准备执行...`);
const browser = await initBrowser();
const results = [];
for (let i = 0; i < accounts.length; i++) {
const { user, pass } = accounts[i];
const token = await loginSingleAccount(browser, user, pass, i + 1);
if (token) {
const checkinRes = await performCheckIn(token, i + 1, user);
results.push({
user,
success: true,
checkin: checkinRes
});
} else {
results.push({ user, success: false, message: "登录失败" });
}
if (i < accounts.length - 1) {
await new Promise(r => setTimeout(r, 2000));
}
}
return {
success: true,
processed: accounts.length,
valid: results.filter(r => r.success).length,
details: results
};
}
// 获取统计信息
async function getStats() {
const accounts = await storage.getAccounts();
let stats = {
status: "ok",
storage_type: "Supabase",
account_count: 0,
active_count: 0,
expired_count: 0,
server_time: new Date().toISOString(),
accounts: []
};
if (accounts && Array.isArray(accounts)) {
stats.account_count = accounts.length;
stats.active_count = accounts.filter(acc => !acc.is_expired).length;
stats.expired_count = accounts.filter(acc => acc.is_expired).length;
stats.accounts = accounts.map(acc => ({
email: acc.email,
user_id: acc.user_id,
created_at: acc.created_at,
last_login_at: acc.last_login_at,
expire_at: acc.expire_at,
status: acc.status,
token_preview: acc.token ? acc.token.substring(0, 20) + "..." : null
}));
}
return stats;
}
// Web 服务器
const server = http.createServer(async (req, res) => {
const url = new URL(req.url, `http://${req.headers.host}`);
if (url.pathname === '/') {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(`<!DOCTYPE html><html><head><meta charset="UTF-8"><title>MindVideo 多账号机器人</title><style>*{margin:0;padding:0;box-sizing:border-box}body{font-family:'Segoe UI',Tahoma,Geneva,Verdana,sans-serif;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);min-height:100vh;padding:20px}.container{max-width:800px;margin:0 auto;background:white;border-radius:16px;box-shadow:0 20px 60px rgba(0,0,0,0.3);padding:40px}h1{color:#333;margin-bottom:10px;font-size:28px}.status{color:#10b981;font-weight:600;margin-bottom:30px}.btn-group{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:15px;margin-bottom:30px}button{padding:14px 24px;border:none;border-radius:8px;font-size:15px;font-weight:600;cursor:pointer;transition:all 0.3s;color:white}button:hover{transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,0,0,0.15)}button:active{transform:translateY(0)}button:disabled{opacity:0.6;cursor:not-allowed;transform:none}.btn-primary{background:linear-gradient(135deg,#667eea 0%,#764ba2 100%)}.btn-success{background:linear-gradient(135deg,#10b981 0%,#059669 100%)}.btn-info{background:linear-gradient(135deg,#3b82f6 0%,#2563eb 100%)}.btn-warning{background:linear-gradient(135deg,#f59e0b 0%,#d97706 100%)}pre{background:#f8f9fa;padding:20px;border-radius:8px;border:1px solid #e9ecef;max-height:400px;overflow-y:auto;font-size:13px;line-height:1.6}.info-box{background:#f0f9ff;padding:15px;border-radius:8px;margin-bottom:20px;border-left:4px solid #3b82f6}.info-box h3{margin:0 0 10px 0;color:#1e40af}table{font-size:14px}table th{background:#f8f9fa;font-weight:600;color:#374151}table td{vertical-align:top}#registrationPanel,#tasksPanel{background:#f9fafb;padding:20px;border-radius:8px;margin-top:20px;border:1px solid #e5e7eb}#registrationPanel h3,#tasksPanel h3{margin-top:0;color:#1f2937}#registrationProgress{background:white;padding:20px;border-radius:8px;border:1px solid #e5e7eb}#registrationProgress h4{margin-top:0;color:#374151}</style></head><body><div class="container"><h1>🤖 MindVideo 多账号机器人</h1><p class="status">● 运行中</p><div class="info-box"><strong>⏰ 定时任务:</strong><br>🔄 Token 刷新: 已取消<br>📅 每日签到: 已取消</div><div class="btn-group"><button class="btn-primary" onclick="processAll()">🔄 完整流程</button><button class="btn-info" onclick="getStats()">📊 查看状态</button></div><div class="btn-group"><button class="btn-primary" onclick="showRegistrationPanel()">🆕 账号注册</button><button class="btn-info" onclick="getRegistrationTasks()">📋 注册任务</button><button class="btn-warning" onclick="getRegistrationStats()">📈 注册统计</button><button class="btn-success" onclick="cleanupTasks()">🧹 清理任务</button></div><div id="registrationPanel" style="display:none"><div class="info-box"><h3>🆕 MindVideo 账号自动注册</h3><p>通过临时邮箱自动注册 MindVideo 账号,支持批量处理和进度跟踪。</p></div><div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:20px"><div><label style="display:block;margin-bottom:5px;font-weight:600">注册数量:</label><input type="number" id="regCount" value="3" min="1" max="20" style="width:100%;padding:10px;border:1px solid #ddd;border-radius:6px"></div><div><label style="display:block;margin-bottom:5px;font-weight:600">密码前缀:</label><input type="text" id="passwordPrefix" value="Pwd" style="width:100%;padding:10px;border:1px solid #ddd;border-radius:6px"></div><div><label style="display:block;margin-bottom:5px;font-weight:600">密码长度:</label><input type="number" id="passwordLength" value="12" min="8" max="20" style="width:100%;padding:10px;border:1px solid #ddd;border-radius:6px"></div><div><label style="display:block;margin-bottom:5px;font-weight:600">任务ID:</label><input type="text" id="taskId" placeholder="自动生成" readonly style="width:100%;padding:10px;border:1px solid #ddd;border-radius:6px;background:#f8f9fa"></div></div><div style="margin-bottom:20px"><button class="btn-primary" onclick="startRegistration()" style="margin-right:10px">🚀 开始注册</button><button class="btn-warning" onclick="cancelRegistration()">🛑 取消任务</button></div><div id="registrationProgress" style="display:none"><h4>📊 注册进度</h4><div style="background:#f0f0f0;border-radius:8px;padding:2px;margin:10px 0"><div id="progressBar" style="background:linear-gradient(90deg,#667eea,#764ba2);height:20px;border-radius:6px;width:0%;transition:width 0.3s"></div></div><div id="progressText" style="font-size:14px;color:#666">准备中...</div></div></div><div id="tasksPanel" style="display:none"><h3>📋 注册任务列表</h3><div id="tasksList"></div></div><pre id="output">点击上方按钮执行操作...</pre></div><script>const output=document.getElementById('output');async function processAll(){output.innerText="正在执行完整流程(登录 + 签到),请耐心等待...";disableButtons(true);try{const res=await fetch('/trigger');const data=await res.json();output.innerText=JSON.stringify(data,null,2)}catch(e){output.innerText="错误: "+e.message}disableButtons(false)}async function getStats(){output.innerText="正在查询状态...";try{const res=await fetch('/stats');const data=await res.json();output.innerText=JSON.stringify(data,null,2)}catch(e){output.innerText="错误: "+e.message}}function disableButtons(disabled){document.querySelectorAll('button').forEach(btn=>btn.disabled=disabled)}let currentTaskId=null;let progressInterval=null;function showRegistrationPanel(){document.getElementById('registrationPanel').style.display='block';document.getElementById('tasksPanel').style.display='none'}function showTasksPanel(){document.getElementById('registrationPanel').style.display='none';document.getElementById('tasksPanel').style.display='block'}async function startRegistration(){const count=parseInt(document.getElementById('regCount').value);const passwordPrefix=document.getElementById('passwordPrefix').value;const passwordLength=parseInt(document.getElementById('passwordLength').value);if(count<1||count>20){alert('注册数量必须在1-20之间');return}output.innerText='正在启动'+count+'个账号的批量注册...';disableButtons(true);try{const res=await fetch('/api/register',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({count:count,password_prefix:passwordPrefix,password_length:passwordLength})});const data=await res.json();if(data.success){currentTaskId=data.task_id;document.getElementById('taskId').value=data.task_id;output.innerText='注册任务已启动!\n任务ID: '+data.task_id+'\n预计时间: '+data.estimated_time+'\n\n正在监控进度...';document.getElementById('registrationProgress').style.display='block';startProgressMonitoring()}else{output.innerText='启动注册失败: '+data.error;disableButtons(false)}}catch(error){output.innerText='启动注册出错: '+error.message;disableButtons(false)}}async function startProgressMonitoring(){if(!currentTaskId)return;progressInterval=setInterval(async()=>{try{const res=await fetch('/api/registration/progress/'+currentTaskId);const data=await res.json();if(data.success){updateProgress(data);if(data.status==='completed'||data.status==='failed'){clearInterval(progressInterval);disableButtons(false);let resultText='\n🎉 注册任务'+(data.status==='completed'?'完成':'失败')+'!\n';resultText+='总计: '+data.progress.total+' | 成功: '+data.progress.completed+' | 失败: '+data.progress.failed+'\n\n';resultText+='📋 详细结果:\n';data.results.forEach(result=>{const status=result.status==='success'?'✅':'❌';resultText+=status+' 账号'+result.account_index+': '+result.email;if(result.error_message){resultText+=' - '+result.error_message}resultText+='\n'});output.innerText+=resultText}}}catch(error){console.error('获取进度失败:',error)}},2000)}function updateProgress(data){const percentage=data.progress.percentage||0;const total=data.progress.total;const completed=data.progress.completed;const failed=data.progress.failed;document.getElementById('progressBar').style.width=percentage+'%';let progressText='进度: '+(completed+failed)+'/'+total+' ('+percentage+'%)';if(completed>0)progressText+=' | ✅ 成功: '+completed;if(failed>0)progressText+=' | ❌ 失败: '+failed;progressText+=' | 状态: '+getStatusText(data.status);document.getElementById('progressText').innerText=progressText}function getStatusText(status){const statusMap={'running':'运行中','completed':'已完成','failed':'失败','cancelled':'已取消'};return statusMap[status]||status}async function cancelRegistration(){if(!currentTaskId){alert('没有正在运行的注册任务');return}if(!confirm('确定要取消任务 '+currentTaskId+' 吗?')){return}try{const res=await fetch('/api/registration/cancel/'+currentTaskId,{method:'DELETE'});const data=await res.json();if(data.success){clearInterval(progressInterval);output.innerText+='\n\n🛑 任务已取消\n取消时间: '+data.cancelled_at;disableButtons(false);currentTaskId=null}else{alert('取消任务失败: '+data.error)}}catch(error){alert('取消任务出错: '+error.message)}}async function getRegistrationTasks(){output.innerText='正在获取注册任务列表...';showTasksPanel();try{const res=await fetch('/api/registration/tasks');const data=await res.json();if(data.success){displayTasks(data.data);output.innerText='找到 '+data.total+' 个注册任务'}else{output.innerText='获取任务列表失败: '+data.error}}catch(error){output.innerText='获取任务列表出错: '+error.message}}function displayTasks(tasks){const tasksList=document.getElementById('tasksList');if(tasks.length===0){tasksList.innerHTML='<p style="text-align:center;color:#666">暂无注册任务</p>';return}let html='<div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse">';html+='<thead><tr style="background:#f8f9fa">';html+='<th style="padding:10px;border:1px solid #ddd">任务ID</th>';html+='<th style="padding:10px;border:1px solid #ddd">状态</th>';html+='<th style="padding:10px;border:1px solid #ddd">总数</th>';html+='<th style="padding:10px;border:1px solid #ddd">成功</th>';html+='<th style="padding:10px;border:1px solid #ddd">失败</th>';html+='<th style="padding:10px;border:1px solid #ddd">创建时间</th>';html+='<th style="padding:10px;border:1px solid #ddd">操作</th>';html+='</tr></thead><tbody>';tasks.forEach(task=>{const statusClass=task.status==='completed'?'color:#10b981;':task.status==='failed'?'color:#ef4444;':task.status==='running'?'color:#f59e0b;':'color:#6b7280;';html+='<tr>';html+='<td style="padding:10px;border:1px solid #ddd;font-family:monospace">'+task.task_id+'</td>';html+='<td style="padding:10px;border:1px solid #ddd;'+statusClass+';font-weight:600">'+getStatusText(task.status)+'</td>';html+='<td style="padding:10px;border:1px solid #ddd;text-align:center">'+task.total_count+'</td>';html+='<td style="padding:10px;border:1px solid #ddd;text-align:center;color:#10b981">'+task.completed_count+'</td>';html+='<td style="padding:10px;border:1px solid #ddd;text-align:center;color:#ef4444">'+task.failed_count+'</td>';html+='<td style="padding:10px;border:1px solid #ddd">'+new Date(task.created_at).toLocaleString()+'</td>';html+='<td style="padding:10px;border:1px solid #ddd">';html+='<button onclick="viewTaskResults(\''+task.task_id+'\')" style="margin-right:5px;padding:5px 10px;border:1px solid #ddd;border-radius:4px;cursor:pointer">查看</button>';if(task.status==='running'){html+='<button onclick="cancelTask(\''+task.task_id+'\')" style="padding:5px 10px;border:1px solid #ef4444;border-radius:4px;cursor:pointer;color:#ef4444">取消</button>'}html+='</td></tr>'});html+='</tbody></table></div>';tasksList.innerHTML=html}async function viewTaskResults(taskId){try{const res=await fetch('/api/registration/progress/'+taskId);const data=await res.json();if(data.success){let resultText='\n📋 任务详情 - '+taskId+'\n';resultText+='状态: '+getStatusText(data.status)+'\n';resultText+='进度: '+data.progress.completed+'/'+data.progress.total+'\n\n';resultText+='📧 账号列表:\n';data.results.forEach(result=>{const status=result.status==='success'?'✅':'❌';resultText+=status+' '+result.email+'\n';if(result.token){resultText+=' Token: '+result.token.substring(0,30)+'...\n'}if(result.error_message){resultText+=' 错误: '+result.error_message+'\n'}});output.innerText=resultText}else{alert('获取任务结果失败: '+data.error)}}catch(error){alert('获取任务结果出错: '+error.message)}}async function cancelTask(taskId){if(!confirm('确定要取消任务 '+taskId+' 吗?')){return}try{const res=await fetch('/api/registration/cancel/'+taskId,{method:'DELETE'});const data=await res.json();if(data.success){alert('任务已取消');getRegistrationTasks()}else{alert('取消任务失败: '+data.error)}}catch(error){alert('取消任务出错: '+error.message)}}async function getRegistrationStats(){output.innerText='正在获取注册统计信息...';try{const res=await fetch('/api/registration/stats');const data=await res.json();if(data.success){const stats=data.data;let statsText='📈 MindVideo 注册统计\n\n';statsText+='📋 任务统计:\n';statsText+=' 总任务数: '+stats.tasks.total+'\n';statsText+=' 已完成: '+stats.tasks.completed+'\n';statsText+=' 失败: '+stats.tasks.failed+'\n';statsText+=' 运行中: '+stats.tasks.running+'\n';statsText+=' 成功率: '+stats.tasks.success_rate+'\n\n';statsText+='👤 账号统计:\n';statsText+=' 总账号数: '+stats.accounts.total+'\n';statsText+=' 成功注册: '+stats.accounts.successful+'\n';statsText+=' 注册失败: '+stats.accounts.failed+'\n';statsText+=' 成功率: '+stats.accounts.success_rate+'\n';output.innerText=statsText}else{output.innerText='获取统计信息失败: '+data.error}}catch(error){output.innerText='获取统计信息出错: '+error.message}}async function cleanupTasks(){if(!confirm('确定要清理过期任务吗?这将删除24小时前的未完成任务。')){return}output.innerText='正在清理过期任务...';try{const res=await fetch('/api/registration/cleanup',{method:'POST'});const data=await res.json();if(data.success){output.innerText='✅ '+data.message;getRegistrationTasks()}else{output.innerText='清理任务失败: '+data.error}}catch(error){output.innerText='清理任务出错: '+error.message}}window.addEventListener('load',getStats);</script></body></html>`);
} else if (url.pathname === '/trigger') {
const result = await processAllAccounts();
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(result));
} else if (url.pathname === '/stats') {
const result = await getStats();
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(result));
} else if (url.pathname === '/api/register' && req.method === 'POST') {
// 启动批量注册
try {
let body = '';
req.on('data', chunk => body += chunk.toString());
req.on('end', async () => {
try {
const params = JSON.parse(body);
const result = await registrationController.batchRegisterAccounts(
params.count || 1,
{
passwordPrefix: params.password_prefix || 'Pwd',
passwordLength: params.password_length || 12
}
);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(result));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, error: error.message }));
}
});
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, error: error.message }));
}
} else if (url.pathname.startsWith('/api/registration/progress/') && req.method === 'GET') {
// 查询注册进度
try {
const taskId = url.pathname.split('/').pop();
const result = await registrationController.getRegistrationProgress(taskId);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(result));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, error: error.message }));
}
} else if (url.pathname.startsWith('/api/registration/cancel/') && req.method === 'DELETE') {
// 取消注册任务
try {
const taskId = url.pathname.split('/').pop();
const result = await registrationController.cancelRegistrationTask(taskId);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(result));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, error: error.message }));
}
} else if (url.pathname === '/api/registration/tasks' && req.method === 'GET') {
// 获取所有注册任务
try {
const result = await registrationController.getAllRegistrationTasks();
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(result));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, error: error.message }));
}
} else if (url.pathname === '/api/registration/stats' && req.method === 'GET') {
// 获取注册统计信息
try {
const stats = await storage.getRegistrationStats();
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, data: stats }));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, error: error.message }));
}
} else if (url.pathname === '/api/registration/cleanup' && req.method === 'POST') {
// 清理过期任务
try {
await registrationController.cleanupExpiredTasks();
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, message: '过期任务清理完成' }));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, error: error.message }));
}
} else {
res.writeHead(404);
res.end('Not Found');
}
});
server.listen(CONFIG.PORT, () => {
console.log(`✅ 服务器运行在端口 ${CONFIG.PORT}`);
console.log(`📱 访问 http://localhost:${CONFIG.PORT} 查看控制面板`);
if (supabase.enabled) {
console.log(`💾 存储方式: Supabase 数据库`);
}
});