最近在看别人的博客的时候发现AI摘要挺方便,遂自己研究了一下hexo的命令以及函数的使用,决定自己写一个小脚本来实现。
于是我想实现在文章最前面生成摘要,有三种情况:
- 不生成AI摘要
- 手动填写摘要,并显示成手动编写摘要
- 自动生成的AI摘要
话不多说,开始敲代码QAQ:
准备工作
安装必要的库:
1
| npm install axios striptags gray-matter
|
axios库用来发送网络请求 gray-matter库用来解析 Markdown 的 Front-matter striptags库用来把 HTML 转为纯文本传给 AI
编写hexo脚本
在你的hexo博客的根目录下创建一个scripts文件夹,用来存放有关hexo的脚本:
进入该文件夹,创建脚本文件:
1 2
| cd scripts vim ai_summary
|
编写脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| const axios = require('axios'); const fs = require('fs'); const matter = require('gray-matter'); const striptags = require('striptags');
const API_URL = 'xxx'; const API_KEY = 'xxx'; const MODEL = 'xxx';
hexo.extend.filter.register('before_post_render', async function(data) { if (data.layout !== 'post') return data; if (data.ai_summary === false) return data;
if (typeof data.ai_summary === 'string' && data.ai_summary.trim().length > 0) { return data; }
const postPath = data.full_source; if (!fs.existsSync(postPath)) return data;
const cleanText = striptags(data.content) .replace(/\n/g, ' ') .substring(0, 1500);
console.log(`[AI_Summary] 正在自动生成摘要: ${data.title}...`);
try { const response = await axios.post(API_URL, { model: MODEL, messages: [ { role: "system", content: "你是一个博客摘要助手。请根据提供的文章内容,写一段简练的摘要。要求:100字以内,不含换行符,语气专业。" }, { role: "user", content: cleanText } ], temperature: 0.7 }, { headers: { 'Authorization': `Bearer ${API_KEY}` }, timeout: 30000 });
const result = response.data.choices[0].message.content.trim(); const file = fs.readFileSync(postPath, 'utf8'); const { data: frontMatter, content } = matter(file); frontMatter.ai_summary = result; frontMatter.ai_generated = true; const updatedFile = matter.stringify(content, frontMatter); fs.writeFileSync(postPath, updatedFile, 'utf8'); console.log(`[AI_Summary] 生成成功: ${data.title}`);
} catch (err) { console.error(`[AI_Summary] 生成失败: ${err.message}`); data.ai_summary = ""; }
return data; });
|
修改模板文件
注意:修改模板文件可能会导致模板主题更新时被覆盖,请注意及时保存修改过后的文件
我是用的是Butterfly框架,找到对应的模板文件夹(themes)修改下面的pug模板:
1
| vim themes/butterfly/layout/post.pug
|
找到下面这段:
1 2 3 4 5 6
| block content #post if top_img === false include includes/header/post-info.pug
article#article-container.container.post-content
|
修改成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| block content if top_img === false include includes/header/post-info.pug
if page.ai_summary !== false && page.ai_summary .ai-summary-card .ai-title if page.ai_generated i.fas.fa-robot span AI 摘要 由xxx 强力驱动 else i.fas.fa-user-edit span AI 摘要 由xxx的大脑强力驱动(?) .ai-content= page.ai_summary
article#article-container.container.post-content
|
修改AI摘要显示样式
在博客根目录下面创建自定义css文件夹:
1
| sudo mkdir -p source/css
|
创建自定义css文件:
以下为我的css格式,可以在这里任意的修改css代码美化你自己的样式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| .ai-summary-card { position: relative; background: rgba(255, 255, 255, 0.9); border-radius: 12px; padding: 15px 20px; margin-bottom: 30px; overflow: hidden; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); }
.ai-summary-card::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 4px; background: linear-gradient(90deg, #49b1f5, #ff0000, #49b1f5); background-size: 200% 100%; animation: streamLight 3s linear infinite; }
.ai-title { font-weight: bold; color: #49b1f5; margin-bottom: 10px; display: flex; align-items: center; }
.ai-title i { margin-right: 8px; font-size: 1.2em; animation: shake-robot 2s infinite; }
.ai-content { font-size: 15px; line-height: 1.6; color: #444; text-align: justify; }
.ai-summary-card .fa-user-edit { color: #2ecc71; }
@keyframes streamLight { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }
@keyframes shake-robot { 0%, 100% { transform: rotate(0deg); } 25% { transform: rotate(10deg); } 75% { transform: rotate(-10deg); } }
[data-theme="dark"] .ai-summary-card { background: #232323; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); } [data-theme="dark"] .ai-content { color: #ccc; }
|
别忘了在配置文件中导入自定义css文件!!!
ps:用于展示的容器结构从pug文件中可以看出
至此,我们可以通过三种方式控制AI摘要的生成:
自动生成AI摘要:
AI摘要总结后:
1 2 3 4 5
| --- title: 文章标题 ai_summary: "AI生成的摘要" ai_generated: true ---
|
手动写摘要:
1 2 3 4
| --- title: 文章标题 ai_summary: 手写摘要 ---
|
这篇文章不想显示摘要
1 2 3 4
| --- title: 秘密文章 ai_summary: false ---
|
注意:
- 如果要手动修改.md文件将ai_summary改成false后,可以删除生成的ai_generated: true字段,保持文件的整洁
- 想重新生成AI摘要(修改过文章或者不满意AI生成的摘要),或从其他方式切换到AI摘要生成,直接将ai_summary和ai_generated字段删除即可
- 无论如何切换方式,注意每一个方法所对应的Front-matter的配置即可
- 如果你使用一些插件,例如永久化文章链接(addrlink),由于hexo的过滤器竞争,可能会导致请求两次API,第一次API写入错误,可以修改如下位置:
1 2 3
| hexo.extend.filter.register('before_post_render', async function(data) { xxxxxx }, 15);
|
这样降低该脚本的执行优先级,保证仅请求1次
本脚本可能存在一些不太成熟的代码,你也可以寻找更成熟的AI摘要生成插件,防止框架升级后修改的模板文件被覆盖。