Repository: zhheo/Post-Abstract-AI Branch: main Commit: 3927dd5763a3 Files: 11 Total size: 36.2 KB Directory structure: gitextract_oa_4a7ro/ ├── .gitattributes ├── .gitignore ├── Advanced.md ├── LICENSE ├── README.md ├── custom/ │ ├── README.md │ └── Siuyo-Ying.js ├── folder-alias.json ├── package.json ├── tianli_gpt.css └── tianli_gpt.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto ================================================ FILE: .gitignore ================================================ .DS_Store dev.html devblacklist.json ================================================ FILE: Advanced.md ================================================ ## 高级技巧 以下都是可选变量,如果能正常运行,可以不填写。添加方式就是在填写key的那一段代码的``之前添加即可。 ![](/img/advanced.png) 例如:`` ### tianliGPT_postURL 当主题存在其他页面和文章页面结构相同时,可以通过通配符url域名来实现只在指定域名中执行。 例如:`let tianliGPT_postURL = '*/p/*';` 只会在地址含有`/p/`的域名中执行。避免非文章页面添加。 ### tianliGPT_wordLimit ⚠️危险:更改此变量损失已消耗过的key,因为你提交的内容发生了变化。 可以设置提交的字数限制,默认为1000字。这意味着每个文章最多消耗你1000字符(因为只有提交扣费,生成文本不扣费)。你可以降低字符数来让扣费变得更少,也可以增加字符数让文章变得更准确。上限为5000,超过5000字符将被截断。 例如:`let tianliGPT_wordLimit = 1000;` ### tianliGPT_typingAnimate 当此变量为false时,那么不执行打字动画。 例如:`let tianliGPT_typingAnimate = false;` ### tianliGPT_Title 当设置此变量后,TianGPT顶部的名称“AI摘要”可以被修改。 例如:`let tianliGPT_Title = '宇宙无敌智能摘要';` ================================================ FILE: LICENSE ================================================ 禁止用于其他项目的商业用途,仅限TianLIGPT使用。 允许个人用途的修改和优化。 ================================================ FILE: README.md ================================================
icon

文章摘要

