QuickReference/public/js/anzhiyu/ai_abstract.js

439 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function () {
const {
randomNum,
basicWordCount,
btnLink,
key: AIKey,
Referer: AIReferer,
gptName,
switchBtn,
mode: initialMode,
} = GLOBAL_CONFIG.postHeadAiDescription;
const { title, postAI, pageFillDescription } = GLOBAL_CONFIG_SITE;
let lastAiRandomIndex = -1;
let animationRunning = true;
let mode = initialMode;
let refreshNum = 0;
let prevParam;
let audio = null;
let isPaused = false;
let summaryID = null;
const post_ai = document.querySelector(".post-ai-description");
const aiTitleRefreshIcon = post_ai.querySelector(".ai-title .anzhiyufont.anzhiyu-icon-arrow-rotate-right");
let aiReadAloudIcon = post_ai.querySelector(".anzhiyu-icon-circle-dot");
const explanation = post_ai.querySelector(".ai-explanation");
let aiStr = "";
let aiStrLength = "";
let delayInit = 600;
let indexI = 0;
let indexJ = 0;
let timeouts = [];
let elapsed = 0;
const observer = createIntersectionObserver();
const aiFunctions = [introduce, aiTitleRefreshIconClick, aiRecommend, aiGoHome];
const aiBtnList = post_ai.querySelectorAll(".ai-btn-item");
const filteredHeadings = Array.from(aiBtnList).filter(heading => heading.id !== "go-tianli-blog");
filteredHeadings.forEach((item, index) => {
item.addEventListener("click", () => {
aiFunctions[index]();
});
});
document.getElementById("ai-tag").addEventListener("click", onAiTagClick);
aiTitleRefreshIcon.addEventListener("click", onAiTitleRefreshIconClick);
document.getElementById("go-tianli-blog").addEventListener("click", () => {
window.open(btnLink, "_blank");
});
aiReadAloudIcon.addEventListener("click", readAloud);
async function readAloud() {
if (!summaryID) {
anzhiyu.snackbarShow("摘要还没加载完呢,请稍后。。。");
return;
}
aiReadAloudIcon = post_ai.querySelector(".anzhiyu-icon-circle-dot");
aiReadAloudIcon.style.opacity = "0.2";
if (audio && !isPaused) {
audio.pause();
isPaused = true;
aiReadAloudIcon.style.opacity = "1";
aiReadAloudIcon.style.animation = "";
aiReadAloudIcon.style.cssText = "animation: ''; opacity: 1;cursor: pointer;";
return;
}
if (audio && isPaused) {
audio.play();
isPaused = false;
aiReadAloudIcon.style.cssText = "animation: breathe .5s linear infinite; opacity: 0.2;cursor: pointer";
return;
}
const options = {
key: AIKey,
Referer: AIReferer,
};
const requestParams = new URLSearchParams({
key: options.key,
id: summaryID,
});
const requestOptions = {
method: "GET",
headers: {
"Content-Type": "application/json",
Referer: options.Referer,
},
};
try {
const response = await fetch(`https://summary.tianli0.top/audio?${requestParams}`, requestOptions);
if (response.status === 403) {
console.error("403 refer与key不匹配。");
} else if (response.status === 500) {
console.error("500 系统内部错误");
} else {
const audioBlob = await response.blob();
const audioURL = URL.createObjectURL(audioBlob);
audio = new Audio(audioURL);
audio.play();
aiReadAloudIcon.style.cssText = "animation: breathe .5s linear infinite; opacity: 0.2;cursor: pointer";
audio.addEventListener("ended", () => {
audio = null;
aiReadAloudIcon.style.opacity = "1";
aiReadAloudIcon.style.animation = "";
});
}
} catch (error) {
console.error("请求发生错误❎");
}
}
if (switchBtn) {
document.getElementById("ai-Toggle").addEventListener("click", changeShowMode);
}
aiAbstract();
showAiBtn();
function createIntersectionObserver() {
return new IntersectionObserver(
entries => {
let isVisible = entries[0].isIntersecting;
animationRunning = isVisible;
if (animationRunning) {
delayInit = indexI === 0 ? 200 : 20;
timeouts[1] = setTimeout(() => {
if (indexJ) {
indexI = 0;
indexJ = 0;
}
if (indexI === 0) {
explanation.innerHTML = aiStr.charAt(0);
}
requestAnimationFrame(animate);
}, delayInit);
}
},
{ threshold: 0 }
);
}
function animate(timestamp) {
if (!animationRunning) {
return;
}
if (!animate.start) animate.start = timestamp;
elapsed = timestamp - animate.start;
if (elapsed >= 20) {
animate.start = timestamp;
if (indexI < aiStrLength - 1) {
let char = aiStr.charAt(indexI + 1);
let delay = /[,.,。!?]/.test(char) ? 150 : 20;
if (explanation.firstElementChild) {
explanation.removeChild(explanation.firstElementChild);
}
explanation.innerHTML += char;
let div = document.createElement("div");
div.className = "ai-cursor";
explanation.appendChild(div);
indexI++;
if (delay === 150) {
post_ai.querySelector(".ai-explanation .ai-cursor").style.opacity = "0.2";
}
if (indexI === aiStrLength - 1) {
observer.disconnect();
explanation.removeChild(explanation.firstElementChild);
}
timeouts[0] = setTimeout(() => {
requestAnimationFrame(animate);
}, delay);
}
} else {
requestAnimationFrame(animate);
}
}
function clearTimeouts() {
if (timeouts.length) {
timeouts.forEach(item => {
if (item) {
clearTimeout(item);
}
});
}
}
function startAI(str, df = true) {
indexI = 0;
indexJ = 1;
clearTimeouts();
animationRunning = false;
elapsed = 0;
observer.disconnect();
explanation.innerHTML = df ? "生成中. . ." : "请等待. . .";
aiStr = str;
aiStrLength = aiStr.length;
observer.observe(post_ai);
}
async function aiAbstract(num = basicWordCount) {
if (mode === "tianli") {
await aiAbstractTianli(num);
} else {
aiAbstractLocal();
}
}
async function aiAbstractTianli(num) {
indexI = 0;
indexJ = 1;
clearTimeouts();
animationRunning = false;
elapsed = 0;
observer.disconnect();
num = Math.max(10, Math.min(2000, num));
const options = {
key: AIKey,
Referer: AIReferer,
};
const truncateDescription = (title + pageFillDescription).trim().substring(0, num);
const requestBody = {
key: options.key,
content: truncateDescription,
url: location.href,
};
const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
Referer: options.Referer,
},
body: JSON.stringify(requestBody),
};
console.info(truncateDescription.length);
try {
let animationInterval = null;
let summary;
if (animationInterval) clearInterval(animationInterval);
animationInterval = setInterval(() => {
const animationText = "生成中" + ".".repeat(indexJ);
explanation.innerHTML = animationText;
indexJ = (indexJ % 3) + 1;
}, 500);
const response = await fetch(`https://summary.tianli0.top/`, requestOptions);
let result;
if (response.status === 403) {
result = {
summary: "403 refer与key不匹配。",
};
} else if (response.status === 500) {
result = {
summary: "500 系统内部错误",
};
} else {
result = await response.json();
}
summary = result.summary.trim();
summaryID = result.id;
setTimeout(() => {
aiTitleRefreshIcon.style.opacity = "1";
}, 300);
if (summary) {
startAI(summary);
} else {
startAI("摘要获取失败!!!请检查Tianli服务是否正常!!!");
}
clearInterval(animationInterval);
} catch (error) {
console.error(error);
explanation.innerHTML = "发生异常" + error;
}
}
function aiAbstractLocal() {
const strArr = postAI.split(",").map(item => item.trim());
if (strArr.length !== 1) {
let randomIndex = Math.floor(Math.random() * strArr.length);
while (randomIndex === lastAiRandomIndex) {
randomIndex = Math.floor(Math.random() * strArr.length);
}
lastAiRandomIndex = randomIndex;
startAI(strArr[randomIndex]);
} else {
startAI(strArr[0]);
}
setTimeout(() => {
aiTitleRefreshIcon.style.opacity = "1";
}, 600);
}
function aiRecommend() {
indexI = 0;
indexJ = 1;
clearTimeouts();
animationRunning = false;
elapsed = 0;
explanation.innerHTML = "生成中. . .";
aiStr = "";
aiStrLength = "";
observer.disconnect();
timeouts[2] = setTimeout(() => {
explanation.innerHTML = recommendList();
}, 600);
}
function recommendList() {
let thumbnail = document.querySelectorAll(".relatedPosts-list a");
if (!thumbnail.length) {
const cardRecentPost = document.querySelector(".card-widget.card-recent-post");
if (!cardRecentPost) return "";
thumbnail = cardRecentPost.querySelectorAll(".aside-list-item a");
let list = "";
for (let i = 0; i < thumbnail.length; i++) {
const item = thumbnail[i];
list += `<div class="ai-recommend-item"><span class="index">${
i + 1
}</span><a href="javascript:;" onclick="pjax.loadUrl('${item.href}')" title="${
item.title
}" data-pjax-state="">${item.title}</a></div>`;
}
return `很抱歉,无法找到类似的文章,你也可以看看本站最新发布的文章:<br /><div class="ai-recommend">${list}</div>`;
}
let list = "";
for (let i = 0; i < thumbnail.length; i++) {
const item = thumbnail[i];
list += `<div class="ai-recommend-item"><span>推荐${
i + 1
}</span><a href="javascript:;" onclick="pjax.loadUrl('${item.href}')" title="${
item.title
}" data-pjax-state="">${item.title}</a></div>`;
}
return `推荐文章:<br /><div class="ai-recommend">${list}</div>`;
}
function aiGoHome() {
startAI("正在前往博客主页...", false);
timeouts[2] = setTimeout(() => {
if (window.pjax) {
pjax.loadUrl("/");
} else {
location.href = location.origin;
}
}, 1000);
}
function introduce() {
if (mode == "tianli") {
startAI("我是文章辅助AI: TianliGPT点击下方的按钮让我生成本文简介、推荐相关文章等。");
} else {
startAI(`我是文章辅助AI: ${gptName} GPT点击下方的按钮让我生成本文简介、推荐相关文章等。`);
}
}
function aiTitleRefreshIconClick() {
aiTitleRefreshIcon.click();
}
function onAiTagClick() {
if (mode === "tianli") {
post_ai.querySelectorAll(".ai-btn-item").forEach(item => (item.style.display = "none"));
document.getElementById("go-tianli-blog").style.display = "block";
startAI(
"你好我是Tianli开发的摘要生成助理TianliGPT是一个基于GPT-4的生成式AI。我在这里只负责摘要的预生成和显示你无法与我直接沟通如果你也需要一个这样的AI摘要接口可以在下方购买。"
);
} else {
post_ai.querySelectorAll(".ai-btn-item").forEach(item => (item.style.display = "block"));
document.getElementById("go-tianli-blog").style.display = "none";
startAI(
`你好,我是本站摘要生成助理${gptName} GPT是一个基于GPT-4的生成式AI。我在这里只负责摘要的预生成和显示你无法与我直接沟通。`
);
}
}
function onAiTitleRefreshIconClick() {
const truncateDescription = (title + pageFillDescription).trim().substring(0, basicWordCount);
aiTitleRefreshIcon.style.opacity = "0.2";
aiTitleRefreshIcon.style.transitionDuration = "0.3s";
aiTitleRefreshIcon.style.transform = "rotate(" + 360 * refreshNum + "deg)";
if (truncateDescription.length <= basicWordCount) {
let param = truncateDescription.length - Math.floor(Math.random() * randomNum);
while (param === prevParam || truncateDescription.length - param === prevParam) {
param = truncateDescription.length - Math.floor(Math.random() * randomNum);
}
prevParam = param;
aiAbstract(param);
} else {
let value = Math.floor(Math.random() * randomNum) + basicWordCount;
while (value === prevParam || truncateDescription.length - value === prevParam) {
value = Math.floor(Math.random() * randomNum) + basicWordCount;
}
aiAbstract(value);
}
refreshNum++;
}
function changeShowMode() {
mode = mode === "tianli" ? "local" : "tianli";
if (mode === "tianli") {
document.getElementById("ai-tag").innerHTML = "TianliGPT";
aiReadAloudIcon.style.opacity = "1";
aiReadAloudIcon.style.cursor = "pointer";
} else {
aiReadAloudIcon.style.opacity = "0";
aiReadAloudIcon.style.cursor = "auto";
if ((document.getElementById("go-tianli-blog").style.display = "block")) {
document.querySelectorAll(".ai-btn-item").forEach(item => (item.style.display = "block"));
document.getElementById("go-tianli-blog").style.display = "none";
}
document.getElementById("ai-tag").innerHTML = gptName + " GPT";
}
aiAbstract();
}
function showAiBtn() {
if (mode === "tianli") {
document.getElementById("ai-tag").innerHTML = "TianliGPT";
} else {
document.getElementById("ai-tag").innerHTML = gptName + " GPT";
}
}
})();