Luogu Chat Better!
Forever_Rain · · 科技·工程
前言
在洛谷私信发一大段消息的时候,由于无法自动聚焦输入框,经常要点击,极大地影响了我们水谷的速度,所以做了这个插件(
目前有自动聚焦输入框、更改自己的消息颜色和私信图片显示功能。
源码
将代码放到油猴里就行了。
整个脚本最前端,有一块代码:
const COLOR_CONFIG = {
enableOwnMessageColor: true,// 是否启用自定义颜色
ownMessageBgColor: "#d9f0ff",// 自己消息框的背景色
ownMessageTextColor: "#000000" // 自己消息的文字颜色
};
你可以修改参数的值来实现不同的效果。
-
enableOwnMessageColor:是否启用(自己的)消息框颜色修改功能,true则启用;false则不启用。 -
ownMessageBgColor:自己消息框的背景色,十六进制格式。 -
ownMessageTextColor:自己消息的文字颜色,十六进制格式。
:::info[Luogu Chat Better!]{open}
// ==UserScript==
// @name Luogu Chat Better!
// @namespace http://tampermonkey.net/
// @version 3.1
// @description 洛谷私信 自动聚焦输入框 + 图片显示 + 颜色修改
// @author Forever_Rain
// @icon https://cdn.luogu.com.cn/upload/usericon/3.png
// @match https://www.luogu.com.cn/chat*
// @match https://www.luogu.com.cn/chat/*
// @grant none
// ==/UserScript==
/* 用户自定义颜色配置 */
const COLOR_CONFIG = {
enableOwnMessageColor: true,// 是否启用自定义颜色
ownMessageBgColor: "#d9f0ff",// 自己消息框的背景色
ownMessageTextColor: "#000000" // 自己消息的文字颜色
};
/* 用户自设部分 */
// 自动聚焦输入框
(function() {
'use strict';
const inputSelectors = [
'textarea.lg-message-input',
'textarea[name="content"]',
'.chat-input textarea',
'.message-editor textarea',
'div[contenteditable="true"]',
'textarea[placeholder*="私信"]',
'textarea[placeholder*="消息"]',
'textarea.lfe-form-sz-middle',
'.input-wrap textarea',
'#chat-input textarea'
];
const sendButtonSelectors = [
'button[type="submit"]',
'button.send-btn',
'button:has(span:contains("发送"))',
'.send-button',
'.chat-send-btn'
];
function getInputElement() {
for (const selector of inputSelectors) {
const element = document.querySelector(selector);
if (element && element.offsetParent !== null) { // 确保可见
return element;
}
}
const allTextareas = document.querySelectorAll('textarea');
for (const ta of allTextareas) {
if (ta.offsetParent !== null && (ta.placeholder?.includes('私信') || ta.placeholder?.includes('消息') || ta.closest('.chat-area'))) {
return ta;
}
}
const editableDivs = document.querySelectorAll('[contenteditable="true"]');
for (const div of editableDivs) {
if (div.offsetParent !== null && div.closest('.chat-area')) {
return div;
}
}
return null;
}
function focusInput(retries = 3) {// 重试:3次
const input = getInputElement();
if (input) {
input.focus();
if (input.isContentEditable) {
const range = document.createRange();
const sel = window.getSelection();
range.selectNodeContents(input);
range.collapse(false); // 折叠到末尾
sel.removeAllRanges();
sel.addRange(range);
}
} else if (retries > 0) {
setTimeout(() => focusInput(retries - 1), 100);// 间隔0.1s
}
}
function isSendButton(el) {
if (!el) return false;
const tag = el.tagName.toLowerCase();
if (tag === 'button' || tag === 'input' && (el.type === 'submit' || el.type === 'button')) {
const text = el.innerText || el.value || '';
if (text.includes('发送') || text.includes('Send')) return true;
if (el.classList && (el.classList.contains('send-btn') || el.classList.contains('chat-send-btn'))) return true;
if (el.getAttribute('aria-label') === '发送') return true;
}
if (el.querySelector && el.querySelector('svg[data-icon="paper-plane"], svg[data-icon="send"]')) return true;
return false;
}
function onDocumentClick(e) {
let target = e.target;
while (target && target !== document.body) {
if (isSendButton(target)) {
setTimeout(focusInput, 120);
break;
}
target = target.parentElement;
}
}
function onKeyDown(e) {
// 检测 Ctrl+Enter
if (e.ctrlKey && e.key === 'Enter') {
const activeEl = document.activeElement;
if (activeEl && (activeEl.tagName === 'TEXTAREA' || activeEl.isContentEditable)) {
setTimeout(focusInput, 100);
}
}
}
document.addEventListener('click', onDocumentClick, true);
document.addEventListener('keydown', onKeyDown);
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'childList' && mutation.addedNodes.length) {
const activeTag = document.activeElement?.tagName;
if (activeTag !== 'TEXTAREA' && !document.activeElement?.isContentEditable) {
const input = getInputElement();
if (input && document.hasFocus()) {
setTimeout(() => {
if (document.activeElement !== input) {
input.focus();
}
}, 200);
}
}
break;
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
// 页面初始加载后,如果已有输入框且未聚焦,尝试聚焦一次(可选)
window.addEventListener('load', () => {
setTimeout(focusInput, 500);
});
})();
// 图片显示
(function() {
'use strict';
// 要搜索的 class 列表
const searchClasses = ['message'];
function replacePics(element) {
if (!element) return;
const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT, {
acceptNode: function(node) {
if (node.classList && searchClasses.some(className => node.classList == className)) {
return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_SKIP;
}
}, false);
let node;
while (node = walker.nextNode()) {
var a = node.innerHTML.indexOf(";
if(b == -1) break;
var c = node.innerHTML.indexOf(")", b);
if(c == -1) break;
var link = node.innerHTML.substr(b + 2, c - b - 2);
var text = node.innerHTML.substr(a + 2, b - a - 2);
if(text == '') text = link;
var regex = node.innerHTML.substr(a, c - a + 1);
node.innerHTML = node.innerHTML.replace(regex, `<img style = "max-width: 516px;" src="${link}" alt="${text}">`);
a = node.innerHTML.indexOf("![")
}
}
}
replacePics(document.body);
// 监听DOM
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
replacePics(node);
}
});
}
});
});
observer.observe(document.body, { childList: true, subtree: true });
(function() {
'use strict';
// 读取
const enable = COLOR_CONFIG.enableOwnMessageColor;
if (!enable) {
return;
}
const bgColor = COLOR_CONFIG.ownMessageBgColor;
const textColor = COLOR_CONFIG.ownMessageTextColor;
// 校验
const isValidColor = (color) => /^#[0-9A-Fa-f]{6}$/.test(color);
const finalBg = isValidColor(bgColor) ? bgColor : "#d9f0ff";
const finalText = isValidColor(textColor) ? textColor : "#000000";
// 注入自定义样式
const styleId = "luogu-own-message-color-style";
if (!document.getElementById(styleId)) {
const style = document.createElement("style");
style.id = styleId;
style.textContent = `
/* 自己发送的消息气泡背景 & 文字颜色 */
.message-block.right .message {
background-color: ${finalBg} !important;
color: ${finalText} !important;
}
/* 调整气泡圆角等保持美观,不覆盖原有设计 */
.message-block.right .message {
border-radius: 12px !important;
padding: 6px 12px !important;
}
`;
document.head.appendChild(style);
}
})();
})();
:::
代码使用AI进行了可读性调整。