init: 狐蒂云活动监控项目初始化
This commit is contained in:
196
checkActivity.js
Normal file
196
checkActivity.js
Normal file
@@ -0,0 +1,196 @@
|
||||
// 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);
|
||||
Reference in New Issue
Block a user