// ==UserScript==
// @name 银河复读放置+1功能
// @namespace http://tampermonkey.net/
// @version 1.2.2
// @description 牛牛+1脚本,点击按钮填充文本将第75,76行取消注释可以加入汪汪队,拒绝猫娘从我做起
// @author Greenwaln, cy1zu
// @match https://www.milkywayidle.com/*
// @match https://test.milkywayidle.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com
// @grant none
// @license GPL-3.0
// ==/UserScript==
(function () {
'use strict';
// 这些下次一定
// const tabSelector = '.TabPanel_tabPanel__tXMJF';
// const tabHidden = '.TabPanel_hidden__26UM3';
const channelSelector = '.Chat_chatChannel__gQ-21';
const chatSelector = 'div.ChatHistory_chatHistory__1EiG3';
const messageSelector = '.ChatMessage_chatMessage__2wev4';
const inputSelector = 'input.Chat_chatInput__16dhX';
// 获取原生 setter,确保在 React 受控输入框上更新值有效
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
// 插入自定义样式
const style = document.createElement('style');
style.innerHTML = `
.tm-plus1-btn {
margin-left: 1ch;
width: 4ch;
padding: 0;
text-align: right;
background-color: rgba(0,0,0,0.001);
color: rgba(0,0,0,0.001);
border: none;
-webkit-user-select: none; /* Safari */
-ms-user-select: none; /* IE 10+ and Edge */
user-select: none; /* Standard syntax */
}
.tm-plus1-btn:hover {
margin-left: 1ch;
border-radius: 4px;
width: 4ch;
padding: 0;
border: none;
font-family: "Roboto";
text-align: center;
background-color: var(--color-primary);
color: var(--color-text-dark-mode);
cursor: pointer;
align-items: center;
-webkit-user-select: none; /* Safari */
-ms-user-select: none; /* IE 10+ and Edge */
user-select: none; /* Standard syntax */
}
`;
document.head.appendChild(style);
// 创建 +1 按钮
function createPlusOneButton(text) {
const btn = document.createElement('button');
btn.innerText = '+1';
btn.classList.add('tm-plus1-btn');
btn.addEventListener('click', (e) => {
e.stopPropagation();
const input = document.querySelector(inputSelector);
if (input) {
// text = text.replace(/喵/g, '汪');
// text += '汪';
nativeInputValueSetter.call(input, text);
input.dispatchEvent(new Event('input', { bubbles: true }));
}
});
return btn;
}
// 从消息节点中提取纯文本内容及其所在的元素(排除时间戳、用户名等)
function extractPureTextAndElement(msgElement) {
const textParts = [];
// 要排除的 class 列表
const excludedSelectors = [
'.ChatMessage_name__1W9tB',
'.ChatMessage_timestamp__1iRZO'
];
// 判断某个节点是否应该被排除(扩展了技能名称判断)
function isExcluded(node) {
return excludedSelectors.some(sel => node.closest(sel)) ||
(node.classList.contains('Skill_level__39kts') && node.closest('.Skill_skill__3MrMc')) ||
(node.classList.contains('Skill_name__1C_fL') && node.closest('.Skill_skill__3MrMc'));
}
// 遍历所有子元素
msgElement.querySelectorAll('*').forEach(el => {
if (isExcluded(el)) return;
if (el.textContent === ': ') return;
// 处理包含技能名称的特殊链接容器
if (el.classList.contains('Skill_skill__3MrMc')) {
const skillNameEl = el.querySelector('.Skill_name__1C_fL');
if (skillNameEl) {
const skillNameText = skillNameEl.textContent.trim();
// 新增:技能名称处理
let skillsMap = new Map([
["挤奶", "[milking]"],
["采摘","[foraging]"],
["伐木","[woodcutting]"],
["奶酪锻造","[cheesesmithing]"],
["制作","[crafting]"],
["缝纫","[tailoring]"],
["烹饪","[cooking]"],
["冲泡","[brewing]"],
["炼金","[alchemy]"],
["强化","[enhancing]"],
["耐力","[stamina]"],
["智力","[intelligence]"],
["攻击","[attack]"],
["力量","[power]"],
["防御","[defense]"],
["远程","[ranged]"],
["魔法","[magic]"],
]);
const processedText = skillsMap.get(skillNameText);
textParts.push(`[SKILL:${processedText}]`);
return; // 处理完成后跳过当前元素的后续处理
}
}
// 原有处理逻辑
if (el.tagName === 'A' && el.href) {
textParts.push(el.href);
} else if (el.childElementCount === 0) {
const text = el.textContent.trim();
if (text) textParts.push(text);
}
});
return {
text: textParts.join(' ').replace(/\s*\[SKILL:(.*?)\]\s*/g, '$1'), //空格处理
element: msgElement
};
}
// 为每条消息添加 +1 按钮
function addButtonsAll() {
const messages = document.querySelectorAll(messageSelector);
messages.forEach((msg) => {
if (!msg.dataset.hasPlusOne) {
const { text, element } = extractPureTextAndElement(msg);
if (text && element) {
if (!msg.querySelector('.tm-plus1-btn')) {
const btn = createPlusOneButton(text);
msg.appendChild(btn); // 插入整个消息节点的末尾
}
}
msg.dataset.hasPlusOne = 'true';
}
});
}
// 为传入dom添加 +1 按钮
function addButton(msg) {
if (!msg.dataset.hasPlusOne) {
const { text, element } = extractPureTextAndElement(msg);
if (text && element) {
if (!msg.querySelector('.tm-plus1-btn')) {
const btn = createPlusOneButton(text);
msg.appendChild(btn); // 插入整个消息节点的末尾
}
}
msg.dataset.hasPlusOne = 'true';
}
}
// 初始化
let chatLoaded = false;
function init() {
if (chatLoaded) {
clearInterval(checkInit);
return;
} else {
// 监听聊天区域的变化(针对 SPA 动态加载新消息)
const chatContainers = document.querySelectorAll(chatSelector);
// 这些下次一定
// const tabContainers = document.querySelectorAll(tabSelector);
if (chatContainers.length > 0) {
addButtonsAll();
chatLoaded = true;
console.log('+1 loaded');
const observer = new MutationObserver((mutations) => {
// 遍历所有变动记录
for (const mutation of mutations) {
// 3. 筛选新增的子节点(直接子节点)
for (const node of mutation.addedNodes) {
// 4. 确保节点是元素且符合条件(例如特定类名)
if (node.nodeType === 1) {
// 5. 挂载按钮
addButton(node);
}
}
}
});
//全频道监听
for (const chatContainer of chatContainers) {
observer.observe(chatContainer, { childList: true });
}
}
}
}
const checkInit = setInterval(init, 250);
// ----轮询已经滚出克了!
// setInterval(addButtonsAll, 500);
})();