// ==UserScript==
// @name GM论坛勋章百宝箱
// @namespace http://tampermonkey.net/
// @version 1.25
// @description 默认关闭回收功能,徽章按类型排序,保存/还原徽章顺序,勋章一键续期,勋章收益统计功能
// @match https://www.gamemale.com/wodexunzhang-showxunzhang.html?action=my
// @grant GM_addStyle
// @license MIT
// ==/UserScript==
// TODO所有勋章一键续,剧情勋章一键续期
// 1.25版本测试自动更新情况
(function () {
'use strict';
// 徽章按类型排序和顺序调整
// 如果想改动默认顺序就改这里
const orderList = ['游戏男从', '真人男从', '女从', '装备', '资产', '宠物', '板块', '天赋', '赠礼', '咒术', '剧情', '其他']
let categoriesData = {}
// 从我的日志里,把最新的勋章分类数据拿过来
fetchBlogArticle().then(articleArray => {
categoriesData = articleArray;
// 此处直接复制粘贴代码不想思考了
let result = { "游戏男从(10)": "", "真人男从(8)": "", "女从(4)": "", "装备(11)": "", "资产(16)": "", "宠物(7)": "", "板块(4)": "", "天赋(4)": "", "赠礼": "", "咒术": "", "剧情": "", "其他": "" };
let categories = {
"youxi": "游戏男从(10)", "zhenren": "真人男从(8)", "Maid": "女从(4)",
"Equip": "装备(11)", "Asset": "资产(16)", "Pet": "宠物(7)",
"Forum": "板块(4)", "Skill": "天赋(4)", "Gift": "赠礼",
"Spell": "咒术", "Plot": "剧情"
};
let myblok = document.getElementsByClassName("myblok");
for (let blok of myblok) {
let regex = /alt="(.+?)"/;
let matches = blok.innerHTML.match(regex)
if (matches) {
let match = matches[1];
let found = false;
for (let key in categories) {
if (categoriesData[key].indexOf(match) >= 0) {
result[categories[key]] += match + ",";
found = true;
break;
}
}
if (!found) {
result["其他"] += match + ",";
}
}
}
let txt = ""
for (let key in result) {
txt += key + " : (" + (result[key].split(",").length - 1) + ") " + result[key].slice(0, -1) + "<br>"
}
function qiwang(pattern) {
let myblok = document.getElementsByClassName("myblok")
let result = { "金币": 0, "血液": 0, "咒术": 0, "知识": 0, "旅程": 0, "堕落": 0, "灵魂": 0 };
for (let blok of myblok) {
if (blok.innerText.indexOf("已寄售") > 0) {
continue
}
let regex = /几率 (\d+)%/i;
let matches = blok.innerText.match(regex)
// let nameRegex=/\t(.+?)\n/;
// let nameMatch=blok.innerText.replaceAll(" ","").match(nameRegex)
if (matches) {
let prob = matches[1]
let symbols = Array.from(blok.innerText.matchAll(pattern), m => m[2]);
let isSame = symbols.every(function (element) {
return element === symbols[0];
});
// console.log(nameMatch[1]+isSame)
matches = blok.innerText.matchAll(pattern);
for (let match of matches) {
let score = prob / 100 * parseInt(match[2] + match[3])
result[match[1]] = Number((result[match[1]] + score).toFixed(4));
}
}
}
return result
}
function getCoin() {
let coin = 0;
let myblok = document.getElementsByClassName("myblok")
for (let blok of myblok) {
let regex = /金币\s+(\d+)寄售/i;
let matches = blok.innerText.match(regex)
if (matches) {
coin += parseInt(matches[1])
}
}
return coin
}
function showValid() {
let myblok = document.getElementsByClassName("myblok")
for (let blok of myblok) {
let regex = /\s+(.+?分)\d{1,2}秒有效期/i;
let matches = blok.innerText.match(regex)
if (matches) {
let newP = document.createElement("p");
let newContent = document.createTextNode(matches[1]);
newP.appendChild(newContent);
blok.firstElementChild.appendChild(newP)
}
}
}
let huiPattern = /回帖\s+(.+?) ([+-])(\d+)/gi
let huiResult = qiwang(huiPattern)
let hui = "回帖期望 "
for (let key in huiResult) {
hui += key + ":" + huiResult[key] + " "
}
let faPattern = /发帖\s+(.+?) ([+-])(\d+)/gi
let faResult = qiwang(faPattern)
let fa = "发帖期望 "
for (let key in faResult) {
fa += key + ":" + faResult[key] + " "
}
let coin = "寄售最大价格总和:" + getCoin()
// document.head.innerHTML += '<style>.myfldiv {display:flex;flex-wrap:wrap;align-items:flex-start;}</style>';
// 找到元素并更新其内容
var badgeOrderElement = document.querySelector(".badge-order");
if (badgeOrderElement) {
badgeOrderElement.innerHTML = "<p>" + hui + "<br>" + fa + "<br>" + coin + "<br><br>" + txt +"</p>";
}
showValid()
})
const linkList = { "游戏男从": "youxi", "真人男从": "zhenren", "女从": "Maid", "装备": "Equip", "资产": "Asset", "宠物": "Pet", "板块": "Forum", "天赋": "Skill", "赠礼": "Gift", "咒术": "Spell", "剧情": "Plot", "其他": "other" }
// 创建一个新的div元素用于管理徽章
const badgeManagerDiv = document.createElement('div');
badgeManagerDiv.className = 'badge-manager';
badgeManagerDiv.innerHTML = '<h2>徽章管理</h2><p>这里可以管理您的徽章。</p><div class="badge-manager-button"><div>';
const badgeOrderDiv = document.createElement('div');
badgeOrderDiv.className = 'badge-order';
badgeOrderDiv.innerHTML = '正在计算您拥有的徽章类型和价值,请稍等。。。'
// 获取目标div并在其前面插入新创建的div
const targetDiv = document.querySelector('.my_fenlei');
targetDiv.parentNode.insertBefore(badgeManagerDiv, targetDiv);
badgeManagerDiv.appendChild(badgeOrderDiv)
// targetDiv.parentNode.insertBefore(badgeOrderDiv, badgeManagerDiv);
// 在这里添加您的自定义样式
const customStyles = `
.badge-manager {
background-color: #f0f0f0; /* 背景颜色 */
padding: 10px; /* 内边距 */
margin-bottom: 10px; /* 底部外边距 */
border: 1px solid #ccc; /* 边框 */
font-family: Arial, sans-serif; /* 字体 */
color: #333; /* 字体颜色 */
}
.badge-manager h2 {
margin: 0; /* 去掉默认的外边距 */
font-size: 18px; /* 标题字体大小 */
color: #007BFF; /* 标题颜色 */
}
.badge-manager p {
margin: 5px 0;
}
.custom-button {
padding: 5px 10px;
margin-right: 5px;
background-color: #007BFF; /* 按钮背景颜色 */
color: white; /* 字体颜色 */
border: none; /* 去掉默认边框 */
border-radius: 5px; /* 圆角 */
cursor: pointer; /* 鼠标悬停时显示手型 */
}
.custom-button:hover {
background-color: #0056b3; /* 悬停时的背景颜色 */
}
`;
// 使用GM_addStyle函数将样式插入到页面中
GM_addStyle(customStyles);
// 添加功能按钮
function createLink(label, onClickMethod) {
const button = document.createElement('button');
button.className = 'custom-button';
button.textContent = label;
button.onclick = (event) => {
event.preventDefault(); // 阻止默认行为
onClickMethod(); // 调用自定义方法
};
// 将链接添加到页面的 body 中
const my_biaoti = document.querySelector('.badge-manager-button')
my_biaoti.appendChild(button);
}
// 默认关闭回收功能
// @params hide none是隐藏 block是显示
createLink('显示/隐藏回收按钮', setHuiShou)
setHuiShou('init')
function setHuiShou(init) {
document.querySelectorAll('button.pn').forEach(element => {
if (element.innerText == '回收') {
// 初始化干掉
if (init) {
element.style.display = "none";
element.parentElement.style.display = "none";
} else {
// 检查元素的display属性
if (element.style.display === "none" || getComputedStyle(element).display === "none") {
// 如果是none,则显示元素和其父元素
element.style.display = "inline";
element.parentElement.style.display = "inline"; // 显示父元素
// alert('回收按钮已显示')
} else {
// 否则隐藏元素和其父元素
element.style.display = "none";
element.parentElement.style.display = "none"; // 隐藏父元素
// alert('回收按钮已隐藏')
}
}
}
})
}
// 勋章排序
createLink('按照类型排序', kindOrder)
function kindOrder() {
// 获取所有匹配的元素
const elements = document.querySelectorAll('.my_fenlei .myblok');
const elementsArray = Array.from(elements);
// 使用 map 函数处理每个元素
const xunzhangList = elementsArray.map(myBlock => {
const key = myBlock.getAttribute('key');
const nameElement = myBlock.querySelector('p b'); // 找到包含名称的 <b> 标签
const name = nameElement ? nameElement.textContent : '';
return { [name]: key }
})
// 使用 reduce 合并字典
const mergedDict = xunzhangList.reduce((acc, curr) => {
return { ...acc, ...curr };
}, {});
// 填补未知的勋章
const mergedDictKey = Object.keys(mergedDict)
const allCategoriesData = Object.values(categoriesData).flat();
categoriesData.other = findUniqueValues(mergedDictKey, allCategoriesData)
function findUniqueValues(a, b) {
// 将数组 b 转换为一个 Set,以提高查找效率
const setB = new Set(b);
// 过滤出在 a 中且不在 b 中的值
const uniqueValues = a.filter(value => !setB.has(value));
return uniqueValues;
}
const previousInput = localStorage.getItem('sortInput') || orderList.join(' ');
// 弹出输入框,默认值为之前的内容
const userInput = prompt("您正在进行一键排序,是否需要修改排序顺序(用空格分隔):", previousInput);
// 如果用户输入了内容
if (userInput !== null) {
// 保存到 localStorage
localStorage.setItem('sortInput', userInput);
// 将输入的内容转换为数组并进行排序
const sortedArray = userInput.split(' ').map(item => item.trim());
const order1 = sortedArray.map(e => categoriesData[linkList[e]])
const order2 = [].concat(...order1);
const result = order2.map(key => mergedDict[key]).filter(value => value !== undefined);
postOrder(result)
// 输出排序后的结果
// alert("排序后的结果:\n" + sortedArray.join(', '));
}
}
//新增按钮保存/还原勋章顺序
createLink('保存勋章顺序', saveKeysOrder)
createLink('还原勋章顺序', loadKeysOrder)
// 保存勋章顺序
function saveArrayToLocalStorage(key, array) {
localStorage.setItem(key, JSON.stringify(array));
}
// 从本地存储获取数组
function getArrayFromLocalStorage(key) {
const storedArray = localStorage.getItem(key);
return storedArray ? JSON.parse(storedArray) : null;
}
// 从本地存储删除数组
function removeArrayFromLocalStorage(key) {
localStorage.removeItem(key);
}
// 获取所有具有指定类名的div元素
function getKeysFromDivs(className) {
// 使用querySelectorAll获取所有带有该类的div
const divs = document.querySelectorAll(`div.${className}`);
// 提取每个div的key属性并返回数组
const keys = Array.from(divs).map(div => div.getAttribute('key'));
return keys.join(',');
}
// 保存勋章顺序
function saveKeysOrder() {
const keys = getKeysFromDivs('myblok')
saveArrayToLocalStorage('keyOrder', keys)
alert('保存成功')
}
function loadKeysOrder() {
const keys = getArrayFromLocalStorage('keyOrder')
postOrder(keys)
}
function postOrder(newOrder) {
const url = 'https://www.gamemale.com/plugin.php?id=wodexunzhang:showxunzhang'
// 创建FormData对象
const formData = new FormData();
const data = { newOrder, action: 'newOrder' }
// 将数据添加到formData
for (const key in data) {
if (data.hasOwnProperty(key)) {
formData.append(key, data[key]);
}
}
// 使用fetch发送POST请求
fetch(url, {
method: 'POST',
body: formData,
})
.then(response => {
// alert('还原勋章顺序成功,点击确认后刷新页面')
location.reload()
if (!response.ok) {
throw new Error('Network response was not ok');
}
// return response.json(); // 或根据需要返回其他格式
})
.then(data => {
console.log('Success:', data);
})
.catch(error => {
console.error('Error:', error);
});
}
// 勋章一键续期
// 疑似存在扣了咒术不会增加时长的bug,暂停开发。似乎是编号的问题,再观察一下
oneClickRenew()
function oneClickRenew() {
// 获取所有的按钮元素
const buttons = document.querySelectorAll('button.pn');
buttons.forEach(button => {
// 检查onclick属性是否包含'可续期'
if (button.innerText == '可续期') {
// 创建新的一键续期按钮
const newButton = document.createElement('button');
const userMedalid = button.getAttribute('onclick').match(/\d+/g)[0]
const titleElement = button.closest('.myimg').querySelector('p[title]');
const name = titleElement.getAttribute('title');
newButton.type = 'button';
newButton.className = 'pn';
newButton.innerHTML = '<em>一键续期</em>';
newButton.onclick = function () {
// 弹出提示框询问续期多少次
const times = prompt(`您正在为【${name}】一键续期,请输入续期次数:`, "1");
const count = parseInt(times);
// 判断输入是否合法
if (isNaN(count) || count <= 0) {
alert("请输入有效的次数!");
return;
}
repeatRequest(count, 5000, userMedalid);
};
// 创建一个<p>标签来包裹新按钮
const p = document.createElement('p');
p.appendChild(newButton);
// 将<p>标签插入到原按钮的父元素的父元素后面,并紧贴
button.parentNode.insertAdjacentElement('afterend', p);
}
});
function postRenew(userMedalid) {
if (!userMedalid) return
const url = 'https://www.gamemale.com/plugin.php?id=wodexunzhang:showxunzhang'
// 创建FormData对象
const formData = new FormData();
const formhash = document.querySelector('input[name="formhash"]').value
const data = { formhash, action: 'xuqi', jishoujiage: '', userMedalid }
// 将数据添加到formData
for (const key in data) {
if (data.hasOwnProperty(key)) {
formData.append(key, data[key]);
}
}
return fetch(url, { method: 'POST', body: formData, })
}
// 模拟网络请求的函数
async function makeRequest(userMedalid) {
try {
// 假设这是一个真实的 API URL
const response = await postRenew(userMedalid)
// if (!response.ok) {
// throw new Error('网络请求失败');
// }
const data = await response.text();
console.log("请求已发送:", data); // 打印响应数据
return data; // 返回请求结果
} catch (error) {
console.error('请求出错:', error);
throw error; // 抛出错误以供调用者处理
}
}
// 显示提示信息的函数
function showMessage(message) {
const messageDiv = document.createElement('div');
messageDiv.textContent = message;
messageDiv.style.position = 'fixed';
messageDiv.style.top = '10px';
messageDiv.style.right = '10px';
messageDiv.style.backgroundColor = '#4caf50'; // 绿色背景
messageDiv.style.color = 'white';
messageDiv.style.padding = '10px';
messageDiv.style.zIndex = '1000';
document.body.appendChild(messageDiv);
// 自动消失
setTimeout(() => {
document.body.removeChild(messageDiv);
}, 3000); // 3秒后消失
}
// 重复请求的函数
async function repeatRequest(times, interval, userMedalid) {
for (let i = 0; i < times; i++) {
try {
const result = await makeRequest(userMedalid); // 等待请求完成
showMessage(`共需${times}次,已经请求 ${i + 1} 次`);
} catch (error) {
showMessage(`请求 ${i + 1} 失败: ${error.message}`);
}
// 等待间隔
if (i < times - 1) {
await new Promise(resolve => setTimeout(resolve, interval)); // 等待间隔
}
}
showMessage('一键续期已完成,3秒后刷新页面');
setTimeout(() => {
location.reload()
}, 3000); // 3秒后消失
// console.log("所有请求已完成");
}
}
// 新增从日志里获取勋章分类的函数
async function fetchBlogArticle() {
const url = 'https://www.gamemale.com/home.php?mod=space&uid=723150&do=blog&quickforward=1&id=111238';
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('网络错误:' + response.status);
}
const html = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// 假设 blog_article 是一个 id 或者 class
const articleElement = doc.querySelector('#blog_article'); // 根据实际情况调整选择器
if (articleElement) {
const articleText = articleElement.textContent || articleElement.innerText;
// 尝试解析为 JSON
try {
const articleArray = JSON.parse(articleText);
return articleArray;
} catch (jsonError) {
console.error("无法解析为 JSON:", jsonError);
return []; // 返回空数组或处理错误的逻辑
}
} else {
console.log('未找到 blog_article 元素');
return []; // 返回空数组或处理未找到元素的逻辑
}
} catch (error) {
console.error('发生错误:', error);
return []; // 返回空数组或处理错误的逻辑
}
}
})();