// ==UserScript==
// @name 巴哈姆特_新版B頁板務功能
// @namespace Bee10301
// @version 8.4
// @description 巴哈姆特哈拉區新體驗。
// @author Bee10301
// @match https://www.gamer.com.tw/
// @match https://www.gamer.com.tw/index2.php*
// @match https://forum.gamer.com.tw/B.php?*
// @match https://forum.gamer.com.tw/C.php?*
// @homepage https://home.gamer.com.tw/home.php?owner=bee10301
// @icon https://home.gamer.com.tw/favicon.ico
// @connect *
// @grant GM_xmlhttpRequest
// @license GPL
// ==/UserScript==
// noinspection SpellCheckingInspection,JSUnresolvedReference,BadExpressionStatementJS
(async function () {
("use strict");
checkFirstRun();
await addSettingElement();
await worker_bPage();
await worker_cPage();
worker_home();
checkTips();
reportAlert();
})();
function checkFirstRun(reset = false) {
console.log("[INFO] Init data");
// add_function > 標題後方插入功能
// preview_auto > 一律即時瀏覽(覆寫文章換頁)
// preview_wait_load > 即時瀏覽:是否等待載入完成才跳出顯示
// preview_size > 即時瀏覽視窗的大小
// new_design > 自適型版面(根據下方自定比例適應)
// new_design_box > 顯示區域佔比(文章顯示區+聊天室區的整體範圍)
// new_design_box_Left > 文章佔比(佔上個設定「顯示區域」範圍內的比例)
// new_design_box_Right > 聊天室佔比(佔上方設定「顯示區域」範圍內的比例)
// new_design_LRSwitch > 左右對調(聊天室在左方,讓文章標題在螢幕中間)
// bee_select_color > 勾選文章時的顏色(可含有透明度屬性)
// preview_LR > 即時瀏覽視窗的位置
const settingsB = [
{ key: "isFirstRun", defaultValue: "false" },
{
key: "add_function",
defaultValue: "true",
},
{
key: "preview_auto",
defaultValue: "true",
},
{
key: "preview_wait_load",
defaultValue: "false",
},
{
key: "preview_size",
defaultValue: "65%",
},
{
key: "new_design",
defaultValue: "true",
},
{
key: "new_design_box",
defaultValue: "80%",
},
{
key: "new_design_box_Left",
defaultValue: "70%",
},
{
key: "new_design_box_Right",
defaultValue: "25%",
},
{
key: "new_design_LRSwitch",
defaultValue: "true",
},
{
key: "bee_select_color",
defaultValue: "#000000b3",
},
{
key: "addBorderInPicMode",
defaultValue: "true",
},
{
key: "showTips",
defaultValue: "true",
},
{
key: "preview_LR",
defaultValue: "true",
},
{
key: "showAbuse",
defaultValue: "true",
},
{
key: "addSummaryBtn",
defaultValue: "true",
},
{
key: "oaiBaseUrl",
defaultValue: "https://api.openai.com/v1/chat/completions",
},
{
key: "oaiKey",
defaultValue: "sk-yourKey",
},
{
key: "oaiModel",
defaultValue: "gpt-3.5-turbo",
},
{
key: "oaiPrompt",
defaultValue: `## workflow
1. 總結:精確的讀懂和理解文章,然後用一句話脈絡清晰的語句,總結出[文章的主旨]。
2. 提煉重點:根據文章的邏輯和結構,清楚列出文章的主要論點,並按照下方範例的格式輸出。
總結:
- 要點1:
- 要點2:
...(依情況增加或減少要點)
## MUST/IMPORTANT/RULES
- 不能添加其他個人觀點或註釋。
- 使用繁體中文
`,
},
{
key: "oaiPromptCmd",
defaultValue:
"以下是一段群組聊天的對話,總結對話中的話題,用條列式列出使用者的想法。\n## workflow \n 1. 整理話題:理解各個使用者討論的話題並以話題為單位整理出整串對話的話題 \n 2. 將相同話題中,對同一件事有相似想法的對話整理在一起(例如 `@user1/@user2:認為太貴了`) ,不同看法則單獨列出。\n 3. 輸出:把冗餘贅字優化,但保留具體描述。(劣例:`@user1/@user2:提及角色在世界觀中的地位和特徵` 在這個例子中沒有具體描述提即了什麼樣的地位或特徵)。使用者以 @id 標記並且不再添加其他md語法。 \n ## MUST/IMPORTANT/RULES \n- 不能添加其他個人觀點或註釋。\n- 使用繁體中文\n",
},
{
key: "oaiPromptChat",
defaultValue:
"根據文章內容,使用繁體中文流暢語言,簡潔的回答使用者的問題。",
},
{
key: "custom_oaiPrompt",
defaultValue: "",
},
{
key: "custom_oaiPromptCmd",
defaultValue: "",
},
{
key: "custom_oaiPromptChat",
defaultValue: "",
},
{
key: "oaiPromptSystemMode",
defaultValue: "true",
},
{
key: "oaiPromptDate",
defaultValue: "20241101",
},
{
key: "oaiPromptUpdateDate",
defaultValue: "20241101",
},
{
key: "oaiPromptUpdateURL",
defaultValue: "https://gamercomtwnew.bee.moe/gamer.prompts.json",
},
{
key: "oaiPromptUpdateSleep",
defaultValue: "1",
},
// Add other settings as needed
];
const settingsHome = [
{ key: "homeStyleSwitch", defaultValue: "true" },
{
key: "homeTips",
defaultValue: "true",
},
];
let settings = window.location.href.includes("forum.gamer.com.tw/B.php")
? settingsB
: settingsHome;
settings.forEach((setting) => {
if (
localStorage.getItem(setting.key) === "" ||
localStorage.getItem(setting.key) === null ||
reset === true
) {
localStorage.setItem(setting.key, setting.defaultValue);
}
});
if (window.location.href.includes("forum.gamer.com.tw/B.php")) {
if (
localStorage.getItem("oaiPromptUpdateURL") ===
"https://gamercomtwnew.bee.moe/gamer.prompts.js"
) {
localStorage.setItem(
"oaiPromptUpdateURL",
"https://gamercomtwnew.bee.moe/gamer.prompts.json"
);
}
}
}
function checkTips() {
if (window.location.href.includes("forum.gamer.com.tw/B.php")) {
if (localStorage.getItem("showTips") === "true") {
loadTips();
localStorage.setItem("showTips", "false");
}
}
if (window.location.href.includes("www.gamer.com.tw")) {
if (localStorage.getItem("homeTips") === "true") {
loadTips_home();
localStorage.setItem("homeTips", "false");
}
}
}
async function addSettingElement() {
if (!window.location.href.includes("forum.gamer.com.tw/B.php")) {
return;
}
const navAddTag = document.querySelector(".BH-menuE");
const navAdd = document.createElement("li");
navAdd.className = "beeSettingTag";
navAdd.innerHTML = "<a>插件設定</a>";
navAdd.addEventListener("click", function () {
let sectionTitleWarp = document.querySelector(".beeSettingWarp");
popElement(sectionTitleWarp, "toggle", "ud");
scrollIntoBee(document.querySelector(".b-list-wrap"), 7);
});
navAddTag.appendChild(navAdd);
// 取得 management-item 元素
const settingsWarp = document.querySelector(".b-list-wrap");
// 插入新內容到最後一個 management-item 元素中
const lastManagementItem = document.createElement("div");
lastManagementItem.className = "beeSettingWarp";
const sectionTitle = document.createElement("h3");
sectionTitle.className = "section-title";
sectionTitle.textContent =
"插件設定(再點一次上方的【插件設定】即可返回【文章列表】)";
sectionTitle.style.margin = "0.6rem 0 0.7rem 0.7rem";
lastManagementItem.appendChild(sectionTitle);
lastManagementItem.appendChild(
createItemCard("add_function", "標題後方插入功能按鈕")
);
lastManagementItem.appendChild(
createItemCard("preview_auto", "點擊文章時使用即時瀏覽")
);
lastManagementItem.appendChild(
createItemCard(null, null, {
inputId: "preview_size",
labelText: " └ 即時瀏覽視窗的大小",
})
);
lastManagementItem.appendChild(
createItemCard("preview_LR", "即時瀏覽從右方彈出(取消則從左)")
);
lastManagementItem.appendChild(
createItemCard("new_design", "自適型版面(根據下方自定比例適應)")
);
lastManagementItem.appendChild(
createItemCard(null, null, {
inputId: "new_design_box",
labelText: " └ 整體顯示區域佔比(文章+聊天室佔整個畫面的比例,< 100%)",
})
);
lastManagementItem.appendChild(
createItemCard(null, null, {
inputId: "new_design_box_Left",
labelText: " ├ 文章佔比(與聊天室佔比總和 <= 100%)",
})
);
lastManagementItem.appendChild(
createItemCard(null, null, {
inputId: "new_design_box_Right",
labelText: " └ 聊天室佔比",
})
);
lastManagementItem.appendChild(
createItemCard(
"new_design_LRSwitch",
"左右對調(聊天室在左方,讓文章標題在螢幕中間)"
)
);
lastManagementItem.appendChild(
createItemCard("addBorderInPicMode", "縮圖列表模式中,加上分隔線")
);
lastManagementItem.appendChild(
createItemCard("showAbuse", "有檢舉時,自動以即時瀏覽開啟")
);
//summary
lastManagementItem.appendChild(
createItemCard(
"addSummaryBtn",
"跳過樓層按鈕/AI總結(AI功能需自備KEY填入下方)"
)
);
lastManagementItem.appendChild(
createItemCard(null, null, {
inputId: "oaiBaseUrl",
labelText: " ├ oai URL",
})
);
lastManagementItem.appendChild(
createItemCard(null, null, {
inputId: "oaiModel",
labelText: " ├ oai model",
})
);
lastManagementItem.appendChild(
createItemCard(null, null, {
inputId: "oaiKey",
labelText: " ├ oai key",
})
);
lastManagementItem.appendChild(
createItemCard(null, null, {
inputId: "custom_oaiPrompt",
labelText: " ├ 「懶人包」提示詞(留空=預設)",
})
);
lastManagementItem.appendChild(
createItemCard(null, null, {
inputId: "custom_oaiPromptCmd",
labelText: " ├ 「留言統整」自訂提示詞(留空=預設)",
})
);
lastManagementItem.appendChild(
createItemCard(null, null, {
inputId: "custom_oaiPromptChat",
labelText: " ├ 「問問」自訂提示詞(留空=預設)",
})
);
lastManagementItem.appendChild(
createItemCard("oaiPromptSystemMode", "├ 自訂提示詞使用 system 模式")
);
lastManagementItem.appendChild(
createItemCard(null, null, {
inputId: "oaiPromptUpdateURL",
labelText: " └ oai prompt settings URL",
})
);
lastManagementItem.appendChild(createItemCard("showTips", "重新觀看TIPs"));
// add one btn in a div ,click to reload the page
const reloadBtn = document.createElement("button");
reloadBtn.textContent = "重新載入";
reloadBtn.style.margin = "0.5rem 0 0.7rem 0.7rem";
reloadBtn.addEventListener("click", () => {
location.reload();
});
const reloadBtnDiv = document.createElement("div");
reloadBtnDiv.className = "BH-rbox BH-qabox1";
reloadBtnDiv.appendChild(reloadBtn);
lastManagementItem.appendChild(reloadBtnDiv);
settingsWarp.insertBefore(lastManagementItem, settingsWarp.firstChild);
await popElementInit(lastManagementItem, false, "ud");
}
// 項目卡函數
function createItemCard(inputId, labelText, additionalContent = null) {
const isDarkTheme =
window.getComputedStyle(document.getElementById("BH-menu-path"))
.backgroundColor === "rgb(28, 28, 28)";
const itemCard = document.createElement("div");
itemCard.className = "item-card management_guild-check single-choice";
const checkGroup = document.createElement("div");
checkGroup.className = "check-group";
checkGroup.style.margin = "0rem 0 0.1rem 0.7rem";
if (inputId) {
const input = document.createElement("input");
input.id = inputId;
input.type = "checkbox";
// 如果 localStorage 有儲存的值,則設置為該值,否則預設為 checked
input.checked = localStorage.getItem(inputId) === "true";
const label = document.createElement("label");
label.htmlFor = inputId;
label.className = "is-active";
const labelIcon = document.createElement("div");
labelIcon.className = "label-icon";
const icon = document.createElement("i");
icon.className = "fa fa-check";
labelIcon.appendChild(icon);
const h6 = document.createElement("h6");
h6.textContent = labelText;
h6.style.display = "inline-block";
h6.style.color = isDarkTheme ? "#C7C6CB" : "#117e96";
label.appendChild(labelIcon);
label.appendChild(h6);
checkGroup.appendChild(input);
checkGroup.appendChild(label);
// 添加 input 事件監聽器,將值保存到 localStorage
input.addEventListener("input", function () {
localStorage.setItem(inputId, this.checked.toString());
});
}
if (additionalContent) {
const h6 = document.createElement("h6");
h6.textContent = additionalContent.labelText;
h6.style.display = "inline-block";
checkGroup.appendChild(h6);
const input = document.createElement("input");
input.className = "form-control";
input.id = additionalContent.inputId;
input.type = "text";
input.size = 25;
input.style.margin = "0px";
input.style.width = additionalContent.inputId.startsWith("custom_")
? "auto"
: "70px";
checkGroup.appendChild(input);
// 如果 localStorage 有儲存的值,則設置為該值
input.value = localStorage.getItem(additionalContent.inputId) || "";
// 添加 input 事件監聽器,將值保存到 localStorage
input.addEventListener("input", function () {
localStorage.setItem(additionalContent.inputId, this.value);
});
}
itemCard.appendChild(checkGroup);
return itemCard;
}
async function worker_cPage() {
if (!window.location.href.includes("forum.gamer.com.tw/C.php")) {
return;
}
let styleSheet = document.createElement("style");
document.head.appendChild(styleSheet);
let sheet = styleSheet.sheet;
sheet.insertRule(
".managertools { position: fixed; bottom: 0; right: 0; z-index: 100; }",
0
);
if (localStorage.getItem("addSummaryBtn") === "false") {
return;
}
await postAddBtn();
}
async function worker_bPage() {
if (window.location.href.includes("forum.gamer.com.tw/B.php")) {
if (localStorage.getItem("preview_auto") === "true") {
bPage_previewAuto();
}
await bPage_addFrame();
bPage_addMenu();
bPage_new_checkbox();
if (localStorage.getItem("new_design") === "true") {
bPage_new_design();
}
if (localStorage.getItem("new_design_LRSwitch") === "true") {
document.getElementById("BH-master").style.float = "right";
document.getElementById("BH-slave").style.float = "left";
}
if (localStorage.getItem("add_function") === "true") {
bPage_addFunction();
}
//add border in pic mode must be excute after, because previewAuto does change the structure
if (localStorage.getItem("addBorderInPicMode") === "true") {
bPage_addBorderInPicMode();
}
}
}
function worker_home() {
if (
!window.location.href.includes("www.gamer.com.tw") ||
!document.querySelectorAll("div.BA-lbox.BA-lbox3")
) {
return;
}
// add btn
const baServeElement = document.querySelector(".BA-serve");
const newElement = document.createElement("li");
newElement.innerHTML = `<a id="homeStyleSwitch" class="gtm-indexservice" title="樣式切換"><img style="pointer-events: none;" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='rgb(32,148,255)' d='M16 16v-4l5 5l-5 5v-4H4v-2zM8 2v3.999L20 6v2H8v4L3 7z'/%3E%3C/svg%3E">首頁滿版切換</a>`;
baServeElement.appendChild(newElement);
// 切換 localStorage 中 homeStyleSwitch 的值
newElement.addEventListener("click", () => {
const currentValue = localStorage.getItem("homeStyleSwitch") === "true";
localStorage.setItem("homeStyleSwitch", (!currentValue).toString());
location.reload(); // 刷新頁面以應用更改
});
// if active
if (localStorage.getItem("homeStyleSwitch") === "true") {
// Get the elements
const hotboardContainer = document.getElementById("hotboardContainer");
const guildContainer = document.getElementById("guildContainer");
const hothalaContainer = document.getElementById("hothalaContainer");
// Append the elements to the "hothalaContainer"
hothalaContainer.appendChild(hotboardContainer);
hothalaContainer.appendChild(guildContainer);
// 2nd left nav
const bahaStoreContainer = document.querySelectorAll(
"div.BA-lbox.BA-lbox3"
)[0];
const bahaAnimeContainer = document.querySelectorAll(
"div.BA-lbox.BA-lbox3"
)[1];
let secondDivLeft = document.createElement("div");
secondDivLeft.className = "BA-left";
secondDivLeft.style.flex = "0 0 11em";
secondDivLeft.style.margin = "0 0 0 1em";
secondDivLeft.appendChild(document.querySelectorAll("h1.BA-ltitle")[1]);
secondDivLeft.appendChild(bahaAnimeContainer);
secondDivLeft.appendChild(document.querySelectorAll("h1.BA-ltitle")[0]);
secondDivLeft.appendChild(bahaStoreContainer);
document
.querySelectorAll("div.BA-wrapper.BA-main")[0]
.insertBefore(
secondDivLeft,
document.querySelectorAll("div.BA-center")[0]
);
// Create a new style element
const newStyle = document.createElement("style");
// Set the CSS content
newStyle.textContent = `
/* 父容器設置 */
.BA-wrapper.BA-main {
display: flex;
flex-wrap: wrap;
width: auto;
}
/* 第一層左側固定寬度 */
.BA-left {
flex: 0 0 11em;
}
/* 第一層右側容器 */
.BA-center {
flex: 1; /* 占據剩餘所有空間 */
display: flex; /* 使其內部也成為flex容器 */
flex-wrap: wrap; /* 允許內部元素換行 */
}
#gnnContainer {
flex: 0 0 45%;
margin: 0 1% 2% 1%;
order: 1;
}
#hothalaContainer {
flex: 45%;
margin: 0 1% 2% 1%;
order: 2;
}
#homeContainer {
flex: 45%;
margin: 0 1% 2% 1%;
order: 3;
}
#buyContainer {
flex: 0 0 45%;
margin: 0 1% 2% 1%;
order: 4;
}
#liveContainer {
flex: 45%;
margin: 0 1% 2% 1%;
order: 5;
}
#gamecrazyContainer {
flex: 0 0 45%;
margin: 0 1% 2% 1%;
order: 6;
}
.BA-cbox7 p{
text-align: left !important;
}
`;
// Append the style element to the head of the document
document.head.appendChild(newStyle);
}
}
async function bPage_addFrame() {
//frame
let beePreviewWd = document.createElement("div");
beePreviewWd.className = "bee_preview_wd";
beePreviewWd.style.height = "100%";
//beePreviewWd.style.width = '0rem';
beePreviewWd.style.width = localStorage.getItem("preview_size");
beePreviewWd.style.transform = "scaleX(" + 0 + ")";
beePreviewWd.style.zIndex = "100";
beePreviewWd.style.position = "fixed";
beePreviewWd.style.top = "0px";
if (localStorage.getItem("preview_LR") === "true") {
beePreviewWd.style.right = "1%";
} else {
beePreviewWd.style.left = "1%";
}
//beePreviewWd.style.transition = 'all 0.5s cubic-bezier(0.21, 0.3, 0.18, 1.37) 0s';
//document.body.appendChild(beePreviewWd);
let beeFrame = document.createElement("iframe");
beeFrame.id = "bee_frame";
beeFrame.title = "bee_frame";
beeFrame.src = "";
//beeFrame.style.transition = 'all 0.5s cubic-bezier(0.21, 0.3, 0.18, 1.37) 0s';
beeFrame.style.border = "0em solid rgb(170, 50, 220, 0)";
beeFrame.width = "100%";
beeFrame.height = "100%";
//document.querySelector('.bee_preview_wd').appendChild(beeFrame);
beePreviewWd.appendChild(beeFrame);
document.body.appendChild(beePreviewWd);
await popElementInit(
beePreviewWd,
false,
localStorage.getItem("preview_LR") === "true" ? "rl" : "lr",
false
);
//close frame by top menu
let BHMenuPath = document.querySelector("#BH-menu-path");
BHMenuPath.style.transition =
"all 0.5s cubic-bezier(0.21, 0.3, 0.18, 1.37) 0s";
BHMenuPath.style.height = "40px";
BHMenuPath.style.opacity = "1";
//BHMenuPath.style.backgroundColor = '#0e4355cc';
BHMenuPath.addEventListener("click", () => {
/*document.querySelector('.bee_preview_wd').style.transform = 'translateX(100%)';
document.querySelector('.bee_preview_wd').style.opacity = '0';*/
popElement(
document.querySelector(".bee_preview_wd"),
"false",
localStorage.getItem("preview_LR") === "true" ? "rl" : "lr"
);
BHMenuPath.style.height = "40px";
BHMenuPath.style.opacity = "1";
});
}
function bPage_addMenu() {
try {
// 獲取 .managertools 元素
const managertools = document.querySelector(".managertools");
// 創建 .b-manager 容器
const bManagerDiv = document.createElement("div");
bManagerDiv.className = "b-manager managertools bee_manager";
bManagerDiv.style.zIndex = "100";
bManagerDiv.style.position = "fixed";
bManagerDiv.style.width = "auto";
// 創建 .checkbox 和 <label> 元素
const checkboxDiv = document.createElement("div");
checkboxDiv.className = "checkbox";
const label = document.createElement("label");
label.setAttribute("for", "check");
// 將 .checkbox 和 <label> 插入到 .b-manager 容器中
bManagerDiv.appendChild(checkboxDiv);
bManagerDiv.appendChild(label);
// 創建並插入包含按鈕的 .bee 容器
const buttonIndexes = [
[0, 3, 7],
[2, 4],
[1, 8],
[5, 6],
];
buttonIndexes.forEach((indexes) => {
const beeDiv = document.createElement("div");
beeDiv.className = "bee";
beeDiv.style.padding = "5px";
indexes.forEach((index) => {
const button = managertools
.querySelectorAll("button")
[index].cloneNode(true);
beeDiv.appendChild(button);
});
bManagerDiv.appendChild(beeDiv);
});
// 將 .b-manager 容器添加到 .managertools 元素中
managertools.appendChild(bManagerDiv);
const all_links = document.querySelectorAll(
"#BH-master > form > div > table > tbody > tr > td.b-list__main > a"
);
Array.from(all_links).forEach(function (link) {
link.addEventListener("click", function (event) {
event.stopPropagation();
});
});
const all_blocks_pic_mode = document.querySelectorAll(
"#BH-master > form > div > table > tbody > tr > td.b-list__main"
);
Array.from(all_blocks_pic_mode).forEach(function (link) {
link.addEventListener("click", function (event) {
event.stopPropagation();
});
});
} catch (e) {}
}
function bPage_new_checkbox() {
let all_title;
if (document.querySelectorAll(".imglist-text").length === 0) {
all_title = document.getElementsByClassName("b-list__main");
} else {
all_title = document.getElementsByClassName("b-list__main");
}
//const all_title_link = document.getElementsByClassName("b-list__main__title");
let temp_elements_checkbox;
try {
temp_elements_checkbox = document.getElementsByName("jsn[]");
} catch (e) {}
for (let i = 0; i < all_title.length; i++) {
//prevent child trigger
let children = all_title[i].querySelectorAll("*");
children.forEach(function (child) {
child.addEventListener("click", function (event) {
event.stopPropagation();
});
});
try {
temp_elements_checkbox[i].checked = false;
} catch (e) {}
// 添加 onclick 事件
all_title[i].onclick = function (e) {
// 如果子元素有 b-list__tile 或 imglist-text 類別,則觸發
if (
all_title[i].querySelector(".b-list__main__title") !== null ||
all_title[i].querySelector(".imglist-text") !== null
) {
// 如果當前有
try {
document.querySelector(".bee_manager").style.display = "none";
} catch (e) {}
// 獲取內部 HTML
let temp_matcher = this.innerHTML;
// 獲取 snA
temp_matcher = temp_matcher.match(/snA=(\d*)/)[1];
let haveCheckedBox = false;
for (let i2 = 0; i2 < temp_elements_checkbox.length; i2++) {
if (temp_elements_checkbox[i2].value === temp_matcher) {
if (temp_elements_checkbox[i2].checked) {
temp_elements_checkbox[i2].checked = false;
this.style.backgroundColor = "";
} else {
temp_elements_checkbox[i2].checked = true;
this.style.backgroundColor =
localStorage.getItem("bee_select_color");
}
}
if (temp_elements_checkbox[i2].checked) {
haveCheckedBox = true;
}
}
if (haveCheckedBox) {
let beeManager = document.querySelector(".bee_manager");
beeManager.style.left =
e.clientX + 50 /*+ document.documentElement.scrollLeft */ + `px`;
beeManager.style.top =
e.clientY - 170 /*+ document.documentElement.scrollTop */ + `px`;
beeManager.style.display = "block";
}
}
};
// 右鍵點擊(*只在元素上)
/*all_title[i].oncontextmenu = () => {
let beeManager = document.querySelector(".bee_manager");
beeManager.style.left = `${BmouseX}px`;
beeManager.style.top = `${BmouseY - temp_scroll}px`;
beeManager.style.display = 'block';
// 右鍵點擊返回
return false;
};*/
}
}
function bPage_new_design() {
// 獲取所有的.b-list_ad元素,並將其display設為none
let bListAdElements = document.querySelectorAll(".b-list_ad");
for (let i = 0; i < bListAdElements.length; i++) {
bListAdElements[i].style.display = "none";
}
// 以下為設置元素的寬度
document.getElementById("BH-wrapper").style.width =
localStorage.getItem("new_design_box");
document.getElementById("BH-master").style.width = localStorage.getItem(
"new_design_box_Left"
);
document.getElementById("BH-slave").style.width = localStorage.getItem(
"new_design_box_Right"
);
}
function bPage_addFunction() {
const all_title = document.getElementsByClassName("b-list__main");
const all_title_link = document.getElementsByClassName("b-list__main__title");
// 創建一個新的 <td> 元素並插入到 .b-list__filter 元素後面
const newTd = document.createElement("td");
document
.querySelector(".b-list__filter")
.insertAdjacentElement("afterend", newTd);
for (let i2 = 0; i2 < all_title.length; i2++) {
const isDarkTheme =
window.getComputedStyle(document.getElementById("BH-menu-path"))
.backgroundColor === "rgb(28, 28, 28)";
const hrefValue = all_title_link[i2].getAttribute("href");
// 創建外層 <td> 元素
const td = document.createElement("td");
td.style.width = "auto";
// 創建各個按鈕的容器和圖標
const buttons = [
{
title: "快速瀏覽",
class: "bee_preview",
icon: "fullscreen",
onclick: () => openInFrame("https://forum.gamer.com.tw/" + hrefValue),
},
{
title: "開新視窗",
class: "bee_open_new_wd",
icon: "open_in_new",
onclick: () => window.open(hrefValue),
},
{
title: "複製連結",
class: "bee_link",
icon: "link",
onclick: () =>
navigator.clipboard.writeText(
"https://forum.gamer.com.tw/" + hrefValue
),
},
];
buttons.forEach((button) => {
const a = document.createElement("a");
a.title = button.title;
a.className = `btn-icon btn-icon--inverse ${button.class}`;
a.style.display = "none";
if (button.onclick) {
a.onclick = button.onclick;
}
const i = document.createElement("i");
i.className = `material-icons ${button.class}`;
i.textContent = button.icon;
//i.style.display = 'none';
if (!isDarkTheme) {
i.style.color = "rgba(0, 0, 0, 0.4)";
}
a.appendChild(i);
td.appendChild(a);
});
// 將生成的 <td> 插入到 .b-list__main 的相應位置
document
.querySelectorAll(".b-list__main")
[i2].insertAdjacentElement("afterend", td);
}
// 文章列表元素
let rows = document.querySelectorAll(".b-list__row");
// 添加事件監聽器 - 顯示/隱藏元素
rows.forEach((row) => {
// Add hover event listener
row.addEventListener("mouseover", function () {
// Show elements
let beePreview = row.querySelector(".bee_preview");
let beeOpenNewWd = row.querySelector(".bee_open_new_wd");
let beeLink = row.querySelector(".bee_link");
if (beePreview) beePreview.style.display = "";
if (beeOpenNewWd) beeOpenNewWd.style.display = "";
if (beeLink) beeLink.style.display = "";
});
row.addEventListener("mouseout", function () {
// Hide elements
let beePreview = row.querySelector(".bee_preview");
let beeOpenNewWd = row.querySelector(".bee_open_new_wd");
let beeLink = row.querySelector(".bee_link");
if (beePreview) beePreview.style.display = "none";
if (beeOpenNewWd) beeOpenNewWd.style.display = "none";
if (beeLink) beeLink.style.display = "none";
});
});
}
function bPage_previewAuto() {
let picMode = document.querySelectorAll(".imglist-text").length !== 0;
if (picMode) {
let switchTopics = document.querySelectorAll(".b-list__main");
switchTopics.forEach((switchTopic) => {
let topicTexts = switchTopic.childNodes[1].childNodes[3];
//only in pic mode will trigger
if (topicTexts.className === "imglist-text") {
switchTopic.childNodes[1].removeChild(topicTexts);
switchTopic.insertAdjacentHTML("beforeend", topicTexts.outerHTML);
}
});
}
let bListMainTitles = document.querySelectorAll(".b-list__main__title");
bListMainTitles.forEach((bListMainTitle) => {
bListMainTitle.addEventListener("click", (e) => {
e.preventDefault();
let href = bListMainTitle.parentNode.parentNode
.querySelector(".b-list__main__title")
.getAttribute("href");
openInFrame(`https://forum.gamer.com.tw/${href}`);
return false;
});
});
let bListMainTitlesPages = document.querySelectorAll(
!picMode
? "#BH-master > form > div > table > tbody > tr > td.b-list__main > span > a"
: "#BH-master > form > div > table > tbody > tr > td.b-list__main > div > div > span > span"
);
bListMainTitlesPages.forEach((bListMainTitlePage) => {
bListMainTitlePage.addEventListener("click", (e) => {
e.preventDefault();
let href = bListMainTitlePage.getAttribute(
!picMode ? "href" : "data-page"
);
openInFrame(`https://forum.gamer.com.tw/${href}`);
return false;
});
});
}
function bPage_addBorderInPicMode() {
const all_blocks_pic_mode = document.querySelectorAll(
"#BH-master > form > div > table > tbody > tr > td.b-list__main > div > p"
);
Array.from(all_blocks_pic_mode).forEach(function (link) {
link.style.borderTop = "dashed";
});
}
function loadTips() {
if (!window.location.href.includes("forum.gamer.com.tw/B.php")) {
return;
}
let picMode;
picMode = document.querySelectorAll(".imglist-text").length !== 0;
let link = document.createElement("link");
link.rel = "stylesheet";
link.href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/driver.css";
document.head.appendChild(link);
let script = document.createElement("script");
script.src =
"https://cdn.jsdelivr.net/npm/[email protected]/dist/driver.js.iife.js";
script.onload = function () {
console.log("Driver.js loaded");
const driver = window.driver.js.driver;
const driverObj = driver({
showButtons: ["next", "previous" /*, 'close'*/],
allowClose: false,
nextBtnText: "▶",
prevBtnText: "◀",
doneBtnText: "好耶" /*onCloseClick: () => {
driverObj.destroy();
},*/,
showProgress: true,
steps: [
{
element: ".beeSettingTag",
popover: {
title: "客製化設定",
description:
"在這裡可以進行詳細的個人設定,設定變更後需要【重新整理】頁面才會生效。",
},
},
{
element: picMode
? "#BH-master > form > div > table > tbody > tr > td.b-list__main > div > div > p"
: "#BH-master > form > div > table > tbody > tr > td.b-list__main > a",
popover: {
title: "即時瀏覽",
description:
"如果開啟「點擊時使用即時預覽」,文章標題的跳轉會以即時預覽的方式啟動。",
side: "bottom",
},
},
{
element:
"#BH-master > form > div > table > tbody > tr > td.b-list__main",
popover: {
title: "快速選取",
description:
"除了文章標題、縮圖模式的預覽圖,其他區域可以觸發快速選取。功能等同左方的勾選方塊。",
side: "bottom",
onNextClick: () => {
document.querySelector(
"#BH-master > form > div > table > tbody > tr:nth-child(2) > td:nth-child(3) > a.btn-icon.btn-icon--inverse.bee_preview"
).style.display = "inline-block";
document.querySelector(
"#BH-master > form > div > table > tbody > tr:nth-child(2) > td:nth-child(3) > a.btn-icon.btn-icon--inverse.bee_open_new_wd"
).style.display = "inline-block";
document.querySelector(
"#BH-master > form > div > table > tbody > tr:nth-child(2) > td:nth-child(3) > a.btn-icon.btn-icon--inverse.bee_link"
).style.display = "inline-block";
driverObj.moveNext();
},
},
},
{
element: picMode
? "#BH-master > form > div > table > tbody > tr:nth-child(2) > td:nth-child(3)"
: "#BH-master > form > div > table > tbody > tr:nth-child(2) > td:nth-child(3)",
popover: {
title: "功能按鈕",
description:
"如果開啟「插入功能按鈕」,指標指向的文章後方會出現三個功能按鈕,分別是「即時預覽」「新分頁開啟」「複製連結」。",
side: "bottom",
onNextClick: () => {
document.querySelector(
"#BH-master > form > div > table > tbody > tr:nth-child(2) > td:nth-child(3) > a.btn-icon.btn-icon--inverse.bee_preview"
).style.display = "none";
document.querySelector(
"#BH-master > form > div > table > tbody > tr:nth-child(2) > td:nth-child(3) > a.btn-icon.btn-icon--inverse.bee_open_new_wd"
).style.display = "none";
document.querySelector(
"#BH-master > form > div > table > tbody > tr:nth-child(2) > td:nth-child(3) > a.btn-icon.btn-icon--inverse.bee_link"
).style.display = "none";
document
.querySelector(
picMode
? "#BH-master > form > div > table > tbody > tr:nth-child(2) > td.b-list__main > div > a"
: "#BH-master > form > div > table > tbody > tr:nth-child(2) > td.b-list__main > a"
)
.click();
driverObj.moveNext();
},
},
},
{
element: "#BH-master > form > section:last-child > div",
popover: {
title: "功能選單",
description: "快速預覽視窗中,功能選單會漂浮在下方,方便使用!",
side: "bottom",
onPrevClick: () => {
document.querySelector("#BH-menu-path").click();
document.querySelector(
"#BH-master > form > div > table > tbody > tr:nth-child(2) > td:nth-child(3) > a.btn-icon.btn-icon--inverse.bee_preview"
).style.display = "inline-block";
document.querySelector(
"#BH-master > form > div > table > tbody > tr:nth-child(2) > td:nth-child(3) > a.btn-icon.btn-icon--inverse.bee_open_new_wd"
).style.display = "inline-block";
document.querySelector(
"#BH-master > form > div > table > tbody > tr:nth-child(2) > td:nth-child(3) > a.btn-icon.btn-icon--inverse.bee_link"
).style.display = "inline-block";
driverObj.movePrevious();
},
onNextClick: () => {
document.querySelector("#BH-menu-path").click();
driverObj.moveNext();
},
},
},
],
});
driverObj.drive();
};
document.head.appendChild(script);
}
function loadTips_home() {
if (!window.location.href.includes("www.gamer.com.tw")) {
return;
}
let link = document.createElement("link");
link.rel = "stylesheet";
link.href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/driver.css";
document.head.appendChild(link);
let script = document.createElement("script");
script.src =
"https://cdn.jsdelivr.net/npm/[email protected]/dist/driver.js.iife.js";
script.onload = function () {
const driver = window.driver.js.driver;
const driverObj = driver({
showButtons: ["next", "previous" /*, 'close'*/],
allowClose: false,
nextBtnText: "▶",
prevBtnText: "◀",
doneBtnText: "好耶",
showProgress: true,
steps: [
{
element: "#homeStyleSwitch",
popover: {
title: "滿版首頁",
description: "點此可以切換首頁排版。",
},
},
],
});
driverObj.drive();
};
document.head.appendChild(script);
}
function reportAlert() {
if (
!window.location.href.includes("forum.gamer.com.tw/B.php") ||
!localStorage.getItem("showAbuse") === "true"
) {
return;
}
let isReported =
document.querySelector(
"#BH-slave > div.BH-rbox.FM-rbox14 > div.FM-master-btn > a > span"
) !== null;
if (!isReported) {
return;
}
const urlParams = new URLSearchParams(window.location.search);
if (!urlParams.get("bsn")) {
console.log("[WARN] 有檢舉但抓取連結失敗");
return;
}
openInFrame(
"https:////forum.gamer.com.tw/gemadmin/accuse.php?bsn=" +
urlParams.get("bsn")
);
}
function addSkipFloor(postSections, postSection) {
// 找到 .c-post__body 元素 添加文章下方的按鈕
const postBody = postSection.querySelector(".c-post__body");
// 找到 .article-footer_right 區域
const footerLeft = postSection.querySelector(".c-section__side");
const footerRight = postBody.querySelector(".article-footer_right"); //.c-section__side
// 跳過按鈕
const skipFloorButtonLeft = document.createElement("a");
skipFloorButtonLeft.classList.add("article-footer_right-btn");
skipFloorButtonLeft.innerHTML =
'<i class="fa fa-archive" style="margin: 0 0.5rem 0 0.5rem;"></i><p>跳過此樓 ▼</p>';
skipFloorButtonLeft.id = `skip-${postBody.querySelector(".c-article").id}`; // 生成唯一 ID
skipFloorButtonLeft.style.display = "flex";
skipFloorButtonLeft.style.alignItems = "center";
skipFloorButtonLeft.style.margin = "1rem 0rem 0.5rem 0rem";
footerLeft.appendChild(skipFloorButtonLeft);
skipFloorButtonLeft.addEventListener("click", async () => {
//scroll to next postSection
let currentSectionIndex = Array.from(postSections).indexOf(postSection);
let nextSection = postSections[currentSectionIndex + 1];
if (nextSection) {
scrollIntoBee(nextSection);
} else {
scrollIntoBee(footerRight);
}
});
}
function addSummaryBtnLeft(postSection, returnSummaryBtn) {
// 找到 .c-post__body 元素 添加文章下方的按鈕
const postBody = postSection.querySelector(".c-post__body");
// 找到 .article-footer_right 區域
const footerLeft = postSection.querySelector(".c-section__side");
const footerRight = postBody.querySelector(".article-footer_right"); //.c-section__side
// 跳到懶人包按鈕
const lazySummaryButtonLeft = document.createElement("a");
lazySummaryButtonLeft.classList.add("article-footer_right-btn");
lazySummaryButtonLeft.innerHTML =
'<i class="fa fa-pencil" style="margin: 0 0.5rem 0 0.5rem;"></i><p>懶人包 ▼</p>';
lazySummaryButtonLeft.id = `jump-lazy-summary-${
postBody.querySelector(".c-article").id
}`; // 生成唯一 ID
lazySummaryButtonLeft.style.display = "flex";
lazySummaryButtonLeft.style.alignItems = "center";
lazySummaryButtonLeft.style.margin = "0rem 0rem 0.5rem 0rem";
footerLeft.appendChild(lazySummaryButtonLeft);
// 添加點擊事件監聽器
lazySummaryButtonLeft.addEventListener("click", async () => {
scrollIntoBee(footerRight);
if (lazySummaryButtonLeft.querySelector("p").textContent !== "懶人包 ▼") {
return;
}
//edit text
lazySummaryButtonLeft.querySelector("p").textContent = "已抵達";
returnSummaryBtn.click();
});
}
function addSummaryCmdBtn(postSection) {
// 找到 .c-post__body 元素 添加文章下方的按鈕
const postBody = postSection.querySelector(".c-post__body");
// 創建新的懶人包按鈕(留言)
const lazySummaryButtonCmd = document.createElement("a");
lazySummaryButtonCmd.classList.add("article-footer_right-btn");
lazySummaryButtonCmd.innerHTML =
'<i class="fa fa-pencil" style="margin: 0 0.5rem 0 0.5rem;"></i><p>留言統整</p>';
lazySummaryButtonCmd.id = `lazy-summaryCmd-${
postBody.querySelector(".c-article").id
}`; // 生成唯一 ID
lazySummaryButtonCmd.style.display = "flex";
lazySummaryButtonCmd.style.margin = "0.3rem 0.5rem 0rem 0rem";
// 將新的按鈕插入到 .article-footer_right 的開頭
//footerRight.insertBefore(lazySummaryButtonCmd, footerRight.firstChild);
postSection
.querySelector(".c-reply__head")
?.appendChild(lazySummaryButtonCmd);
// 添加點擊事件監聽器
lazySummaryButtonCmd.addEventListener("click", async () => {
scrollIntoBee(lazySummaryButtonCmd);
const postId = postBody.querySelector(".c-article").id.replace("cf", "");
// 檢查
if (lazySummaryButtonCmd.querySelector("p").textContent === "產生中...") {
return;
}
let oaikeyTemp = localStorage.getItem("oaiKey");
if (oaikeyTemp === "sk-yourKey" || oaikeyTemp === "") {
alert("請先設定 API Key 才能使用 AI 功能");
return;
}
if (
document.getElementById(`${postId}-cleanCmd`) &&
lazySummaryButtonCmd.querySelector("p").textContent === "摺疊 ▲"
) {
//將本原建設為不可見 並將摺疊 ▲ 改為 展開 ▼
popElement(document.getElementById(`${postId}-cleanCmd`), "toggle");
lazySummaryButtonCmd.querySelector("p").textContent = "展開 ▼";
return;
}
if (
document.getElementById(`${postId}-cleanCmd`) &&
lazySummaryButtonCmd.querySelector("p").textContent === "展開 ▼"
) {
popElement(document.getElementById(`${postId}-cleanCmd`), "toggle");
lazySummaryButtonCmd.querySelector("p").textContent = "摺疊 ▲";
return;
}
if (
document.getElementById(`${postId}-cleanCmd`) &&
lazySummaryButtonCmd.querySelector("p").textContent !== "留言統整"
) {
return;
}
lazySummaryButtonCmd.querySelector("p").textContent = "產生中...";
getCmdById(postId).then(async (cmdData) => {
// 構建 GPT prompt
//如果空,則從settings陣列裡面oaiPromptCmd取得default
const custom_oaiPromptCmd = localStorage.getItem("custom_oaiPromptCmd");
const prompt =
!custom_oaiPromptCmd || custom_oaiPromptCmd === ""
? localStorage.getItem("oaiPromptCmd")
: custom_oaiPromptCmd;
postGpt(prompt, "對話內容:\n ```" + cmdData.textContent + "\n```").then(
async ({ response, data }) => {
if (!response) {
lazySummaryButtonCmd.querySelector("p").textContent = "留言統整";
return;
}
//textContent 比對使用者並還原URL
const gptReply = restoreOriginalFormat(
data.choices[0].message.content,
cmdData.textContentOrigin
);
//debug mode
//const gptReply = restoreOriginalFormat(textContent, textContentOrigin);
// 創建新的 .c-article 元素
const newArticle = document.createElement("article");
newArticle.classList.add("c-reply__item", "c-article", "FM-P2");
newArticle.id = `${postId}-cleanCmd`;
newArticle.style.display = "block";
newArticle.style.overflow = "hidden";
newArticle.style.maxHeight = "auto";
newArticle.style.minHeight = "0px";
// 創建新的 .c-article__content 元素並插入文本
const newContent = document.createElement("div");
newContent.classList.add("c-article__content");
newContent.style.whiteSpace = "pre-wrap"; // 添加這行來顯示換行符號
//newContent.textContent = gptReply;
newContent.innerHTML = gptReply; // 使用 innerHTML 來插入包含 HTML 語法的內容
newArticle.appendChild(newContent);
// 將新的 .c-article 插入
lazySummaryButtonCmd.querySelector("p").textContent = "摺疊 ▲";
//postBody.appendChild(newArticle);
postSection
.querySelector(".c-post__footer")
.insertBefore(
newArticle,
document.getElementById(`Commendlist_${postId}`)
);
await popElementInit(newArticle, true, "ud", true);
}
);
});
});
}
function addSummaryBtn(postSection) {
// 找到 .c-post__body 元素 添加文章下方的按鈕
const postBody = postSection.querySelector(".c-post__body");
// 找到 .article-footer_right 區域
const footerRight = postBody.querySelector(".article-footer_right"); //.c-section__side
// 創建新的懶人包按鈕
const lazySummaryButton = document.createElement("a");
lazySummaryButton.classList.add("article-footer_right-btn");
lazySummaryButton.innerHTML =
'<i class="fa fa-pencil" style="margin: 0 0.5rem 0 0.5rem;"></i><p>懶人包</p>';
lazySummaryButton.id = `lazy-summary-${
postBody.querySelector(".c-article").id
}`; // 生成唯一 ID
// 將新的按鈕插入到 .article-footer_right 的開頭
footerRight.insertBefore(lazySummaryButton, footerRight.firstChild);
// 添加點擊事件監聽器
lazySummaryButton.addEventListener("click", async () => {
scrollIntoBee(lazySummaryButton);
// 檢查
if (lazySummaryButton.querySelector("p").textContent === "產生中...") {
return;
}
let oaikeyTemp = localStorage.getItem("oaiKey");
if (oaikeyTemp === "sk-yourKey" || oaikeyTemp === "") {
alert("請先設定 API Key 才能使用 AI 功能");
return;
}
if (
document.getElementById(
`${postBody.querySelector(".c-article").id}-clean`
) &&
lazySummaryButton.querySelector("p").textContent === "摺疊 ▲"
) {
//將本原建設為不可見 並將摺疊 ▲ 改為 展開 ▼
//v1
//document.getElementById(`${postBody.querySelector('.c-article').id}-clean`).style.display = 'none';
//v2
//document.getElementById(`${postBody.querySelector('.c-article').id}-clean`).style.maxHeight = '0px';
popElement(
document.getElementById(
`${postBody.querySelector(".c-article").id}-clean`
),
"toggle"
);
lazySummaryButton.querySelector("p").textContent = "展開 ▼";
return;
}
if (
document.getElementById(
`${postBody.querySelector(".c-article").id}-clean`
) &&
lazySummaryButton.querySelector("p").textContent === "展開 ▼"
) {
//v1
//document.getElementById(`${postBody.querySelector('.c-article').id}-clean`).style.display = 'block';
//v2
//document.getElementById(`${postBody.querySelector('.c-article').id}-clean`).style.maxHeight = document.getElementById(`${postBody.querySelector('.c-article').id}-clean`).style.readHeight;
popElement(
document.getElementById(
`${postBody.querySelector(".c-article").id}-clean`
),
"toggle"
);
lazySummaryButton.querySelector("p").textContent = "摺疊 ▲";
return;
}
if (
document.getElementById(
`${postBody.querySelector(".c-article").id}-clean`
) &&
lazySummaryButton.querySelector("p").textContent !== "懶人包"
) {
return;
}
lazySummaryButton.querySelector("p").textContent = "產生中...";
// 找到 .c-article__content 元素
const articleContent = postBody.querySelector(".c-article__content");
// 獲取所有子節點的文本內容,並合併成一個字符串
let textContent = "";
articleContent.childNodes.forEach((node) => {
textContent += node.textContent.trim() + "\n";
});
// 去除多餘的換行
textContent = textContent.replace(/\n+/g, "\n");
// 構建 GPT prompt
const custom_oaiPrompt = localStorage.getItem("custom_oaiPrompt");
const prompt =
!custom_oaiPrompt || custom_oaiPrompt === ""
? localStorage.getItem("oaiPrompt")
: custom_oaiPrompt;
postGpt(prompt, "文章內容:```" + textContent + "```").then(
async ({ response, data }) => {
if (!response) {
lazySummaryButton.querySelector("p").textContent = "懶人包";
return;
}
// 創建新的 .c-article 元素
const newArticle = document.createElement("article");
newArticle.classList.add("c-article", "FM-P2");
newArticle.id = `${postBody.querySelector(".c-article").id}-clean`;
newArticle.style.display = "block";
newArticle.style.overflow = "hidden";
newArticle.style.maxHeight = "auto";
newArticle.style.minHeight = "0px";
// 創建新的 .c-article__content 元素並插入文本
const newContent = document.createElement("div");
newContent.classList.add("c-article__content");
newContent.style.whiteSpace = "pre-wrap"; // 添加這行來顯示換行符號
//newContent.textContent = data.choices[0].message.content;
newContent.innerHTML = data.choices[0].message.content; // 使用 innerHTML 來插入包含 HTML 語法的內容
newArticle.appendChild(newContent);
// 將新的 .c-article 插入
lazySummaryButton.querySelector("p").textContent = "摺疊 ▲";
postBody.appendChild(newArticle);
await popElementInit(newArticle);
}
);
});
return lazySummaryButton;
}
function addAskBtn(postSection) {
// 找到 .c-post__body 元素 添加文章下方的按鈕
const postBody = postSection.querySelector(".c-post__body");
// 找到 .article-footer_right 區域
const footerRight = postBody.querySelector(".article-footer_right"); //.c-section__side
// 創建對話框
// <div class="c-reply__editor">
// <div class="reply-input">
// <textarea class="content-edit" placeholder="詢問⋯"></textarea>
// </div></div>
const askInput = document.createElement("div");
askInput.classList.add("c-reply__editor");
const replyInput = document.createElement("div");
replyInput.classList.add("reply-input");
const askTextarea = document.createElement("textarea");
askTextarea.classList.add("content-edit");
askTextarea.placeholder = "詢問⋯";
replyInput.appendChild(askTextarea);
askInput.appendChild(replyInput);
postBody.appendChild(askInput);
popElementInit(askInput, false, "ud", true).then((r) => {});
//create chat area
const chatArea = document.createElement("div");
chatArea.classList.add("chatArea");
chatArea.style.overflow = "hidden";
postBody.insertBefore(chatArea, askInput);
popElementInit(chatArea, false, "ud", false).then((r) => {});
// while user press Enter
askTextarea.addEventListener("keydown", async (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
// 檢查
let oaikeyTemp = localStorage.getItem("oaiKey");
if (oaikeyTemp === "sk-yourKey" || oaikeyTemp === "") {
alert("請先設定 API Key 才能使用 AI 功能");
return;
}
if (askTextarea.placeholder !== "詢問⋯") {
return;
}
let gptArray = [];
// 找到 .c-article__content 元素
const articleContent = postBody.querySelector(".c-article__content");
// 獲取所有子節點的文本內容,並合併成一個字符串
let textContent = "";
articleContent.childNodes.forEach((node) => {
textContent += node.textContent.trim() + "\n";
});
// 去除多餘的換行
textContent = textContent.replace(/\n+/g, "\n");
// 構建 GPT prompt
const custom_oaiPromptChat = localStorage.getItem("custom_oaiPromptChat");
const prompt =
!custom_oaiPromptChat || custom_oaiPromptChat === ""
? localStorage.getItem("oaiPromptChat")
: custom_oaiPromptChat;
const tempSystemMode = localStorage.getItem("oaiPromptSystemMode");
gptArray.push({
role:
tempSystemMode === "true"
? "system"
: "user",
content: prompt,
});
if (tempSystemMode !== "true") {
gptArray.push({
role: "system",
content: "好的,請提供文章。",
});
}
gptArray.push({
role: "user",
content: "文章內容:\n```\n" + textContent + "\n```",
});
// 取得對話紀錄(user-ask + gpt-reply)
const chatHistory = postBody.querySelectorAll(".chatHistory");
if (chatHistory) {
for (let i = 0; i < chatHistory.length; i++) {
let chat = chatHistory[i];
let role = chat.classList.contains("user-ask") ? "user" : "assistant";
gptArray.push({
role: role,
content: chat.querySelector(".c-article__content").textContent,
});
}
}
// add new chat
gptArray.push({
role: "user",
content: askTextarea.value,
});
const tempUserInput = askTextarea.value;
askTextarea.placeholder = "載入中⋯";
askTextarea.value = "";
postGptArray(gptArray).then(async ({ response, data }) => {
if (!response) {
askTextarea.placeholder = "詢問⋯";
askTextarea.value = tempUserInput;
return;
}
// create user input to article
const userArticle = document.createElement("article");
userArticle.classList.add(
"c-article",
"FM-P2",
"chatHistory",
"user-ask"
);
userArticle.id = `${
postBody.querySelector(".c-article").id
}-ask-${Date.now()}`;
userArticle.style.display = "block";
userArticle.style.minHeight = "0px";
userArticle.style.marginBottom = "0.8rem";
//add border bottom
userArticle.style.borderBottom = "1px solid var(--primary-text)";
const userContent = document.createElement("div");
userContent.classList.add("c-article__content");
userContent.style.whiteSpace = "pre-wrap";
userContent.innerHTML = tempUserInput;
userArticle.appendChild(userContent);
//postBody.insertBefore(userArticle, askInput);
chatArea.appendChild(userArticle);
//await popElementInit(userArticle, true, "ud", true);
askTextarea.value = "";
// 創建新的 .c-article 元素
const newArticle = document.createElement("article");
newArticle.classList.add(
"c-article",
"FM-P2",
"chatHistory",
"gpt-reply"
);
newArticle.id = `${
postBody.querySelector(".c-article").id
}-reply-${Date.now()}`;
newArticle.style.display = "block";
newArticle.style.minHeight = "0px";
newArticle.style.marginBottom = "1.6rem";
newArticle.style.borderBottom = "1px solid var(--primary)";
const newContent = document.createElement("div");
newContent.classList.add("c-article__content");
newContent.style.whiteSpace = "pre-wrap";
newContent.innerHTML = data.choices[0].message.content;
newArticle.appendChild(newContent);
askTextarea.placeholder = "詢問⋯";
//postBody.insertBefore(newArticle, askInput);
chatArea.appendChild(newArticle);
//await popElementInit(newArticle, true, "ud", true);
// set chatArea readHeight
requestAnimationFrame(() => {
chatArea.readHeight = `${chatArea.scrollHeight}px`;
chatArea.style.maxHeight = `${chatArea.scrollHeight}px`;
});
//focus to input
askTextarea.focus();
});
}
});
// 問問按鈕
const askButton = document.createElement("a");
askButton.classList.add("article-footer_right-btn");
askButton.innerHTML =
'<i style="margin: 0 0.5rem 0 0.5rem;" class="fa fa-comment-o"></i><p>問問 ▼</p>';
askButton.id = `ask-${postBody.querySelector(".c-article").id}`; // 生成唯一 ID
// 將新的按鈕插入到 .article-footer_right 的開頭
footerRight.insertBefore(askButton, footerRight.firstChild);
// 添加點擊事件監聽器
askButton.addEventListener("click", async () => {
scrollIntoBee(askButton);
// 檢查
let oaikeyTemp = localStorage.getItem("oaiKey");
if (oaikeyTemp === "sk-yourKey" || oaikeyTemp === "") {
alert("請先設定 API Key 才能使用 AI 功能");
return;
}
if (askButton.querySelector("p").textContent === "問問 ▲") {
//將本原建設為不可見 並將摺疊 ▲ 改為 展開 ▼
popElement(askInput, "toggle");
/*document.querySelectorAll('.chatHistory').forEach((chat) => {
popElement(chat, "toggle");
});*/
chatArea.style.readHeight =
chatArea.scrollHeight === 0 ? `999px` : `${chatArea.scrollHeight}px`;
chatArea.style.readWidth =
chatArea.scrollWidth === 0 ? `999px` : `${chatArea.scrollWidth}px`;
popElement(chatArea, "toggle");
askButton.querySelector("p").textContent = "問問 ▼";
return;
}
if (askButton.querySelector("p").textContent === "問問 ▼") {
popElement(askInput, "toggle");
/*document.querySelectorAll('.chatHistory').forEach((chat) => {
popElement(chat, "toggle");
});*/
popElement(chatArea, "toggle");
askButton.querySelector("p").textContent = "問問 ▲";
//focus to input
askTextarea.focus();
}
});
}
async function postGpt(promptSystem, promptUser) {
return new Promise((resolve) => {
let oaikeyTemp = localStorage.getItem("oaiKey");
GM_xmlhttpRequest({
method: "POST",
url: localStorage.getItem("oaiBaseUrl"),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${oaikeyTemp}`,
},
data: JSON.stringify({
messages: [
{
role:
localStorage.getItem("oaiPromptSystemMode") === "true"
? "system"
: "user",
content: promptSystem,
},
{
role: "user",
content: promptUser,
},
],
max_tokens: 4090,
model: localStorage.getItem("oaiModel"),
stream: false,
temperature: 0.7,
presence_penalty: 0,
frequency_penalty: 0,
}),
timeout: 30000,
onload: function (response) {
try {
if (response.status !== 200) {
console.error(`伺服器回應錯誤: ${response.status}`);
alert("取得 GPT 回覆時發生錯誤,請稍後再試。");
resolve({ response: false, data: null });
return;
}
const data = JSON.parse(response.responseText);
if (data?.choices?.[0]?.message?.content) {
resolve({ response: true, data: data });
} else {
console.error("API 返回的數據格式不正確");
alert("取得 GPT 回覆時發生錯誤,請稍後再試。");
resolve({ response: false, data: null });
}
} catch (error) {
console.error("取得 GPT 回覆時發生錯誤:", error);
alert("取得 GPT 回覆時發生錯誤,請稍後再試。");
resolve({ response: false, data: null });
}
},
onerror: function (error) {
console.error("取得 GPT 回覆時發生錯誤:", error);
alert("取得 GPT 回覆時發生錯誤,請稍後再試。");
resolve({ response: false, data: null });
},
ontimeout: function () {
console.error("取得 GPT 回覆超時");
alert("取得 GPT 回覆時發生錯誤,請稍後再試。");
resolve({ response: false, data: null });
},
});
});
}
async function postGptArray(gptArray) {
return new Promise((resolve) => {
let oaikeyTemp = localStorage.getItem("oaiKey");
GM_xmlhttpRequest({
method: "POST",
url: localStorage.getItem("oaiBaseUrl"),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${oaikeyTemp}`,
},
data: JSON.stringify({
messages: gptArray,
max_tokens: 4090,
model: localStorage.getItem("oaiModel"),
stream: false,
temperature: 0.7,
presence_penalty: 0,
frequency_penalty: 0,
}),
timeout: 30000,
onload: function (response) {
try {
if (response.status !== 200) {
console.error(`伺服器回應錯誤: ${response.status}`);
alert("取得 GPT 回覆時發生錯誤,請稍後再試。");
resolve({ response: false, data: null });
return;
}
const data = JSON.parse(response.responseText);
if (data?.choices?.[0]?.message?.content) {
resolve({ response: true, data: data });
} else {
console.error("API 返回的數據格式不正確");
alert("取得 GPT 回覆時發生錯誤,請稍後再試。");
resolve({ response: false, data: null });
}
} catch (error) {
console.error("取得 GPT 回覆時發生錯誤:", error);
alert("取得 GPT 回覆時發生錯誤,請稍後再試。");
resolve({ response: false, data: null });
}
},
onerror: function (error) {
console.error("取得 GPT 回覆時發生錯誤:", error);
alert("取得 GPT 回覆時發生錯誤,請稍後再試。");
resolve({ response: false, data: null });
},
ontimeout: function () {
console.error("取得 GPT 回覆超時");
alert("取得 GPT 回覆時發生錯誤,請稍後再試。");
resolve({ response: false, data: null });
},
});
});
}
async function postAddBtn() {
// check if oaiUpdateDate is more than today (format: yyyymmdd)
await getPrompt();
// 尋找所有 .c-post__body 元素
const postSections = Array.from(
document.querySelectorAll(".c-section")
).filter((postSection) => postSection.querySelector(".c-post__body"));
postSections.forEach((postSection) => {
addAskBtn(postSection);
const returnSummaryBtn = addSummaryBtn(postSection);
addSummaryCmdBtn(postSection);
addSkipFloor(postSections, postSection);
addSummaryBtnLeft(postSection, returnSummaryBtn);
});
}
async function getCmdById(postId) {
// 找到 cmd 元素
let cmdContents = document
.getElementById(`Commendlist_${postId}`)
.querySelectorAll(".c-reply__item");
// 展開留言
const showCmd = document.getElementById(`showoldCommend_${postId}`);
if (
showCmd &&
(showCmd.style.display === "block" || showCmd.style.display === "")
) {
// 持續偵測 dom 直到留言展開
let cmdCount = cmdContents.length;
showCmd.click();
await new Promise((resolve) => {
const observer = new MutationObserver((mutations) => {
//console.log(document.getElementById(`Commendlist_${postId}`).querySelectorAll('.c-reply__item').length + '/' + cmdCount);
if (
document
.getElementById(`Commendlist_${postId}`)
.querySelectorAll(".c-reply__item").length >= cmdCount
) {
cmdContents = document
.getElementById(`Commendlist_${postId}`)
.querySelectorAll(".c-reply__item");
// 第二次 DOM 改變後的程式碼
document.getElementById(`closeCommend_${postId}`).click();
//console.log('s1');
observer.disconnect(); // 停止觀察
resolve();
}
});
observer.observe(document.getElementById(`Commendlist_${postId}`), {
childList: true,
subtree: true,
characterData: true,
});
});
//console.log('s2');
document.getElementById(`closeCommend_${postId}`).click();
}
//console.log('s3');
// 獲取全部 id跟留言內容 並結合成一個文本
// 文本格式 = "@" + id + ":" + 留言內容 + "\n"
let textContent = "";
let textContentOrigin = Array.from(cmdContents)
.map((node) => node.innerHTML)
.join("");
cmdContents.forEach((node) => {
textContent +=
"@" +
node.querySelector(".reply-content__user").innerHTML +
":" +
node.querySelector(".comment_content").innerHTML +
"\n";
});
// 去除多餘的換行
textContent = textContent.replace(/\n+/g, "\n");
// 轉換回應
const pattern =
/<a href="javascript:Forum\.C\.openCommentDialog\(\d+,\s*\d+,\d+\);">([^<]+)\((.*?)\)<\/a>/g;
const pattern2 =
/<a target="_blank" href="https:\/\/home\.gamer\.com\.tw\/[^"]+">([^<]+)<\/a>/g;
textContent = textContent.replace(pattern, (match, prefix, name) => {
return `回應@${name} => `;
});
textContent = textContent.replace(pattern2, (match, name) => {
return `回應@${name},`;
});
return { textContent: textContent, textContentOrigin: textContentOrigin };
}
async function getPrompt() {
let today = new Date();
let oaiPromptUpdateDate = localStorage.getItem("oaiPromptUpdateDate");
if (
today.toISOString().slice(0, 10).replace(/-/g, "") - oaiPromptUpdateDate <
localStorage.getItem("oaiPromptUpdateSleep")
) {
return null;
}
try {
const response = await fetch(localStorage.getItem("oaiPromptUpdateURL"));
if (!response.ok) {
console.error("[ERROR] fetching prompt:", response.status);
return null;
}
const data = await response.json();
localStorage.setItem(
"oaiPromptUpdateDate",
today.toISOString().slice(0, 10).replace(/-/g, "")
);
if (localStorage.getItem("oaiPromptDate") >= data.oaiPromptDate) {
return data;
}
localStorage.setItem("oaiPromptDate", data.oaiPromptDate);
localStorage.setItem("oaiPrompt", data.oaiPrompt);
localStorage.setItem("oaiPromptUpdateSleep", data.oaiPromptUpdateSleep);
localStorage.setItem("oaiPromptCmd", data.oaiPromptCmd);
return data;
} catch (error) {
console.error("[ERROR] fetching prompt:", error);
return null;
}
}
function openInFrame(url) {
let iframe = document.getElementById("bee_frame");
iframe.src = url;
let BHMenuPath = document.querySelector("#BH-menu-path");
BHMenuPath.style.height = "100%";
BHMenuPath.style.opacity = "0.6";
setTimeout(() => {
/*document.querySelector('.bee_preview_wd').style.transform = 'translateX(0%) scaleX(' + 1 + ')';
document.querySelector('.bee_preview_wd').style.opacity = '1';*/
popElement(
document.querySelector(".bee_preview_wd"),
"true",
localStorage.getItem("preview_LR") === "true" ? "rl" : "lr"
);
}, 1000);
//wait 1 sec
setTimeout(() => {
let iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
let styleSheet = iframeDoc.createElement("style");
iframeDoc.head.appendChild(styleSheet);
let sheet = styleSheet.sheet;
sheet.insertRule(
".managertools { position: fixed; bottom: 0; right: 0; z-index: 100; }",
0
);
}, 1000);
}
async function popElementInit(
element,
show = true,
anime = "ud",
waitAppend = true
) {
if (waitAppend) {
requestAnimationFrame(() => {
element.style.readHeight =
element.scrollHeight === 0 ? `999px` : `${element.scrollHeight}px`;
element.style.readWidth =
element.scrollWidth === 0 ? `999px` : `${element.scrollWidth}px`;
//console.log('after frame',element.style.readHeight, '/', element.style.readWidth);
});
} else {
element.style.readHeight =
element.scrollHeight === 0 ? `999px` : `${element.scrollHeight}px`;
element.style.readWidth =
element.scrollWidth === 0 ? `999px` : `${element.scrollWidth}px`;
//console.log('without wait',element.style.readHeight, '/', element.style.readWidth);
}
element.style.transition = "";
element.style.overflow = "hidden";
element.style.opacity = "0";
popElement(element, "false", anime);
element.style.transition = "all 0.5s cubic-bezier(0.21, 0.3, 0.18, 1.37) 0s";
if (!show) {
element.style.beeShow = "false";
return;
}
element.style.beeShow = "true";
element.style.opacity = "1";
requestAnimationFrame(() => {
///console.log(element.scrollWidth, '///', element.scrollHeight, '///', element.style.readWidth, '///', element.style.readHeight);
element.style.maxHeight = element.style.readHeight;
});
}
function popElement(element, show = "true", anime = "ud") {
let doShow;
if (show === "toggle") {
doShow = !(element.style.beeShow === "true");
} else {
doShow = show === "true";
}
if (doShow) {
element.style.opacity = "1";
element.style.maxHeight = element.style.readHeight;
element.style.maxWidth = element.style.readWidth;
element.style.transform = "translateX(0px) translateY(0px)";
element.style.beeShow = "true";
return;
}
element.style.beeShow = "false";
element.style.opacity = "0";
//element.style.readHeight = element.scrollHeight === 0 ? `999px` : `${element.scrollHeight}px`;
//element.style.readWidth = element.scrollWidth === 0 ? `999px` : `${element.scrollWidth}px`;
if (anime.includes("u")) {
element.style.opacity = "0";
element.style.maxHeight = "0px";
if (anime.startsWith("d")) {
element.style.transform = `translateX(0px) translateY(${element.style.readWidth}px)`;
//move as same as how it was
/*element.style.marginTop = `${parseInt(element.style.marginTop.replace("px", ""))
+ parseInt(element.style.readHeight.replace("px", ""))}px`;*/
}
}
if (anime.includes("l")) {
element.style.opacity = "0";
element.style.maxWidth = "0px";
if (anime.startsWith("r")) {
element.style.transform = `translateX(${element.style.readHeight}) translateY(0px)`;
//move as same as how it was
/*element.style.marginLeft = `${parseInt(element.style.marginLeft.replace("px", ""))
+ parseInt(element.style.readWidth.replace("px", ""))}px`;*/
}
}
}
function scrollIntoBee(element, marginOffset = 7) {
const temp_marginTop =
element.style.marginTop === undefined || ""
? "0px"
: element.style.marginTop;
element.style.marginTop = `-${marginOffset}rem`;
element.scrollIntoView({ behavior: "smooth" });
element.style.marginTop = temp_marginTop;
//check after 0.3 sec, if not inInViewport, recall
setTimeout(() => {
if (!isInViewport(element)) {
scrollIntoBee(element, marginOffset);
}
}, 300);
}
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.top <= (window.innerHeight || document.documentElement.clientHeight)
//rect.left >= 0 &&
//rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
//rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
function restoreOriginalFormat(textContent, cmdContents) {
// 儲存所有在原始文本中找到的名字和對應的HTML
const nameToHtmlMap = new Map();
// 從原始文本(cmdContents)中提取所有HTML格式及對應的用戶名
const htmlPattern =
/<a class="reply-content__user" href="\/\/home\.gamer\.com\.tw\/[^"]+" target="_blank">([^<]+)<\/a>/g;
let match;
while ((match = htmlPattern.exec(cmdContents)) !== null) {
nameToHtmlMap.set(match[1], match[0]);
}
// 在 textContent 中尋找並替換所有 @id
let processedText = textContent;
nameToHtmlMap.forEach((html, name) => {
/*const atPattern = new RegExp(`@${name}`, 'g');
processedText = processedText.replace(atPattern, html);*/
// 對特殊字符進行轉義
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const atPattern = new RegExp(`@${escapedName}`, "g");
processedText = processedText.replace(atPattern, html);
});
return processedText;
}