// checkActivity.js const axios = require('axios'); const cheerio = require('cheerio'); const notifier = require('node-notifier'); // 新增桌面通知模块 const url = 'https://www.szhdy.com/activities/default.html?method=activity&id=9'; const checkInterval = 300000; // 5分钟检查一次 // 设置请求头,模拟浏览器(已包含完整浏览器特征) const headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-User': '?1' }; async function checkActivity() { try { const response = await axios.get(url, { headers }); const html = response.data; const $ = cheerio.load(html); // 限定在活动主容器内检测,避免扫描其他区域 const $main = $('main.et-main.activities_bg'); if (!$main.length) { console.error('活动主容器未找到,请检查页面结构'); return; } // 提取产品详情的工具函数 const extractAttribute = ($product, label) => { const $container = $product.find('.form-container').filter((i, el) => { const labelText = $(el).find('.form-title h5').text().trim(); // 同时匹配中文/英文冒号和空格差异 return labelText.replace(/:|:/g, '').includes(label.replace(/:|:/g, '')); }); // 优先获取.form-text中的文本 let value = $container.find('.form-text').text().trim(); // 如果没有.form-text,尝试获取下拉选项中的文本 if (!value) { value = $container.find('.act-dropdown-option').text().trim(); } return value || '未知'; }; const extractProductDetails = (product) => { const $product = $(product); const name = $product.find('.cat-styleCtitle h1, .cat-styleBtitle h1').text().trim() || '未知服务器'; const core = extractAttribute($product, '核心:'); const memory = extractAttribute($product, '内存:'); const bandwidth = extractAttribute($product, '带宽:'); const duration = extractAttribute($product, '时长:'); // 同时提取价格和单位 const priceElement = $product.find('.main-price-current'); const priceUnitElement = $product.find('.price-current-unit'); const price = priceElement.text().trim() + (priceUnitElement.length ? priceUnitElement.text().trim() : ''); return { name, core, memory, bandwidth, duration, price }; }; // 格式化产品列表(精确空行格式) const formatProducts = (zoneTitle, products) => { if (products.length === 0) return ''; const productLines = products.map(product => { if ($(product).hasClass('cat-styleB-promotion-card') || $(product).hasClass('cat-styleC-promotion-card')) { const details = extractProductDetails(product); return `${details.name}\n${details.core} + ${details.memory}\n${details.bandwidth}\n${details.duration} + ${details.price}`; } return '未知产品'; }).join('\n\n'); // 产品之间空一行 // 严格按用户要求: // - 专区标题前空两行 // - 专区标题后空一行 // - 产品列表前空一行 return `\n\n- [${zoneTitle}] 可购买产品:\n\n${productLines}`; }; // 动态识别专区类型(解决ID变化问题) const activityZones = $main.find('.activityContent_bg'); let valueZoneProducts = []; let qualityZoneProducts = []; let valueZoneSoldOut = []; let qualityZoneSoldOut = []; const debugInfo = []; activityZones.each((i, zone) => { const $zone = $(zone); const title = $zone.find('.currency-title-a').text().trim(); // 支持两种产品结构:旧版(activity-row)和新版(cat-styleB/C-promotion-card) const oldProducts = $zone.find('.activity-row a.list-product-content'); const newProducts = $zone.find('.cat-styleB-promotion-card, .cat-styleC-promotion-card'); // 处理旧版产品结构 const oldValidProducts = oldProducts.filter((i, el) => { const href = $(el).attr('href'); const isNotAffiliate = !href.includes('fid=9'); const isNotSoldOut = !$(el).find('.list-product-desc').text().includes('已售'); return isNotAffiliate && isNotSoldOut; }); const oldSoldOutProducts = oldProducts.filter((i, el) => { const href = $(el).attr('href'); const isNotAffiliate = !href.includes('fid=9'); const isSoldOut = $(el).find('.list-product-desc').text().includes('已售'); return isNotAffiliate && isSoldOut; }); // 处理新版产品结构 const newValidProducts = newProducts.filter((i, el) => { return !$(el).find('.form-footer-butt').hasClass('disableButton'); }); const newSoldOutProducts = newProducts.filter((i, el) => { return $(el).find('.form-footer-butt').hasClass('disableButton'); }); // 合并新旧产品 const validProducts = [...oldValidProducts, ...newValidProducts]; const soldOutProducts = [...oldSoldOutProducts, ...newSoldOutProducts]; debugInfo.push({ title, total: oldProducts.length + newProducts.length, valid: validProducts.length, soldOut: soldOutProducts.length }); if (title.includes('性价比专区')) { valueZoneProducts = validProducts; valueZoneSoldOut = soldOutProducts; } if (title.includes('质量型专区')) { qualityZoneProducts = validProducts; qualityZoneSoldOut = soldOutProducts; } }); // 输出调试信息 console.log(`\n检测到${activityZones.length}个活动专区:`); debugInfo.forEach(info => { console.log(`- [${info.title}] 总产品: ${info.total}, 可购买: ${info.valid}, 已售罄: ${info.soldOut}`); }); // 状态判断逻辑 if (valueZoneProducts.length > 0 || qualityZoneProducts.length > 0) { let productInfo = ''; // 生成性价比专区产品列表(严格按空行要求) if (valueZoneProducts.length > 0) { productInfo += formatProducts('性价比专区!预算党优选云服务器!', valueZoneProducts); } // 生成质量型专区产品列表(两个专区之间空两行) if (qualityZoneProducts.length > 0) { if (productInfo) productInfo += '\n\n'; productInfo += formatProducts('质量型专区!需求稳定用户优选!', qualityZoneProducts); } console.log(productInfo); notifier.notify({ title: '活动产品上架提醒', message: `监控的活动产品已经上架!${productInfo.replace(/\\n/g, '\n')}`, sound: true, wait: true }); process.exit(0); } else if (valueZoneSoldOut.length > 0 || qualityZoneSoldOut.length > 0) { console.log('商品已售罄'); } else { console.log(`[${new Date().toLocaleString()}] 暂未上架,继续监控...`); } } catch (error) { console.error(`请求失败: ${error.message}`); } } // 立即执行一次 checkActivity(); // 定时执行 setInterval(checkActivity, checkInterval);