439 lines
14 KiB
JavaScript
439 lines
14 KiB
JavaScript
(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";
|
||
}
|
||
}
|
||
})();
|