UNPKG

hexo-afdian-plugin

Version:

爱发电插件,用于展示爱发电赞助信息

407 lines (358 loc) 13.7 kB
'use strict'; const path = require('path'); const fs = require('fs'); const axios = require('axios'); const crypto = require('crypto-js'); const moment = require('moment'); // 默认配置 const DEFAULT_CONFIG = { title: '赞助名单', description: '感谢您的赞助!', cacheTime: 60, // 缓存时间,单位分钟 pageSize: 10, // 每页显示数量 emptyText: '暂无赞助', moreText: '更多赞助', moreLink: 'https://afdian.com/', buttonText: '赞助', buttonLink: 'https://afdian.com/', sponsorNumber: 66, // 高亮显示金额阈值 sponsorUrl: 'https://afdian.com/' // 赞助链接 }; // 注册插件 module.exports = function(hexo) { // 输出插件信息 hexo.log.info('=========================================='); hexo.log.info('Hexo 爱发电插件已加载'); hexo.log.info('版本: 1.0.1'); hexo.log.info('作者: 心都'); hexo.log.info('=========================================='); // 合并配置 hexo.config.afdian = Object.assign({}, DEFAULT_CONFIG, hexo.config.afdian); // 复制布局文件到主题目录 const themeName = hexo.config.theme; const themeLayoutDir = path.join(hexo.base_dir, 'themes', themeName, 'layout'); const pluginLayoutFile = path.join(__dirname, 'layout/afdian.ejs'); const themeLayoutFile = path.join(themeLayoutDir, 'afdian.ejs'); try { // 确保主题布局目录存在 if (!fs.existsSync(themeLayoutDir)) { hexo.log.warn('爱发电插件:主题布局目录不存在,将尝试创建'); fs.mkdirSync(themeLayoutDir, { recursive: true }); } // 复制布局文件(如果不存在) if (!fs.existsSync(themeLayoutFile)) { hexo.log.info('爱发电插件:正在复制布局文件到主题目录'); fs.copyFileSync(pluginLayoutFile, themeLayoutFile); hexo.log.info('爱发电插件:布局文件复制成功'); } } catch (error) { hexo.log.error('爱发电插件:复制布局文件失败', error.message); hexo.log.warn('爱发电插件:请手动将 node_modules/hexo-plugin-afdian/layout/afdian.ejs 文件复制到 themes/' + themeName + '/layout/ 目录下'); } // 注册静态资源 hexo.extend.generator.register('afdian_assets', function() { return [ { path: 'afdian/css/afdian.css', data: function() { return fs.createReadStream(path.join(__dirname, 'assets/afdian.css')); } }, { path: 'afdian/css/afdian-widget.css', data: function() { return fs.createReadStream(path.join(__dirname, 'assets/afdian-widget.css')); } }, { path: 'afdian/images/default-avatar.svg', data: function() { return fs.createReadStream(path.join(__dirname, 'static/images/default-avatar.svg')); } } ]; }); // 注册路由 hexo.extend.generator.register('afdian', function(locals) { const sponsors = getSponsors(hexo); return { path: 'afdian/index.html', layout: ['afdian', 'page', 'index'], data: { title: hexo.config.afdian.title || '赞助名单', sponsors: sponsors, currentPage: 1, totalPages: Math.ceil(sponsors.length / hexo.config.afdian.pageSize) } }; }); // 注册分页路由 hexo.extend.generator.register('afdian_page', function(locals) { const sponsors = getSponsors(hexo); const pageSize = hexo.config.afdian.pageSize; const totalPages = Math.ceil(sponsors.length / pageSize); const pages = []; for (let i = 2; i <= totalPages; i++) { pages.push({ path: `afdian/page/${i}/index.html`, layout: ['afdian', 'page', 'index'], data: { title: hexo.config.afdian.title || '赞助名单', sponsors: sponsors.slice((i - 1) * pageSize, i * pageSize), currentPage: i, totalPages: totalPages } }); } return pages; }); // 注册辅助函数 hexo.extend.helper.register('afdian_sponsors', function() { return getSponsors(hexo); }); // 注册小部件辅助函数 hexo.extend.helper.register('afdian_widget', function(options = {}) { const config = Object.assign({}, hexo.config.afdian, options); return ` <div class="afdian-widget"> <h3 class="afdian-widget-title">${config.title || '赞助名单'}</h3> <p class="afdian-widget-description">${config.description || '感谢您的赞助!'}</p> <div class="afdian-widget-list"> ${renderSponsors(getSponsors(hexo).slice(0, 5), config)} </div> <div class="afdian-widget-footer"> <a href="/afdian/" class="afdian-widget-more">${config.moreText || '更多赞助'}</a> <a href="${config.sponsorUrl || 'https://afdian.com/'}" class="afdian-widget-button" target="_blank"> ${config.buttonText || '赞助'} </a> </div> </div> <link rel="stylesheet" href="/afdian/css/afdian-widget.css"> `; }); // 注册统计辅助函数 hexo.extend.helper.register('afdian_stats', function() { const sponsors = getSponsors(hexo); const totalAmount = sponsors.reduce((sum, sponsor) => sum + sponsor.amount, 0); return ` <div class="afdian-stats"> <div class="afdian-stats-item"> <span class="afdian-stats-number">${sponsors.length}</span> <span class="afdian-stats-label">赞助人数</span> </div> <div class="afdian-stats-item"> <span class="afdian-stats-number">¥${totalAmount.toFixed(2)}</span> <span class="afdian-stats-label">赞助总额</span> </div> </div> `; }); // 注册时间格式化辅助函数 hexo.extend.helper.register('parse_time', function(timestamp) { return moment(timestamp * 1000).format('YYYY-MM-DD'); }); }; // 渲染赞助者列表 function renderSponsors(sponsors, config) { if (!sponsors || sponsors.length === 0) { return `<div class="afdian-empty">${config.emptyText || '暂无赞助'}</div>`; } return sponsors.map(sponsor => { const isVip = sponsor.amount >= (config.sponsorNumber || 66); return ` <div class="afdian-sponsor-item ${isVip ? 'highlight' : ''}"> <div class="afdian-sponsor-avatar"> <img src="${sponsor.avatar}" alt="${sponsor.name}" onerror="this.src='/afdian/images/default-avatar.svg'; this.onerror=null;"> </div> <div class="afdian-sponsor-info"> <div class="afdian-sponsor-name">${sponsor.name}</div> <div class="afdian-sponsor-amount ${isVip ? 'vip' : ''}">¥${sponsor.amount}</div> <div class="afdian-sponsor-time">${sponsor.time}</div> </div> </div> `; }).join(''); } // 获取赞助者数据 async function getSponsorsData(hexo) { const config = hexo.config.afdian; // 检查必要配置 if (!config.userId || !config.token) { hexo.log.warn('爱发电插件:缺少必要的配置项 userId 或 token'); // 如果配置了使用示例数据,则返回示例数据 if (hexo.config.afdian.useExampleData) { hexo.log.info('爱发电插件:使用示例数据'); return getExampleData(); } // 尝试读取本地数据文件 try { const dataDir = path.join(hexo.source_dir, '_data'); const dataFile = path.join(dataDir, 'sponsors.json'); if (fs.existsSync(dataFile)) { hexo.log.info('爱发电插件:使用本地数据文件'); return { timestamp: new Date(), data: { data: { list: JSON.parse(fs.readFileSync(dataFile, 'utf8')) } } }; } } catch (error) { hexo.log.error('爱发电插件:读取本地数据文件失败', error.message); } return { error: '配置错误' }; } // 缓存文件路径 const cacheDir = path.join(hexo.public_dir, 'data'); const cacheFile = path.join(cacheDir, 'sponsors.json'); // 检查缓存 if (fs.existsSync(cacheFile)) { try { const cacheData = JSON.parse(fs.readFileSync(cacheFile, 'utf8')); const cacheTime = new Date(cacheData.timestamp); const now = new Date(); // 如果缓存未过期,直接返回缓存数据 if ((now - cacheTime) / 1000 / 60 < config.cacheTime) { hexo.log.debug('爱发电插件:使用缓存数据'); return cacheData; } } catch (error) { hexo.log.error('爱发电插件:读取缓存文件失败', error.message); } } // 准备API请求参数 const timestamp = Math.floor(Date.now() / 1000); const params = { user_id: config.userId, params: JSON.stringify({}), ts: timestamp, sign: crypto.MD5(`${config.token}params{}ts${timestamp}user_id${config.userId}`).toString() }; try { // 发起API请求 hexo.log.debug('爱发电插件:正在请求API数据'); const response = await axios.post('https://afdian.com/api/open/query-sponsor', params); // 确保缓存目录存在 if (!fs.existsSync(cacheDir)) { fs.mkdirSync(cacheDir, { recursive: true }); } // 写入缓存 const data = { timestamp: new Date(), data: response.data }; fs.writeFileSync(cacheFile, JSON.stringify(data)); hexo.log.info('爱发电插件:成功获取赞助数据'); return data; } catch (error) { hexo.log.error('爱发电插件:获取赞助数据失败', error.message); // 如果有缓存,返回过期的缓存 if (fs.existsSync(cacheFile)) { hexo.log.warn('爱发电插件:使用过期的缓存数据'); return JSON.parse(fs.readFileSync(cacheFile, 'utf8')); } // 如果配置了使用示例数据,则返回示例数据 if (hexo.config.afdian.useExampleData) { hexo.log.info('爱发电插件:使用示例数据'); return getExampleData(); } return { error: '获取数据失败' }; } } // 处理赞助者数据 function getSponsors(hexo) { // 尝试从缓存获取数据 let cachedSponsors = hexo.locals.get('afdian_sponsors'); if (cachedSponsors) { return cachedSponsors; } // 如果没有缓存,则获取数据 try { const sponsorsData = getSponsorsData(hexo); // 处理Promise返回的情况 if (sponsorsData instanceof Promise) { return sponsorsData.then(processSponsorsData).catch(error => { hexo.log.error('爱发电插件:处理赞助数据失败', error.message); return []; }); } // 处理直接返回的情况 const sponsors = processSponsorsData(sponsorsData); // 缓存处理后的数据 hexo.locals.set('afdian_sponsors', sponsors); return sponsors; } catch (error) { hexo.log.error('爱发电插件:处理赞助数据失败', error.message); return []; } // 处理赞助数据的辅助函数 function processSponsorsData(sponsorsData) { if (sponsorsData.error || !sponsorsData.data || !sponsorsData.data.data || !sponsorsData.data.data.list) { // 如果配置了使用示例数据,则返回示例数据 if (hexo.config.afdian.useExampleData) { const exampleData = getExampleData(); return exampleData.data.data.list.map(formatSponsor); } // 尝试读取本地数据文件 try { const dataDir = path.join(hexo.source_dir, '_data'); const dataFile = path.join(dataDir, 'sponsors.json'); if (fs.existsSync(dataFile)) { return JSON.parse(fs.readFileSync(dataFile, 'utf8')).map(sponsor => { return { name: sponsor.name, avatar: sponsor.avatar || '/afdian/images/default-avatar.svg', amount: parseFloat(sponsor.amount), time: sponsor.time }; }); } } catch (error) { hexo.log.error('爱发电插件:读取本地数据文件失败', error.message); } return []; } return sponsorsData.data.data.list.map(formatSponsor); } // 格式化赞助者数据 function formatSponsor(sponsor) { return { name: sponsor.user ? sponsor.user.name : sponsor.name, avatar: (sponsor.user ? sponsor.user.avatar : sponsor.avatar) || '/afdian/images/default-avatar.svg', amount: parseFloat(sponsor.all_sum_amount || sponsor.amount), time: sponsor.first_pay_time ? moment(sponsor.first_pay_time * 1000).format('YYYY-MM-DD') : sponsor.time }; } } // 获取示例数据 function getExampleData() { return { timestamp: new Date(), data: { data: { list: [ { name: '赞助者1', avatar: '/afdian/images/default-avatar.svg', amount: 50, time: '2023-05-15' }, { name: '赞助者2', avatar: '/afdian/images/default-avatar.svg', amount: 88, time: '2023-06-20' }, { name: '赞助者3', avatar: '/afdian/images/default-avatar.svg', amount: 100, time: '2023-07-05' } ] } } }; }