init: 狐蒂云活动监控项目初始化

This commit is contained in:
ikmkj
2026-03-18 12:16:18 +08:00
commit b0afdbd9a0
20 changed files with 16147 additions and 0 deletions

196
checkActivity.js Normal file
View 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);