适用于绝大多数博客的文章摘要生成器
官方文档 · 管理后台
![](/img/workflows.png) **目前使用文章摘要的最佳实践是使用PostChat插件。对于只使用文章摘要的用户,请关闭PostChat的智能对话功能即可。** 插件安装地址:https://postchat.zhheo.com/plugin/ ## 什么是TianliGPT TianliGPT是一个专业的文字摘要生成工具,你可以将需要提取摘要的文本内容发送给TianliGPT,稍等一会他就可以给你发送一个基于这段文本内容的摘要。 - 实时生成的摘要 - 自动生成,无需人工干预 - 一次生成,再次生成无需消耗key - 包含文字审核过滤,适用于中国大陆 - 支持中国大陆访问 ## 如何部署TianliGPT 我们可以通过在网页中嵌入TianliGPT的服务支持,让TianliGPT能够获取到你需要提交的内容。 1. 你需要在博客后面位置引入js和css(**引入在网站的任意位置**) ```html ``` 2. 我们需要更改一些参数来让这个模型运作起来。 `tianliGPT_postSelector`和`tianliGPT_key` ### tianliGPT_postSelector 这个参数是填写你的博客文章所在的元素属性的选择器,在生成提交的文本时,只会将这个选择器对应的元素内的文本进行提交,并且在这个选择器对应的元素上放插入AI摘要。如果你使用的是Butterfly主题,那么为`#post #article-container`。 这里列出一些常见博客主题的选择器值(部分未经过测试,如果有问题欢迎反馈): [查看主题适配文档](https://postsummary.zhheo.com/install.html) 如果你没有在上面看到你的主题,查看[通用教程](https://flowus.cn/zhheo/7a353126-f225-4e5c-8c11-f5adefe85b7f),手把手教你如何使用前端选择器。 **如果你写了你的主题适配教程,欢迎在issues里投稿,我会收录到官方文档中,帮助更多的小伙伴搭建。** ### tianliGPT_key 到[爱发电](https://store.zhheo.com?cid=1&mid=3)中购买,10元5万字符(限时折扣9元)。请求过的内容再次请求不会消耗key,可以无限期使用。 - 相比实时请求openai,使用tianliGPT可以让你请求过的内容不再消耗key,适合生产环境。 - 相比实时请求openai,使用tianliGPT可以在国内更快速的获取摘要。 - 符合中国大陆法律法规。 - key消耗完毕,已经请求过的内容仍然可以继续请求,避免了被恶意请求造成的资金损失和业务停摆。 购买完成后,进入管理后台:https://summary.zhheo.com/ 登录后点击右上角的“添加新网站”,输入密钥即可 即可绑定成功。 ## 高级技巧 更多的参数变量详见[高级文档](https://postsummary.zhheo.com/parameters.html) 例如关闭文字动画、限制每次提交最大字数等。 ## 开发团队 后端开发:[@Tianli](https://github.com/Tianli0) 产品设计与前端开发:[@张洪Heo](https://github.com/zhheo) ## 技术支持请联系 zhheo@qq.com 点击链接加入讨论子频道【TianliGPT 问题交流】:https://pd.qq.com/s/7cx85i9l0 针对此项目的任何技术支持与关于API的相关问题,可以联系此邮箱,会在24小时内回复。 ## 更多支持TianliGPT的项目 [Post-Summary-AI](https://github.com/qxchuckle/Post-Summary-AI) - 轻笑开发的博客摘要生成工具 [hexo-ai-excerpt](https://github.com/rootlexblog/hexo-ai-excerpt) - 在本地部署时添加AI摘要 # vitepress npm run docs:build npm run docs:dev ## 赞助 本项目 CDN 加速及安全防护由 [Tencent EdgeOne](https://edgeone.ai/zh?from=github) 赞助 ![34fe3a45-492d-4ea4-ae5d-ea1087ca7b4b](https://github.com/user-attachments/assets/0f3da64f-9e35-4118-9286-7ad46e753604) ================================================ FILE: custom/README.md ================================================ # 定制JS 部分主题不兼容本项目,所以需要定制js。 ## Siuyo/Ying js代码替换为: `` ================================================ FILE: custom/Siuyo-Ying.js ================================================ console.log("\n %c Post-Abstract-AI 开源博客文章摘要AI生成工具 %c https://github.com/zhheo/Post-Abstract-AI \n", "color: #fadfa3; background: #030307; padding:5px 0;", "background: #fadfa3; padding:5px 0;") function insertAIDiv(selector) { // 首先移除现有的 "post-TianliGPT" 类元素(如果有的话) removeExistingAIDiv(); // 获取目标元素 const targetElement = document.querySelector(selector); // 如果没有找到目标元素,不执行任何操作 if (!targetElement) { return; } // 创建要插入的HTML元素 const aiDiv = document.createElement('div'); aiDiv.className = 'post-TianliGPT'; const aiTitleDiv = document.createElement('div'); aiTitleDiv.className = 'tianliGPT-title'; aiDiv.appendChild(aiTitleDiv); const aiIcon = document.createElement('i'); aiIcon.className = 'tianliGPT-title-icon'; aiTitleDiv.appendChild(aiIcon); // 插入 SVG 图标 aiIcon.innerHTML = ` 机器人 ` const aiTitleTextDiv = document.createElement('div'); aiTitleTextDiv.className = 'tianliGPT-title-text'; aiTitleTextDiv.textContent = 'AI摘要'; aiTitleDiv.appendChild(aiTitleTextDiv); const aiTagDiv = document.createElement('div'); aiTagDiv.className = 'tianliGPT-tag'; aiTagDiv.id = 'tianliGPT-tag'; aiTagDiv.textContent = 'TianliGPT'; aiTitleDiv.appendChild(aiTagDiv); const aiExplanationDiv = document.createElement('div'); aiExplanationDiv.className = 'tianliGPT-explanation'; aiExplanationDiv.innerHTML = '生成中...' + ''; aiDiv.appendChild(aiExplanationDiv); // 将 tianliGPT-explanation 插入到 aiDiv,而不是 aiTitleDiv // 将创建的元素插入到目标元素的顶部 targetElement.insertBefore(aiDiv, targetElement.firstChild); } function removeExistingAIDiv() { // 查找具有 "post-TianliGPT" 类的元素 const existingAIDiv = document.querySelector(".post-TianliGPT"); // 如果找到了这个元素,就从其父元素中删除它 if (existingAIDiv) { existingAIDiv.parentElement.removeChild(existingAIDiv); } } var tianliGPT = { //读取文章中的所有文本 getTitleAndContent: function() { try { const title = document.title; const container = document.querySelector(tianliGPT_postSelector); if (!container) { console.warn('TianliGPT:找不到文章容器。请尝试将引入的代码放入到文章容器之后。如果本身没有打算使用摘要功能可以忽略此提示。'); return ''; } const paragraphs = container.getElementsByTagName('p'); const headings = container.querySelectorAll('h1, h2, h3, h4, h5'); let content = ''; for (let h of headings) { if (h.closest('#comments')) continue; content += h.innerText + ' '; } for (let p of paragraphs) { if (p.closest('#comments')) continue; // 移除包含'http'的链接 const filteredText = p.innerText.replace(/https?:\/\/[^\s]+/g, ''); content += filteredText; } const combinedText = title + ' ' + content; let wordLimit = 1000; if (typeof tianliGPT_wordLimit !== "undefined") { wordLimit = tianliGPT_wordLimit; } const truncatedText = combinedText.slice(0, wordLimit); return truncatedText; } catch (e) { console.error('TianliGPT错误:可能由于一个或多个错误导致没有正常运行,原因出在获取文章容器中的内容失败,或者可能是在文章转换过程中失败。', e); return ''; } }, fetchTianliGPT: async function(content) { if (!tianliGPT_key) { return "没有获取到key,代码可能没有安装正确。如果你需要在tianli_gpt文件引用前定义tianliGPT_key变量。详细请查看文档。"; } if (tianliGPT_key === "5Q5mpqRK5DkwT1X9Gi5e") { return "请购买 key 使用,如果你能看到此条内容,则说明代码安装正确。"; } const apiUrl = `https://summary.tianli0.top/?content=${encodeURIComponent(content)}&key=${encodeURIComponent(tianliGPT_key)}`; const timeout = 20000; // 设置超时时间(毫秒) try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); const response = await fetch(apiUrl, { signal: controller.signal }); if (response.ok) { const data = await response.json(); return data.summary; } else { throw new Error('请求失败'); } } catch (error) { if (error.name === 'AbortError') { if (window.location.hostname === 'localhost') { console.warn('警告:请勿在本地主机上测试 API 密钥。'); return '获取文章摘要超时。请勿在本地主机上测试 API 密钥。'; } else { console.error('请求超时'); return '获取文章摘要超时。当你出现这个问题时,可能是key或者绑定的域名不正确。也可能是因为文章过长导致的 AI 运算量过大,您可以稍等一下然后刷新页面重试。'; } } else { console.error('请求失败:', error); return '获取文章摘要失败,请稍后再试。'; } } } } function runTianliGPT() { insertAIDiv(tianliGPT_postSelector); const content = tianliGPT.getTitleAndContent(); if (!content && content !== '') { console.log('TianliGPT本次提交的内容为:' + content); } tianliGPT.fetchTianliGPT(content).then(summary => { const aiExplanationDiv = document.querySelector('.tianliGPT-explanation'); aiExplanationDiv.innerHTML = summary; }) } function checkURLAndRun() { if (typeof tianliGPT_postURL === "undefined") { runTianliGPT(); // 如果没有设置自定义 URL,则直接执行 runTianliGPT() 函数 return; } try { const wildcardToRegExp = (s) => { return new RegExp('^' + s.split(/\*+/).map(regExpEscape).join('.*') + '$'); }; const regExpEscape = (s) => { return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); }; const urlPattern = wildcardToRegExp(tianliGPT_postURL); const currentURL = window.location.href; if (urlPattern.test(currentURL)) { runTianliGPT(); // 如果当前 URL 符合用户设置的 URL,则执行 runTianliGPT() 函数 } else { console.log("TianliGPT:因为不符合自定义的链接规则,我决定不执行摘要功能。"); } } catch (error) { console.error("TianliGPT:我没有看懂你编写的自定义链接规则,所以我决定不执行摘要功能", error); } } checkURLAndRun(); ================================================ FILE: folder-alias.json ================================================ {} ================================================ FILE: package.json ================================================ { "devDependencies": { "vitepress": "^1.0.0-rc.10" }, "scripts": { "docs:dev": "vitepress dev docs", "docs:build": "vitepress build docs", "docs:preview": "vitepress preview docs" } } ================================================ FILE: tianli_gpt.css ================================================ :root { --heo-white: #fff; --heo-white-op: rgba(255, 255, 255, 0.2); --heo-black: #000; --heo-black-op: rgba(0, 0, 0, 0.2); --heo-none: #00000000; --heo-gray: #999999; --heo-gray-op: #9999992b; --heo-vip: #e5a80d; --heo-main: var(--heo-theme); --heo-main-op: var(--heo-theme-op); --heo-main-op-deep: var(--heo-theme-op-deep); --heo-main-none: var(--heo-theme-none); --heo-shadow-theme: 0 8px 12px -3px var(--heo-theme-op); --heo-shadow-blackdeep: 0 2px 16px -3px rgba(0, 0, 0, .15); --heo-shadow-main: 0 8px 12px -3px var(--heo-main-op); --heo-shadow-blue: 0 8px 12px -3px rgba(40, 109, 234, .20); --heo-shadow-white: 0 8px 12px -3px rgba(255, 255, 255, .20); --heo-shadow-black: 0 0 12px 4px rgba(0, 0, 0, .05); --heo-shadow-yellow: 0px 38px 77px -26px rgba(255, 201, 62, .12); --heo-shadow-red: 0 8px 12px -3px #ee7d7936; --heo-shadow-green: 0 8px 12px -3px #87ee7936; --heo-logo-color: linear-gradient(215deg, #4584ff 0%, #cf0db9 100%); --heo-snackbar-time: 5s; --heo-theme: #425AEF; --heo-theme-op: #4259ef23; --heo-theme-op-deep: #4259efdd; --heo-theme-none: #4259ef01; --heo-blue: #425AEF; --heo-red: #f04a63; --heo-pink: #FF7C7C; --heo-green: #57bd6a; --heo-yellow: #c28b00; --heo-yellow-op: #d99c001a; --heo-orange: #e38100; --heo-fontcolor: #363636; --heo-background: #f7f9fe; --heo-reverse: #000; --heo-maskbg: rgba(255, 255, 255, 0.6); --heo-maskbgdeep: rgba(255, 255, 255, 0.85); --heo-hovertext: var(--heo-main); --heo-ahoverbg: #F7F7FA; --heo-lighttext: var(--heo-main); --heo-secondtext: rgba(60, 60, 67, 0.8); --heo-scrollbar: rgba(60, 60, 67, 0.4); --heo-card-btn-bg: #edf0f7; --heo-post-blockquote-bg: #fafcff; --heo-post-tabs-bg: #f2f5f8; --heo-secondbg: #f1f3f8; --heo-shadow-nav: 0 5px 12px -5px rgba(102, 68, 68, 0.05); --heo-card-bg: #fff; --heo-card-bg-op: var(--heo-black-op); --heo-card-bg-none: rgba(255, 255, 255, 0); --heo-shadow-lightblack: 0 5px 12px -5px rgba(102, 68, 68, 0.00); --heo-shadow-light2black: 0 5px 12px -5px rgba(102, 68, 68, 0.00); --heo-card-border: #e3e8f7; --heo-shadow-border: 0 8px 16px -4px #2c2d300c; --style-border: 1px solid var(--heo-card-border); --style-border-always: 1px solid var(--heo-card-border); --style-border-hover: 1px solid var(--heo-main); --style-border-hover-always: 1px solid var(--heo-main); --style-border-dashed: 1px dashed var(--heo-theme-op); --style-border-forever: 2px solid var(--heo-main); } [data-theme=light] { --heo-theme: #425AEF; --heo-theme-op: #4259ef23; --heo-theme-op-deep: #4259efdd; --heo-theme-none: #4259ef01; --heo-blue: #425AEF; --heo-red: #f04a63; --heo-pink: #FF7C7C; --heo-green: #57bd6a; --heo-yellow: #c28b00; --heo-yellow-op: #d99c001a; --heo-orange: #e38100; --heo-fontcolor: #363636; --heo-background: #f7f9fe; --heo-reverse: #000; --heo-maskbg: rgba(255, 255, 255, 0.6); --heo-maskbgdeep: rgba(255, 255, 255, 0.85); --heo-hovertext: var(--heo-main); --heo-ahoverbg: #F7F7FA; --heo-lighttext: var(--heo-main); --heo-secondtext: rgba(60, 60, 67, 0.8); --heo-scrollbar: rgba(60, 60, 67, 0.4); --heo-card-btn-bg: #edf0f7; --heo-post-blockquote-bg: #fafcff; --heo-post-tabs-bg: #f2f5f8; --heo-secondbg: #f1f3f8; --heo-shadow-nav: 0 5px 12px -5px rgba(102, 68, 68, 0.05); --heo-card-bg: #fff; --heo-card-bg-op: var(--heo-black-op); --heo-card-bg-none: rgba(255, 255, 255, 0); --heo-shadow-lightblack: 0 5px 12px -5px rgba(102, 68, 68, 0.00); --heo-shadow-light2black: 0 5px 12px -5px rgba(102, 68, 68, 0.00); --heo-card-border: #e3e8f7; --heo-shadow-border: 0 8px 16px -4px #2c2d300c; --style-border: 1px solid var(--heo-card-border); --style-border-always: 1px solid var(--heo-card-border); --style-border-hover: 1px solid var(--heo-main); --style-border-hover-always: 1px solid var(--heo-main); --style-border-dashed: 1px dashed var(--heo-theme-op); --style-border-forever: 2px solid var(--heo-main); } [data-theme=dark], body.dark, body.dark-theme,[eagle-extension-theme="dark"],body.dark-open,[color-scheme=dark],[data-scheme="inverse"] { --heo-theme: #f2b94b; --heo-theme-op: #f2b94b23; --heo-theme-op-deep: #f2b94bdd; --heo-theme-none: #f2b94b00; --heo-blue: #0084FF; --heo-red: #FF3842; --heo-pink: #d44040; --heo-green: #3e9f50; --heo-yellow: #ffc93e; --heo-yellow-op: #ffc93e30; --heo-orange: #ff953e; --heo-fontcolor: #F7F7FA; --heo-background: #18171d; --heo-reverse: #fff; --heo-maskbg: rgba(0, 0, 0, 0.6); --heo-maskbgdeep: rgba(0, 0, 0, 0.85); --heo-hovertext: #0A84FF; --heo-ahoverbg: #fff; --heo-lighttext: var(--heo-theme); --heo-secondtext: #a1a2b8; --heo-scrollbar: rgba(200, 200, 223, 0.4); --heo-card-btn-bg: #30343f; --heo-post-blockquote-bg: #000; --heo-post-tabs-bg: #121212; --heo-secondbg: #30343f; --heo-shadow-nav: 0 5px 20px 0px rgba(28, 28, 28, 0.4); --heo-card-bg: #1d1e22; --heo-card-bg-op: var(--heo-white-op); --heo-card-bg-none: #1d1b2600; --heo-shadow-lightblack: 0 5px 12px -5px rgba(102, 68, 68, 0.0); --heo-shadow-light2black: 0 5px 12px -5px rgba(102, 68, 68, 0.0); --heo-card-border: #3d3d3f; --heo-shadow-border: 0 8px 16px -4px #00000050; --style-border: 1px solid var(--heo-card-border); --style-border-always: 1px solid var(--heo-card-border); --style-border-hover: 1px solid var(--heo-theme); --style-border-hover-always: 1px solid var(--heo-theme); --style-border-dashed: 1px dashed var(--heo-theme-op); --style-border-forever: 2px solid var(--heo-lighttext); } /* AI */ .post-TianliGPT { background: var(--heo-secondbg); border-radius: 12px; padding: 12px; line-height: 1.3; border: var(--style-border-always); margin: 16px 0; } @media screen and (max-width: 768px) { .post-TianliGPT { margin-top: 22px; } } .tianliGPT-title { display: flex; color: var(--heo-lighttext); border-radius: 8px; align-items: center; padding: 0 12px; cursor: default; user-select: none; position: relative; } .tianliGPT-title-text { font-weight: bold; margin-left: 8px; line-height: 1; } .tianliGPT-explanation { margin-top: 12px; padding: 8px 12px; background: var(--heo-card-bg); border-radius: 8px; border: var(--style-border-always); font-size: 15px; line-height: 1.4; display: block; color: var(--heo-fontcolor); } .tianliGPT-suggestions { display: flex; flex-wrap: wrap; } .tianliGPT-suggestions .tianliGPT-suggestions-item { margin-top: 12px; padding: 8px 12px; background: var(--heo-card-bg); border-radius: 8px 8px 8px 0; border: var(--style-border-always); font-size: 15px; line-height: 1.4; display: flex; width: fit-content; margin-right: 12px; cursor: pointer; transition: 0.3s; } .tianliGPT-suggestions .tianliGPT-suggestions-item:hover { background: var(--heo-main); color: var(--heo-white); } .blinking-cursor { background-color: var(--heo-lighttext); width: 14px; height: 14px; border-radius: 16px; display: inline-block; vertical-align: middle; animation: blinking-cursor 2s infinite; -webkit-animation: blinking-cursor 2s infinite; margin-left: 4px; margin-bottom: 3px; transform: scale(0.6); } @keyframes blinking-cursor { 0% { transform: scale(0.6); } 25% { transform: scale(1); } 50% { transform: scale(0.6); } 75% { transform: scale(1); } 100% { transform: scale(0.6); } } .tianliGPT-tag { font-size: 12px; background-color: var(--heo-lighttext); box-shadow: var(--heo-shadow-main); color: var(--heo-card-bg); font-weight: 700; border-radius: 12px; margin-left: auto; line-height: 12px; padding: 6px 8px; display: flex; align-items: center; justify-content: center; transition: .3s; } .tianliGPT-tag.loadingAI { animation-duration: 2s; animation-name: AILoading; animation-iteration-count: infinite; animation-direction: alternate; cursor: default; } @keyframes AILoading { 0% { opacity: 1; } 25% { opacity: 0.3; } 50% { opacity: 1; } 75% { opacity: 0.3; } 100% { opacity: 1; } } ins.adsbygoogle { margin: 16px 0; background: var(--heo-card-bg); border-radius: 12px; overflow: hidden; border: var(--style-border-always); } #tianliGPT-Toggle { font-size: 12px; background: var(--heo-lighttext); color: var(--heo-card-bg); padding: 4px; border-radius: 4px; margin-left: 6px; transform: scale(0.8); cursor: pointer; transition: 0.3s; font-weight: bold; } #tianliGPT-Toggle:hover { background: var(--heo-fontcolor); color: var(--heo-card-bg); } .tianliGPT-title-icon { width: 24px; height: 24px; display: flex; background: var(--heo-lighttext); color: var(--heo-card-bg); font-size: 14px; border-radius: 20px; justify-content: center; align-items: center; } .tianliGPT-title-icon svg { width: 14px; height: 14px; fill: var(--heo-card-bg); } .tianliGPT-title-icon svg path { fill: var(--heo-card-bg); } ================================================ FILE: tianli_gpt.js ================================================ console.log("\n %c Post-Abstract-AI 博客文章摘要AI生成工具 %c https://github.com/zhheo/Post-Abstract-AI \n", "color: #fadfa3; background: #030307; padding:5px 0;", "background: #fadfa3; padding:5px 0;") var tianliGPTIsRunning = false; function tianliGPT(usePjax) { function insertAIDiv(selector) { // 首先移除现有的 "post-TianliGPT" 类元素(如果有的话) removeExistingAIDiv(); // 获取目标元素 let targetElement = document.querySelector(selector); // 如果没有找到目标元素,持续尝试直到超时 if (!targetElement) { const maxDuration = 20000; // 最大等待时间10秒 const interval = 300; // 每0.3秒检查一次 let elapsed = 0; // 已经过去的时间 const intervalId = setInterval(() => { elapsed += interval; targetElement = document.querySelector(selector); // 使用let声明后,这里可以更新外部的targetElement变量 if (targetElement) { clearInterval(intervalId); // 找到元素,清除定时器 // console.log('找到目标元素:', targetElement); // 这里可以添加对找到的元素的操作 } else if (elapsed >= maxDuration) { clearInterval(intervalId); // 超时,清除定时器 console.log('超时未找到元素'); return } }, interval); } // 创建要插入的HTML元素 const aiDiv = document.createElement('div'); aiDiv.className = 'post-TianliGPT'; const aiTitleDiv = document.createElement('div'); aiTitleDiv.className = 'tianliGPT-title'; aiDiv.appendChild(aiTitleDiv); const aiIcon = document.createElement('i'); aiIcon.className = 'tianliGPT-title-icon'; aiTitleDiv.appendChild(aiIcon); // 插入 SVG 图标 aiIcon.innerHTML = ` `; const aiTitleTextDiv = document.createElement('div'); aiTitleTextDiv.className = 'tianliGPT-title-text'; if (typeof tianliGPT_Title === "undefined") { aiTitleTextDiv.textContent = 'AI摘要'; }else { aiTitleTextDiv.textContent = tianliGPT_Title; } aiTitleDiv.appendChild(aiTitleTextDiv); const aiTagDiv = document.createElement('div'); aiTagDiv.className = 'tianliGPT-tag'; aiTagDiv.id = 'tianliGPT-tag'; if (typeof tianliGPT_Name === "undefined") { aiTagDiv.textContent = 'TianliGPT'; }else { aiTagDiv.textContent = tianliGPT_Name; } aiTitleDiv.appendChild(aiTagDiv); const aiExplanationDiv = document.createElement('div'); aiExplanationDiv.className = 'tianliGPT-explanation'; aiExplanationDiv.innerHTML = '生成中...' + ''; aiDiv.appendChild(aiExplanationDiv); // 将 tianliGPT-explanation 插入到 aiDiv,而不是 aiTitleDiv // 将创建的元素插入到目标元素的顶部 targetElement.insertBefore(aiDiv, targetElement.firstChild); } function removeExistingAIDiv() { // 查找具有 "post-TianliGPT" 类的元素 const existingAIDiv = document.querySelector(".post-TianliGPT"); // 如果找到了这个元素,就从其父元素中删除它 if (existingAIDiv) { existingAIDiv.parentElement.removeChild(existingAIDiv); } } var tianliGPT = { //读取文章中的所有文本 getTitleAndContent: function() { try { const title = document.title; const container = document.querySelector(tianliGPT_postSelector); if (!container) { console.warn('TianliGPT:找不到文章容器。请尝试将引入的代码放入到文章容器之后。如果本身没有打算使用摘要功能可以忽略此提示。'); return ''; } const paragraphs = container.getElementsByTagName('p'); const headings = container.querySelectorAll('h1, h2, h3, h4, h5'); let content = ''; for (let h of headings) { content += h.innerText + ' '; } for (let p of paragraphs) { // 移除包含'http'的链接 const filteredText = p.innerText.replace(/https?:\/\/[^\s]+/g, ''); content += filteredText; } const combinedText = title + ' ' + content; let wordLimit = 1000; if (typeof tianliGPT_wordLimit !== "undefined") { wordLimit = tianliGPT_wordLimit; } const truncatedText = combinedText.slice(0, wordLimit); return truncatedText; } catch (e) { console.error('TianliGPT错误:可能由于一个或多个错误导致没有正常运行,原因出在获取文章容器中的内容失败,或者可能是在文章转换过程中失败。', e); return ''; } }, fetchTianliGPT: async function(content) { if (!tianliGPT_key) { let info = "没有获取到key,代码可能没有安装正确。如果你需要在tianli_gpt文件引用前定义tianliGPT_key变量。详细请查看文档。" tianliGPT.aiShowAnimation(info) return info; } if (tianliGPT_key === "5Q5mpqRK5DkwT1X9Gi5e") { let info = "请购买 key 使用,如果你能看到此条内容,则说明代码安装正确。" tianliGPT.aiShowAnimation(info) return info; } var url = window.location.href; const title = document.title; const apiUrl = 'https://summary.tianli0.top/'; const timeout = 20000; // 设置超时时间(毫秒) try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content: content, key: tianliGPT_key, url: window.location.href, title: document.title }), signal: controller.signal }); clearTimeout(timeoutId); // 清除定时器,避免不必要的abort if (response.ok) { const data = await response.json(); return { summary: data.summary }; } else { let info = "" const errorData = await response.json(); // 假设错误响应也是JSON格式 if (response.status === 514) { info = "TianliGPT is only available in mainland China, and is not yet open to overseas users, so stay tuned!"; tianliGPT.aiShowAnimation(info); return info; } // 根据403错误处理 if (response.status === 403) { switch (errorData.err_code) { case 1: // 你的具体错误处理逻辑,例如显示某个特定的错误信息 info = "你的网站设置了Referrer-Policy为same-origin,这会导致Tianli无法验证你的请求来源。TianliGPT依赖refer进行来源判断,特别是meta标签的referrer属性需要修改,至少为origin。例如:"; tianliGPT.aiShowAnimation(info); return info; case 2: // 你的具体错误处理逻辑,例如显示某个特定的错误信息 info = "你正在使用的tianliGPT_key没有绑定当前网站,请检查当前的密钥是否绑定了当前网站地址。可以到summary.zhheo.com中绑定。"; tianliGPT.aiShowAnimation(info); return info; // 这里可以添加更多的err_code判断分支 case 3: info = "参数缺失,请检查是否正确配置tianliGPT_key" tianliGPT.aiShowAnimation(info); return info; case 4: document.querySelectorAll('.post-TianliGPT').forEach(el => { el.style.display = 'none'; }); info = "Key错误或余额不足,请充值后请求新的文章" throw new Error('TianliGPT:'+info); case 5: info = errorData.err_msg tianliGPT.aiShowAnimation(info); return info; case 6: info = errorData.err_msg tianliGPT.aiShowAnimation(info); return info; case 7: info = errorData.err_msg tianliGPT.aiShowAnimation(info); return info; default: // 默认的处理逻辑 tianliGPT.aiShowAnimation("未知错误,请检查API文档"); return "未知错误,请检查API文档"; } } } } catch (error) { if (error.name === 'AbortError') { if (window.location.hostname === 'localhost') { console.warn('警告:请勿在本地主机上测试 API 密钥。'); return '获取文章摘要超时。请勿在本地主机上测试 API 密钥。'; } else { console.error('请求超时'); return '获取文章摘要超时。当你出现这个问题时,可能是key或者绑定的域名不正确。也可能是因为文章过长导致的 AI 运算量过大,您可以稍等一下然后刷新页面重试。'; } } else { console.error('请求失败:', error); return '获取文章摘要失败,请稍后再试。'; } } }, aiShowAnimation: function (text) { const element = document.querySelector(".tianliGPT-explanation"); if (!element) { return; } if (tianliGPTIsRunning) { return; } // 检查用户是否已定义tianliGPT_typingAnimate并且其值为false if (typeof tianliGPT_typingAnimate !== "undefined" && !tianliGPT_typingAnimate) { // 如果用户已定义tianliGPT_typingAnimate并且其值为false,则立即显示完整文本 element.innerHTML = text; return; } tianliGPTIsRunning = true; const typingDelay = 25; const waitingTime = 1000; const punctuationDelayMultiplier = 6; element.style.display = "block"; element.innerHTML = "生成中..." + ''; //给AItag添加动画 const aiTag = document.querySelector('.tianliGPT-tag'); aiTag.classList.add('loadingAI'); let animationRunning = true; let currentIndex = 0; let initialAnimation = true; let lastUpdateTime = performance.now(); const animate = () => { if (currentIndex < text.length && animationRunning) { const currentTime = performance.now(); const timeDiff = currentTime - lastUpdateTime; const letter = text.slice(currentIndex, currentIndex + 1); const isPunctuation = /[,。!、?,.!?]/.test(letter); const delay = isPunctuation ? typingDelay * punctuationDelayMultiplier : typingDelay; if (timeDiff >= delay) { element.innerText = text.slice(0, currentIndex + 1); lastUpdateTime = currentTime; currentIndex++; if (currentIndex < text.length) { element.innerHTML = text.slice(0, currentIndex) + ''; } else { element.innerHTML = text; element.style.display = "block"; tianliGPTIsRunning = false; observer.disconnect(); // 暂停监听 //给AItag停止动画 const aiTag = document.querySelector('.tianliGPT-tag'); aiTag.classList.remove('loadingAI'); } } requestAnimationFrame(animate); } } // 使用IntersectionObserver对象优化ai离开视口后暂停的业务逻辑,提高性能 const observer = new IntersectionObserver((entries) => { let isVisible = entries[0].isIntersecting; animationRunning = isVisible; // 标志变量更新 if (animationRunning && initialAnimation) { setTimeout(() => { requestAnimationFrame(animate); }, 200); } }, { threshold: 0 }); let post_ai = document.querySelector('.post-TianliGPT'); observer.observe(post_ai); //启动新监听 }, } function runTianliGPT() { if (typeof tianliGPT_postSelector === 'undefined') { return; } insertAIDiv(tianliGPT_postSelector); const content = tianliGPT.getTitleAndContent(); if (content) { console.log('TianliGPT本次提交的内容为:' + content); } tianliGPT.fetchTianliGPT(content).then(data => { const summary = data.summary; tianliGPT.aiShowAnimation(summary); }) } function checkURLAndRun() { if (typeof tianliGPT_postURL === "undefined") { tianliGPTCustomBlackList(); // 如果没有设置自定义 URL,则直接执行 runTianliGPT() 函数 return; } try { const wildcardToRegExp = (s) => { return new RegExp('^' + s.split(/\*+/).map(regExpEscape).join('.*') + '$'); }; const regExpEscape = (s) => { return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); }; const urlPattern = wildcardToRegExp(tianliGPT_postURL); const currentURL = window.location.href; if (urlPattern.test(currentURL)) { attemptRunTianliGPT(); // 使用新函数处理重试逻辑 } else { console.log(`TianliGPT:当前 URL '${currentURL}' 不符合规则 '${tianliGPT_postURL}',所以我决定不执行摘要功能。`); } } catch (error) { console.error("TianliGPT:我没有看懂你编写的自定义链接规则,所以我决定不执行摘要功能", error); } } function attemptRunTianliGPT() { const maxAttempts = 20; // 最多尝试10秒 let attempts = 0; const interval = setInterval(() => { try { tianliGPTCustomBlackList(); // 尝试执行 runTianliGPT clearInterval(interval); // 如果成功,清除定时器 // console.log("TianliGPT:成功执行!"); } catch (error) { if (attempts >= maxAttempts) { clearInterval(interval); // 超过尝试次数,清除定时器 console.error("TianliGPT:超时失败,停止尝试。", error); } attempts++; } }, 200); // 每1秒尝试一次 } function tianliGPTCustomBlackList() { if (typeof tianliGPT_blacklist === "undefined" || !tianliGPT_blacklist) { runTianliGPT(); // 如果没有设置自定义 URL 或 URL 为空,则直接执行 runTianliGPT() 函数 return; } else { // 使用 fetch 请求 JSON 文件 fetch(tianliGPT_blacklist) .then(response => response.json()) .then(data => { const urlList = data.blackurls; // 假设 JSON 文件中有一个 blackurls 键 let currentPageUrl = window.location.href; let isBlacklisted = urlList.some(pattern => { // 将通配符转换为正则表达式 let regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$"); return regex.test(currentPageUrl); }); // 如果当前页面 URL 不在黑名单中,则执行 tianliGPT if (!isBlacklisted) { runTianliGPT(); } }) .catch(error => { console.error('请求黑名单失败。Error fetching blacklist:', error); runTianliGPT(); // 请求出错时继续执行 runTianliGPT() }); } } if (usePjax) { checkURLAndRun(); } else { document.addEventListener("DOMContentLoaded", function () { checkURLAndRun(); }); } } tianliGPT(false); document.addEventListener('pjax:complete', function () { tianliGPT(true); }) (function(history){ var pushState = history.pushState; history.pushState = function(state) { if (typeof history.onpushstate == "function") { setTimeout(function () { history.onpushstate({ state: state }); }, 100); } // 调用原函数并返回结果 return pushState.apply(history, arguments); }; })(window.history); // 现在你可以定义一个onpushstate函数来处理pushState调用 window.history.onpushstate = function(event) { // 调用你的统计函数 tianliGPT(true); };